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.

party.gno

5.03 Kb · 277 lines
  1// Package party is the Shoal crew book. Members share a fight and do not
  2// bite each other. This realm holds no ugnot. The fish realm pays a kill.
  3// Not deployed.
  4package party
  5
  6import (
  7	"chain"
  8	"chain/runtime/unsafe"
  9	"strconv"
 10)
 11
 12const (
 13	partyCap = 8
 14	nameMax  = 16
 15)
 16
 17type crew struct {
 18	id      int
 19	name    string
 20	leader  address
 21	members []address
 22	closed  bool
 23}
 24
 25var (
 26	crews   []*crew
 27	seat    map[address]int
 28	invited map[address]int
 29	nextPID int
 30)
 31
 32func Create(cur realm, name string) int {
 33	caller := requireUser(cur)
 34	takeExact(0)
 35	if seatOf(caller) != 0 {
 36		panic("party: seat")
 37	}
 38	clean := cleanName(name)
 39	if clean == "" || taken(clean) {
 40		panic("party: name")
 41	}
 42	ensure()
 43	nextPID++
 44	id := nextPID
 45	body := &crew{
 46		id:      id,
 47		name:    clean,
 48		leader:  caller,
 49		members: []address{caller},
 50	}
 51	for len(crews) <= id {
 52		crews = append(crews, nil)
 53	}
 54	crews[id] = body
 55	seat[caller] = id
 56	chain.Emit("Create", "id", strconv.Itoa(id), "leader", caller.String(), "name", clean)
 57	return id
 58}
 59
 60func Invite(cur realm, who address) {
 61	caller := requireUser(cur)
 62	takeExact(0)
 63	if !who.IsValid() {
 64		panic("party: addr")
 65	}
 66	if who == caller {
 67		panic("party: self")
 68	}
 69	id := seatOf(caller)
 70	body := openCrew(id)
 71	if seatOf(who) != 0 {
 72		panic("party: seat")
 73	}
 74	if len(body.members) >= partyCap {
 75		panic("party: full")
 76	}
 77	ensure()
 78	invited[who] = id
 79	chain.Emit("Invite", "id", strconv.Itoa(id), "who", who.String())
 80}
 81
 82func Accept(cur realm) int {
 83	caller := requireUser(cur)
 84	takeExact(0)
 85	if seatOf(caller) != 0 {
 86		panic("party: seat")
 87	}
 88	id := 0
 89	if invited != nil {
 90		id = invited[caller]
 91	}
 92	body := openCrew(id)
 93	if len(body.members) >= partyCap {
 94		panic("party: full")
 95	}
 96	delete(invited, caller)
 97	body.members = append(body.members, caller)
 98	seat[caller] = id
 99	chain.Emit("Join", "id", strconv.Itoa(id), "who", caller.String())
100	return id
101}
102
103func Leave(cur realm) {
104	caller := requireUser(cur)
105	takeExact(0)
106	id := seatOf(caller)
107	body := openCrew(id)
108	dropMember(body, caller)
109	chain.Emit("Leave", "id", strconv.Itoa(id), "who", caller.String())
110}
111
112func Kick(cur realm, who address) {
113	caller := requireUser(cur)
114	takeExact(0)
115	id := seatOf(caller)
116	body := openCrew(id)
117	if body.leader != caller {
118		panic("party: leader")
119	}
120	if who == caller || seatOf(who) != id {
121		panic("party: member")
122	}
123	dropMember(body, who)
124	chain.Emit("Kick", "id", strconv.Itoa(id), "who", who.String())
125}
126
127func Disband(cur realm) {
128	caller := requireUser(cur)
129	takeExact(0)
130	id := seatOf(caller)
131	body := openCrew(id)
132	if body.leader != caller {
133		panic("party: leader")
134	}
135	for _, member := range body.members {
136		delete(seat, member)
137	}
138	body.members = nil
139	body.closed = true
140	body.name = ""
141	dropInvites(id)
142	chain.Emit("Disband", "id", strconv.Itoa(id))
143}
144
145func PartyOf(cur realm, who address) int {
146	takeExact(0)
147	return seatOf(who)
148}
149
150func SameParty(cur realm, a, b address) bool {
151	takeExact(0)
152	id := seatOf(a)
153	return id != 0 && id == seatOf(b)
154}
155
156func NameOf(cur realm, id int) string {
157	takeExact(0)
158	return openCrew(id).name
159}
160
161func LeaderOf(cur realm, id int) address {
162	takeExact(0)
163	return openCrew(id).leader
164}
165
166func MemberCount(cur realm, id int) int {
167	takeExact(0)
168	return len(openCrew(id).members)
169}
170
171func MemberAt(cur realm, id, index int) address {
172	takeExact(0)
173	body := openCrew(id)
174	if index < 0 || index >= len(body.members) {
175		panic("party: index")
176	}
177	return body.members[index]
178}
179
180func requireUser(cur realm) address {
181	if !cur.Previous().IsUserCall() {
182		panic("party: eoa")
183	}
184	return cur.Previous().Address()
185}
186
187func takeExact(n int64) {
188	sent := unsafe.OriginSend()
189	if n == 0 {
190		if len(sent) != 0 {
191			panic("party: payment")
192		}
193		return
194	}
195	panic("party: payment")
196}
197
198func ensure() {
199	if seat == nil {
200		seat = make(map[address]int)
201	}
202	if invited == nil {
203		invited = make(map[address]int)
204	}
205}
206
207func seatOf(who address) int {
208	if seat == nil || !who.IsValid() {
209		return 0
210	}
211	return seat[who]
212}
213
214func openCrew(id int) *crew {
215	if id < 1 || id >= len(crews) || crews[id] == nil || crews[id].closed {
216		panic("party: none")
217	}
218	return crews[id]
219}
220
221func taken(name string) bool {
222	for _, body := range crews {
223		if body != nil && !body.closed && body.name == name {
224			return true
225		}
226	}
227	return false
228}
229
230func cleanName(name string) string {
231	if len(name) < 1 || len(name) > nameMax {
232		return ""
233	}
234	for _, c := range name {
235		if (c < 'a' || c > 'z') && (c < 'A' || c > 'Z') && (c < '0' || c > '9') && c != ' ' {
236			return ""
237		}
238	}
239	return name
240}
241
242func dropMember(body *crew, who address) {
243	next := make([]address, 0, len(body.members))
244	for _, member := range body.members {
245		if member != who {
246			next = append(next, member)
247		}
248	}
249	delete(seat, who)
250	if invited != nil {
251		delete(invited, who)
252	}
253	if len(next) == 0 {
254		body.members = nil
255		body.closed = true
256		body.name = ""
257		dropInvites(body.id)
258		return
259	}
260	body.members = next
261	body.leader = next[0]
262}
263
264func dropInvites(id int) {
265	if invited == nil {
266		return
267	}
268	var drop []address
269	for who, pid := range invited {
270		if pid == id {
271			drop = append(drop, who)
272		}
273	}
274	for _, who := range drop {
275		delete(invited, who)
276	}
277}