Archived
1
0
Fork 0

Refactor client

This commit is contained in:
Sebastian Benjamin 2025-05-20 18:24:31 -07:00
parent 265bf61406
commit 7ee2d7b3b8
34 changed files with 255 additions and 139 deletions

View file

@ -1,9 +1,9 @@
[gd_scene load_steps=5 format=3 uid="uid://dsiowq0rnacln"]
[ext_resource type="PackedScene" uid="uid://qvo806pvgbdn" path="res://danmaku!/Player.tscn" id="1_22cjd"]
[ext_resource type="Script" uid="uid://ggkxv1cb1bjk" path="res://danmaku!/scaling.gd" id="1_o1mqp"]
[ext_resource type="Script" uid="uid://cd67rrch5h4t7" path="res://danmaku!/network_manager.gd" id="2_b2dol"]
[ext_resource type="Script" uid="uid://ddc5iqgtyv2ns" path="res://danmaku!/BulletManager.gd" id="4_ubrrh"]
[ext_resource type="PackedScene" uid="uid://qvo806pvgbdn" path="res://danmaku!/Player/Player.tscn" id="1_22cjd"]
[ext_resource type="Script" uid="uid://ggkxv1cb1bjk" path="res://danmaku!/Board/boardsize_worldscaling.gd" id="1_o1mqp"]
[ext_resource type="Script" uid="uid://6ssjul4hjmot" path="res://danmaku!/Board/world.gd" id="2_jxycg"]
[ext_resource type="Script" uid="uid://ddc5iqgtyv2ns" path="res://danmaku!/Board/bullet_path_manager.gd" id="4_ubrrh"]
[node name="Board" type="Control"]
custom_minimum_size = Vector2(607.5, 1080)
@ -15,7 +15,7 @@ size_flags_horizontal = 0
size_flags_vertical = 0
script = ExtResource("1_o1mqp")
[node name="ReferenceRect" type="ReferenceRect" parent="."]
[node name="DebugBoardBorder" type="ReferenceRect" parent="."]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
@ -29,20 +29,22 @@ editor_only = false
[node name="World" type="Node2D" parent="."]
unique_name_in_owner = true
scale = Vector2(6.75, 6.75)
script = ExtResource("2_jxycg")
[node name="BulletPathManager" type="Node2D" parent="World"]
unique_name_in_owner = true
script = ExtResource("4_ubrrh")
[node name="Player" parent="World" instance=ExtResource("1_22cjd")]
unique_name_in_owner = true
[node name="BulletManager" type="Node2D" parent="World"]
unique_name_in_owner = true
script = ExtResource("4_ubrrh")
[node name="UI" type="Control" parent="."]
layout_mode = 1
anchors_preset = 10
anchor_right = 1.0
grow_horizontal = 2
[node name="NetworkManager" type="Node" parent="World" node_paths=PackedStringArray("player")]
unique_name_in_owner = true
script = ExtResource("2_b2dol")
player = NodePath("../Player")
[node name="GrazeLabel" type="RichTextLabel" parent="."]
[node name="GrazeLabel" type="RichTextLabel" parent="UI"]
unique_name_in_owner = true
z_index = 5
layout_mode = 1

View file

@ -1,6 +1,7 @@
[gd_scene load_steps=5 format=3 uid="uid://b1m2pclbncn68"]
[gd_scene load_steps=6 format=3 uid="uid://b1m2pclbncn68"]
[ext_resource type="PackedScene" uid="uid://dsiowq0rnacln" path="res://danmaku!/Board.tscn" id="2_25dpb"]
[ext_resource type="Script" uid="uid://cnon26famcjo8" path="res://danmaku!/Board/board_renderer.gd" id="1_ggtde"]
[ext_resource type="PackedScene" uid="uid://dsiowq0rnacln" path="res://danmaku!/Board/Board.tscn" id="2_25dpb"]
[sub_resource type="ViewportTexture" id="ViewportTexture_0pmy2"]
viewport_path = NodePath("SubViewport")
@ -16,6 +17,7 @@ material = SubResource("StandardMaterial3D_foys1")
size = Vector2(0.563, 1)
[node name="BoardRenderer" type="Node3D"]
script = ExtResource("1_ggtde")
[node name="BoardMesh" type="MeshInstance3D" parent="."]
mesh = SubResource("QuadMesh_txqb1")
@ -26,3 +28,4 @@ handle_input_locally = false
size = Vector2i(607, 1080)
[node name="Board" parent="SubViewport" instance=ExtResource("2_25dpb")]
unique_name_in_owner = true

View file

@ -0,0 +1,4 @@
extends Node3D
func get_world() -> Node2D:
return %Board.get_world()

View file

@ -0,0 +1 @@
uid://cnon26famcjo8

View file

@ -1,6 +1,10 @@
@tool
extends Control
func _process(delta: float) -> void:
# Maps from world coordinate space to the board size
func _process(_delta: float) -> void:
var board_screen_size = self.get_rect()
%World.scale = Vector2(board_screen_size.size.x / Globals.SERVER_SIZE.x, board_screen_size.size.y / Globals.SERVER_SIZE.y)
func get_world() -> Node2D:
return %World

View file

@ -1,21 +1,22 @@
extends Node2D
var bullet_intraframe_mov_delta := 0.0
var predicted_tick := 0
var bullets = []
func _ready() -> void:
child_exiting_tree.connect(_on_child_exiting_tree)
func _process(delta: float) -> void:
func _process(_delta: float) -> void:
bullet_intraframe_mov_delta = LocalTimer.delta_counter / Globals.TICK_TIME
for bullet in bullets:
var prev_pos = bullet.get_current_pos(predicted_tick)
var next_pos = bullet.get_current_pos(predicted_tick + 1)
var prev_pos = bullet.get_current_pos(LocalTimer.predicted_tick)
var next_pos = bullet.get_current_pos(LocalTimer.predicted_tick + 1)
var interpolated_pos = prev_pos.lerp(next_pos, bullet_intraframe_mov_delta)
bullet.position = interpolated_pos
if bullet.beyond_kill_boundary(predicted_tick):
if bullet.beyond_kill_boundary(LocalTimer.predicted_tick):
bullet.queue_free()
func _on_child_exiting_tree(node: DanmakuBullet):

View file

@ -0,0 +1,9 @@
class_name World
extends Node2D
@onready var player = %Player
@onready var ui = %GrazeLabel
@onready var bullet_manager = %BulletPathManager
func set_player_controlled(val: bool):
%Player.controlled = true

View file

@ -0,0 +1 @@
uid://6ssjul4hjmot

View file

@ -1,15 +1,14 @@
[gd_scene load_steps=2 format=3 uid="uid://3a8txh83qfu5"]
[ext_resource type="PackedScene" uid="uid://b1m2pclbncn68" path="res://danmaku!/BoardRenderer.tscn" id="1_pv3ov"]
[ext_resource type="PackedScene" uid="uid://e800nylcbhkx" path="res://danmaku!/Network/network.tscn" id="1_ls1em"]
[node name="Game" type="Node"]
[node name="NetworkManager" type="Node" parent="."]
[node name="Network" parent="." node_paths=PackedStringArray("boards") instance=ExtResource("1_ls1em")]
boards = NodePath("../Boards")
[node name="Boards" type="Node3D" parent="."]
[node name="PlayerBoard" parent="Boards" instance=ExtResource("1_pv3ov")]
[node name="Camera3D" type="Camera3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0.699727)
current = true

View file

@ -0,0 +1,6 @@
[gd_scene load_steps=2 format=3 uid="uid://dji2asg6fknhf"]
[ext_resource type="Script" uid="uid://dum1qusns10yo" path="res://danmaku!/Network/Game Modes/br_server_tick_manager.gd" id="1_h1jsh"]
[node name="BattleRoyaleServerTickManager" type="Node"]
script = ExtResource("1_h1jsh")

View file

@ -0,0 +1,66 @@
extends Node
@export var controlled_world: Node2D
@export var network: Network
func _ready() -> void:
LocalTimer.next_tick.connect(_broadcast)
LocalTimer.start_ticking()
func _broadcast() -> void:
if !controlled_world:
return
var pos = controlled_world.player.position
var json_string = JSON.stringify({"x": pos.x, "y": pos.y})
network.nakama_socket.send_match_state_async(network.current_match_id, 0, json_string)
func _on_match_state(p_state : NakamaRTAPI.MatchData):
if !controlled_world:
return
match p_state.op_code:
2:
var data = JSON.parse_string(p_state.data)
# Set player position if server demands a forced position
if data["forcePlayerPos"]:
controlled_world.player.set_position_data(
Vector2(
float(data["playerHitPos"]["x"]),
float(data["playerHitPos"]["y"])
),
float(data["playerHitPos"]["radius"]),
float(data["playerGrazePos"]["radius"])
)
controlled_world.ui.text = "Graze: " + str(data["graze"])
# Handle player death if there is an ongoing death timer
if int(data["deathTimer"]) > 0:
controlled_world.player.kill()
elif int(data["deathTimer"]) == 0:
controlled_world.player.resurrect()
# Spawn new bullets
for b in data["newBullets"]:
var bullet = DanmakuBullet.new()
bullet.setup_bullet(
int(b["class"]),
int(b["tick"]),
b["x"],
b["y"],
b["radius"],
b["vel_x"],
b["vel_y"])
bullet.texture = load("res://test-bullet.png")
bullet.position = bullet.get_current_pos(int(b["tick"]))
# Reimplemented from ScalableSprite2D here atm
var scale_ratio = (b["radius"] * 2) / bullet.texture.get_width()
bullet.scale = Vector2(scale_ratio, scale_ratio)
bullet.z_index = 4
controlled_world.bullet_manager.add_child(bullet)
controlled_world.bullet_manager.bullets.append(bullet)
LocalTimer.predicted_tick = int(b["tick"])
LocalTimer.delta_counter = 0

View file

@ -0,0 +1 @@
uid://dum1qusns10yo

View file

@ -0,0 +1,17 @@
[gd_scene load_steps=4 format=3 uid="uid://e800nylcbhkx"]
[ext_resource type="Script" uid="uid://cy6bf5qqnku6a" path="res://danmaku!/Network/network_state.gd" id="1_vo6kr"]
[ext_resource type="Script" uid="uid://de5tjg0h3aqrn" path="res://danmaku!/Network/session_manager.gd" id="1_wvqc0"]
[ext_resource type="Script" uid="uid://cy8homfyumhf2" path="res://danmaku!/Network/temp_test_match_maker.gd" id="4_ymcg2"]
[node name="Network" type="Node"]
script = ExtResource("1_vo6kr")
[node name="SessionManager" type="Node" parent="." node_paths=PackedStringArray("network")]
unique_name_in_owner = true
script = ExtResource("1_wvqc0")
network = NodePath("..")
[node name="TempTestMatchMaker" type="Node" parent="." node_paths=PackedStringArray("network")]
script = ExtResource("4_ymcg2")
network = NodePath("..")

View file

@ -0,0 +1,9 @@
class_name Network
extends Node
@export var boards: Node3D
var nakama_client: NakamaClient
var nakama_session: NakamaSession
var nakama_socket: NakamaSocket
var current_match_id = ""

View file

@ -0,0 +1 @@
uid://cy6bf5qqnku6a

View file

@ -0,0 +1,24 @@
extends Node
signal on_auth_success
@export var network: Node
func _ready() -> void:
print("Attempting auth.")
if await auth():
print("Oh baby we're ready.")
on_auth_success.emit()
else:
print("That is so sad.")
func auth() -> bool:
network.nakama_client = Nakama.create_client("defaultkey", "127.0.0.1", 7350, "http")
network.nakama_session = await network.nakama_client.authenticate_device_async(OS.get_unique_id())
network.nakama_socket = Nakama.create_socket_from(network.nakama_client)
var connected: NakamaAsyncResult = await network.nakama_socket.connect_async(network.nakama_session)
if connected.is_exception():
print("An error occured when creating nakama socket: %s" % connected)
return false
return true

View file

@ -0,0 +1 @@
uid://de5tjg0h3aqrn

View file

@ -0,0 +1,39 @@
extends Node
@export var network: Node
var br_gamemode_manager = preload("res://danmaku!/Network/Game Modes/battle_royale_server_tick_manager.tscn")
var board = preload("res://danmaku!/Board/BoardRenderer.tscn")
@onready var player_board = board.instantiate()
var player_world: Node2D
func _ready() -> void:
player_world = player_board.get_world()
player_world.set_player_controlled(true)
%SessionManager.on_auth_success.connect(_make_br_match_post_auth_success)
network.boards.add_child(player_board)
func _make_br_match_post_auth_success() -> void:
print("Attempting to create debug match.")
await create_and_join_debug_match()
var tick_manager = br_gamemode_manager.instantiate()
tick_manager.controlled_world = player_world
tick_manager.network = network
network.add_child(tick_manager)
network.nakama_socket.received_match_state.connect(tick_manager._on_match_state)
func create_and_join_debug_match() -> void:
var response: NakamaAPI.ApiRpc = await network.nakama_client.rpc_async(network.nakama_session, "manual_force_create_br_match_rpc")
if response.is_exception():
print("An error occurred when calling manual_force_create_br_match_rpc: %s" % response)
return
var debug_br_match: NakamaRTAPI.Match = await network.nakama_socket.join_match_async(response.payload)
if debug_br_match.is_exception():
print("An error occurred when joining debug BR match: %s" % response)
return
else:
network.current_match_id = response.payload

View file

@ -0,0 +1 @@
uid://cy8homfyumhf2

View file

@ -1,9 +1,9 @@
[gd_scene load_steps=7 format=3 uid="uid://qvo806pvgbdn"]
[ext_resource type="Script" uid="uid://bhwiun72wpk6e" path="res://danmaku!/player.gd" id="1_r7xhp"]
[ext_resource type="Script" uid="uid://bhwiun72wpk6e" path="res://danmaku!/Player/player.gd" id="1_r7xhp"]
[ext_resource type="Texture2D" uid="uid://bs3fntlmlqpt2" path="res://icon.svg" id="2_04s0l"]
[ext_resource type="Texture2D" uid="uid://c3deywcu4du2b" path="res://test-collision.png" id="3_gf44i"]
[ext_resource type="Script" uid="uid://v6jris184o8u" path="res://danmaku!/ScalableSprite2D.gd" id="3_u0x7w"]
[ext_resource type="Script" uid="uid://v6jris184o8u" path="res://danmaku!/Utils/ScalableSprite2D.gd" id="3_u0x7w"]
[ext_resource type="Texture2D" uid="uid://brcxly3s7d1jt" path="res://test-graze.png" id="5_273wv"]
[ext_resource type="AudioStream" uid="uid://c5n7x6q67tp78" path="res://test-death-noise.mp3" id="5_poktv"]

View file

@ -2,6 +2,7 @@ class_name Player
extends Node2D
@export var speed = 80
@export var controlled = false
var velocity := Vector2.ZERO
var hurt_collision: DanmakuCircle = DanmakuCircle.new()
var graze_collision: DanmakuCircle = DanmakuCircle.new()

View file

@ -0,0 +1,6 @@
[gd_scene load_steps=2 format=3 uid="uid://d4dt7b4ewhl7u"]
[ext_resource type="Script" uid="uid://dkv6vrgslqjvx" path="res://danmaku!/Utils/client_timer.gd" id="1_wgxit"]
[node name="LocalTimer" type="Node"]
script = ExtResource("1_wgxit")

View file

@ -0,0 +1,28 @@
extends Node
var predicted_tick := 0
var delta_counter := 0.0
var ticking := false
signal next_tick
func _process(delta) -> void:
if ticking == true:
predict_tick(delta)
func predict_tick(delta) -> void:
delta_counter += delta
if delta_counter >= Globals.TICK_TIME:
predicted_tick += 1
delta_counter -= Globals.TICK_TIME
next_tick.emit()
func set_tick_timer_state(new_tick_val) -> void:
predicted_tick = new_tick_val
delta_counter = 0
func start_ticking():
predicted_tick = 0
delta_counter = 0
ticking = true

View file

@ -0,0 +1 @@
uid://dkv6vrgslqjvx

View file

@ -1,2 +1,3 @@
class_name Globals
const SERVER_SIZE = Vector2(90.0, 160.0)
const TICK_TIME = 0.01666666666

View file

@ -1,110 +0,0 @@
extends Node
var nakama_client: NakamaClient
var nakama_session: NakamaSession
var nakama_socket: NakamaSocket
@export var player: Player
var predicted_tick = 0
var delta_counter = 0
var current_match_id = ""
func _ready() -> void:
print("Attempting auth.")
await attempt_auth()
print("Attempting to create debug match.")
await create_and_join_debug_match()
nakama_socket.received_match_state.connect(self._on_match_state)
func _process(delta: float) -> void:
if current_match_id == "":
return
%BulletManager.bullet_intraframe_mov_delta = predict_tick_and_broadcast(delta)
%BulletManager.predicted_tick = predicted_tick
func _on_match_state(p_state : NakamaRTAPI.MatchData):
match p_state.op_code:
2:
var data = JSON.parse_string(p_state.data)
# Set player position if server demands a forced position
if data["forcePlayerPos"]:
player.set_position_data(
Vector2(
float(data["playerHitPos"]["x"]),
float(data["playerHitPos"]["y"])
),
float(data["playerHitPos"]["radius"]),
float(data["playerGrazePos"]["radius"])
)
%GrazeLabel.text = "Graze: " + str(data["graze"])
# Handle player death if there is an ongoing death timer
if int(data["deathTimer"]) > 0:
%Player.kill()
elif int(data["deathTimer"]) == 0:
%Player.resurrect()
# Spawn new bullets
for b in data["newBullets"]:
var bullet = DanmakuBullet.new()
bullet.setup_bullet(
int(b["class"]),
int(b["tick"]),
b["x"],
b["y"],
b["radius"],
b["vel_x"],
b["vel_y"])
bullet.texture = load("res://test-bullet.png")
bullet.position = bullet.get_current_pos(int(b["tick"]))
# Reimplemented from ScalableSprite2D here atm
var scale_ratio = (b["radius"] * 2) / bullet.texture.get_width()
bullet.scale = Vector2(scale_ratio, scale_ratio)
bullet.z_index = 4
%BulletManager.add_child(bullet)
%BulletManager.bullets.append(bullet)
predicted_tick = int(b["tick"])
delta_counter = 0
func attempt_auth() -> void:
nakama_client = Nakama.create_client("defaultkey", "127.0.0.1", 7350, "http")
nakama_session = await nakama_client.authenticate_device_async(OS.get_unique_id())
nakama_socket = Nakama.create_socket_from(nakama_client)
var connected: NakamaAsyncResult = await nakama_socket.connect_async(nakama_session)
if connected.is_exception():
print("An error occured when creating nakama socket: %s" % connected)
return
print("Oh baby we're ready.")
func create_and_join_debug_match() -> void:
var response: NakamaAPI.ApiRpc = await nakama_client.rpc_async(nakama_session, "manual_force_create_br_match_rpc")
if response.is_exception():
print("An error occurred when calling manual_force_create_br_match_rpc: %s" % response)
return
var debug_br_match: NakamaRTAPI.Match = await nakama_socket.join_match_async(response.payload)
if debug_br_match.is_exception():
print("An error occurred when joining debug BR match: %s" % response)
return
else:
current_match_id = response.payload
func predict_tick_and_broadcast(delta) -> float:
delta_counter += delta
# New tick (60 tick rate), broadcast player inputs
var tick_time = 0.01666666666
if delta_counter >= tick_time:
predicted_tick += 1
delta_counter -= tick_time
var pos = %Player.position
var json_string = JSON.stringify({"x": pos.x, "y": pos.y})
nakama_socket.send_match_state_async(current_match_id, 0, json_string)
return delta_counter / tick_time

View file

@ -1 +0,0 @@
uid://cd67rrch5h4t7

View file

@ -18,6 +18,7 @@ config/icon="res://icon.svg"
[autoload]
Nakama="*res://addons/com.heroiclabs.nakama/Nakama.gd"
LocalTimer="*res://danmaku!/Utils/LocalTimer.tscn"
[display]