shqke / sp_public

GNU General Public License v3.0
13 stars 3 forks source link

remove_touch_link cause teleport #8

Closed lunatixxx closed 2 years ago

lunatixxx commented 2 years ago

The moment i installed your plugin it teleported 4 players in less than one minute in versus. I was alerted by the jockey unteleport plugin, it even teleported special infecteds.

"[!] Attempting to fix position of Hunter after jockey teleportation bug."

Uninstalled it and nothing happen anymore.

Server Ubuntu versions newer than 18, i think what this plugin modify could give us a lead on what cause this jockey teleportation bug.

https://github.com/SirPlease/L4D2-Competitive-Rework/issues/482

shqke commented 2 years ago

Could you provide with steps on how to repro it on a clean server setup?

lunatixxx commented 2 years ago

I don't think there is a specific stepto reproduce that, the jockey teleport is something that happen on official too randomly. But your plugin really broke the game and started to teleport players without even a jockey alive, install it on a server of the said version with the jockey unteleport plugin and launch a versus game.

#include <sourcemod>
#include <sdkhooks>
#include <sdktools>
#include <colors>
#include <left4dhooks>
#include <smac>
#include <smac_stocks>

char path[256];

#define debug 0

#define VICTIM_SAVE_INTERVAL 1.2
#define VICTIM_CHECK_INTERVAL 2.0

enum L4D2Team
{
    L4D2Team_None = 0,
    L4D2Team_Spectator,
    L4D2Team_Survivor,
    L4D2Team_Infected
}

Handle g_hJockeyRideCheck_Timer;
Handle g_hSaveVictimPosition_Timer;
new Handle:hDisableJockeyUnteleportMaps;

int g_iJockeyVictim;
int g_iJockey;
float g_fVictimPrevPos[3];
float g_fMapCenter[3];
bool g_bIsEnabled;

public Plugin:myinfo =
{
    name = "Jockey Unteleport",
    author = "larrybrains",
    description = "Teleports a survivor back into the map if they are randomly teleported outside of the map while jockeyed.",
    version = "1.0",
    url = "https://larrymod.com"
};

public OnPluginStart()
{

    HookEvent("jockey_ride", Event_JockeyRide);
    HookEvent("jockey_ride_end", Event_JockeyRideEnd);
    HookEvent("jockey_killed", Event_JockeyDeath);
    HookEvent("player_death", Event_PlayerDeath, EventHookMode_Pre);
    HookEvent("player_disconnect", Event_PlayerDisconnect, EventHookMode_Pre);

    hDisableJockeyUnteleportMaps = CreateTrie();

    RegServerCmd("jockey_unteleport_disabled", DisableJockeyUnteleport);

    BuildPath(Path_SM, path, 256, "logs/jockeybug.txt");

}

public Action:DisableJockeyUnteleport(args)
{
    decl String:mapname[64];
    GetCmdArg(1, mapname, sizeof(mapname));
    SetTrieValue(hDisableJockeyUnteleportMaps, mapname, true);
}

public void OnMapStart()
{
    g_hJockeyRideCheck_Timer = null;
    g_hSaveVictimPosition_Timer = null;

    g_fMapCenter[0] = 0.0;
    g_fMapCenter[1] = 0.0;
    g_fMapCenter[2] = 0.0;

    decl String:mapname[64];
    GetCurrentMap(mapname, sizeof(mapname));

    decl dummy;
    if (GetTrieValue(hDisableJockeyUnteleportMaps, mapname, dummy))
    {
        g_bIsEnabled = false;

        #if debug
            CPrintToChatAll("{blue}[Jockey UnTeleport]{default} is disabled.");
        #endif

    }
    else
    {
        g_bIsEnabled = true;

        #if debug
            CPrintToChatAll("{blue}[Jockey UnTeleport]{default} is enabled.");
        #endif

    }
}

public Action Event_JockeyDeath(Event h_Event, const char[] s_Name, bool b_DontBroadcast)
{
    if (!g_bIsEnabled)
        return Plugin_Handled;

    delete g_hJockeyRideCheck_Timer;
    delete g_hSaveVictimPosition_Timer;
    g_iJockeyVictim = -1;
    g_iJockey = -1;
    #if debug
        CPrintToChatAll("{blue}[Jockey UnTeleport]{default} Jockey killed.");
    #endif

    return Plugin_Continue;
}

public Action Event_JockeyRideEnd(Event hEvent, const char[] s_Name, bool b_DontBroadcast)
{
    if (!g_bIsEnabled)
        return Plugin_Handled;

    delete g_hJockeyRideCheck_Timer;
    delete g_hSaveVictimPosition_Timer;
    g_iJockeyVictim = -1;
    g_iJockey = -1;

    #if debug
        CPrintToChatAll("{blue}[Jockey UnTeleport]{default} Jockey ride ended.");
    #endif

    return Plugin_Continue;
}

public Action Event_JockeyRide(Event event, const char[] name, bool dontBroadcast)
{
    if (!g_bIsEnabled)
        return Plugin_Handled;

    new jockey = GetClientOfUserId(GetEventInt(event, "userid"));
    new victim = GetClientOfUserId(GetEventInt(event, "victim"));
    static float startVictimPos[3];

    if (IsClientInGame(jockey))
    {
        if (IsPlayerAlive(jockey))
        {
            g_iJockey = jockey;
        }
    }

    if (IsClientInGame(victim))
    {
        if (IsPlayerAlive(victim))
        {

            GetClientAbsOrigin(victim, startVictimPos);
            g_fVictimPrevPos = startVictimPos;

            #if debug
                CPrintToChatAll("{blue}[Jockey UnTeleport]{default} initial victim position saved. Origin: %.1f, %.1f, %.1f", g_fVictimPrevPos[0], g_fVictimPrevPos[1], g_fVictimPrevPos[2] );
            #endif

            g_iJockeyVictim = victim;

            if (g_hJockeyRideCheck_Timer == null)
            {
                g_hJockeyRideCheck_Timer = CreateTimer(VICTIM_CHECK_INTERVAL, JockeyRideCheck_Timer, victim, TIMER_REPEAT);
            }
            if (g_hSaveVictimPosition_Timer == null)
            {
                g_hSaveVictimPosition_Timer = CreateTimer(VICTIM_SAVE_INTERVAL, SaveVictimPosition_Timer, victim, TIMER_REPEAT);
            }
        }
    }
    return Plugin_Continue;
}

public void Event_PlayerDeath(Event hEvent, const char[] name, bool dontBroadcast)
{
    if (!g_bIsEnabled)
        return;

    static int client;
    client = GetClientOfUserId(hEvent.GetInt("userid"));

    if (client != 0) {
        if (client == g_iJockeyVictim || client == g_iJockey) {
            g_iJockeyVictim = -1;
            g_iJockey = -1;
            delete g_hJockeyRideCheck_Timer;
            delete g_hSaveVictimPosition_Timer;
        }
    }
}

public void Event_PlayerDisconnect(Event hEvent, const char[] name, bool dontBroadcast)
{
    if (!g_bIsEnabled)
        return;

    static int client;
    client = GetClientOfUserId(hEvent.GetInt("userid"));

    if (client != 0) {
        if (client == g_iJockeyVictim || client == g_iJockey ) {
            g_iJockeyVictim = -1;
            g_iJockey = -1;
            delete g_hJockeyRideCheck_Timer;
            delete g_hSaveVictimPosition_Timer;
        }
    }
}

public Action SaveVictimPosition_Timer(Handle timer, any victim)
{
    if (!g_bIsEnabled)
        return Plugin_Stop;

    static float prevVictimPos[3];
    static bool isOutsideWorld;
    if (IsClientInGame(victim))
    {
        if (IsPlayerAlive(victim))
        {
            GetClientAbsOrigin(victim, prevVictimPos);
            isOutsideWorld = TR_PointOutsideWorld(prevVictimPos);
            float distanceToCenter = GetVectorDistance(prevVictimPos, g_fMapCenter);

            if ( !isOutsideWorld && distanceToCenter >= 150)
            {
                g_fVictimPrevPos = prevVictimPos;

                #if debug
                    CPrintToChatAll("{blue}[Jockey UnTeleport]{default} victim position saved. Origin: %.1f, %.1f, %.1f", g_fVictimPrevPos[0], g_fVictimPrevPos[1], g_fVictimPrevPos[2] );
                #endif
            }
        }
    }
    return Plugin_Continue;
}

public Action JockeyRideCheck_Timer(Handle timer, any victim)
{
    if (!g_bIsEnabled)
        return Plugin_Stop;

    static float newVictimPos[3];
    static bool isOutsideWorld;
    if(IsClientInGame(victim))
    {
        if (IsPlayerAlive(victim))
        {
            GetClientAbsOrigin(victim, newVictimPos);
            isOutsideWorld = TR_PointOutsideWorld(newVictimPos);
            float distance = GetVectorDistance(newVictimPos, g_fVictimPrevPos);
            float distanceToCenter = GetVectorDistance(newVictimPos, g_fMapCenter);

            if ( isOutsideWorld || distance >= 750 || distanceToCenter < 150 )
            {
                TeleportToPrevPos(victim);

                SMAC_PrintAdminNotice("Attempting to fix position of %N after jockey teleportation bug.", victim, g_iJockey);

                LogAction(-1, -1, "[Jockey UnTeleport] Survivor %N teleported by Jockey %N, origin: %.1f, %.1f, %.1f distance: %.1f distance to center: %.1f world:(%s)", victim, g_iJockey, newVictimPos[0], newVictimPos[1], newVictimPos[2], distance, distanceToCenter, isOutsideWorld ? "OutsideWorld" : "InsideWorld");

                LogToFile(path, "[Jockey UnTeleport] Survivor %N teleported by Jockey %N, origin: %.1f, %.1f, %.1f distance: %.1f distance to center: %.1f world:(%s)", victim, g_iJockey, newVictimPos[0], newVictimPos[1], newVictimPos[2], distance, distanceToCenter, isOutsideWorld ? "OutsideWorld" : "InsideWorld");

            }
        }
    }
    return Plugin_Continue;
}

void TeleportToPrevPos(int victim)
{
    if(IsClientInGame(victim))
    {
        if (IsPlayerAlive(victim))
        {
            TeleportEntity(victim, g_fVictimPrevPos, NULL_VECTOR, NULL_VECTOR);

            #if debug
                CPrintToChatAll("{blue}[Jockey UnTeleport]{default} trying to teleport survivor.");
            #endif
        }
    }
}
shqke commented 2 years ago

If you install plugin A, then install plugin B, and it misbehaves, would you automatically assume plugin B is a culprit?

My plugin couples tight with game logic by unlinking triggers from player on every team change event. Its behavior is easier to predict than combination of many individual plugins in linked repository.

Known problems could cause nested player_team events (including ones from death events), which can usually be patched up by a delayed team change (in case of plugins), or an additional condition in my plugin, example: https://github.com/shqke/sp_public/blob/b3da30154d4a137d7193923ab7446a56f82b6510/remove_touch_links/scripting/remove_touch_links.sp#L18-L21

I'd need more information, if you want me to help with your case.

lunatixxx commented 2 years ago

What will be your conclusion if you had no problem before, install a plugin and sunddenly everyone teleport, then remove it and no more problems for the next games ?

Jockey anti teleport does nothing on his own, the player have to be jockeyed and teleported. The fact that you are removing touch_link seems to cause a problem that's all i know.

shqke commented 2 years ago

You've spotted a false pattern and made abrupt conclusions, but you can't claim that you know which modification is the culprit, before figuring out what exactly is happening on your server. You shouldn't debug a problem by historical activation order of server mods. I'm also not going to modify it just to support intrusive behavior of mods you have installed.

Specifics of the entry point and bug itself also disallow me to do much about nested consequences of player_team event invocation, other than placing additional condition just to support a clean server installation. My plugin mimicks natural game behavior and avoids any internal stateful logic.

If you can't be bothered to locate a problem, then simply don't use my plugin. But, if you're determined to find the issue, then I'll need more information.