From 0f6908f7f4a35f2c64009a732df848da474e59c4 Mon Sep 17 00:00:00 2001 From: Dillon Skaggs Date: Thu, 28 May 2026 16:10:49 -0500 Subject: [PATCH] fix(server/state): scope our access to the client properly to prevent deadlock If you listened to onPlayerBucketChange you would deadlock if you called GetPlayerRoutingBucket, while someone shouldn't do this since the argument is already provided in the event, we should also not deadlock. --- .../src/state/ServerGameState_Scripting.cpp | 51 +++++++++++-------- 1 file changed, 31 insertions(+), 20 deletions(-) diff --git a/code/components/citizen-server-impl/src/state/ServerGameState_Scripting.cpp b/code/components/citizen-server-impl/src/state/ServerGameState_Scripting.cpp index bb64bc2953..47823ebd35 100644 --- a/code/components/citizen-server-impl/src/state/ServerGameState_Scripting.cpp +++ b/code/components/citizen-server-impl/src/state/ServerGameState_Scripting.cpp @@ -1627,9 +1627,15 @@ static void Init() // get the server's game state auto gameState = instance->GetComponent(); + + uint32_t routingBucket = 0; - auto [lock, clientData] = gameState->ExternalGetClientData(client); - return int(clientData->routingBucket); + { + auto [lock, clientData] = gameState->ExternalGetClientData(client); + routingBucket = clientData->routingBucket; + } + + return int(routingBucket); })); fx::ScriptEngine::RegisterNativeHandler("SET_PLAYER_ROUTING_BUCKET", MakeClientFunction([](fx::ScriptContext& context, const fx::ClientSharedPtr& client) @@ -1649,26 +1655,31 @@ static void Init() // get the server's game state auto gameState = instance->GetComponent(); - - auto [lock, clientData] = gameState->ExternalGetClientData(client); - // store old bucket for event - const auto oldBucket = clientData->routingBucket; - - gameState->ClearClientFromWorldGrid(client); - clientData->routingBucket = bucket; - - fx::sync::SyncEntityPtr playerEntity; - - { - std::shared_lock _lock(clientData->playerEntityMutex); - playerEntity = clientData->playerEntity.lock(); - } + + uint32_t oldBucket = 0; - if (playerEntity) - { - playerEntity->routingBucket = bucket; - } + { + auto [lock, clientData] = gameState->ExternalGetClientData(client); + + // store old bucket for event + oldBucket = clientData->routingBucket; + + gameState->ClearClientFromWorldGrid(client); + clientData->routingBucket = bucket; + + fx::sync::SyncEntityPtr playerEntity; + + { + std::shared_lock _lock(clientData->playerEntityMutex); + playerEntity = clientData->playerEntity.lock(); + } + + if (playerEntity) + { + playerEntity->routingBucket = bucket; + } + } auto eventManager = resourceManager->GetComponent();