Написал плагин смены неба по времени и картам

Paranoiiik

хачю клиентмод
Сообщения
2,047
Реакции
1,490
Давно хочу заиметь адекватное решение по изменению неба в определённое время, вот, написал.
Если кому-то интересно, потестируйте, пожалуйста. Самому, по сути, негде, да и лень.
В Ресурсы не хочу пока добавлять что-то такое сырое.

Плагин работает точно в CS:S v34, CS:S v92+ и CS:GO. Ну и, в целом, должен работать везде, где есть sv_skyname.
В конфиге вам нужно указать время начала и окончания неба, на каких картах оно будет и само небо.
Не нужно никуда прописывать файлы для загрузки. Даже если ваше небо находится не в materials/skybox/, плагин сам отыщет пути и добавит в загрузку. По названию.

Я вообще его не проверял, но должно работать.
Проблема с тем, что в CS:S небо нельзя изменить "на лету" (игроки будут видеть старое), к сожалению, никуда не делась. Сделал, как смог.
Может, в целом, кто-то замечания по коду напишет, укажет на мои косяки.

Особенности работы плагина отличаются для CS:S и CS:GO. Обращайте внимание на переменную change_sky_now и параметр change_map в конфигах, если будете тестировать!
Скомпилирован плагин на SourceMod 1.10.0.6490, если что.

🙂🎅

------------------------------------------------------------------------------------------------------
UPD 9:19: Забыл добавить код определения названия карты из WorkShop для CS:GO
UPD 9:33: Я дебил. Форматировал время, которое даже не получил. Исправил
 

Вложения

  • Change Sky.zip
    11.5 КБ · Просмотры: 55
Последнее редактирование:

Grey83

не пишу плагины с весны 2022
Сообщения
8,521
Реакции
4,980
вроде же был плагин смены скайбоксов по времени
и даже я что-то там фиксил
 

Shid0

🦾 Свет даëт мне сил!
Сообщения
1,130
Реакции
1,255
вроде же был плагин смены скайбоксов по времени
и даже я что-то там фиксил
 

Grey83

не пишу плагины с весны 2022
Сообщения
8,521
Реакции
4,980
C-подобный:
    FormatTime(szTime, 16, "%H:%M", GetTime());
    int iTime = StringToInt(szTime);
O RLY?! O_o
 

Paranoiiik

хачю клиентмод
Сообщения
2,047
Реакции
1,490
вроде же был плагин смены скайбоксов по времени
и даже я что-то там фиксил
Находил, всегда хотел свою реализацию со своими задумками и учётом особенности CS:S
Сообщения автоматически склеены:

C-подобный:
    FormatTime(szTime, 16, "%H:%M", GetTime());
    int iTime = StringToInt(szTime);
O RLY?! O_o
Не понял.
Не уверен в том, как именно это работает, и как сделать так, чтобы и в конфиге в человекопонятном виде можно было записывать, и в коде проверить больше/меньше
 

Grey83

не пишу плагины с весны 2022
Сообщения
8,521
Реакции
4,980
Вот чуток почистилю но косяки править не буду (лень искать):
C-подобный:
#pragma semicolon 1
#pragma newdecls required

#include sdktools_stringtables

static const char SIDE[][] = {"ft", "bk", "up", "dn", "lf", "rt"};

KeyValues
    kvSettings;
ConVar
    convarNow,
    convarSky;
bool
    bTimer,
    bChangeMap,
    bGO,
    bNow;
char
    szSky[128],
    szSkyDefault[128],
    szLog[PLATFORM_MAX_PATH],
    szMap[128];

public Plugin myinfo =
{
    name        = "Change Sky",
    author        = "Paranoiiik",
    version        = "0.1 [Test Version]",
    description    = "Change sky by time and maps",
    url            = "vk.com/oldfreelance"
}

public void OnPluginStart()
{
    bGO = GetEngineVersion() == Engine_CSGO;

    convarNow = CreateConVar("change_sky_now", "0", "1 - Менять небо прямо во время игры, 0 - После окончания раунда\nПригодится для случаев, когда на сервере не происходит конец раунда", _, true, _, true, 1.0);
    convarNow.AddChangeHook(cvarNow);
    bNow = convarNow.BoolValue;

    convarSky = FindConVar("sv_skyname");

    AutoExecConfig(true, "ChangeSky");

    HookEvent("round_end", eventRoundEnd);
}

public void cvarNow(ConVar cvar, const char[] oldValue, const char[] newValue)
{
    bNow = cvar.BoolValue;
}

public void OnMapStart()
{
    GetCurrentMap(szMap, sizeof szMap);
    GetMapDisplayName(szMap, szMap, sizeof szMap);

    // Чтобы было стандартное небо, если в определённое время никакое небо в конфиге не установлено
    if(convarSky) convarSky.GetString(szSkyDefault, sizeof szSkyDefault);

    // Настройки карт, времени и неба | Если там есть небо в определённое время, оно перебьёт стандартное
    loadSettings();

    // Ставим небо в зависимости от условий выше
    convarSky.SetString(!szSky[0] ? szSkyDefault : szSky, true);

    // Проверяем текущее время. Если совпадает, решаем, менять ли небо сразу и менять ли карту для его смены
    CreateTimer(60.0, timerCheckTime, _, TIMER_REPEAT|TIMER_FLAG_NO_MAPCHANGE);
    timerCheckTime(null);
}

stock void loadSettings()
{
    delete kvSettings;
    kvSettings = new KeyValues("change_sky");

    char szBuffer[PLATFORM_MAX_PATH];
    BuildPath(Path_SM, szBuffer, sizeof szBuffer, "configs/change_sky.ini");
    if(!kvSettings.ImportFromFile(szBuffer))
    {
        skyLog(szBuffer);
        return;
    }

    bChangeMap = view_as<bool>(kvSettings.GetNum("change_map", 1));

    FormatTime(szBuffer, sizeof szBuffer, "%H:%M", GetTime());
    int iTime = StringToInt(szBuffer);

    kvSettings.Rewind();
    if(kvSettings.GotoFirstSubKey())
    {
        char szStart[16], szEnd[16], szName[32];

        do
        {
            // Время начала и конца неба
            kvSettings.GetString("start", szStart, sizeof szStart);
            kvSettings.GetString("end", szEnd, sizeof szEnd);

            if(iTime >= StringToInt(szStart) && iTime < StringToInt(szEnd))
            {
                szSky[0] = 0;

                // Есть ли небо для всех карт
                kvSettings.GetString("sky", szName, sizeof szName);
                if(szName[0]) FormatEx(szSky, sizeof(szSky), szName);
                else
                {
                    // Есть ли небо по префиксам карт
                    if(kvSettings.JumpToKey("maps_prefix", false))
                    {
                        if(SplitString(szMap, "_", szMap, sizeof szMap) != -1)
                        {
                            kvSettings.GetString(szMap, szName, sizeof szName);
                            if(szName[0]) szSky = szName;
                        }

                        kvSettings.GoBack();
                    }

                    // Есть ли небо по определённым картам
                    if(kvSettings.JumpToKey("maps", false))
                    {
                        kvSettings.GetString(szMap, szName, sizeof szName);
                        if(szName[0]) szSky = szName;

                        kvSettings.GoBack();
                    }
                }

                // Загружаем нужное небо игроку
                if(szSky[0]) skyDownload(szSky);
            }
        }
        while(kvSettings.GotoNextKey());
    }
}

public Action eventRoundEnd(Event event, const char[] name, bool dontBroadcast)
{
    if(bTimer) CreateTimer(3.0, timerChangeMap, _, TIMER_FLAG_NO_MAPCHANGE);
}

public Action timerCheckTime(Handle timer)
{
    char szTime[16];
    FormatTime(szTime, sizeof szTime, "%H:%M", GetTime());

    kvSettings.Rewind();
    if(kvSettings.GotoFirstSubKey())
    {
        do
        {
            // Время начала и конца неба
            char szStart[16], szEnd[16];

            kvSettings.GetString("start", szStart, sizeof szStart);
            if(!strcmp(szTime, szStart))
            {
                // В CS:GO небо можно менять "на лету"
                // В CS:S изменения sv_skyname применятся лишь при старте карты | "на лету" останется старое небо, даже если значение поменялось
                // Выбор, менять ли карту, я оставил больше для CS:GO, по сути, чтоб у владельца сервера было больше свободы
                if(bGO) convarSky.SetString(szSky, true);
                if(bChangeMap)
                {
                    // Меняем небо либо через 3 секунды после окончания раунда, либо сразу
                    if(!bNow)    bTimer = true;
                    else
                    {
                        bTimer = false;
                        ForceChangeLevel(szMap, "change_sky plugin");
                    }
                }
            }

            kvSettings.GetString("end", szEnd, sizeof szEnd);
            if(!strcmp(szTime, szEnd))
            {
                // В CS:GO небо можно менять "на лету"
                // В CS:S изменения sv_skyname применятся лишь при старте карты | "на лету" останется старое небо, даже если значение поменялось
                // Выбор, менять ли карту, я оставил больше для CS:GO, по сути, чтоб у владельца сервера было больше свободы
                if(bGO) convarSky.SetString(szSkyDefault, true);
                if(bChangeMap)
                {
                    // Меняем небо либо через 3 секунды после окончания раунда, либо сразу
                    if(!bNow)    bTimer = true;
                    else
                    {
                        bTimer = false;
                        ForceChangeLevel(szMap, "change_sky plugin");
                    }
                }
            }
        }
        while(kvSettings.GotoNextKey());
    }
}

public Action timerChangeMap(Handle timer)
{
    bTimer = false;
    ForceChangeLevel(szMap, "change_sky plugin");
}

// Функция позволит нам скачать небо клиенту без сторонних файлов
// Админ должен будет указать в конфиге лишь название, плагин сам отыщет пути и добавит файлы в загрузку
stock void skyDownload(char[] szName)
{
    // Если небо находится по нестандартному пути "materials/my_folder/sky" - sv_skyname "../my_folder/sky"
    // Убираем лишние символы из названия, чтобы адеквато скачать небо клиенту, используя название неба в поиске пути
    char szPath[256];

    if(StrContains(szName, "../", true) != -1)
    {
        ReplaceString(szName, sizeof szName, "../", "");
        FormatEx(szPath, sizeof szPath, "materials/");
    }
    else if(StrContains(szName, "./", true) != -1)
    {
        ReplaceString(szName, sizeof szName, "./", "");
        FormatEx(szPath, sizeof szPath, "materials/");
    }
    else FormatEx(szPath, sizeof szPath, "materials/skybox/");

    // Собственно, загрузка и прекэш файлов неба игроку
    // Я думал, над тем, как укоротить эту часть кода, но не хватило мозгов. Может, позже что-то придумаю
    char szBuffer[256];

    for(int i; i < sizeof(SIDE); i++)
    {
        FormatEx(szBuffer, sizeof(szBuffer), "%s%s%s.vmt", szPath, szName, SIDE);
        if(!FileExists(szBuffer)) skyLog(szBuffer);
        else
        {
            AddFileToDownloadsTable(szBuffer);
            if(!IsModelPrecached(szBuffer)) PrecacheModel(szBuffer, true);
        }

        FormatEx(szBuffer, sizeof(szBuffer), "%s%s%s.vtf", szPath, szName, SIDE);
        if(!FileExists(szBuffer)) skyLog(szBuffer);
        else AddFileToDownloadsTable(szBuffer);
    }
}

stock void skyLog(char[] fileName)
{
    BuildPath(Path_SM, szLog, sizeof szLog, "logs/ChangeSky.log");
    LogToFileEx(szLog, "Missing file: \"%s\"", fileName);
}
Сообщения автоматически склеены:

Не уверен в том, как именно это работает, и как сделать так, чтобы и в конфиге в человекопонятном виде можно было записывать, и в коде проверить больше/меньше
нужно время без двоеточия писать, кмк
 
Сверху Снизу