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

Community realm. Read the code first. Community realm, deployed by its author. Read the code before you interact or send coins.

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}