Search Apps Documentation Source Content File Folder Download Copy Actions Download State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

policy.gno

6.32 Kb · 253 lines
  1package game_world
  2
  3import (
  4	"chain"
  5	"strconv"
  6
  7	"gno.land/p/akkadia/beta/accesscontrol"
  8	"gno.land/p/nt/bptree/v0"
  9	"gno.land/r/akkadia/beta/admin"
 10)
 11
 12const (
 13	SetGameTypePolicyEvent     = "SetGameTypePolicy"
 14	AddApprovedCreatorEvent    = "AddApprovedCreator"
 15	RemoveApprovedCreatorEvent = "RemoveApprovedCreator"
 16	SetApprovedCreatorsEvent   = "SetApprovedCreators"
 17	approvedCreatorListMax     = 100
 18	approvedCreatorBatchMax    = 100
 19	gameTypeMaxLen             = 64
 20)
 21
 22var (
 23	gameTypePolicies *bptree.BPTree
 24	approvedCreators *bptree.BPTree
 25)
 26
 27func init() {
 28	initializePolicyState()
 29}
 30
 31func SetGameTypePolicy(cur realm, gameType string, creationCost int64, mode CreationMode) {
 32	executor := accesscontrol.MustGetAdminOrOperatorCaller(0, cur, admin.IsAdmin, admin.IsOperator)
 33	assertValidGameType(gameType)
 34	if creationCost < 0 {
 35		panic("creation cost must not be negative")
 36	}
 37	assertValidCreationMode(mode)
 38
 39	oldValue := gameTypePolicies.Get(gameType)
 40	old := GameTypePolicy{}
 41	found := oldValue != nil
 42	if found {
 43		old = oldValue.(GameTypePolicy)
 44	}
 45	if found && old.CreationCost == creationCost && old.CreationMode == mode {
 46		return
 47	}
 48	gameTypePolicies.Set(gameType, GameTypePolicy{CreationCost: creationCost, CreationMode: mode})
 49	chain.Emit(
 50		SetGameTypePolicyEvent,
 51		"executor", executor.String(),
 52		"gameType", gameType,
 53		"oldCreationCost", strconv.FormatInt(old.CreationCost, 10),
 54		"newCreationCost", strconv.FormatInt(creationCost, 10),
 55		"oldCreationMode", strconv.Itoa(int(old.CreationMode)),
 56		"newCreationMode", strconv.Itoa(int(mode)),
 57	)
 58}
 59
 60func AddApprovedCreator(cur realm, gameType string, creator address) {
 61	executor := accesscontrol.MustGetAdminOrOperatorCaller(0, cur, admin.IsAdmin, admin.IsOperator)
 62	mustGetGameTypePolicy(gameType)
 63	assertValidCreator(creator)
 64	if !setApprovedCreator(gameType, creator, true) {
 65		return
 66	}
 67	chain.Emit(
 68		AddApprovedCreatorEvent,
 69		"executor", executor.String(),
 70		"gameType", gameType,
 71		"creator", creator.String(),
 72	)
 73}
 74
 75func RemoveApprovedCreator(cur realm, gameType string, creator address) {
 76	executor := accesscontrol.MustGetAdminOrOperatorCaller(0, cur, admin.IsAdmin, admin.IsOperator)
 77	mustGetGameTypePolicy(gameType)
 78	assertValidCreator(creator)
 79	if !setApprovedCreator(gameType, creator, false) {
 80		return
 81	}
 82	chain.Emit(
 83		RemoveApprovedCreatorEvent,
 84		"executor", executor.String(),
 85		"gameType", gameType,
 86		"creator", creator.String(),
 87	)
 88}
 89
 90func SetApprovedCreators(cur realm, gameType, creatorsCSV string, approved bool) {
 91	executor := accesscontrol.MustGetAdminOrOperatorCaller(0, cur, admin.IsAdmin, admin.IsOperator)
 92	mustGetGameTypePolicy(gameType)
 93	if creatorsCSV == "" {
 94		panic("creator list must not be empty")
 95	}
 96
 97	requestedCount := 0
 98	changedCount := 0
 99	start := 0
100	for {
101		end := start
102		for end < len(creatorsCSV) && creatorsCSV[end] != ',' {
103			end++
104		}
105		requestedCount++
106		if requestedCount > approvedCreatorBatchMax {
107			panic("creator list exceeds limit")
108		}
109		creator := address(creatorsCSV[start:end])
110		assertValidCreator(creator)
111		if setApprovedCreator(gameType, creator, approved) {
112			changedCount++
113		}
114		if end == len(creatorsCSV) {
115			break
116		}
117		start = end + 1
118	}
119	if changedCount == 0 {
120		return
121	}
122	chain.Emit(
123		SetApprovedCreatorsEvent,
124		"executor", executor.String(),
125		"gameType", gameType,
126		"creatorsCSV", creatorsCSV,
127		"approved", strconv.FormatBool(approved),
128		"requestedCount", strconv.Itoa(requestedCount),
129		"changedCount", strconv.Itoa(changedCount),
130	)
131}
132
133func setApprovedCreator(gameType string, creator address, approved bool) bool {
134	set, found := getApprovedCreators(gameType)
135	old := found && set.Has(creator.String())
136	if old == approved {
137		return false
138	}
139	if approved {
140		if !found {
141			set = createApprovedCreators(gameType)
142		}
143		set.Set(creator.String(), true)
144	} else {
145		set.Remove(creator.String())
146	}
147	return true
148}
149
150func IsApprovedCreator(gameType string, creator address) bool {
151	mustGetGameTypePolicy(gameType)
152	assertValidCreator(creator)
153	return isApprovedCreator(gameType, creator)
154}
155
156func GetApprovedCreatorCount(gameType string) int {
157	mustGetGameTypePolicy(gameType)
158	set, found := getApprovedCreators(gameType)
159	if !found {
160		return 0
161	}
162	return set.Size()
163}
164
165func ListApprovedCreators(gameType string, offset, count int) []address {
166	mustGetGameTypePolicy(gameType)
167	if offset < 0 {
168		panic("offset must not be negative")
169	}
170	if count < 1 || count > approvedCreatorListMax {
171		panic("count must be between 1 and 100")
172	}
173	result := []address{}
174	set, found := getApprovedCreators(gameType)
175	if !found {
176		return result
177	}
178	set.IterateByOffset(offset, count, func(value string, _ any) bool {
179		result = append(result, address(value))
180		return false
181	})
182	return result
183}
184
185func HasGameTypePolicy(gameType string) bool {
186	assertValidGameType(gameType)
187	return gameTypePolicies.Has(gameType)
188}
189
190func GetGameTypePolicy(gameType string) GameTypePolicy {
191	return mustGetGameTypePolicy(gameType)
192}
193
194func initializePolicyState() {
195	gameTypePolicies = bptree.NewBPTree32()
196	approvedCreators = bptree.NewBPTree32()
197}
198
199func assertValidGameType(gameType string) {
200	if len(gameType) < 1 || len(gameType) > gameTypeMaxLen {
201		panic("game type key length must be between 1 and 64")
202	}
203	for _, ch := range gameType {
204		if (ch < 'a' || ch > 'z') && (ch < '0' || ch > '9') && ch != '_' && ch != '-' {
205			panic("invalid game type key")
206		}
207	}
208}
209
210func mustGetGameTypePolicy(gameType string) GameTypePolicy {
211	assertValidGameType(gameType)
212	value := gameTypePolicies.Get(gameType)
213	if value == nil {
214		panic("game type policy not found: " + gameType)
215	}
216	return value.(GameTypePolicy)
217}
218
219func getApprovedCreators(gameType string) (*bptree.BPTree, bool) {
220	value := approvedCreators.Get(gameType)
221	if value == nil {
222		return nil, false
223	}
224	return value.(*bptree.BPTree), true
225}
226
227func createApprovedCreators(gameType string) *bptree.BPTree {
228	set := bptree.NewBPTree32()
229	approvedCreators.Set(gameType, set)
230	return set
231}
232
233func isApprovedCreator(gameType string, creator address) bool {
234	set, found := getApprovedCreators(gameType)
235	return found && set.Has(creator.String())
236}
237
238func assertValidCreationMode(mode CreationMode) {
239	if mode < CreationModeOperatorOnly || mode > CreationModePublicCreator {
240		panic("invalid creation mode")
241	}
242}
243
244func assertValidCreator(creator address) {
245	if !creator.IsValid() {
246		panic("invalid creator address")
247	}
248}
249
250func resetGameWorldState() {
251	initializePolicyState()
252	resetGameWorldStore()
253}