summary refs log tree commit diff stats
path: root/racing/lobby.gd
diff options
context:
space:
mode:
authorStar Rauchenberger <fefferburbia@gmail.com>2024-02-08 16:29:48 -0500
committerStar Rauchenberger <fefferburbia@gmail.com>2024-02-08 16:29:48 -0500
commit60a0a573f0d0329e8d5c73878165ba80fa2d2628 (patch)
treebe2c9519c47ca3aa28f539587aae4ef9274b9e2d /racing/lobby.gd
downloadlingo-race-60a0a573f0d0329e8d5c73878165ba80fa2d2628.tar.gz
lingo-race-60a0a573f0d0329e8d5c73878165ba80fa2d2628.tar.bz2
lingo-race-60a0a573f0d0329e8d5c73878165ba80fa2d2628.zip
Initial commit
Diffstat (limited to 'racing/lobby.gd')
-rw-r--r--racing/lobby.gd407
1 files changed, 407 insertions, 0 deletions
diff --git a/racing/lobby.gd b/racing/lobby.gd new file mode 100644 index 0000000..87647bb --- /dev/null +++ b/racing/lobby.gd
@@ -0,0 +1,407 @@
1extends Node
2
3var player_steam_id
4var active_lobby_id = 0
5var active_lobby_members = []
6var next_message_id = 0
7var messages_needing_ack = {}
8var is_vip = false
9var is_ready = false
10var everyone_ready = false
11var members_to_join = []
12var is_starting = false
13
14const MAX_PLAYERS = 250
15const PROTOCOL_VERSION = 2
16const RECIPIENT_BROADCAST_ALL = -1
17const LOBBY_MAP_NAME = "ll1_racing"
18
19const LL1_AREAS = [
20 ["Starting Room", 0, 0, 0],
21 ["Second Room", 0, 0, -15],
22 ["The Traveled", 34, 0, -18],
23 ["The Agreeable", 30, 0, -45],
24 ["The Colorful", 10, 0, -83],
25 ["Suits Area", 0, 0, -78, true],
26 ["Arrow Garden", -93, 1, -93],
27 ["The Wondrous (Table)", -108, 1, -78],
28 ["Courtyard", -64, 0, -71],
29 ["Yellow Backside Nine", -38, 0, -58],
30 ["Hot Crusts Area", -20, 0, -81],
31 ["Crossroads Corner", -28, 0, -54],
32 ["The Discerning", -54, 0, -34, true],
33 ["Green Backside", 22, 0, -94],
34 ["Observant Upstairs", 40, 9, -92, true],
35 ["Eight Room", 95, 15, -28],
36 ["The Perceptive", 60, 9, -57],
37 ["The Tenacious", 0, 0, -43],
38 ["Rainbow", -96, 0, -41],
39 ["The Undeterred", -87, 0, 25, true],
40 ["Directional Gallery", -57, 0, 0],
41 ["The Eyes They See", -54, 0, -23],
42 ["Tower First Floor", -27, 0, -23],
43 ["The Optimistic", 76, 0, -17],
44 ["The Initiated", 63, 0, -0, true],
45 ["Art Gallery", 92, 0, 15],
46 ["Art Gallery Top", 80, 30, 15],
47 ["Lookout", 75, 18, 51],
48 ["Knight Night Room", 37, 0, 7],
49 ["The Seeker", 9, 0, 16, true],
50 ["Hidden Room", 13, 0, 4],
51 ["Owl Hallway", 44, 0, -26],
52 ["Challenge Room", -9, 6, 13],
53 ["Pilgrim Room", -22, 0, 24, true],
54 ["Cellar Replica", -44, 0, 30],
55 ["Elements Area", -61, 0, 40],
56 ["The Artistic", -25, 0, 54, true],
57 ["Outside The Wise", -44, 0, 71],
58 ["The Wise", -72, 0, 72, true],
59 ["The Scientific", -18, 0, 89],
60 ["The Wanderer", 0, 0, 80],
61 ["The Fearless", 18, 10, 90],
62 ["Champion's Rest", 23, 0, 62, true],
63 ["The Steady", 31, 0, 77, true],
64 ["The Bold", 67, 0, 77, true],
65 ["Color Hunt", 45, 0, 69],
66 ["Room Room", 95, 6, 84],
67 ["The Bearer", 61, 0, 51],
68 ["Tower Third Floor", 18, 0, 33],
69 ["Rhyme Room (Cross)", 0, 9, 42],
70 ["Tower Seventh Floor", 0, 37, 64],
71]
72
73
74func _ready():
75 installScriptExtension(ResourceLoader.load("user://maps/racing/load.gd"))
76
77 # P2P solve messages should still be received while paused.
78 set_pause_mode(Node.PAUSE_MODE_PROCESS)
79
80 # Undo the load screen removing our cursor
81 get_tree().get_root().set_disable_input(false)
82 Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
83
84 if global.get_node_or_null("RaceManager") == null:
85 var race_manager_script = load("user://maps/racing/manager.gd")
86 var race_manager = race_manager_script.new()
87 race_manager.name = "RaceManager"
88
89 race_manager.SCRIPT_multiplayer = load("user://maps/racing/multiplayer.gd")
90
91 global.add_child(race_manager)
92
93 var _ignore = get_node("Panel/main_button").connect("pressed", self, "_main_button_pressed")
94 _ignore = get_node("Panel/return_button").connect("pressed", self, "_return_button_pressed")
95
96 var dynamic_font = DynamicFont.new()
97 dynamic_font.font_data = load("res://fonts/Lingo.ttf")
98 dynamic_font.size = 36
99 dynamic_font.outline_color = Color(0, 0, 0, 1)
100 dynamic_font.outline_size = 2
101 get_node("Panel/ItemList").add_font_override("font", dynamic_font)
102
103 get_node("Panel/title").text = "LINGO RACING LOBBY (%s)" % global.save_file
104
105 player_steam_id = Steam.getSteamID()
106
107 _ignore = Steam.connect("lobby_match_list", self, "_on_lobby_match_list")
108 _ignore = Steam.connect("lobby_created", self, "_on_lobby_created")
109 _ignore = Steam.connect("lobby_joined", self, "_on_lobby_joined")
110 _ignore = Steam.connect("lobby_data_update", self, "_on_lobby_data_update")
111 _ignore = Steam.connect("persona_state_change", self, "_on_persona_state_change")
112 _ignore = Steam.connect("p2p_session_request", self, "_on_p2p_session_request")
113
114 _setup_recurring_task(5.0, "_resend_messages_needing_ack")
115 _setup_recurring_task(10.0, "_request_lobby_list")
116
117 _request_lobby_list()
118
119
120func _process(_delta: float) -> void:
121 _read_p2p_packet()
122
123
124func _exit_tree():
125 if active_lobby_id != 0 and !is_starting:
126 Steam.leaveLobby(active_lobby_id)
127 active_lobby_id = 0
128
129
130func _setup_recurring_task(wait_time, function):
131 var timer = Timer.new()
132 timer.set_wait_time(wait_time)
133 timer.set_one_shot(false)
134 timer.connect("timeout", self, function)
135 add_child(timer)
136 timer.start()
137
138
139func _resend_messages_needing_ack():
140 for message_id in messages_needing_ack:
141 var message = messages_needing_ack[message_id]
142 if message["recipient_id"] in active_lobby_members:
143 _send_p2p_packet(message["data"], message["recipient_id"], message["mode"], true)
144 else:
145 messages_needing_ack.erase(message_id)
146
147
148func _request_lobby_list():
149 Steam.addRequestLobbyListDistanceFilter(Steam.LOBBY_DISTANCE_FILTER_WORLDWIDE)
150 Steam.addRequestLobbyListNumericalFilter(
151 "protocol_version", PROTOCOL_VERSION, Steam.LOBBY_COMPARISON_EQUAL
152 )
153 Steam.addRequestLobbyListStringFilter("map", LOBBY_MAP_NAME, Steam.LOBBY_COMPARISON_EQUAL)
154 Steam.addRequestLobbyListStringFilter(
155 "save_file", global.save_file.to_lower(), Steam.LOBBY_COMPARISON_EQUAL
156 )
157 Steam.requestLobbyList()
158
159
160func _on_lobby_match_list(lobbies: Array) -> void:
161 if active_lobby_id != 0 && not active_lobby_id in lobbies:
162 # Not sure why this happens, but it seems to sometimes.
163 lobbies.append(active_lobby_id)
164 var best_lobby_id = 0
165 var best_lobby_size = -1
166 for lobby_id in lobbies:
167 var lobby_size = Steam.getNumLobbyMembers(lobby_id)
168 if (
169 lobby_size > best_lobby_size
170 || (lobby_size == best_lobby_size && lobby_id < best_lobby_id)
171 ):
172 best_lobby_id = lobby_id
173 best_lobby_size = lobby_size
174 if best_lobby_id == 0:
175 Steam.createLobby(Steam.LOBBY_TYPE_INVISIBLE, MAX_PLAYERS)
176 elif best_lobby_id != active_lobby_id:
177 Steam.joinLobby(best_lobby_id)
178 elif best_lobby_size <= 1:
179 _request_lobby_list()
180
181
182func _on_lobby_created(result: int, lobby_id: int) -> void:
183 if result != Steam.RESULT_OK:
184 return
185 var _ignore = Steam.setLobbyData(lobby_id, "map", LOBBY_MAP_NAME)
186 _ignore = Steam.setLobbyData(lobby_id, "protocol_version", str(PROTOCOL_VERSION))
187 _ignore = Steam.setLobbyData(lobby_id, "save_file", global.save_file.to_lower())
188 _ignore = Steam.setLobbyData(lobby_id, "racing_vip", str(player_steam_id))
189 is_vip = true
190 _request_lobby_list()
191
192
193func _on_lobby_joined(lobby_id: int, _permissions: int, _locked: bool, result: int) -> void:
194 if result != Steam.RESULT_OK:
195 return
196 if active_lobby_id != 0 && active_lobby_id != lobby_id:
197 Steam.leaveLobby(active_lobby_id)
198 active_lobby_id = lobby_id
199 if Steam.getLobbyData(lobby_id, "racing_vip") == str(player_steam_id):
200 is_vip = true
201 _update_lobby_members()
202
203
204func _on_lobby_data_update(_lobby_id: int, _member_id: int, _key: int) -> void:
205 _update_lobby_members()
206
207
208func _on_persona_state_change(_persona_id: int, _flag: int) -> void:
209 _update_lobby_members()
210
211
212func _update_lobby_members():
213 if active_lobby_id == 0:
214 return
215 var lobby_size: int = Steam.getNumLobbyMembers(active_lobby_id)
216 var itemlist = get_node("Panel/ItemList")
217 itemlist.clear()
218 active_lobby_members.clear()
219 var temp_everyone_ready = true
220 for i in range(0, lobby_size):
221 var member_id: int = Steam.getLobbyMemberByIndex(active_lobby_id, i)
222 var steam_name: String = Steam.getFriendPersonaName(member_id)
223 itemlist.add_item(steam_name, null, false)
224 active_lobby_members.append(member_id)
225
226 var mem_is_ready = Steam.getLobbyMemberData(active_lobby_id, member_id, "ready") == "true"
227 if mem_is_ready:
228 itemlist.set_item_custom_fg_color(itemlist.get_item_count() - 1, Color.green)
229 else:
230 temp_everyone_ready = false
231 everyone_ready = temp_everyone_ready
232 var main_button = get_node("Panel/main_button")
233 if everyone_ready and is_vip:
234 main_button.text = "START GAME"
235 main_button.disabled = false
236 elif is_ready and is_vip:
237 main_button.text = "START GAME"
238 main_button.disabled = true
239 elif is_ready:
240 main_button.text = "READY"
241 main_button.disabled = true
242 else:
243 main_button.text = "READY"
244 main_button.disabled = false
245
246
247func _on_p2p_session_request(remote_id: int) -> void:
248 if remote_id in active_lobby_members:
249 var _ignore = Steam.acceptP2PSessionWithUser(remote_id)
250
251
252func _read_p2p_packet() -> void:
253 var packet_size: int = Steam.getAvailableP2PPacketSize(0)
254 if packet_size > 0:
255 var packet: Dictionary = Steam.readP2PPacket(packet_size, 0)
256 var remote_id = packet["steam_id_remote"]
257 if remote_id in active_lobby_members:
258 var serialized: PoolByteArray = packet["data"]
259 var data: Dictionary = bytes2var(serialized, false)
260 if "message_id" in data:
261 _send_p2p_packet(
262 {
263 "ack": data["message_id"],
264 },
265 remote_id,
266 Steam.P2P_SEND_RELIABLE_WITH_BUFFERING,
267 false
268 )
269 if "start_x" in data:
270 var race_manager = global.get_node("RaceManager")
271 race_manager.start_pos = [
272 data["start_name"],
273 int(data["start_x"]),
274 int(data["start_y"]),
275 int(data["start_z"])
276 ]
277 race_manager.end_pos = [
278 data["end_name"], int(data["end_x"]), int(data["end_y"]), int(data["end_z"])
279 ]
280
281 is_starting = true
282 _start_game()
283 if "ack" in data:
284 messages_needing_ack.erase(data["ack"])
285
286 if is_starting:
287 members_to_join.erase(remote_id)
288
289 if members_to_join.empty():
290 _start_game()
291
292
293func _send_p2p_packet(data: Dictionary, recipient_id: int, mode: int, needs_ack: bool) -> void:
294 if recipient_id == RECIPIENT_BROADCAST_ALL:
295 for member_id in active_lobby_members:
296 _send_p2p_packet(data.duplicate(), member_id, mode, needs_ack)
297 return
298
299 if needs_ack:
300 var message_id
301 if "message_id" in data:
302 message_id = data["message_id"]
303 else:
304 message_id = next_message_id
305 next_message_id += 1
306 data["message_id"] = message_id
307 if not message_id in messages_needing_ack:
308 messages_needing_ack[message_id] = {
309 "data": data,
310 "recipient_id": recipient_id,
311 "mode": mode,
312 }
313 var serialized: PoolByteArray = []
314 serialized.append_array(var2bytes(data))
315 var _ignore = Steam.sendP2PPacket(recipient_id, serialized, mode)
316
317
318func _main_button_pressed():
319 if everyone_ready and is_vip:
320 get_node("Panel/main_button").disabled = true
321
322 var rng = RandomNumberGenerator.new()
323 rng.randomize()
324
325 var start_pos
326 var end_pos
327 var found = false
328 while !found:
329 var areas_dupe = LL1_AREAS.duplicate()
330 var i = rng.randi_range(0, areas_dupe.size() - 1)
331 start_pos = areas_dupe[i]
332 areas_dupe.remove(i)
333 i = rng.randi_range(0, areas_dupe.size() - 1)
334 end_pos = areas_dupe[i]
335
336 var start_vec = Vector3(start_pos[1], start_pos[2], start_pos[3])
337 var end_vec = Vector3(end_pos[1], end_pos[2], end_pos[3])
338 if start_vec.distance_to(end_vec) > 50 and not (start_pos.size() >= 5 and start_pos[4]):
339 found = true
340
341 members_to_join = active_lobby_members.duplicate()
342 members_to_join.erase(player_steam_id)
343 is_starting = true
344
345 var race_manager = global.get_node("RaceManager")
346 race_manager.start_pos = start_pos
347 race_manager.end_pos = end_pos
348
349 if active_lobby_members.size() == 1:
350 _start_game()
351 else:
352 _send_p2p_packet(
353 {
354 "start_name": start_pos[0],
355 "start_x": str(start_pos[1]),
356 "start_y": str(start_pos[2]),
357 "start_z": str(start_pos[3]),
358 "end_name": end_pos[0],
359 "end_x": str(end_pos[1]),
360 "end_y": str(end_pos[2]),
361 "end_z": str(end_pos[3]),
362 },
363 RECIPIENT_BROADCAST_ALL,
364 Steam.P2P_SEND_RELIABLE,
365 true
366 )
367 else:
368 Steam.setLobbyMemberData(active_lobby_id, "ready", "true")
369 is_ready = true
370 _update_lobby_members()
371
372
373func _return_button_pressed():
374 _exit_tree()
375 fader._fade_start("main_menu")
376
377
378func _start_game():
379 var race_manager = global.get_node("RaceManager")
380 race_manager.lobby_id = active_lobby_id
381
382 # Switch to LL1
383 Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
384 global.map = "level1"
385 global.entry_point = Vector3(
386 race_manager.start_pos[1], race_manager.start_pos[2] + 0.25, race_manager.start_pos[3]
387 )
388 global.entry_rotate = Vector3(0, 0, 0)
389 global.sets_entry_point = true
390 var _discard = get_tree().change_scene("res://scenes/load_screen.tscn")
391
392
393# Adapted from https://gitlab.com/Delta-V-Modding/Mods/-/blob/main/game/ModLoader.gd
394func installScriptExtension(childScript: Resource):
395 # Force Godot to compile the script now.
396 # We need to do this here to ensure that the inheritance chain is
397 # properly set up, and multiple mods can chain-extend the same
398 # class multiple times.
399 # This is also needed to make Godot instantiate the extended class
400 # when creating singletons.
401 # The actual instance is thrown away.
402 childScript.new()
403
404 var parentScript = childScript.get_base_script()
405 var parentScriptPath = parentScript.resource_path
406 global._print("ModLoader: Installing script extension over %s" % parentScriptPath)
407 childScript.take_over_path(parentScriptPath)