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:
@@ -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 "F4TInterpolationSystem.h"
|
||||
#include "F4TProxyGameAPI.h"
|
||||
#include "F4TSoloCell.h"
|
||||
|
||||
#include "RE/A/ACTOR_STANCE.h"
|
||||
#include "RE/A/ActorValue.h"
|
||||
@@ -1700,6 +1701,13 @@ namespace
|
||||
const RE::PlayerCharacter& a_player,
|
||||
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);
|
||||
if (!remoteCellId) {
|
||||
LogThrottledWarning(
|
||||
|
||||
+37
-1
@@ -6,6 +6,7 @@
|
||||
#include "F4TProxyActorController.h"
|
||||
#include "F4TProxyAnimationSync.h"
|
||||
#include "F4TProxyPuppet.h"
|
||||
#include "F4TSoloCell.h"
|
||||
#include "F4TWorldStateSync.h"
|
||||
|
||||
#include "RE/A/ACTOR_STANCE.h"
|
||||
@@ -34,6 +35,7 @@ namespace
|
||||
std::uint32_t cellId;
|
||||
bool hasWorldspaceId;
|
||||
std::uint32_t worldspaceId;
|
||||
bool isSoloCell;
|
||||
};
|
||||
|
||||
struct PlayerMovementState
|
||||
@@ -133,13 +135,15 @@ namespace
|
||||
false,
|
||||
0,
|
||||
false,
|
||||
0
|
||||
0,
|
||||
false
|
||||
};
|
||||
|
||||
const auto* parentCell = a_player.GetParentCell();
|
||||
if (parentCell) {
|
||||
location.hasCellId = true;
|
||||
location.cellId = parentCell->GetFormID();
|
||||
location.isSoloCell = F4T::SoloCell::IsSoloCell(*parentCell);
|
||||
}
|
||||
|
||||
if (a_player.cachedWorldspace) {
|
||||
@@ -480,6 +484,7 @@ namespace
|
||||
static auto lastDebugSampleTime = 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 bool soloCellTransformSuppressed = false;
|
||||
|
||||
const auto currentTransform = GetPlayerTransform(*player);
|
||||
const auto currentLocation = GetPlayerLocation(*player);
|
||||
@@ -501,6 +506,29 @@ namespace
|
||||
debugMovementState.weaponDrawn,
|
||||
debugMovementState.movementSpeed,
|
||||
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) {
|
||||
lastSentTransform = currentTransform;
|
||||
lastDebugSampleTransform = currentTransform;
|
||||
@@ -538,6 +566,9 @@ namespace
|
||||
std::format(", worldspaceId={:08X}", currentLocation.worldspaceId) :
|
||||
std::string{}));
|
||||
}
|
||||
if (currentLocation.isSoloCell) {
|
||||
soloCellTransformSuppressed = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -627,6 +658,11 @@ namespace
|
||||
LogPlayerTransform(currentTransform);
|
||||
lastLogTime = currentTime;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (currentLocation.isSoloCell) {
|
||||
soloCellTransformSuppressed = true;
|
||||
}
|
||||
|
||||
lastDebugSampleTransform = currentTransform;
|
||||
|
||||
Reference in New Issue
Block a user