about summary refs log tree commit diff stats
path: root/client/Archipelago/client.gd
diff options
context:
space:
mode:
Diffstat (limited to 'client/Archipelago/client.gd')
-rw-r--r--client/Archipelago/client.gd415
1 files changed, 0 insertions, 415 deletions
diff --git a/client/Archipelago/client.gd b/client/Archipelago/client.gd deleted file mode 100644 index 2e080fd..0000000 --- a/client/Archipelago/client.gd +++ /dev/null
@@ -1,415 +0,0 @@
1extends Node
2
3const ap_version = {"major": 0, "minor": 6, "build": 3, "class": "Version"}
4
5var SCRIPT_uuid
6
7var _ws = WebSocketPeer.new()
8var _should_process = false
9var _initiated_disconnect = false
10var _try_wss = false
11var _has_connected = false
12
13var _datapackages = {}
14var _pending_packages = []
15var _item_id_to_name = {} # All games
16var _location_id_to_name = {} # All games
17var _item_name_to_id = {} # Lingo 2 only
18var _location_name_to_id = {} # Lingo 2 only
19
20var _remote_version = {"major": 0, "minor": 0, "build": 0}
21var _gen_version = {"major": 0, "minor": 0, "build": 0}
22
23var ap_server = ""
24var ap_user = ""
25var ap_pass = ""
26
27var _authenticated = false
28var _seed = ""
29var _team = 0
30var _slot = 0
31var _players = []
32var _player_name_by_slot = {}
33var _game_by_player = {}
34var _checked_locations = []
35var _received_indexes = []
36var _received_items = {}
37var _slot_data = {}
38
39signal could_not_connect
40signal connect_status
41signal client_connected(slot_data)
42signal item_received(item_id, index, player, flags, amount)
43signal message_received(message)
44signal location_scout_received(item_id, location_id, player, flags)
45
46
47func _init():
48 set_process_mode(Node.PROCESS_MODE_ALWAYS)
49
50 global._print("Instantiated APClient")
51
52 # Read AP datapackages from file, if there are any
53 if FileAccess.file_exists("user://ap_datapackages"):
54 var file = FileAccess.open("user://ap_datapackages", FileAccess.READ)
55 var data = file.get_var(true)
56 file.close()
57
58 if typeof(data) != TYPE_DICTIONARY:
59 global._print("AP datapackages file is corrupted")
60 data = {}
61
62 _datapackages = data
63
64 processDatapackages()
65
66
67func _ready():
68 pass
69 #_ws.connect("connection_closed", _closed)
70 #_ws.connect("connection_failed", _closed)
71 #_ws.connect("server_disconnected", _closed)
72 #_ws.connect("connection_error", _errored)
73 #_ws.connect("connection_established", _connected)
74
75
76func _reset_state():
77 _should_process = false
78 _authenticated = false
79 _try_wss = false
80 _has_connected = false
81 _received_items = {}
82 _received_indexes = []
83
84
85func _errored():
86 if _try_wss:
87 global._print("Could not connect to AP with ws://, now trying wss://")
88 connectToServer(ap_server, ap_user, ap_pass)
89 else:
90 global._print("AP connection failed")
91 _reset_state()
92
93 emit_signal(
94 "could_not_connect",
95 "Could not connect to Archipelago. Check that your server and port are correct. See the error log for more information."
96 )
97
98
99func _closed(_was_clean = true):
100 global._print("Connection closed")
101 _reset_state()
102
103 if not _initiated_disconnect:
104 emit_signal("could_not_connect", "Disconnected from Archipelago")
105
106 _initiated_disconnect = false
107
108
109func _connected(_proto = ""):
110 global._print("Connected!")
111 _try_wss = false
112
113
114func disconnect_from_ap():
115 _initiated_disconnect = true
116 _ws.close()
117
118
119func _process(_delta):
120 if _should_process:
121 _ws.poll()
122
123 var state = _ws.get_ready_state()
124 if state == WebSocketPeer.STATE_OPEN:
125 if not _has_connected:
126 _has_connected = true
127
128 _connected()
129
130 while _ws.get_available_packet_count():
131 var packet = _ws.get_packet()
132 global._print("Got data from server: " + packet.get_string_from_utf8())
133 var json = JSON.new()
134 var jserror = json.parse(packet.get_string_from_utf8())
135 if jserror != OK:
136 global._print("Error parsing packet from AP: " + jserror.error_string)
137 return
138
139 for message in json.data:
140 var cmd = message["cmd"]
141 global._print("Received command: " + cmd)
142
143 if cmd == "RoomInfo":
144 _seed = message["seed_name"]
145 _remote_version = message["version"]
146 _gen_version = message["generator_version"]
147
148 var needed_games = []
149 for game in message["datapackage_checksums"].keys():
150 if (
151 !_datapackages.has(game)
152 or (
153 _datapackages[game]["checksum"]
154 != message["datapackage_checksums"][game]
155 )
156 ):
157 needed_games.append(game)
158
159 if !needed_games.is_empty():
160 _pending_packages = needed_games
161 var cur_needed = _pending_packages.pop_front()
162 requestDatapackages([cur_needed])
163 else:
164 connectToRoom()
165
166 elif cmd == "DataPackage":
167 for game in message["data"]["games"].keys():
168 _datapackages[game] = message["data"]["games"][game]
169 saveDatapackages()
170
171 if !_pending_packages.is_empty():
172 var cur_needed = _pending_packages.pop_front()
173 requestDatapackages([cur_needed])
174 else:
175 processDatapackages()
176 connectToRoom()
177
178 elif cmd == "Connected":
179 _authenticated = true
180 _team = message["team"]
181 _slot = message["slot"]
182 _players = message["players"]
183 _checked_locations = message["checked_locations"]
184 _slot_data = message["slot_data"]
185
186 for player in _players:
187 _player_name_by_slot[player["slot"]] = player["alias"]
188 _game_by_player[player["slot"]] = message["slot_info"][str(
189 player["slot"]
190 )]["game"]
191
192 emit_signal("client_connected", _slot_data)
193
194 elif cmd == "ConnectionRefused":
195 var error_message = ""
196 for error in message["errors"]:
197 var submsg = ""
198 if error == "InvalidSlot":
199 submsg = "Invalid player name."
200 elif error == "InvalidGame":
201 submsg = "The specified player is not playing Lingo."
202 elif error == "IncompatibleVersion":
203 submsg = (
204 "The Archipelago server is not the correct version for this client. Expected v%d.%d.%d. Found v%d.%d.%d."
205 % [
206 ap_version["major"],
207 ap_version["minor"],
208 ap_version["build"],
209 _remote_version["major"],
210 _remote_version["minor"],
211 _remote_version["build"]
212 ]
213 )
214 elif error == "InvalidPassword":
215 submsg = "Incorrect password."
216 elif error == "InvalidItemsHandling":
217 submsg = "Invalid item handling flag. This is a bug with the client."
218
219 if submsg != "":
220 if error_message != "":
221 error_message += " "
222 error_message += submsg
223
224 if error_message == "":
225 error_message = "Unknown error."
226
227 _initiated_disconnect = true
228 _ws.disconnect_from_host()
229
230 emit_signal("could_not_connect", error_message)
231 global._print("Connection to AP refused")
232 global._print(message)
233
234 elif cmd == "ReceivedItems":
235 var i = 0
236 for item in message["items"]:
237 var index = int(message["index"] + i)
238 i += 1
239
240 if _received_indexes.has(index):
241 # Do not re-process items.
242 continue
243
244 _received_indexes.append(index)
245
246 var item_id = int(item["item"])
247 _received_items[item_id] = _received_items.get(item_id, 0) + 1
248
249 emit_signal(
250 "item_received",
251 item_id,
252 index,
253 int(item["player"]),
254 int(item["flags"]),
255 _received_items[item_id]
256 )
257
258 elif cmd == "PrintJSON":
259 emit_signal("message_received", message)
260
261 elif cmd == "LocationInfo":
262 for loc in message["locations"]:
263 emit_signal(
264 "location_scout_received",
265 int(loc["item"]),
266 int(loc["location"]),
267 int(loc["player"]),
268 int(loc["flags"])
269 )
270
271 elif state == WebSocketPeer.STATE_CLOSED:
272 if _has_connected:
273 _closed()
274 else:
275 _errored()
276
277
278func saveDatapackages():
279 # Save the AP datapackages to disk.
280 var file = FileAccess.open("user://ap_datapackages", FileAccess.WRITE)
281 file.store_var(_datapackages, true)
282 file.close()
283
284
285func connectToServer(server, un, pw):
286 ap_server = server
287 ap_user = un
288 ap_pass = pw
289
290 _initiated_disconnect = false
291
292 var url = ""
293 if ap_server.begins_with("ws://") or ap_server.begins_with("wss://"):
294 url = ap_server
295 _try_wss = false
296 elif _try_wss:
297 url = "wss://" + ap_server
298 _try_wss = false
299 else:
300 url = "ws://" + ap_server
301 _try_wss = true
302
303 var err = _ws.connect_to_url(url)
304 if err != OK:
305 emit_signal(
306 "could_not_connect",
307 (
308 "Could not connect to Archipelago. Check that your server and port are correct. See the error log for more information. Error code: %d."
309 % err
310 )
311 )
312 global._print("Could not connect to AP: " + err)
313 return
314 _should_process = true
315
316 emit_signal("connect_status", "Connecting...")
317
318
319func sendMessage(msg):
320 var payload = JSON.stringify(msg)
321 _ws.send_text(payload)
322
323
324func requestDatapackages(games):
325 emit_signal("connect_status", "Downloading %s data package..." % games[0])
326
327 sendMessage([{"cmd": "GetDataPackage", "games": games}])
328
329
330func processDatapackages():
331 _item_id_to_name = {}
332 _location_id_to_name = {}
333 for game in _datapackages.keys():
334 var package = _datapackages[game]
335
336 _item_id_to_name[game] = {}
337 for item_name in package["item_name_to_id"].keys():
338 _item_id_to_name[game][int(package["item_name_to_id"][item_name])] = item_name
339
340 _location_id_to_name[game] = {}
341 for location_name in package["location_name_to_id"].keys():
342 _location_id_to_name[game][int(package["location_name_to_id"][location_name])] = location_name
343
344 if _datapackages.has("Lingo 2"):
345 _item_name_to_id = _datapackages["Lingo 2"]["item_name_to_id"]
346 _location_name_to_id = _datapackages["Lingo 2"]["location_name_to_id"]
347
348
349func connectToRoom():
350 emit_signal("connect_status", "Authenticating...")
351
352 sendMessage(
353 [
354 {
355 "cmd": "Connect",
356 "password": ap_pass,
357 "game": "Lingo 2",
358 "name": ap_user,
359 "uuid": SCRIPT_uuid.v4(),
360 "version": ap_version,
361 "items_handling": 0b111, # always receive our items
362 "tags": [],
363 "slot_data": true
364 }
365 ]
366 )
367
368
369func sendConnectUpdate(tags):
370 sendMessage([{"cmd": "ConnectUpdate", "tags": tags}])
371
372
373func requestSync():
374 sendMessage([{"cmd": "Sync"}])
375
376
377func sendLocation(loc_id):
378 sendMessage([{"cmd": "LocationChecks", "locations": [loc_id]}])
379
380
381func sendLocations(loc_ids):
382 sendMessage([{"cmd": "LocationChecks", "locations": loc_ids}])
383
384
385func setValue(key, value, operation = "replace"):
386 sendMessage(
387 [
388 {
389 "cmd": "Set",
390 "key": "Lingo2_%d_%s" % [_slot, key],
391 "want_reply": false,
392 "operations": [{"operation": operation, "value": value}]
393 }
394 ]
395 )
396
397
398func say(textdata):
399 sendMessage([{"cmd": "Say", "text": textdata}])
400
401
402func completedGoal():
403 sendMessage([{"cmd": "StatusUpdate", "status": 30}]) # CLIENT_GOAL
404
405
406func scoutLocations(loc_ids):
407 sendMessage([{"cmd": "LocationScouts", "locations": loc_ids}])
408
409
410func hasItem(item_id):
411 return _received_items.has(item_id)
412
413
414func getItemAmount(item_id):
415 return _received_items.get(item_id, 0)