summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Game/Client.gd32
-rw-r--r--Game/Network.gd154
-rw-r--r--Game/Network.tscn13
-rw-r--r--Game/Player.gd33
-rw-r--r--Game/Player.tscn24
-rw-r--r--Stages/Stage.gd9
-rw-r--r--Towers/Tower.gd23
-rw-r--r--Towers/Tower.tscn2
-rw-r--r--UI/Lobby.gd4
-rw-r--r--UI/players_list.gd9
-rw-r--r--Units/Unit.gd17
-rw-r--r--project.godot2
12 files changed, 138 insertions, 184 deletions
diff --git a/Game/Client.gd b/Game/Client.gd
index dbaf69e..dc18765 100644
--- a/Game/Client.gd
+++ b/Game/Client.gd
@@ -9,17 +9,16 @@ var state: State :
stage_state_changed.emit(value)
var stage: Stage
-var player: Player = Player.new()
-
-func _ready():
- #player = Player.new()
- player.id = multiplayer.get_unique_id()
+var _player: Player # workaround for MultiplayerSpawner
+var player: Player:
+ get():
+ if _player: return _player
+ else: return Network.get_player(multiplayer.get_unique_id())
-func update_player():
- Network.players[multiplayer.get_unique_id()] = player
- Network.update_player.rpc(Network.to_rpc_object(player))
+func _ready():
+ _player = preload("res://Game/Player.tscn").instantiate()
func initialize_stage(current_stage: Stage):
@@ -37,25 +36,20 @@ func place_tower(tower: Tower, position: Vector2):
#Network.place_tower.rpc(Network.to_rpc_object(tower), position)
player.towers[position] = tower
- player.score += 1
- update_player()
+ #player.score += 1
func remove_tower(tower: Tower):
if tower.owner_id == multiplayer.get_unique_id():
destroy_tower(tower)
- player.score -= 1
- update_player()
+ #player.score -= 1
func destroy_tower(tower: Tower):
- if multiplayer.is_server():
- stage.destroy_tower(tower)
- else:
- Network.destroy_tower.rpc_id(1, tower.global_position)
+ stage.destroy_tower(tower)
+ Network.destroy_tower.rpc(tower.global_position)
player.towers.erase(tower.global_position)
- update_player()
func select_tower(tower: Tower):
@@ -69,8 +63,8 @@ func deselect_tower():
tower.is_selected = false
-func update_tower(tower: Tower, data: Dictionary):
- Network.update_tower.rpc(tower.get_path(), data)
+func update_tower(path: NodePath, data: Tower.NetworkData):
+ Network.update_tower.rpc(path, inst_to_dict(data))
func spawn_unit(unit: Unit, spawn: Spawn):
diff --git a/Game/Network.gd b/Game/Network.gd
index 48558ce..5f8be5c 100644
--- a/Game/Network.gd
+++ b/Game/Network.gd
@@ -19,140 +19,66 @@ func host_game(port):
peer.create_server(int(port))
multiplayer.multiplayer_peer = peer
- players[1] = Client.player
+
+ Client.player.id = multiplayer.get_unique_id()
+ %Players.add_child(Client.player)
func join_game(ip, port):
var peer = ENetMultiplayerPeer.new()
peer.create_client(ip, int(port))
multiplayer.multiplayer_peer = peer
+
+ Client.player.id = multiplayer.get_unique_id()
func _on_connected_to_server():
- print("connected to server")
+ print(multiplayer.get_unique_id(), ": connected to server")
func _on_peer_connected(id):
- print("peer connected: ", id)
- add_to_players.rpc(to_rpc_object(Client.player))
+ print(multiplayer.get_unique_id(), ": peer connected: ", id)
+
+ if id == 1: # tell host to add player
+ add_to_players.rpc_id(1, inst_to_dict(Client.player))
+ Client._player = null
# TODO: add existing towers to new peers
func _on_peer_disconnected(id):
- print("peer disconnected: ", id)
+ print(multiplayer.get_unique_id(), ": peer disconnected: ", id)
# TODO: move towers owned by peer to host
- if id == 1:
+ if id == 1: # if host disconnected go back to Lobby
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):
+func get_player(id: int) -> Player:
+ return %Players.get_node(str(id))
+
+func get_ordered_player_ids(): # TODO: return type needed for players_list.gd "find" method?
+ var keys = %Players.get_children().map(func(item: Player):
+ return item.id
+ )
+ keys = keys.filter(func(item: int): # workaround for MultiplayerSpawner + Synchronizer
+ return item != 0
+ )
+ keys.sort_custom(func(a: int, b: int):
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):
+@rpc("any_peer")
+func add_to_players(remote_data: Dictionary):
var id = multiplayer.get_remote_sender_id()
- var player = merge_with_rpc_object(players[id], remote_player)
+
+ var player = preload("res://Game/Player.tscn").instantiate()
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)
+ player.username = remote_data.username
- 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 }
+ %Players.add_child(player)
+
+ players_changed.emit()
@rpc("any_peer")
@@ -164,7 +90,7 @@ func place_tower(remote_data: Dictionary):
var tower = Tower.from_network_data(data)
tower.owner_id = remote_player_id
- players[remote_player_id].towers[tower.global_position] = tower
+ Network.get_player(remote_player_id).towers[tower.global_position] = tower
Client.stage.place_tower(tower, tower.global_position)
@@ -173,23 +99,19 @@ func place_tower(remote_data: Dictionary):
#func destroy_tower(remote_tower: Dictionary):
func destroy_tower(position: Vector2):
var owner_id = multiplayer.get_remote_sender_id()
- var player = players[owner_id] as Player
+ var player = get_player(owner_id)
var tower = player.towers.get(position)
Client.stage.destroy_tower(tower)
@rpc("any_peer")
-func update_tower(remote_tower_node_path, data):
+func update_tower(remote_tower_node_path: NodePath, remote_data: Dictionary):
+ var data: Tower.NetworkData = dict_to_inst(remote_data)
var tower: Tower = get_tree().current_scene.get_node_or_null(remote_tower_node_path)
+
if tower:
- if "components" in data:
- for c in tower.components.duplicate():
- tower.remove_component(c.name)
- for c in data.components:
- tower.add_component(
- load("res://Towers/Components/" + c + "Component.gd").new()
- )
+ tower.update_with_network_data(data)
@rpc("any_peer")
diff --git a/Game/Network.tscn b/Game/Network.tscn
new file mode 100644
index 0000000..5e7c131
--- /dev/null
+++ b/Game/Network.tscn
@@ -0,0 +1,13 @@
+[gd_scene load_steps=2 format=3 uid="uid://dknyh6isiv5w4"]
+
+[ext_resource type="Script" path="res://Game/Network.gd" id="1_p1jvi"]
+
+[node name="Network" type="Node"]
+script = ExtResource("1_p1jvi")
+
+[node name="Players" type="Node" parent="."]
+unique_name_in_owner = true
+
+[node name="MultiplayerSpawner" type="MultiplayerSpawner" parent="."]
+_spawnable_scenes = PackedStringArray("res://Game/Player.tscn")
+spawn_path = NodePath("../Players")
diff --git a/Game/Player.gd b/Game/Player.gd
index dce19db..57eb73b 100644
--- a/Game/Player.gd
+++ b/Game/Player.gd
@@ -1,22 +1,25 @@
class_name Player
-extends Resource
+extends Node
signal score_changed
-var id := 1
-var name := ""
+@export var username: String
-var towers: Dictionary :
- set(value):
- towers = value
-
-var score: int :
+@export var score: int :
set(value):
score = value
score_changed.emit()
+@export var id: int:
+ set(value):
+ id = value
+ name = str(value)
+
var units: Array[Unit]
+var towers: Dictionary :
+ set(value):
+ towers = value
func get_color():
@@ -30,9 +33,11 @@ func get_color():
return Color(rng.randf(), rng.randf(), rng.randf())
-func get_rpc_properties() -> Dictionary:
- return {
- "id": null,
- "name": null,
- "score": null,
- }
+func _on_multiplayer_synchronizer_synchronized() -> void:
+ Network.players_changed.emit()
+
+func _on_multiplayer_synchronizer_delta_synchronized() -> void:
+ Network.players_changed.emit()
+
+func _on_score_changed() -> void:
+ Network.players_changed.emit()
diff --git a/Game/Player.tscn b/Game/Player.tscn
new file mode 100644
index 0000000..38e1d48
--- /dev/null
+++ b/Game/Player.tscn
@@ -0,0 +1,24 @@
+[gd_scene load_steps=3 format=3 uid="uid://fvspuiqj0osm"]
+
+[ext_resource type="Script" path="res://Game/Player.gd" id="1_37njm"]
+
+[sub_resource type="SceneReplicationConfig" id="SceneReplicationConfig_6h1lm"]
+properties/0/path = NodePath(".:score")
+properties/0/spawn = true
+properties/0/replication_mode = 2
+properties/1/path = NodePath(".:username")
+properties/1/spawn = true
+properties/1/replication_mode = 2
+properties/2/path = NodePath(".:id")
+properties/2/spawn = true
+properties/2/replication_mode = 0
+
+[node name="Player" type="Node"]
+script = ExtResource("1_37njm")
+
+[node name="MultiplayerSynchronizer" type="MultiplayerSynchronizer" parent="."]
+replication_config = SubResource("SceneReplicationConfig_6h1lm")
+
+[connection signal="score_changed" from="." to="." method="_on_score_changed"]
+[connection signal="delta_synchronized" from="MultiplayerSynchronizer" to="." method="_on_multiplayer_synchronizer_delta_synchronized"]
+[connection signal="synchronized" from="MultiplayerSynchronizer" to="." method="_on_multiplayer_synchronizer_synchronized"]
diff --git a/Stages/Stage.gd b/Stages/Stage.gd
index 1f73a09..47f42a2 100644
--- a/Stages/Stage.gd
+++ b/Stages/Stage.gd
@@ -37,18 +37,23 @@ func _ready() -> void:
func place_tower(tower: Tower, position: Vector2):
- var player: Player = Network.players[tower.owner_id]
+ var player: Player = Network.get_player(tower.owner_id)
tower.get_node("Sprite2D").modulate = player.get_color()
tower.get_node("ComponentsAnchor").modulate = player.get_color()
tower.global_position = position
fill_tower_region(tower, true)
towers.add_child(tower, true)
+
+ player.score += 1
func destroy_tower(tower: Tower):
fill_tower_region(tower, false)
tower.queue_free()
+
+ var player: Player = Network.get_player(tower.owner_id)
+ player.score -= 1
func fill_tower_region(tower: Tower, solid = true):
@@ -75,7 +80,7 @@ func fill_tower_region(tower: Tower, solid = true):
@warning_ignore("shadowed_variable")
func spawn_unit(unit: Unit):#, _spawn: Spawn):
- var player: Player = Network.players[unit.owner_id]
+ var player: Player = Network.get_player(unit.owner_id)
unit.get_node("Sprite2D").modulate = player.get_color()
units.add_child(unit, true)
diff --git a/Towers/Tower.gd b/Towers/Tower.gd
index 4262af1..8e22c3e 100644
--- a/Towers/Tower.gd
+++ b/Towers/Tower.gd
@@ -225,10 +225,7 @@ func _on_h_box_container_gui_input(event: InputEvent) -> void:
if not found:
tower.add_component(preload("res://Towers/Components/FrostComponent.gd").new())
- var data = {"components": []}
- for c in tower.components:
- data["components"].append(c.name)
- Client.update_tower(tower, data)
+ Client.update_tower(tower.get_path(), tower.to_network_data())
func _on_h_box_container_2_gui_input(event: InputEvent) -> void:
@@ -247,24 +244,34 @@ func _on_h_box_container_2_gui_input(event: InputEvent) -> void:
if not found:
tower.add_component(preload("res://Towers/Components/BurnComponent.gd").new())
- var data = {"components": []}
- for c in tower.components:
- data["components"].append(c.name)
- Client.update_tower(tower, data)
+ Client.update_tower(tower.get_path(), tower.to_network_data())
class NetworkData extends Resource:
var name: String
var position: Vector2
+ var components: Array
func to_network_data() -> NetworkData:
var data = NetworkData.new()
data.name = name
data.position = global_position
+ data.components = components.map(func(item: TowerComponent):
+ return item.name
+ )
return data
+func update_with_network_data(data: NetworkData):
+ for component in components.duplicate():
+ remove_component(component.name)
+
+ for component_name in data.components:
+ add_component(
+ load("res://Towers/Components/" + component_name + "Component.gd").new()
+ )
+
static func from_network_data(data: NetworkData) -> Tower:
var tower: Tower = preload("res://Towers/Tower.tscn").instantiate()
diff --git a/Towers/Tower.tscn b/Towers/Tower.tscn
index 6bff2e3..43c3c35 100644
--- a/Towers/Tower.tscn
+++ b/Towers/Tower.tscn
@@ -115,8 +115,6 @@ text = "Burn"
replication_config = SubResource("SceneReplicationConfig_spp26")
[connection signal="input_event" from="." to="." method="_on_input_event"]
-[connection signal="mouse_entered" from="." to="." method="_on_mouse_entered"]
-[connection signal="mouse_exited" from="." to="." method="_on_mouse_exited"]
[connection signal="tree_exiting" from="." to="." method="_on_tree_exiting"]
[connection signal="body_entered" from="Range" to="." method="_on_range_body_entered"]
[connection signal="body_exited" from="Range" to="." method="_on_range_body_exited"]
diff --git a/UI/Lobby.gd b/UI/Lobby.gd
index 68d843c..ba76ecc 100644
--- a/UI/Lobby.gd
+++ b/UI/Lobby.gd
@@ -24,12 +24,12 @@ func get_port():
return port
func _on_host_pressed() -> void:
- Client.player.name = %Name.text
+ Client.player.username = %Name.text
Network.host_game(get_port())
get_tree().change_scene_to_file("res://Stages/world.tscn")
func _on_join_pressed() -> void:
- Client.player.name = %Name.text
+ Client.player.username = %Name.text
Network.join_game(get_ip(), get_port())
get_tree().change_scene_to_file("res://Stages/world.tscn")
diff --git a/UI/players_list.gd b/UI/players_list.gd
index 04bfe6b..6cfd26c 100644
--- a/UI/players_list.gd
+++ b/UI/players_list.gd
@@ -6,6 +6,7 @@ extends PanelContainer
func _ready() -> void:
#players_list_container.visible = false
+ Client.player.score_changed.connect(update_players)
Network.players_changed.connect(update_players)
update_players()
@@ -13,8 +14,8 @@ func _ready() -> void:
func update_players():
- for id in Network.players.keys():
- var player: Player = Network.players[id]
+ for id in Network.get_ordered_player_ids():
+ var player: Player = Network.get_player(id)
var control: Control = list.get_node_or_null(str(id))
if not control:
@@ -24,9 +25,9 @@ func update_players():
control.get_node("%Indicator").visible = id == multiplayer.get_unique_id()
list.add_child(control)
- control.get_node("%ID").text = str(player.name)
+ control.get_node("%ID").text = str(player.username)
control.get_node("%Score").text = str(player.score)
- list.move_child(control, Network.get_ordered_player_ids().find(id) + 1)
+ #list.move_child(control, Network.get_ordered_player_ids().find(id) + 1)
func remove_player(id):
diff --git a/Units/Unit.gd b/Units/Unit.gd
index 3b91d73..06cf346 100644
--- a/Units/Unit.gd
+++ b/Units/Unit.gd
@@ -68,6 +68,7 @@ func _ready():
if not target:
target = Client.stage.get_node("%Goal").path_position
+ line.name = "Line@" + name
Client.stage.units.add_child(line)
reset_path()
@@ -160,7 +161,6 @@ func _on_navigation_base_area_entered(area: Area2D):
if area.is_in_group("goal"):
Client.player.score += 1
- Client.update_player()
queue_free()
if area.is_in_group("path"):
var path_node: PathNode = area.get_parent()
@@ -279,24 +279,9 @@ func _on_tree_exiting() -> void:
is_selected = false
line.queue_free()
- #if get_multiplayer_authority() == multiplayer.get_unique_id():
Network.remove_unit.rpc(get_path())
-func get_rpc_properties():
- return {
- #"name": null,
- #"global_position": null,
- #"target": "res://Stages/Paths/PathNode.tscn",
- #"hp": null,
- #"speed": null,
- #"current_path": null,
- #"current_path_idx": null,
- #"sprite": "node://Sprite2D",
- #"owner_id": null,
- }
-
-
class NetworkData extends Resource:
var name: String
var position: Vector2
diff --git a/project.godot b/project.godot
index 7bad496..95486a1 100644
--- a/project.godot
+++ b/project.godot
@@ -17,7 +17,7 @@ config/icon="res://icon.svg"
[autoload]
-Network="*res://Game/Network.gd"
+Network="*res://Game/Network.tscn"
Client="*res://Game/Client.gd"
[display]