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.

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}