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.

game_world.gno

5.13 Kb · 191 lines
  1package game_world
  2
  3import (
  4	"chain"
  5	"chain/banker"
  6	"chain/runtime"
  7	"chain/runtime/unsafe"
  8	"strconv"
  9
 10	"gno.land/p/akkadia/beta/accesscontrol"
 11	"gno.land/r/akkadia/beta/admin"
 12)
 13
 14const (
 15	CreateGameWorldEvent   = "CreateGameWorld"
 16	RestrictGameWorldEvent = "RestrictGameWorld"
 17)
 18
 19type CreationMode int
 20
 21const (
 22	CreationModeInvalid CreationMode = iota
 23	CreationModeOperatorOnly
 24	CreationModeApprovedCreator
 25	CreationModePublicCreator
 26)
 27
 28type GameWorldStatus int
 29
 30const (
 31	GameWorldStatusInvalid GameWorldStatus = iota
 32	GameWorldStatusExists
 33	GameWorldStatusRestricted
 34)
 35
 36type GameWorld struct {
 37	ID               uint64
 38	Owner            address
 39	GameType         string
 40	Status           GameWorldStatus
 41	CreatedHeight    int64
 42	RestrictedHeight int64
 43}
 44
 45type GameTypePolicy struct {
 46	CreationCost int64
 47	CreationMode CreationMode
 48}
 49
 50func CreateGameWorld(cur realm, gameType string) uint64 {
 51	owner := accesscontrol.MustGetUserCaller(0, cur)
 52	policy := mustGetGameTypePolicy(gameType)
 53	assertCreationAllowed(gameType, owner, policy.CreationMode)
 54	assertExactUgnotPayment(policy.CreationCost)
 55
 56	id := createGameWorld(owner, gameType)
 57	if policy.CreationCost > 0 {
 58		bnk := banker.NewBanker(banker.BankerTypeRealmSend, cur)
 59		bnk.SendCoins(cur.Address(), admin.GetFeeCollector(), chain.Coins{{"ugnot", policy.CreationCost}})
 60	}
 61	emitCreateGameWorld(owner, id, gameType, policy.CreationCost)
 62	return id
 63}
 64
 65func RestrictGameWorld(cur realm, gameWorldID uint64) {
 66	executor := accesscontrol.MustGetAdminOrOperatorCaller(0, cur, admin.IsAdmin, admin.IsOperator)
 67	world := GetGameWorld(gameWorldID)
 68	if world.Status == GameWorldStatusRestricted {
 69		return
 70	}
 71	world.Status = GameWorldStatusRestricted
 72	world.RestrictedHeight = runtime.ChainHeight()
 73	gameWorlds.Set(uint64Key(gameWorldID), world)
 74	removeOwnerGameWorldID(world.Owner, gameWorldID)
 75	restrictedGameWorldIDs.Set(uint64Key(gameWorldID), true)
 76	chain.Emit(
 77		RestrictGameWorldEvent,
 78		"executor", executor.String(),
 79		"id", strconv.FormatUint(gameWorldID, 10),
 80		"oldStatus", strconv.Itoa(int(GameWorldStatusExists)),
 81		"newStatus", strconv.Itoa(int(GameWorldStatusRestricted)),
 82		"restrictedHeight", strconv.FormatInt(world.RestrictedHeight, 10),
 83	)
 84}
 85
 86func HasGameWorld(gameWorldID uint64) bool { return gameWorlds.Has(uint64Key(gameWorldID)) }
 87
 88func GetGameWorld(gameWorldID uint64) GameWorld {
 89	value := gameWorlds.Get(uint64Key(gameWorldID))
 90	if value == nil {
 91		panic("game world not found: " + strconv.FormatUint(gameWorldID, 10))
 92	}
 93	return value.(GameWorld)
 94}
 95
 96func GetLatestGameWorldID() uint64 { return latestGameWorldID }
 97
 98func GetListLimit() int { return gameWorldListLimit }
 99
100func GetRestrictedGameWorldCount() int { return restrictedGameWorldIDs.Size() }
101
102func ListRestrictedGameWorldIDs(page, count int) []uint64 {
103	if page < 1 {
104		panic("page must be at least 1")
105	}
106	if count < 1 || count > gameWorldListLimit {
107		panic("count must be between 1 and 100")
108	}
109	result := []uint64{}
110	restrictedGameWorldIDs.IterateByOffset((page-1)*count, count, func(key string, _ any) bool {
111		result = append(result, parseUint64Key(key))
112		return false
113	})
114	return result
115}
116
117func ListLatestGameWorldsByOwner(owner address) []GameWorld {
118	ids, found := getOwnerGameWorldIDs(owner)
119	if !found {
120		return []GameWorld{}
121	}
122	offset := ids.Size() - gameWorldListLimit
123	if offset < 0 {
124		offset = 0
125	}
126	result := []GameWorld{}
127	ids.IterateByOffset(offset, gameWorldListLimit, func(key string, _ any) bool {
128		result = append(result, GetGameWorld(parseUint64Key(key)))
129		return false
130	})
131	return result
132}
133
134func createGameWorld(owner address, gameType string) uint64 {
135	if latestGameWorldID == uint64(18446744073709551615) {
136		panic("game world id exhausted")
137	}
138	latestGameWorldID++
139	id := latestGameWorldID
140	gameWorlds.Set(uint64Key(id), GameWorld{
141		ID:            id,
142		Owner:         owner,
143		GameType:      gameType,
144		Status:        GameWorldStatusExists,
145		CreatedHeight: runtime.ChainHeight(),
146	})
147	addOwnerGameWorldID(owner, id)
148	return id
149}
150
151func assertCreationAllowed(gameType string, creator address, mode CreationMode) {
152	switch mode {
153	case CreationModeOperatorOnly:
154		if !admin.IsAdmin(creator) && !admin.IsOperator(creator) {
155			panic("game type creation is operator only")
156		}
157	case CreationModeApprovedCreator:
158		if !isApprovedCreator(gameType, creator) {
159			panic("creator is not approved for game type")
160		}
161	case CreationModePublicCreator:
162		return
163	default:
164		panic("invalid creation mode")
165	}
166}
167
168func assertExactUgnotPayment(expected int64) {
169	coins := unsafe.OriginSend()
170	if expected == 0 {
171		if len(coins) != 0 {
172			panic("exact payment required: expected no coins, got " + coins.String())
173		}
174		return
175	}
176	if len(coins) != 1 || coins[0].Denom != "ugnot" || coins[0].Amount != expected {
177		panic("exact payment required: expected " + strconv.FormatInt(expected, 10) + "ugnot, got " + coins.String())
178	}
179}
180
181func emitCreateGameWorld(owner address, id uint64, gameType string, creationCost int64) {
182	chain.Emit(
183		CreateGameWorldEvent,
184		"executor", owner.String(),
185		"id", strconv.FormatUint(id, 10),
186		"owner", owner.String(),
187		"gameType", gameType,
188		"creationCost", strconv.FormatInt(creationCost, 10),
189		"createdHeight", strconv.FormatInt(runtime.ChainHeight(), 10),
190	)
191}