extends Node signal players_changed var players = {} func _ready(): multiplayer.connected_to_server.connect(_on_connected_to_server) multiplayer.peer_connected.connect(_on_peer_connected) multiplayer.peer_disconnected.connect(_on_peer_disconnected) multiplayer.allow_object_decoding = true func host_game(port): var peer = ENetMultiplayerPeer.new() peer.create_server(int(port)) multiplayer.multiplayer_peer = peer players[1] = Client.player func join_game(ip, port): var peer = ENetMultiplayerPeer.new() peer.create_client(ip, int(port)) multiplayer.multiplayer_peer = peer func _on_connected_to_server(): print("connected to server") func _on_peer_connected(id): print("peer connected: ", id) add_to_players.rpc(to_rpc_object(Client.player)) # TODO: add existing towers to new peers func _on_peer_disconnected(id): print("peer disconnected: ", id) # TODO: move towers owned by peer to host if id == 1: get_tree().change_scene_to_file("res://UI/Lobby.tscn") func get_ordered_player_ids(): var keys = players.keys() keys.sort_custom(func(a, b): return int(str(a).substr(0, 8)) < int(str(b).substr(0, 8)) ) return keys func to_rpc_object(object: Variant): var remote_object = {} var properties = object.get_rpc_properties() for property in properties: var property_class = properties[property] if object[property] is Dictionary: remote_object[property] = {} for key in object[property]: remote_object[property][key] = to_rpc_object(object[property][key]) elif property_class is String and property_class.begins_with("node://"): remote_object[property] = object[property] elif property_class: remote_object[property] = to_rpc_object(object[property]) else: remote_object[property] = object[property] return remote_object func from_rpc_object(remote_object: Dictionary, remote_class_path: String): var object var remote_class = load(remote_class_path) if remote_class is PackedScene: object = remote_class.instantiate() elif remote_class is GDScript: object = remote_class.new() else: assert(false, "unexpected remote class type") var properties = object.get_rpc_properties() for property in properties: var property_class = properties[property] if object[property] is Dictionary: for key in object[property]: object[property][key] = from_rpc_object(remote_object[property], property_class) elif property_class is String and property_class.begins_with("node://"): var node_path = property_class.substr(7) # after node:// object.remove_child(object.get_node(node_path)) remote_object[property].name = node_path.get_basename() object[property] = remote_object[property] object.add_child(remote_object[property]) elif property_class: object[property] = from_rpc_object(remote_object[property], property_class) else: object[property] = remote_object[property] return object func merge_with_rpc_object(object: Variant, remote_object: Dictionary): var properties = object.get_rpc_properties() for property in properties: var property_class = properties[property] if object[property] is Dictionary: object[property] = {} for key in remote_object[property]: object[property][key] = from_rpc_object(remote_object[property][key], property_class) elif property_class: object[property] = merge_with_rpc_object(object[property], remote_object[property]) else: object[property] = remote_object[property] return object @rpc("call_local", "any_peer") func add_to_players(remote_player: Dictionary): var id = multiplayer.get_remote_sender_id() var player = from_rpc_object(remote_player, "res://Game/Player.gd") player.id = id players[id] = player players_changed.emit() @rpc("call_local", "any_peer") func update_player(remote_player: Dictionary): var id = multiplayer.get_remote_sender_id() var player = merge_with_rpc_object(players[id], remote_player) player.id = id players[id] = player players_changed.emit() @rpc("any_peer", "reliable") func update_node(node_path: NodePath, properties: Dictionary): var root_node = get_tree().root.get_node(node_path) for property in properties: if property.contains(":"): var child_node = get_nested_node_and_property(root_node, property) child_node.node[child_node.property] = properties[property] else: root_node[property] = properties[property] func get_nested_node_and_property(node: Node, property: String) -> Dictionary: if property.contains(":"): var nested_node_path = property.substr(0, property.find(":")) var nested_property = property.substr(property.find(":") + 1) return get_nested_node_and_property(node.get_node(nested_node_path), nested_property) return { "node": node, "property": property } @rpc("any_peer") func place_tower(remote_tower: Dictionary, position: Vector2): var remote_player_id = multiplayer.get_remote_sender_id() var tower = from_rpc_object(remote_tower, "res://Towers/Tower.tscn") tower.owner_id = remote_player_id players[remote_player_id].towers[tower.global_position] = tower Client.stage.place_tower(tower, position) @rpc("any_peer") func destroy_tower(remote_tower: Dictionary): var player = players[remote_tower.owner_id] as Player var tower = player.towers.get(remote_tower.global_position) Client.stage.destroy_tower(tower) @rpc("any_peer") func spawn_unit(remote_unit: Dictionary, remote_spawn: Dictionary): var unit = from_rpc_object(remote_unit, "res://Units/Unit.tscn") var spawn = from_rpc_object(remote_spawn, "res://Stages/Paths/Spawn.tscn") var remote_id = multiplayer.get_remote_sender_id() unit.owner_id = remote_id unit.set_multiplayer_authority(remote_id) Client.stage.spawn_unit(unit, spawn) @rpc("any_peer") func remove_unit(remote_unit_node_path): var unit = get_tree().current_scene.get_node_or_null(remote_unit_node_path) if unit: unit.queue_free() @rpc("any_peer") func update_unit(remote_unit_node_path, data): var unit: Unit = get_tree().current_scene.get_node_or_null(remote_unit_node_path) if unit: if "position" in data: unit.position = data.position unit.hp = data.hp if "sprite" in data: unit.get_node("Sprite2D").self_modulate = data.sprite.self_modulate