json.gno
2.65 Kb · 98 lines
1package game
2
3import "strings"
4
5// The JSON documents are built by hand. That is safe because every string
6// that reaches them was validated against a JSON-safe charset when it was
7// written (see validate.gno): ids, names, models, colors and bech32 addresses
8// need no escaping. The one string that is not under the realm's control, an
9// address typed into a query, goes through quote.
10//
11// Ordering is deterministic everywhere: params follow the registry and the
12// leaderboard is best first.
13
14// StateJSON returns everything the game needs from the chain, as one JSON
15// object:
16//
17// {
18// "version": 12,
19// "treasury": "g1...",
20// "params": { "plane_speed": 70, "boost_speed": 130 }
21// }
22//
23// The output has no whitespace. It only changes when Version does. The skins
24// are in their own realm, gnofly/nfts/skins.
25func StateJSON() string {
26 var b strings.Builder
27
28 b.WriteString(`{"version":`)
29 b.WriteString(itoa(version))
30 b.WriteString(`,"treasury":"`)
31 b.WriteString(treasury.String())
32
33 b.WriteString(`","params":{`)
34 for i, p := range params {
35 if i > 0 {
36 b.WriteByte(',')
37 }
38 b.WriteByte('"')
39 b.WriteString(p.key)
40 b.WriteString(`":`)
41 b.WriteString(itoa(p.value))
42 }
43
44 b.WriteString(`}}`)
45 return b.String()
46}
47
48// PlayerJSON returns one player as a JSON object:
49//
50// { "address": "g1...", "kills": 17 }
51//
52// The output has no whitespace. An unknown address has zero kills. The skins
53// and planes a player owns are read from their realms (SkinsOf, PlanesOf).
54func PlayerJSON(addr address) string {
55 var b strings.Builder
56 b.WriteString(`{"address":`)
57 b.WriteString(quote(addr.String()))
58 b.WriteString(`,"kills":`)
59 b.WriteString(itoa(recordedKills(addr)))
60 b.WriteByte('}')
61 return b.String()
62}
63
64// KillLeaderboardJSON returns the top players by kills recorded on-chain as
65// a JSON array, best first, at most leaderboard_size entries:
66//
67// [ { "address": "g1...", "kills": 17 } ]
68//
69// The output has no whitespace. With equal kills, the player who got there
70// first stays ahead.
71func KillLeaderboardJSON() string {
72 var b strings.Builder
73 writeRanking(&b, killers, "kills")
74 return b.String()
75}
76
77// quote returns s as a JSON string literal, quotes included. It is for the
78// strings the realm did not validate. Quotes and backslashes are escaped and
79// anything outside printable ASCII is replaced by "?", so the result is
80// valid JSON whatever s holds.
81func quote(s string) string {
82 var b strings.Builder
83 b.WriteByte('"')
84 for i := 0; i < len(s); i++ {
85 c := s[i]
86 switch {
87 case c == '"' || c == '\\':
88 b.WriteByte('\\')
89 b.WriteByte(c)
90 case c < 0x20 || c > 0x7e:
91 b.WriteByte('?')
92 default:
93 b.WriteByte(c)
94 }
95 }
96 b.WriteByte('"')
97 return b.String()
98}