970 lines
24 KiB
SourcePawn
970 lines
24 KiB
SourcePawn
|
|
#pragma semicolon 1
|
||
|
|
#pragma newdecls required
|
||
|
|
|
||
|
|
#include <sourcemod>
|
||
|
|
#include <sdktools>
|
||
|
|
#include <clientprefs>
|
||
|
|
|
||
|
|
public Plugin myinfo =
|
||
|
|
{
|
||
|
|
name = "[VIP] Custom Models (R1KO API-safe)",
|
||
|
|
author = "OpenAI",
|
||
|
|
description = "VIP custom player models with persistence, robust downloads parsing and safer default arms handling",
|
||
|
|
version = "1.2.0",
|
||
|
|
url = ""
|
||
|
|
};
|
||
|
|
|
||
|
|
#define FEATURE_NAME "CUSTOM_MODELS"
|
||
|
|
#define CONFIG_PATH "configs/vip_custom_models.ini"
|
||
|
|
#define COOKIE_NAME "vip_custom_model_selection"
|
||
|
|
#define MAX_CUSTOM_MODELS 128
|
||
|
|
#define MAX_DOWNLOADS_PER_MODEL 64
|
||
|
|
#define TEAM_ANY 0
|
||
|
|
#define TEAM_T 2
|
||
|
|
#define TEAM_CT 3
|
||
|
|
#define DEFAULT_T_ARMS "models/weapons/t_arms_leet.mdl"
|
||
|
|
#define DEFAULT_CT_ARMS "models/weapons/ct_arms_fbi.mdl"
|
||
|
|
|
||
|
|
// Manual VIP API declarations to avoid include/core version mismatch.
|
||
|
|
enum VIP_ValueType
|
||
|
|
{
|
||
|
|
VIP_NULL = 0,
|
||
|
|
INT,
|
||
|
|
FLOAT,
|
||
|
|
BOOL,
|
||
|
|
STRING
|
||
|
|
};
|
||
|
|
|
||
|
|
enum VIP_FeatureType
|
||
|
|
{
|
||
|
|
TOGGLABLE = 0,
|
||
|
|
SELECTABLE,
|
||
|
|
HIDE
|
||
|
|
};
|
||
|
|
|
||
|
|
enum VIP_AccessStatus
|
||
|
|
{
|
||
|
|
NO_ACCESS = 0,
|
||
|
|
DISABLED,
|
||
|
|
ENABLED
|
||
|
|
};
|
||
|
|
|
||
|
|
native bool VIP_IsVIPLoaded();
|
||
|
|
native void VIP_RegisterFeature(const char[] sFeatureName,
|
||
|
|
VIP_ValueType valType = VIP_NULL,
|
||
|
|
VIP_FeatureType featureType = TOGGLABLE,
|
||
|
|
Function itemSelectCallback = INVALID_FUNCTION,
|
||
|
|
Function itemDisplayCallback = INVALID_FUNCTION,
|
||
|
|
Function itemDrawCallback = INVALID_FUNCTION,
|
||
|
|
bool bDefStatus = true,
|
||
|
|
bool bCookie = true);
|
||
|
|
native void VIP_UnregisterFeature(const char[] sFeatureName);
|
||
|
|
native int VIP_GetClientFeatureStatus(int client, const char[] sFeatureName);
|
||
|
|
native void VIP_SendClientVIPMenu(int client, bool bSelection = false);
|
||
|
|
native void VIP_GetClientVIPGroup(int client, char[] group, int maxlen);
|
||
|
|
|
||
|
|
Cookie g_hSelectionCookie = null;
|
||
|
|
Database g_hDb = null;
|
||
|
|
bool g_bFeatureRegistered = false;
|
||
|
|
bool g_bCookiesReady[MAXPLAYERS + 1];
|
||
|
|
bool g_bDbReady = false;
|
||
|
|
bool g_bDbLoaded[MAXPLAYERS + 1];
|
||
|
|
char g_sLoadedModelId[MAXPLAYERS + 1][32];
|
||
|
|
|
||
|
|
int g_iModelCount = 0;
|
||
|
|
char g_sModelId[MAX_CUSTOM_MODELS][32];
|
||
|
|
char g_sModelName[MAX_CUSTOM_MODELS][128];
|
||
|
|
char g_sModelPath[MAX_CUSTOM_MODELS][PLATFORM_MAX_PATH];
|
||
|
|
char g_sArmsPath[MAX_CUSTOM_MODELS][PLATFORM_MAX_PATH];
|
||
|
|
char g_sGroupMask[MAX_CUSTOM_MODELS][256];
|
||
|
|
int g_iTeamLimit[MAX_CUSTOM_MODELS];
|
||
|
|
int g_iDownloadCount[MAX_CUSTOM_MODELS];
|
||
|
|
char g_sDownloads[MAX_CUSTOM_MODELS][MAX_DOWNLOADS_PER_MODEL][PLATFORM_MAX_PATH];
|
||
|
|
|
||
|
|
int g_iSelectedModel[MAXPLAYERS + 1];
|
||
|
|
|
||
|
|
public void OnPluginStart()
|
||
|
|
{
|
||
|
|
LoadTranslations("common.phrases");
|
||
|
|
LoadTranslations("vip_custom_models.phrases");
|
||
|
|
|
||
|
|
g_hSelectionCookie = RegClientCookie(COOKIE_NAME, "VIP custom model selection", CookieAccess_Private);
|
||
|
|
|
||
|
|
HookEvent("player_spawn", Event_PlayerSpawn, EventHookMode_Post);
|
||
|
|
HookEvent("player_team", Event_PlayerTeam, EventHookMode_Post);
|
||
|
|
|
||
|
|
RegConsoleCmd("sm_vmodel", Command_OpenMenu);
|
||
|
|
RegConsoleCmd("sm_models", Command_OpenMenu);
|
||
|
|
RegConsoleCmd("sm_agents", Command_OpenMenu);
|
||
|
|
|
||
|
|
for (int i = 1; i <= MaxClients; i++)
|
||
|
|
{
|
||
|
|
g_iSelectedModel[i] = -1;
|
||
|
|
g_bCookiesReady[i] = false;
|
||
|
|
g_bDbLoaded[i] = false;
|
||
|
|
g_sLoadedModelId[i][0] = '\0';
|
||
|
|
}
|
||
|
|
|
||
|
|
LoadModelsConfig();
|
||
|
|
InitDatabase();
|
||
|
|
|
||
|
|
if (VIP_IsVIPLoaded())
|
||
|
|
{
|
||
|
|
VIP_OnVIPLoaded();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public void OnMapStart()
|
||
|
|
{
|
||
|
|
LoadModelsConfig();
|
||
|
|
PrecacheAllModels();
|
||
|
|
}
|
||
|
|
|
||
|
|
public void OnClientPutInServer(int client)
|
||
|
|
{
|
||
|
|
g_iSelectedModel[client] = -1;
|
||
|
|
g_bCookiesReady[client] = false;
|
||
|
|
g_bDbLoaded[client] = false;
|
||
|
|
g_sLoadedModelId[client][0] = '\0';
|
||
|
|
|
||
|
|
if (!IsClientInGame(client) || IsFakeClient(client))
|
||
|
|
{
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
CreateTimer(3.0, Timer_ApplyModel, GetClientUserId(client), TIMER_FLAG_NO_MAPCHANGE);
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
public void OnClientPostAdminCheck(int client)
|
||
|
|
{
|
||
|
|
if (!IsValidHuman(client))
|
||
|
|
{
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
LoadClientSelectionFromDatabase(client);
|
||
|
|
CreateTimer(1.5, Timer_ApplyModel, GetClientUserId(client), TIMER_FLAG_NO_MAPCHANGE);
|
||
|
|
}
|
||
|
|
|
||
|
|
public void OnClientCookiesCached(int client)
|
||
|
|
{
|
||
|
|
g_iSelectedModel[client] = -1;
|
||
|
|
g_bCookiesReady[client] = true;
|
||
|
|
|
||
|
|
if (!IsValidHuman(client))
|
||
|
|
{
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
char value[16];
|
||
|
|
GetClientCookie(client, g_hSelectionCookie, value, sizeof(value));
|
||
|
|
if (value[0] != '\0')
|
||
|
|
{
|
||
|
|
int idx = FindModelIndexById(value);
|
||
|
|
if (idx != -1)
|
||
|
|
{
|
||
|
|
g_iSelectedModel[client] = idx;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
CreateTimer(0.5, Timer_ApplyModel, GetClientUserId(client), TIMER_FLAG_NO_MAPCHANGE);
|
||
|
|
}
|
||
|
|
|
||
|
|
public void OnClientDisconnect(int client)
|
||
|
|
{
|
||
|
|
g_iSelectedModel[client] = -1;
|
||
|
|
g_bCookiesReady[client] = false;
|
||
|
|
g_bDbLoaded[client] = false;
|
||
|
|
g_sLoadedModelId[client][0] = '\0';
|
||
|
|
}
|
||
|
|
|
||
|
|
void InitDatabase()
|
||
|
|
{
|
||
|
|
char error[256];
|
||
|
|
g_hDb = SQLite_UseDatabase("storage-local", error, sizeof(error));
|
||
|
|
if (g_hDb == null)
|
||
|
|
{
|
||
|
|
LogError("[VIP Models] SQLite init failed: %s", error);
|
||
|
|
g_bDbReady = false;
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
g_bDbReady = true;
|
||
|
|
g_hDb.Query(SQL_GenericCallback,
|
||
|
|
"CREATE TABLE IF NOT EXISTS vip_custom_models_selection (steamid TEXT PRIMARY KEY, modelid TEXT NOT NULL, updated_at INTEGER NOT NULL DEFAULT (strftime('%s','now')))");
|
||
|
|
}
|
||
|
|
|
||
|
|
public void SQL_GenericCallback(Database db, DBResultSet results, const char[] error, any data)
|
||
|
|
{
|
||
|
|
if (error[0] != ' |