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
+24 -12
View File
@@ -55,6 +55,9 @@ namespace
// Pre-placed proxies we've discovered and can reuse
static std::vector<RE::ObjectRefHandle> g_availableProxyPool;
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 kDebugPlaceAtMeSpawnFallbackToCodsworth = true;
constexpr float kPlaceAtMeProxySettleSeconds = 2.0F;
@@ -2146,17 +2149,17 @@ namespace
return RE::BSContainer::ForEachResult::kContinue;
}
// Check if this looks like a remote player proxy
// (actor with AI in the cell)
if (actor->currentProcess) {
// Use existing candidate detection logic
// This checks for the right actor base form (Codsworth proxy)
if (IsConsolePlaceAtMeCandidateActor(*actor, a_player)) {
auto handle = actor->GetHandle();
g_availableProxyPool.push_back(handle);
LogInfoWithLocalPlayerPrefix(std::format(
"Proxy pool: Found pre-placed proxy {:08X} (base: {:08X}), total proxies: {}",
actor->GetFormID(),
actor->GetNPC() ? actor->GetNPC()->GetFormID() : 0,
g_availableProxyPool.size()));
}
g_availableProxyPool.push_back(handle);
LogInfoWithLocalPlayerPrefix(std::format(
"Proxy pool: Found pre-placed proxy {:08X} (base: {:08X}), total proxies: {}",
actor->GetFormID(),
actor->GetNPC() ? actor->GetNPC()->GetFormID() : 0,
g_availableProxyPool.size()));
}
return RE::BSContainer::ForEachResult::kContinue;
});
@@ -2166,7 +2169,7 @@ namespace
if (g_availableProxyPool.empty()) {
LogWarningWithLocalPlayerPrefix(
"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 {
LogInfoWithLocalPlayerPrefix(std::format(
"Proxy pool ready: {} pre-placed proxies available. Max concurrent players: {}",
@@ -4284,7 +4287,16 @@ bool IsConsolePlaceAtMeCandidateActor(const RE::Actor& a_actor, const RE::Player
const RE::PlayerCharacter& a_player)
{
if (!a_slot.proxyHandle) {
return nullptr;
// 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;
}
}
const auto proxyRef = a_slot.proxyHandle.get();