Функция ложит сервер

Vit_ amin

Добрая душа
Сообщения
1,504
Реакции
660
Доброго времени суток
Есть код (старый и заведово "плохой", по не понятной мне причине он ложит сервер)
PHP:
void RemoveSurvivorRagdoll(int iSurvivorCharacter)
{
    int iCharacterType;
    while ((iEntity = FindEntityByClassname(MaxClients + 1, "survivor_death_model")) != INVALID_ENT_REFERENCE)
    {
        iCharacterType = GetEntProp(iEntity, Prop_Send, "m_nCharacterType");
    
        for (int i = 1; i <= MaxClients; i++)
        {
            if (!IsClientInGame(i) || GetClientTeam(i) != TEAM_SURVIVOR || iCharacterType != iSurvivorCharacter)
            {
                continue;
            }
        
            if (IsValidEntity(iEntity))
            {
                RemoveEntity(iEntity);
            }
        }
    }
}
Данный кусок код (подчеркиваю именно данный кусок кода, так как я все продебажил и пришёл к выводу что это из-за него), ложит сервер. Так сервер падает таким образом, что в логах нету ошибок и даже Acelerator молчит. Сервер на ОС Linux. Я уже всё что можно поменял в данном куске кода, но так и не смог понять почему (делал RequestFrame и CreateTimer на удаление Ragdoll'ов, думал уже надо делать проверку на то, жив ли игрок или нет - написал в консоли
C-подобный:
ent_fire survivor_death_model Kill
Все нормально, никаких проблем. Странно только то, что сервер падает только в том случае, если трупы удаляются через MultiFilter (@Dead, @All и прочие) так как удаляю ragdoll'ы я через RegCmdAdmin
Ошибка: Alarm Clock, Segment Fault

А вот следующий код работает стабильно:
PHP:
void RemoveSurvivorRagdoll(int iSurvivorCharacter)
{
    int iMaxEntities = GetMaxEntities();
    char szClassname[32];
    for (int iEntity = MaxClients + 1; iEntity < iMaxEntities; iEntity++)
    {
        if (!IsValidEntity(iEntity))
        {
            continue;
        }
    
        GetEntityClassname(iEntity, szClassname, sizeof(szClassname));
        if (strcmp(szClassname, "survivor_death_model", false) != 0)
        {
            continue;
        }
    
        for (int iClient = 1; iClient <= MaxClients; iClient++)
        {
            if (!IsClientInGame(iClient) || GetClientTeam(iClient) != TEAM_SURVIVOR)
            {
                continue;
            }
        
            if (iSurvivorCharacter != GetEntProp(iEntity, Prop_Send, "m_nCharacterType"))
            {
                continue;
            }
        
            RemoveEntity(iEntity);
            return;
        }
    }
}
Я даже не знаю куда копать, может я в скриптинге что-то перестал понимать
Собственно вопрос интереса, так как я уже написал готовое решение.
Почему так и где у меня ошибка ?
P.S. Полный код, если нужно:
PHP:
//float g_flPosition[3];
Handle g_hRespawnPlayer = null;
TopMenu g_hTopMenu = null;

public void OnPluginStart()
{
    LoadTranslations("common.phrases");
    LoadTranslations("l4d2_respawn.phrases");

    RegAdminCmd("sm_respawn", Command_Respawn, ADMFLAG_SLAY, "sm_respawn <#userid|name>");

    RegisterTeamFilter();

    // ========================================================================================
    /* Account for late loading */
    // ========================================================================================
    TopMenu hTopMenu;
    if (LibraryExists("adminmenu") && ((hTopMenu = GetAdminTopMenu()) != null))
    {
        OnAdminMenuReady(hTopMenu);
    }

    // ========================================================================================

    GameData hGameData = new GameData("l4d2_gamedata");
    if (hGameData == null)
    {
        delete hGameData;
        SetFailState("Failed to load \"l4d2_gamedata.txt\" gamedata");
    }

    // ========================================================================================
    // CTerrorPlayer::RoundRespawn - Signature
    // ========================================================================================
    StartPrepSDKCall(SDKCall_Player);
    if (!PrepSDKCall_SetFromConf(hGameData, SDKConf_Signature, "CTerrorPlayer::RoundRespawn"))
    {
        delete hGameData;
        SetFailState("Error finding the 'CTerrorPlayer::RoundRespawn' signature.");
    }

    g_hRespawnPlayer = EndPrepSDKCall();
    if (g_hRespawnPlayer == null)
    {
        delete hGameData;
        SetFailState("Unable to prep SDKCall 'CTerrorPlayer::RoundRespawn'");
    }

    // ========================================================================================
    delete hGameData;
}

// ========================================================================================
// RegCmdAdmin
// ========================================================================================
public Action Command_Respawn(int iClient, int iArgs)
{
    if (iArgs < 1)
    {
        ReplyToCommand(iClient, "%s Usage: sm_respawn <#userid|name>", MESSAGE_PREFIX);
        return Plugin_Handled;
    }

    char arg[19];
    GetCmdArg(1, arg, sizeof(arg));

    char target_name[MAX_TARGET_LENGTH];
    int target_list[MAXPLAYERS_L4D2], target_count;
    bool tn_is_ml;

    if ((target_count = ProcessTargetString(
            arg,
            iClient,
            target_list,
            MAXPLAYERS,
            COMMAND_FILTER_DEAD,
            target_name,
            sizeof(target_name),
            tn_is_ml)) <= 0)
    {
        ReplyToTargetError(iClient, target_count);
        return Plugin_Handled;
    }

    int iTarget;
    int iTargetCountPost;

    // Team filter dead players, re-order target_list array with iTargetCountPost
    for (int i = 0; i < target_count; i++)
    {
        iTarget = target_list[i];

        if (GetClientTeam(iTarget) == TEAM_SURVIVOR)
        {
            target_list[iTargetCountPost] = iTarget; // re-order
            iTargetCountPost++;
        }
    }

    if (iTargetCountPost == COMMAND_TARGET_NONE)
    {
        ReplyToTargetError(iClient, iTargetCountPost);
        return Plugin_Handled;
    }

    // re-set new value
    target_count = iTargetCountPost;

    if (tn_is_ml)
    {
        ShowActivity2(iClient, MESSAGE_PREFIX_COLOR, "%t", "Respawned survivors", target_name);
    }
    else
    {
        ShowActivity2(iClient, MESSAGE_PREFIX_COLOR, "%t", "Respawned survivor", "_s", target_name);
    }

    for (int i = 0; i < target_count; i++)
    {
        PerformRespawn(iClient, target_list[i]);
    }

    return Plugin_Handled;
}

// ========================================================================================
// Spawn Survivor Entity
// ========================================================================================
void PerformRespawn(int iClient, int iTarget)
{
    LogAction(iClient, iTarget, "\"%L\" respawned \"%L\"", iClient, iTarget);

    //RespawnPlayer(iTarget);
    SDKCall(g_hRespawnPlayer, iTarget);

    RemoveSurvivorRagdoll(GetEntProp(iTarget, Prop_Send, "m_survivorCharacter"));
}

/*void RespawnPlayer(int iClient)
{
    int iFlags = GetCommandFlags("respawn");
    SetCommandFlags("respawn", iFlags &~FCVAR_CHEAT);
    FakeClientCommand(iClient, "respawn");
    SetCommandFlags("respawn", iFlags|FCVAR_CHEAT);
}*/

// ========================================================================================
// Remove Old Ragdoll
// ========================================================================================
void RemoveSurvivorRagdoll(int iSurvivorCharacter)
{
    int iMaxEntities = GetMaxEntities();
    char szClassname[32];
    for (int iEntity = MaxClients + 1; iEntity < iMaxEntities; iEntity++)
    {
        if (!IsValidEntity(iEntity))
        {
            continue;
        }
    
        GetEntityClassname(iEntity, szClassname, sizeof(szClassname));
        if (strcmp(szClassname, "survivor_death_model", false) != 0)
        {
            continue;
        }
    
        for (int iClient = 1; iClient <= MaxClients; iClient++)
        {
            if (!IsClientInGame(iClient) || GetClientTeam(iClient) != TEAM_SURVIVOR)
            {
                continue;
            }
        
            if (iSurvivorCharacter != GetEntProp(iEntity, Prop_Send, "m_nCharacterType"))
            {
                continue;
            }
        
            RemoveEntity(iEntity);
            return;
        }
    }

    /*int iCharacterType;
    while ((iEntity = FindEntityByClassname(MaxClients + 1, "survivor_death_model")) != INVALID_ENT_REFERENCE)
    {
        iCharacterType = GetEntProp(iEntity, Prop_Send, "m_nCharacterType");
    
        for (int i = 1; i <= MaxClients; i++)
        {
            if (!IsClientInGame(i) || GetClientTeam(i) != TEAM_SURVIVOR || iCharacterType != iSurvivorCharacter)
            {
                continue;
            }
        
            if (IsValidEntity(iEntity))
            {
                RemoveEntity(iEntity);
            }
        }
    }*/
}

// ========================================================================================
// OnAdminMenuReady - Building Menu Section in Main Menu
// ========================================================================================
public void OnAdminMenuReady(Handle aTopMenu)
{
    TopMenu hTopMenu = TopMenu.FromHandle(aTopMenu);

    /* Block us from being called twice */
    if (hTopMenu == g_hTopMenu)
    {
        return;
    }

    /* Save the Handle */
    g_hTopMenu = hTopMenu;

    /* Build the "Player Commands" category */
    TopMenuObject player_commands = g_hTopMenu.FindCategory(ADMINMENU_PLAYERCOMMANDS);

    if (player_commands != INVALID_TOPMENUOBJECT)
    {
        g_hTopMenu.AddItem("sm_respawn", AdminMenu_Respawn, player_commands, "", ADMFLAG_SLAY);
    }
}

// ========================================================================================
public void AdminMenu_Respawn(TopMenu hTopMenu,
                              TopMenuAction hAction,
                              TopMenuObject hObject,
                              int iAdminID,
                              char[] szBuffer,
                              int iMaxLength)
// ========================================================================================
{
    switch (hAction)
    {
        case TopMenuAction_DisplayOption:
        {
            FormatEx(szBuffer, iMaxLength, "%T", "Respawn player", iAdminID);
        }
        case TopMenuAction_SelectOption:
        {
            DisplayRespawnMenu(iAdminID);
        }
    }
}

// ========================================================================================
void DisplayRespawnMenu(int iAdminID)
{
    Menu hMenu = new Menu(MenuHandler_RespawnPlayer, MENU_ACTIONS_DEFAULT | MenuAction_Display);

    char szBuffer[40];
    char szUserID[8];
    int iDeadSurvivors = 0;

    for (int i = 1; i <= MaxClients; i++)
    {
        if (!IsClientInGame(i) || GetClientTeam(i) != TEAM_SURVIVOR || IsPlayerAlive(i))
        {
            continue;
        }
    
        FormatEx(szUserID, sizeof(szUserID), "%i", GetClientUserId(i));
        FormatEx(szBuffer, sizeof(szBuffer), "%N (%s)", i, szUserID);
    
        hMenu.AddItem(szUserID, szBuffer);
    
        iDeadSurvivors++;
    }

    switch (iDeadSurvivors)
    {
        case 0:
        {
            PrintToChat(iAdminID, "%s %t", MESSAGE_PREFIX_COLOR, "No matching clients");
            g_hTopMenu.Display(iAdminID, TopMenuPosition_LastCategory);
        }
        default:
        {
            hMenu.ExitBackButton = true;
            hMenu.Display(iAdminID, MENU_TIME_FOREVER);
        }
    }
}

// ========================================================================================
public int MenuHandler_RespawnPlayer(Menu hMenu, MenuAction hAction, int iAdminID, int iItem)
{
    switch (hAction)
    {
        case MenuAction_Cancel:
        {
            if (iItem == MenuCancel_ExitBack && g_hTopMenu != null)
            {
                g_hTopMenu.Display(iAdminID, TopMenuPosition_LastCategory);
            }
        }
        case MenuAction_Display:
        {
            char szTitle[128];
            FormatEx(szTitle, sizeof(szTitle), "%T:", "Respawn player", iAdminID);
            (view_as<Panel>(iItem)).SetTitle(szTitle);
        }
        case MenuAction_End:
        {
            delete hMenu;
        }
        case MenuAction_Select:
        {
            char szUserID[8];
            int iTarget;
        
            hMenu.GetItem(iItem, szUserID, sizeof(szUserID));
        
            if ((iTarget = GetClientOfUserId(StringToInt(szUserID))) == 0)
            {
                PrintToChat(iAdminID, "%s %t", MESSAGE_PREFIX_COLOR, "Player no longer available");
            }
            else if (!CanUserTarget(iAdminID, iTarget))
            {
                PrintToChat(iAdminID, "%s %t", MESSAGE_PREFIX_COLOR, "Unable to target");
            }
            else
            {
                LogAction(iAdminID, iTarget, "\"%L\" respawned \"%L\"", iAdminID, iTarget);
                PerformRespawn(iAdminID, iTarget);
            }

            /* Re-draw the menu if they're still valid */
            if (IsClientInGame(iAdminID) && !IsClientInKickQueue(iAdminID))
            {
                DisplayRespawnMenu(iAdminID);
            }
        }
    }
}
 

DarklSide

Участник
Сообщения
931
Реакции
468
@Vit_ amin, у вас два цикла, попробуйте завершить их после нужного действия.
В первом варианте вы удаляете заного iEntity каждый раз в цикле игроков, если iCharacterType == iSurvivorCharacter.
Во втором, сразу завершаете функцию после удаления.
 

Vit_ amin

Добрая душа
Сообщения
1,504
Реакции
660
@DarklSide
Я делал return во втором цикле (цикл for) - сервер все равно падает
Я просто убрал его (так как код закомментировал)
 

Grey83

не пишу плагины с весны 2022
Сообщения
8,519
Реакции
4,979
Попробуй заменить функцию на вот эту:
C-подобный:
void RemoveSurvivorRagdoll(int iSurvivorCharacter)
{
    int ent = MaxClients+1, i;
    while((ent = FindEntityByClassname(ent, "survivor_death_model")) != INVALID_ENT_REFERENCE)
    {
        if(iSurvivorCharacter == GetEntProp(ent, Prop_Send, "m_nCharacterType"))
        {
            AcceptEntityInput(ent, "kill");
            return;
        }
    }
}
 

DarklSide

Участник
Сообщения
931
Реакции
468
Почему так и где у меня ошибка ?

C-подобный:
void RemoveSurvivorRagdoll(int iSurvivorCharacter)
{
    int iCharacterType;
    while ((iEntity = FindEntityByClassname(MaxClients + 1, "survivor_death_model")) != INVALID_ENT_REFERENCE)
    {
        iCharacterType = GetEntProp(iEntity, Prop_Send, "m_nCharacterType");

        for (int i = 1; i <= MaxClients; i++)
        {
            if (!IsClientInGame(i) || GetClientTeam(i) != TEAM_SURVIVOR || iCharacterType != iSurvivorCharacter)
            {
                continue;
            }

            if (IsValidEntity(iEntity))
            {
                RemoveEntity(iEntity);
            }
        }
    }
}

Ошибка заключается в цикле поиска entity.
Она состоит в том, что поиск entity "survivor_death_model" начинается с MaxClients + 1, т.е. это заведомо исключает игроков, но и каждый раз как найдет индекс и будет проверен на INVALID_ENT_REFERENCE, следующая итерация цикла будет заново начинаться с MaxClients + 1, и найдет тот же самый индекс entity, т.е. создается бесконечный цикл.

Если в секции блока, поставим return и сервер её выполнит, сервер не должен падать или выдавать timed out выполнения:
C-подобный:
if (IsValidEntity(iEntity))
{
    RemoveEntity(iEntity);
    return;
}


FindEntityByClassname имеет первый параметр с которого начнет поиск entity, но его не включит:
C-подобный:
while ((iEntity = FindEntityByClassname(iEntity, "survivor_death_model")) != INVALID_ENT_REFERENCE)
Т.е. в таком случае, поиск будет с последнего найденного, который будет пропущен:

Т.к. передаем нужный параметр, цикл игроков уже не нужен.
В таком случае, результат функции может быть такой:
C-подобный:
void RemoveSurvivorRagdoll(int iSurvivorCharacter)
{
    int iCharacterType, iEntity = INVALID_ENT_REFERENCE;
    while ((iEntity = FindEntityByClassname(iEntity, "survivor_death_model")) != INVALID_ENT_REFERENCE)
    {
        if (!IsValidEntity(iEntity))
        {
             continue;
        }
        iCharacterType = GetEntProp(iEntity, Prop_Send, "m_nCharacterType");

        if (iCharacterType == iSurvivorCharacter)
        {
            RemoveEntity(iEntity);
        }
    }
}
 
Последнее редактирование:
Сверху Снизу