diff --git a/Game/Assets/Characters/Player.gd b/Game/Assets/Characters/Player.gd index b2b36a2..3a75863 100644 --- a/Game/Assets/Characters/Player.gd +++ b/Game/Assets/Characters/Player.gd @@ -5,10 +5,15 @@ var impact_player = preload("res://Assets/Effects/ImpactBlood.tscn") var max_health = 100 var health = max_health: set(value): - main.player_list.players[self.get_multiplayer_authority()].health = value - main.push_local_player_info() + if not dead: + if main.player_list.players.has(self.get_multiplayer_authority()): + main.player_list.players[self.get_multiplayer_authority()].health = value + main.push_local_player_info() get: - return main.player_list.players[self.get_multiplayer_authority()].health + if not dead: + return main.player_list.players[self.get_multiplayer_authority()].health + else: + return 0 @export var mouse_sensitivity := 0.15 @@ -380,41 +385,45 @@ func _process(delta): crosshair.show() @rpc(any_peer, call_local, reliable) func die(killer_pid: int): + if killer_pid == -1: # we're disconnecting from the game + #main.chat.chat_notification("Player [/i][b][color=" + main.player_list.players[self.get_multiplayer_authority()].color.to_html() + "]" + main.player_list.players[self.get_multiplayer_authority()].name + "[/color][/b][i] left the game.") + pass + else: + var gibs = gibs_vfx.instantiate() + get_tree().root.add_child(gibs) + gibs.global_transform = self.global_transform + + var decal = blood_decal.instantiate() + get_tree().root.add_child(decal) + decal.global_transform = self.global_transform + + if is_multiplayer_authority(): # don't touch these on puppets + hud.pain = 3 + crosshair.hide() + + #main.chat.rpc(&'chat_notification', "Player [/i][b][color=" + main.player_list.players[self.get_multiplayer_authority()].color.to_html() + "]" + main.player_list.players[self.get_multiplayer_authority()].name + "[/color][/b][i] was killed by " + main.player_list.players[killer_pid].name ) + main.chat.chat_notification("Player [/i][b][color=" + main.player_list.players[self.get_multiplayer_authority()].color.to_html() + "]" + main.player_list.players[self.get_multiplayer_authority()].name + "[/color][/b][i] was killed by " + main.player_list.players[killer_pid].name ) + + revenge_pid = killer_pid + + $Head/Camera.position.y = -1 # lower the head to the ground, let the player see their gibs + $Head/Camera.rotation.x = 0 # reset the tilt so the camera looks forward + $Head/Camera.rotation.z = -20 + + jetpack_active = false + view_zoom_target = 1.0 + view_zoom = 1 + dead = true - - var gibs = gibs_vfx.instantiate() - get_tree().root.add_child(gibs) - gibs.global_transform = self.global_transform - - var decal = blood_decal.instantiate() - get_tree().root.add_child(decal) - decal.global_transform = self.global_transform + main.destroy_player(self.get_multiplayer_authority()) + self.hide() + $Body.disabled = true #if get_tree().get_rpc_sender_id() != get_multiplayer_authority(): # print ("Death requested by a non-master. Ignoring") # return #main.rpc(&'destroy_player', self.get_multiplayer_authority()) - main.destroy_player(self.get_multiplayer_authority()) - - self.hide() - $Body.disabled = true - - if is_multiplayer_authority(): # don't touch these on puppets - hud.pain = 3 - crosshair.hide() - - #main.chat.rpc(&'chat_notification', "Player [/i][b][color=" + main.player_list.players[self.get_multiplayer_authority()].color.to_html() + "]" + main.player_list.players[self.get_multiplayer_authority()].name + "[/color][/b][i] was killed by " + main.player_list.players[killer_pid].name ) - main.chat.chat_notification("Player [/i][b][color=" + main.player_list.players[self.get_multiplayer_authority()].color.to_html() + "]" + main.player_list.players[self.get_multiplayer_authority()].name + "[/color][/b][i] was killed by " + main.player_list.players[killer_pid].name ) - - revenge_pid = killer_pid - - $Head/Camera.position.y = -1 # lower the head to the ground, let the player see their gibs - $Head/Camera.rotation.x = 0 # reset the tilt so the camera looks forward - $Head/Camera.rotation.z = -20 - - jetpack_active = false - view_zoom_target = 1.0 - view_zoom = 1 + #queue_free() diff --git a/Game/Assets/HUD/HUD.tscn b/Game/Assets/HUD/HUD.tscn index 917e16d..f15bd53 100644 --- a/Game/Assets/HUD/HUD.tscn +++ b/Game/Assets/HUD/HUD.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=23 format=3 uid="uid://bff5uslrxesjx"] +[gd_scene load_steps=24 format=3 uid="uid://bff5uslrxesjx"] [ext_resource type="Texture2D" uid="uid://blnjjtjifk22i" path="res://Assets/HUD/Vignette.png" id="1"] [ext_resource type="Script" path="res://Assets/HUD/HUD.gd" id="1_wc430"] @@ -16,6 +16,7 @@ [ext_resource type="AudioStream" uid="uid://d36o2duyrij57" path="res://Assets/SFX/UI_Message.wav" id="9_ubwxw"] [ext_resource type="Texture2D" uid="uid://dvt25wji1pdyl" path="res://Assets/HUD/BarOver.svg" id="10_5i332"] [ext_resource type="Texture2D" uid="uid://0j6rxd7ncmu1" path="res://Assets/HUD/BarProgress.svg" id="11_tlgqu"] +[ext_resource type="Script" path="res://Assets/HUD/Performance.gd" id="17_getmp"] [sub_resource type="ShaderMaterial" id="ShaderMaterial_dxk5q"] resource_local_to_scene = true @@ -459,4 +460,21 @@ stretch_margin_right = 16 stretch_margin_bottom = 16 script = null +[node name="Performance" type="Label" parent="."] +material = ExtResource( "4_f6eam" ) +anchor_top = 1.0 +anchor_bottom = 1.0 +offset_left = 4.0 +offset_top = -106.0 +offset_right = 202.0 +offset_bottom = -2.0 +text = "FPS: +PING:" +valign = 2 +structured_text_bidi_override_options = [] +script = ExtResource( "17_getmp" ) +__meta__ = { +"_edit_use_anchors_": false +} + [connection signal="text_submitted" from="Chat/VBoxContainer/Typing/Editor" to="Chat" method="_on_Editor_text_submitted"] diff --git a/Game/Assets/HUD/Performance.gd b/Game/Assets/HUD/Performance.gd new file mode 100644 index 0000000..c398573 --- /dev/null +++ b/Game/Assets/HUD/Performance.gd @@ -0,0 +1,27 @@ +extends Label + +# Declare member variables here. Examples: +# var a = 2 +# var b = "text" +@onready var main = get_tree().root.get_node("Main") + +# Called when the node enters the scene tree for the first time. +func _ready(): + pass # Replace with function body. + + +# Called every frame. 'delta' is the elapsed time since the previous frame. +func _process(delta): + + if not get_tree().multiplayer.has_multiplayer_peer(): + text = "Offline" + elif get_tree().multiplayer.is_server(): + text = "Hosting" + else: + if main.ping_time >= 0: + text = "Ping: " + str(round(main.ping_time * 1000)) + " ms" + else: + text = "Ping unknown" + + text += "\nFPS: " + str(Engine.get_frames_per_second()) + text += "\nLiblast 0.1.1 pre-alpha" diff --git a/Game/Assets/UI/GUI.gd b/Game/Assets/UI/GUI.gd index 5393b8e..f98e056 100644 --- a/Game/Assets/UI/GUI.gd +++ b/Game/Assets/UI/GUI.gd @@ -50,6 +50,18 @@ func apply_settings(): # These functions are for applying settings changes func quit_game(): + + var main = get_tree().get_root().get_node("Main") + var pid = main.get_multiplayer_authority() + var player_node = main.get_node("Players").get_node(str(pid)) + + #main.get_node("HUD/Chat").rpc(&'chat_notification', "Player " + str(pid) +" left the game.") + #player_node.rpc(&'die', -1) + #main.rpc(&'disconnect_player', pid) # tell everyone we're leaving + #main.multiplayer = null + + get_tree().multiplayer.multiplayer_peer = null + get_tree().quit() func toggle_fullscreen(is_fullscreen): diff --git a/Game/Main.gd b/Game/Main.gd index 0bb7ba9..007c764 100644 --- a/Game/Main.gd +++ b/Game/Main.gd @@ -10,6 +10,11 @@ const NET_SERVER = "localhost" var peer = ENetMultiplayerPeer.new() +var ping_time = -1 +var ping_sent = 0 +var ping_recieved = 0 +var ping_token = 0 + var role = MultiplayerRole.NONE: set(new_role): role = new_role @@ -22,6 +27,23 @@ var player_scene = preload("res://Assets/Characters/Player.tscn") @onready var chat = hud.get_node("Chat") var local_player: Node = null +@rpc(call_remote, any_peer, unreliable) func ping(token:int = -1): + if get_tree().multiplayer.is_server(): # we are the sever - reply to pinging client +# for i in range(0, 100): +# print(i) + rpc_id(get_tree().multiplayer.get_remote_sender_id(), &'ping', token) + else: + if get_tree().multiplayer.get_remote_sender_id() == 1: #recieve answer from server + if token == ping_token: # if the token is correct + ping_recieved = uptime + ping_time = ping_recieved - ping_sent + #else: + # ping_time = -1 + elif token == -1: # send ping + ping_sent = uptime + ping_token = randi() + rpc_id(1, &'ping', ping_token) + class PlayerInfo: var name: String var team: int @@ -133,7 +155,10 @@ class PlayerList: players[pid] = info func get(pid: int) -> PlayerInfo: - return players[pid] + if players.has(pid): + return players[pid] + else: + return PlayerInfo.new() @onready var player_list = PlayerList.new() @@ -184,7 +209,7 @@ func _input(_event) -> void: @rpc(any_peer, call_local, reliable) func game_over(winner): if local_player: - local_player.rpc(&'set_dead', true) + local_player.die(-1) hud.game_over(winner) spawn_queue.clear() @@ -244,6 +269,9 @@ func update_hud(): @rpc(any_peer, reliable) func player_list_update(info, pid = get_tree().get_rpc_sender_id(), erase:=false): if erase: player_list.erase(pid) + spawn_queue.erase(pid) + get_node("Players").get_node(str(pid)).die(-1) + return var new_info = PlayerInfo.new() new_info.deserialize(info) @@ -268,6 +296,7 @@ func update_hud(): func push_local_player_info(): # var pid = get_tree().multiplayer.get_unique_id() + assert(pid >= 1, "Another server must be running. PID is 0, that means Enet did not initialize.") rpc(&'player_list_update', player_list.get(pid).serialize(), pid) @rpc(call_local, any_peer, reliable) func destroy_player(pid: int): @@ -290,7 +319,9 @@ func push_local_player_info(): # if destroy_free_player_crash_workaround: pass #print("Setting player ", pid, " as DEAD") - #player_node.set_dead(true) + if not player_node.dead: # disconnected, not killed + player_node.die(-1) + return # skip the rest - don't add to respawn queue else: # regular method follows player_node.name = str(player_node.name) + "_dead" # avoids name collision when instancing another player scene @@ -298,7 +329,8 @@ func push_local_player_info(): # player_node.queue_free() print("after free") # respawn queue applies ot both implementations of death - spawn_queue[pid] = uptime + respawn_delay + if player_list.players.has(pid): # don't respawn players that are not there, lol + spawn_queue[pid] = uptime + respawn_delay if pid == get_tree().multiplayer.get_unique_id(): update_hud() @@ -443,7 +475,7 @@ func _player_connected(pid) -> void: # if role in [MultiplayerRole.SERVER, MultiplayerRole.DEDICATED_SERVER]: # rpc_id(id, "player_list_update", player_list) -func _player_disconnected(pid) -> void: +@rpc(call_remote, any_peer, reliable) func disconnect_player(pid): print("player disconnected, id: ", pid) spawn_queue.erase(pid) if get_tree().multiplayer.is_server(): @@ -451,6 +483,9 @@ func _player_disconnected(pid) -> void: player_list.erase(pid) rpc(&'player_list_update', null, pid, true) rpc(&'destroy_player', pid) + +func _player_disconnected(pid) -> void: + disconnect_player(pid) func _connected_ok() -> void: print("connected to server") diff --git a/Game/Main.tscn b/Game/Main.tscn index 474f142..8bc6677 100644 --- a/Game/Main.tscn +++ b/Game/Main.tscn @@ -241,6 +241,9 @@ offset_top = 49.0 custom_effects = [] structured_text_bidi_override_options = [] +[node name="Performance" parent="HUD" index="8"] +structured_text_bidi_override_options = [] + [node name="GUI" parent="." instance=ExtResource( "2" )] [node name="Players" type="Node3D" parent="."] @@ -336,11 +339,16 @@ stream = ExtResource( "5_ilayd" ) bus = &"Announcer" script = ExtResource( "6_gcsgr" ) +[node name="PingTimer" type="Timer" parent="."] +autostart = true +script = null + [connection signal="toggled" from="NetworkTesting/CheckButton" to="." method="_on_CheckButton_toggled"] [connection signal="pressed" from="NetworkTesting/Host" to="." method="_on_Host_pressed"] [connection signal="pressed" from="NetworkTesting/Connect" to="." method="_on_Connect_pressed"] [connection signal="text_submitted" from="NetworkTesting/TextEdit" to="." method="_on_TextEdit_text_submitted"] [connection signal="color_changed" from="NetworkTesting/ColorPickerButton" to="." method="_on_ColorPickerButton_color_changed"] [connection signal="pressed" from="NetworkTesting/Button" to="." method="_on_Button_pressed"] +[connection signal="timeout" from="PingTimer" to="." method="ping"] [editable path="HUD"]