Add COVault109 solo-cell handling
Implement solo-cell logic to suppress transform packet sends and remote proxy representation while the local player is in COVault109, allowing first-time character creation to remain visually single-player. One entry location update is sent so other clients can hold stale proxies before suppression begins. Added F4TSoloCell.h helper for load-order-independent solo cell recognition by editor ID and cached FormID.
This commit is contained in:
@@ -10,6 +10,7 @@ For testing notes, milestone summaries, known issues, and next steps, see [`docs
|
|||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
- `COVault109` solo-cell handling in the plugin so first-time character-creation space suppresses outgoing transform packets and remote proxy representation.
|
||||||
- `compile-papyrus.bat` to compile active Papyrus sources from `creation-kit\scripts\source` into `creation-kit\scripts\compiled` using the Fallout 4 Creation Kit Papyrus compiler, resolving the Fallout 4 install with the same path workflow as `deploy-all.bat`, using saved `.fallout4-path` values without re-prompting during builds, and skipping reference-only `CoSync*.psc` files.
|
- `compile-papyrus.bat` to compile active Papyrus sources from `creation-kit\scripts\source` into `creation-kit\scripts\compiled` using the Fallout 4 Creation Kit Papyrus compiler, resolving the Fallout 4 install with the same path workflow as `deploy-all.bat`, using saved `.fallout4-path` values without re-prompting during builds, and skipping reference-only `CoSync*.psc` files.
|
||||||
- `build-all.bat` now compiles Papyrus scripts before staging the mod package, and still stages the repo build folder for inspection when Papyrus compilation fails.
|
- `build-all.bat` now compiles Papyrus scripts before staging the mod package, and still stages the repo build folder for inspection when Papyrus compilation fails.
|
||||||
- `deploy-all.bat` now resolves the Fallout 4 path, compiles Papyrus scripts, stages the repo build folder, and then deploys from `build\Fallout 4\Data` into the Fallout 4 `Data` folder.
|
- `deploy-all.bat` now resolves the Fallout 4 path, compiles Papyrus scripts, stages the repo build folder, and then deploys from `build\Fallout 4\Data` into the Fallout 4 `Data` folder.
|
||||||
|
|||||||
Binary file not shown.
+23
-15
@@ -37,23 +37,29 @@ both send and receive paths:
|
|||||||
`serverWorldState` to all clients for admin weather (`fw`) and time
|
`serverWorldState` to all clients for admin weather (`fw`) and time
|
||||||
(`set gamehour to HHmm`) commands. Promoted hosts continue with their
|
(`set gamehour to HHmm`) commands. Promoted hosts continue with their
|
||||||
already-synced local time and weather so remaining clients should not see a jump.
|
already-synced local time and weather so remaining clients should not see a jump.
|
||||||
12. On the game-thread update path, the proxy controller reads a copied
|
12. The plugin treats `COVault109` as a solo cell for first-time character
|
||||||
|
creation. On entry it can send one location update so other clients hold any
|
||||||
|
stale proxy, then outgoing transform packets are suppressed and remote proxy
|
||||||
|
representation is skipped while the local player remains inside. Looks and
|
||||||
|
SPECIAL setup therefore stay visually single-player. The relay protocol is
|
||||||
|
unchanged; the rule is enforced client-side by the plugin.
|
||||||
|
13. On the game-thread update path, the proxy controller reads a copied
|
||||||
remote-player snapshot before attempting any actor work. The snapshot is
|
remote-player snapshot before attempting any actor work. The snapshot is
|
||||||
sorted by `playerId`, and only remote players with valid transform data in
|
sorted by `playerId`, and only remote players with valid transform data in
|
||||||
the same cell are eligible for runtime proxy representation.
|
the same cell are eligible for runtime proxy representation.
|
||||||
13. For each eligible remote `playerId`, the controller maintains a
|
14. For each eligible remote `playerId`, the controller maintains a
|
||||||
`remotePlayerId -> runtime proxy slot` mapping. Each slot stores its
|
`remotePlayerId -> runtime proxy slot` mapping. Each slot stores its
|
||||||
`ObjectRefHandle`, lifecycle state, PlaceAtMe-backed spawn/candidate state,
|
`ObjectRefHandle`, lifecycle state, PlaceAtMe-backed spawn/candidate state,
|
||||||
movement smoothing state, and data-only animation debug state. Stage 4 caps runtime
|
movement smoothing state, and data-only animation debug state. Stage 4 caps runtime
|
||||||
proxy slots at four actors for testing.
|
proxy slots at four actors for testing.
|
||||||
14. Runtime proxies are demand-driven. A slot executes the Stage 3.7
|
15. Runtime proxies are demand-driven. A slot executes the Stage 3.7
|
||||||
PlaceAtMe-backed spawn flow only when that remote player needs a same-cell
|
PlaceAtMe-backed spawn flow only when that remote player needs a same-cell
|
||||||
representation, and only one PlaceAtMe spawn attempt may run at a time so
|
representation, and only one PlaceAtMe spawn attempt may run at a time so
|
||||||
candidate isolation remains unambiguous. Stage 4 captures the remote
|
candidate isolation remains unambiguous. Stage 4 captures the remote
|
||||||
player's current valid transform before spawning and moves the isolated
|
player's current valid transform before spawning and moves the isolated
|
||||||
runtime actor to that transform immediately, so normal remote representation
|
runtime actor to that transform immediately, so normal remote representation
|
||||||
does not visually start at the local player offset.
|
does not visually start at the local player offset.
|
||||||
15. Runtime proxies receive a small game-thread neutralization pass when a
|
16. Runtime proxies receive a small game-thread neutralization pass when a
|
||||||
PlaceAtMe-backed actor is promoted, when a reusable slot is reassigned, while
|
PlaceAtMe-backed actor is promoted, when a reusable slot is reassigned, while
|
||||||
active on a throttle, and when moved or kept in holding. The pass only uses
|
active on a throttle, and when moved or kept in holding. The pass only uses
|
||||||
confirmed CommonLibF4 actor APIs/fields to stop combat, clear combat target
|
confirmed CommonLibF4 actor APIs/fields to stop combat, clear combat target
|
||||||
@@ -63,60 +69,60 @@ both send and receive paths:
|
|||||||
alarm, bump, or package movement intent is observed. It does not disable,
|
alarm, bump, or package movement intent is observed. It does not disable,
|
||||||
hide, delete, despawn, alpha-fade, or force animation graph events on the
|
hide, delete, despawn, alpha-fade, or force animation graph events on the
|
||||||
actor.
|
actor.
|
||||||
16. Proxy visual updates are independent per slot. Normal movement is smoothed
|
17. Proxy visual updates are independent per slot. Normal movement is smoothed
|
||||||
toward that player’s latest target, while `cell_change`, `worldspace_change`,
|
toward that player’s latest target, while `cell_change`, `worldspace_change`,
|
||||||
and `teleport` snap directly. Movement and animation-related remote state is
|
and `teleport` snap directly. Movement and animation-related remote state is
|
||||||
still observed and logged only; no visual crouch/sneak, jump, weapon drawn,
|
still observed and logged only; no visual crouch/sneak, jump, weapon drawn,
|
||||||
locomotion, animation graph, or actor-state application is attempted.
|
locomotion, animation graph, or actor-state application is attempted.
|
||||||
17. When a remote player leaves the cell or disconnects, only that player’s
|
18. When a remote player leaves the cell or disconnects, only that player’s
|
||||||
runtime proxy is moved to the hidden holding position inside
|
runtime proxy is moved to the hidden holding position inside
|
||||||
`F4TTestCell01`. Runtime actors are not disabled or deleted. Connected
|
`F4TTestCell01`. Runtime actors are not disabled or deleted. Connected
|
||||||
players who leave the cell keep their slot reserved, while disconnected
|
players who leave the cell keep their slot reserved, while disconnected
|
||||||
held slots become reusable only after the configured grace period. Runtime
|
held slots become reusable only after the configured grace period. Runtime
|
||||||
slots use per-slot holding positions with spacing and a game-thread
|
slots use per-slot holding positions with spacing and a game-thread
|
||||||
correction check so held actors do not stack or drift back into view.
|
correction check so held actors do not stack or drift back into view.
|
||||||
18. The placed fallback reference `F4TProxyRemotePlayer01REF` remains a single
|
19. The placed fallback reference `F4TProxyRemotePlayer01REF` remains a single
|
||||||
backup representation. If runtime spawning fails for one deterministic
|
backup representation. If runtime spawning fails for one deterministic
|
||||||
selected remote player, the fallback can represent that one player only; it
|
selected remote player, the fallback can represent that one player only; it
|
||||||
is not used for multiple remote players.
|
is not used for multiple remote players.
|
||||||
19. As part of the staged runtime proxy actor manager work, the same game-thread
|
20. As part of the staged runtime proxy actor manager work, the same game-thread
|
||||||
proxy controller also logs whether `Fallout4Together_Test.esp` is loaded and
|
proxy controller also logs whether `Fallout4Together_Test.esp` is loaded and
|
||||||
whether the actor base `F4T_RemotePlayerProxy` resolves by editor ID.
|
whether the actor base `F4T_RemotePlayerProxy` resolves by editor ID.
|
||||||
20. Stage 3.1 adds a temporary diagnostic layer around the single runtime proxy:
|
21. Stage 3.1 adds a temporary diagnostic layer around the single runtime proxy:
|
||||||
post-spawn visibility data, loaded-3D pointer checks where safe, console
|
post-spawn visibility data, loaded-3D pointer checks where safe, console
|
||||||
helper logs, active source logs, and throttled post-movement diagnostics.
|
helper logs, active source logs, and throttled post-movement diagnostics.
|
||||||
The diagnostic mode can force the runtime proxy near the local player to
|
The diagnostic mode can force the runtime proxy near the local player to
|
||||||
prove whether the created actor is visibly renderable.
|
prove whether the created actor is visibly renderable.
|
||||||
21. Stage 3.2 adds a temporary near-player visibility hold for that same single
|
22. Stage 3.2 adds a temporary near-player visibility hold for that same single
|
||||||
runtime proxy. During the hold, remote state is still selected and validated,
|
runtime proxy. During the hold, remote state is still selected and validated,
|
||||||
but runtime proxy restore and remote movement are deferred long enough to
|
but runtime proxy restore and remote movement are deferred long enough to
|
||||||
test whether a runtime-created actor ref can load visible 3D while kept near
|
test whether a runtime-created actor ref can load visible 3D while kept near
|
||||||
the local player.
|
the local player.
|
||||||
22. Stage 3.3 adds a temporary vanilla actor-base diagnostic layer. It attempts
|
23. Stage 3.3 adds a temporary vanilla actor-base diagnostic layer. It attempts
|
||||||
to resolve Codsworth as a visible vanilla `TESNPC` candidate, then passes
|
to resolve Codsworth as a visible vanilla `TESNPC` candidate, then passes
|
||||||
that actor base into the same runtime spawn, handle validation, near-player
|
that actor base into the same runtime spawn, handle validation, near-player
|
||||||
hold, and visibility diagnostic path. This isolates custom actor-base issues
|
hold, and visibility diagnostic path. This isolates custom actor-base issues
|
||||||
from runtime-spawn/init issues without changing the final architecture.
|
from runtime-spawn/init issues without changing the final architecture.
|
||||||
23. Stage 3.4 adds a temporary absolute FormID and existing-reference diagnostic
|
24. Stage 3.4 adds a temporary absolute FormID and existing-reference diagnostic
|
||||||
layer. It compares Codsworth base-form runtime spawning against moving the
|
layer. It compares Codsworth base-form runtime spawning against moving the
|
||||||
existing placed Codsworth reference near the local player as a visibility
|
existing placed Codsworth reference near the local player as a visibility
|
||||||
control. This separates runtime actor creation/init problems from vanilla
|
control. This separates runtime actor creation/init problems from vanilla
|
||||||
actor-base or placed-reference movement problems and does not change the
|
actor-base or placed-reference movement problems and does not change the
|
||||||
final architecture.
|
final architecture.
|
||||||
24. Stage 3.5 adds a temporary PlaceAtMe-equivalent diagnostic after manual
|
25. Stage 3.5 adds a temporary PlaceAtMe-equivalent diagnostic after manual
|
||||||
console validation proved `player.placeatme 000179FF 1` can spawn visible
|
console validation proved `player.placeatme 000179FF 1` can spawn visible
|
||||||
Codsworth in `F4TTestCell01`. The plugin executes that console placement path
|
Codsworth in `F4TTestCell01`. The plugin executes that console placement path
|
||||||
from the local player, resolves the newly created Codsworth actor near the
|
from the local player, resolves the newly created Codsworth actor near the
|
||||||
player, and holds/logs it separately from the `CreateReferenceAtLocation`
|
player, and holds/logs it separately from the `CreateReferenceAtLocation`
|
||||||
runtime proxy path.
|
runtime proxy path.
|
||||||
25. Stage 3.6 refines that temporary PlaceAtMe diagnostic layer. It snapshots
|
26. Stage 3.6 refines that temporary PlaceAtMe diagnostic layer. It snapshots
|
||||||
known Codsworth refs before executing `player.placeatme 000179FF 1`, accepts
|
known Codsworth refs before executing `player.placeatme 000179FF 1`, accepts
|
||||||
only a newly isolated Codsworth ref for the primary diagnostic, and preserves
|
only a newly isolated Codsworth ref for the primary diagnostic, and preserves
|
||||||
that actor at the original console-spawned location during a short settle
|
that actor at the original console-spawned location during a short settle
|
||||||
window. This tests whether the actor becomes visible naturally or requires a
|
window. This tests whether the actor becomes visible naturally or requires a
|
||||||
render/process refresh such as the manually observed pause-menu or alt-tab
|
render/process refresh such as the manually observed pause-menu or alt-tab
|
||||||
behavior. Stage 3.6 does not change the final architecture.
|
behavior. Stage 3.6 does not change the final architecture.
|
||||||
26. Stage 3.7 promotes the working PlaceAtMe-style spawn pattern into the
|
27. Stage 3.7 promotes the working PlaceAtMe-style spawn pattern into the
|
||||||
preferred single runtime proxy spawn path. The proxy controller uses the
|
preferred single runtime proxy spawn path. The proxy controller uses the
|
||||||
resolved runtime FormID for `F4T_RemotePlayerProxy` when available, falls
|
resolved runtime FormID for `F4T_RemotePlayerProxy` when available, falls
|
||||||
back to Codsworth only for diagnostics, defers active proxy selection during
|
back to Codsworth only for diagnostics, defers active proxy selection during
|
||||||
@@ -155,6 +161,8 @@ The native plugin is responsible for:
|
|||||||
- Loading into Fallout 4
|
- Loading into Fallout 4
|
||||||
- Reading local player state
|
- Reading local player state
|
||||||
- Sending local player data to the server
|
- Sending local player data to the server
|
||||||
|
- Suppressing local transform sends and remote proxy representation while the
|
||||||
|
local player is in solo cells such as `COVault109`
|
||||||
- Adding basic data-only movement state to transform packets
|
- Adding basic data-only movement state to transform packets
|
||||||
- Throttling normal transform sends separately from readable local movement logs
|
- Throttling normal transform sends separately from readable local movement logs
|
||||||
- Receiving server packets on a background thread
|
- Receiving server packets on a background thread
|
||||||
|
|||||||
@@ -9,6 +9,41 @@ failed experiments, successful tests, and next steps.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 2026-06-29 - `COVault109` Solo Cell
|
||||||
|
|
||||||
|
### Summary
|
||||||
|
Made the first-time `COVault109` starting cell single-player from the multiplayer sync perspective so character customization and SPECIAL setup are not disturbed by remote player proxies.
|
||||||
|
|
||||||
|
### Files Changed
|
||||||
|
- `plugin/include/F4TSoloCell.h`
|
||||||
|
- `plugin/src/main.cpp`
|
||||||
|
- `plugin/src/F4TProxyActorController.cpp`
|
||||||
|
- `changelog.md`
|
||||||
|
- `docs/dev-log.md`
|
||||||
|
- `docs/architecture.md`
|
||||||
|
- `protocol/player-sync.md`
|
||||||
|
|
||||||
|
### Details
|
||||||
|
- Added a shared solo-cell helper that recognizes `COVault109` by editor ID and cached runtime FormID, avoiding load-order-dependent hardcoded IDs.
|
||||||
|
- Sends one entry location update for `COVault109`, then suppresses outgoing local transform packets while the local player remains inside so other clients can hold stale proxies and stop receiving fresh position updates for a player in character creation.
|
||||||
|
- Suppressed remote proxy representation while the local player is inside `COVault109`; existing proxy slots are held instead of being moved or restored in the starting cell.
|
||||||
|
- Kept the Python relay and transform packet structure unchanged.
|
||||||
|
|
||||||
|
### Testing
|
||||||
|
- Not run in-game in this session.
|
||||||
|
- Expected result: two connected clients can both be in `COVault109` without seeing runtime proxies in that cell.
|
||||||
|
- Expected result: leaving `COVault109` resumes transform sending and normal same-cell proxy behavior outside the solo cell.
|
||||||
|
|
||||||
|
### Known Issues
|
||||||
|
- The solo-cell list is currently code-defined and contains only `COVault109`.
|
||||||
|
|
||||||
|
### Next Steps
|
||||||
|
- Run two Fallout 4 instances through first-time character creation and confirm no proxy actors appear in `COVault109`.
|
||||||
|
- Check `CommonwealthOnline.log` for solo-cell suppression/resume messages.
|
||||||
|
- Verify normal transform and proxy sync still works after leaving `COVault109`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 2026-06-28 - Papyrus Build Integration
|
## 2026-06-28 - Papyrus Build Integration
|
||||||
|
|
||||||
### Summary
|
### Summary
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <optional>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
namespace F4T::SoloCell
|
||||||
|
{
|
||||||
|
inline constexpr auto kVault109EditorId = "COVault109";
|
||||||
|
|
||||||
|
inline std::optional<std::uint32_t>& GetVault109CellIdCache()
|
||||||
|
{
|
||||||
|
static std::optional<std::uint32_t> s_vault109CellId;
|
||||||
|
return s_vault109CellId;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool HasEditorId(const RE::TESObjectCELL& a_cell, std::string_view a_editorId)
|
||||||
|
{
|
||||||
|
const auto* cellEditorId = a_cell.GetFormEditorID();
|
||||||
|
return cellEditorId && a_editorId == cellEditorId;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool IsSoloCell(const RE::TESObjectCELL& a_cell)
|
||||||
|
{
|
||||||
|
if (HasEditorId(a_cell, kVault109EditorId)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& vault109CellId = GetVault109CellIdCache();
|
||||||
|
if (!vault109CellId) {
|
||||||
|
const auto* vault109Cell = RE::TESForm::GetFormByEditorID<RE::TESObjectCELL>(
|
||||||
|
RE::BSFixedString{ kVault109EditorId });
|
||||||
|
if (vault109Cell) {
|
||||||
|
vault109CellId = vault109Cell->GetFormID();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return vault109CellId && a_cell.GetFormID() == *vault109CellId;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,6 +9,7 @@
|
|||||||
#include "F4RemoteActionComponent.h"
|
#include "F4RemoteActionComponent.h"
|
||||||
#include "F4TInterpolationSystem.h"
|
#include "F4TInterpolationSystem.h"
|
||||||
#include "F4TProxyGameAPI.h"
|
#include "F4TProxyGameAPI.h"
|
||||||
|
#include "F4TSoloCell.h"
|
||||||
|
|
||||||
#include "RE/A/ACTOR_STANCE.h"
|
#include "RE/A/ACTOR_STANCE.h"
|
||||||
#include "RE/A/ActorValue.h"
|
#include "RE/A/ActorValue.h"
|
||||||
@@ -1700,6 +1701,13 @@ namespace
|
|||||||
const RE::PlayerCharacter& a_player,
|
const RE::PlayerCharacter& a_player,
|
||||||
const RE::TESObjectCELL& a_parentCell)
|
const RE::TESObjectCELL& a_parentCell)
|
||||||
{
|
{
|
||||||
|
if (F4T::SoloCell::IsSoloCell(a_parentCell)) {
|
||||||
|
LogThrottledInfo(
|
||||||
|
"solo_cell_proxy_suppressed",
|
||||||
|
"COVault109 is a solo cell; remote proxy representation is suppressed while local player is inside.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
const auto remoteCellId = ParseHexFormId(a_remotePlayer.cellId);
|
const auto remoteCellId = ParseHexFormId(a_remotePlayer.cellId);
|
||||||
if (!remoteCellId) {
|
if (!remoteCellId) {
|
||||||
LogThrottledWarning(
|
LogThrottledWarning(
|
||||||
|
|||||||
+37
-1
@@ -6,6 +6,7 @@
|
|||||||
#include "F4TProxyActorController.h"
|
#include "F4TProxyActorController.h"
|
||||||
#include "F4TProxyAnimationSync.h"
|
#include "F4TProxyAnimationSync.h"
|
||||||
#include "F4TProxyPuppet.h"
|
#include "F4TProxyPuppet.h"
|
||||||
|
#include "F4TSoloCell.h"
|
||||||
#include "F4TWorldStateSync.h"
|
#include "F4TWorldStateSync.h"
|
||||||
|
|
||||||
#include "RE/A/ACTOR_STANCE.h"
|
#include "RE/A/ACTOR_STANCE.h"
|
||||||
@@ -34,6 +35,7 @@ namespace
|
|||||||
std::uint32_t cellId;
|
std::uint32_t cellId;
|
||||||
bool hasWorldspaceId;
|
bool hasWorldspaceId;
|
||||||
std::uint32_t worldspaceId;
|
std::uint32_t worldspaceId;
|
||||||
|
bool isSoloCell;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct PlayerMovementState
|
struct PlayerMovementState
|
||||||
@@ -133,13 +135,15 @@ namespace
|
|||||||
false,
|
false,
|
||||||
0,
|
0,
|
||||||
false,
|
false,
|
||||||
0
|
0,
|
||||||
|
false
|
||||||
};
|
};
|
||||||
|
|
||||||
const auto* parentCell = a_player.GetParentCell();
|
const auto* parentCell = a_player.GetParentCell();
|
||||||
if (parentCell) {
|
if (parentCell) {
|
||||||
location.hasCellId = true;
|
location.hasCellId = true;
|
||||||
location.cellId = parentCell->GetFormID();
|
location.cellId = parentCell->GetFormID();
|
||||||
|
location.isSoloCell = F4T::SoloCell::IsSoloCell(*parentCell);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (a_player.cachedWorldspace) {
|
if (a_player.cachedWorldspace) {
|
||||||
@@ -480,6 +484,7 @@ namespace
|
|||||||
static auto lastDebugSampleTime = std::chrono::steady_clock::time_point{};
|
static auto lastDebugSampleTime = std::chrono::steady_clock::time_point{};
|
||||||
static auto debugMovingHoldUntil = std::chrono::steady_clock::time_point{};
|
static auto debugMovingHoldUntil = std::chrono::steady_clock::time_point{};
|
||||||
static auto networkMovingHoldUntil = std::chrono::steady_clock::time_point{};
|
static auto networkMovingHoldUntil = std::chrono::steady_clock::time_point{};
|
||||||
|
static bool soloCellTransformSuppressed = false;
|
||||||
|
|
||||||
const auto currentTransform = GetPlayerTransform(*player);
|
const auto currentTransform = GetPlayerTransform(*player);
|
||||||
const auto currentLocation = GetPlayerLocation(*player);
|
const auto currentLocation = GetPlayerLocation(*player);
|
||||||
@@ -501,6 +506,29 @@ namespace
|
|||||||
debugMovementState.weaponDrawn,
|
debugMovementState.weaponDrawn,
|
||||||
debugMovementState.movementSpeed,
|
debugMovementState.movementSpeed,
|
||||||
currentTime);
|
currentTime);
|
||||||
|
|
||||||
|
if (currentLocation.isSoloCell && soloCellTransformSuppressed) {
|
||||||
|
lastSentTransform = currentTransform;
|
||||||
|
lastDebugSampleTransform = currentTransform;
|
||||||
|
lastKnownLocation = currentLocation;
|
||||||
|
lastSentMovementState = debugMovementState;
|
||||||
|
lastSendTime = currentTime;
|
||||||
|
lastLogTime = currentTime;
|
||||||
|
lastDebugSampleTime = currentTime;
|
||||||
|
hasLastTransform = true;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentLocation.isSoloCell) {
|
||||||
|
LogInfoWithLocalPlayerPrefix("COVault109 is a solo cell; sending entry location update before suppressing further transforms.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (soloCellTransformSuppressed) {
|
||||||
|
LogInfoWithLocalPlayerPrefix("Local player left solo cell; transform packet sending resumed.");
|
||||||
|
soloCellTransformSuppressed = false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!hasLastTransform) {
|
if (!hasLastTransform) {
|
||||||
lastSentTransform = currentTransform;
|
lastSentTransform = currentTransform;
|
||||||
lastDebugSampleTransform = currentTransform;
|
lastDebugSampleTransform = currentTransform;
|
||||||
@@ -538,6 +566,9 @@ namespace
|
|||||||
std::format(", worldspaceId={:08X}", currentLocation.worldspaceId) :
|
std::format(", worldspaceId={:08X}", currentLocation.worldspaceId) :
|
||||||
std::string{}));
|
std::string{}));
|
||||||
}
|
}
|
||||||
|
if (currentLocation.isSoloCell) {
|
||||||
|
soloCellTransformSuppressed = true;
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -627,6 +658,11 @@ namespace
|
|||||||
LogPlayerTransform(currentTransform);
|
LogPlayerTransform(currentTransform);
|
||||||
lastLogTime = currentTime;
|
lastLogTime = currentTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentLocation.isSoloCell) {
|
||||||
|
soloCellTransformSuppressed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
lastDebugSampleTransform = currentTransform;
|
lastDebugSampleTransform = currentTransform;
|
||||||
|
|||||||
@@ -31,6 +31,13 @@ Fallout 4 actors or references. If a remote player is not in the same
|
|||||||
cell/worldspace, the controller holds that player's proxy at a hidden in-cell
|
cell/worldspace, the controller holds that player's proxy at a hidden in-cell
|
||||||
position instead of leaving it at the last visible location.
|
position instead of leaving it at the last visible location.
|
||||||
|
|
||||||
|
`COVault109` is treated as a solo cell for the first-time character-creation
|
||||||
|
flow. On entry the plugin can send one location update so other clients hold any
|
||||||
|
stale proxy, then it suppresses outgoing transform packets while the local
|
||||||
|
player remains inside and the proxy controller skips remote representation. This
|
||||||
|
is a client-side visibility/isolation rule and does not change the transform
|
||||||
|
packet schema.
|
||||||
|
|
||||||
## Current Remote Player State Model
|
## Current Remote Player State Model
|
||||||
|
|
||||||
The plugin and fake client store remote players by `playerId`.
|
The plugin and fake client store remote players by `playerId`.
|
||||||
@@ -97,6 +104,8 @@ player exists.
|
|||||||
resuming smoothing.
|
resuming smoothing.
|
||||||
- Transform packets include movement state data (`isMoving`, `movementSpeed`,
|
- Transform packets include movement state data (`isMoving`, `movementSpeed`,
|
||||||
`isSprinting`, `isSneaking`, `isJumping`, `weaponDrawn`).
|
`isSprinting`, `isSneaking`, `isJumping`, `weaponDrawn`).
|
||||||
|
- The plugin suppresses transform sends and proxy representation in the
|
||||||
|
`COVault109` solo cell.
|
||||||
- The game-thread proxy controller applies confirmed Havok animation graph
|
- The game-thread proxy controller applies confirmed Havok animation graph
|
||||||
variables to runtime proxy actors from that remote state (locomotion, sneak,
|
variables to runtime proxy actors from that remote state (locomotion, sneak,
|
||||||
jump, weapon drawn). Transform position/heading sync is unchanged.
|
jump, weapon drawn). Transform position/heading sync is unchanged.
|
||||||
|
|||||||
Reference in New Issue
Block a user