freeftf/global/multiplayer.gd
Patrick 2dce012535 0.1.0
Added a basic test map, options menu, bug fixes, and much more.
2025-08-08 10:57:42 +02:00

170 lines
4.5 KiB
GDScript

class_name MultiplayerClass
extends Node
const PORT: int = 4516
var peer: ENetMultiplayerPeer = null
var players: Dictionary = {}
var fuseboxes: int = 0
var playerlist_label: Label = null
var entered_username: String = ""
## Host Specific
# Hosts server
func host_server() -> void:
peer = ENetMultiplayerPeer.new()
if peer.create_server(PORT) != OK:
peer = null
return
multiplayer.multiplayer_peer = peer
if multiplayer.peer_connected.connect(_on_player_joined) != OK:
print("Failed to connect signal")
if multiplayer.peer_disconnected.connect(_on_player_left) != OK:
print("Failed to connect signal")
if get_tree().change_scene_to_file("res://ui/lobby.tscn") != OK:
print("Failed to change to scene.")
_on_player_joined(multiplayer.get_unique_id())
player_username.rpc_id(1, Options.username)
# Runs when player joins
func _on_player_joined(id: int) -> void:
players[id] = {
"id": id,
"username": "Waiting...",
"loaded": false,
"hunter": false,
"captured": false,
"escaped": false,
}
send_playerlist()
# Runs when player leaves
func _on_player_left(id: int) -> void:
if !players.erase(id):
print("Player was never registered!")
send_playerlist()
# Receives the players username
@rpc("any_peer", "call_local", "reliable")
func player_username(username: String) -> void:
players[multiplayer.get_remote_sender_id()].username = username
send_playerlist()
# Prepares the label text for the playerlist on the join/host screen
func send_playerlist() -> void:
var playerlist: String = ""
for id: int in players:
playerlist += players[id].username
playerlist += "\n"
update_playerlist.rpc(playerlist)
@rpc("any_peer", "call_local", "reliable")
func request_playerlist() -> void:
if multiplayer.is_server():
send_playerlist()
# Switches the scene, and waits until everyone is fully loaded.
@rpc("authority", "call_local", "reliable")
func switch_scene(path: String) -> void:
playerlist_label = null
if multiplayer.is_server():
_scene_loaded.rpc_id(1)
while true:
var loaded: bool = true
for id: int in players:
if players[id].loaded == false:
loaded = false
break
if loaded:
break
await get_tree().create_timer(0.1).timeout
if get_tree().change_scene_to_file(path) != OK:
print("Failed to change to scene.")
else:
if get_tree().change_scene_to_file(path) != OK:
print("Failed to change to scene.")
_scene_loaded.rpc_id(1)
# Callback to switch_scene from the remote peer
@rpc("any_peer", "call_local", "reliable")
func _scene_loaded() -> void:
if multiplayer.is_server():
players[multiplayer.get_remote_sender_id()].loaded = true
func _process(_delta: float) -> void:
if multiplayer.multiplayer_peer and multiplayer.is_server() and !players.is_empty():
var lost: bool = true
for id: int in players:
if !(players[id].captured or players[id].escaped) and !players[id].hunter:
lost = false
if lost:
terminate_multiplayer.rpc()
## Client Specific
# Joins a server
func join_server(username: String = Options.username, ip_address: String = Options.ip_address) -> void:
if !username:
username = Options.username
if !ip_address:
ip_address = Options.ip_address
entered_username = username
peer = ENetMultiplayerPeer.new()
if peer.create_client(ip_address, PORT) != OK:
terminate_multiplayer()
return
multiplayer.multiplayer_peer = peer
if multiplayer.connected_to_server.connect(_on_server_joined) != OK:
print("Failed to connect signal")
if multiplayer.connection_failed.connect(_on_server_join_failed) != OK:
print("Failed to connect signal")
# After successful join
func _on_server_joined() -> void:
if get_tree().change_scene_to_file("res://ui/lobby.tscn") != OK:
print("Failed to change to scene.")
player_username.rpc_id(1, entered_username)
# After unsuccessful join
func _on_server_join_failed() -> void:
terminate_multiplayer()
## Shared
# Terminates all Multiplayer functionality.
@rpc("authority", "call_local", "reliable")
func terminate_multiplayer() -> void:
multiplayer.multiplayer_peer = null
players = {}
fuseboxes = 0
Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
if get_tree().change_scene_to_file("res://ui/main_menu.tscn") != OK:
print("Failed to change to scene.")
# Updates the player list
@rpc("authority", "call_local", "reliable")
func update_playerlist(list: String) -> void:
if playerlist_label != null:
playerlist_label.text = list
# Adds Fusebox
@rpc("authority", "call_local", "reliable")
func add_fusebox() -> void:
fuseboxes += 1
# Removes Fusebox
@rpc("authority", "call_local", "reliable")
func remove_fusebox() -> void:
fuseboxes -= 1