-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathNetworkInterpreter.adept
278 lines (226 loc) · 9.76 KB
/
NetworkInterpreter.adept
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
import 'main.adept'
import 'Action.adept'
import 'ActionAgent.adept'
#default NetworkInterpreter_print_fulfilled_actions false
struct NetworkInterpreter (
thread pthread_t,
manager *NetworkManager,
running bool,
should_stop bool,
should_stop_mutex pthread_mutex_t,
access_mutex pthread_mutex_t,
listings <String> List,
new_listings bool,
monitor_listings bool,
monitor_listings_timestamp_seconds time_t,
room_movements <RoomMovement> List,
agents <ActionAgent> List,
ignore_next_join_message_from String,
room_was_closed bool
) {
func start(network_manager *NetworkManager) {
this.manager = network_manager
pthread_mutex_init(&this.should_stop_mutex, null)
pthread_mutex_init(&this.access_mutex, null)
this.should_stop = false
this.monitor_listings = false
this.monitor_listings_timestamp_seconds = time(null)
if pthread_create(&this.thread, null, func &interpreting(*NetworkInterpreter) as ptr as func(ptr) ptr, this as ptr) {
printf('Failed to start interpreter thread\n')
return
}
this.running = true
}
func stop {
pthread_mutex_lock(&this.should_stop_mutex)
this.should_stop = true
pthread_mutex_unlock(&this.should_stop_mutex)
if this.running && pthread_join(this.thread, null) {
printf('Failed to join interpreter thread\n')
return
}
pthread_mutex_destroy(&this.should_stop_mutex)
pthread_mutex_destroy(&this.access_mutex)
this.running = false
}
func interpreting ptr {
this.manager.writeOutgoing("$list\n")
until break {
pthread_mutex_lock(&this.should_stop_mutex)
should_stop bool = this.should_stop
pthread_mutex_unlock(&this.should_stop_mutex)
if should_stop, break
// Handle incoming
if this.manager.readIncoming(def string String), this.process(string)
// Handle auto outgoing
pthread_mutex_lock(&this.access_mutex)
if this.monitor_listings {
if this.monitor_listings_timestamp_seconds < time(null) {
this.monitor_listings_timestamp_seconds = time(null)
this.manager.writeOutgoing("$list\n")
}
}
pthread_mutex_unlock(&this.access_mutex)
}
return null
}
func process(string String) {
string.trim()
subject String
if string.startsWith("$list-response ") {
list <String> List = this.parseStringArray(string.segment(15, string.length))
pthread_mutex_lock(&this.access_mutex)
defer pthread_mutex_unlock(&this.access_mutex)
this.new_listings = true
this.listings = list.commit()
} else if string.startsWith("$lobby-response ") {
list <String> List = this.parseStringArray(string.segment(16, string.length))
pthread_mutex_lock(&this.access_mutex)
defer pthread_mutex_unlock(&this.access_mutex)
each String in list, this.room_movements.addMovement(true, it.commit())
} else if string.startsWith("$error ") {
print("NETWORK ERROR: " + string)
} else if string == "$close" {
pthread_mutex_lock(&this.access_mutex)
this.room_was_closed = true
pthread_mutex_unlock(&this.access_mutex)
} else if isPlayerMonoAction(string, '$'ub, "join", &subject) {
pthread_mutex_lock(&this.access_mutex)
if this.ignore_next_join_message_from == subject {
this.ignore_next_join_message_from = ""
} else {
this.room_movements.addMovement(true, subject.commit())
}
pthread_mutex_unlock(&this.access_mutex)
} else if isPlayerMonoAction(string, '$'ub, "leave", &subject) {
pthread_mutex_lock(&this.access_mutex)
this.room_movements.addMovement(false, subject.commit())
pthread_mutex_unlock(&this.access_mutex)
} else if string.contains("@") {
pthread_mutex_lock(&this.access_mutex)
defer pthread_mutex_unlock(&this.access_mutex)
agent_name StringView = string.span(0, string.first('@'ub))
agent *ActionAgent = this.getOrCreateAgent(agent_name)
if agent == null {
print("ERROR: NetworkInterpreter.process(String): this.getOrCreateAgent(String) failed to create agent")
return
}
agent.actions.add(action(string.commit()))
}
}
func parseStringArray(text String) <String> List {
// NOTE: Does not support escape sequences
// TODO: Clean up this messy code
// SAFETY: LAZY: This function parses the text without regards to correctness
list <String> List
if text.length == 0, return list.commit()
if text.array[0] != '['ub, printf('NetworkInterpreter.parseStringArray(): "[" expected'); return list.commit()
i usize = 1; while i < text.length && text.array[i] != ']'ub {
if text.array[i] != '\''ub, printf('NetworkInterpreter.parseStringArray(): "\'" expected'); return list.commit()
j usize = i + 1; while j < text.length && text.array[j] != '\''ub, j++
list.add(text.segment(i + 1, j))
i = j + 1
while i < text.length && text.array[i] == ' 'ub || text.array[i] == ','ub, i++
}
return list.commit()
}
func setMonitorListings(value bool) {
pthread_mutex_lock(&this.access_mutex)
defer pthread_mutex_unlock(&this.access_mutex)
if this.monitor_listings == value, return
this.monitor_listings = value
this.monitor_listings_timestamp_seconds = 0
}
func updatedListings(out list *<String> List) bool {
pthread_mutex_lock(&this.access_mutex)
defer pthread_mutex_unlock(&this.access_mutex)
if this.new_listings {
*list = this.listings.clone()
this.new_listings = false
return true
}
return false
}
func updatedRoomMovements(out list *<RoomMovement> List) bool {
pthread_mutex_lock(&this.access_mutex)
defer pthread_mutex_unlock(&this.access_mutex)
if this.room_movements.length != 0 {
*list = this.room_movements.commit()
this.room_movements.clear()
return true
}
return false
}
func updatedActions(attempt_apply_action func(ptr, *Action) successful, extra_data ptr) {
pthread_mutex_lock(&this.access_mutex)
defer pthread_mutex_unlock(&this.access_mutex)
each agent ActionAgent in this.agents {
while agent.actions.length > 0 && attempt_apply_action(extra_data, agent.actions.getPointer(0)) {
#if NetworkInterpreter_print_fulfilled_actions
print("fulfilled action %" % agent.actions.getPointer(0).command_text)
#end
agent.actions.remove(0)
}
// Attempt to apply actions that should be done as soon as possible
// regardless of rescheduling
each Action in agent.actions, if it.isUrgent() {
if attempt_apply_action(extra_data, &it) {
#if NetworkInterpreter_print_fulfilled_actions
print("fulfilled (urgently) action %" % it.command_text)
#end
agent.actions.remove(idx--)
}
}
}
}
func wasRoomClosed bool {
pthread_mutex_lock(&this.access_mutex)
defer pthread_mutex_unlock(&this.access_mutex)
if this.room_was_closed {
this.room_was_closed = false
return true
}
return false
}
func clearRoomMovements(){
pthread_mutex_lock(&this.access_mutex)
defer pthread_mutex_unlock(&this.access_mutex)
this.room_movements.clear()
}
func clearActions(){
pthread_mutex_lock(&this.access_mutex)
defer pthread_mutex_unlock(&this.access_mutex)
each ActionAgent in this.agents, it.clearActions()
}
func clearAgents {
pthread_mutex_lock(&this.access_mutex)
defer pthread_mutex_unlock(&this.access_mutex)
this.agents.clear()
}
func ignoreNextJoinMessageFrom(name String){
pthread_mutex_lock(&this.access_mutex)
this.ignore_next_join_message_from = name
pthread_mutex_unlock(&this.access_mutex)
}
func getOrCreateAgent(agent_name String) *ActionAgent {
// NOTE: access_mutex should already be acquired
each ActionAgent in this.agents, if it.name == agent_name, return &it
return this.agents.addAgent(agent_name)
}
}
func isPlayerMonoAction(command_text String, separator ubyte, monoaction String, out subject *String) bool {
// Examples: "isaac@leave", "my-cool-name@join", "John_Smith@close"
// > command_text == "isaac@leave"
// > separator == '$'ub
// > monoaction == "leave"
// > out *subject == "isaac"
if command_text.first(" ") != -1sl, return false
symbol long = command_text.first(separator)
if symbol < 0, return false
action String = command_text.segment(symbol + 1, command_text.length)
if action == monoaction {
*subject = command_text.segment(0, symbol)
return true
}
return false
}