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}