Fix GUI weather: valid IDs, defer & reset

Replace invalid Fallout 4 weather form IDs with valid ones (including fixing Clear from 0002852a -> 0002b52a) and update protocol examples and server presets accordingly. Change the plugin to re-queue/defer server weather payloads when the local assigned player ID is not yet available (so early GUI clicks are not dropped) and add a log entry when deferring. Reset player/host assignment on server startup (_next_player_id = 1 and clear _world_state_host_player_id) to avoid stale host IDs after restarts. Also update changelog and developer log entries describing the fixes and recommended manual testing steps.
This commit is contained in:
2026-06-24 12:26:55 +12:00
parent 4c17e70620
commit 1493e68349
6 changed files with 126 additions and 20 deletions
+5
View File
@@ -20,9 +20,14 @@ For testing notes, milestone summaries, known issues, and next steps, see [`docs
### Fixed ### Fixed
- Server GUI weather now runs `fw <8-digit-form-id>` on player 1 only (not editor names), scheduled on the game thread. - Server GUI weather now runs `fw <8-digit-form-id>` on player 1 only (not editor names), scheduled on the game thread.
- Weather `serverWorldState` packets are broadcast to all clients so player 1's game instance always receives them. - Weather `serverWorldState` packets are broadcast to all clients so player 1's game instance always receives them.
- Server restart now resets player/host assignment so the first client is `playerId` 1 again, preventing GUI weather from being ignored after restart.
- Plugin now defers server weather commands until player assignment is available, preventing initial GUI clicks from being dropped right after connect.
- Replaced invalid default GUI weather IDs with valid Fallout 4 weather form IDs so `fw <id>` no longer fails with "Invalid weather ...".
- Corrected `Clear` preset ID typo from `0002852a` to valid `0002b52a` (`CommonwealthClear`).
### Changed ### Changed
- Weather presets store the exact `fw` console argument (8-digit hex); edit `server/world_state_presets.py` only if a preset ID fails in your console. - Weather presets store the exact `fw` console argument (8-digit hex); edit `server/world_state_presets.py` only if a preset ID fails in your console.
- Updated protocol weather examples/default preset table to match the new valid Fallout 4 preset IDs.
- World time no longer re-applies on every `worldState` heartbeat; weather heartbeats continue at ~1 Hz while time stays in sync during normal gameplay. - World time no longer re-applies on every `worldState` heartbeat; weather heartbeats continue at ~1 Hz while time stays in sync during normal gameplay.
- Reformatted `changelog.md` to follow [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). - Reformatted `changelog.md` to follow [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
+86
View File
@@ -9,6 +9,92 @@ failed experiments, successful tests, and next steps.
--- ---
## 2026-06-24 - Replace Invalid GUI Weather IDs
### Summary
Fixed GUI weather presets that were using invalid weather IDs (`00148c..`), which caused Fallout 4 to report `Invalid weather '<id>' for parameter Weather ID.` when running `fw`.
### Files Changed
- `server/world_state_presets.py`
- `protocol/server-world-state.md`
- `changelog.md`
### Details
- Updated default GUI weather presets to valid Fallout 4 weather form IDs:
- Clear: `0002b52a` (corrected from typo `0002852a`)
- Cloudy: `001cc186`
- Overcast: `001c8556`
- Fog: `001c3473`
- Rain: `001ca7e4`
- Radstorm: `001c3d5e`
- Glowing Sea: `000f1033`
- Updated protocol examples/documentation to match these IDs.
### Testing
- Not re-tested in-game in this session.
- User-provided screenshot confirms previous IDs failed with `Invalid weather` in console.
- Recommended: run `fw <each preset id>` manually once, then verify GUI buttons trigger the same IDs.
### Known Issues
- Weather IDs can differ with unusual load-order/mod setups; override presets in `server/world_state_presets.py` if needed.
### Next Steps
- Restart server GUI so it loads updated presets, then test one-click weather changes outdoors.
---
## 2026-06-24 - Reset Player IDs on Server Restart
### Summary
Fixed a restart regression where GUI weather could silently do nothing after server restarts because player IDs and world-state host assignment were not reset between runs.
### Files Changed
- `server/server_core.py`
- `changelog.md`
### Details
- Server startup now resets `_next_player_id` to `1`.
- Server startup now clears `_world_state_host_player_id` so the first newly connected client becomes the host for that run.
- This keeps the plugin's weather gate (`playerId == 1`) aligned after restart.
### Testing
- Not re-tested in-game in this session.
- Recommended: start server, connect client, set weather; stop server; start again; reconnect same client and set weather again.
### Known Issues
- None currently known.
### Next Steps
- Rebuild/reload as needed, then verify `CommonwealthOnline.log` shows weather command execution after both first start and restart.
---
## 2026-06-24 - Defer Weather Until Player Assignment
### Summary
Fixed intermittent GUI weather clicks by deferring weather apply when `serverWorldState` arrives before local player assignment is complete.
### Files Changed
- `plugin/src/F4TWorldStateSync.cpp`
- `changelog.md`
### Details
- If a weather request arrives before `GetAssignedPlayerId()` is available, the plugin now re-queues that server world-state payload instead of discarding it.
- Once assignment completes, player 1 applies the queued weather command normally.
- Non-player-1 clients still ignore weather commands as intended.
### Testing
- Not re-tested in-game in this session.
- Recommended: connect client, immediately click weather once in GUI, confirm the first click now applies without repeated clicks.
### Known Issues
- None currently known.
### Next Steps
- Verify in logs that early weather packets show a one-time "deferred" message followed by weather command execution after assignment.
---
## 2026-06-23 - GUI Weather Uses Numeric `fw` ID ## 2026-06-23 - GUI Weather Uses Numeric `fw` ID
### Summary ### Summary
+14 -4
View File
@@ -268,10 +268,11 @@ namespace
return std::nullopt; return std::nullopt;
} }
bool IsAssignedPlayerOne() void RequeueServerWorldState(F4T::WorldStateSync::PendingServerWorldState a_state)
{ {
const auto assignedPlayerId = F4T::RemotePlayerState::GetAssignedPlayerId(); const std::scoped_lock lock(g_pendingMutex);
return assignedPlayerId && *assignedPlayerId == 1U; g_pendingServerWorldState = std::move(a_state);
g_hasPendingServerWorldState = true;
} }
bool ShouldSendTimeState(float a_gameHour, float a_gameDaysPassed) bool ShouldSendTimeState(float a_gameHour, float a_gameDaysPassed)
@@ -425,7 +426,16 @@ namespace
return; return;
} }
if (!IsAssignedPlayerOne()) { const auto assignedPlayerId = F4T::RemotePlayerState::GetAssignedPlayerId();
if (!assignedPlayerId) {
RequeueServerWorldState(a_state);
if (ShouldLog("server_world_state_weather_defer")) {
LogInfo("Deferred server weather command until local player assignment is available.");
}
return;
}
if (*assignedPlayerId != 1U) {
if (ShouldLog("server_world_state_weather_skip")) { if (ShouldLog("server_world_state_weather_skip")) {
LogInfo("Ignored server weather command on non-player-1 client."); LogInfo("Ignored server weather command on non-player-1 client.");
} }
+9 -9
View File
@@ -24,8 +24,8 @@ Weather (all clients receive; only player 1 acts):
```json ```json
{ {
"type": "serverWorldState", "type": "serverWorldState",
"weatherConsoleArg": "00148c45", "weatherConsoleArg": "001ca7e4",
"weatherFormId": "00148C45", "weatherFormId": "001CA7E4",
"serverTime": 1780212128.301 "serverTime": 1780212128.301
} }
``` ```
@@ -53,13 +53,13 @@ Edit `server/world_state_presets.py` so each preset uses the exact `fw` ID that
| Label | Default `fw` arg | | Label | Default `fw` arg |
|-------|------------------| |-------|------------------|
| Clear | `00148c3c` | | Clear | `0002b52a` |
| Cloudy | `00148c42` | | Cloudy | `001cc186` |
| Overcast | `00148c43` | | Overcast | `001c8556` |
| Fog | `00148c44` | | Fog | `001c3473` |
| Rain | `00148c45` | | Rain | `001ca7e4` |
| Radstorm | `00148c47` | | Radstorm | `001c3d5e` |
| Glowing Sea | `001b9779` | | Glowing Sea | `000f1033` |
## Backward Compatibility ## Backward Compatibility
+5
View File
@@ -205,6 +205,11 @@ class FalloutTogetherServer:
self._server_socket = server_socket self._server_socket = server_socket
self._running = True self._running = True
self._started_at = time.time() self._started_at = time.time()
# Each server run is a fresh session for player/host assignment.
# Without this reset, restarts can leave stale host IDs and player IDs
# > 1, which breaks weather commands gated to player 1.
self._next_player_id = 1
self._world_state_host_player_id = None
self._log(f"Commonwealth Online server listening on {self.host}:{self.port}") self._log(f"Commonwealth Online server listening on {self.host}:{self.port}")
self._log(f"LAN discovery listening on UDP port {DISCOVERY_PORT}") self._log(f"LAN discovery listening on UDP port {DISCOVERY_PORT}")
+7 -7
View File
@@ -6,13 +6,13 @@ from __future__ import annotations
# `fw_console_arg` is passed directly to the in-game `fw` command on player 1 # `fw_console_arg` is passed directly to the in-game `fw` command on player 1
# (8-digit hex form ID, not an editor name like CommonwealthRain). # (8-digit hex form ID, not an editor name like CommonwealthRain).
WEATHER_PRESETS: list[tuple[str, str]] = [ WEATHER_PRESETS: list[tuple[str, str]] = [
("Clear", "00148c3c"), ("Clear", "0002b52a"),
("Cloudy", "00148c42"), ("Cloudy", "001cc186"),
("Overcast", "00148c43"), ("Overcast", "001c8556"),
("Fog", "00148c44"), ("Fog", "001c3473"),
("Rain", "00148c45"), ("Rain", "001ca7e4"),
("Radstorm", "00148c47"), ("Radstorm", "001c3d5e"),
("Glowing Sea", "001b9779"), ("Glowing Sea", "000f1033"),
] ]
# HHmm values for the in-game `set gamehour to HHmm` console command. # HHmm values for the in-game `set gamehour to HHmm` console command.