diff --git a/plugins/adminpanel.smx b/plugins/adminpanel.smx index 5d20155..21258e4 100644 Binary files a/plugins/adminpanel.smx and b/plugins/adminpanel.smx differ diff --git a/plugins/l4d2_hats.smx b/plugins/l4d2_hats.smx index b7be2b9..92224d2 100644 Binary files a/plugins/l4d2_hats.smx and b/plugins/l4d2_hats.smx differ diff --git a/scripting/adminpanel.sp b/scripting/adminpanel.sp index fc44308..26ff6e7 100644 --- a/scripting/adminpanel.sp +++ b/scripting/adminpanel.sp @@ -7,6 +7,8 @@ #define DEFAULT_SERVER_PORT 7888 #define SOCKET_TIMEOUT_DURATION 90.0 +#define DATABASE_NAME "adminpanel" + #include #include #include @@ -14,6 +16,7 @@ #include #include #include +#include #undef REQUIRE_PLUGIN #tryinclude @@ -89,12 +92,20 @@ GameState g_gameState = State_Hibernating; Buffer sendBuffer; Buffer receiveBuffer; // Unfortunately there's no easy way to have this not be the same as BUFFER_SIZE +Database g_db; + public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) { // lateLoaded = late; return APLRes_Success; } public void OnPluginStart() { + if(!SQL_CheckConfig(DATABASE_NAME)) { + SetFailState("No database entry for '%s'; no database to connect to.", DATABASE_NAME); + } else if(!ConnectDB()) { + SetFailState("Failed to connect to database."); + } + g_socket = new Socket(SOCKET_TCP, OnSocketError); g_socket.SetOption(SocketKeepAlive, 1); g_socket.SetOption(SocketReuseAddr, 1); @@ -126,6 +137,7 @@ public void OnPluginStart() { cvar_difficulty.AddChangeHook(OnCvarChanged); gameDifficulty = GetDifficultyInt(); + HookEvent("player_info", Event_PlayerInfo); HookEvent("game_init", Event_GameStart); HookEvent("game_end", Event_GameEnd); HookEvent("heal_begin", Event_HealStart); @@ -139,6 +151,7 @@ public void OnPluginStart() { HookEvent("player_death", Event_PlayerDeath); HookEvent("player_bot_replace", Event_PlayerToBot); HookEvent("bot_player_replace", Event_BotToPlayer); + HookEvent("player_first_spawn", Event_PlayerFirstSpawn); campaignStartTime = GetTime(); char auth[32]; @@ -160,6 +173,101 @@ public void OnPluginStart() { CreateTimer(300.0, Timer_FullSync, _, TIMER_REPEAT); } +bool ConnectDB() { + char error[255]; + g_db = SQL_Connect(DATABASE_NAME, true, error, sizeof(error)); + if (g_db == null) { + LogError("Database error %s", error); + delete g_db; + return false; + } else { + PrintToServer("Connected to database %s", DATABASE_NAME); + SQL_LockDatabase(g_db); + SQL_FastQuery(g_db, "SET NAMES \"UTF8mb4\""); + SQL_UnlockDatabase(g_db); + g_db.SetCharset("utf8mb4"); + return true; + } +} +//Setups a user, this tries to fetch user by steamid +void SetupUserInDB(int client) { + if(client > 0 && !IsFakeClient(client)) { + char country[128]; + char region[128]; + char ip[64]; + if(GetClientIP(client, ip, sizeof(ip))) { + GeoipCountry(ip, country, sizeof(country)); + GeoipRegion(ip, region, sizeof(region)); + } + int time = GetTime(); + + char query[512]; + g_db.Format(query, sizeof(query), "INSERT INTO panel_user " + ..."(account_id,last_join_time,last_ip,last_country,last_region)" + ..."VALUES ('%s',%d,'%s','%s','%s')" + ..."ON DUPLICATE KEY UPDATE last_join_time=%d,last_ip='%s',last_country='%s',last_region='%s';", + steamidCache[client][10], // strip STEAM_#:#:##### returning only ending ####### + // insert: + time, + ip, + country, + region, + // update: + time, + ip, + country, + region + ); + g_db.Query(DBCT_PanelUser, query, GetClientUserId(client)); + } +} + +void DBCT_PanelUser(Database db, DBResultSet results, const char[] error, int userId) { + if(db == null || results == null) { + LogError("DBCT_Insert returned error: %s", error); + } + int client = GetClientOfUserId(userId); + if(client > 0) { + char query[128]; + g_db.Format(query, sizeof(query), "SELECT name FROM panel_user_names WHERE account_id = '%s' ORDER BY name_update_time DESC LIMIT 1", steamidCache[client][10]); // strip STEAM_#:#:##### returning only ending ####### + g_db.Query(DBCT_CheckUserName, query, userId); + } +} + +void DBCT_Insert(Database db, DBResultSet results, const char[] error, any data) { + if(db == null || results == null) { + LogError("DBCT_Insert returned error: %s", error); + } +} + +void DBCT_CheckUserName(Database db, DBResultSet results, const char[] error, int userId) { + if(db == null || results == null) { + LogError("DBCT_CheckUserName returned error: %s", error); + } else { + int client = GetClientOfUserId(userId); + if(client == 0) return; // Client left, ignore + + // Insert new name if we have none, or prev differs + bool insertNewName = true; + if(results.FetchRow()) { + if(nameCache[client][0] == '\0') { + LogError("DBCT_CheckUserName user %N(#%d) missing namecache", client, userId); + } + char prevName[64]; + results.FetchString(0, prevName, sizeof(prevName)); + if(StrEqual(prevName, nameCache[client])) { + insertNewName = false; + } + } + + if(insertNewName) { + PrintToServer("[AdminPanel] Updating/Inserting name '%s' for %s", nameCache[client], steamidCache[client]); + char query[255]; + g_db.Format(query, sizeof(query), "INSERT INTO panel_user_names (account_id,name,name_update_time) VALUES ('%s','%s',%d)", steamidCache[client][10], nameCache[client], GetTime()); + g_db.Query(DBCT_Insert, query); + } + } +} stock void Debug(const char[] format, any ...) { if(!cvar_debug.BoolValue) return; @@ -654,6 +762,20 @@ void StopServer() { ServerCommand("exit"); } +void Event_PlayerInfo(Event event, const char[] name, bool dontBroadcast) { + int client = GetClientOfUserId(event.GetInt("userid")); + if(client && !IsFakeClient(client)) { + GetClientName(client, nameCache[client], 32); + } +} + +void Event_PlayerFirstSpawn(Event event, const char[] name, bool dontBroadcast) { + int userid = event.GetInt("userid"); + int client = GetClientOfUserId(userid); + if(client > 0) + SetupUserInDB(client); +} + void Event_GameStart(Event event, const char[] name, bool dontBroadcast) { campaignStartTime = GetTime(); g_gameState = State_NewGame; @@ -925,7 +1047,8 @@ void OnCvarChanged(ConVar convar, const char[] oldValue, const char[] newValue) if(serverPort == 0) serverPort = DEFAULT_SERVER_PORT; } PrintToServer("[AdminPanel] Sending data to %s:%d", serverIp, serverPort); - ConnectSocket(); + if(authToken[0] != '\0') + ConnectSocket(); } } else if(cvar_gamemode == convar) { strcopy(gamemode, sizeof(gamemode), newValue); diff --git a/scripting/include/hats/hats.sp b/scripting/include/hats/hats.sp index e51ef96..1c93451 100644 --- a/scripting/include/hats/hats.sp +++ b/scripting/include/hats/hats.sp @@ -336,7 +336,6 @@ Action Command_DoAHat(int client, int args) { // Find a new hatable entity int flags = 0; if(args > 0 && isForced) { - char arg[16]; entity = GetCmdArgInt(1); } else { entity = GetLookingEntity(client, Filter_ValidHats); @@ -814,10 +813,6 @@ void EquipHat(int client, int entity, const char[] classname = "", int flags = H SetParentAttachment(modifyEntity, attachPoint, true); } - if(HasFlag(client, HAT_COMMANDABLE)) { - ChooseRandomPosition(hatData[client].offset); - L4D2_CommandABot(entity, client, BOT_CMD_MOVE, hatData[client].offset); - } } else { SetParent(entity, client); SetParentAttachment(modifyEntity, attachPoint, true); diff --git a/scripting/l4d2_editor.sp b/scripting/l4d2_editor.sp index 43aea98..920658b 100644 --- a/scripting/l4d2_editor.sp +++ b/scripting/l4d2_editor.sp @@ -34,9 +34,9 @@ ConVar enabledBlacklist; #include public Plugin myinfo = { - name = "L4D2 Hats & Editor", + name = "L4D2 Editor", author = "jackzmc", - description = "", + description = "Advanced prop spawner and entity editing", version = PLUGIN_VERSION, url = "https://github.com/Jackzmc/sourcemod-plugins" }; diff --git a/scripting/l4d2_extraplayeritems.sp b/scripting/l4d2_extraplayeritems.sp index 9d94302..22157e0 100644 --- a/scripting/l4d2_extraplayeritems.sp +++ b/scripting/l4d2_extraplayeritems.sp @@ -78,7 +78,7 @@ PlayerItems items[MAXPLAYERS+1]; public Plugin myinfo = { - name = "L4D2 Extra Player Tools", + name = "L4D2 5+ Extra Tools & Director", author = "jackzmc", description = "Automatic system for management of 5+ player games. Provides extra kits, items, and more", version = PLUGIN_VERSION, diff --git a/scripting/l4d2_hats.sp b/scripting/l4d2_hats.sp index 9e761d1..63b775b 100644 --- a/scripting/l4d2_hats.sp +++ b/scripting/l4d2_hats.sp @@ -15,6 +15,7 @@ static float EMPTY_ANG[3] = { 0.0, 0.0, 0.0 }; #include #include #include +#include #include @@ -41,14 +42,13 @@ char g_currentMap[64]; #include public Plugin myinfo = { - name = "L4D2 Hats & Editor", + name = "L4D2 Hats", author = "jackzmc", - description = "", + description = "Hat props and cause problems", version = PLUGIN_VERSION, url = "https://github.com/Jackzmc/sourcemod-plugins" }; -ArrayList NavAreas; public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) { return APLRes_Success; } @@ -343,10 +343,10 @@ void Event_PlayerOutOfIdle(Event event, const char[] name, bool dontBroadcast) { void Frame_FixClient(int client) { if(IsClientConnected(client) && GetClientTeam(client) == 2) { - ClearParent(client); - SetEntProp(client, Prop_Send, "m_CollisionGroup", 5); - SetEntProp(client, Prop_Send, "m_nSolidType", 2); - SetEntityMoveType(client, MOVETYPE_WALK); + ClearParent(client); + SetEntProp(client, Prop_Send, "m_CollisionGroup", 5); + SetEntProp(client, Prop_Send, "m_nSolidType", 2); + SetEntityMoveType(client, MOVETYPE_WALK); } // SetEntProp(client, Prop_Send, "movetype", MOVETYPE_ISOMETRIC); } @@ -574,12 +574,21 @@ public void OnMapStart() { tempGod[i] = false; } GetCurrentMap(g_currentMap, sizeof(g_currentMap)); - NavAreas = GetSpawnLocations(); +} + +stock bool L4D_IsPlayerCapped(int client) { + if(GetEntPropEnt(client, Prop_Send, "m_pummelAttacker") > 0 || + GetEntPropEnt(client, Prop_Send, "m_carryAttacker") > 0 || + GetEntPropEnt(client, Prop_Send, "m_pounceAttacker") > 0 || + GetEntPropEnt(client, Prop_Send, "m_jockeyAttacker") > 0 || + GetEntPropEnt(client, Prop_Send, "m_pounceAttacker") > 0 || + GetEntPropEnt(client, Prop_Send, "m_tongueOwner") > 0) + return true; + return false; } public void OnMapEnd() { - delete NavAreas; ClearHats(); } public void OnPluginEnd() {