state.gno
3.71 Kb · 137 lines
1package shots
2
3import (
4 "chain"
5 "chain/banker"
6 "strconv"
7
8 "gno.land/p/g1lnkytfqcjwllws63gvf0mv9yt04aswy4y9amhm/shootout/v0"
9 "gno.land/p/nt/avl/v0"
10)
11
12// The game state lives here, at the permanent path, so accepting a new
13// implementation changes the rules engine without moving a single pot.
14//
15// Implementations never hold references into it. They read copies through
16// StateRef and write whole values back, so every mutation is performed by
17// code declared in this realm and persists here.
18
19var (
20 games = avl.NewTree() // gameKey(id) -> *shootout.Game
21 sessions = avl.NewTree() // sessionKey(id, no) -> *shootout.Session
22 lastID uint64
23)
24
25func gameKey(id uint64) string {
26 s := strconv.FormatUint(id, 10)
27 for len(s) < 20 {
28 s = "0" + s
29 }
30 return s
31}
32
33func sessionKey(id uint64, no int) string {
34 s := strconv.Itoa(no)
35 for len(s) < 10 {
36 s = "0" + s
37 }
38 return gameKey(id) + "/" + s
39}
40
41func getGame(id uint64) (shootout.Game, bool) {
42 v := games.Get(gameKey(id))
43 if v == nil {
44 return shootout.Game{}, false
45 }
46 return *(v.(*shootout.Game)), true
47}
48
49func getSession(id uint64, no int) (shootout.Session, bool) {
50 v := sessions.Get(sessionKey(id, no))
51 if v == nil {
52 return shootout.Session{}, false
53 }
54 s := *(v.(*shootout.Session))
55 s.Kicks = append([]shootout.Kick(nil), s.Kicks...)
56 return s, true
57}
58
59// StateRef is a permission to read and write the game state, handed to code
60// running on this realm's cur. It carries the caller's realm value and
61// re-checks it on every operation, so it cannot be stashed (the VM refuses to
62// persist a realm value) or replayed after its frame returns.
63type StateRef struct {
64 rlm realm
65}
66
67// Store hands a StateRef to the live implementation, or to a registered
68// extension. Any other realm presents its own cur and is refused.
69func Store(_ int, rlm realm) *StateRef {
70 assertStateAccess(0, rlm)
71 return &StateRef{rlm: rlm}
72}
73
74// NewGameID allocates the next game id.
75func (r *StateRef) NewGameID() uint64 {
76 assertStateAccess(0, r.rlm)
77 lastID++
78 return lastID
79}
80
81// Game returns a copy of game id.
82func (r *StateRef) Game(id uint64) (shootout.Game, bool) {
83 assertStateAccess(0, r.rlm)
84 return getGame(id)
85}
86
87// PutGame stores g (by value) under g.ID.
88func (r *StateRef) PutGame(g shootout.Game) {
89 assertStateAccess(0, r.rlm)
90 if g.ID == 0 || g.ID > lastID {
91 panic("shots: PutGame with an id that was never allocated")
92 }
93 c := g
94 games.Set(gameKey(g.ID), &c)
95}
96
97// Session returns a copy of session no of game id, kicks included.
98func (r *StateRef) Session(id uint64, no int) (shootout.Session, bool) {
99 assertStateAccess(0, r.rlm)
100 return getSession(id, no)
101}
102
103// PutSession stores s (by value, kicks copied).
104func (r *StateRef) PutSession(s shootout.Session) {
105 assertStateAccess(0, r.rlm)
106 if _, ok := getGame(s.GameID); !ok || s.No <= 0 {
107 panic("shots: PutSession for a game or session that does not exist")
108 }
109 c := s
110 c.Kicks = append([]shootout.Kick(nil), s.Kicks...)
111 sessions.Set(sessionKey(s.GameID, s.No), &c)
112}
113
114// Pay sends amount ugnot from this realm's balance to to.
115func (r *StateRef) Pay(to address, amount int64) {
116 assertStateAccess(0, r.rlm)
117 if amount <= 0 {
118 return
119 }
120 if !to.IsValid() {
121 panic("shots: invalid payee address")
122 }
123 bk := banker.NewBanker(banker.BankerTypeRealmSend, r.rlm)
124 bk.SendCoins(r.rlm.Address(), to, chain.NewCoins(chain.NewCoin(Denom, amount)))
125}
126
127// assertStateAccess panics unless rlm is a live cur belonging to this realm
128// or to a registered extension.
129func assertStateAccess(_ int, rlm realm) {
130 if !rlm.IsCurrent() {
131 panic("shots: realm value is not the caller's live cur")
132 }
133 if c := rlm.PkgPath(); c != selfPath && !proxy.IsExtension(c) {
134 panic("shots: state is reachable only through " + selfPath +
135 " or a registered extension, not from " + c)
136 }
137}