Add one-shot runtime proxy spawn diagnostic
Introduce a Stage 2 diagnostic that attempts a single runtime spawn of F4T_RemotePlayerProxy in F4TTestCell01 from the game-thread proxy controller. Changes include a new enable flag (kEnableRuntimeProxySpawnDiagnostic), diagnostic offset constant, new diagnostic ObjectRefHandle and boolean state flags, and a TrySpawnRuntimeProxyDiagnostic function that builds NEW_REFR_DATA, calls TESDataHandler::CreateReferenceAtLocation, validates the returned handle/actor/cell, logs a single attempt and success/failure outcome, and intentionally leaves the spawned actor in place. The diagnostic spawn is run before placed-proxy lookup and the spawned reference is skipped during placed proxy scanning so the placed proxy remains the active visual fallback. Updated header comment and documentation (architecture.md and dev-log.md) to describe the new diagnostic behavior and testing notes.
This commit is contained in:
+15
-8
@@ -25,22 +25,26 @@ both send and receive paths:
|
|||||||
9. The server broadcasts `disconnect` packets when clients disconnect.
|
9. The server broadcasts `disconnect` packets when clients disconnect.
|
||||||
10. The plugin and fake client remove disconnected players from their remote
|
10. The plugin and fake client remove disconnected players from their remote
|
||||||
player tables.
|
player tables.
|
||||||
11. On the game-thread update path, the plugin reads a copied remote-player
|
11. On the game-thread update path, the proxy controller performs a one-shot
|
||||||
|
runtime spawn diagnostic in `F4TTestCell01` from the resolved
|
||||||
|
`F4T_RemotePlayerProxy` actor base. This spawned actor is not yet driven
|
||||||
|
from remote-player state.
|
||||||
|
12. On the same game-thread update path, the plugin reads a copied remote-player
|
||||||
snapshot and moves the single placed proxy actor in `F4TTestCell01`.
|
snapshot and moves the single placed proxy actor in `F4TTestCell01`.
|
||||||
Proxy visual updates are independent from local movement log throttling:
|
Proxy visual updates are independent from local movement log throttling:
|
||||||
normal movement is smoothed toward the latest target, while cell changes,
|
normal movement is smoothed toward the latest target, while cell changes,
|
||||||
worldspace changes, and teleports snap directly.
|
worldspace changes, and teleports snap directly.
|
||||||
12. When the selected remote player is missing, disconnected, or not in the same
|
13. When the selected remote player is missing, disconnected, or not in the same
|
||||||
test cell, the game-thread proxy controller moves the single placed proxy to
|
test cell, the game-thread proxy controller moves the single placed proxy to
|
||||||
a hidden holding position inside `F4TTestCell01` until a valid same-cell
|
a hidden holding position inside `F4TTestCell01` until a valid same-cell
|
||||||
remote player is available again.
|
remote player is available again.
|
||||||
13. As the first runtime proxy actor manager stage, the same game-thread proxy
|
14. 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
|
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.
|
||||||
|
|
||||||
The Fallout 4 plugin still does not dynamically spawn remote actors. The actor
|
The Fallout 4 plugin now has a diagnostic-only runtime spawn test, but it still
|
||||||
base lookup is diagnostic-only and keeps the placed proxy reference as the
|
does not dynamically spawn actors for remote player representation. The placed
|
||||||
visual representation path.
|
proxy reference remains the active visual representation path.
|
||||||
|
|
||||||
## Main Components
|
## Main Components
|
||||||
|
|
||||||
@@ -61,11 +65,13 @@ The native plugin is responsible for:
|
|||||||
remote player should be represented
|
remote player should be represented
|
||||||
- Logging staged runtime proxy actor base lookup diagnostics on the same
|
- Logging staged runtime proxy actor base lookup diagnostics on the same
|
||||||
game-thread controller path
|
game-thread controller path
|
||||||
|
- Attempting one game-thread-only runtime spawn diagnostic from
|
||||||
|
`F4T_RemotePlayerProxy` in `F4TTestCell01`
|
||||||
|
|
||||||
Planned later:
|
Planned later:
|
||||||
|
|
||||||
- Runtime spawning one proxy actor from `F4T_RemotePlayerProxy`
|
|
||||||
- Supporting one proxy actor per represented remote `playerId`
|
- Supporting one proxy actor per represented remote `playerId`
|
||||||
|
- Driving runtime-spawned proxy actors from remote-player state
|
||||||
|
|
||||||
### External Server
|
### External Server
|
||||||
|
|
||||||
@@ -117,6 +123,7 @@ Networking thread receives packets
|
|||||||
Networking thread stores remote player state
|
Networking thread stores remote player state
|
||||||
Game update reads a copied remote player snapshot
|
Game update reads a copied remote player snapshot
|
||||||
Game update resolves/logs proxy actor base diagnostics
|
Game update resolves/logs proxy actor base diagnostics
|
||||||
|
Game update attempts one diagnostic runtime actor spawn in F4TTestCell01
|
||||||
Game update moves the placed test proxy actor
|
Game update moves the placed test proxy actor
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -127,7 +134,7 @@ update path.
|
|||||||
|
|
||||||
## Out Of Scope For Current Prototype
|
## Out Of Scope For Current Prototype
|
||||||
|
|
||||||
- Runtime actor spawning
|
- Using runtime-spawned actors for remote player representation
|
||||||
- Multiple proxy actors
|
- Multiple proxy actors
|
||||||
- Animation graph sync or animation application
|
- Animation graph sync or animation application
|
||||||
- Combat sync
|
- Combat sync
|
||||||
|
|||||||
@@ -1232,6 +1232,75 @@ plugin-local form ID on the existing game-thread proxy controller path.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 2026-06-02 - Runtime Proxy Spawning Stage 2
|
||||||
|
|
||||||
|
### Summary
|
||||||
|
|
||||||
|
Added a one-shot diagnostic runtime spawn attempt for `F4T_RemotePlayerProxy` in
|
||||||
|
`F4TTestCell01`. The spawn runs only from the game-thread proxy controller path
|
||||||
|
and remains separate from the placed proxy fallback, which is still the active
|
||||||
|
remote player representation.
|
||||||
|
|
||||||
|
### Files Changed
|
||||||
|
|
||||||
|
- `plugin/src/F4TProxyActorController.cpp`
|
||||||
|
- `plugin/include/F4TProxyActorController.h`
|
||||||
|
- `docs/dev-log.md`
|
||||||
|
- `docs/architecture.md`
|
||||||
|
|
||||||
|
### Details
|
||||||
|
|
||||||
|
- Added `kEnableRuntimeProxySpawnDiagnostic` as the explicit internal gate for
|
||||||
|
the Stage 2 diagnostic.
|
||||||
|
- Added one-shot diagnostic state flags and a separate `RE::ObjectRefHandle` for
|
||||||
|
the runtime-spawned actor.
|
||||||
|
- Reused the Stage 1 `F4T_RemotePlayerProxy` actor-base resolution path.
|
||||||
|
- Calls `RE::TESDataHandler::CreateReferenceAtLocation(RE::NEW_REFR_DATA&)`
|
||||||
|
only after the local player exists, the parent cell exists, the player is in
|
||||||
|
`F4TTestCell01`, and the actor base has resolved.
|
||||||
|
- Fills `NEW_REFR_DATA` for the interior test cell with the proxy actor base as
|
||||||
|
the bound object, the local player's parent cell as `interior`, no worldspace,
|
||||||
|
scripts initialized, and an X offset of `250.0`.
|
||||||
|
- Revalidates the returned handle immediately as a non-player `RE::Actor` in the
|
||||||
|
expected test cell.
|
||||||
|
- Logs a single readable attempt and a single success or failure outcome with
|
||||||
|
actor base, cell, position, handle, spawned reference, actor cast, and parent
|
||||||
|
cell details where available.
|
||||||
|
- Explicitly skips the diagnostic spawned reference during placed fallback
|
||||||
|
base-actor scanning so it is not selected as the active remote proxy.
|
||||||
|
- Leaves the spawned actor in place for the session; cleanup/despawn/hold
|
||||||
|
behavior is a later runtime proxy manager milestone.
|
||||||
|
- Left placed proxy movement, smoothing, special movement snapping, holding
|
||||||
|
lifecycle, sneak-state observation, networking, protocol, Python server code,
|
||||||
|
and fake-client tooling unchanged.
|
||||||
|
|
||||||
|
### Testing
|
||||||
|
|
||||||
|
- Ran `xmake build` from `plugin`; it succeeded.
|
||||||
|
- Pending: launch Fallout 4 through F4SE, load `Fallout4Together_Test.esp`, and
|
||||||
|
`coc F4TTestCell01`.
|
||||||
|
- Pending: confirm `Fallout4Together.log` shows actor-base resolution, one
|
||||||
|
runtime spawn attempt, and either spawn success details or a clear failure
|
||||||
|
reason.
|
||||||
|
- Pending: confirm the new runtime proxy actor appears near the player if spawn
|
||||||
|
succeeds.
|
||||||
|
- Pending: confirm the original placed proxy fallback still represents fake
|
||||||
|
clients and remains the active remote representation.
|
||||||
|
|
||||||
|
### Known Issues
|
||||||
|
|
||||||
|
- In-game runtime spawn behavior is not yet validated.
|
||||||
|
- Runtime-spawned actors are not cleaned up, held, or driven from remote-player
|
||||||
|
state in this stage.
|
||||||
|
|
||||||
|
### Next Steps
|
||||||
|
|
||||||
|
- Run the build and in-game diagnostic validation.
|
||||||
|
- If the diagnostic spawn is stable, plan a later stage for safe runtime proxy
|
||||||
|
lifecycle cleanup/holding before driving spawned proxies from remote state.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Entry Template
|
## Entry Template
|
||||||
|
|
||||||
Use this format for future updates:
|
Use this format for future updates:
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace F4T::ProxyActorController
|
namespace F4T::ProxyActorController
|
||||||
{
|
{
|
||||||
// Game-thread-only actor controller for the single placed proxy test actor.
|
// Game-thread-only actor controller for proxy diagnostics and the active placed proxy test actor.
|
||||||
// Networking threads must only update plain remote-player state.
|
// Networking threads must only update plain remote-player state.
|
||||||
void Update();
|
void Update();
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ namespace
|
|||||||
constexpr RE::TESFormID kProxyBaseActorLocalFormId = 0x0020A1;
|
constexpr RE::TESFormID kProxyBaseActorLocalFormId = 0x0020A1;
|
||||||
constexpr RE::TESFormID kProxyFallbackLocalFormId = 0x0020A2;
|
constexpr RE::TESFormID kProxyFallbackLocalFormId = 0x0020A2;
|
||||||
constexpr auto kProxyOffsetX = 150.0F;
|
constexpr auto kProxyOffsetX = 150.0F;
|
||||||
|
constexpr bool kEnableRuntimeProxySpawnDiagnostic = true;
|
||||||
|
constexpr auto kRuntimeProxySpawnDiagnosticOffsetX = 250.0F;
|
||||||
constexpr float kProxyPositionLerpAlpha = 0.15F;
|
constexpr float kProxyPositionLerpAlpha = 0.15F;
|
||||||
// Temporary single-proxy lifecycle fallback. Keep the placed proxy inside the
|
// Temporary single-proxy lifecycle fallback. Keep the placed proxy inside the
|
||||||
// loaded test cell instead of disabling a persistent reference before that path
|
// loaded test cell instead of disabling a persistent reference before that path
|
||||||
@@ -67,10 +69,15 @@ namespace
|
|||||||
constexpr auto kProxyMovementMode = ProxyMovementMode::kRemotePlayerStateTest;
|
constexpr auto kProxyMovementMode = ProxyMovementMode::kRemotePlayerStateTest;
|
||||||
|
|
||||||
RE::ObjectRefHandle g_proxyHandle;
|
RE::ObjectRefHandle g_proxyHandle;
|
||||||
|
RE::ObjectRefHandle g_runtimeProxySpawnDiagnosticHandle;
|
||||||
bool g_proxyResolvedLogged = false;
|
bool g_proxyResolvedLogged = false;
|
||||||
bool g_proxyActorBaseDiagnosticStartedLogged = false;
|
bool g_proxyActorBaseDiagnosticStartedLogged = false;
|
||||||
bool g_testPluginLoadedLogged = false;
|
bool g_testPluginLoadedLogged = false;
|
||||||
bool g_proxyActorBaseResolvedLogged = false;
|
bool g_proxyActorBaseResolvedLogged = false;
|
||||||
|
bool g_runtimeProxySpawnAttempted = false;
|
||||||
|
bool g_runtimeProxySpawnAttemptInProgress = false;
|
||||||
|
bool g_runtimeProxySpawnSucceeded = false;
|
||||||
|
bool g_runtimeProxySpawnFailed = false;
|
||||||
bool g_safetyOffsetMovedLogged = false;
|
bool g_safetyOffsetMovedLogged = false;
|
||||||
bool g_remoteStateMovementStartedLogged = false;
|
bool g_remoteStateMovementStartedLogged = false;
|
||||||
bool g_remoteStateMovedLogged = false;
|
bool g_remoteStateMovedLogged = false;
|
||||||
@@ -362,7 +369,7 @@ namespace
|
|||||||
|
|
||||||
if (!g_proxyActorBaseResolvedLogged) {
|
if (!g_proxyActorBaseResolvedLogged) {
|
||||||
LogInfoWithLocalPlayerPrefix(std::format(
|
LogInfoWithLocalPlayerPrefix(std::format(
|
||||||
"Runtime proxy actor base resolved: {} by plugin-local FormID fallback. plugin={}, localFormId={:06X}, runtimeFormId={:08X}, formType={}, isTESNPC=true, editorId={}, displayName={}, accepted=true. Runtime spawning remains disabled for Stage 1.",
|
"Runtime proxy actor base resolved: {} by plugin-local FormID fallback. plugin={}, localFormId={:06X}, runtimeFormId={:08X}, formType={}, isTESNPC=true, editorId={}, displayName={}, accepted=true.",
|
||||||
kProxyBaseActorEditorId,
|
kProxyBaseActorEditorId,
|
||||||
kTestPluginName,
|
kTestPluginName,
|
||||||
kProxyBaseActorLocalFormId,
|
kProxyBaseActorLocalFormId,
|
||||||
@@ -393,7 +400,7 @@ namespace
|
|||||||
if (baseActor && HasEditorId(*baseActor, kProxyBaseActorEditorId) && IsUsableProxyActorBase(*baseActor)) {
|
if (baseActor && HasEditorId(*baseActor, kProxyBaseActorEditorId) && IsUsableProxyActorBase(*baseActor)) {
|
||||||
if (!g_proxyActorBaseResolvedLogged) {
|
if (!g_proxyActorBaseResolvedLogged) {
|
||||||
LogInfoWithLocalPlayerPrefix(std::format(
|
LogInfoWithLocalPlayerPrefix(std::format(
|
||||||
"Runtime proxy actor base resolved: {} by editor ID as form {:08X}. Runtime spawning remains disabled for Stage 1.",
|
"Runtime proxy actor base resolved: {} by editor ID as form {:08X}.",
|
||||||
kProxyBaseActorEditorId,
|
kProxyBaseActorEditorId,
|
||||||
baseActor->GetFormID()));
|
baseActor->GetFormID()));
|
||||||
g_proxyActorBaseResolvedLogged = true;
|
g_proxyActorBaseResolvedLogged = true;
|
||||||
@@ -546,6 +553,153 @@ namespace
|
|||||||
return std::addressof(a_proxy) != std::addressof(a_player) && a_proxy.GetParentCell() == std::addressof(a_expectedCell);
|
return std::addressof(a_proxy) != std::addressof(a_player) && a_proxy.GetParentCell() == std::addressof(a_expectedCell);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsRuntimeProxySpawnDiagnosticRef(const RE::TESObjectREFR& a_ref)
|
||||||
|
{
|
||||||
|
if (!g_runtimeProxySpawnDiagnosticHandle) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto spawnedRef = g_runtimeProxySpawnDiagnosticHandle.get();
|
||||||
|
return spawnedRef && spawnedRef.get() == std::addressof(a_ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MarkRuntimeProxySpawnDiagnosticFailed(std::string_view a_reason)
|
||||||
|
{
|
||||||
|
g_runtimeProxySpawnFailed = true;
|
||||||
|
g_runtimeProxySpawnAttemptInProgress = false;
|
||||||
|
LogWarningWithLocalPlayerPrefix(std::format(
|
||||||
|
"Runtime proxy spawn diagnostic failed: {}",
|
||||||
|
a_reason));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TrySpawnRuntimeProxyDiagnostic(
|
||||||
|
RE::TESNPC* a_baseActor,
|
||||||
|
RE::TESObjectCELL& a_parentCell,
|
||||||
|
const RE::PlayerCharacter& a_player)
|
||||||
|
{
|
||||||
|
if constexpr (!kEnableRuntimeProxySpawnDiagnostic) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g_runtimeProxySpawnAttempted ||
|
||||||
|
g_runtimeProxySpawnSucceeded ||
|
||||||
|
g_runtimeProxySpawnAttemptInProgress ||
|
||||||
|
g_runtimeProxySpawnFailed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!a_baseActor) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* dataHandler = RE::TESDataHandler::GetSingleton();
|
||||||
|
if (!dataHandler) {
|
||||||
|
g_runtimeProxySpawnAttempted = true;
|
||||||
|
MarkRuntimeProxySpawnDiagnosticFailed("TESDataHandler is unavailable.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* boundObject = a_baseActor->As<RE::TESBoundObject>();
|
||||||
|
if (!boundObject) {
|
||||||
|
g_runtimeProxySpawnAttempted = true;
|
||||||
|
MarkRuntimeProxySpawnDiagnosticFailed(std::format(
|
||||||
|
"actor base {:08X} is not usable as NEW_REFR_DATA::object.",
|
||||||
|
a_baseActor->GetFormID()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_runtimeProxySpawnAttempted = true;
|
||||||
|
g_runtimeProxySpawnAttemptInProgress = true;
|
||||||
|
|
||||||
|
auto spawnPosition = a_player.GetPosition();
|
||||||
|
spawnPosition.x += kRuntimeProxySpawnDiagnosticOffsetX;
|
||||||
|
|
||||||
|
RE::NiPoint3 spawnDirection{};
|
||||||
|
spawnDirection.z = a_player.data.angle.z;
|
||||||
|
|
||||||
|
RE::NEW_REFR_DATA spawnData;
|
||||||
|
spawnData.object = boundObject;
|
||||||
|
spawnData.location = spawnPosition;
|
||||||
|
spawnData.direction = spawnDirection;
|
||||||
|
spawnData.interior = std::addressof(a_parentCell);
|
||||||
|
spawnData.world = nullptr;
|
||||||
|
spawnData.reference = nullptr;
|
||||||
|
spawnData.forcePersist = false;
|
||||||
|
spawnData.initializeScripts = true;
|
||||||
|
spawnData.initiallyDisabled = false;
|
||||||
|
|
||||||
|
LogInfoWithLocalPlayerPrefix(std::format(
|
||||||
|
"Runtime proxy spawn diagnostic attempting: actorBase={:08X}, cell={:08X}, position=({:.2f}, {:.2f}, {:.2f}), heading={:.2f}.",
|
||||||
|
a_baseActor->GetFormID(),
|
||||||
|
a_parentCell.GetFormID(),
|
||||||
|
spawnPosition.x,
|
||||||
|
spawnPosition.y,
|
||||||
|
spawnPosition.z,
|
||||||
|
spawnDirection.z));
|
||||||
|
|
||||||
|
g_runtimeProxySpawnDiagnosticHandle = dataHandler->CreateReferenceAtLocation(spawnData);
|
||||||
|
if (!g_runtimeProxySpawnDiagnosticHandle) {
|
||||||
|
MarkRuntimeProxySpawnDiagnosticFailed(std::format(
|
||||||
|
"CreateReferenceAtLocation returned an invalid handle for actorBase={:08X}, cell={:08X}, position=({:.2f}, {:.2f}, {:.2f}).",
|
||||||
|
a_baseActor->GetFormID(),
|
||||||
|
a_parentCell.GetFormID(),
|
||||||
|
spawnPosition.x,
|
||||||
|
spawnPosition.y,
|
||||||
|
spawnPosition.z));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto spawnedRef = g_runtimeProxySpawnDiagnosticHandle.get();
|
||||||
|
if (!spawnedRef) {
|
||||||
|
MarkRuntimeProxySpawnDiagnosticFailed(std::format(
|
||||||
|
"created handle {:08X} could not resolve to a reference.",
|
||||||
|
g_runtimeProxySpawnDiagnosticHandle.get_handle()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* spawnedActor = spawnedRef->As<RE::Actor>();
|
||||||
|
if (!spawnedActor) {
|
||||||
|
MarkRuntimeProxySpawnDiagnosticFailed(std::format(
|
||||||
|
"created reference {:08X} resolved but was not an actor.",
|
||||||
|
spawnedRef->GetFormID()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (std::addressof(*spawnedActor) == std::addressof(a_player)) {
|
||||||
|
MarkRuntimeProxySpawnDiagnosticFailed(std::format(
|
||||||
|
"created reference {:08X} unexpectedly resolved as the local player.",
|
||||||
|
spawnedRef->GetFormID()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto* spawnedCell = spawnedActor->GetParentCell();
|
||||||
|
if (spawnedCell != std::addressof(a_parentCell)) {
|
||||||
|
MarkRuntimeProxySpawnDiagnosticFailed(std::format(
|
||||||
|
"created actor {:08X} parent cell validation failed: expected={:08X}, actual={}.",
|
||||||
|
spawnedRef->GetFormID(),
|
||||||
|
a_parentCell.GetFormID(),
|
||||||
|
spawnedCell ? std::format("{:08X}", spawnedCell->GetFormID()) : "unavailable"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
spawnedActor->SetPosition(spawnPosition, true);
|
||||||
|
spawnedActor->SetHeading(spawnDirection.z);
|
||||||
|
|
||||||
|
// TODO: Add safe cleanup/despawn/hold behavior once runtime proxy lifecycle
|
||||||
|
// ownership is designed. Stage 2 intentionally leaves the actor in place.
|
||||||
|
g_runtimeProxySpawnSucceeded = true;
|
||||||
|
g_runtimeProxySpawnAttemptInProgress = false;
|
||||||
|
LogInfoWithLocalPlayerPrefix(std::format(
|
||||||
|
"Runtime proxy spawn diagnostic succeeded: actorBase={:08X}, handle={:08X}, createdRef={:08X}, resolvedAsActor=true, parentCell={:08X}, position=({:.2f}, {:.2f}, {:.2f}). The placed proxy fallback remains the active remote representation. TODO: add safe runtime proxy cleanup/despawn/hold behavior in a later stage.",
|
||||||
|
a_baseActor->GetFormID(),
|
||||||
|
g_runtimeProxySpawnDiagnosticHandle.get_handle(),
|
||||||
|
spawnedRef->GetFormID(),
|
||||||
|
spawnedCell->GetFormID(),
|
||||||
|
spawnPosition.x,
|
||||||
|
spawnPosition.y,
|
||||||
|
spawnPosition.z));
|
||||||
|
}
|
||||||
|
|
||||||
bool HasProxyBaseActorEditorId(const RE::Actor& a_actor)
|
bool HasProxyBaseActorEditorId(const RE::Actor& a_actor)
|
||||||
{
|
{
|
||||||
const auto* baseActor = a_actor.GetNPC();
|
const auto* baseActor = a_actor.GetNPC();
|
||||||
@@ -600,6 +754,10 @@ namespace
|
|||||||
return RE::BSContainer::ForEachResult::kContinue;
|
return RE::BSContainer::ForEachResult::kContinue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (IsRuntimeProxySpawnDiagnosticRef(*a_ref)) {
|
||||||
|
return RE::BSContainer::ForEachResult::kContinue;
|
||||||
|
}
|
||||||
|
|
||||||
auto* actor = a_ref->As<RE::Actor>();
|
auto* actor = a_ref->As<RE::Actor>();
|
||||||
if (!actor || !IsValidProxyActor(*actor, a_currentCell, a_player)) {
|
if (!actor || !IsValidProxyActor(*actor, a_currentCell, a_player)) {
|
||||||
return RE::BSContainer::ForEachResult::kContinue;
|
return RE::BSContainer::ForEachResult::kContinue;
|
||||||
@@ -916,9 +1074,10 @@ namespace F4T::ProxyActorController
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stage 1 runtime-spawn diagnostic only. Run before placed-proxy lookup and
|
// Runtime spawn diagnostics run before placed-proxy lookup and before
|
||||||
// before remote-player selection so it is not hidden behind visual fallback state.
|
// remote-player selection so failures are visible even when the fallback works.
|
||||||
(void)ResolveProxyActorBase();
|
auto* runtimeProxyActorBase = ResolveProxyActorBase();
|
||||||
|
TrySpawnRuntimeProxyDiagnostic(runtimeProxyActorBase, *parentCell, *player);
|
||||||
|
|
||||||
if constexpr (kProxyMovementMode == ProxyMovementMode::kSafetyOffsetTest) {
|
if constexpr (kProxyMovementMode == ProxyMovementMode::kSafetyOffsetTest) {
|
||||||
auto* proxy = ResolveProxy(*parentCell, *player);
|
auto* proxy = ResolveProxy(*parentCell, *player);
|
||||||
|
|||||||
Reference in New Issue
Block a user