diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 9c7c828..885d13b 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -4,8 +4,16 @@ name: .NET on: push: branches: [main] + paths: + - "NeonSprawl.sln" + - "server/**" + - ".github/workflows/dotnet.yml" pull_request: branches: [main] + paths: + - "NeonSprawl.sln" + - "server/**" + - ".github/workflows/dotnet.yml" jobs: build: diff --git a/.github/workflows/gdscript.yml b/.github/workflows/gdscript.yml new file mode 100644 index 0000000..f8a7ab3 --- /dev/null +++ b/.github/workflows/gdscript.yml @@ -0,0 +1,33 @@ +# Lint and format-check GDScript (Godot 4). Uses gdtoolkit: https://github.com/Scony/godot-gdscript-toolkit +name: GDScript + +on: + push: + branches: [main] + paths: + - "client/**/*.gd" + - ".github/workflows/gdscript.yml" + pull_request: + branches: [main] + paths: + - "client/**/*.gd" + - ".github/workflows/gdscript.yml" + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: "3.x" + + - name: Install gdtoolkit + run: pip install "gdtoolkit==4.5.0" + + - name: gdlint + run: gdlint client/ + + - name: gdformat (check only) + run: gdformat --check client/ diff --git a/client/scripts/ground_pick.gd b/client/scripts/ground_pick.gd index c388777..fd2b819 100644 --- a/client/scripts/ground_pick.gd +++ b/client/scripts/ground_pick.gd @@ -5,7 +5,8 @@ extends Node3D signal target_chosen(world: Vector3) -## Fallback when `Viewport.get_camera_3d()` is null (e.g. some editor setups). Set from `main.gd` in `_ready`. +## Fallback when `Viewport.get_camera_3d()` is null (e.g. some editor setups). Set from `main.gd` +## in `_ready`. var fallback_camera: Camera3D diff --git a/client/scripts/main.gd b/client/scripts/main.gd index 3f28654..341f974 100644 --- a/client/scripts/main.gd +++ b/client/scripts/main.gd @@ -1,6 +1,7 @@ extends Node3D -## NS-16: composes ground pick + server authority; see `ground_pick.gd` / `position_authority_client.gd`. +## NS-16: composes ground pick + server authority; see `ground_pick.gd` / +## `position_authority_client.gd`. @onready var _camera: Camera3D = $World/Camera3D @onready var _player: CharacterBody3D = $Player @@ -11,7 +12,9 @@ extends Node3D func _ready() -> void: _ground_pick.set("fallback_camera", _camera) _ground_pick.connect("target_chosen", Callable(self, "_on_target_chosen")) - _authority.connect("authoritative_position_received", Callable(self, "_on_authoritative_position")) + _authority.connect( + "authoritative_position_received", Callable(self, "_on_authoritative_position") + ) _authority.call("sync_from_server") diff --git a/client/scripts/player.gd b/client/scripts/player.gd index 8a9db56..48edee7 100644 --- a/client/scripts/player.gd +++ b/client/scripts/player.gd @@ -13,13 +13,13 @@ func _ready() -> void: _goal = global_position - func set_move_goal(world_pos: Vector3) -> void: _goal = world_pos _goal.y = global_position.y -## NS-16: snap to authoritative server position; clears velocity so `move_and_slide` does not fight the sync. +## NS-16: snap to authoritative server position; clears velocity so `move_and_slide` does not fight +## the sync. func snap_to_server(world_pos: Vector3) -> void: global_position = world_pos velocity = Vector3.ZERO diff --git a/client/scripts/position_authority_client.gd b/client/scripts/position_authority_client.gd index 085ff46..049e8e3 100644 --- a/client/scripts/position_authority_client.gd +++ b/client/scripts/position_authority_client.gd @@ -1,18 +1,19 @@ extends Node -## NS-16: POST MoveCommand then GET PositionState; emits authoritative world position for the avatar. +## NS-16: POST MoveCommand then GET PositionState; emits authoritative world position for the +## avatar. ## (No `class_name` so headless/CI can load the project before `.godot` import exists.) signal authoritative_position_received(world: Vector3) +enum Phase { BOOT_GET, POST_MOVE, VERIFY_GET } + @export var base_url: String = "http://127.0.0.1:5253" @export var dev_player_id: String = "dev-local-1" var _http: HTTPRequest var _busy: bool = false - -enum _Phase { BOOT_GET, POST_MOVE, VERIFY_GET } -var _phase: _Phase = _Phase.BOOT_GET +var _phase: Phase = Phase.BOOT_GET func _ready() -> void: @@ -25,7 +26,7 @@ func sync_from_server() -> void: if _busy: return _busy = true - _phase = _Phase.BOOT_GET + _phase = Phase.BOOT_GET _request_get() @@ -33,7 +34,7 @@ func submit_move_target(world: Vector3) -> void: if _busy: return _busy = true - _phase = _Phase.POST_MOVE + _phase = Phase.POST_MOVE var url := "%s/game/players/%s/move" % [_base_root(), _player_path_segment()] var payload := { "schemaVersion": 1, @@ -64,7 +65,9 @@ func _request_get() -> void: _busy = false -func _on_request_completed(_result: int, response_code: int, _headers: PackedStringArray, body: PackedByteArray) -> void: +func _on_request_completed( + _result: int, response_code: int, _headers: PackedStringArray, body: PackedByteArray +) -> void: if _result != HTTPRequest.RESULT_SUCCESS: _busy = false return @@ -72,17 +75,17 @@ func _on_request_completed(_result: int, response_code: int, _headers: PackedStr var text := body.get_string_from_utf8() match _phase: - _Phase.BOOT_GET: + Phase.BOOT_GET: _busy = false if response_code == 200: _emit_position_from_response(text) - _Phase.POST_MOVE: + Phase.POST_MOVE: if response_code != 200: _busy = false return - _phase = _Phase.VERIFY_GET + _phase = Phase.VERIFY_GET _request_get() - _Phase.VERIFY_GET: + Phase.VERIFY_GET: _busy = false if response_code == 200: _emit_position_from_response(text)