Add crouch state and fix jump sync
Add isCrouching to network, state and animation systems; enable proxy jump syncing with a jump-hold debounce and update animation descriptor and server tooling. Key changes: - Networking: F4TNetworking.h/cpp: added a_isCrouching, include "isCrouching" in transform packets and parsing, and logging. - Remote state: F4TRemotePlayerState.h: added isCrouching field. - Proxy animation: F4TProxyAnimationSync.*: added crouch fields, enabled jump sync, implemented ApplyJumpHoldState and jump hold timing, wrote crouch/jump bools into descriptors when available, updated event firing and logs. - Animation descriptor: F4AnimationDescriptor.cpp: added isJumping and isCrouching to bool variable list. - Local detection: main.cpp: added isCrouching in movement state, stubbed GetCrouchState(), adjusted jump hold timing and included crouch in outgoing transform calls. - Server/dev tooling: server/dev_server_app.py, fake_client.py, fake_player.py, README.md: added crouch reporting, toggle UI/control and fake client support for crouch, and updated docs/logging. - Docs: docs/dev-log.md updated with notes on jump/crouch fixes and testing. Rationale: ensure remote players can report crouch state and make jump animations transition reliably by debouncing jump state on proxies and writing available graph bools; also provide dev-server controls and fake-client support for testing.
This commit is contained in:
+1
-1
@@ -10,7 +10,7 @@ to every other connected client, and broadcasts disconnect packets when clients
|
||||
disconnect.
|
||||
|
||||
Transform packets may include optional movement-state fields such as
|
||||
`isMoving`, `isSprinting`, `isSneaking`, `isJumping`, `weaponDrawn`, and
|
||||
`isMoving`, `isSprinting`, `isSneaking`, `isJumping`, `isCrouching`, `weaponDrawn`, and
|
||||
`movementSpeed`. The server preserves these fields automatically because it
|
||||
broadcasts the original transform packet after adding server-owned fields.
|
||||
|
||||
|
||||
@@ -202,6 +202,10 @@ class DevServerWindow(QMainWindow):
|
||||
self.toggle_sneak_button.clicked.connect(self._toggle_selected_fake_client_sneak)
|
||||
script_action_layout.addWidget(self.toggle_sneak_button)
|
||||
|
||||
self.toggle_crouch_button = QPushButton("Toggle Crouch")
|
||||
self.toggle_crouch_button.clicked.connect(self._toggle_selected_fake_client_crouch)
|
||||
script_action_layout.addWidget(self.toggle_crouch_button)
|
||||
|
||||
self.leave_cell_button = QPushButton("Leave Cell")
|
||||
self.leave_cell_button.clicked.connect(self._trigger_selected_fake_client_leave_cell)
|
||||
script_action_layout.addWidget(self.leave_cell_button)
|
||||
@@ -327,6 +331,17 @@ class DevServerWindow(QMainWindow):
|
||||
|
||||
self._refresh_fake_clients_table()
|
||||
|
||||
def _toggle_selected_fake_client_crouch(self) -> None:
|
||||
client_key = self._get_selected_fake_client_key_for_control()
|
||||
if client_key is None:
|
||||
return
|
||||
|
||||
if not self.fake_player_manager.toggle_client_crouch(client_key):
|
||||
self._append_log("Select a fake client first.")
|
||||
return
|
||||
|
||||
self._refresh_fake_clients_table()
|
||||
|
||||
def _trigger_selected_fake_client_leave_cell(self) -> None:
|
||||
client_key = self._get_selected_fake_client_key_for_control()
|
||||
if client_key is None:
|
||||
|
||||
@@ -92,6 +92,7 @@ def update_remote_player_state(packet: dict[str, Any]) -> None:
|
||||
"isSprinting": get_optional_bool(packet, "isSprinting"),
|
||||
"isSneaking": get_optional_bool(packet, "isSneaking"),
|
||||
"isJumping": get_optional_bool(packet, "isJumping"),
|
||||
"isCrouching": get_optional_bool(packet, "isCrouching"),
|
||||
"weaponDrawn": get_optional_bool(packet, "weaponDrawn"),
|
||||
"movementSpeed": get_optional_float(packet, "movementSpeed"),
|
||||
"actorStateFlags1": get_optional_uint32(packet, "actorStateFlags1"),
|
||||
@@ -117,7 +118,7 @@ def print_remote_player(player: dict[str, Any]) -> None:
|
||||
"Movement State: "
|
||||
f"moving={player['isMoving']}, sprinting={player['isSprinting']}, "
|
||||
f"sneaking={player['isSneaking']}, jumping={player['isJumping']}, "
|
||||
f"weaponDrawn={player['weaponDrawn']}, speed={player['movementSpeed']:.1f}"
|
||||
f"crouching={player['isCrouching']}, weaponDrawn={player['weaponDrawn']}, speed={player['movementSpeed']:.1f}"
|
||||
),
|
||||
f"Actor State: flags1={player['actorStateFlags1']:08X}, flags2={player['actorStateFlags2']:08X}",
|
||||
f"Cell: {player['cellId']}",
|
||||
@@ -144,7 +145,7 @@ def print_remote_players() -> None:
|
||||
f"angleZ={player['angleZ']:.2f}, movementType={player['movementType']}, "
|
||||
f"moving={player['isMoving']}, sprinting={player['isSprinting']}, "
|
||||
f"sneaking={player['isSneaking']}, jumping={player['isJumping']}, "
|
||||
f"weaponDrawn={player['weaponDrawn']}, speed={player['movementSpeed']:.1f}, "
|
||||
f"crouching={player['isCrouching']}, weaponDrawn={player['weaponDrawn']}, speed={player['movementSpeed']:.1f}, "
|
||||
f"flags1={player['actorStateFlags1']:08X}, flags2={player['actorStateFlags2']:08X}, "
|
||||
f"cell={player['cellId']}, worldspace={player['worldspaceId']}, "
|
||||
f"serverTime={player['serverTime']}"
|
||||
|
||||
@@ -86,6 +86,7 @@ class FakePlayerClient:
|
||||
self.angle_z = IDLE_ANGLE_Z
|
||||
self.is_sneaking = False
|
||||
self.is_jumping = False
|
||||
self.is_crouching = False
|
||||
|
||||
self._log_callback = log_callback
|
||||
self._walk_target_provider = walk_target_provider
|
||||
@@ -176,6 +177,15 @@ class FakePlayerClient:
|
||||
self._log(f"{self.name} toggled sneak: {str(is_sneaking).lower()}.")
|
||||
return is_sneaking
|
||||
|
||||
def toggle_crouch(self) -> bool:
|
||||
with self._lock:
|
||||
self.is_crouching = not self.is_crouching
|
||||
is_crouching = self.is_crouching
|
||||
|
||||
self._script_changed_event.set()
|
||||
self._log(f"{self.name} toggled crouch: {str(is_crouching).lower()}.")
|
||||
return is_crouching
|
||||
|
||||
def trigger_leave_cell(self) -> None:
|
||||
with self._lock:
|
||||
self.script = SCRIPT_LEFT_CELL
|
||||
@@ -498,6 +508,7 @@ class FakePlayerClient:
|
||||
is_moving: bool,
|
||||
movement_speed: float,
|
||||
is_jumping: bool | None = None,
|
||||
is_crouching: bool | None = None,
|
||||
) -> None:
|
||||
with self._lock:
|
||||
connection = self._socket
|
||||
@@ -513,6 +524,7 @@ class FakePlayerClient:
|
||||
is_moving=is_moving,
|
||||
movement_speed=movement_speed,
|
||||
is_jumping=is_jumping,
|
||||
is_crouching=is_crouching,
|
||||
)
|
||||
except OSError as error:
|
||||
if not self._stop_event.is_set():
|
||||
@@ -527,6 +539,7 @@ class FakePlayerClient:
|
||||
movement_speed: float,
|
||||
movement_type: str = "normal",
|
||||
is_jumping: bool | None = None,
|
||||
is_crouching: bool | None = None,
|
||||
) -> None:
|
||||
with self._lock:
|
||||
x, y, z = self.position
|
||||
@@ -535,6 +548,7 @@ class FakePlayerClient:
|
||||
worldspace_id = self.worldspace_id
|
||||
is_sneaking = self.is_sneaking
|
||||
jumping = self.is_jumping if is_jumping is None else is_jumping
|
||||
crouching = self.is_crouching if is_crouching is None else is_crouching
|
||||
|
||||
packet = {
|
||||
"type": "transform",
|
||||
@@ -550,6 +564,7 @@ class FakePlayerClient:
|
||||
"isSprinting": False,
|
||||
"isSneaking": is_sneaking,
|
||||
"isJumping": jumping,
|
||||
"isCrouching": crouching,
|
||||
"weaponDrawn": False,
|
||||
"movementSpeed": movement_speed,
|
||||
}
|
||||
@@ -702,6 +717,14 @@ class FakePlayerManager:
|
||||
client.toggle_sneak()
|
||||
return True
|
||||
|
||||
def toggle_client_crouch(self, client_key: int) -> bool:
|
||||
client = self._get_client(client_key)
|
||||
if client is None:
|
||||
return False
|
||||
|
||||
client.toggle_crouch()
|
||||
return True
|
||||
|
||||
def trigger_client_leave_cell(self, client_key: int) -> bool:
|
||||
client = self._get_client(client_key)
|
||||
if client is None:
|
||||
|
||||
Reference in New Issue
Block a user