// --------------------------------------------------------------------------------------------
#include "Logger.hpp"
#include "Core.hpp"
#include "Command.hpp"
#include "SqMod.h"

// ------------------------------------------------------------------------------------------------
#include <stdio.h>

// ------------------------------------------------------------------------------------------------
namespace SqMod {

// ------------------------------------------------------------------------------------------------
static bool     g_Reload        = false;
static Int32    g_ReloadHeader  = -1;
static Object   g_ReloadPayload;

// ------------------------------------------------------------------------------------------------
extern void InitExports();

// ------------------------------------------------------------------------------------------------
void EnableReload(Int32 header, Object & payload)
{
    g_Reload = true;
    g_ReloadHeader = header;
    g_ReloadPayload = payload;
}

void DisableReload()
{
    g_Reload = false;
    g_ReloadHeader = -1;
    g_ReloadPayload.Release();
}

// ------------------------------------------------------------------------------------------------
bool ReloadEnabled()
{
    return g_Reload;
}

// ------------------------------------------------------------------------------------------------

} // Namespace:: SqMod

// ------------------------------------------------------------------------------------------------
using namespace SqMod;

// ------------------------------------------------------------------------------------------------
void BindCallbacks();
void UnbindCallbacks();

// ------------------------------------------------------------------------------------------------
void DoReload()
{
    // Disable reloading in case of failure
    g_Reload = false;
    // Allow reloading by default
    _Core->SetState(1);
    // Emit the reload event
    _Core->EmitScriptReload(g_ReloadHeader, g_ReloadPayload);
    // Release the specified payload, if any
    DisableReload();
    // Are we allowed to reload?
    if (!_Core->GetState())
    {
        return;
    }
    // Terminate the current VM and release resources
    _Core->Terminate();
    // Attempt to initialize it the central core
    if (!_Core->Init())
    {
        throw std::runtime_error("Unable to initialize plugin central core");
    }
    // Attempt to load resources
    else if (!_Core->Load())
    {
        throw std::runtime_error("Unable to load plugin central core");
    }
    // Prevent recursive reloading
    DisableReload();
}

// ------------------------------------------------------------------------------------------------
void DestroyComponents()
{
    // Destroy command component
    if (_Cmd)
    {
        SQMOD_MANAGEDPTR_DEL(CmdManager, _Cmd);
        _Cmd = SQMOD_MANAGEDPTR_MAKE(CmdManager, nullptr);
    }
    // Destroy core component
    if (_Core)
    {
        SQMOD_MANAGEDPTR_DEL(Core, _Core);
        _Core = SQMOD_MANAGEDPTR_MAKE(Core, nullptr);
    }
    // Destroy logger component
    if (_Log)
    {
        SQMOD_MANAGEDPTR_DEL(Logger, _Log);
        _Log = SQMOD_MANAGEDPTR_MAKE(Logger, nullptr);
    }
}

// ------------------------------------------------------------------------------------------------
SQMOD_API_EXPORT unsigned int VcmpPluginInit(PluginFuncs * funcs, PluginCallbacks * calls, PluginInfo * info)
{
    // Output plugin header
    puts("");
    OutputMessage("--------------------------------------------------------------------");
    OutputMessage("Plugin: %s", SQMOD_NAME);
    OutputMessage("Author: %s", SQMOD_AUTHOR);
    OutputMessage("Legal: %s", SQMOD_COPYRIGHT);
    OutputMessage("--------------------------------------------------------------------");
    puts("");

    // Verify that core components are working
    if (!Logger::Get())
    {
        puts("[SQMOD] Unable to start because the logging class could not be instantiated");
        return SQMOD_FAILURE;
    }
    if (!Core::Get())
    {
        DestroyComponents();
        puts("[SQMOD] Unable to start because the central core class could not be instantiated");
        return SQMOD_FAILURE;
    }
    if (!CmdManager::Get())
    {
        DestroyComponents();
        puts("[SQMOD] Unable to start because the command class could not be instantiated");
        return SQMOD_FAILURE;
    }
    // Store server proxies
    _Func = funcs;
    _Clbk = calls;
    _Info = info;
    // Assign plugin information
    _Info->uPluginVer = SQMOD_VERSION;
    strcpy(_Info->szName, SQMOD_HOST_NAME);
    // Attempt to initialize the plugin
    if (!_Core->Init())
    {
        LogFtl("The plugin failed to initialize");
        _Core->Terminate();
        DestroyComponents();
        return SQMOD_FAILURE;
    }
    else if (_Clbk)
        BindCallbacks();
    else
    {
        _Core->Terminate();
        DestroyComponents();
        LogFtl("Unable to start because the server callbacks are missing");
        return SQMOD_FAILURE;
    }
    // Attempt to initialize the plugin exports
    InitExports();
    // Notify that the plugin was successfully loaded
    OutputMessage("Successfully loaded %s", SQMOD_NAME);
    // Dummy spacing
    puts("");
    // Initialization was successful
    return SQMOD_SUCCESS;
}

// --------------------------------------------------------------------------------------------
#define SQMOD_CATCH_EVENT_EXCEPTION(ev) /*
*/ catch (const Sqrat::Exception & e) /*
*/ { /*
*/  LogErr("Squirrel exception caught during (" #ev ") event"); /*
*/  LogInf("Message: %s", e.Message().c_str()); /*
*/ } /*
*/ catch (const std::exception & e) /*
*/ { /*
*/  LogErr("Program exception caught during (" #ev ") event"); /*
*/  LogInf("Message: %s", e.what()); /*
*/ } /*
*/ catch (...) /*
*/ { /*
*/  LogErr("Unknown exception caught during (" #ev ") event"); /*
*/ } /*
*/

// --------------------------------------------------------------------------------------------
#define SQMOD_RELOAD_CHECK if (g_Reload) DoReload();

// --------------------------------------------------------------------------------------------
static int VC_InitServer(void)
{
    // Don't even try to initialize if there's no core instance
    if (!_Core)
    {
        return SQMOD_FAILURE;
    }
    // Mark the initialization as successful by default
    _Core->SetState(1);
    // Attempt to forward the event
    try
    {

        // Obtain the API version as a string
        String apiver(ToStrF("%d", SQMOD_API_VER));
        // Signal outside plug-ins to do fetch our proxies
        _Func->SendCustomCommand(0xDABBAD00, apiver.c_str());
        // Attempt to load the module core
        if (_Core->Load())
        {
            _Core->EmitServerStartup();
        }
        else
        {
            LogFtl("Unable to load the plugin resources properly");
        }
    }
    SQMOD_CATCH_EVENT_EXCEPTION(InitServer)
    // See if a reload was requested
    SQMOD_RELOAD_CHECK
    // Return the last known plug-in state
    return _Core->GetState();
}

static void VC_ShutdownServer(void)
{
    // Don't even try to de-initialize if there's no core instance
    if (!_Core)
    {
        return;
    }
    // Attempt to forward the event
    try
    {
        _Core->EmitServerShutdown();
        // Deallocate and release everything obtained at startup
        _Core->Terminate();
        // The server still triggers callbacks and we deallocated everything!
        UnbindCallbacks();
        // Destroy components
        DestroyComponents();
    }
    SQMOD_CATCH_EVENT_EXCEPTION(ShutdownServer)
    // See if a reload was requested (quite useless here but why not)
    SQMOD_RELOAD_CHECK
}

static void VC_Frame(float delta)
{
    // Attempt to forward the event
    try
    {
        _Core->EmitServerFrame(delta);
    }
    SQMOD_CATCH_EVENT_EXCEPTION(Frame)
    // See if a reload was requested
    SQMOD_RELOAD_CHECK
}

static void VC_PlayerConnect(int player)
{
    // Attempt to forward the event
    try
    {
        _Core->ConnectPlayer(player, SQMOD_CREATE_AUTOMATIC, NullObject());
    }
    SQMOD_CATCH_EVENT_EXCEPTION(PlayerConnect)
    // See if a reload was requested
    SQMOD_RELOAD_CHECK
}

static void VC_PlayerDisconnect(int player, int reason)
{
    // Attempt to forward the event
    try
    {
        _Core->DisconnectPlayer(player, reason, NullObject());
    }
    SQMOD_CATCH_EVENT_EXCEPTION(PlayerDisconnect)
    // See if a reload was requested
    SQMOD_RELOAD_CHECK
}

static void VC_PlayerBeginTyping(int player)
{
    // Attempt to forward the event
    try
    {
        _Core->EmitPlayerStartTyping(player);
    }
    SQMOD_CATCH_EVENT_EXCEPTION(PlayerBeginTyping)
    // See if a reload was requested
    SQMOD_RELOAD_CHECK
}

static void VC_PlayerEndTyping(int player)
{
    // Attempt to forward the event
    try
    {
        _Core->EmitPlayerStopTyping(player);
    }
    SQMOD_CATCH_EVENT_EXCEPTION(PlayerEndTyping)
    // See if a reload was requested
    SQMOD_RELOAD_CHECK
}

static int VC_PlayerRequestClass(int player, int offset)
{
    // Mark the initialization as successful by default
    _Core->SetState(SQMOD_SUCCESS);
    // Attempt to forward the event
    try
    {
        _Core->EmitPlayerRequestClass(player, offset);
    }
    SQMOD_CATCH_EVENT_EXCEPTION(PlayerEndTyping)
    // See if a reload was requested
    SQMOD_RELOAD_CHECK
    // Return the last known plug-in state
    return _Core->GetState();
}

static int VC_PlayerRequestSpawn(int player)
{
    // Mark the initialization as successful by default
    _Core->SetState(SQMOD_SUCCESS);
    // Attempt to forward the event
    try
    {
        _Core->EmitPlayerRequestSpawn(player);
    }
    SQMOD_CATCH_EVENT_EXCEPTION(PlayerRequestSpawn)
    // See if a reload was requested
    SQMOD_RELOAD_CHECK
    // Return the last known plug-in state
    return _Core->GetState();
}

static void VC_PlayerSpawn(int player)
{
    // Attempt to forward the event
    try
    {
        _Core->EmitPlayerSpawn(player);
    }
    SQMOD_CATCH_EVENT_EXCEPTION(PlayerSpawn)
    // See if a reload was requested
    SQMOD_RELOAD_CHECK
}

static void VC_PlayerDeath(int player, int killer, int reason, int body_part)
{
    // Attempt to forward the event
    try
    {
        if (_Func->IsPlayerConnected(killer))
        {
            _Core->EmitPlayerKilled(player, killer, reason, body_part);
        }
        else
        {
            _Core->EmitPlayerWasted(player, reason);
        }
    }
    SQMOD_CATCH_EVENT_EXCEPTION(PlayerDeath)
    // See if a reload was requested
    SQMOD_RELOAD_CHECK
}

static void VC_PlayerUpdate(int player, int type)
{
    // Attempt to forward the event
    try
    {
        _Core->EmitPlayerUpdate(player, type);
    }
    SQMOD_CATCH_EVENT_EXCEPTION(PlayerUpdate)
    // See if a reload was requested
    SQMOD_RELOAD_CHECK
}

static int VC_PlayerRequestEnter(int player, int vehicle, int slot)
{
    // Mark the initialization as successful by default
    _Core->SetState(SQMOD_SUCCESS);
    // Attempt to forward the event
    try
    {
        _Core->EmitPlayerEmbarking(player, vehicle, slot);
    }
    SQMOD_CATCH_EVENT_EXCEPTION(PlayerRequestEnter)
    // See if a reload was requested
    SQMOD_RELOAD_CHECK
    // Return the last known plug-in state
    return _Core->GetState();
}

static void VC_PlayerEnterVehicle(int player, int vehicle, int slot)
{
    // Attempt to forward the event
    try
    {
        _Core->EmitPlayerEmbarked(player, vehicle, slot);
    }
    SQMOD_CATCH_EVENT_EXCEPTION(PlayerEnterVehicle)
    // See if a reload was requested
    SQMOD_RELOAD_CHECK
}

static void VC_PlayerExitVehicle(int player, int vehicle)
{
    // Attempt to forward the event
    try
    {
        _Core->EmitPlayerDisembark(player, vehicle);
    }
    SQMOD_CATCH_EVENT_EXCEPTION(PlayerExitVehicle)
    // See if a reload was requested
    SQMOD_RELOAD_CHECK
}

static int VC_PickupClaimPicked(int pickup, int player)
{
    // Mark the initialization as successful by default
    _Core->SetState(SQMOD_SUCCESS);
    // Attempt to forward the event
    try
    {
        _Core->EmitPickupClaimed(player, pickup);
    }
    SQMOD_CATCH_EVENT_EXCEPTION(PickupClaimPicked)
    // See if a reload was requested
    SQMOD_RELOAD_CHECK
    // Return the last known plug-in state
    return _Core->GetState();
}

static void VC_PickupPickedUp(int pickup, int player)
{
    // Attempt to forward the event
    try
    {
        _Core->EmitPickupCollected(player, pickup);
    }
    SQMOD_CATCH_EVENT_EXCEPTION(PickupPickedUp)
    // See if a reload was requested
    SQMOD_RELOAD_CHECK
}

static void VC_PickupRespawn(int pickup)
{
    // Attempt to forward the event
    try
    {
        _Core->EmitPickupRespawn(pickup);
    }
    SQMOD_CATCH_EVENT_EXCEPTION(PickupRespawn)
    // See if a reload was requested
    SQMOD_RELOAD_CHECK
}

static void VC_VehicleUpdate(int vehicle, int type)
{
    // Attempt to forward the event
    try
    {
        _Core->EmitVehicleUpdate(vehicle, type);
    }
    SQMOD_CATCH_EVENT_EXCEPTION(VehicleUpdate)
    // See if a reload was requested
    SQMOD_RELOAD_CHECK
}

static void VC_VehicleExplode(int vehicle)
{
    // Attempt to forward the event
    try
    {
        _Core->EmitVehicleExplode(vehicle);
    }
    SQMOD_CATCH_EVENT_EXCEPTION(VehicleExplode)
    // See if a reload was requested
    SQMOD_RELOAD_CHECK
}

static void VC_VehicleRespawn(int vehicle)
{
    // Attempt to forward the event
    try
    {
        _Core->EmitVehicleRespawn(vehicle);
    }
    SQMOD_CATCH_EVENT_EXCEPTION(VehicleRespawn)
    // See if a reload was requested
    SQMOD_RELOAD_CHECK
}

static void VC_ObjectShot(int object, int player, int weapon)
{
    // Attempt to forward the event
    try
    {
        _Core->EmitObjectShot(player, object, weapon);
    }
    SQMOD_CATCH_EVENT_EXCEPTION(ObjectShot)
    // See if a reload was requested
    SQMOD_RELOAD_CHECK
}

static void VC_ObjectBump(int object, int player)
{
    // Attempt to forward the event
    try
    {
        _Core->EmitObjectBump(player, object);
    }
    SQMOD_CATCH_EVENT_EXCEPTION(ObjectBump)
    // See if a reload was requested
    SQMOD_RELOAD_CHECK
}

static int VC_PublicMessage(int player, const char * text)
{
    // Mark the initialization as successful by default
    _Core->SetState(SQMOD_SUCCESS);
    // Attempt to forward the event
    try
    {
        _Core->EmitPlayerChat(player, text);
    }
    SQMOD_CATCH_EVENT_EXCEPTION(PublicMessage)
    // See if a reload was requested
    SQMOD_RELOAD_CHECK
    // Return the last known plug-in state
    return _Core->GetState();
}

static int VC_CommandMessage(int player, const char * text)
{
    // Mark the initialization as successful by default
    _Core->SetState(SQMOD_SUCCESS);
    // Attempt to forward the event
    try
    {
        _Core->EmitPlayerCommand(player, text);
    }
    SQMOD_CATCH_EVENT_EXCEPTION(CommandMessage)
    // See if a reload was requested
    SQMOD_RELOAD_CHECK
    // Return the last known plug-in state
    return _Core->GetState();
}

static int VC_PrivateMessage(int player, int target, const char * text)
{
    // Mark the initialization as successful by default
    _Core->SetState(SQMOD_SUCCESS);
    // Attempt to forward the event
    try
    {
        _Core->EmitPlayerMessage(player, target, text);
    }
    SQMOD_CATCH_EVENT_EXCEPTION(PrivateMessage)
    // See if a reload was requested
    SQMOD_RELOAD_CHECK
    // Return the last known plug-in state
    return _Core->GetState();
}

static int VC_InternalCommand(unsigned int type, const char * text)
{
    // Mark the initialization as successful by default
    _Core->SetState(SQMOD_SUCCESS);
    // Attempt to forward the event
    try
    {
        _Core->EmitInternalCommand(type, text);
    }
    SQMOD_CATCH_EVENT_EXCEPTION(InternalCommand)
    // See if a reload was requested
    SQMOD_RELOAD_CHECK
    // Return the last known plug-in state
    return _Core->GetState();
}

static int VC_LoginAttempt(char * name, const char * passwd, const char * address)
{
    // Mark the initialization as successful by default
    _Core->SetState(SQMOD_SUCCESS);
    // Attempt to forward the event
    try
    {
        _Core->EmitLoginAttempt(name, passwd, address);
    }
    SQMOD_CATCH_EVENT_EXCEPTION(LoginAttempt)
    // See if a reload was requested
    SQMOD_RELOAD_CHECK
    // Return the last known plug-in state
    return _Core->GetState();
}

static void VC_EntityPool(int type, int id, unsigned int deleted)
{
    // Attempt to forward the event
    try
    {
        _Core->EmitEntityPool(type, id, static_cast< bool >(deleted));
    }
    SQMOD_CATCH_EVENT_EXCEPTION(EntityPool)
    // See if a reload was requested
    SQMOD_RELOAD_CHECK
}

static void VC_KeyBindDown(int player, int bind)
{
    // Attempt to forward the event
    try
    {
        _Core->EmitPlayerKeyPress(player, bind);
    }
    SQMOD_CATCH_EVENT_EXCEPTION(KeyBindDown)
    // See if a reload was requested
    SQMOD_RELOAD_CHECK
}

static void VC_KeyBindUp(int player, int bind)
{
    // Attempt to forward the event
    try
    {
        _Core->EmitPlayerKeyRelease(player, bind);
    }
    SQMOD_CATCH_EVENT_EXCEPTION(KeyBindUp)
    // See if a reload was requested
    SQMOD_RELOAD_CHECK
}

static void VC_PlayerAway(int player, unsigned int status)
{
    // Attempt to forward the event
    try
    {
        _Core->EmitPlayerAway(player, static_cast< bool >(status));
    }
    SQMOD_CATCH_EVENT_EXCEPTION(PlayerAway)
    // See if a reload was requested
    SQMOD_RELOAD_CHECK
}

static void VC_PlayerSpectate(int player, int target)
{
    // Attempt to forward the event
    try
    {
        _Core->EmitPlayerSpectate(player, target);
    }
    SQMOD_CATCH_EVENT_EXCEPTION(PlayerSpectate)
    // See if a reload was requested
    SQMOD_RELOAD_CHECK
}

static void VC_PlayerCrashReport(int player, const char * report)
{
    // Attempt to forward the event
    try
    {
        _Core->EmitPlayerCrashreport(player, report);
    }
    SQMOD_CATCH_EVENT_EXCEPTION(PlayerCrashReport)
    // See if a reload was requested
    SQMOD_RELOAD_CHECK
}

static void VC_ServerPerformanceReport(int /*count*/, const char ** /*description*/, unsigned long long * /*millis*/)
{
    // Ignored for now...
}

static void VC_PlayerName(int player, const char * previous, const char * current)
{
    // Attempt to forward the event
    try
    {
        _Core->EmitPlayerRename(player, previous, current);
    }
    SQMOD_CATCH_EVENT_EXCEPTION(PlayerName)
    // See if a reload was requested
    SQMOD_RELOAD_CHECK
}

static void VC_PlayerState(int player, int previous, int current)
{
    // Attempt to forward the event
    try
    {
        _Core->EmitPlayerState(player, previous, current);
        // Identify the current state and trigger the listeners specific to that
        switch (current)
        {
            case SQMOD_PLAYER_STATE_NONE:
                _Core->EmitStateNone(player, previous);
            break;
            case SQMOD_PLAYER_STATE_NORMAL:
                _Core->EmitStateNormal(player, previous);
            break;
            case SQMOD_PLAYER_STATE_SHOOTING:
                _Core->EmitStateShooting(player, previous);
            break;
            case SQMOD_PLAYER_STATE_DRIVER:
                _Core->EmitStateDriver(player, previous);
            break;
            case SQMOD_PLAYER_STATE_PASSENGER:
                _Core->EmitStatePassenger(player, previous);
            break;
            case SQMOD_PLAYER_STATE_ENTERING_AS_DRIVER:
                _Core->EmitStateEnterDriver(player, previous);
            break;
            case SQMOD_PLAYER_STATE_ENTERING_AS_PASSENGER:
                _Core->EmitStateEnterPassenger(player, previous);
            break;
            case SQMOD_PLAYER_STATE_EXITING_VEHICLE:
                _Core->EmitStateExitVehicle(player, previous);
            break;
            case SQMOD_PLAYER_STATE_UNSPAWNED:
                _Core->EmitStateUnspawned(player, previous);
            break;
        }
    }
    SQMOD_CATCH_EVENT_EXCEPTION(PlayerState)
    // See if a reload was requested
    SQMOD_RELOAD_CHECK
}

static void VC_PlayerAction(int player, int previous, int current)
{
    // Attempt to forward the event
    try
    {
        _Core->EmitPlayerAction(player, previous, current);
        // Identify the current action and trigger the listeners specific to that
        switch (current)
        {
            case SQMOD_PLAYER_ACTION_NONE:
                _Core->EmitActionNone(player, previous);
            break;
            case SQMOD_PLAYER_ACTION_NORMAL:
                _Core->EmitActionNormal(player, previous);
            break;
            case SQMOD_PLAYER_ACTION_AIMING:
                _Core->EmitActionAiming(player, previous);
            break;
            case SQMOD_PLAYER_ACTION_SHOOTING:
                _Core->EmitActionShooting(player, previous);
            break;
            case SQMOD_PLAYER_ACTION_JUMPING:
                _Core->EmitActionJumping(player, previous);
            break;
            case SQMOD_PLAYER_ACTION_LYING_ON_GROUND:
                _Core->EmitActionLieDown(player, previous);
            break;
            case SQMOD_PLAYER_ACTION_GETTING_UP:
                _Core->EmitActionGettingUp(player, previous);
            break;
            case SQMOD_PLAYER_ACTION_JUMPING_FROM_VEHICLE:
                _Core->EmitActionJumpVehicle(player, previous);
            break;
            case SQMOD_PLAYER_ACTION_DRIVING:
                _Core->EmitActionDriving(player, previous);
            break;
            case SQMOD_PLAYER_ACTION_DYING:
                _Core->EmitActionDying(player, previous);
            break;
            case SQMOD_PLAYER_ACTION_WASTED:
                _Core->EmitActionWasted(player, previous);
            break;
            case SQMOD_PLAYER_ACTION_ENTERING_VEHICLE:
                _Core->EmitActionEmbarking(player, previous);
            break;
            case SQMOD_PLAYER_ACTION_EXITING_VEHICLE:
                _Core->EmitActionDisembarking(player, previous);
            break;
        }
    }
    SQMOD_CATCH_EVENT_EXCEPTION(PlayerAction)
    // See if a reload was requested
    SQMOD_RELOAD_CHECK
}

static void VC_PlayerOnFire(int player, unsigned int state)
{
    // Attempt to forward the event
    try
    {
        _Core->EmitPlayerBurning(player, static_cast< bool >(state));
    }
    SQMOD_CATCH_EVENT_EXCEPTION(PlayerOnFire)
    // See if a reload was requested
    SQMOD_RELOAD_CHECK
}

static void VC_PlayerCrouch(int player, unsigned int state)
{
    // Attempt to forward the event
    try
    {
        _Core->EmitPlayerCrouching(player, static_cast< bool >(state));
    }
    SQMOD_CATCH_EVENT_EXCEPTION(PlayerCrouch)
    // See if a reload was requested
    SQMOD_RELOAD_CHECK
}

static void VC_PlayerGameKeys(int player, int previous, int current)
{
    // Attempt to forward the event
    try
    {
        _Core->EmitPlayerGameKeys(player, previous, current);
    }
    SQMOD_CATCH_EVENT_EXCEPTION(PlayerGameKeys)
    // See if a reload was requested
    SQMOD_RELOAD_CHECK
}

static void VC_OnCheckpointEntered(int checkpoint, int player)
{
    // Attempt to forward the event
    try
    {
        _Core->EmitCheckpointEntered(player, checkpoint);
    }
    SQMOD_CATCH_EVENT_EXCEPTION(CheckpointEntered)
    // See if a reload was requested
    SQMOD_RELOAD_CHECK
}

static void VC_OnCheckpointExited(int checkpoint, int player)
{
    // Attempt to forward the event
    try
    {
        _Core->EmitCheckpointExited(player, checkpoint);
    }
    SQMOD_CATCH_EVENT_EXCEPTION(CheckpointExited)
    // See if a reload was requested
    SQMOD_RELOAD_CHECK
}

static void VC_OnSphereEntered(int sphere, int player)
{
    // Attempt to forward the event
    try
    {
        _Core->EmitForcefieldEntered(player, sphere);
    }
    SQMOD_CATCH_EVENT_EXCEPTION(SphereEntered)
    // See if a reload was requested
    SQMOD_RELOAD_CHECK
}

static void VC_OnSphereExited(int sphere, int player)
{
    // Attempt to forward the event
    try
    {
        _Core->EmitForcefieldExited(player, sphere);
    }
    SQMOD_CATCH_EVENT_EXCEPTION(SphereExited)
    // See if a reload was requested
    SQMOD_RELOAD_CHECK
}

// ------------------------------------------------------------------------------------------------
void BindCallbacks()
{
    _Clbk->OnInitServer                 = VC_InitServer;
    _Clbk->OnShutdownServer             = VC_ShutdownServer;
    _Clbk->OnFrame                      = VC_Frame;
    _Clbk->OnPlayerConnect              = VC_PlayerConnect;
    _Clbk->OnPlayerDisconnect           = VC_PlayerDisconnect;
    _Clbk->OnPlayerBeginTyping          = VC_PlayerBeginTyping;
    _Clbk->OnPlayerEndTyping            = VC_PlayerEndTyping;
    _Clbk->OnPlayerRequestClass         = VC_PlayerRequestClass;
    _Clbk->OnPlayerRequestSpawn         = VC_PlayerRequestSpawn;
    _Clbk->OnPlayerSpawn                = VC_PlayerSpawn;
    _Clbk->OnPlayerDeath                = VC_PlayerDeath;
    _Clbk->OnPlayerUpdate               = VC_PlayerUpdate;
    _Clbk->OnPlayerRequestEnter         = VC_PlayerRequestEnter;
    _Clbk->OnPlayerEnterVehicle         = VC_PlayerEnterVehicle;
    _Clbk->OnPlayerExitVehicle          = VC_PlayerExitVehicle;
    _Clbk->OnPickupClaimPicked          = VC_PickupClaimPicked;
    _Clbk->OnPickupPickedUp             = VC_PickupPickedUp;
    _Clbk->OnPickupRespawn              = VC_PickupRespawn;
    _Clbk->OnVehicleUpdate              = VC_VehicleUpdate;
    _Clbk->OnVehicleExplode             = VC_VehicleExplode;
    _Clbk->OnVehicleRespawn             = VC_VehicleRespawn;
    _Clbk->OnObjectShot                 = VC_ObjectShot;
    _Clbk->OnObjectBump                 = VC_ObjectBump;
    _Clbk->OnPublicMessage              = VC_PublicMessage;
    _Clbk->OnCommandMessage             = VC_CommandMessage;
    _Clbk->OnPrivateMessage             = VC_PrivateMessage;
    _Clbk->OnInternalCommand            = VC_InternalCommand;
    _Clbk->OnLoginAttempt               = VC_LoginAttempt;
    _Clbk->OnEntityPoolChange           = VC_EntityPool;
    _Clbk->OnKeyBindDown                = VC_KeyBindDown;
    _Clbk->OnKeyBindUp                  = VC_KeyBindUp;
    _Clbk->OnPlayerAwayChange           = VC_PlayerAway;
    _Clbk->OnPlayerSpectate             = VC_PlayerSpectate;
    _Clbk->OnPlayerCrashReport          = VC_PlayerCrashReport;
    _Clbk->OnServerPerformanceReport    = VC_ServerPerformanceReport;
    _Clbk->OnPlayerNameChange           = VC_PlayerName;
    _Clbk->OnPlayerStateChange          = VC_PlayerState;
    _Clbk->OnPlayerActionChange         = VC_PlayerAction;
    _Clbk->OnPlayerOnFireChange         = VC_PlayerOnFire;
    _Clbk->OnPlayerCrouchChange         = VC_PlayerCrouch;
    _Clbk->OnPlayerGameKeysChange       = VC_PlayerGameKeys;
    _Clbk->OnCheckpointEntered          = VC_OnCheckpointEntered;
    _Clbk->OnCheckpointExited           = VC_OnCheckpointExited;
    _Clbk->OnSphereEntered              = VC_OnSphereEntered;
    _Clbk->OnSphereExited               = VC_OnSphereExited;
}

// ------------------------------------------------------------------------------------------------
void UnbindCallbacks()
{
    _Clbk->OnInitServer                 = nullptr;
    _Clbk->OnShutdownServer             = nullptr;
    _Clbk->OnFrame                      = nullptr;
    _Clbk->OnPlayerConnect              = nullptr;
    _Clbk->OnPlayerDisconnect           = nullptr;
    _Clbk->OnPlayerBeginTyping          = nullptr;
    _Clbk->OnPlayerEndTyping            = nullptr;
    _Clbk->OnPlayerRequestClass         = nullptr;
    _Clbk->OnPlayerRequestSpawn         = nullptr;
    _Clbk->OnPlayerSpawn                = nullptr;
    _Clbk->OnPlayerDeath                = nullptr;
    _Clbk->OnPlayerUpdate               = nullptr;
    _Clbk->OnPlayerRequestEnter         = nullptr;
    _Clbk->OnPlayerEnterVehicle         = nullptr;
    _Clbk->OnPlayerExitVehicle          = nullptr;
    _Clbk->OnPickupClaimPicked          = nullptr;
    _Clbk->OnPickupPickedUp             = nullptr;
    _Clbk->OnPickupRespawn              = nullptr;
    _Clbk->OnVehicleUpdate              = nullptr;
    _Clbk->OnVehicleExplode             = nullptr;
    _Clbk->OnVehicleRespawn             = nullptr;
    _Clbk->OnObjectShot                 = nullptr;
    _Clbk->OnObjectBump                 = nullptr;
    _Clbk->OnPublicMessage              = nullptr;
    _Clbk->OnCommandMessage             = nullptr;
    _Clbk->OnPrivateMessage             = nullptr;
    _Clbk->OnInternalCommand            = nullptr;
    _Clbk->OnLoginAttempt               = nullptr;
    _Clbk->OnEntityPoolChange           = nullptr;
    _Clbk->OnKeyBindDown                = nullptr;
    _Clbk->OnKeyBindUp                  = nullptr;
    _Clbk->OnPlayerAwayChange           = nullptr;
    _Clbk->OnPlayerSpectate             = nullptr;
    _Clbk->OnPlayerCrashReport          = nullptr;
    _Clbk->OnServerPerformanceReport    = nullptr;
    _Clbk->OnPlayerNameChange           = nullptr;
    _Clbk->OnPlayerStateChange          = nullptr;
    _Clbk->OnPlayerActionChange         = nullptr;
    _Clbk->OnPlayerOnFireChange         = nullptr;
    _Clbk->OnPlayerCrouchChange         = nullptr;
    _Clbk->OnPlayerGameKeysChange       = nullptr;
    _Clbk->OnCheckpointEntered          = nullptr;
    _Clbk->OnCheckpointExited           = nullptr;
    _Clbk->OnSphereEntered              = nullptr;
    _Clbk->OnSphereExited               = nullptr;
}