Иконка ресурса

Player preferences 1.0.7

Нет прав для скачивания
Установка
Раскидать по папкам, скомпилировать, прописать в plugins.ini

Файл Init.sql необходимо экспортировать в вашу базу данных, данные от которой нужно ввести в configs/preferences.json
Исходный код
Код:
//Based on plugin: https://dev-cs.ru/resources/984/ by GM-X Team

#include <amxmodx>
#include <sqlx>
#include <json>

new bool: DEBUG;

public stock const PluginName[] = "Player preferences";
public stock const PluginVersion[] = "1.0.7";
public stock const PluginAuthor[] = "GM-X Team, cpctrl";
public stock const PluginURL[] = "https://goldsrc.ru/members/3085/";

#define CHECK_NATIVE_ARGS_NUM(%1,%2,%3) \
    if (%1 < %2) { \
        DEBUG && log_error(AMX_ERR_NATIVE, "Invalid num of arguments %d. Expected %d", %1, %2); \
        return %3; \
    }

#define CHECK_NATIVE_PLAYER(%1,%2) \
    if (!g_bConnected[%1]) { \
        DEBUG && log_error(AMX_ERR_NATIVE, "Invalid player %d", %1); \
        return %2; \
    }

const MAX_KEY_LENGTH = 32;
const MAX_VALUE_STRING_LENGTH = 32;

enum sqlx_e {
    table[32],
    host[32],
    user[64],
    pass[64],
    db[32]
};

enum fwdStruct  {
    Fwd_Loaded,
    Fwd_KeyChanged
};

new g_eForwards[fwdStruct];

new Handle: g_hTuple;
new dbdata[sqlx_e];

new bool: g_bConnected[MAX_PLAYERS + 1];
new Trie: g_tPlayerPreferences[MAX_PLAYERS + 1];
new JSON: g_jObject[MAX_PLAYERS + 1];

public plugin_init()    {
    register_plugin(PluginName, PluginVersion, PluginAuthor);

    g_eForwards[Fwd_Loaded] = CreateMultiForward("player_loaded", ET_IGNORE, FP_CELL);
    g_eForwards[Fwd_KeyChanged] = CreateMultiForward("player_key_changed", ET_IGNORE, FP_CELL, FP_STRING);

    read_json();

    DEBUG = bool: (plugin_flags() & AMX_FLAG_DEBUG);
}

public read_json()   {
    new filePath[PLATFORM_MAX_PATH];
    get_localinfo("amxx_configsdir", filePath, PLATFORM_MAX_PATH - 1);

    add(filePath, PLATFORM_MAX_PATH - 1, "/preferences.json");

    if (!file_exists(filePath)) {
        set_fail_state("Configuration file '%s' not found", filePath);
        return;
    }

    new JSON: config = json_parse(filePath, true);

    if (config == Invalid_JSON)    {
        set_fail_state("Configuration file '%s' read error", filePath);
        return;
    }

    new temp[64];

    json_object_get_string(config, "sql_table", temp, charsmax(temp));
    copy(dbdata[table], charsmax(dbdata[table]), temp);

    json_object_get_string(config, "sql_host", temp, charsmax(temp));
    copy(dbdata[host], charsmax(dbdata[host]), temp);

    json_object_get_string(config, "sql_user", temp, charsmax(temp));
    copy(dbdata[user], charsmax(dbdata[user]), temp);

    json_object_get_string(config, "sql_password", temp, charsmax(temp));
    copy(dbdata[pass], charsmax(dbdata[pass]), temp);

    json_object_get_string(config, "sql_db", temp, charsmax(temp));
    copy(dbdata[db], charsmax(dbdata[db]), temp);

    json_free(config);

    server_print("Preferences config has been loaded");

    sql_test_init();
}

public sql_test_init() {
    new Handle: sConnection;

    g_hTuple = SQL_MakeDbTuple(
        dbdata[host],
        dbdata[user],
        dbdata[pass],
        dbdata[db]
    );

    new errCode, error[512];
    sConnection = SQL_Connect(g_hTuple, errCode, error, charsmax(error));

    if (sConnection == Empty_Handle)    {
        SQL_FreeHandle(g_hTuple);
        g_hTuple = Empty_Handle;

        DEBUG && log_amx("[PP] Error connecting to db '%s': #%d: %s", dbdata[db], errCode, error);

        return;
    }

    server_print("[PP] Connection to '%s' database success", dbdata[db]);

    SQL_FreeHandle(sConnection);
}

public plugin_natives() {
    register_native("pp_has_key", "native_has_key");

    register_native("pp_get_number", "native_get_number");
    register_native("pp_get_float", "native_get_float");
    register_native("pp_get_bool", "native_get_bool");
    register_native("pp_get_string", "native_get_string");

    register_native("pp_set_number", "native_set_number");
    register_native("pp_set_float", "native_set_float");
    register_native("pp_set_bool", "native_set_bool");
    register_native("pp_set_string", "native_set_string");
}

public bool: native_has_key(plugin, argc) {
    enum    {
        arg_player = 1,
        arg_key
    };

    CHECK_NATIVE_ARGS_NUM(argc, 2, false)

    new id = get_param(arg_player);
    CHECK_NATIVE_PLAYER(id, false)

    new key[MAX_KEY_LENGTH];
    get_string(arg_key, key, MAX_KEY_LENGTH - 1);

    return TrieKeyExists(g_tPlayerPreferences[id], key);
}

public native_get_number(plugin, argc)  {
    enum    {
        arg_player = 1,
        arg_key,
        arg_default
    };

    CHECK_NATIVE_ARGS_NUM(argc, 2, 0)

    new id = get_param(arg_player);
    CHECK_NATIVE_PLAYER(id, 0)

    new key[MAX_KEY_LENGTH];
    get_string(arg_key, key, MAX_KEY_LENGTH - 1);

    if (!TrieKeyExists(g_tPlayerPreferences[id], key))  {
        return argc >= arg_default ? get_param(arg_default) : 0;
    }

    new value;
    TrieGetCell(g_tPlayerPreferences[id], key, value);

    return value;
}

public Float: native_get_float(plugin, argc)  {
    enum    {
        arg_player = 1,
        arg_key,
        arg_default
    };

    CHECK_NATIVE_ARGS_NUM(argc, 2, 0.0)

    new id = get_param(arg_player);
    CHECK_NATIVE_PLAYER(id, 0.0)

    new key[MAX_KEY_LENGTH];
    get_string(arg_key, key, MAX_KEY_LENGTH - 1);

    if (!TrieKeyExists(g_tPlayerPreferences[id], key))  {
        return argc >= arg_default ? get_param_f(arg_default) : 0.0;
    }

    new value;
    TrieGetCell(g_tPlayerPreferences[id], key, value);

    return float(value);
}

public bool: native_get_bool(plugin, argc)  {
    enum    {
        arg_player = 1,
        arg_key,
        arg_default
    };

    CHECK_NATIVE_ARGS_NUM(argc, 2, false)

    new id = get_param(arg_player);
    CHECK_NATIVE_PLAYER(id, false)

    new key[MAX_KEY_LENGTH];
    get_string(arg_key, key, MAX_KEY_LENGTH - 1);

    if (!TrieKeyExists(g_tPlayerPreferences[id], key))  {
        return bool: (argc >= arg_default ? get_param(arg_default) : 0);
    }

    new value;
    TrieGetCell(g_tPlayerPreferences[id], key, value);

    return bool: value;
}

public native_get_string(plugin, argc)  {
    enum    {
        arg_player = 1,
        arg_key,
        arg_dest,
        arg_length,
        arg_default
    };

    CHECK_NATIVE_ARGS_NUM(argc, 2, 0)

    new id = get_param(arg_player);
    CHECK_NATIVE_PLAYER(id, 0)

    new key[MAX_KEY_LENGTH], value[MAX_VALUE_STRING_LENGTH];

    get_string(arg_key, key, MAX_KEY_LENGTH - 1);

    if (TrieKeyExists(g_tPlayerPreferences[id], key))  {
        TrieGetString(g_tPlayerPreferences[id], key, value, MAX_VALUE_STRING_LENGTH - 1);
    }
    else if (argc >= arg_default)  {
        get_string(arg_default, value, MAX_VALUE_STRING_LENGTH - 1);
    }

    return set_string(arg_dest, value, get_param(arg_length));
}

public native_set_number(plugin, argc)  {
    enum    {
        arg_player = 1,
        arg_key,
        arg_value
    };

    CHECK_NATIVE_ARGS_NUM(argc, 3, 0)

    new id = get_param(arg_player);
    CHECK_NATIVE_PLAYER(id, 0)

    new key[MAX_KEY_LENGTH];
    get_string(arg_key, key, charsmax(key));

    new value = get_param(arg_value);
    TrieSetCell(g_tPlayerPreferences[id], key, value);

    return setValue(id, key, json_init_number(value));
}

public native_set_bool(plugin, argc)  {
    enum    {
        arg_player = 1,
        arg_key,
        arg_value
    };

    CHECK_NATIVE_ARGS_NUM(argc, 3, 0)

    new id = get_param(arg_player);
    CHECK_NATIVE_PLAYER(id, 0)

    new key[MAX_KEY_LENGTH];
    get_string(arg_key, key, charsmax(key));

    new bool: value = bool: get_param(arg_value);
    TrieSetCell(g_tPlayerPreferences[id], key, value ? 1 : 0);

    return setValue(id, key, json_init_bool(value));
}

public native_set_float(plugin, argc)  {
    enum    {
        arg_player = 1,
        arg_key,
        arg_value
    };

    CHECK_NATIVE_ARGS_NUM(argc, 3, 0)

    new id = get_param(arg_player);
    CHECK_NATIVE_PLAYER(id, 0)

    new key[MAX_KEY_LENGTH];
    get_string(arg_key, key, charsmax(key));

    new Float: value = get_param_f(arg_value);
    TrieSetCell(g_tPlayerPreferences[id], key, value);

    return setValue(id, key, json_init_number(cell: value));
}

public native_set_string(plugin, argc)  {
    enum    {
        arg_player = 1,
        arg_key,
        arg_value
    };

    CHECK_NATIVE_ARGS_NUM(argc, 3, 0)

    new id = get_param(arg_player);
    CHECK_NATIVE_PLAYER(id, 0)

    new key[MAX_KEY_LENGTH], value[MAX_VALUE_STRING_LENGTH];
    get_string(arg_key, key, charsmax(key));
    get_string(arg_value, key, charsmax(key));

    TrieSetString(g_tPlayerPreferences[id], key, value);

    return setValue(id, key, json_init_string(value));
}

stock setValue(const id, const key[], JSON: value)    {
    new fwReturn;
    ExecuteForward(g_eForwards[Fwd_KeyChanged], fwReturn, id, key);

    if (g_hTuple == Empty_Handle || fwReturn == PLUGIN_HANDLED) {
        return -1;
    }

    json_object_set_value(g_jObject[id], key, value);
    json_free(value);

    return 1;
}

public client_putinserver(id)   {
    load_player(id);
}

public client_disconnected(id)  {
    if (g_bConnected[id])   {
        save_values(id);
    }
}

public ChallengeClear(id)   {
    g_bConnected[id] = false;

    json_free(g_jObject[id]);
    TrieDestroy(g_tPlayerPreferences[id]);
}

save_values(const id)  {
    if (g_hTuple == Empty_Handle)   {
        ChallengeClear(id);
        return;
    }

    if (json_serial_size(g_jObject[id]) < 3)  {
        ChallengeClear(id);
        return;
    }

    new buffer[1024], serial[612];
    new auth[MAX_AUTHID_LENGTH];
    get_user_authid(id, auth, charsmax(auth));
    json_serial_to_string(g_jObject[id], serial, charsmax(serial));

    formatex(buffer, charsmax(buffer), "INSERT INTO `%s` (`auth`, `data`) VALUES ('%s', \
        '%s') ON DUPLICATE KEY UPDATE `data` = '%s';", dbdata[table], auth, serial, serial);

    DEBUG && log_amx(buffer);

    SQL_ThreadQuery(g_hTuple, "ThreadHandler", buffer);

    ChallengeClear(id);
}

load_player(id)  {
    if (is_user_hltv(id) || is_user_bot(id))    {
        return;
    }

    g_bConnected[id] = true;
    g_jObject[id] = json_init_object();
    g_tPlayerPreferences[id] = TrieCreate();

    if (g_hTuple == Empty_Handle)   {
        return;
    }

    new buffer[128], szAuth[MAX_AUTHID_LENGTH];
    get_user_authid(id, szAuth, MAX_AUTHID_LENGTH - 1);
    formatex(buffer, charsmax(buffer), "SELECT `auth`, `data` FROM `%s` WHERE `auth` = '%s'", dbdata[table], szAuth);

    new data[2];
    data[0] = id;
    data[1] = get_user_userid(id);
    SQL_ThreadQuery(g_hTuple, "ThreadHandler", buffer, data, sizeof data);
}

public ThreadHandler(failstate, Handle: query, error[], errnum, data[], size, Float: queuetime)   {
    if (failstate)  {
        log_error(AMX_ERR_NATIVE, "[PP] [%d]: %s", errnum, error);
        return PLUGIN_HANDLED;
    }

    new id = data[0];

    CHECK_NATIVE_PLAYER(id, PLUGIN_HANDLED)

    if (get_user_userid(id) != data[1]) {
        DEBUG && log_amx("[PP] Userid %d != Pushed userid %d", get_user_userid(id), data[1]);

        return PLUGIN_HANDLED;
    }

    if (SQL_NumResults(query))  {
        new preferences[612];
        SQL_ReadResult(query, SQL_FieldNameToNum(query, "data"), preferences, charsmax(preferences));

        new JSON: jsonValue = json_parse(preferences);

        if (jsonValue == Invalid_JSON || preferences[0] != '{' || preferences[strlen(preferences) - 1] != '}')   {
            json_free(jsonValue);

            if (DEBUG)  {
                new auth[MAX_AUTHID_LENGTH];
                SQL_ReadResult(query, SQL_FieldNameToNum(query, "auth"), auth, MAX_AUTHID_LENGTH - 1);

                log_error(AMX_ERR_NATIVE, "[PP] Skipped load from bad format json `auth` = %s", auth);
            }

            ExecuteForward(g_eForwards[Fwd_Loaded], _, id);

            return PLUGIN_HANDLED;
        }

        new bool: bSomeBoolean, iSomeNumber;
        for (new i = 0, n = json_object_get_count(jsonValue), JSON: element, key[MAX_KEY_LENGTH], value[MAX_VALUE_STRING_LENGTH]; i < n; i++)  {
            json_object_get_name(jsonValue, i, key, charsmax(key));
            element = json_object_get_value_at(jsonValue, i);

            switch  (json_get_type(element)) {
                case JSONString:    {
                    json_get_string(element, value, MAX_VALUE_STRING_LENGTH - 1);

                    TrieSetString(g_tPlayerPreferences[id], key, value);
                    json_object_set_string(g_jObject[id], key, value);
                }
                case JSONNumber:    {
                    iSomeNumber = json_get_number(element);

                    TrieSetCell(g_tPlayerPreferences[id], key, iSomeNumber);
                    json_object_set_number(g_jObject[id], key, iSomeNumber);
                }
                case JSONBoolean:   {
                    bSomeBoolean = json_get_bool(element);

                    TrieSetCell(g_tPlayerPreferences[id], key, bSomeBoolean ? 1 : 0);
                    json_object_set_bool(g_jObject[id], key, bSomeBoolean);
                }
            }
            json_free(element);
        }
        json_free(jsonValue);
    }
    ExecuteForward(g_eForwards[Fwd_Loaded], _, id);

    return PLUGIN_HANDLED;
}

public plugin_end() {
    if (g_hTuple != Empty_Handle) {
        SQL_FreeHandle(g_hTuple);
    }
}
Настройки
preferences.json:
sql_table - название таблицы, которое вы впишите в Init.sql
sql_host - Адрес базы данных
sql_user - Пользователь
sql_password - Пароль
sql_db - Название базы
Интерфейсы (API)
player_preferences.inc:
#if defined _player_preferences_included
    #endinput
#endif

#define _player_preferences_included

/**
* Called when a player is loaded.
*
* @param id                Client index
*
* @noreturn
*/
forward player_loaded(const id);

/**
* Called BEFORE when a value of a key is changed.
*
* @param id                Client index
* @param key               Unique key
*
* @return PLUGIN_HANDLED to blocking key change
*/
forward player_key_changed(const id, const key[]);

/**
* Checks if the specified key for player exists.
*
* @param id                Client index
* @param key               Unique key to which the value will be set
*
* @return                  true on success, false otherwise
* @error                   If the index is not within the range of 1 to MaxClients,
*                          an error will be thrown.
*/
native bool: pp_has_key(const id, const key[]);

/**
* Gets the value of the specified key set to the player as string.
*
* @param id                Client index
* @param key               Unique key to search for
* @param dest              Buffer to copy value to
* @param length            Maximum buffer size
* @param defaultValue      Default value to set if key wasn't found
*
* @return                  Key's value set to the player or defaultValue if key wasn't found
* @error                   If the index is not within the range of 1 to MaxClients
*                          or an invalid number of arguments is set, an error will be thrown.
*/
native pp_get_string(const id, const key[], dest[], const length, const defaultValue[] = "");

/**
* Sets a string value for a player for the specified key.
*
* @param id                Client index
* @param key               Unique key to set the value to
* @param value             Value to set
*
* @return                  1 on success, 0 otherwise
* @error                   If the index is not within the range of 1 to MaxClients
*                          or an invalid number of arguments is set, an error will be thrown.
*/
native pp_set_string(const id, const key[], const value[]);

/**
* Gets the value of the specified key set to the player as integer.
*
* @param id                Client index
* @param key               Unique key to search for
* @param defaultValue      Default value to set if key wasn't found
*
* @return                  Key's value set to the player or defaultValue if key wasn't found
* @error                   If the index is not within the range of 1 to MaxClients
*                          or an invalid number of arguments is set, an error will be thrown.
*/
native pp_get_number(const id, const key[], const defaultValue = 0);

/**
* Sets an integer value for a player for the specified key.
*
* @param id                Client index
* @param key               Unique key to set the value to
* @param value             Value to set
*
* @return                  1 on success, 0 otherwise
* @error                   If the index is not within the range of 1 to MaxClients
*                          or an invalid number of arguments is set, an error will be thrown.
*/
native pp_set_number(const id, const key[], const value);

/**
* Gets the value of the specified key set to the player as boolean.
*
* @param id                Client index
* @param key               Unique key to search for
* @param defaultValue      Default value to set if key wasn't found
*
* @return                  Key's value set to the player or defaultValue if key wasn't found
* @error                   If the index is not within the range of 1 to MaxClients
*                          or an invalid number of arguments is set, an error will be thrown.
*/
native bool: pp_get_bool(const id, const key[], const bool:defaultValue = false);

/**
* Sets a boolean value for a player for the specified key.
*
* @param id                Client index
* @param key               Unique key to set the value to
* @param value             Value to set
*
* @return                  1 on success, 0 otherwise
* @error                   If the index is not within the range of 1 to MaxClients
*                          or an invalid number of arguments is set, an error will be thrown.
*/
native pp_set_bool(const id, const key[], const bool:value);

/**
* Gets the value of the specified key set to the player as float.
*
* @param id                Client index
* @param key               Unique key to search for
* @param defaultValue      Default value to set if key wasn't found
*
* @return                  Key's value set to the player or defaultValue if key wasn't found
* @error                   If the index is not within the range of 1 to MaxClients
*                          or an invalid number of arguments is set, an error will be thrown.
*/
native Float: pp_get_float(const id, const key[], const Float:defaultValue = 0.0);

/**
* Sets a float value for a player for the specified key.
*
* @param id                Client index
* @param key               Unique key to set the value to
* @param value             Value to set
*
* @return                  1 on success, 0 otherwise
* @error                   If the index is not within the range of 1 to MaxClients
*                          or an invalid number of arguments is set, an error will be thrown.
*/
native pp_set_float(const id, const key[], const Float:value);
Сверху Снизу