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({"utctime": Time.get_unix_time_from_system(), "tick": LocalTimer.predicted_tick, "x": pos.x, "y": pos.y}) print("SEND: ", json_string) 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) print("RECV: ", 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