extends Node enum GameFocus {MENU, GAME, CHAT, AWAY} enum NetworkRole {NONE, CLIENT, SERVER, DEDICATED_SERVER, RELAY_SERVER} const NET_PORT = 12597 #const NET_SERVER = "liblast.unfa.xyz" const NET_SERVER = "localhost" var peer = ENetMultiplayerPeer.new() var role = NetworkRole.NONE: set(new_role): role = new_role print("Network Role changed to ", NetworkRole.keys()[new_role]) var player_scene = preload("res://Assets/Characters/Player.tscn") @onready var gui = $GUI @onready var hud = $HUD @onready var chat = hud.get_node("Chat") var local_player: Node = null class PlayerInfo: var name: String var team: int var color: Color var focus: GameFocus var health: int var score: int func _init():#name: String, team: int, color: Color): var player_name = "" for i in range(0, 4): player_name += ['a','b','c', 'd', 'e', 'f'][randi() % 5] self.name = player_name self.color = Color(randf(),randf(),randf()) self.team = 0 self.focus = 999 self.health = 100 self.score = 0 # else: # self.name = name # self.team = team # self.color = color # self.focus = 0 func serialize(): return { 'name': self.name, 'team': str(self.team), 'color': self.color.to_html(), 'focus': self.focus, 'health': self.health, 'score': self.score, } func set(name: String, team: int, color: Color, focus: int, health: int, score: int): self.name = name self.team = team self.color = color self.focus = focus self.health = health self.score = score func deserialize(info): self.name = info['name'] self.team = info['team'].to_int() self.color = Color.html(info['color']) self.focus = info['focus'] self.health = info['health'] self.score = info['score'] #func generate(): var uptime = 0 # seconds const respawn_delay = 5 # seconds var spawn_queue = {} var game_score_limit = 15 func _process(delta): uptime += delta $Label.text = "player_list: \n" for i in player_list.players.keys(): if player_list.players[i]: $Label.text += str(i) + " = " + str(player_list.get(i).serialize()) + "\n" else: $Label.text += str(i) + " = ???" # poll respawn queue $Label.text += "\n\nspawn_queue: \n" $Label.text += str(spawn_queue) for i in spawn_queue.keys(): if spawn_queue[i] <= uptime: var is_local = true if i == get_tree().multiplayer.get_network_unique_id() else false create_player(i, is_local, true) spawn_queue.erase(i) class PlayerList: var players = {} # # func store(pid, item): # items[pid] = item func erase(pid): players.erase(pid) func set(pid: int, info: PlayerInfo): # if info is PlayerInfo: players[pid] = info # else: # var new_info = PlayerInfo.deserialize(info) # players[pid] = new_info func get(pid: int) -> PlayerInfo: return players[pid] # func update(): # if get_tree().multiplayer.get_network_unique_id() == 1: # for i in players.keys: # rpc(&'player_list_update', i, players[i].serialize()) # else: # rpc_id(1, &'player_list_update', players[get_tree().multiplayer.get_network_unique_id()].serialize()) @onready var player_list = PlayerList.new() var focus = GameFocus.MENU : set(new_focus): if local_player != null: assert(local_player != null, "local_player is not null but it is null, WTF?!") match new_focus: 0: Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE) gui.show() if local_player: local_player.input_active = false 1: Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED) gui.hide() if local_player: local_player.input_active = true 2: if local_player: local_player.input_active = false 3: if local_player: local_player.input_active = true focus = new_focus #@remote set_info(info) -> void: # player_info = PlayerInfo.new(info['name'], info['team'].to_int(), Color(info['color'])) # print("set_info - rpc called from ", get_tree().get_rpc_sender_id()) #@master func update_info() -> void: # rpc(&'set_info', player_info.serialize()) #@master func generate_info() -> void: #rpc(&'set_info', PlayerInfo.new(player_name, 0, color).serialize() ) func _input(_event) -> void: if Input.is_action_just_pressed("ui_cancel"): if focus == GameFocus.GAME: focus = GameFocus.MENU elif focus == GameFocus.MENU: focus = GameFocus.GAME if Input.is_action_just_pressed("show_scoretab"): hud.scoretab(true) elif Input.is_action_just_released("show_scoretab"): hud.scoretab(false) #@remote func st(player_list): # self.player_list = player_list func game_over(): pass func check_game_win_condition(): for i in player_list.players.keys(): if player_list.players[i].score >= game_score_limit: chat.rpc(&'chat_notification', "Player " + player_list.players[i].name + " has won this round!") #rpc(&'game_over', player_list.players[i].name) game_over() func update_hud(): hud.get_node("ScoreRank").text = "SCORE: " + str(player_list.get( get_tree().multiplayer.get_network_unique_id() ).score) var score = player_list.get( get_tree().multiplayer.get_network_unique_id() ).score var scores = [] for i in player_list.players.values(): scores.append(i.score) scores.sort() scores.reverse() var rank = scores.find(score) + 1 scores.remove(scores.find(score)) scores.sort() scores.reverse() var lead = score - scores[0] #var rank = hud.get_node("ScoreRank").text = "SCORE: " + str(score) + "\nRANK: " if lead > 0: hud.get_node("ScoreRank").text += str(rank) + "\nLEAD: " + str(lead) else: hud.get_node("ScoreRank").text += str(rank) + "\nGAP: " + str(-lead) @rpc(any, reliable) func player_list_update(info, pid = get_tree().get_rpc_sender_id()): var new_info = PlayerInfo.new() new_info.deserialize(info) if player_list.players.has(pid): var old_name = player_list.get(pid).name if old_name != new_info.name: chat.chat_notification("Player [b]" + old_name + "[/b] changed name to [b]" + new_info.name + "[/b]") var player = $Players.get_node(str(pid)) if player: player.update_player(new_info) player_list.set(pid, new_info) # server relays other PID's data # update local HUD update_hud() func push_local_player_info(): # var id = get_tree().multiplayer.get_network_unique_id() rpc(&'player_list_update', player_list.get(id).serialize(), get_tree().multiplayer.get_network_unique_id()) @rpc(sync,auth,reliable) func destroy_player(pid: int): var player_node = $Players.get_node(str(pid)) assert(player_node != null, "Attempting to delete a player node that does not exist") player_node.name = str(player_node.name) + "_dead" # avoids name collision when instancing another player scene player_node.queue_free() spawn_queue[pid] = uptime + respawn_delay func create_player(pid: int, is_local:= false, respawn:= false) -> void: var new_player new_player = player_scene.instantiate() var spawnpoint = $Map/SpawnPoints.get_children()[randi() % len($Map/SpawnPoints.get_children())] new_player.name = str(pid) new_player.global_transform = spawnpoint.global_transform new_player.set_network_authority(pid) $Players.add_child(new_player) var new_info: PlayerInfo if not respawn: # first spawn new_info = PlayerInfo.new() # generate name, color etc else: # respawn new_info = player_list.players[pid] # reuse previous name, color etc new_info.health = 100 # give the respawned player full health focus = GameFocus.GAME # make sure the player has no menu displayed when they respawn player_list.set(pid, new_info) # superfluous and harmful code that messes up player data: # if get_tree().multiplayer.get_network_unique_id() != 1: # if we're not the server - update the server # rpc_id(1, &'player_list_update', new_info.serialize(), pid) # send local player info to the server # else: # if we're the server, update the clients # rpc(&'player_list_update', new_info.serialize(), pid) if is_local: local_player = new_player #$Players.get_node(str(id)) # local_player.get_node("Head/Camera").current = true $NetworkTesting/TextEdit.text = new_info.name $NetworkTesting/ColorPickerButton.color = new_info.color #local_player.rpc(&'set_color', new_info.color) # elif local_player: # if there is a local player, make sure we keep using it's camera # new_player.get_node("Head/Camera").current = false # local_player.get_node("Head/Camera").current = true #new_player.rpc(&'set_color', player_list.get(pid).color) func start_dedicated_server(): # start server without creating a local player role = NetworkRole.DEDICATED_SERVER peer.create_server(NET_PORT, 16) get_tree().network_peer = peer #create_player(1, true) func _on_Host_pressed(): # start server and create a local player role = NetworkRole.SERVER $NetworkTesting/Host.disabled = true $NetworkTesting/Connect.disabled = true peer.create_server(NET_PORT, 16) get_tree().multiplayer.network_peer = peer create_player(1, true) focus = GameFocus.GAME chat.chat_notification("Started server") func _on_Connect_pressed(): $NetworkTesting/Host.disabled = true $NetworkTesting/Connect.disabled = true peer.create_client(NET_SERVER, NET_PORT) get_tree().multiplayer.network_peer = peer func _player_connected(pid) -> void: print("player connected, id: ", pid) create_player(pid, false) if get_tree().multiplayer.get_network_unique_id() == 1: # if we're the server for i in player_list.players.keys(): # send the player_list to the new client #pass rpc_id(pid, &'player_list_update', player_list.get(i).serialize(), i) # send local player info to the server # if local_player: # local_player.rpc(&"update_info") # # if role in [NetworkRole.SERVER, NetworkRole.DEDICATED_SERVER]: # rpc_id(id, "player_list_update", player_list) func _player_disconnected(pid) -> void: print("player disconnected, id: ", pid) if get_tree().multiplayer.get_network_unique_id() == 1: # if we're the server, broadcast that a player left chat.rpc(&'chat_notification', "Player [b]" + player_list.get(pid).name + "[/b] left") func _connected_ok() -> void: print("connected to server") chat.chat_notification("Connected to server") var pid = get_tree().multiplayer.get_network_unique_id() create_player(pid, true) focus = GameFocus.GAME role = NetworkRole.CLIENT func _connected_fail() -> void: print("connection to server failed") chat.chat_notification("Connection failed") func _server_disconnected() -> void: print("server disconnected") role = NetworkRole.NONE chat.chat_notification("Server disconnected") func _ready() -> void: #peer.compression_mode = NetworkedMultiplayerENet.COMPRESS_ZSTD print("Commandline arguments: ", OS.get_cmdline_args()) get_tree().multiplayer.connect("network_peer_connected", self._player_connected) get_tree().multiplayer.connect("network_peer_disconnected", self._player_disconnected) get_tree().multiplayer.connect("connected_to_server", self._connected_ok) get_tree().multiplayer.connect("connection_failed", self._connected_fail) get_tree().multiplayer.connect("server_disconnected", self._server_disconnected) if OS.get_cmdline_args().has("dedicated_server"): start_dedicated_server() func _on_TextEdit_text_submitted(new_text): player_list.players[get_tree().multiplayer.get_network_unique_id()].name = new_text push_local_player_info() #chat_announcement("Player " + old_name + " is now known as " + new_name) func _on_ColorPickerButton_color_changed(color): player_list.players[get_tree().multiplayer.get_network_unique_id()].color = color push_local_player_info() #local_player.rpc(&'set_color', color)