punteroo / TF2-Item-Plugins

SourceMod plugins for TF2 that allow players to manage their weapons (australium, festivizer, unusuals, spells, war paints) and cosmetics (unusuals, paints, spell paints, halloween spells). Now with SQLite support.
GNU General Public License v3.0
31 stars 4 forks source link

[ERROR] "Exception reported: Client index XX is invalid" on server start #30

Closed DARKSIDE99199 closed 2 months ago

DARKSIDE99199 commented 2 months ago

Describe the bug When starting the server, many errors of this type occur:

[SM] Exception reported: Client index 53 is invalid
[SM] Blaming: tf2itemplugin_cosmetics.smx
[SM] Call stack trace:
[SM]   [0] IsClientInGame
[SM]   [1] Line 65, tf2itemplugin_cosmetics.sp::OnStartTouchSpawnRoom
[SM] Exception reported: Client index 53 is invalid
[SM] Blaming: tf2itemplugin_weapons.smx
[SM] Call stack trace:
[SM]   [0] IsClientInGame
[SM]   [1] Line 65, tf2itemplugin_weapons.sp::OnStartTouchSpawnRoom
[SM] Exception reported: Client index 54 is invalid

To Reproduce

  1. Install plugin
  2. Start server

Screenshots image image image image

vexx-sm commented 2 months ago

33 TLDR;

Compile with these,

tf2itemplugin_cosmetics.sp ``` #include "tf2itemplugin/tf2itemplugin_base.sp" #include "tf2itemplugin/tf2itemplugin_cosmetics_base.sp" #include "tf2itemplugin/tf2itemplugin_cosmetics_data.sp" #include "tf2itemplugin/tf2itemplugin_cosmetics_requests.sp" #include "tf2itemplugin/tf2itemplugin_cosmetics_sqlite.sp" #include "tf2itemplugin/tf2itemplugin_cosmetics_menus.sp" #pragma semicolon 1 #pragma newdecls required #define PLUGIN_VERSION "4.0.0" #define DEBUG true public Plugin myinfo = { name = "TF2 Item Plugins - Cosmetics Manager", author = "Lucas 'punteroo' Maza", description = "Customize your cosmetic items and manage your server inventory.", version = PLUGIN_VERSION, url = "https://github.com/punteroo/TF2-Item-Plugins" }; /** * Load the "Regenerate" SDK call to refresh player inventories. * * @return Handle to the "Regenerate" SDK call. */ public Handle TF2ItemPlugin_LoadRegenerateSDK() { // Load TF2 gamedata. Handle hGameConf = LoadGameConfigFile("sm-tf2.games"); // Prepare the SDK call for the player entity. StartPrepSDKCall(SDKCall_Player); // Set the SDK call to find the "Regenerate" signature. PrepSDKCall_SetFromConf(hGameConf, SDKConf_Signature, "Regenerate"); PrepSDKCall_AddParameter(SDKType_Bool, SDKPass_Plain); // End the SDK call preparation. return EndPrepSDKCall(); } /** * Uses hooks to attach into the maps' `func_respawnroom` entities to determine if a player is within a spawn room. * * @return void */ public void TF2ItemPlugin_AttachSpawnRoomHooks() { // Declare a starting index for the spawn room entities. int trigger = -1; while ((trigger = FindEntityByClassname(trigger, "func_respawnroom")) != -1) { // Hook callbacks into the trigger. SDKHook(trigger, SDKHook_StartTouch, OnStartTouchSpawnRoom); SDKHook(trigger, SDKHook_EndTouch, OnEndTouchSpawnRoom); } } public Action OnStartTouchSpawnRoom(int entity, int other) { // Check if the entity is a valid client index if (other < 1 || other > MaxClients) return Plugin_Continue; // Is this entity a player? if (!IsClientInGame(other)) return Plugin_Continue; // If player is alive and real, mark them as in spawn room. if (IsPlayerAlive(other) && !IsClientSourceTV(other) && !IsClientObserver(other)) g_bInSpawnRoom[other] = true; return Plugin_Continue; } public Action OnEndTouchSpawnRoom(int entity, int other) { // Check if the entity is a valid client index if (other < 1 || other > MaxClients) return Plugin_Continue; // Is this entity a player? if (!IsClientInGame(other)) return Plugin_Continue; // If player is alive and real, mark them as not in spawn room. if (IsPlayerAlive(other) && !IsClientSourceTV(other) && !IsClientObserver(other)) g_bInSpawnRoom[other] = false; return Plugin_Continue; } public void OnPluginStart() { g_cvar_cosmetics_onlySpawn = CreateConVar("tf2items_cosmetics_spawn_only", "0.0", "If enabled, cosmetic overrides can only be changed in spawn regions.", 0, true, 0.0, true, 1.0); g_cvar_cosmetics_unusualEffectsURL = CreateConVar("tf2items_cosmetics_unusuals_url", "https://raw.githubusercontent.com/punteroo/TF2-Item-Plugins/production/tf2_unusuals.json", "URL from where to fetch the Unusual effects data."); g_cvar_cosmetics_searchTimeout = CreateConVar("tf2items_cosmetics_search_timeout", "15.0", "The amount of time (in seconds) to wait before timing out a player Unusual effect search.", 0, true, 5.0, true, 60.0); g_cvar_cosmetics_databaseCooldown = CreateConVar("tf2items_cosmetics_database_cooldown", "15.0", "The amount of time in seconds to wait before a player can perform a database action. -1 disables the cooldown.", 0, true, -1.0, true, 60.0); // Load the "Regenerate" SDK call. hRegen = TF2ItemPlugin_LoadRegenerateSDK(); // Find the network offsets for the weapon clip and ammo. clipOff = FindSendPropInfo("CTFWeaponBase", "m_iClip1"); ammoOff = FindSendPropInfo("CTFPlayer", "m_iAmmo"); // Register the cosmetic manager commands. static const char commandNames[][24] = { "sm_hats", "sm_hat", "sm_cosmetics", "sm_cosmetic", "sm_myhats" }; for (int i = 0; i < sizeof(commandNames); i++) RegAdminCmd(commandNames[i], CMD_TF2ItemPlugin_CosmeticManager, ADMFLAG_GENERIC, "Manage your cosmetics on the server."); // Connect to the SQlite database. Database.Connect(TF2ItemPlugin_SQL_ConnectToDatabase, "tf2itemplugins_db"); } public void OnClientAuthorized(int client, const char[] auth) { // Initialize the inventory for the client. TF2ItemPlugin_InitializeInventory(client); // Search for the client's preferences. TF2ItemPlugin_SQL_SearchPlayerPreferences(client); // Disable their cooldown flag. g_bIsOnDatabaseCooldown[client] = false; } public void OnMapStart() { // Attach hooks to the spawn room entities. TF2ItemPlugin_AttachSpawnRoomHooks(); // Setup an HTTP request to obtain latest Unusual effects information. char url[512]; g_cvar_cosmetics_unusualEffectsURL.GetString(url, sizeof(url)); LogMessage("Requesting Unusual effects information from %s", url); TF2ItemPlugin_RequestUnusualEffectsData(url); } public Action CMD_TF2ItemPlugin_CosmeticManager(int client, int args) { // If the client is not in a spawn room and the cvar is enabled, notify them. if (!g_bInSpawnRoom[client] && g_cvar_cosmetics_onlySpawn.BoolValue) { CPrintToChat(client, "%s You must be in a spawn room to manage your cosmetic items.", PLUGIN_CHATTAG); return Plugin_Handled; } // Open the cosmetics menu for the client. TF2ItemPlugin_Menus_MainMenu(client); return Plugin_Handled; } ```
tf2itemplugin_weapons.sp ``` #include #include #include #include #include #include #include #include "tf2itemplugin/tf2itemplugin_base.sp" #include "tf2itemplugin/tf2itemplugin_weapon_base.sp" #include "tf2itemplugin/tf2itemplugin_weapon_sqlite.sp" #include "tf2itemplugin/tf2itemplugin_weapon_data.sp" #include "tf2itemplugin/tf2itemplugin_weapon_menus.sp" #include "tf2itemplugin/tf2itemplugin_weapon_requests.sp" #pragma semicolon 1 #pragma newdecls required #define PLUGIN_VERSION "4.0.0" #define DEBUG false public Plugin myinfo = { name = "TF2 Item Plugins - Weapons Manager", author = "Lucas 'punteroo' Maza", description = "Customize your weapons and manage your server inventory.", version = PLUGIN_VERSION, url = "https://github.com/punteroo/TF2-Item-Plugins" }; /** * Load the "Regenerate" SDK call to refresh player inventories. * * @return Handle to the "Regenerate" SDK call. */ Handle TF2ItemPlugin_LoadRegenerateSDK() { Handle hGameConf = LoadGameConfigFile("sm-tf2.games"); StartPrepSDKCall(SDKCall_Player); PrepSDKCall_SetFromConf(hGameConf, SDKConf_Signature, "Regenerate"); PrepSDKCall_AddParameter(SDKType_Bool, SDKPass_Plain); Handle call = EndPrepSDKCall(); delete hGameConf; return call; } /** * Uses hooks to attach into the maps' `func_respawnroom` entities to determine if a player is within a spawn room. * * @return void */ void TF2ItemPlugin_AttachSpawnRoomHooks() { int trigger = -1; while ((trigger = FindEntityByClassname(trigger, "func_respawnroom")) != -1) { SDKHook(trigger, SDKHook_StartTouch, OnStartTouchSpawnRoom); SDKHook(trigger, SDKHook_EndTouch, OnEndTouchSpawnRoom); } } public Action OnStartTouchSpawnRoom(int entity, int other) { if (IsValidClient(other)) g_bInSpawnRoom[other] = true; return Plugin_Continue; } public Action OnEndTouchSpawnRoom(int entity, int other) { if (IsValidClient(other)) g_bInSpawnRoom[other] = false; return Plugin_Continue; } public void OnPluginStart() { g_cvar_weapons_onlySpawn = CreateConVar("tf2items_weapons_spawnonly", "0", "If enabled, weapon changes are only allowed when the player is within a spawn room.", _, true, 0.0, true, 1.0); g_cvar_weapons_paintKitsUrl = CreateConVar("tf2items_weapons_paintkits_url", "https://raw.githubusercontent.com/punteroo/TF2-Item-Plugins/production/tf2_protos.json", "The URL to the JSON file containing the War Paints and their IDs. Must be a valid JSON array."); g_cvar_weapons_searchTimeout = CreateConVar("tf2items_weapons_search_timeout", "20.0", "The amount of time in seconds to wait for a search to complete before timing out.", _, true, 5.0, true, 60.0); g_cvar_weapons_databaseCooldown = CreateConVar("tf2items_weapons_database_cooldown", "15.0", "The amount of time in seconds to wait before a player can perform a database action. -1 disables the cooldown.", _, true, -1.0); hRegen = TF2ItemPlugin_LoadRegenerateSDK(); clipOff = FindSendPropInfo("CTFWeaponBase", "m_iClip1"); ammoOff = FindSendPropInfo("CTFPlayer", "m_iAmmo"); RegConsoleCmd("sm_weapons", CMD_TF2ItemPlugin_WeaponManager, "Manage weapons on the server."); RegConsoleCmd("sm_weapon", CMD_TF2ItemPlugin_WeaponManager, "Manage weapons on the server."); RegConsoleCmd("sm_wep", CMD_TF2ItemPlugin_WeaponManager, "Manage weapons on the server."); RegConsoleCmd("sm_weps", CMD_TF2ItemPlugin_WeaponManager, "Manage weapons on the server."); Database.Connect(TF2ItemPlugin_SQL_ConnectToDatabase, "tf2itemplugins_db"); #if defined DEBUG RegAdminCmd("sm_weapons_debug", CMD_TF2ItemPlugin_DebugInventory, ADMFLAG_ROOT, "Print the player's inventory state."); RegAdminCmd("sm_weapons_debug_current", CMD_TF2ItemPlugin_DebugCurrent, ADMFLAG_ROOT, "Print the player's current weapon state."); RegAdminCmd("sm_weapons_debug_force_call", CMD_TF2ItemPlugin_ForceCall, ADMFLAG_ROOT, "Force a call to the Regenerate function."); #endif } public void OnMapStart() { TF2ItemPlugin_AttachSpawnRoomHooks(); char url[512]; g_cvar_weapons_paintKitsUrl.GetString(url, sizeof(url)); LogMessage("Requesting paint kit data from %s", url); TF2ItemPlugin_RequestPaintKitData(url); } public void OnClientAuthorized(int client) { TF2ItemPlugin_InitializeInventory(client); TF2ItemPlugin_SQL_SearchPlayerPreferences(client); g_bIsOnDatabaseCooldown[client] = false; } public Action CMD_TF2ItemPlugin_WeaponManager(int client, int args) { if (!IsValidClient(client)) return Plugin_Handled; if (!g_bInSpawnRoom[client] && g_cvar_weapons_onlySpawn.BoolValue) { CPrintToChat(client, "%s You must be in a spawn room to manage your weapons.", PLUGIN_CHATTAG); return Plugin_Handled; } TF2ItemPlugin_Menus_MainMenu(client); return Plugin_Handled; } #if defined DEBUG void Print_Slot(int client, int class, int slot) { PrintToConsole(client, "Slot %d:\n" ... "Client ID: %d\n" ... "Class ID: %d\n" ... "Slot ID: %d\n" ... "Active Override: %d\n" ... "Weapon Def Index: %d\n" ... "Stock Def Index: %d\n" ... "Quality: %d\n" ... "Level: %d\n" ... "Australium: %d\n" ... "Festive: %d\n" ... "Unusual Effect: %d\n" ... "War Paint ID: %d\n" ... "War Paint Wear: %f\n" ... "- Killstreak:\n" ... " - Active: %d\n" ... " - Tier: %d\n" ... " - Sheen: %d\n" ... " - Killstreaker: %d\n" ... "Spells Bitfield: %d\n" ... "\n\n", slot, g_inventories[client][class][slot].client, g_inventories[client][class][slot].class, g_inventories[client][class][slot].slotId, g_inventories[client][class][slot].isActiveOverride, g_inventories[client][class][slot].weaponDefIndex, g_inventories[client][class][slot].stockWeaponDefIndex, g_inventories[client][class][slot].quality, g_inventories[client][class][slot].level, g_inventories[client][class][slot].isAustralium, g_inventories[client][class][slot].isFestive, g_inventories[client][class][slot].unusualEffectId, g_inventories[client][class][slot].warPaintId, g_inventories[client][class][slot].warPaintWear, g_inventories[client][class][slot].killstreak.isActive, g_inventories[client][class][slot].killstreak.tier, g_inventories[client][class][slot].killstreak.sheen, g_inventories[client][class][slot].killstreak.killstreaker, g_inventories[client][class][slot].halloweenSpell.spells); } public Action CMD_TF2ItemPlugin_DebugInventory(int client, int args) { if (!IsValidClient(client)) return Plugin_Handled; PrintToConsole(client, "Inventory Debug\n\n\n"); if (args >= 1) { char class[2]; GetCmdArg(1, class, sizeof(class)); int iclass = StringToInt(class); if (args >= 2) { char slot[2]; GetCmdArg(2, slot, sizeof(slot)); int islot = StringToInt(slot); PrintToConsole(client, "For class %d, slot %d\n", iclass, islot); Print_Slot(client, iclass, islot); return Plugin_Handled; } PrintToConsole(client, "For class %d\n", iclass); for (int j = 0; j < MAX_WEAPONS; j++) Print_Slot(client, iclass, j); return Plugin_Handled; } for (int i = 0; i < MAX_CLASSES; i++) { PrintToConsole(client, "For class %d\n", i); for (int j = 0; j < MAX_WEAPONS; j++) { Print_Slot(client, i, j); } } return Plugin_Handled; } public Action CMD_TF2ItemPlugin_DebugCurrent(int client, int args) { if (!IsValidClient(client)) return Plugin_Handled; int weapon = GetEntPropEnt(client, Prop_Send, "m_hActiveWeapon"); if (!IsValidEntity(weapon)) { PrintToConsole(client, "No weapon found."); return Plugin_Handled; } int defIndex = GetEntProp(weapon, Prop_Send, "m_iItemDefinitionIndex"); int indexes[16]; float values[16]; TF2Attrib_GetSOCAttribs(weapon, indexes, values); PrintToConsole(client, "Current Weapon: %d\n" ... "Item Definition Index: %d\n\n", weapon, defIndex); PrintToConsole(client, "SOC Attributes\n\n"); for (int i = 0; i < 16; i++) { if (indexes[i] == 0) break; PrintToConsole(client, "SOC Attribute %d: %d = %f", i, indexes[i], values[i]); } PrintToConsole(client, "Attributes\n\n"); int attributes[16]; int count = TF2Attrib_ListDefIndices(weapon, attributes); for (int i = 0; i < count; i++) { if (attributes[i] == 0) continue; Address attrib = TF2Attrib_GetByDefIndex(weapon, attributes[i]); float value = TF2Attrib_GetValue(attrib); PrintToConsole(client, "Attribute %d: %d = %f", i, attributes[i], value); } return Plugin_Handled; } public Action CMD_TF2ItemPlugin_ForceCall(int client, int args) { if (!IsValidClient(client)) return Plugin_Handled; SDKCall(hRegen, client, false); return Plugin_Handled; } #endif bool IsValidClient(int client) { return (client > 0 && client <= MaxClients && IsClientInGame(client) && !IsClientSourceTV(client) && !IsClientReplay(client)); } ```

Or just use this

punteroo commented 2 months ago

Fixed in #34