Browse Source

Merge pull request #717 from Faless/mp/4.x_bomber

Port Multiplayer bomber to replication system.
Aaron Franke 3 years ago
parent
commit
a0a5b0e7b0

+ 6 - 5
networking/multiplayer_bomber/bomb.gd

@@ -5,17 +5,18 @@ var from_player
 
 # Called from the animation.
 func explode():
-	if not is_network_master():
-		# Explode only on master.
+	if not is_multiplayer_authority():
+		# Explode only on authority.
 		return
 	for p in in_area:
 		if p.has_method("exploded"):
-			# Exploded has a master keyword, so it will only be received by the master.
-			p.rpc("exploded", from_player)
+			# Exploded can only be called by the authority, but will also be called locally.
+			p.rpc(&"exploded", from_player)
 
 
 func done():
-	queue_free()
+	if is_multiplayer_authority():
+		queue_free()
 
 
 func _on_bomb_body_enter(body):

+ 43 - 46
networking/multiplayer_bomber/bomb.tscn

@@ -1,27 +1,36 @@
-[gd_scene load_steps=8 format=2]
+[gd_scene load_steps=10 format=3 uid="uid://enwoaqi0rnei"]
 
-[ext_resource path="res://bomb.gd" type="Script" id=1]
-[ext_resource path="res://brickfloor.png" type="Texture2D" id=2]
-[ext_resource path="res://explosion.png" type="Texture2D" id=3]
+[ext_resource type="Script" path="res://bomb.gd" id="1"]
+[ext_resource type="Texture2D" uid="uid://bdomqql6y50po" path="res://brickfloor.png" id="2"]
+[ext_resource type="Texture2D" uid="uid://drfbkdqmj0gu2" path="res://explosion.png" id="3"]
 
-[sub_resource type="RectangleShape2D" id=1]
-extents = Vector2(8, 96)
+[sub_resource type="RectangleShape2D" id="1"]
+size = Vector2(16, 192)
 
-[sub_resource type="RectangleShape2D" id=2]
-extents = Vector2(96, 8)
+[sub_resource type="RectangleShape2D" id="2"]
+size = Vector2(192, 16)
 
-[sub_resource type="Curve" id=3]
+[sub_resource type="Curve" id="3"]
 max_value = 2.0
 _data = [Vector2(0.00150494, 0.398437), 0.0, 0.0, 0, 0, Vector2(0.0152287, 1.42969), 0.0, 0.0, 0, 0, Vector2(0.478607, 1.30078), 0.0, 0.0, 0, 0, Vector2(1, 0.291016), 0.0, 0.0, 0, 0]
 
-[sub_resource type="Animation" id=4]
+[sub_resource type="CurveTexture" id="4"]
+curve = SubResource( "3" )
+
+[sub_resource type="ParticlesMaterial" id="5"]
+emission_shape = 2
+emission_sphere_radius = 1.0
+gravity = Vector3(0, 0, 0)
+scale_curve = SubResource( "4" )
+
+[sub_resource type="Animation" id="6"]
 length = 4.0
 tracks/0/type = "value"
-tracks/0/path = NodePath("Sprite2D:self_modulate")
-tracks/0/interp = 1
-tracks/0/loop_wrap = true
 tracks/0/imported = false
 tracks/0/enabled = true
+tracks/0/path = NodePath("Sprite:self_modulate")
+tracks/0/interp = 1
+tracks/0/loop_wrap = true
 tracks/0/keys = {
 "times": PackedFloat32Array(0, 0.4, 0.6, 0.8, 1.1, 1.3, 1.5, 1.8, 1.9, 2, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, 2.8, 3),
 "transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1),
@@ -29,28 +38,28 @@ tracks/0/keys = {
 "values": [Color(1, 1, 1, 1), Color(1, 1, 1, 1), Color(8, 8, 8, 1), Color(1, 1, 1, 1), Color(1, 1, 1, 1), Color(8, 8, 8, 1), Color(1, 1, 1, 1), Color(1, 1, 1, 1), Color(8, 8, 8, 1), Color(1, 1, 1, 1), Color(1, 1, 1, 1), Color(8, 8, 8, 1), Color(1, 1, 1, 1), Color(8, 8, 8, 1), Color(1, 1, 1, 1), Color(8, 8, 8, 1), Color(1, 1, 1, 1), Color(1, 1, 1, 0)]
 }
 tracks/1/type = "method"
+tracks/1/imported = false
+tracks/1/enabled = true
 tracks/1/path = NodePath(".")
 tracks/1/interp = 1
 tracks/1/loop_wrap = true
-tracks/1/imported = false
-tracks/1/enabled = true
 tracks/1/keys = {
 "times": PackedFloat32Array(2.8, 3.4),
 "transitions": PackedFloat32Array(1, 1),
 "values": [{
 "args": [],
-"method": "explode"
+"method": &"explode"
 }, {
 "args": [],
-"method": "done"
+"method": &"done"
 }]
 }
 tracks/2/type = "value"
+tracks/2/imported = false
+tracks/2/enabled = true
 tracks/2/path = NodePath("Explosion1:emitting")
 tracks/2/interp = 1
 tracks/2/loop_wrap = true
-tracks/2/imported = false
-tracks/2/enabled = true
 tracks/2/keys = {
 "times": PackedFloat32Array(0, 2.8),
 "transitions": PackedFloat32Array(1, 1),
@@ -58,11 +67,11 @@ tracks/2/keys = {
 "values": [false, true]
 }
 tracks/3/type = "value"
+tracks/3/imported = false
+tracks/3/enabled = true
 tracks/3/path = NodePath("Explosion2:emitting")
 tracks/3/interp = 1
 tracks/3/loop_wrap = true
-tracks/3/imported = false
-tracks/3/enabled = true
 tracks/3/keys = {
 "times": PackedFloat32Array(0, 2.8),
 "transitions": PackedFloat32Array(1, 1),
@@ -71,52 +80,40 @@ tracks/3/keys = {
 }
 
 [node name="Bomb" type="Area2D"]
-script = ExtResource( 1 )
+monitorable = false
+script = ExtResource( "1" )
 
-[node name="Sprite2D" type="Sprite2D" parent="."]
+[node name="Sprite" type="Sprite2D" parent="."]
 position = Vector2(-2.92606, -2.92606)
-texture = ExtResource( 2 )
+texture = ExtResource( "2" )
 region_enabled = true
 region_rect = Rect2(144, 0, 48, 48)
 
 [node name="Shape1" type="CollisionShape2D" parent="."]
-shape = SubResource( 1 )
+shape = SubResource( "1" )
 
 [node name="Shape2" type="CollisionShape2D" parent="."]
-shape = SubResource( 2 )
+shape = SubResource( "2" )
 
-[node name="Explosion1" type="CPUParticles2D" parent="."]
+[node name="Explosion1" type="GPUParticles2D" parent="."]
 emitting = false
 lifetime = 0.5
 one_shot = true
 explosiveness = 0.95
-texture = ExtResource( 3 )
-emission_shape = 2
-emission_rect_extents = Vector2(80, 1)
-gravity = Vector2(0, 0)
-initial_velocity = 1.0
-angular_velocity = 187.85
-angular_velocity_random = 1.0
-scale_amount_curve = SubResource( 3 )
+process_material = SubResource( "5" )
+texture = ExtResource( "3" )
 
-[node name="Explosion2" type="CPUParticles2D" parent="."]
+[node name="Explosion2" type="GPUParticles2D" parent="."]
 rotation = 1.57162
-scale = Vector2(1, 1)
 emitting = false
 one_shot = true
 explosiveness = 0.95
-texture = ExtResource( 3 )
-emission_shape = 2
-emission_rect_extents = Vector2(80, 1)
-gravity = Vector2(0, 0)
-initial_velocity = 1.0
-angular_velocity = 187.85
-angular_velocity_random = 1.0
-scale_amount_curve = SubResource( 3 )
+process_material = SubResource( "5" )
+texture = ExtResource( "3" )
 
 [node name="AnimationPlayer" type="AnimationPlayer" parent="."]
 autoplay = "anim"
-anims/anim = SubResource( 4 )
+anims/anim = SubResource( "6" )
 
 [connection signal="body_entered" from="." to="." method="_on_bomb_body_enter"]
 [connection signal="body_exited" from="." to="." method="_on_bomb_body_exit"]

+ 9 - 0
networking/multiplayer_bomber/bomb_spawner.gd

@@ -0,0 +1,9 @@
+extends MultiplayerSpawner
+
+func _spawn_custom(data):
+	if data.size() != 2 or typeof(data[0]) != TYPE_VECTOR2 or typeof(data[1]) != TYPE_INT:
+		return null
+	var bomb = preload("res://bomb.tscn").instantiate()
+	bomb.position = data[0]
+	bomb.from_player = data[1]
+	return bomb

+ 13 - 15
networking/multiplayer_bomber/brickfloor.png.import

@@ -1,8 +1,9 @@
 [remap]
 
 importer="texture"
-type="StreamTexture2D"
-path="res://.godot/imported/brickfloor.png-bab1cbace80ab627972eea565951db9e.stex"
+type="CompressedTexture2D"
+uid="uid://bdomqql6y50po"
+path="res://.godot/imported/brickfloor.png-bab1cbace80ab627972eea565951db9e.ctex"
 metadata={
 "vram_texture": false
 }
@@ -10,26 +11,23 @@ metadata={
 [deps]
 
 source_file="res://brickfloor.png"
-dest_files=["res://.godot/imported/brickfloor.png-bab1cbace80ab627972eea565951db9e.stex"]
+dest_files=["res://.godot/imported/brickfloor.png-bab1cbace80ab627972eea565951db9e.ctex"]
 
 [params]
 
 compress/mode=0
 compress/lossy_quality=0.7
-compress/hdr_mode=0
+compress/hdr_compression=1
 compress/bptc_ldr=0
 compress/normal_map=0
-flags/repeat=0
-flags/filter=true
-flags/mipmaps=false
-flags/anisotropic=false
-flags/srgb=2
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
 process/fix_alpha_border=true
 process/premult_alpha=false
-process/HDR_as_SRGB=false
-process/invert_color=false
 process/normal_map_invert_y=false
-stream=false
-size_limit=0
-detect_3d=true
-svg/scale=1.0
+process/hdr_as_srgb=false
+process/size_limit=0
+detect_3d/compress_to=1

+ 13 - 15
networking/multiplayer_bomber/charwalk.png.import

@@ -1,8 +1,9 @@
 [remap]
 
 importer="texture"
-type="StreamTexture2D"
-path="res://.godot/imported/charwalk.png-a9f067962a6454cc2f52a6e82832cbc5.stex"
+type="CompressedTexture2D"
+uid="uid://bsqovikudjr0q"
+path="res://.godot/imported/charwalk.png-a9f067962a6454cc2f52a6e82832cbc5.ctex"
 metadata={
 "vram_texture": false
 }
@@ -10,26 +11,23 @@ metadata={
 [deps]
 
 source_file="res://charwalk.png"
-dest_files=["res://.godot/imported/charwalk.png-a9f067962a6454cc2f52a6e82832cbc5.stex"]
+dest_files=["res://.godot/imported/charwalk.png-a9f067962a6454cc2f52a6e82832cbc5.ctex"]
 
 [params]
 
 compress/mode=0
 compress/lossy_quality=0.7
-compress/hdr_mode=0
+compress/hdr_compression=1
 compress/bptc_ldr=0
 compress/normal_map=0
-flags/repeat=0
-flags/filter=true
-flags/mipmaps=false
-flags/anisotropic=false
-flags/srgb=2
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
 process/fix_alpha_border=true
 process/premult_alpha=false
-process/HDR_as_SRGB=false
-process/invert_color=false
 process/normal_map_invert_y=false
-stream=false
-size_limit=0
-detect_3d=true
-svg/scale=1.0
+process/hdr_as_srgb=false
+process/size_limit=0
+detect_3d/compress_to=1

+ 13 - 15
networking/multiplayer_bomber/explosion.png.import

@@ -1,8 +1,9 @@
 [remap]
 
 importer="texture"
-type="StreamTexture2D"
-path="res://.godot/imported/explosion.png-730076d88b39dbfd5c22ad71f1135b01.stex"
+type="CompressedTexture2D"
+uid="uid://drfbkdqmj0gu2"
+path="res://.godot/imported/explosion.png-730076d88b39dbfd5c22ad71f1135b01.ctex"
 metadata={
 "vram_texture": false
 }
@@ -10,26 +11,23 @@ metadata={
 [deps]
 
 source_file="res://explosion.png"
-dest_files=["res://.godot/imported/explosion.png-730076d88b39dbfd5c22ad71f1135b01.stex"]
+dest_files=["res://.godot/imported/explosion.png-730076d88b39dbfd5c22ad71f1135b01.ctex"]
 
 [params]
 
 compress/mode=0
 compress/lossy_quality=0.7
-compress/hdr_mode=0
+compress/hdr_compression=1
 compress/bptc_ldr=0
 compress/normal_map=0
-flags/repeat=0
-flags/filter=true
-flags/mipmaps=false
-flags/anisotropic=false
-flags/srgb=2
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
 process/fix_alpha_border=true
 process/premult_alpha=false
-process/HDR_as_SRGB=false
-process/invert_color=false
 process/normal_map_invert_y=false
-stream=false
-size_limit=0
-detect_3d=true
-svg/scale=1.0
+process/hdr_as_srgb=false
+process/size_limit=0
+detect_3d/compress_to=1

+ 42 - 76
networking/multiplayer_bomber/gamestate.gd

@@ -27,14 +27,14 @@ signal game_error(what)
 # Callback from SceneTree.
 func _player_connected(id):
 	# Registration of a client beings here, tell the connected player that we are here.
-	rpc_id(id, "register_player", player_name)
+	rpc_id(id, StringName("register_player"), player_name)
 
 
 # Callback from SceneTree.
 func _player_disconnected(id):
-	if has_node("/root/World3D"): # Game is in progress.
-		if get_tree().is_network_server():
-			emit_signal("game_error", "Player " + players[id] + " disconnected")
+	if has_node("/root/World"): # Game is in progress.
+		if multiplayer.is_server():
+			game_error.emit("Player " + players[id] + " disconnected")
 			end_game()
 	else: # Game is not in progress.
 		# Unregister this player.
@@ -44,101 +44,60 @@ func _player_disconnected(id):
 # Callback from SceneTree, only for clients (not server).
 func _connected_ok():
 	# We just connected to a server
-	emit_signal("connection_succeeded")
+	connection_succeeded.emit()
 
 
 # Callback from SceneTree, only for clients (not server).
 func _server_disconnected():
-	emit_signal("game_error", "Server disconnected")
+	game_error.emit("Server disconnected")
 	end_game()
 
 
 # Callback from SceneTree, only for clients (not server).
 func _connected_fail():
-	get_tree().set_network_peer(null) # Remove peer
-	emit_signal("connection_failed")
+	multiplayer.set_network_peer(null) # Remove peer
+	connection_failed.emit()
 
 
 # Lobby management functions.
-
-remote func register_player(new_player_name):
-	var id = get_tree().get_rpc_sender_id()
-	print(id)
+@rpc(any_peer)
+func register_player(new_player_name):
+	var id = multiplayer.get_remote_sender_id()
 	players[id] = new_player_name
-	emit_signal("player_list_changed")
+	player_list_changed.emit()
 
 
 func unregister_player(id):
 	players.erase(id)
-	emit_signal("player_list_changed")
+	player_list_changed.emit()
 
 
-remote func pre_start_game(spawn_points):
+@rpc(call_local)
+func load_world():
 	# Change scene.
 	var world = load("res://world.tscn").instantiate()
 	get_tree().get_root().add_child(world)
-
-	get_tree().get_root().get_node(^"Lobby").hide()
-
-	var player_scene = load("res://player.tscn")
-
-	for p_id in spawn_points:
-		var spawn_pos = world.get_node(^"SpawnPoints/" + str(spawn_points[p_id])).position
-		var player = player_scene.instantiate()
-
-		player.set_name(str(p_id)) # Use unique ID as node name.
-		player.position=spawn_pos
-		player.set_network_master(p_id) #set unique id as master.
-
-		if p_id == get_tree().get_network_unique_id():
-			# If node for this peer id, set name.
-			player.set_player_name(player_name)
-		else:
-			# Otherwise set name from peer.
-			player.set_player_name(players[p_id])
-
-		world.get_node(^"Players").add_child(player)
+	get_tree().get_root().get_node("Lobby").hide()
 
 	# Set up score.
-	world.get_node(^"Score").add_player(get_tree().get_network_unique_id(), player_name)
+	world.get_node("Score").add_player(multiplayer.get_unique_id(), player_name)
 	for pn in players:
-		world.get_node(^"Score").add_player(pn, players[pn])
-
-	if not get_tree().is_network_server():
-		# Tell server we are ready to start.
-		rpc_id(1, "ready_to_start", get_tree().get_network_unique_id())
-	elif players.size() == 0:
-		post_start_game()
-
-
-remote func post_start_game():
+		world.get_node("Score").add_player(pn, players[pn])
 	get_tree().set_pause(false) # Unpause and unleash the game!
 
 
-remote func ready_to_start(id):
-	assert(get_tree().is_network_server())
-
-	if not id in players_ready:
-		players_ready.append(id)
-
-	if players_ready.size() == players.size():
-		for p in players:
-			rpc_id(p, "post_start_game")
-		post_start_game()
-
-
 func host_game(new_player_name):
 	player_name = new_player_name
-	peer = NetworkedMultiplayerENet.new()
+	peer = ENetMultiplayerPeer.new()
 	peer.create_server(DEFAULT_PORT, MAX_PEERS)
-	get_tree().set_network_peer(peer)
+	multiplayer.set_multiplayer_peer(peer)
 
 
 func join_game(ip, new_player_name):
 	player_name = new_player_name
-	peer = NetworkedMultiplayerENet.new()
+	peer = ENetMultiplayerPeer.new()
 	peer.create_client(ip, DEFAULT_PORT)
-	get_tree().set_network_peer(peer)
+	multiplayer.set_multiplayer_peer(peer)
 
 
 func get_player_list():
@@ -150,7 +109,11 @@ func get_player_name():
 
 
 func begin_game():
-	assert(get_tree().is_network_server())
+	assert(multiplayer.is_server())
+	load_world.rpc()
+
+	var world = get_tree().get_root().get_node("World")
+	var player_scene = load("res://player.tscn")
 
 	# Create a dictionary with peer id and respective spawn points, could be improved by randomizing.
 	var spawn_points = {}
@@ -159,25 +122,28 @@ func begin_game():
 	for p in players:
 		spawn_points[p] = spawn_point_idx
 		spawn_point_idx += 1
-	# Call to pre-start game with the spawn points.
-	for p in players:
-		rpc_id(p, "pre_start_game", spawn_points)
 
-	pre_start_game(spawn_points)
+	for p_id in spawn_points:
+		var spawn_pos = world.get_node("SpawnPoints/" + str(spawn_points[p_id])).position
+		var player = player_scene.instantiate()
+		player.synced_position = spawn_pos
+		player.name = str(p_id)
+		player.set_player_name(player_name if p_id == multiplayer.get_unique_id() else players[p_id])
+		world.get_node("Players").add_child(player)
 
 
 func end_game():
-	if has_node("/root/World3D"): # Game is in progress.
+	if has_node("/root/World"): # Game is in progress.
 		# End it
-		get_node(^"/root/World3D").queue_free()
+		get_node("/root/World").queue_free()
 
-	emit_signal("game_ended")
+	game_ended.emit()
 	players.clear()
 
 
 func _ready():
-	get_tree().connect(&"network_peer_connected", self._player_connected)
-	get_tree().connect("network_peer_disconnected", self,"_player_disconnected")
-	get_tree().connect(&"connected_to_server", self._connected_ok)
-	get_tree().connect(&"connection_failed", self._connected_fail)
-	get_tree().connect(&"server_disconnected", self._server_disconnected)
+	multiplayer.peer_connected.connect(_player_connected)
+	multiplayer.peer_disconnected.connect(_player_disconnected)
+	multiplayer.connected_to_server.connect(_connected_ok)
+	multiplayer.connection_failed.connect(_connected_fail)
+	multiplayer.server_disconnected.connect(_server_disconnected)

+ 13 - 15
networking/multiplayer_bomber/icon.png.import

@@ -1,8 +1,9 @@
 [remap]
 
 importer="texture"
-type="StreamTexture2D"
-path="res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.stex"
+type="CompressedTexture2D"
+uid="uid://cbby50724oj4c"
+path="res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.ctex"
 metadata={
 "vram_texture": false
 }
@@ -10,26 +11,23 @@ metadata={
 [deps]
 
 source_file="res://icon.png"
-dest_files=["res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.stex"]
+dest_files=["res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.ctex"]
 
 [params]
 
 compress/mode=0
 compress/lossy_quality=0.7
-compress/hdr_mode=0
+compress/hdr_compression=1
 compress/bptc_ldr=0
 compress/normal_map=0
-flags/repeat=0
-flags/filter=true
-flags/mipmaps=false
-flags/anisotropic=false
-flags/srgb=2
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
 process/fix_alpha_border=true
 process/premult_alpha=false
-process/HDR_as_SRGB=false
-process/invert_color=false
 process/normal_map_invert_y=false
-stream=false
-size_limit=0
-detect_3d=true
-svg/scale=1.0
+process/hdr_as_srgb=false
+process/size_limit=0
+detect_3d/compress_to=1

+ 7 - 7
networking/multiplayer_bomber/lobby.gd

@@ -2,11 +2,11 @@ extends Control
 
 func _ready():
 	# Called every time the node is added to the scene.
-	gamestate.connect(&"connection_failed", self._on_connection_failed)
-	gamestate.connect(&"connection_succeeded", self._on_connection_success)
-	gamestate.connect(&"player_list_changed", self.refresh_lobby)
-	gamestate.connect(&"game_ended", self._on_game_ended)
-	gamestate.connect(&"game_error", self._on_game_error)
+	gamestate.connection_failed.connect(_on_connection_failed)
+	gamestate.connection_succeeded.connect(_on_connection_success)
+	gamestate.player_list_changed.connect(refresh_lobby)
+	gamestate.game_ended.connect(_on_game_ended)
+	gamestate.game_error.connect(_on_game_error)
 	# Set the player name according to the system username. Fallback to the path.
 	if OS.has_environment("USERNAME"):
 		$Connect/Name.text = OS.get_environment("USERNAME")
@@ -68,7 +68,7 @@ func _on_game_ended():
 
 func _on_game_error(errtxt):
 	$ErrorDialog.dialog_text = errtxt
-	$ErrorDialog.popup_centered_clamped()
+	$ErrorDialog.popup_centered()
 	$Connect/Host.disabled = false
 	$Connect/Join.disabled = false
 
@@ -81,7 +81,7 @@ func refresh_lobby():
 	for p in players:
 		$Players/List.add_item(p)
 
-	$Players/Start.disabled = not get_tree().is_network_server()
+	$Players/Start.disabled = not multiplayer.is_server()
 
 
 func _on_start_pressed():

+ 6 - 14
networking/multiplayer_bomber/lobby.tscn

@@ -1,16 +1,13 @@
-[gd_scene load_steps=2 format=2]
+[gd_scene load_steps=2 format=3 uid="uid://jhdlqsokif5o"]
 
-[ext_resource path="res://lobby.gd" type="Script" id=1]
+[ext_resource type="Script" path="res://lobby.gd" id="1"]
 
 [node name="Lobby" type="Control"]
 anchor_right = 1.0
 anchor_bottom = 1.0
 size_flags_horizontal = 2
 size_flags_vertical = 2
-script = ExtResource( 1 )
-__meta__ = {
-"_edit_use_anchors_": false
-}
+script = ExtResource( "1" )
 
 [node name="Players" type="Panel" parent="."]
 visible = false
@@ -56,11 +53,10 @@ offset_left = -124.0
 offset_top = 375.0
 offset_right = 128.0
 offset_bottom = 429.0
-custom_constants/line_spacing = 6
+theme_override_constants/line_spacing = 6
 text = "If you want non-LAN clients to connect,
 make sure the port 10567 in UDP
 is forwarded on your router."
-align = 1
 
 [node name="FindPublicIP" type="LinkButton" parent="Players"]
 offset_left = 168.0
@@ -106,6 +102,7 @@ offset_right = 57.0
 offset_bottom = 80.0
 size_flags_horizontal = 2
 size_flags_vertical = 0
+theme_override_font_sizes/font_size = 16
 text = "IP:"
 
 [node name="IPAddress" type="LineEdit" parent="Connect"]
@@ -142,14 +139,9 @@ offset_right = 257.0
 offset_bottom = 139.0
 size_flags_horizontal = 2
 size_flags_vertical = 0
-custom_colors/font_color = Color(0.820312, 0.291595, 0.291595, 1)
-align = 1
+theme_override_colors/font_color = Color(0.820312, 0.291595, 0.291595, 1)
 
 [node name="ErrorDialog" type="AcceptDialog" parent="."]
-offset_right = 55.0
-offset_bottom = 58.0
-size_flags_horizontal = 2
-size_flags_vertical = 2
 
 [connection signal="pressed" from="Players/Start" to="." method="_on_start_pressed"]
 [connection signal="pressed" from="Players/FindPublicIP" to="." method="_on_find_public_ip_pressed"]

+ 33 - 0
networking/multiplayer_bomber/montserrat.otf.import

@@ -0,0 +1,33 @@
+[remap]
+
+importer="font_data_dynamic"
+type="FontData"
+uid="uid://dksjmw4cy83s4"
+path="res://.godot/imported/montserrat.otf-0d875bde8933edfaf65407b0d9da84e5.fontdata"
+
+[deps]
+
+source_file="res://montserrat.otf"
+dest_files=["res://.godot/imported/montserrat.otf-0d875bde8933edfaf65407b0d9da84e5.fontdata"]
+
+[params]
+
+antialiased=true
+multichannel_signed_distance_field=false
+msdf_pixel_range=8
+msdf_size=48
+force_autohinter=false
+hinting=1
+subpixel_positioning=1
+embolden=0.0
+transform=Transform2D(1, 0, 0, 1, 0, 0)
+oversampling=0.0
+compress=true
+opentype_feature_overrides={}
+preload/char_ranges=PackedStringArray()
+preload/glyph_ranges=PackedStringArray()
+preload/configurations=PackedStringArray()
+support_overrides/language_enabled=PackedStringArray()
+support_overrides/language_disabled=PackedStringArray()
+support_overrides/script_enabled=PackedStringArray()
+support_overrides/script_disabled=PackedStringArray()

+ 48 - 70
networking/multiplayer_bomber/player.gd

@@ -1,66 +1,58 @@
 extends CharacterBody2D
 
 const MOTION_SPEED = 90.0
+const BOMB_RATE = 0.5
 
-puppet var puppet_pos = Vector2()
-puppet var puppet_motion = Vector2()
+@export
+var synced_position := Vector2()
 
-@export var stunned = false
-
-# Use sync because it will be called everywhere
-remotesync func setup_bomb(bomb_name, pos, by_who):
-	var bomb = preload("res://bomb.tscn").instantiate()
-	bomb.set_name(bomb_name) # Ensure unique name for the bomb
-	bomb.position = pos
-	bomb.from_player = by_who
-	# No need to set network master to bomb, will be owned by server by default
-	get_node(^"../..").add_child(bomb)
+@export
+var stunned = false
 
+@onready
+var inputs = $Inputs
+var last_bomb_time = BOMB_RATE
 var current_anim = ""
-var prev_bombing = false
-var bomb_index = 0
-
-
-func _physics_process(_delta):
-	var motion = Vector2()
-
-	if is_network_master():
-		if Input.is_action_pressed(&"move_left"):
-			motion += Vector2(-1, 0)
-		if Input.is_action_pressed(&"move_right"):
-			motion += Vector2(1, 0)
-		if Input.is_action_pressed(&"move_up"):
-			motion += Vector2(0, -1)
-		if Input.is_action_pressed(&"move_down"):
-			motion += Vector2(0, 1)
-
-		var bombing = Input.is_action_pressed(&"set_bomb")
-
-		if stunned:
-			bombing = false
-			motion = Vector2()
 
-		if bombing and not prev_bombing:
-			var bomb_name = String(get_name()) + str(bomb_index)
-			var bomb_pos = position
-			rpc("setup_bomb", bomb_name, bomb_pos, get_tree().get_network_unique_id())
-
-		prev_bombing = bombing
-
-		rset("puppet_motion", motion)
-		rset("puppet_pos", position)
+func _ready():
+	stunned = false
+	position = synced_position
+	if str(name).is_valid_int():
+		get_node("Inputs/InputsSync").set_multiplayer_authority(str(name).to_int())
+
+
+func _physics_process(delta):
+	if multiplayer.multiplayer_peer == null or str(multiplayer.get_unique_id()) == str(name):
+		# The client which this player represent will update the controls state, and notify it to everyone.
+		inputs.update()
+
+	if multiplayer.multiplayer_peer == null or is_multiplayer_authority():
+		# The server updates the position that will be notified to the clients.
+		synced_position = position
+		# And increase the bomb cooldown spawning one if the client wants to.
+		last_bomb_time += delta
+		if not stunned and is_multiplayer_authority() and inputs.bombing and last_bomb_time >= BOMB_RATE:
+			last_bomb_time = 0.0
+			get_node("../../BombSpawner").spawn([position, str(name).to_int()])
 	else:
-		position = puppet_pos
-		motion = puppet_motion
+		# The client simply updates the position to the last known one.
+		position = synced_position
+
+	if not stunned:
+		# Everybody runs physics. I.e. clients tries to predict where they will be during the next frame.
+		velocity = inputs.motion * MOTION_SPEED
+		move_and_slide()
 
+	# Also update the animation based on the last known player input state
 	var new_anim = "standing"
-	if motion.y < 0:
+
+	if inputs.motion.y < 0:
 		new_anim = "walk_up"
-	elif motion.y > 0:
+	elif inputs.motion.y > 0:
 		new_anim = "walk_down"
-	elif motion.x < 0:
+	elif inputs.motion.x < 0:
 		new_anim = "walk_left"
-	elif motion.x > 0:
+	elif inputs.motion.x > 0:
 		new_anim = "walk_right"
 
 	if stunned:
@@ -68,30 +60,16 @@ func _physics_process(_delta):
 
 	if new_anim != current_anim:
 		current_anim = new_anim
-		get_node(^"anim").play(current_anim)
-
-	# TODO: This information should be set to the CharacterBody properties instead of arguments.
-	# FIXME: Use move_and_slide
-	move_and_slide(motion * MOTION_SPEED)
-	if not is_network_master():
-		puppet_pos = position # To avoid jitter
+		get_node("anim").play(current_anim)
 
 
-puppet func stun():
-	stunned = true
+func set_player_name(value):
+	get_node("label").text = value
 
 
-master func exploded(_by_who):
+@rpc(call_local)
+func exploded(_by_who):
 	if stunned:
 		return
-	rpc("stun") # Stun puppets
-	stun() # Stun master - could use sync to do both at once
-
-
-func set_player_name(new_name):
-	get_node(^"label").set_text(new_name)
-
-
-func _ready():
-	stunned = false
-	puppet_pos = position
+	stunned = true
+	get_node("anim").play("stunned")

+ 86 - 56
networking/multiplayer_bomber/player.tscn

@@ -1,22 +1,23 @@
-[gd_scene load_steps=12 format=2]
+[gd_scene load_steps=15 format=3 uid="uid://dviwgv2ty8v6u"]
 
-[ext_resource path="res://player.gd" type="Script" id=1]
-[ext_resource path="res://charwalk.png" type="Texture2D" id=2]
-[ext_resource path="res://montserrat.otf" type="FontData" id=3]
+[ext_resource type="Script" path="res://player.gd" id="1"]
+[ext_resource type="Texture2D" uid="uid://bsqovikudjr0q" path="res://charwalk.png" id="2"]
+[ext_resource type="FontData" uid="uid://dksjmw4cy83s4" path="res://montserrat.otf" id="3"]
+[ext_resource type="Script" path="res://player_controls.gd" id="4_k1vfr"]
 
-[sub_resource type="CircleShape2D" id=1]
+[sub_resource type="CircleShape2D" id="1"]
 radius = 20.0
 
-[sub_resource type="Animation" id=2]
+[sub_resource type="Animation" id="2"]
 resource_name = "standing"
 length = 0.8
-loop = true
+loop_mode = 1
 tracks/0/type = "value"
+tracks/0/imported = false
+tracks/0/enabled = true
 tracks/0/path = NodePath("sprite:frame")
 tracks/0/interp = 1
 tracks/0/loop_wrap = true
-tracks/0/imported = false
-tracks/0/enabled = true
 tracks/0/keys = {
 "times": PackedFloat32Array(0, 0.2, 0.4, 0.6),
 "transitions": PackedFloat32Array(1, 1, 1, 1),
@@ -24,15 +25,15 @@ tracks/0/keys = {
 "values": [0, 4, 8, 12]
 }
 
-[sub_resource type="Animation" id=3]
+[sub_resource type="Animation" id="3"]
 resource_name = "stunned"
 length = 1.2
 tracks/0/type = "value"
+tracks/0/imported = false
+tracks/0/enabled = true
 tracks/0/path = NodePath("sprite:frame")
 tracks/0/interp = 1
 tracks/0/loop_wrap = true
-tracks/0/imported = false
-tracks/0/enabled = true
 tracks/0/keys = {
 "times": PackedFloat32Array(0),
 "transitions": PackedFloat32Array(1),
@@ -40,39 +41,39 @@ tracks/0/keys = {
 "values": [0]
 }
 tracks/1/type = "value"
-tracks/1/path = NodePath("sprite:rotation_degrees")
-tracks/1/interp = 1
-tracks/1/loop_wrap = true
 tracks/1/imported = false
 tracks/1/enabled = true
+tracks/1/path = NodePath(".:stunned")
+tracks/1/interp = 1
+tracks/1/loop_wrap = true
 tracks/1/keys = {
-"times": PackedFloat32Array(0, 1, 1.1),
-"transitions": PackedFloat32Array(1, 0, 1),
-"update": 0,
-"values": [0.0, 720.0, 0.0]
+"times": PackedFloat32Array(1),
+"transitions": PackedFloat32Array(1),
+"update": 1,
+"values": [false]
 }
 tracks/2/type = "value"
-tracks/2/path = NodePath(".:stunned")
-tracks/2/interp = 1
-tracks/2/loop_wrap = true
 tracks/2/imported = false
 tracks/2/enabled = true
+tracks/2/path = NodePath("sprite:rotation")
+tracks/2/interp = 1
+tracks/2/loop_wrap = true
 tracks/2/keys = {
-"times": PackedFloat32Array(1),
-"transitions": PackedFloat32Array(1),
-"update": 1,
-"values": [false]
+"times": PackedFloat32Array(0, 1),
+"transitions": PackedFloat32Array(1, 1),
+"update": 0,
+"values": [-6.28319, 0.0]
 }
 
-[sub_resource type="Animation" id=4]
+[sub_resource type="Animation" id="4"]
 length = 0.8
-loop = true
+loop_mode = 1
 tracks/0/type = "value"
+tracks/0/imported = false
+tracks/0/enabled = true
 tracks/0/path = NodePath("sprite:frame")
 tracks/0/interp = 1
 tracks/0/loop_wrap = true
-tracks/0/imported = false
-tracks/0/enabled = true
 tracks/0/keys = {
 "times": PackedFloat32Array(0, 0.2, 0.4, 0.6),
 "transitions": PackedFloat32Array(1, 1, 1, 1),
@@ -80,16 +81,16 @@ tracks/0/keys = {
 "values": [0, 4, 8, 12]
 }
 
-[sub_resource type="Animation" id=5]
+[sub_resource type="Animation" id="5"]
 length = 0.8
-loop = true
+loop_mode = 1
 step = 0.2
 tracks/0/type = "value"
+tracks/0/imported = false
+tracks/0/enabled = true
 tracks/0/path = NodePath("sprite:frame")
 tracks/0/interp = 1
 tracks/0/loop_wrap = true
-tracks/0/imported = false
-tracks/0/enabled = true
 tracks/0/keys = {
 "times": PackedFloat32Array(0, 0.2, 0.4, 0.6),
 "transitions": PackedFloat32Array(1, 1, 1, 1),
@@ -97,16 +98,16 @@ tracks/0/keys = {
 "values": [1, 5, 9, 13]
 }
 
-[sub_resource type="Animation" id=6]
+[sub_resource type="Animation" id="6"]
 length = 0.8
-loop = true
+loop_mode = 1
 step = 0.2
 tracks/0/type = "value"
+tracks/0/imported = false
+tracks/0/enabled = true
 tracks/0/path = NodePath("sprite:frame")
 tracks/0/interp = 1
 tracks/0/loop_wrap = true
-tracks/0/imported = false
-tracks/0/enabled = true
 tracks/0/keys = {
 "times": PackedFloat32Array(0, 0.2, 0.4, 0.6),
 "transitions": PackedFloat32Array(1, 1, 1, 1),
@@ -114,16 +115,16 @@ tracks/0/keys = {
 "values": [3, 7, 11, 15]
 }
 
-[sub_resource type="Animation" id=7]
+[sub_resource type="Animation" id="7"]
 length = 0.8
-loop = true
+loop_mode = 1
 step = 0.2
 tracks/0/type = "value"
+tracks/0/imported = false
+tracks/0/enabled = true
 tracks/0/path = NodePath("sprite:frame")
 tracks/0/interp = 1
 tracks/0/loop_wrap = true
-tracks/0/imported = false
-tracks/0/enabled = true
 tracks/0/keys = {
 "times": PackedFloat32Array(0, 0.2, 0.4, 0.6),
 "transitions": PackedFloat32Array(1, 1, 1, 1),
@@ -131,30 +132,47 @@ tracks/0/keys = {
 "values": [2, 6, 10, 14]
 }
 
-[sub_resource type="Font" id=8]
-size = 14
-font_data = ExtResource( 3 )
+[sub_resource type="Font" id="8"]
+data/0 = ExtResource( "3" )
+
+[sub_resource type="SceneReplicationConfig" id="SceneReplicationConfig_xm6wv"]
+properties/0/path = NodePath(".:synced_position")
+properties/0/spawn = true
+properties/0/sync = true
+properties/1/path = NodePath("label:text")
+properties/1/spawn = true
+properties/1/sync = false
+
+[sub_resource type="SceneReplicationConfig" id="SceneReplicationConfig_kkxsa"]
+properties/0/path = NodePath(".:motion")
+properties/0/spawn = true
+properties/0/sync = true
+properties/1/path = NodePath(".:bombing")
+properties/1/spawn = true
+properties/1/sync = true
 
 [node name="player" type="CharacterBody2D"]
-script = ExtResource( 1 )
+motion_mode = 1
+script = ExtResource( "1" )
 
 [node name="sprite" type="Sprite2D" parent="."]
 position = Vector2(0.0750351, 6.23615)
-texture = ExtResource( 2 )
+rotation = -6.28319
+texture = ExtResource( "2" )
 offset = Vector2(-0.0750351, -6.23615)
 hframes = 4
 vframes = 4
 
 [node name="shape" type="CollisionShape2D" parent="."]
-shape = SubResource( 1 )
+shape = SubResource( "1" )
 
 [node name="anim" type="AnimationPlayer" parent="."]
-anims/standing = SubResource( 2 )
-anims/stunned = SubResource( 3 )
-anims/walk_down = SubResource( 4 )
-anims/walk_left = SubResource( 5 )
-anims/walk_right = SubResource( 6 )
-anims/walk_up = SubResource( 7 )
+anims/standing = SubResource( "2" )
+anims/stunned = SubResource( "3" )
+anims/walk_down = SubResource( "4" )
+anims/walk_left = SubResource( "5" )
+anims/walk_right = SubResource( "6" )
+anims/walk_up = SubResource( "7" )
 
 [node name="label" type="Label" parent="."]
 offset_left = -82.0
@@ -163,6 +181,18 @@ offset_right = 85.0
 offset_bottom = -14.0
 size_flags_horizontal = 2
 size_flags_vertical = 0
-custom_fonts/font = SubResource( 8 )
+theme_override_fonts/font = SubResource( "8" )
+theme_override_font_sizes/font_size = 16
 text = "Player 1"
-align = 1
+horizontal_alignment = 1
+
+[node name="MultiplayerSynchronizer" type="MultiplayerSynchronizer" parent="."]
+root_path = NodePath("..")
+resource = SubResource( "SceneReplicationConfig_xm6wv" )
+
+[node name="Inputs" type="Node" parent="."]
+script = ExtResource( "4_k1vfr" )
+
+[node name="InputsSync" type="MultiplayerSynchronizer" parent="Inputs"]
+root_path = NodePath("..")
+resource = SubResource( "SceneReplicationConfig_kkxsa" )

+ 24 - 0
networking/multiplayer_bomber/player_controls.gd

@@ -0,0 +1,24 @@
+extends Node
+
+@export
+var motion = Vector2() :
+	set(value):
+		# This will be sent by players, make sure values are within limits.
+		motion = clamp(value, Vector2(-1, -1), Vector2(1, 1))
+
+@export
+var bombing = false
+
+func update():
+	var m = Vector2()
+	if Input.is_action_pressed("move_left"):
+		m += Vector2(-1, 0)
+	if Input.is_action_pressed("move_right"):
+		m += Vector2(1, 0)
+	if Input.is_action_pressed("move_up"):
+		m += Vector2(0, -1)
+	if Input.is_action_pressed("move_down"):
+		m += Vector2(0, 1)
+
+	motion = m
+	bombing = Input.is_action_pressed("set_bomb")

+ 27 - 11
networking/multiplayer_bomber/project.godot

@@ -6,7 +6,7 @@
 ;   [section] ; section goes between []
 ;   param=value ; assign values to parameters
 
-config_version=4
+config_version=5
 
 [application]
 
@@ -16,6 +16,7 @@ One of the players should press 'host', while the other
 should type in his address and press 'play'."
 run/main_scene="res://lobby.tscn"
 config/icon="res://icon.png"
+config/features=PackedStringArray("4.0")
 
 [autoload]
 
@@ -27,10 +28,13 @@ gdscript/warnings/return_value_discarded=false
 
 [display]
 
-window/dpi/allow_hidpi=true
-window/vsync/use_vsync=false
 window/stretch/mode="2d"
 window/stretch/aspect="expand"
+window/vsync/use_vsync=false
+
+[editor_plugins]
+
+enabled=PackedStringArray()
 
 [gdnative]
 
@@ -45,32 +49,32 @@ gen_mipmaps=false
 
 move_down={
 "deadzone": 0.5,
-"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"store_command":true,"alt_pressed":false,"shift_pressed":false,"meta_pressed":false,"command_pressed":false,"pressed":false,"keycode":16777234,"physical_keycode":0,"unicode":0,"echo":false,"script":null)
-, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"store_command":true,"alt_pressed":false,"shift_pressed":false,"meta_pressed":false,"command_pressed":false,"pressed":false,"keycode":83,"physical_keycode":0,"unicode":0,"echo":false,"script":null)
+"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"store_command":true,"alt_pressed":false,"shift_pressed":false,"meta_pressed":false,"command_pressed":false,"pressed":false,"keycode":83,"physical_keycode":0,"unicode":0,"echo":false,"script":null)
+, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"store_command":true,"alt_pressed":false,"shift_pressed":false,"meta_pressed":false,"command_pressed":false,"pressed":false,"keycode":16777234,"physical_keycode":0,"unicode":0,"echo":false,"script":null)
 , Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":0,"axis":1,"axis_value":1.0,"script":null)
 , Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":13,"pressure":0.0,"pressed":false,"script":null)
 ]
 }
 move_left={
 "deadzone": 0.5,
-"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"store_command":true,"alt_pressed":false,"shift_pressed":false,"meta_pressed":false,"command_pressed":false,"pressed":false,"keycode":16777231,"physical_keycode":0,"unicode":0,"echo":false,"script":null)
-, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"store_command":true,"alt_pressed":false,"shift_pressed":false,"meta_pressed":false,"command_pressed":false,"pressed":false,"keycode":65,"physical_keycode":0,"unicode":0,"echo":false,"script":null)
+"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"store_command":true,"alt_pressed":false,"shift_pressed":false,"meta_pressed":false,"command_pressed":false,"pressed":false,"keycode":65,"physical_keycode":0,"unicode":0,"echo":false,"script":null)
+, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"store_command":true,"alt_pressed":false,"shift_pressed":false,"meta_pressed":false,"command_pressed":false,"pressed":false,"keycode":16777231,"physical_keycode":0,"unicode":0,"echo":false,"script":null)
 , Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":0,"axis":0,"axis_value":-1.0,"script":null)
 , Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":14,"pressure":0.0,"pressed":false,"script":null)
 ]
 }
 move_right={
 "deadzone": 0.5,
-"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"store_command":true,"alt_pressed":false,"shift_pressed":false,"meta_pressed":false,"command_pressed":false,"pressed":false,"keycode":16777233,"physical_keycode":0,"unicode":0,"echo":false,"script":null)
-, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"store_command":true,"alt_pressed":false,"shift_pressed":false,"meta_pressed":false,"command_pressed":false,"pressed":false,"keycode":68,"physical_keycode":0,"unicode":0,"echo":false,"script":null)
+"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"store_command":true,"alt_pressed":false,"shift_pressed":false,"meta_pressed":false,"command_pressed":false,"pressed":false,"keycode":68,"physical_keycode":0,"unicode":0,"echo":false,"script":null)
+, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"store_command":true,"alt_pressed":false,"shift_pressed":false,"meta_pressed":false,"command_pressed":false,"pressed":false,"keycode":16777233,"physical_keycode":0,"unicode":0,"echo":false,"script":null)
 , Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":0,"axis":0,"axis_value":1.0,"script":null)
 , Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":15,"pressure":0.0,"pressed":false,"script":null)
 ]
 }
 move_up={
 "deadzone": 0.5,
-"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"store_command":true,"alt_pressed":false,"shift_pressed":false,"meta_pressed":false,"command_pressed":false,"pressed":false,"keycode":16777232,"physical_keycode":0,"unicode":0,"echo":false,"script":null)
-, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"store_command":true,"alt_pressed":false,"shift_pressed":false,"meta_pressed":false,"command_pressed":false,"pressed":false,"keycode":87,"physical_keycode":0,"unicode":0,"echo":false,"script":null)
+"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"store_command":true,"alt_pressed":false,"shift_pressed":false,"meta_pressed":false,"command_pressed":false,"pressed":false,"keycode":87,"physical_keycode":0,"unicode":0,"echo":false,"script":null)
+, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"store_command":true,"alt_pressed":false,"shift_pressed":false,"meta_pressed":false,"command_pressed":false,"pressed":false,"keycode":16777232,"physical_keycode":0,"unicode":0,"echo":false,"script":null)
 , Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":0,"axis":1,"axis_value":-1.0,"script":null)
 , Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":12,"pressure":0.0,"pressed":false,"script":null)
 ]
@@ -87,3 +91,15 @@ set_bomb={
 [rendering]
 
 quality/driver/driver_name="GLES2"
+
+[replication]
+
+config={
+"uid://dviwgv2ty8v6u": {
+"replicated": true,
+"spawn_properties": [],
+"sync_interval": 16,
+"sync_properties": [&":stunned", &"sprite:hframes", &"sprite:vframes", &":server_position", &"label:text"],
+"synced": false
+}
+}

+ 3 - 9
networking/multiplayer_bomber/rock.gd

@@ -1,12 +1,6 @@
 extends CharacterBody2D
 
-# Sent to everyone else
-puppet func do_explosion():
+@rpc(call_local)
+func exploded(by_who):
+	$"../../Score".increase_score(by_who)
 	$"AnimationPlayer".play("explode")
-
-
-# Received by owner of the rock
-master func exploded(by_who):
-	rpc("do_explosion") # Re-sent to puppet rocks
-	$"../../Score".rpc("increase_score", by_who)
-	do_explosion()

+ 19 - 18
networking/multiplayer_bomber/rock.tscn

@@ -1,33 +1,33 @@
-[gd_scene load_steps=5 format=2]
+[gd_scene load_steps=5 format=3 uid="uid://bao3yernlglws"]
 
-[ext_resource path="res://rock.gd" type="Script" id=1]
-[ext_resource path="res://brickfloor.png" type="Texture2D" id=2]
+[ext_resource type="Script" path="res://rock.gd" id="1"]
+[ext_resource type="Texture2D" uid="uid://bdomqql6y50po" path="res://brickfloor.png" id="2"]
 
-[sub_resource type="RectangleShape2D" id=1]
-extents = Vector2(24, 24)
+[sub_resource type="RectangleShape2D" id="1"]
+size = Vector2(48, 48)
 
-[sub_resource type="Animation" id=2]
+[sub_resource type="Animation" id="2"]
 resource_name = "explode"
 tracks/0/type = "method"
+tracks/0/imported = false
+tracks/0/enabled = true
 tracks/0/path = NodePath(".")
 tracks/0/interp = 1
 tracks/0/loop_wrap = true
-tracks/0/imported = false
-tracks/0/enabled = true
 tracks/0/keys = {
 "times": PackedFloat32Array(1),
 "transitions": PackedFloat32Array(1),
 "values": [{
 "args": [],
-"method": "queue_free"
+"method": &"queue_free"
 }]
 }
 tracks/1/type = "value"
-tracks/1/path = NodePath("Sprite2D:visible")
-tracks/1/interp = 1
-tracks/1/loop_wrap = true
 tracks/1/imported = false
 tracks/1/enabled = true
+tracks/1/path = NodePath("Sprite:visible")
+tracks/1/interp = 1
+tracks/1/loop_wrap = true
 tracks/1/keys = {
 "times": PackedFloat32Array(0),
 "transitions": PackedFloat32Array(1),
@@ -36,15 +36,16 @@ tracks/1/keys = {
 }
 
 [node name="Rock" type="CharacterBody2D"]
-script = ExtResource( 1 )
+motion_mode = 1
+script = ExtResource( "1" )
 
-[node name="Sprite2D" type="Sprite2D" parent="."]
-texture = ExtResource( 2 )
+[node name="Sprite" type="Sprite2D" parent="."]
+texture = ExtResource( "2" )
 region_enabled = true
 region_rect = Rect2(96, 0, 48, 48)
 
-[node name="Shape3D" type="CollisionShape2D" parent="."]
-shape = SubResource( 1 )
+[node name="Shape" type="CollisionShape2D" parent="."]
+shape = SubResource( "1" )
 
 [node name="AnimationPlayer" type="AnimationPlayer" parent="."]
-anims/explode = SubResource( 2 )
+anims/explode = SubResource( "2" )

+ 13 - 15
networking/multiplayer_bomber/rock_bit.png.import

@@ -1,8 +1,9 @@
 [remap]
 
 importer="texture"
-type="StreamTexture2D"
-path="res://.godot/imported/rock_bit.png-cd30ec3dce7edf848ee632b29d4d0c95.stex"
+type="CompressedTexture2D"
+uid="uid://bh4gbjcayios1"
+path="res://.godot/imported/rock_bit.png-cd30ec3dce7edf848ee632b29d4d0c95.ctex"
 metadata={
 "vram_texture": false
 }
@@ -10,26 +11,23 @@ metadata={
 [deps]
 
 source_file="res://rock_bit.png"
-dest_files=["res://.godot/imported/rock_bit.png-cd30ec3dce7edf848ee632b29d4d0c95.stex"]
+dest_files=["res://.godot/imported/rock_bit.png-cd30ec3dce7edf848ee632b29d4d0c95.ctex"]
 
 [params]
 
 compress/mode=0
 compress/lossy_quality=0.7
-compress/hdr_mode=0
+compress/hdr_compression=1
 compress/bptc_ldr=0
 compress/normal_map=0
-flags/repeat=0
-flags/filter=true
-flags/mipmaps=false
-flags/anisotropic=false
-flags/srgb=2
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
 process/fix_alpha_border=true
 process/premult_alpha=false
-process/HDR_as_SRGB=false
-process/invert_color=false
 process/normal_map_invert_y=false
-stream=false
-size_limit=0
-detect_3d=true
-svg/scale=1.0
+process/hdr_as_srgb=false
+process/size_limit=0
+detect_3d/compress_to=1

+ 5 - 5
networking/multiplayer_bomber/score.gd

@@ -16,7 +16,7 @@ func _process(_delta):
 		$"../Winner".show()
 
 
-remotesync func increase_score(for_who):
+func increase_score(for_who):
 	assert(for_who in player_labels)
 	var pl = player_labels[for_who]
 	pl.score += 1
@@ -25,13 +25,13 @@ remotesync func increase_score(for_who):
 
 func add_player(id, new_player_name):
 	var l = Label.new()
-	l.set_align(Label.ALIGN_CENTER)
+	l.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
 	l.set_text(new_player_name + "\n" + "0")
 	l.set_h_size_flags(SIZE_EXPAND_FILL)
 	var font = Font.new()
-	font.set_size(18)
-	font.set_font_data(preload("res://montserrat.otf"))
-	l.add_font_override("font", font)
+	font.add_data(preload("res://montserrat.otf"))
+	l.set("custom_fonts/font", font)
+	l.set("custom_font_size/font_size", 18)
 	add_child(l)
 
 	player_labels[id] = { name = new_player_name, label = l, score = 0 }

+ 7 - 7
networking/multiplayer_bomber/tile_scene.tscn

@@ -1,23 +1,23 @@
-[gd_scene load_steps=3 format=2]
+[gd_scene load_steps=3 format=3 uid="uid://c5m3rogpaglk1"]
 
-[ext_resource path="res://brickfloor.png" type="Texture2D" id=1]
+[ext_resource type="Texture2D" uid="uid://bdomqql6y50po" path="res://brickfloor.png" id="1"]
 
-[sub_resource type="RectangleShape2D" id=1]
-extents = Vector2(24, 24)
+[sub_resource type="RectangleShape2D" id="1"]
+size = Vector2(48, 48)
 
 [node name="TileScene" type="Node2D"]
 
 [node name="Wall" type="Sprite2D" parent="."]
 position = Vector2(24, 24)
-texture = ExtResource( 1 )
+texture = ExtResource( "1" )
 region_rect = Rect2(0, 0, 48, 48)
 
 [node name="StaticBody2D" type="StaticBody2D" parent="Wall"]
 
 [node name="CollisionShape2D" type="CollisionShape2D" parent="Wall/StaticBody2D"]
-shape = SubResource( 1 )
+shape = SubResource( "1" )
 
 [node name="Floor" type="Sprite2D" parent="."]
 position = Vector2(72, 24)
-texture = ExtResource( 1 )
+texture = ExtResource( "1" )
 region_rect = Rect2(48, 0, 48, 48)

+ 15 - 39
networking/multiplayer_bomber/tileset.tres

@@ -1,43 +1,19 @@
-[gd_resource type="TileSet" load_steps=3 format=2]
+[gd_resource type="TileSet" load_steps=3 format=3 uid="uid://do2l6lpuotti8"]
 
-[ext_resource path="res://brickfloor.png" type="Texture2D" id=1]
+[ext_resource type="Texture2D" uid="uid://bdomqql6y50po" path="res://brickfloor.png" id="1"]
 
-[sub_resource type="RectangleShape2D" id=1]
-extents = Vector2( 24, 24 )
+[sub_resource type="TileSetAtlasSource" id="TileSetAtlasSource_qhkfp"]
+texture = ExtResource( "1" )
+texture_region_size = Vector2i(48, 48)
+0:0/0 = 0
+0:0/0/physics_layer_0/linear_velocity = Vector2(0, 0)
+0:0/0/physics_layer_0/angular_velocity = 0.0
+0:0/0/physics_layer_0/polygon_0/points = PackedVector2Array(-24, -24, 24, -24, 24, 24, -24, 24)
+1:0/0 = 0
+1:0/0/physics_layer_0/linear_velocity = Vector2(0, 0)
+1:0/0/physics_layer_0/angular_velocity = 0.0
 
 [resource]
-0/name = "wall"
-0/texture = ExtResource( 1 )
-0/tex_offset = Vector2( 0, 0 )
-0/modulate = Color( 1, 1, 1, 1 )
-0/region = Rect2( 0, 0, 48, 48 )
-0/tile_mode = 0
-0/occluder_offset = Vector2( 24, 24 )
-0/navigation_offset = Vector2( 24, 24 )
-0/shape_offset = Vector2( 24, 24 )
-0/shape_transform = Transform2D( 1, 0, 0, 1, 24, 24 )
-0/shape = SubResource( 1 )
-0/shape_one_way = false
-0/shape_one_way_margin = 1.0
-0/shapes = [ {
-"autotile_coord": Vector2( 0, 0 ),
-"one_way": false,
-"one_way_margin": 1.0,
-"shape": SubResource( 1 ),
-"shape_transform": Transform2D( 1, 0, 0, 1, 24, 24 )
-} ]
-0/z_index = 0
-1/name = "floor"
-1/texture = ExtResource( 1 )
-1/tex_offset = Vector2( 0, 0 )
-1/modulate = Color( 1, 1, 1, 1 )
-1/region = Rect2( 48, 0, 48, 48 )
-1/tile_mode = 0
-1/occluder_offset = Vector2( 24, 24 )
-1/navigation_offset = Vector2( 24, 24 )
-1/shape_offset = Vector2( 0, 0 )
-1/shape_transform = Transform2D( 1, 0, 0, 1, 0, 0 )
-1/shape_one_way = false
-1/shape_one_way_margin = 0.0
-1/shapes = [  ]
-1/z_index = 0
+tile_size = Vector2i(48, 48)
+physics_layer_0/collision_layer = 1
+sources/0 = SubResource( "TileSetAtlasSource_qhkfp" )

File diff suppressed because it is too large
+ 10 - 12
networking/multiplayer_bomber/world.tscn


Some files were not shown because too many files changed in this diff