From 4bee3ad69a03e07ca85e1c066f594df3cfe119f7 Mon Sep 17 00:00:00 2001 From: Denis Ahmatovic <37641046+Andosius@users.noreply.github.com> Date: Sat, 24 Feb 2024 16:44:13 +0100 Subject: [PATCH 1/5] Call OnScript(Un)LoadForPlayer on script un(load) --- Server/Components/Pawn/Manager/Manager.cpp | 39 +++------------------- 1 file changed, 5 insertions(+), 34 deletions(-) diff --git a/Server/Components/Pawn/Manager/Manager.cpp b/Server/Components/Pawn/Manager/Manager.cpp index 09405a131..cb1a2671e 100644 --- a/Server/Components/Pawn/Manager/Manager.cpp +++ b/Server/Components/Pawn/Manager/Manager.cpp @@ -532,10 +532,10 @@ void PawnManager::openAMX(PawnScript& script, bool isEntryScript) // Assume that all initialisation and header mangling is now complete, and that it is safe to // cache public pointers. - // Call `OnPlayerConnect` (can be after caching). + // Call `OnScriptLoadPlayer` (can be after caching). for (auto p : players->entries()) { - script.Call("OnPlayerConnect", DefaultReturnValue_True, p->getID()); + script.Call("OnScriptLoadPlayer", DefaultReturnValue_True, p->getID()); } } @@ -593,40 +593,11 @@ bool PawnManager::Load(std::string const& name, bool isEntryScript) void PawnManager::closeAMX(PawnScript& script, bool isEntryScript) { - AMX* amx = script.GetAMX(); - int idx; - bool once = true; - // Reason 4, to match fixes.inc. Why was it not 3? I don't know. - if (amx_FindPublic(amx, "OnPlayerDisconnect", &idx) == AMX_ERR_NONE) + for (auto p : players->entries()) { - for (auto const p : players->entries()) - { - cell ret = 1; - int err = script.CallChecked(idx, ret, p->getID(), PeerDisconnectReason_ModeEnd); - switch (err) - { - case AMX_ERR_NONE: - break; - case AMX_ERR_BOUNDS: - // Test the `OP_BOUNDS` parameter and the current index. - if (once && (*(cell*)((uintptr_t)amx->base + (((AMX_HEADER*)amx->base)->cod + amx->cip - sizeof(cell)))) == 2) // && amx->pri == 4) - { - core->printLn(R"( -Array out-of-bounds encountered during `OnPlayerDisconnect` with reason `4` -(script exit). This may be due to old code assuming the highest possible reason -is `2`. -)"); - // Only show the error once, don't spam it. - once = false; - break; - } - // Fallthrough - default: - core->logLn(LogLevel::Error, "%s", aux_StrError(err)); - break; - } - } + script.Call("OnScriptUnloadPlayer", DefaultReturnValue_True, p->getID()); } + if (isEntryScript) { script.Call("OnGameModeExit", DefaultReturnValue_False); From 4689d0ff5554c2cbbd9c5086132ab5f73b01f13e Mon Sep 17 00:00:00 2001 From: Denis Ahmatovic <37641046+Andosius@users.noreply.github.com> Date: Sat, 24 Feb 2024 19:51:19 +0100 Subject: [PATCH 2/5] add OnPlayer(Dis)Connect for main script only --- Server/Components/Pawn/Manager/Manager.cpp | 44 +++++++++++++++++++++- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/Server/Components/Pawn/Manager/Manager.cpp b/Server/Components/Pawn/Manager/Manager.cpp index cb1a2671e..a30b9e629 100644 --- a/Server/Components/Pawn/Manager/Manager.cpp +++ b/Server/Components/Pawn/Manager/Manager.cpp @@ -521,6 +521,11 @@ void PawnManager::openAMX(PawnScript& script, bool isEntryScript) // If there's no `main` ignore it for now. core->logLn(LogLevel::Error, "%s", aux_StrError(err)); } + + for (auto const p : players->entries()) + { + script.Call("OnPlayerConnect", DefaultReturnValue_True, p->getID()); + } // TODO: `AMX_EXEC_CONT` support. } else @@ -533,7 +538,7 @@ void PawnManager::openAMX(PawnScript& script, bool isEntryScript) // cache public pointers. // Call `OnScriptLoadPlayer` (can be after caching). - for (auto p : players->entries()) + for (auto const p : players->entries()) { script.Call("OnScriptLoadPlayer", DefaultReturnValue_True, p->getID()); } @@ -593,13 +598,48 @@ bool PawnManager::Load(std::string const& name, bool isEntryScript) void PawnManager::closeAMX(PawnScript& script, bool isEntryScript) { - for (auto p : players->entries()) + for (auto const p : players->entries()) { script.Call("OnScriptUnloadPlayer", DefaultReturnValue_True, p->getID()); } if (isEntryScript) { + AMX* amx = script.GetAMX(); + int idx; + bool once = true; + // Reason 4, to match fixes.inc. Why was it not 3? I don't know. + if (amx_FindPublic(amx, "OnPlayerDisconnect", &idx) == AMX_ERR_NONE) + { + for (auto const p : players->entries()) + { + cell ret = 1; + int err = script.CallChecked(idx, ret, p->getID(), PeerDisconnectReason_ModeEnd); + switch (err) + { + case AMX_ERR_NONE: + break; + case AMX_ERR_BOUNDS: + // Test the `OP_BOUNDS` parameter and the current index. + if (once && (*(cell*)((uintptr_t)amx->base + (((AMX_HEADER*)amx->base)->cod + amx->cip - sizeof(cell)))) == 2) // && amx->pri == 4) + { + core->printLn(R"( +Array out-of-bounds encountered during `OnPlayerDisconnect` with reason `4` +(script exit). This may be due to old code assuming the highest possible reason +is `2`. +)"); + // Only show the error once, don't spam it. + once = false; + break; + } + // Fallthrough + default: + core->logLn(LogLevel::Error, "%s", aux_StrError(err)); + break; + } + } + } + script.Call("OnGameModeExit", DefaultReturnValue_False); CallInSides("OnGameModeExit", DefaultReturnValue_False); } From da6293ea6d3531472da3cea7236345454ed67ca6 Mon Sep 17 00:00:00 2001 From: iAmir Date: Tue, 27 Feb 2024 04:03:34 +0330 Subject: [PATCH 3/5] call OnPlayerConnect for gm and all fs on restart --- Server/Components/Pawn/Manager/Manager.cpp | 24 ++++++++++------------ Server/Components/Pawn/Manager/Manager.hpp | 4 ++-- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/Server/Components/Pawn/Manager/Manager.cpp b/Server/Components/Pawn/Manager/Manager.cpp index a30b9e629..d53445013 100644 --- a/Server/Components/Pawn/Manager/Manager.cpp +++ b/Server/Components/Pawn/Manager/Manager.cpp @@ -365,7 +365,7 @@ void PawnManager::ProcessTick(Microseconds elapsed, TimePoint now) if (nextRestart_ != TimePoint::min() && nextRestart_ <= now) { // Reloading a script. Restart is in the past, load the next GM. - Load(mainName_, true); + Load(mainName_, true, true); nextRestart_ = TimePoint::min(); } if (mainScript_ && nextSleep_ != TimePoint::min() && nextSleep_ <= now) @@ -441,7 +441,7 @@ bool PawnManager::Load(DynamicArray const& mainScripts) return Load("gamemodes/" + gamemodes_[0], true); } -void PawnManager::openAMX(PawnScript& script, bool isEntryScript) +void PawnManager::openAMX(PawnScript& script, bool isEntryScript, bool restarting) { script.Register("CallLocalFunction", &utils::pawn_Script_Call); script.Register("Script_CallByIndex", &utils::pawn_Script_CallByIndex); @@ -521,11 +521,6 @@ void PawnManager::openAMX(PawnScript& script, bool isEntryScript) // If there's no `main` ignore it for now. core->logLn(LogLevel::Error, "%s", aux_StrError(err)); } - - for (auto const p : players->entries()) - { - script.Call("OnPlayerConnect", DefaultReturnValue_True, p->getID()); - } // TODO: `AMX_EXEC_CONT` support. } else @@ -534,17 +529,20 @@ void PawnManager::openAMX(PawnScript& script, bool isEntryScript) script.cache_.inited = true; } - // Assume that all initialisation and header mangling is now complete, and that it is safe to - // cache public pointers. - - // Call `OnScriptLoadPlayer` (can be after caching). for (auto const p : players->entries()) { - script.Call("OnScriptLoadPlayer", DefaultReturnValue_True, p->getID()); + + // If it's entry script and it's restarting, after loading we call OnPlayerConnect in all scripts + // Regardless of their types, as if players have rejoined the server. This is also what SA-MP does. + if (isEntryScript && restarting) + { + script.Call("OnPlayerConnect", DefaultReturnValue_True, p->getID()); + CallInSides("OnPlayerConnect", DefaultReturnValue_True, p->getID()); + } } } -bool PawnManager::Load(std::string const& name, bool isEntryScript) +bool PawnManager::Load(std::string const& name, bool isEntryScript, bool restarting) { std::string normal_script_name; utils::NormaliseScriptName(name, normal_script_name); diff --git a/Server/Components/Pawn/Manager/Manager.hpp b/Server/Components/Pawn/Manager/Manager.hpp index 360d8d33c..0f1b606b5 100644 --- a/Server/Components/Pawn/Manager/Manager.hpp +++ b/Server/Components/Pawn/Manager/Manager.hpp @@ -89,7 +89,7 @@ class PawnManager : public Singleton, public PawnLookup }); } - void openAMX(PawnScript& script, bool isEntryScript); + void openAMX(PawnScript& script, bool isEntryScript, bool restarting = false); void closeAMX(PawnScript& script, bool isEntryScript); public: @@ -104,7 +104,7 @@ class PawnManager : public Singleton, public PawnLookup void SetBasePath(std::string const& path); void SetScriptPath(std::string const& path); - bool Load(std::string const& name, bool primary = false); + bool Load(std::string const& name, bool primary = false, bool restarting = false); bool Load(DynamicArray const& mainScripts); bool Reload(std::string const& name); bool Unload(std::string const& name); From 603f68b8a5319dd0d3c22a95bbd27a9164115502 Mon Sep 17 00:00:00 2001 From: iAmir Date: Tue, 27 Feb 2024 04:06:11 +0330 Subject: [PATCH 4/5] clean OnPlayerDisconnect code, call it first --- Server/Components/Pawn/Manager/Manager.cpp | 43 ++++------------------ 1 file changed, 8 insertions(+), 35 deletions(-) diff --git a/Server/Components/Pawn/Manager/Manager.cpp b/Server/Components/Pawn/Manager/Manager.cpp index d53445013..6eec547c2 100644 --- a/Server/Components/Pawn/Manager/Manager.cpp +++ b/Server/Components/Pawn/Manager/Manager.cpp @@ -596,6 +596,14 @@ bool PawnManager::Load(std::string const& name, bool isEntryScript, bool restart void PawnManager::closeAMX(PawnScript& script, bool isEntryScript) { + // Call OnPlayerDisconnect on entry script close first, then we proceed to do unload player callback + if (isEntryScript) + { + for (auto const p : players->entries()) + { + PawnManager::Get()->CallInEntry("OnPlayerDisconnect", DefaultReturnValue_True, p->getID(), PeerDisconnectReason_Quit); + } + } for (auto const p : players->entries()) { script.Call("OnScriptUnloadPlayer", DefaultReturnValue_True, p->getID()); @@ -603,41 +611,6 @@ void PawnManager::closeAMX(PawnScript& script, bool isEntryScript) if (isEntryScript) { - AMX* amx = script.GetAMX(); - int idx; - bool once = true; - // Reason 4, to match fixes.inc. Why was it not 3? I don't know. - if (amx_FindPublic(amx, "OnPlayerDisconnect", &idx) == AMX_ERR_NONE) - { - for (auto const p : players->entries()) - { - cell ret = 1; - int err = script.CallChecked(idx, ret, p->getID(), PeerDisconnectReason_ModeEnd); - switch (err) - { - case AMX_ERR_NONE: - break; - case AMX_ERR_BOUNDS: - // Test the `OP_BOUNDS` parameter and the current index. - if (once && (*(cell*)((uintptr_t)amx->base + (((AMX_HEADER*)amx->base)->cod + amx->cip - sizeof(cell)))) == 2) // && amx->pri == 4) - { - core->printLn(R"( -Array out-of-bounds encountered during `OnPlayerDisconnect` with reason `4` -(script exit). This may be due to old code assuming the highest possible reason -is `2`. -)"); - // Only show the error once, don't spam it. - once = false; - break; - } - // Fallthrough - default: - core->logLn(LogLevel::Error, "%s", aux_StrError(err)); - break; - } - } - } - script.Call("OnGameModeExit", DefaultReturnValue_False); CallInSides("OnGameModeExit", DefaultReturnValue_False); } From ac2d70020068d5a1ce7034a2a9b55454bf1c9ae2 Mon Sep 17 00:00:00 2001 From: iAmir Date: Tue, 27 Feb 2024 04:07:22 +0330 Subject: [PATCH 5/5] on script load and unload player CBs, called for all scripts --- Server/Components/Pawn/Manager/Manager.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Server/Components/Pawn/Manager/Manager.cpp b/Server/Components/Pawn/Manager/Manager.cpp index 6eec547c2..647f6570f 100644 --- a/Server/Components/Pawn/Manager/Manager.cpp +++ b/Server/Components/Pawn/Manager/Manager.cpp @@ -531,6 +531,11 @@ void PawnManager::openAMX(PawnScript& script, bool isEntryScript, bool restartin for (auto const p : players->entries()) { + // Call OnScriptUnloadPlayer for any pawn script that is being loaded, this way people can + // Make use of this callback for resetting variables, or initializing anything player related. + // First paramter is obviously player ID, and second parameter is a boolean determining whether it's + // An entry script (main script) or a side script + script.Call("OnScriptLoadPlayer", DefaultReturnValue_True, p->getID(), isEntryScript); // If it's entry script and it's restarting, after loading we call OnPlayerConnect in all scripts // Regardless of their types, as if players have rejoined the server. This is also what SA-MP does. @@ -604,9 +609,14 @@ void PawnManager::closeAMX(PawnScript& script, bool isEntryScript) PawnManager::Get()->CallInEntry("OnPlayerDisconnect", DefaultReturnValue_True, p->getID(), PeerDisconnectReason_Quit); } } + + // Call OnScriptUnloadPlayer for any pawn script that is being unloaded, this way people can + // Make use of this callback for resetting variables, or uninitializing anything player related. + // First paramter is obviously player ID, and second parameter is a boolean determining whether it's + // An entry script (main script) or a side script for (auto const p : players->entries()) { - script.Call("OnScriptUnloadPlayer", DefaultReturnValue_True, p->getID()); + script.Call("OnScriptUnloadPlayer", DefaultReturnValue_True, p->getID(), isEntryScript); } if (isEntryScript)