Improved reconnect flow
This commit is contained in:
parent
bf08205c7d
commit
b22d0b1367
@ -1,26 +1,40 @@
|
|||||||
extends Node
|
extends Node
|
||||||
|
|
||||||
func _ready():
|
func _ready():
|
||||||
|
pause(true)
|
||||||
|
|
||||||
Global.instance_id = OS.get_process_id() % 4
|
Global.instance_id = OS.get_process_id() % 4
|
||||||
Global.username = "user%d" % Global.instance_id
|
Global.username = "user%d" % Global.instance_id
|
||||||
|
|
||||||
%Login.success.connect(on_login)
|
%Login.success.connect(on_login)
|
||||||
|
%Logout.success.connect(on_logout)
|
||||||
%Login.send_login()
|
%Login.send_login()
|
||||||
|
|
||||||
mute_audio()
|
|
||||||
|
|
||||||
func _input(event):
|
func _input(event):
|
||||||
if event.is_action_pressed("toggle_fullscreen"):
|
if event.is_action_pressed("toggle_fullscreen"):
|
||||||
toggle_fullscreen()
|
toggle_fullscreen()
|
||||||
get_viewport().set_input_as_handled()
|
get_viewport().set_input_as_handled()
|
||||||
|
|
||||||
func mute_audio():
|
|
||||||
var master_sound = AudioServer.get_bus_index("Master")
|
|
||||||
AudioServer.set_bus_mute(master_sound, true)
|
|
||||||
|
|
||||||
func on_login():
|
func on_login():
|
||||||
|
print("[%s] Login succeeded." % Global.username)
|
||||||
|
print("[%s] ID: %s" % [Global.username, Global.account_id])
|
||||||
|
print("[%s] Auth token: %s" % [Global.username, Global.auth_token])
|
||||||
|
|
||||||
DisplayServer.window_set_title("%s - %s" % [Global.username, Global.account_id])
|
DisplayServer.window_set_title("%s - %s" % [Global.username, Global.account_id])
|
||||||
DisplayServer.window_set_position(Vector2((Global.instance_id % 2) * 960, (Global.instance_id / 2 % 2) * 540))
|
DisplayServer.window_set_position(Vector2((Global.instance_id % 2) * 960, (Global.instance_id / 2 % 2) * 540))
|
||||||
|
pause(false)
|
||||||
|
|
||||||
|
func on_logout():
|
||||||
|
print("[%s] Logout." % Global.username)
|
||||||
|
pause(true)
|
||||||
|
|
||||||
|
func pause(enabled: bool):
|
||||||
|
get_tree().paused = enabled
|
||||||
|
mute_audio(enabled)
|
||||||
|
|
||||||
|
func mute_audio(enabled: bool):
|
||||||
|
var master_sound = AudioServer.get_bus_index("Master")
|
||||||
|
AudioServer.set_bus_mute(master_sound, enabled)
|
||||||
|
|
||||||
func toggle_fullscreen():
|
func toggle_fullscreen():
|
||||||
var mode = DisplayServer.window_get_mode()
|
var mode = DisplayServer.window_get_mode()
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
[gd_scene load_steps=24 format=3 uid="uid://b40y7iuskv1ar"]
|
[gd_scene load_steps=25 format=3 uid="uid://b40y7iuskv1ar"]
|
||||||
|
|
||||||
[ext_resource type="Script" path="res://Main.gd" id="1_cw3ws"]
|
[ext_resource type="Script" path="res://Main.gd" id="1_cw3ws"]
|
||||||
[ext_resource type="Script" path="res://network/Client.gd" id="2_8hxcx"]
|
[ext_resource type="Script" path="res://network/Client.gd" id="2_8hxcx"]
|
||||||
[ext_resource type="Script" path="res://network/Ping.gd" id="3_d6qf1"]
|
[ext_resource type="Script" path="res://network/Ping.gd" id="3_d6qf1"]
|
||||||
[ext_resource type="Script" path="res://network/Login.gd" id="4_fsx7a"]
|
[ext_resource type="Script" path="res://network/Login.gd" id="4_fsx7a"]
|
||||||
[ext_resource type="Script" path="res://network/PlayerAdd.gd" id="5_376ik"]
|
[ext_resource type="Script" path="res://network/PlayerAdd.gd" id="5_376ik"]
|
||||||
|
[ext_resource type="Script" path="res://network/Logout.gd" id="5_au5w3"]
|
||||||
[ext_resource type="PackedScene" uid="uid://2lcnu3dy54lx" path="res://player/Player.tscn" id="6_cdj8w"]
|
[ext_resource type="PackedScene" uid="uid://2lcnu3dy54lx" path="res://player/Player.tscn" id="6_cdj8w"]
|
||||||
[ext_resource type="Script" path="res://network/PlayerRemove.gd" id="7_2r42o"]
|
[ext_resource type="Script" path="res://network/PlayerRemove.gd" id="7_2r42o"]
|
||||||
[ext_resource type="Script" path="res://network/PlayerMove.gd" id="8_ke1yy"]
|
[ext_resource type="Script" path="res://network/PlayerMove.gd" id="8_ke1yy"]
|
||||||
@ -40,6 +41,7 @@ script = ExtResource("1_cw3ws")
|
|||||||
|
|
||||||
[node name="Client" type="Node" parent="."]
|
[node name="Client" type="Node" parent="."]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
|
process_mode = 3
|
||||||
script = ExtResource("2_8hxcx")
|
script = ExtResource("2_8hxcx")
|
||||||
|
|
||||||
[node name="Ping" type="Node" parent="Client"]
|
[node name="Ping" type="Node" parent="Client"]
|
||||||
@ -59,6 +61,11 @@ packet_type = 2
|
|||||||
wait_time = 5.0
|
wait_time = 5.0
|
||||||
autostart = true
|
autostart = true
|
||||||
|
|
||||||
|
[node name="Logout" type="Node" parent="Client"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
script = ExtResource("5_au5w3")
|
||||||
|
packet_type = 3
|
||||||
|
|
||||||
[node name="PlayerAdd" type="Node" parent="Client"]
|
[node name="PlayerAdd" type="Node" parent="Client"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
script = ExtResource("5_376ik")
|
script = ExtResource("5_376ik")
|
||||||
@ -71,7 +78,6 @@ packet_type = 11
|
|||||||
|
|
||||||
[node name="PlayerMove" type="Node" parent="Client"]
|
[node name="PlayerMove" type="Node" parent="Client"]
|
||||||
script = ExtResource("8_ke1yy")
|
script = ExtResource("8_ke1yy")
|
||||||
delay = null
|
|
||||||
packet_type = 12
|
packet_type = 12
|
||||||
|
|
||||||
[node name="PlayerJump" type="Node" parent="Client"]
|
[node name="PlayerJump" type="Node" parent="Client"]
|
||||||
@ -131,6 +137,7 @@ unique_name_in_owner = true
|
|||||||
script = ExtResource("16_dp6bj")
|
script = ExtResource("16_dp6bj")
|
||||||
|
|
||||||
[node name="UI" parent="." instance=ExtResource("17_43qhq")]
|
[node name="UI" parent="." instance=ExtResource("17_43qhq")]
|
||||||
|
process_mode = 3
|
||||||
|
|
||||||
[node name="Viewport" type="SubViewportContainer" parent="."]
|
[node name="Viewport" type="SubViewportContainer" parent="."]
|
||||||
texture_filter = 1
|
texture_filter = 1
|
||||||
|
@ -8,7 +8,7 @@ func send_message(message: String):
|
|||||||
buffer.put_data(message.to_utf8_buffer())
|
buffer.put_data(message.to_utf8_buffer())
|
||||||
%Client.send(buffer.data_array)
|
%Client.send(buffer.data_array)
|
||||||
|
|
||||||
func handle_packet(data: PackedByteArray, _peer: PacketPeer):
|
func handle_packet(data: PackedByteArray):
|
||||||
var buffer := StreamPeerBuffer.new()
|
var buffer := StreamPeerBuffer.new()
|
||||||
buffer.data_array = data
|
buffer.data_array = data
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ func _enter_tree():
|
|||||||
func _process(_delta):
|
func _process(_delta):
|
||||||
while socket.get_available_packet_count() > 0:
|
while socket.get_available_packet_count() > 0:
|
||||||
var packet := socket.get_packet()
|
var packet := socket.get_packet()
|
||||||
handle_packet(packet, socket)
|
handle_packet(packet)
|
||||||
|
|
||||||
func update_statistics():
|
func update_statistics():
|
||||||
download_changed.emit(download)
|
download_changed.emit(download)
|
||||||
@ -31,4 +31,4 @@ func update_statistics():
|
|||||||
|
|
||||||
func send(data: PackedByteArray):
|
func send(data: PackedByteArray):
|
||||||
socket.put_packet(data)
|
socket.put_packet(data)
|
||||||
upload += data.size()
|
upload += data.size()
|
||||||
|
@ -3,7 +3,7 @@ extends PacketHandler
|
|||||||
signal success
|
signal success
|
||||||
signal failure
|
signal failure
|
||||||
|
|
||||||
func handle_packet(data: PackedByteArray, _peer: PacketPeer):
|
func handle_packet(data: PackedByteArray):
|
||||||
var buffer := StreamPeerBuffer.new()
|
var buffer := StreamPeerBuffer.new()
|
||||||
buffer.data_array = data
|
buffer.data_array = data
|
||||||
|
|
||||||
@ -15,10 +15,6 @@ func handle_packet(data: PackedByteArray, _peer: PacketPeer):
|
|||||||
|
|
||||||
Global.account_id = buffer.get_string()
|
Global.account_id = buffer.get_string()
|
||||||
Global.auth_token = buffer.get_string()
|
Global.auth_token = buffer.get_string()
|
||||||
|
|
||||||
print("[%s] Login succeeded." % Global.username)
|
|
||||||
print("[%s] ID: %s" % [Global.username, Global.account_id])
|
|
||||||
print("[%s] Auth token: %s" % [Global.username, Global.auth_token])
|
|
||||||
|
|
||||||
success.emit()
|
success.emit()
|
||||||
|
|
||||||
|
12
client/network/Logout.gd
Normal file
12
client/network/Logout.gd
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
extends PacketHandler
|
||||||
|
|
||||||
|
signal success
|
||||||
|
|
||||||
|
func handle_packet(_data: PackedByteArray):
|
||||||
|
logout()
|
||||||
|
success.emit()
|
||||||
|
|
||||||
|
func logout():
|
||||||
|
Global.auth_token = ""
|
||||||
|
Global.account_id = ""
|
||||||
|
Global.player = null
|
@ -10,7 +10,7 @@ var history: Array[float] = []
|
|||||||
func _init():
|
func _init():
|
||||||
history.resize(HISTORY_SIZE)
|
history.resize(HISTORY_SIZE)
|
||||||
|
|
||||||
func handle_packet(data: PackedByteArray, _peer: PacketPeer):
|
func handle_packet(data: PackedByteArray):
|
||||||
var id := data[0]
|
var id := data[0]
|
||||||
var ping := get_time() - history[id]
|
var ping := get_time() - history[id]
|
||||||
changed.emit(ping)
|
changed.emit(ping)
|
||||||
|
@ -7,7 +7,7 @@ signal main_player_spawned(player: Player)
|
|||||||
func _ready():
|
func _ready():
|
||||||
assert(player_scene)
|
assert(player_scene)
|
||||||
|
|
||||||
func handle_packet(data: PackedByteArray, _peer: PacketPeer):
|
func handle_packet(data: PackedByteArray):
|
||||||
var buffer := StreamPeerBuffer.new()
|
var buffer := StreamPeerBuffer.new()
|
||||||
buffer.data_array = data
|
buffer.data_array = data
|
||||||
|
|
||||||
@ -24,19 +24,29 @@ func handle_packet(data: PackedByteArray, _peer: PacketPeer):
|
|||||||
var player := spawn_player(player_id)
|
var player := spawn_player(player_id)
|
||||||
player.position = server_position
|
player.position = server_position
|
||||||
player.set_character_name(player_name)
|
player.set_character_name(player_name)
|
||||||
|
Global.players.add(player)
|
||||||
|
|
||||||
func spawn_player(id: String) -> Player:
|
func spawn_player(id: String) -> Player:
|
||||||
var player = player_scene.instantiate()
|
var player: Player
|
||||||
|
|
||||||
|
if Global.players.has(id):
|
||||||
|
player = Global.players.get_player(id)
|
||||||
|
|
||||||
|
if id == Global.account_id:
|
||||||
|
Global.player = player
|
||||||
|
|
||||||
|
return player
|
||||||
|
|
||||||
|
player = player_scene.instantiate()
|
||||||
player.id = id
|
player.id = id
|
||||||
|
|
||||||
if id == Global.account_id:
|
if id == Global.account_id:
|
||||||
Global.player = player
|
|
||||||
player.controller = PlayerController.new()
|
player.controller = PlayerController.new()
|
||||||
|
Global.player = player
|
||||||
main_player_spawned.emit(player)
|
main_player_spawned.emit(player)
|
||||||
else:
|
else:
|
||||||
player.controller = ProxyController.new()
|
player.controller = ProxyController.new()
|
||||||
|
|
||||||
player.controller.character = player
|
player.controller.character = player
|
||||||
player.add_child(player.controller)
|
player.add_child(player.controller)
|
||||||
%Players.add(player)
|
|
||||||
return player
|
return player
|
||||||
|
@ -11,7 +11,7 @@ func on_jump():
|
|||||||
buffer.put_8(PacketHandler.Packet.PLAYER_JUMP)
|
buffer.put_8(PacketHandler.Packet.PLAYER_JUMP)
|
||||||
%Client.send(buffer.data_array)
|
%Client.send(buffer.data_array)
|
||||||
|
|
||||||
func handle_packet(data: PackedByteArray, _peer: PacketPeer):
|
func handle_packet(data: PackedByteArray):
|
||||||
var player_id := data.get_string_from_ascii()
|
var player_id := data.get_string_from_ascii()
|
||||||
var player := Global.players.get_player(player_id)
|
var player := Global.players.get_player(player_id)
|
||||||
player.jump()
|
player.jump()
|
||||||
|
@ -5,7 +5,7 @@ extends PacketHandler
|
|||||||
var last_sent := Time.get_ticks_msec()
|
var last_sent := Time.get_ticks_msec()
|
||||||
var last_sent_position := Vector3.ZERO
|
var last_sent_position := Vector3.ZERO
|
||||||
|
|
||||||
func handle_packet(data: PackedByteArray, _peer: PacketPeer):
|
func handle_packet(data: PackedByteArray):
|
||||||
var buffer := StreamPeerBuffer.new()
|
var buffer := StreamPeerBuffer.new()
|
||||||
buffer.data_array = data
|
buffer.data_array = data
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
extends PacketHandler
|
extends PacketHandler
|
||||||
|
|
||||||
func handle_packet(data: PackedByteArray, _peer: PacketPeer):
|
func handle_packet(data: PackedByteArray):
|
||||||
var player_id := data.get_string_from_ascii()
|
var player_id := data.get_string_from_ascii()
|
||||||
print("[%s] Remove player: %s" % [Global.username, player_id])
|
print("[%s] Remove player: %s" % [Global.username, player_id])
|
||||||
%Players.remove(player_id)
|
Global.players.remove(player_id)
|
@ -13,7 +13,7 @@ func get_handler(index: int) -> PacketHandler:
|
|||||||
func set_handler(index: int, node: PacketHandler):
|
func set_handler(index: int, node: PacketHandler):
|
||||||
handlers[index] = node
|
handlers[index] = node
|
||||||
|
|
||||||
func handle_packet(packet: PackedByteArray, peer: PacketPeer):
|
func handle_packet(packet: PackedByteArray):
|
||||||
var type := packet.decode_u8(0)
|
var type := packet.decode_u8(0)
|
||||||
var handler := get_handler(type)
|
var handler := get_handler(type)
|
||||||
|
|
||||||
@ -21,5 +21,5 @@ func handle_packet(packet: PackedByteArray, peer: PacketPeer):
|
|||||||
push_warning("Unknown packet type %d" % type)
|
push_warning("Unknown packet type %d" % type)
|
||||||
return
|
return
|
||||||
|
|
||||||
handler.handle_packet(packet.slice(1), peer)
|
handler.handle_packet(packet.slice(1))
|
||||||
download += packet.size()
|
download += packet.size()
|
||||||
|
@ -16,5 +16,5 @@ enum Packet {
|
|||||||
|
|
||||||
@export var packet_type: Packet
|
@export var packet_type: Packet
|
||||||
|
|
||||||
func handle_packet(_data: PackedByteArray, _peer: PacketPeer):
|
func handle_packet(_data: PackedByteArray):
|
||||||
pass
|
pass
|
5
client/player/controller/Controller.gd
Normal file
5
client/player/controller/Controller.gd
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
class_name Controller
|
||||||
|
extends Node
|
||||||
|
|
||||||
|
## The character that we're controlling.
|
||||||
|
@export var character: Character
|
@ -1,8 +1,5 @@
|
|||||||
class_name PlayerController
|
class_name PlayerController
|
||||||
extends Node
|
extends Controller
|
||||||
|
|
||||||
## The character that we're controlling.
|
|
||||||
@export var character: Character
|
|
||||||
|
|
||||||
var move: Vector2
|
var move: Vector2
|
||||||
|
|
||||||
|
@ -1,20 +1,19 @@
|
|||||||
class_name ProxyController
|
class_name ProxyController
|
||||||
extends Node
|
extends Controller
|
||||||
|
|
||||||
## The character that we're controlling.
|
|
||||||
@export var character: Character
|
|
||||||
|
|
||||||
|
## The authoritative position on the server.
|
||||||
var server_position: Vector3
|
var server_position: Vector3
|
||||||
|
|
||||||
func _ready():
|
func _ready():
|
||||||
|
print(character.name, ".old: ", server_position)
|
||||||
server_position = character.position
|
server_position = character.position
|
||||||
|
print(character.name, ".new: ", server_position)
|
||||||
|
|
||||||
func _process(_delta):
|
func _process(_delta):
|
||||||
var move := server_position - character.position
|
var move := server_position - character.position
|
||||||
move.y = 0.0
|
move.y = 0.0
|
||||||
|
|
||||||
if move.length_squared() < 0.01:
|
if move.length_squared() < 0.01:
|
||||||
# character.position = server_position
|
|
||||||
character.direction = Vector3.ZERO
|
character.direction = Vector3.ZERO
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -7,9 +7,13 @@ var ping_changed: Signal
|
|||||||
var download_changed: Signal
|
var download_changed: Signal
|
||||||
var upload_changed: Signal
|
var upload_changed: Signal
|
||||||
var message_received: Signal
|
var message_received: Signal
|
||||||
|
var login: Signal
|
||||||
|
var logout: Signal
|
||||||
|
|
||||||
func _enter_tree():
|
func _enter_tree():
|
||||||
ping_changed = %Ping.changed
|
ping_changed = %Ping.changed
|
||||||
download_changed = %Client.download_changed
|
download_changed = %Client.download_changed
|
||||||
upload_changed = %Client.upload_changed
|
upload_changed = %Client.upload_changed
|
||||||
message_received = %Chat.message_received
|
message_received = %Chat.message_received
|
||||||
|
login = %Login.success
|
||||||
|
logout = %Logout.success
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
[gd_scene load_steps=11 format=3 uid="uid://dagn5bf7ou3sd"]
|
[gd_scene load_steps=12 format=3 uid="uid://dagn5bf7ou3sd"]
|
||||||
|
|
||||||
[ext_resource type="PackedScene" uid="uid://cch67vqpsmtej" path="res://ui/debug/DebugLabel.tscn" id="1_7s8uu"]
|
[ext_resource type="PackedScene" uid="uid://cch67vqpsmtej" path="res://ui/debug/DebugLabel.tscn" id="1_7s8uu"]
|
||||||
[ext_resource type="Script" path="res://ui/UI.gd" id="1_l5b6o"]
|
[ext_resource type="Script" path="res://ui/UI.gd" id="1_l5b6o"]
|
||||||
@ -10,6 +10,7 @@
|
|||||||
[ext_resource type="Script" path="res://ui/chat/ChatInput.gd" id="6_cg2h5"]
|
[ext_resource type="Script" path="res://ui/chat/ChatInput.gd" id="6_cg2h5"]
|
||||||
[ext_resource type="Script" path="res://ui/debug/UploadLabel.gd" id="7_cfnpx"]
|
[ext_resource type="Script" path="res://ui/debug/UploadLabel.gd" id="7_cfnpx"]
|
||||||
[ext_resource type="Script" path="res://ui/debug/DownloadLabel.gd" id="8_ogt38"]
|
[ext_resource type="Script" path="res://ui/debug/DownloadLabel.gd" id="8_ogt38"]
|
||||||
|
[ext_resource type="Script" path="res://ui/connect/ConnectPanel.gd" id="11_cwl0t"]
|
||||||
|
|
||||||
[node name="UI" type="Control"]
|
[node name="UI" type="Control"]
|
||||||
layout_mode = 3
|
layout_mode = 3
|
||||||
@ -102,6 +103,29 @@ layout_mode = 2
|
|||||||
alignment = 2
|
alignment = 2
|
||||||
script = ExtResource("5_8lm6a")
|
script = ExtResource("5_8lm6a")
|
||||||
|
|
||||||
|
[node name="ConnectPanel" type="Panel" parent="CanvasLayer"]
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
script = ExtResource("11_cwl0t")
|
||||||
|
|
||||||
|
[node name="ConnectLabel" type="Label" parent="CanvasLayer/ConnectPanel"]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 8
|
||||||
|
anchor_left = 0.5
|
||||||
|
anchor_top = 0.5
|
||||||
|
anchor_right = 0.5
|
||||||
|
anchor_bottom = 0.5
|
||||||
|
offset_left = -47.5
|
||||||
|
offset_top = -10.0
|
||||||
|
offset_right = 47.5
|
||||||
|
offset_bottom = 10.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
text = "Connecting..."
|
||||||
|
|
||||||
[connection signal="focus_entered" from="CanvasLayer/BottomLeft/Chat/ChatInput" to="CanvasLayer/BottomLeft/Chat/ChatInput" method="_on_focus_entered"]
|
[connection signal="focus_entered" from="CanvasLayer/BottomLeft/Chat/ChatInput" to="CanvasLayer/BottomLeft/Chat/ChatInput" method="_on_focus_entered"]
|
||||||
[connection signal="focus_exited" from="CanvasLayer/BottomLeft/Chat/ChatInput" to="CanvasLayer/BottomLeft/Chat/ChatInput" method="_on_focus_exited"]
|
[connection signal="focus_exited" from="CanvasLayer/BottomLeft/Chat/ChatInput" to="CanvasLayer/BottomLeft/Chat/ChatInput" method="_on_focus_exited"]
|
||||||
[connection signal="text_submitted" from="CanvasLayer/BottomLeft/Chat/ChatInput" to="CanvasLayer/BottomLeft/Chat/ChatInput" method="_on_text_submitted"]
|
[connection signal="text_submitted" from="CanvasLayer/BottomLeft/Chat/ChatInput" to="CanvasLayer/BottomLeft/Chat/ChatInput" method="_on_text_submitted"]
|
||||||
|
11
client/ui/connect/ConnectPanel.gd
Normal file
11
client/ui/connect/ConnectPanel.gd
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
extends Panel
|
||||||
|
|
||||||
|
func _ready():
|
||||||
|
owner.login.connect(on_login)
|
||||||
|
owner.logout.connect(on_logout)
|
||||||
|
|
||||||
|
func on_login():
|
||||||
|
hide()
|
||||||
|
|
||||||
|
func on_logout():
|
||||||
|
show()
|
@ -7,12 +7,18 @@ func _ready():
|
|||||||
Global.players = self
|
Global.players = self
|
||||||
|
|
||||||
func add(player: Player):
|
func add(player: Player):
|
||||||
|
if has(player.id):
|
||||||
|
return
|
||||||
|
|
||||||
add_child(player)
|
add_child(player)
|
||||||
players[player.id] = player
|
players[player.id] = player
|
||||||
|
|
||||||
func get_player(id: String) -> Player:
|
func get_player(id: String) -> Player:
|
||||||
return players[id] as Player
|
return players[id] as Player
|
||||||
|
|
||||||
|
func has(id: String) -> bool:
|
||||||
|
return players.has(id)
|
||||||
|
|
||||||
func remove(id: String):
|
func remove(id: String):
|
||||||
if !players.has(id):
|
if !players.has(id):
|
||||||
return
|
return
|
||||||
|
@ -2,6 +2,7 @@ package game
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Chat is used for chat messages.
|
// Chat is used for chat messages.
|
||||||
@ -12,9 +13,30 @@ func (game *Game) Chat(data []byte, address *net.UDPAddr) error {
|
|||||||
return ErrUnknownAddress
|
return ErrUnknownAddress
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message := string(data)
|
||||||
|
|
||||||
|
if game.ChatCommand(player, message) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
newData := []byte{}
|
newData := []byte{}
|
||||||
newData = AppendString(newData, player.ID)
|
newData = AppendString(newData, player.ID)
|
||||||
newData = AppendStringBytes(newData, data)
|
newData = AppendString(newData, message)
|
||||||
game.Broadcast(Chat, newData)
|
game.Broadcast(Chat, newData)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ChatCommand executes chat commands and returns true if it was one.
|
||||||
|
func (game *Game) ChatCommand(player *Player, message string) bool {
|
||||||
|
if strings.HasPrefix(message, "/") {
|
||||||
|
switch message {
|
||||||
|
case "/logout":
|
||||||
|
game.server.Send(Logout, nil, player.address)
|
||||||
|
game.players.Remove(player)
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
@ -10,7 +10,7 @@ import (
|
|||||||
// Game represents the entire state of the game server.
|
// Game represents the entire state of the game server.
|
||||||
type Game struct {
|
type Game struct {
|
||||||
server *Server
|
server *Server
|
||||||
router Router
|
router *Router
|
||||||
players *PlayerManager
|
players *PlayerManager
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -21,19 +21,26 @@ func New() *Game {
|
|||||||
players: NewPlayerManager(),
|
players: NewPlayerManager(),
|
||||||
}
|
}
|
||||||
|
|
||||||
game.router.Get(Ping, game.Ping)
|
game.router = NewRouter(game)
|
||||||
game.router.Get(Login, game.Login)
|
|
||||||
game.router.Get(Move, game.Move)
|
|
||||||
game.router.Get(Jump, game.Jump)
|
|
||||||
game.router.Get(Chat, game.Chat)
|
|
||||||
return game
|
return game
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewRouter creates a new router.
|
||||||
|
func NewRouter(game *Game) *Router {
|
||||||
|
router := &Router{}
|
||||||
|
router.Get(Ping, game.Ping)
|
||||||
|
router.Get(Login, game.Login)
|
||||||
|
router.Get(Move, game.Move)
|
||||||
|
router.Get(Jump, game.Jump)
|
||||||
|
router.Get(Chat, game.Chat)
|
||||||
|
return router
|
||||||
|
}
|
||||||
|
|
||||||
// Run starts all game systems.
|
// Run starts all game systems.
|
||||||
func (game *Game) Run() {
|
func (game *Game) Run() {
|
||||||
physics := time.NewTicker(20 * time.Millisecond)
|
physics := time.NewTicker(20 * time.Millisecond).C
|
||||||
statistics := time.NewTicker(time.Second)
|
statistics := time.NewTicker(time.Second).C
|
||||||
clean := time.NewTicker(time.Second)
|
clean := time.NewTicker(time.Second).C
|
||||||
close := make(chan os.Signal, 1)
|
close := make(chan os.Signal, 1)
|
||||||
signal.Notify(close, os.Interrupt)
|
signal.Notify(close, os.Interrupt)
|
||||||
|
|
||||||
@ -44,16 +51,16 @@ func (game *Game) Run() {
|
|||||||
case p := <-game.server.incoming:
|
case p := <-game.server.incoming:
|
||||||
game.router.handle(p)
|
game.router.handle(p)
|
||||||
|
|
||||||
case <-physics.C:
|
case <-physics:
|
||||||
game.players.Each(func(c *Player) {
|
game.players.Each(func(c *Player) {
|
||||||
c.Tick()
|
c.Tick()
|
||||||
})
|
})
|
||||||
|
|
||||||
case <-statistics.C:
|
case <-statistics:
|
||||||
fmt.Printf("%d players | %d packets\n", game.players.Count(), game.server.PacketCount())
|
fmt.Printf("%d players | %d packets\n", game.players.Count(), game.server.PacketCount())
|
||||||
game.server.ResetPacketCount()
|
game.server.ResetPacketCount()
|
||||||
|
|
||||||
case <-clean.C:
|
case <-clean:
|
||||||
game.players.Clean(5 * time.Second)
|
game.players.Clean(5 * time.Second)
|
||||||
|
|
||||||
case <-close:
|
case <-close:
|
||||||
|
@ -50,21 +50,14 @@ func (game *Game) Login(data []byte, address *net.UDPAddr) error {
|
|||||||
player = game.players.ByAccount(account.ID)
|
player = game.players.ByAccount(account.ID)
|
||||||
|
|
||||||
if player != nil {
|
if player != nil {
|
||||||
game.reconnect(player, address)
|
game.server.Send(Login, []byte{Failure}, address)
|
||||||
} else {
|
return ErrAlreadyLoggedIn
|
||||||
game.connect(account, address)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
game.connect(account, address)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (game *Game) reconnect(player *Player, address *net.UDPAddr) {
|
|
||||||
player.KeepAlive()
|
|
||||||
game.players.ChangeAddress(player, address)
|
|
||||||
game.sendLoginSuccess(player)
|
|
||||||
player.OnConnect()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (game *Game) connect(account *Account, address *net.UDPAddr) {
|
func (game *Game) connect(account *Account, address *net.UDPAddr) {
|
||||||
player := NewPlayer(address, account, game)
|
player := NewPlayer(address, account, game)
|
||||||
player.authToken = createAuthToken()
|
player.authToken = createAuthToken()
|
||||||
|
@ -48,6 +48,7 @@ func (player *Player) State() []byte {
|
|||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OnConnect is executed when a connection has been established.
|
||||||
func (player *Player) OnConnect() {
|
func (player *Player) OnConnect() {
|
||||||
fmt.Printf("%s connected.\n", player.Name)
|
fmt.Printf("%s connected.\n", player.Name)
|
||||||
players := player.game.players
|
players := player.game.players
|
||||||
@ -62,6 +63,7 @@ func (player *Player) OnConnect() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OnDisconnect is executed when the connection has been closed.
|
||||||
func (player *Player) OnDisconnect() {
|
func (player *Player) OnDisconnect() {
|
||||||
fmt.Printf("%s disconnected.\n", player.Name)
|
fmt.Printf("%s disconnected.\n", player.Name)
|
||||||
player.game.BroadcastOthers(PlayerRemove, []byte(player.ID), player)
|
player.game.BroadcastOthers(PlayerRemove, []byte(player.ID), player)
|
||||||
|
@ -2,92 +2,96 @@ package game
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
"sync"
|
|
||||||
"sync/atomic"
|
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// PlayerManager keeps tracks of all players.
|
// PlayerManager keeps tracks of all players.
|
||||||
type PlayerManager struct {
|
type PlayerManager struct {
|
||||||
players sync.Map
|
players map[string]*Player
|
||||||
accounts sync.Map
|
accounts map[string]*Player
|
||||||
count atomic.Int64
|
count int
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewPlayerManager creates a new player manager.
|
// NewPlayerManager creates a new player manager.
|
||||||
func NewPlayerManager() *PlayerManager {
|
func NewPlayerManager() *PlayerManager {
|
||||||
return &PlayerManager{}
|
return &PlayerManager{
|
||||||
}
|
players: map[string]*Player{},
|
||||||
|
accounts: map[string]*Player{},
|
||||||
func (m *PlayerManager) Clean(timeout time.Duration) {
|
}
|
||||||
now := time.Now()
|
|
||||||
|
|
||||||
m.players.Range(func(key, value interface{}) bool {
|
|
||||||
player := value.(*Player)
|
|
||||||
|
|
||||||
if !player.lastPacket.IsZero() && now.After(player.lastPacket.Add(timeout)) {
|
|
||||||
m.players.Delete(key)
|
|
||||||
m.accounts.Delete(player.ID)
|
|
||||||
m.count.Add(-1)
|
|
||||||
player.OnDisconnect()
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add adds a new player.
|
// Add adds a new player.
|
||||||
func (m *PlayerManager) Add(player *Player) {
|
func (m *PlayerManager) Add(player *Player) {
|
||||||
m.players.Store(player.address.String(), player)
|
m.players[player.address.String()] = player
|
||||||
m.accounts.Store(player.ID, player)
|
m.accounts[player.ID] = player
|
||||||
m.count.Add(1)
|
m.count++
|
||||||
player.OnConnect()
|
player.OnConnect()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ChangeAddress changes the address of a player.
|
// ChangeAddress changes the address of a player.
|
||||||
func (m *PlayerManager) ChangeAddress(player *Player, address *net.UDPAddr) {
|
func (m *PlayerManager) ChangeAddress(player *Player, address *net.UDPAddr) {
|
||||||
m.players.Delete(player.address.String())
|
delete(m.players, player.address.String())
|
||||||
player.address = address
|
player.address = address
|
||||||
m.players.Store(player.address.String(), player)
|
m.players[player.address.String()] = player
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean checks for players who haven't responded in the timeout duration and disconnects them.
|
||||||
|
func (m *PlayerManager) Clean(timeout time.Duration) {
|
||||||
|
now := time.Now()
|
||||||
|
|
||||||
|
for _, player := range m.players {
|
||||||
|
if player.lastPacket.IsZero() || now.Before(player.lastPacket.Add(timeout)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
m.Remove(player)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove removes a player.
|
||||||
|
func (m *PlayerManager) Remove(player *Player) {
|
||||||
|
delete(m.players, player.address.String())
|
||||||
|
delete(m.accounts, player.ID)
|
||||||
|
m.count--
|
||||||
|
player.OnDisconnect()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Contains tells you whether the address is already a registered client.
|
// Contains tells you whether the address is already a registered client.
|
||||||
func (m *PlayerManager) Contains(address *net.UDPAddr) bool {
|
func (m *PlayerManager) Contains(address *net.UDPAddr) bool {
|
||||||
_, exists := m.players.Load(address.String())
|
_, exists := m.players[address.String()]
|
||||||
return exists
|
return exists
|
||||||
}
|
}
|
||||||
|
|
||||||
// Count returns the number of clients.
|
// Count returns the number of clients.
|
||||||
func (m *PlayerManager) Count() int {
|
func (m *PlayerManager) Count() int {
|
||||||
return int(m.count.Load())
|
return m.count
|
||||||
}
|
}
|
||||||
|
|
||||||
// Each calls the callback function for each client.
|
// Each calls the callback function for each client.
|
||||||
func (m *PlayerManager) Each(callback func(*Player)) {
|
func (m *PlayerManager) Each(callback func(*Player)) {
|
||||||
m.players.Range(func(key, value any) bool {
|
for _, player := range m.players {
|
||||||
callback(value.(*Player))
|
callback(player)
|
||||||
return true
|
}
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ByAddress returns an existing client for the requested address.
|
// ByAddress returns an existing client for the requested address.
|
||||||
func (m *PlayerManager) ByAddress(address *net.UDPAddr) *Player {
|
func (m *PlayerManager) ByAddress(address *net.UDPAddr) *Player {
|
||||||
obj, exists := m.players.Load(address.String())
|
player, exists := m.players[address.String()]
|
||||||
|
|
||||||
if !exists {
|
if !exists {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return obj.(*Player)
|
return player
|
||||||
}
|
}
|
||||||
|
|
||||||
// ByAccount returns the player with the given account ID.
|
// ByAccount returns the player with the given account ID.
|
||||||
func (m *PlayerManager) ByAccount(id string) *Player {
|
func (m *PlayerManager) ByAccount(id string) *Player {
|
||||||
obj, exists := m.accounts.Load(id)
|
player, exists := m.accounts[id]
|
||||||
|
|
||||||
if !exists {
|
if !exists {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return obj.(*Player)
|
return player
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user