@@ -365,7 +365,7 @@ void PawnManager::ProcessTick(Microseconds elapsed, TimePoint now)
365
365
if (nextRestart_ != TimePoint::min () && nextRestart_ <= now)
366
366
{
367
367
// Reloading a script. Restart is in the past, load the next GM.
368
- Load (mainName_, true , true );
368
+ Load (mainName_, true );
369
369
nextRestart_ = TimePoint::min ();
370
370
}
371
371
if (mainScript_ && nextSleep_ != TimePoint::min () && nextSleep_ <= now)
@@ -441,7 +441,7 @@ bool PawnManager::Load(DynamicArray<StringView> const& mainScripts)
441
441
return Load (" gamemodes/" + gamemodes_[0 ], true );
442
442
}
443
443
444
- void PawnManager::openAMX (PawnScript& script, bool isEntryScript, bool restarting )
444
+ void PawnManager::openAMX (PawnScript& script, bool isEntryScript)
445
445
{
446
446
script.Register (" CallLocalFunction" , &utils::pawn_Script_Call);
447
447
script.Register (" Script_CallByIndex" , &utils::pawn_Script_CallByIndex);
@@ -529,25 +529,17 @@ void PawnManager::openAMX(PawnScript& script, bool isEntryScript, bool restartin
529
529
script.cache_ .inited = true ;
530
530
}
531
531
532
- for (auto const p : players->entries ())
533
- {
534
- // Call OnScriptUnloadPlayer for any pawn script that is being loaded, this way people can
535
- // Make use of this callback for resetting variables, or initializing anything player related.
536
- // First paramter is obviously player ID, and second parameter is a boolean determining whether it's
537
- // An entry script (main script) or a side script
538
- script.Call (" OnScriptLoadPlayer" , DefaultReturnValue_True, p->getID (), isEntryScript);
532
+ // Assume that all initialisation and header mangling is now complete, and that it is safe to
533
+ // cache public pointers.
539
534
540
- // If it's entry script and it's restarting, after loading we call OnPlayerConnect in all scripts
541
- // Regardless of their types, as if players have rejoined the server. This is also what SA-MP does.
542
- if (isEntryScript && restarting)
543
- {
544
- script.Call (" OnPlayerConnect" , DefaultReturnValue_True, p->getID ());
545
- CallInSides (" OnPlayerConnect" , DefaultReturnValue_True, p->getID ());
546
- }
535
+ // Call `OnPlayerConnect` (can be after caching).
536
+ for (auto p : players->entries ())
537
+ {
538
+ script.Call (" OnPlayerConnect" , DefaultReturnValue_True, p->getID ());
547
539
}
548
540
}
549
541
550
- bool PawnManager::Load (std::string const & name, bool isEntryScript, bool restarting )
542
+ bool PawnManager::Load (std::string const & name, bool isEntryScript)
551
543
{
552
544
std::string normal_script_name;
553
545
utils::NormaliseScriptName (name, normal_script_name);
@@ -601,24 +593,40 @@ bool PawnManager::Load(std::string const& name, bool isEntryScript, bool restart
601
593
602
594
void PawnManager::closeAMX (PawnScript& script, bool isEntryScript)
603
595
{
604
- // Call OnPlayerDisconnect on entry script close first, then we proceed to do unload player callback
605
- if (isEntryScript)
596
+ AMX* amx = script.GetAMX ();
597
+ int idx;
598
+ bool once = true ;
599
+ // Reason 4, to match fixes.inc. Why was it not 3? I don't know.
600
+ if (amx_FindPublic (amx, " OnPlayerDisconnect" , &idx) == AMX_ERR_NONE)
606
601
{
607
602
for (auto const p : players->entries ())
608
603
{
609
- PawnManager::Get ()->CallInEntry (" OnPlayerDisconnect" , DefaultReturnValue_True, p->getID (), PeerDisconnectReason_Quit);
604
+ cell ret = 1 ;
605
+ int err = script.CallChecked (idx, ret, p->getID (), PeerDisconnectReason_ModeEnd);
606
+ switch (err)
607
+ {
608
+ case AMX_ERR_NONE:
609
+ break ;
610
+ case AMX_ERR_BOUNDS:
611
+ // Test the `OP_BOUNDS` parameter and the current index.
612
+ if (once && (*(cell*)((uintptr_t )amx->base + (((AMX_HEADER*)amx->base )->cod + amx->cip - sizeof (cell)))) == 2 ) // && amx->pri == 4)
613
+ {
614
+ core->printLn (R"(
615
+ Array out-of-bounds encountered during `OnPlayerDisconnect` with reason `4`
616
+ (script exit). This may be due to old code assuming the highest possible reason
617
+ is `2`.
618
+ )" );
619
+ // Only show the error once, don't spam it.
620
+ once = false ;
621
+ break ;
622
+ }
623
+ // Fallthrough
624
+ default :
625
+ core->logLn (LogLevel::Error, " %s" , aux_StrError (err));
626
+ break ;
627
+ }
610
628
}
611
629
}
612
-
613
- // Call OnScriptUnloadPlayer for any pawn script that is being unloaded, this way people can
614
- // Make use of this callback for resetting variables, or uninitializing anything player related.
615
- // First paramter is obviously player ID, and second parameter is a boolean determining whether it's
616
- // An entry script (main script) or a side script
617
- for (auto const p : players->entries ())
618
- {
619
- script.Call (" OnScriptUnloadPlayer" , DefaultReturnValue_True, p->getID (), isEntryScript);
620
- }
621
-
622
630
if (isEntryScript)
623
631
{
624
632
script.Call (" OnGameModeExit" , DefaultReturnValue_False);
0 commit comments