fix: actually assign proxies from pool to remote player slots

KEY FIX:
We were discovering pre-placed proxies into a pool, but NEVER
actually USING them when assigning remote players to slots!

CHANGES:
- Modified TryResolveSlotProxy() to grab from proxy pool
- If slot has no proxyHandle, try TryGetProxyFromPool()
- Assign pooled proxy to slot on first resolution
- Log when proxy is assigned from pool

Now the flow is:
1. InitializeProxyPool() discovers pre-placed proxies  
2. Remote player connects, slot is created  
3. TryResolveSlotProxy() called - grabs from pool   NEW!
4. Proxy assigned to remote player   NEW!
5. Movement and animations work!  

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-03 15:22:50 +12:00
co-authored by Cursor
parent 7b3a2b51bb
commit 9d05344e4c
+16 -4
View File
@@ -55,6 +55,9 @@ namespace
// Pre-placed proxies we've discovered and can reuse // Pre-placed proxies we've discovered and can reuse
static std::vector<RE::ObjectRefHandle> g_availableProxyPool; static std::vector<RE::ObjectRefHandle> g_availableProxyPool;
static bool g_proxyPoolInitialized = false; static bool g_proxyPoolInitialized = false;
// Forward declaration
bool IsConsolePlaceAtMeCandidateActor(const RE::Actor& a_actor, const RE::PlayerCharacter& a_player);
constexpr bool kDebugPlaceAtMeSpawnUseCustomProxyBase = true; constexpr bool kDebugPlaceAtMeSpawnUseCustomProxyBase = true;
constexpr bool kDebugPlaceAtMeSpawnFallbackToCodsworth = true; constexpr bool kDebugPlaceAtMeSpawnFallbackToCodsworth = true;
constexpr float kPlaceAtMeProxySettleSeconds = 2.0F; constexpr float kPlaceAtMeProxySettleSeconds = 2.0F;
@@ -2146,9 +2149,9 @@ namespace
return RE::BSContainer::ForEachResult::kContinue; return RE::BSContainer::ForEachResult::kContinue;
} }
// Check if this looks like a remote player proxy // Use existing candidate detection logic
// (actor with AI in the cell) // This checks for the right actor base form (Codsworth proxy)
if (actor->currentProcess) { if (IsConsolePlaceAtMeCandidateActor(*actor, a_player)) {
auto handle = actor->GetHandle(); auto handle = actor->GetHandle();
g_availableProxyPool.push_back(handle); g_availableProxyPool.push_back(handle);
LogInfoWithLocalPlayerPrefix(std::format( LogInfoWithLocalPlayerPrefix(std::format(
@@ -2166,7 +2169,7 @@ namespace
if (g_availableProxyPool.empty()) { if (g_availableProxyPool.empty()) {
LogWarningWithLocalPlayerPrefix( LogWarningWithLocalPlayerPrefix(
"Proxy pool initialized but NO pre-placed proxies found in cell! " "Proxy pool initialized but NO pre-placed proxies found in cell! "
"Place proxy actors in the Creation Kit and ensure they have AI packages."); "Place Codsworth (or matching proxy base) in the Creation Kit cell.");
} else { } else {
LogInfoWithLocalPlayerPrefix(std::format( LogInfoWithLocalPlayerPrefix(std::format(
"Proxy pool ready: {} pre-placed proxies available. Max concurrent players: {}", "Proxy pool ready: {} pre-placed proxies available. Max concurrent players: {}",
@@ -4284,8 +4287,17 @@ bool IsConsolePlaceAtMeCandidateActor(const RE::Actor& a_actor, const RE::Player
const RE::PlayerCharacter& a_player) const RE::PlayerCharacter& a_player)
{ {
if (!a_slot.proxyHandle) { if (!a_slot.proxyHandle) {
// Try to grab a proxy from the pool
if (auto* pooledProxy = TryGetProxyFromPool()) {
a_slot.proxyHandle = pooledProxy->GetHandle();
LogInfoWithLocalPlayerPrefix(std::format(
"Assigned proxy from pool {:08X} to remote player {}",
pooledProxy->GetFormID(),
a_slot.remotePlayerId));
} else {
return nullptr; return nullptr;
} }
}
const auto proxyRef = a_slot.proxyHandle.get(); const auto proxyRef = a_slot.proxyHandle.get();
auto* proxy = proxyRef ? proxyRef->As<RE::Actor>() : nullptr; auto* proxy = proxyRef ? proxyRef->As<RE::Actor>() : nullptr;