Compare commits
211
Commits
bface70a04
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
54a921c1a5 | ||
|
|
1e969e697f | ||
|
|
eb19a779a6 | ||
|
|
f38a714a2e | ||
|
|
64bc2be4aa | ||
|
|
854da9b615 | ||
|
|
0a1bf2e910 | ||
|
|
f7995a3656 | ||
|
|
014f15acea | ||
|
|
d6f3d622dd | ||
|
|
cbe461ac26 | ||
|
|
aa7b60343f | ||
|
|
63c218a0e9 | ||
|
|
598dc20ae3 | ||
|
|
867b3bfbba | ||
|
|
41f86102e5 | ||
|
|
fc64e0ca07 | ||
|
|
2250d0009c | ||
|
|
2841927ea0 | ||
|
|
9c1a884643 | ||
|
|
b7b7828f65 | ||
|
|
6268e9ed19 | ||
|
|
5729eaa051 | ||
|
|
79bd5af4dd | ||
|
|
c6b1c74135 | ||
|
|
1b3860f7f6 | ||
|
|
fd63b4c235 | ||
|
|
da984e5266 | ||
|
|
82b0fa7eb1 | ||
|
|
27e584bbca | ||
|
|
2947bef434 | ||
|
|
789b8ebef9 | ||
|
|
1ccb5cc387 | ||
|
|
fbc8a56fe1 | ||
|
|
42ffd211e7 | ||
|
|
cfa37daeb0 | ||
|
|
07f9a6768e | ||
|
|
6b7803d46e | ||
|
|
ac67f2e27e | ||
|
|
ef4bc91712 | ||
|
|
e3e70fcb12 | ||
|
|
4fd3017e5f | ||
|
|
b791b050fc | ||
|
|
48ff38522b | ||
|
|
5a8c97e082 | ||
|
|
d697faa470 | ||
|
|
cb8d244030 | ||
|
|
2e21de0c47 | ||
|
|
80642cb0f0 | ||
|
|
06d0b43b9c | ||
|
|
b6c0cb232d | ||
|
|
a02fea8cc8 | ||
|
|
a4e2d541a9 | ||
|
|
1cb0905d78 | ||
|
|
14e83ec9a3 | ||
|
|
a72b0d9a8d | ||
|
|
4cf475c109 | ||
|
|
ef5ba318c1 | ||
|
|
2bde95afcc | ||
|
|
1fa8a916d6 | ||
|
|
52a452708e | ||
|
|
8ef2f2f2cc | ||
|
|
b527ce5473 | ||
|
|
921ae4e136 | ||
|
|
fd32ddc101 | ||
|
|
2b9224f11a | ||
|
|
ef15dfc71c | ||
|
|
2d91202469 | ||
|
|
622bc8da4e | ||
|
|
295c92f49f | ||
|
|
47460a2db4 | ||
|
|
a23be2483f | ||
|
|
b6e7443448 | ||
|
|
e3e5945e77 | ||
|
|
a8dbd00f89 | ||
|
|
f6536fd3c5 | ||
|
|
8ca6ecae97 | ||
|
|
02a990a29f | ||
|
|
1139fd89d0 | ||
|
|
5986bedc42 | ||
|
|
ee18f49be0 | ||
|
|
5a1ca4959d | ||
|
|
4a9a8e06cf | ||
|
|
6b625bc7ea | ||
|
|
bf6ed6e4e1 | ||
|
|
4469d07602 | ||
|
|
ce78793029 | ||
|
|
509bd521fb | ||
|
|
71d8773767 | ||
|
|
111cc27521 | ||
|
|
c03f450e8e | ||
|
|
e2b3ead9c3 | ||
|
|
c379458632 | ||
|
|
9e59258ab0 | ||
|
|
e2659d79a6 | ||
|
|
e74db611c4 | ||
|
|
fefad5ce8a | ||
|
|
3d7e279cc7 | ||
|
|
e86e90838a | ||
|
|
7ec6fed775 | ||
|
|
e5974b7478 | ||
|
|
429a2d1b0f | ||
|
|
07c8d450b3 | ||
|
|
a460fbeaeb | ||
|
|
4b89862452 | ||
|
|
8be8ced61a | ||
|
|
88b153ea3c | ||
|
|
e0672ab09b | ||
|
|
56c97b7d29 | ||
|
|
bf4c76a17c | ||
|
|
06e5c325ac | ||
|
|
4914461539 | ||
|
|
e3a85e1b65 | ||
|
|
5cce927ced | ||
|
|
b8ac8cc571 | ||
|
|
723525f8c5 | ||
|
|
890e2e1418 | ||
|
|
61568dc032 | ||
|
|
8b6f2e0d96 | ||
|
|
9ff0faf337 | ||
|
|
b6a1c15b7e | ||
|
|
0866d58b59 | ||
|
|
6acdc80767 | ||
|
|
80d0b85367 | ||
|
|
1df4cdf8dc | ||
|
|
c819974dd6 | ||
|
|
d48a5b14c1 | ||
|
|
45258da9e1 | ||
|
|
8e46053c0b | ||
|
|
2b67f52368 | ||
|
|
f5804eed72 | ||
|
|
c57aa5bcc2 | ||
|
|
4a1ac398d0 | ||
|
|
f590dcc7d1 | ||
|
|
01816792ae | ||
|
|
f37711fbdf | ||
|
|
7d5e56540a | ||
|
|
8e33a8bd87 | ||
|
|
208d084024 | ||
|
|
b4c4b11c3d | ||
|
|
778fdd2f7a | ||
|
|
d396e006b9 | ||
|
|
d12aff14ac | ||
|
|
7e3ad44699 | ||
|
|
13f4727826 | ||
|
|
06734d1b6e | ||
|
|
ff012d2df5 | ||
|
|
ef537b8826 | ||
|
|
6790e463fc | ||
|
|
6a5cbf74f1 | ||
|
|
a5a856073e | ||
|
|
3122ed0210 | ||
|
|
9d3a9e6b89 | ||
|
|
4c69c978b2 | ||
|
|
940b53fffc | ||
|
|
5855e49f78 | ||
|
|
1bbed31610 | ||
|
|
6867ef2482 | ||
|
|
d78c39cf3c | ||
|
|
c0cddad8cb | ||
|
|
c0fb1dd61a | ||
|
|
01fad8b648 | ||
|
|
4efd426893 | ||
|
|
005552248b | ||
|
|
3dc480d564 | ||
|
|
2e99fec5de | ||
|
|
b81b8d0773 | ||
|
|
b789c71d23 | ||
|
|
cb7dd7462b | ||
|
|
363233b044 | ||
|
|
cd4937521b | ||
|
|
f3995b4b6d | ||
|
|
e49f0a3f3a | ||
|
|
29971e96fc | ||
|
|
41b775f611 | ||
|
|
7d940c6509 | ||
|
|
c1da687fa8 | ||
|
|
31d550bbcf | ||
|
|
2ad829bd54 | ||
|
|
414ade0856 | ||
|
|
d45bcf955a | ||
|
|
4f57a8957c | ||
|
|
484430cd50 | ||
|
|
4d3d774cc0 | ||
|
|
1cc618ed04 | ||
|
|
3da429b91f | ||
|
|
b57f8fdd68 | ||
|
|
9606606baf | ||
|
|
9527c5bc9b | ||
|
|
126c976a65 | ||
|
|
4b27950b98 | ||
|
|
9e79d95041 | ||
|
|
8ff4eee7e0 | ||
|
|
c72a911bcb | ||
|
|
60d4ba7828 | ||
|
|
72bd0c3ce5 | ||
|
|
fca3e99528 | ||
|
|
c002407410 | ||
|
|
f3575929f7 | ||
|
|
895c9639d6 | ||
|
|
15d44df991 | ||
|
|
de4f04a310 | ||
|
|
6a0b75ed61 | ||
|
|
3ec612167d | ||
|
|
cf58c745d2 | ||
|
|
8c72b1cae1 | ||
|
|
c9637a753f | ||
|
|
09aa1218ac | ||
|
|
386da2e248 | ||
|
|
786225b6a2 | ||
|
|
b824afd1a3 |
@@ -0,0 +1,76 @@
|
||||
#include <sourcemod>
|
||||
#include <sdktools>
|
||||
#include <cstrike>
|
||||
#include <AFKManager>
|
||||
|
||||
int g_iPlayer_move_spec_count;
|
||||
|
||||
public Plugin myinfo =
|
||||
{
|
||||
name = "spectator_handler",
|
||||
author = "jenz",
|
||||
description = "moves afk spectators to team",
|
||||
version = "1.3",
|
||||
url = ""
|
||||
};
|
||||
|
||||
public void OnPluginStart()
|
||||
{
|
||||
CreateTimer(60.0, CheckAfks, _, TIMER_REPEAT);
|
||||
|
||||
ConVar cvar;
|
||||
HookConVarChange((cvar = CreateConVar("sm_move_spec_to_team_count", "30", "Move real players from spec to teams until this many real players are connected")), Cvar_move_spec_team);
|
||||
g_iPlayer_move_spec_count = cvar.IntValue;
|
||||
delete cvar;
|
||||
}
|
||||
|
||||
public void Cvar_move_spec_team(ConVar convar, const char[] oldValue, const char[] newValue)
|
||||
{
|
||||
g_iPlayer_move_spec_count = convar.IntValue;
|
||||
}
|
||||
|
||||
public Action CheckAfks(Handle timer)
|
||||
{
|
||||
int real_players = 0;
|
||||
for (int i = 0; i < MaxClients; i++)
|
||||
{
|
||||
if (IsValidClient(i) && !IsFakeClient(i) && !IsClientSourceTV(i))
|
||||
{
|
||||
real_players++;
|
||||
}
|
||||
}
|
||||
if (real_players < g_iPlayer_move_spec_count)
|
||||
{
|
||||
for (int i = 0; i < MaxClients; i++)
|
||||
{
|
||||
if (IsValidClient(i) && !IsFakeClient(i) && !IsClientSourceTV(i) && GetClientTeam(i) <= CS_TEAM_SPECTATOR && GetClientIdleTime(i) > 60)
|
||||
{
|
||||
ChangeClientTeam(i, CS_TEAM_T);
|
||||
CreateTimer(1.0, zspawn, GetClientSerial(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
public Action zspawn(Handle hTimer, int Serial)
|
||||
{
|
||||
int client;
|
||||
if ((client = GetClientFromSerial(Serial)) == 0)
|
||||
{
|
||||
return Plugin_Handled;
|
||||
}
|
||||
if (IsValidClient(client))
|
||||
{
|
||||
FakeClientCommandEx(client, "joinclass");
|
||||
FakeClientCommandEx(client, "say /zspawn");
|
||||
}
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
stock bool IsValidClient(int client)
|
||||
{
|
||||
if (client > 0 && client <= MaxClients && IsClientConnected(client) && IsClientInGame(client))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
#include <sourcemod>
|
||||
#include <sdktools>
|
||||
#include <SteamWorks>
|
||||
#include <ripext>
|
||||
#include <cstrike>
|
||||
#include <AdvancedTargeting>
|
||||
|
||||
@@ -437,17 +437,14 @@ public void OnClientAuthorized(int client, const char[] auth)
|
||||
char sSteam64ID[32];
|
||||
Steam32IDtoSteam64ID(auth, sSteam64ID, sizeof(sSteam64ID));
|
||||
|
||||
// format=json instead of format=vdf: ripext only exposes the response body
|
||||
// as decoded JSON, so we ask Steam's own API for JSON directly rather than
|
||||
// working around a missing raw-body accessor.
|
||||
static char sRequest[256];
|
||||
FormatEx(sRequest, sizeof(sRequest), "http://api.steampowered.com/ISteamUser/GetFriendList/v0001/?key=%s&steamid=%s&relationship=friend&format=vdf", STEAM_API_KEY, sSteam64ID);
|
||||
FormatEx(sRequest, sizeof(sRequest), "http://api.steampowered.com/ISteamUser/GetFriendList/v0001/?key=%s&steamid=%s&relationship=friend&format=json", STEAM_API_KEY, sSteam64ID);
|
||||
|
||||
Handle hRequest = SteamWorks_CreateHTTPRequest(k_EHTTPMethodGET, sRequest);
|
||||
if (!hRequest ||
|
||||
!SteamWorks_SetHTTPRequestContextValue(hRequest, client) ||
|
||||
!SteamWorks_SetHTTPCallbacks(hRequest, OnTransferComplete) ||
|
||||
!SteamWorks_SendHTTPRequest(hRequest))
|
||||
{
|
||||
CloseHandle(hRequest);
|
||||
}
|
||||
HTTPRequest hRequest = new HTTPRequest(sRequest);
|
||||
hRequest.Get(OnTransferComplete, client);
|
||||
}
|
||||
|
||||
public void OnClientDisconnect(int client)
|
||||
@@ -458,52 +455,38 @@ public void OnClientDisconnect(int client)
|
||||
g_FriendsArray[client] = INVALID_HANDLE;
|
||||
}
|
||||
|
||||
public int OnTransferComplete(Handle hRequest, bool bFailure, bool bRequestSuccessful, EHTTPStatusCode eStatusCode, int client)
|
||||
public void OnTransferComplete(HTTPResponse response, any client)
|
||||
{
|
||||
if(bFailure || !bRequestSuccessful || eStatusCode != k_EHTTPStatusCode200OK)
|
||||
if(response.Status != HTTPStatus_OK || response.Data == null)
|
||||
{
|
||||
// Private profile or maybe steam down?
|
||||
//LogError("SteamAPI HTTP Response failed: %d", eStatusCode);
|
||||
CloseHandle(hRequest);
|
||||
//LogError("SteamAPI HTTP Response failed: %d", response.Status);
|
||||
return;
|
||||
}
|
||||
|
||||
int Length;
|
||||
SteamWorks_GetHTTPResponseBodySize(hRequest, Length);
|
||||
|
||||
char[] sData = new char[Length];
|
||||
SteamWorks_GetHTTPResponseBodyData(hRequest, sData, Length);
|
||||
//SteamWorks_GetHTTPResponseBodyCallback(hRequest, APIWebResponse, client);
|
||||
|
||||
CloseHandle(hRequest);
|
||||
|
||||
APIWebResponse(sData, client);
|
||||
APIWebResponse(view_as<JSONObject>(response.Data), client);
|
||||
}
|
||||
|
||||
public void APIWebResponse(const char[] sData, int client)
|
||||
public void APIWebResponse(JSONObject Response, int client)
|
||||
{
|
||||
KeyValues Response = new KeyValues("SteamAPIResponse");
|
||||
if(!Response.ImportFromString(sData, "SteamAPIResponse"))
|
||||
if(!Response.HasKey("friendslist"))
|
||||
{
|
||||
LogError("ImportFromString(sData, \"SteamAPIResponse\") failed.");
|
||||
LogError("GetFriendList response missing \"friendslist\" key.");
|
||||
delete Response;
|
||||
return;
|
||||
}
|
||||
|
||||
if(!Response.JumpToKey("friends"))
|
||||
JSONObject friendslist = view_as<JSONObject>(Response.Get("friendslist"));
|
||||
|
||||
// No friends, or private profile - Steam returns {"friendslist":{}} in that case.
|
||||
if(!friendslist.HasKey("friends"))
|
||||
{
|
||||
LogError("JumpToKey(\"friends\") failed.");
|
||||
delete friendslist;
|
||||
delete Response;
|
||||
return;
|
||||
}
|
||||
|
||||
// No friends?
|
||||
if(!Response.GotoFirstSubKey())
|
||||
{
|
||||
//LogError("GotoFirstSubKey() failed.");
|
||||
delete Response;
|
||||
return;
|
||||
}
|
||||
JSONArray friends = view_as<JSONArray>(friendslist.Get("friends"));
|
||||
|
||||
if(g_FriendsArray[client] != INVALID_HANDLE)
|
||||
CloseHandle(g_FriendsArray[client]);
|
||||
@@ -511,18 +494,20 @@ public void APIWebResponse(const char[] sData, int client)
|
||||
g_FriendsArray[client] = CreateArray();
|
||||
|
||||
char sCommunityID[32];
|
||||
do
|
||||
int len = friends.Length;
|
||||
for(int i = 0; i < len; i++)
|
||||
{
|
||||
Response.GetString("steamid", sCommunityID, sizeof(sCommunityID));
|
||||
|
||||
PushArrayCell(g_FriendsArray[client], Steam64toSteam3(sCommunityID));
|
||||
JSONObject friendEntry = view_as<JSONObject>(friends.Get(i));
|
||||
if(friendEntry.GetString("steamid", sCommunityID, sizeof(sCommunityID)))
|
||||
PushArrayCell(g_FriendsArray[client], Steam64toSteam3(sCommunityID));
|
||||
delete friendEntry;
|
||||
}
|
||||
while(Response.GotoNextKey());
|
||||
|
||||
delete friends;
|
||||
delete friendslist;
|
||||
delete Response;
|
||||
}
|
||||
|
||||
|
||||
stock bool Steam32IDtoSteam64ID(const char[] sSteam32ID, char[] sSteam64ID, int Size)
|
||||
{
|
||||
if(strlen(sSteam32ID) < 11 || strncmp(sSteam32ID[0], "STEAM_0:", 8))
|
||||
@@ -634,3 +619,4 @@ public int Native_ReadClientFriends(Handle plugin, int numParams)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -426,9 +426,12 @@ NotifyAdmins(int client, const char[] sReason)
|
||||
if(IsClientInGame(i) && !IsFakeClient(i) && CheckCommandAccess(i, "sm_stats", ADMFLAG_GENERIC))
|
||||
{
|
||||
CPrintToChat(i, "{green}[SM]{default} %L has been detected for {red}%s{default}, please check your console!", client, sReason);
|
||||
FormatStats(i, client);
|
||||
char sBuffer[2000];
|
||||
FormatStats(i, client, sBuffer, 0);
|
||||
PrintToConsole(client, "%s", "\n");
|
||||
FormatStreak(i, client, 0);
|
||||
int len = FormatStats(-1, client, sBuffer, sizeof(sBuffer));
|
||||
sBuffer[len++] = '\n';
|
||||
FormatStreak(i, client, 0, sBuffer[len], sizeof(sBuffer) - len);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -464,12 +467,13 @@ public Action Command_Stats(int client, int argc)
|
||||
|
||||
for(int i = 0; i < iTargetCount; i++)
|
||||
{
|
||||
FormatStats(client, iTargets[i]);
|
||||
char sBuffer[2000];
|
||||
FormatStats(i, client, sBuffer, 0);
|
||||
PrintToConsole(client, "%s", "\n");
|
||||
|
||||
for(int j = 0; j < 3; j++)
|
||||
{
|
||||
FormatStreak(client, iTargets[i], j);
|
||||
FormatStreak(client, iTargets[i], j, sBuffer[0], sizeof(sBuffer) - 0);
|
||||
PrintToConsole(client, "%s", "\n");
|
||||
}
|
||||
}
|
||||
@@ -509,13 +513,14 @@ public Action Command_Streak(int client, int argc)
|
||||
|
||||
for(int i = 0; i < iTargetCount; i++)
|
||||
{
|
||||
FormatStreak(client, iTargets[i], iStreak);
|
||||
char sBuffer[2000];
|
||||
FormatStreak(client, iTargets[i], iStreak, sBuffer[0], 0);
|
||||
}
|
||||
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
int FormatStats(int client, int iTarget, char[] sBuf=0, int len=0)
|
||||
int FormatStats(int client, int iTarget, char[] sBuf, int len=0)
|
||||
{
|
||||
int iUserID = GetClientUserId(iTarget);
|
||||
char sAuth[32];
|
||||
@@ -545,7 +550,7 @@ int FormatStats(int client, int iTarget, char[] sBuf=0, int len=0)
|
||||
return iBuf;
|
||||
}
|
||||
|
||||
int FormatStreak(int client, int iTarget, int iStreak, char[] sBuf=0, int len=0)
|
||||
int FormatStreak(int client, int iTarget, int iStreak, char[] sBuf, int len=0)
|
||||
{
|
||||
int iUserID = GetClientUserId(iTarget);
|
||||
char sAuth[32];
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
#include <sourcemod>
|
||||
#include <cstrike>
|
||||
#include <mapchooser_extended>
|
||||
|
||||
int g_iDoingMapTouristMode;
|
||||
int g_iActivePlayerCount;
|
||||
bool g_bMapended;
|
||||
|
||||
public Plugin myinfo =
|
||||
{
|
||||
name = "knockback low pop modifier and helper",
|
||||
author = "jenz",
|
||||
description = "balances knockback and human settings for low population",
|
||||
version = "2.1.0",
|
||||
url = ""
|
||||
}
|
||||
|
||||
public void OnPluginStart()
|
||||
{
|
||||
HookEvent("round_start", OnRoundStart);
|
||||
RegAdminCmd("sm_helpon", Cmd_enableHelp, ADMFLAG_GENERIC);
|
||||
RegAdminCmd("sm_helpoff", Cmd_disableHelp, ADMFLAG_GENERIC);
|
||||
CreateTimer(80.0, CheckPopulation, _, TIMER_REPEAT);
|
||||
g_iDoingMapTouristMode = 0;
|
||||
g_iActivePlayerCount = 0;
|
||||
g_bMapended = false;
|
||||
}
|
||||
|
||||
public Action Cmd_enableHelp(int client, int args)
|
||||
{
|
||||
PrintToChatAll("ADMIN ENABLED LOW POP HELP");
|
||||
CreateTimer(1.0, SetHealthOnCT);
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
public Action Cmd_disableHelp(int client, int args)
|
||||
{
|
||||
PrintToChatAll("ADMIN DISABLED LOW POP HELP");
|
||||
ServerCommand("sm_iammo @all 0");
|
||||
ServerCommand("sm_hp @ct 100");
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
public Action CheckPopulation(Handle timer)
|
||||
{
|
||||
int TimeLeft;
|
||||
if (GetMapTimeLeft(TimeLeft) && TimeLeft < 0)
|
||||
{
|
||||
return Plugin_Continue;
|
||||
}
|
||||
if (g_bMapended)
|
||||
{
|
||||
g_bMapended = false;
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
g_iActivePlayerCount = 0;
|
||||
for (int i = 1; i <= MaxClients; i++)
|
||||
{
|
||||
if (IsClientConnected(i) && IsClientInGame(i) && IsClientAuthorized(i))
|
||||
{
|
||||
g_iActivePlayerCount++;
|
||||
}
|
||||
}
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
public void OnPluginEnd()
|
||||
{
|
||||
ServerCommand("zr_class_set_multiplier zombies knockback 1.0");
|
||||
g_iDoingMapTouristMode = 0;
|
||||
g_iActivePlayerCount = 0;
|
||||
}
|
||||
|
||||
public void OnMapEnd()
|
||||
{
|
||||
g_iDoingMapTouristMode = 0;
|
||||
ServerCommand("zr_class_set_multiplier zombies knockback 1.0");
|
||||
g_bMapended = true;
|
||||
}
|
||||
|
||||
public void OnMapStart()
|
||||
{
|
||||
char map[PLATFORM_MAX_PATH];
|
||||
GetCurrentMap(map, PLATFORM_MAX_PATH);
|
||||
|
||||
//skipping de_, cs_, zm_ maps
|
||||
if (StrContains(map, "ze_") == 0)
|
||||
{
|
||||
//LowPopHelpCount
|
||||
g_iDoingMapTouristMode = GetMapLowPopHelpCount(map);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnRoundStart(Event hEvent, const char[] sEvent, bool bDontBroadcast)
|
||||
{
|
||||
ServerCommand("zr_class_set_multiplier zombies knockback 1.0");
|
||||
ServerCommand("sm_iammo @all 0");
|
||||
|
||||
int choosen_number = 35;
|
||||
if (g_iDoingMapTouristMode > g_iActivePlayerCount > 0)
|
||||
{
|
||||
CreateTimer(3.0, SetHealthOnCT);
|
||||
}
|
||||
else if (g_iActivePlayerCount < choosen_number)
|
||||
{
|
||||
int zombie_ratio = GetConVarInt(FindConVar("zr_infect_mzombie_ratio"));
|
||||
int infectectable_players = 0;
|
||||
for (int i = 1; i <= MaxClients; i++)
|
||||
{
|
||||
if (IsClientConnected(i) && IsClientInGame(i) && IsClientAuthorized(i) && GetClientTeam(i) > CS_TEAM_SPECTATOR)
|
||||
{
|
||||
infectectable_players++;
|
||||
}
|
||||
}
|
||||
float knockback_increase = float(zombie_ratio) / float(infectectable_players);
|
||||
if (knockback_increase > 3.0)
|
||||
knockback_increase = 3.0;
|
||||
else if(knockback_increase < 1.0)
|
||||
knockback_increase = 1.0
|
||||
|
||||
if (knockback_increase > 1.0)
|
||||
{
|
||||
PrintToChatAll("LOW POP: increased zombie knockback to X%.2f to balance gameplay", knockback_increase);
|
||||
PrintToChatAll("LOW POP: increased zombie knockback to X%.2f to balance gameplay", knockback_increase);
|
||||
PrintToChatAll("LOW POP: increased zombie knockback to X%.2f to balance gameplay", knockback_increase);
|
||||
ServerCommand("zr_class_set_multiplier zombies knockback %.2f", knockback_increase);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Action SetHealthOnCT(Handle timer)
|
||||
{
|
||||
PrintToChatAll("LOW POP ON DIFFICULT MAP: ENABLING INFINITE AMMO AND 500 HP FOR CT");
|
||||
PrintToChatAll("LOW POP ON DIFFICULT MAP: ENABLING INFINITE AMMO AND 500 HP FOR CT");
|
||||
PrintToChatAll("LOW POP ON DIFFICULT MAP: ENABLING INFINITE AMMO AND 500 HP FOR CT");
|
||||
ServerCommand("sm_iammo @all 1");
|
||||
ServerCommand("sm_hp @ct 500");
|
||||
return Plugin_Handled;
|
||||
}
|
||||
+52
-35
@@ -21,7 +21,7 @@ ConVar g_hCvar_DebugMode;
|
||||
|
||||
char g_sHUDText[256];
|
||||
|
||||
Handle g_aHurtEntityToBoss[4096] = {INVALID_HANDLE, ...};
|
||||
Handle g_aHurtEntityToBoss[8191] = {INVALID_HANDLE, ...};
|
||||
|
||||
public Plugin myinfo =
|
||||
{
|
||||
@@ -248,7 +248,8 @@ public void OnMapStart()
|
||||
if(StrEqual(sMethod, "breakable"))
|
||||
{
|
||||
char sBreakable[64];
|
||||
if(!KvConfig.GetString("breakable", sBreakable, sizeof(sBreakable)))
|
||||
KvConfig.GetString("breakable", sBreakable, sizeof(sBreakable), "not found");
|
||||
if(StrEqual(sBreakable, "not found"))
|
||||
{
|
||||
LogError("Could not find \"breakable\" in \"%s\"", sSection);
|
||||
continue;
|
||||
@@ -263,7 +264,8 @@ public void OnMapStart()
|
||||
else if(StrEqual(sMethod, "counter"))
|
||||
{
|
||||
char sCounter[64];
|
||||
if(!KvConfig.GetString("counter", sCounter, sizeof(sCounter)))
|
||||
KvConfig.GetString("counter", sCounter, sizeof(sCounter), "not found");
|
||||
if (StrEqual(sCounter, "not found"))
|
||||
{
|
||||
LogError("Could not find \"counter\" in \"%s\"", sSection);
|
||||
continue;
|
||||
@@ -278,21 +280,24 @@ public void OnMapStart()
|
||||
else if(StrEqual(sMethod, "hpbar"))
|
||||
{
|
||||
char sIterator[64];
|
||||
if(!KvConfig.GetString("iterator", sIterator, sizeof(sIterator)))
|
||||
KvConfig.GetString("iterator", sIterator, sizeof(sIterator), "not found");
|
||||
if (StrEqual(sIterator, "not found"))
|
||||
{
|
||||
LogError("Could not find \"iterator\" in \"%s\"", sSection);
|
||||
continue;
|
||||
}
|
||||
|
||||
char sCounter[64];
|
||||
if(!KvConfig.GetString("counter", sCounter, sizeof(sCounter)))
|
||||
KvConfig.GetString("counter", sCounter, sizeof(sCounter), "not found");
|
||||
if (StrEqual(sCounter, "not found"))
|
||||
{
|
||||
LogError("Could not find \"counter\" in \"%s\"", sSection);
|
||||
continue;
|
||||
}
|
||||
|
||||
char sBackup[64];
|
||||
if(!KvConfig.GetString("backup", sBackup, sizeof(sBackup)))
|
||||
KvConfig.GetString("backup", sBackup, sizeof(sBackup), "not found");
|
||||
if (StrEqual(sBackup, "not found"))
|
||||
{
|
||||
LogError("Could not find \"backup\" in \"%s\"", sSection);
|
||||
continue;
|
||||
@@ -495,7 +500,7 @@ public void OnEntitySpawned(int entity, const char[] classname)
|
||||
{
|
||||
char sBoss[64];
|
||||
Config.GetName(sBoss, sizeof(sBoss));
|
||||
LogDebugMessage("Couldn't find boss \"%s\" for hurttrigger %s:%s", sBoss, sHurtTrigger, sHurtOutput);
|
||||
LogDebugMessage("Couldnt find boss \"%s\" for hurttrigger %s:%s", sBoss, sHurtTrigger, sHurtOutput);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -931,33 +936,45 @@ public void OnGameFrame()
|
||||
}
|
||||
}
|
||||
|
||||
if(!IsVoteInProgress())
|
||||
{
|
||||
if(g_sHUDText[0])
|
||||
{
|
||||
for(int client = 1; client <= MaxClients; client++)
|
||||
{
|
||||
if(IsClientInGame(client))
|
||||
{
|
||||
PrintHintText(client, g_sHUDText);
|
||||
StopSound(client, SNDCHAN_STATIC, "UI/hint.wav");
|
||||
}
|
||||
}
|
||||
s_bLastHudPrinted = true;
|
||||
}
|
||||
else if(s_bLastHudPrinted)
|
||||
{
|
||||
for(int client = 1; client <= MaxClients; client++)
|
||||
{
|
||||
if(IsClientInGame(client))
|
||||
{
|
||||
PrintHintText(client, "");
|
||||
StopSound(client, SNDCHAN_STATIC, "UI/hint.wav");
|
||||
}
|
||||
}
|
||||
s_bLastHudPrinted = false;
|
||||
}
|
||||
}
|
||||
bool voteInProgress = IsVoteInProgress();
|
||||
if(g_sHUDText[0])
|
||||
{
|
||||
for(int client = 1; client <= MaxClients; client++)
|
||||
{
|
||||
if(IsClientInGame(client))
|
||||
{
|
||||
if (voteInProgress)
|
||||
{
|
||||
PrintCenterText(client, g_sHUDText);
|
||||
}
|
||||
else
|
||||
{
|
||||
PrintHintText(client, g_sHUDText);
|
||||
}
|
||||
StopSound(client, SNDCHAN_STATIC, "UI/hint.wav");
|
||||
}
|
||||
}
|
||||
s_bLastHudPrinted = true;
|
||||
}
|
||||
else if(s_bLastHudPrinted)
|
||||
{
|
||||
for(int client = 1; client <= MaxClients; client++)
|
||||
{
|
||||
if(IsClientInGame(client))
|
||||
{
|
||||
if (voteInProgress)
|
||||
{
|
||||
PrintCenterText(client, g_sHUDText);
|
||||
}
|
||||
else
|
||||
{
|
||||
PrintHintText(client, g_sHUDText);
|
||||
}
|
||||
StopSound(client, SNDCHAN_STATIC, "UI/hint.wav");
|
||||
}
|
||||
}
|
||||
s_bLastHudPrinted = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool BossInit(CBoss _Boss)
|
||||
@@ -1415,7 +1432,7 @@ bool BossProcess(CBoss _Boss)
|
||||
_Boss.iLastChange = iLastChange;
|
||||
}
|
||||
|
||||
// Boss hasn't initialized HP yet.
|
||||
// Boss hasnt initialized HP yet.
|
||||
if(iHealth == 0 && iLastHealth == 0)
|
||||
{
|
||||
// Boss invalid: Delete boss
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#include <sourcemod>
|
||||
#include <BossHP>
|
||||
#include <zombiereloaded>
|
||||
#include <hlstatsx_loghelper>
|
||||
|
||||
ArrayList g_hStats[MAXPLAYERS+1];
|
||||
|
||||
@@ -208,19 +207,16 @@ public void OnBossKilled(CBoss Boss, CConfig Config, int reason)
|
||||
{
|
||||
Format(sBuffer, sizeof(sBuffer), "%s\n1. %N - %d %s", sBuffer, iSortedList[0][0], iSortedList[0][1], sType);
|
||||
|
||||
LH_LogPlayerEvent(iSortedList[0][0], "triggered", "ze_boss_damage_first", true);
|
||||
}
|
||||
if (iSortedList[1][0])
|
||||
{
|
||||
Format(sBuffer, sizeof(sBuffer), "%s\n2. %N - %d %s", sBuffer, iSortedList[1][0], iSortedList[1][1], sType);
|
||||
|
||||
LH_LogPlayerEvent(iSortedList[1][0], "triggered", "ze_boss_damage_second", true);
|
||||
}
|
||||
if (iSortedList[2][0])
|
||||
{
|
||||
Format(sBuffer, sizeof(sBuffer), "%s\n3. %N - %d %s", sBuffer, iSortedList[2][0], iSortedList[2][1], sType);
|
||||
|
||||
LH_LogPlayerEvent(iSortedList[2][0], "triggered", "ze_boss_damage_third", true);
|
||||
}
|
||||
|
||||
Format(sBuffer, sizeof(sBuffer), "%s\n*************************", sBuffer);
|
||||
@@ -280,6 +276,22 @@ public void OnBossKilled(CBoss Boss, CConfig Config, int reason)
|
||||
}
|
||||
else
|
||||
PrintToChatAll(sBuffer);
|
||||
|
||||
for (int i = 0; i <= MaxClients; i++)
|
||||
{
|
||||
if (iSortedList[i][0])
|
||||
{
|
||||
char sBuffer_rest[512];
|
||||
Format(sBuffer_rest, sizeof(sBuffer_rest), "%s\n%i. %N - %d %s", sBuffer_rest, i, iSortedList[i][0], iSortedList[i][1], sType);
|
||||
PrintToConsoleAll(sBuffer_rest);
|
||||
}
|
||||
else if (IsValidClient(i))
|
||||
{
|
||||
char sBuffer_rest[512];
|
||||
Format(sBuffer_rest, sizeof(sBuffer_rest), "%s\n-1. %N - Did not shoot.", sBuffer_rest, i);
|
||||
PrintToConsoleAll(sBuffer_rest);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,340 @@
|
||||
#include <sourcemod>
|
||||
#include <BossHP>
|
||||
#include <zombiereloaded>
|
||||
|
||||
ArrayList g_hStats[MAXPLAYERS+1];
|
||||
|
||||
bool g_bZRLoaded;
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public Plugin myinfo =
|
||||
{
|
||||
name = "BossHP - Ranking",
|
||||
author = "Neon & zaCade",
|
||||
description = "",
|
||||
version = "1.0.0",
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public void OnPluginStart()
|
||||
{
|
||||
HookEvent("player_death", EventHook_PlayerDeath, EventHookMode_Pre);
|
||||
|
||||
for (int client = 1; client <= MaxClients; client++)
|
||||
{
|
||||
if (!IsClientInGame(client))
|
||||
continue;
|
||||
|
||||
OnClientPutInServer(client);
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public void OnAllPluginsLoaded()
|
||||
{
|
||||
g_bZRLoaded = LibraryExists("zombiereloaded");
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public void OnLibraryAdded(const char[] sName)
|
||||
{
|
||||
if (strcmp(sName, "zombiereloaded", false) == 0)
|
||||
g_bZRLoaded = true;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public void OnLibraryRemoved(const char[] sName)
|
||||
{
|
||||
if (strcmp(sName, "zombiereloaded", false) == 0)
|
||||
g_bZRLoaded = false;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public void OnClientPutInServer(int client)
|
||||
{
|
||||
g_hStats[client] = new ArrayList(64);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public void OnClientDisconnect(int client)
|
||||
{
|
||||
delete g_hStats[client];
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public void OnBossDamaged(CBoss Boss, CConfig Config, int client, float damage)
|
||||
{
|
||||
if (!IsValidClient(client))
|
||||
return;
|
||||
|
||||
int iMaxCash = GetConVarInt(FindConVar("sm_maxcash"));
|
||||
|
||||
if(!(GetEntProp(client, Prop_Send, "m_iAccount") >= iMaxCash))
|
||||
SetEntProp(client, Prop_Send, "m_iAccount", GetEntProp(client, Prop_Send, "m_iAccount") + 2);
|
||||
|
||||
bool bBreakable;
|
||||
CConfig _Config = view_as<CConfig>(Config);
|
||||
|
||||
if (_Config.IsBreakable)
|
||||
bBreakable = true;
|
||||
else
|
||||
bBreakable = false;
|
||||
|
||||
delete _Config;
|
||||
|
||||
for (int index = 0; index < g_hStats[client].Length; index++)
|
||||
{
|
||||
int BossDamage[2];
|
||||
g_hStats[client].GetArray(index, BossDamage, sizeof(BossDamage));
|
||||
|
||||
if (BossDamage[0] == view_as<int>(Boss))
|
||||
{
|
||||
if (bBreakable)
|
||||
BossDamage[1] += RoundToNearest(damage);
|
||||
else
|
||||
BossDamage[1] += 1;
|
||||
|
||||
g_hStats[client].SetArray(index, BossDamage, sizeof(BossDamage));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int BossDamage[2];
|
||||
BossDamage[0] = view_as<int>(Boss);
|
||||
|
||||
if (bBreakable)
|
||||
BossDamage[1] = RoundToNearest(damage);
|
||||
else
|
||||
BossDamage[1] = 1;
|
||||
|
||||
g_hStats[client].PushArray(BossDamage, sizeof(BossDamage));
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public void OnBossKilled(CBoss Boss, CConfig Config, int reason)
|
||||
{
|
||||
if (reason == 0)
|
||||
{
|
||||
for (int client = 1; client <= MaxClients; client++)
|
||||
{
|
||||
if (!IsClientInGame(client))
|
||||
continue;
|
||||
|
||||
for (int index = 0; index < g_hStats[client].Length; index++)
|
||||
{
|
||||
int BossDamage[2];
|
||||
g_hStats[client].GetArray(index, BossDamage, sizeof(BossDamage));
|
||||
|
||||
if (BossDamage[0] == view_as<int>(Boss))
|
||||
{
|
||||
g_hStats[client].Erase(index);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int iSortedList[MAXPLAYERS+1][2];
|
||||
int iSortedCount;
|
||||
|
||||
for (int client = 1; client <= MaxClients; client++)
|
||||
{
|
||||
if (!IsClientInGame(client))
|
||||
continue;
|
||||
|
||||
for (int index = 0; index < g_hStats[client].Length; index++)
|
||||
{
|
||||
int BossDamage[2];
|
||||
g_hStats[client].GetArray(index, BossDamage, sizeof(BossDamage));
|
||||
|
||||
if (BossDamage[0] == view_as<int>(Boss))
|
||||
{
|
||||
iSortedList[iSortedCount][0] = client;
|
||||
iSortedList[iSortedCount][1] = BossDamage[1];
|
||||
iSortedCount++;
|
||||
|
||||
g_hStats[client].Erase(index);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SortCustom2D(iSortedList, iSortedCount, SortBossHitsList);
|
||||
|
||||
if (iSortedCount)
|
||||
{
|
||||
CConfig _Config = view_as<CConfig>(Config);
|
||||
|
||||
char sBossName[64];
|
||||
_Config.GetName(sBossName, sizeof(sBossName));
|
||||
|
||||
char sBuffer[512];
|
||||
char sType[16];
|
||||
if (_Config.IsBreakable)
|
||||
{
|
||||
sType = "damage"
|
||||
Format(sBuffer, sizeof(sBuffer), "BOSS DAMAGE [%s]:", sBossName);
|
||||
}
|
||||
else
|
||||
{
|
||||
sType = "hits"
|
||||
Format(sBuffer, sizeof(sBuffer), "BOSS HITS [%s]:", sBossName);
|
||||
}
|
||||
delete _Config;
|
||||
|
||||
Format(sBuffer, sizeof(sBuffer), "%s\n*************************", sBuffer);
|
||||
|
||||
if (iSortedList[0][0])
|
||||
{
|
||||
Format(sBuffer, sizeof(sBuffer), "%s\n1. %N - %d %s", sBuffer, iSortedList[0][0], iSortedList[0][1], sType);
|
||||
|
||||
}
|
||||
if (iSortedList[1][0])
|
||||
{
|
||||
Format(sBuffer, sizeof(sBuffer), "%s\n2. %N - %d %s", sBuffer, iSortedList[1][0], iSortedList[1][1], sType);
|
||||
|
||||
}
|
||||
if (iSortedList[2][0])
|
||||
{
|
||||
Format(sBuffer, sizeof(sBuffer), "%s\n3. %N - %d %s", sBuffer, iSortedList[2][0], iSortedList[2][1], sType);
|
||||
|
||||
}
|
||||
|
||||
Format(sBuffer, sizeof(sBuffer), "%s\n*************************", sBuffer);
|
||||
|
||||
Handle hMessage = StartMessageAll("HudMsg");
|
||||
if (hMessage)
|
||||
{
|
||||
if (GetUserMessageType() == UM_Protobuf)
|
||||
{
|
||||
PbSetInt(hMessage, "channel", 50);
|
||||
PbSetInt(hMessage, "effect", 0);
|
||||
PbSetColor(hMessage, "clr1", {255, 255, 255, 255});
|
||||
PbSetColor(hMessage, "clr2", {255, 255, 255, 255});
|
||||
PbSetVector2D(hMessage, "pos", Float:{0.02, 0.45});
|
||||
PbSetFloat(hMessage, "fade_in_time", 0.1);
|
||||
PbSetFloat(hMessage, "fade_out_time", 0.1);
|
||||
PbSetFloat(hMessage, "hold_time", 7.0);
|
||||
PbSetFloat(hMessage, "fx_time", 0.0);
|
||||
PbSetString(hMessage, "text", sBuffer);
|
||||
EndMessage();
|
||||
}
|
||||
else
|
||||
{
|
||||
BfWriteByte(hMessage, 50);
|
||||
BfWriteFloat(hMessage, 0.02);
|
||||
BfWriteFloat(hMessage, 0.25);
|
||||
BfWriteByte(hMessage, 255);
|
||||
BfWriteByte(hMessage, 255);
|
||||
BfWriteByte(hMessage, 0);
|
||||
BfWriteByte(hMessage, 255);
|
||||
BfWriteByte(hMessage, 255);
|
||||
BfWriteByte(hMessage, 255);
|
||||
BfWriteByte(hMessage, 255);
|
||||
BfWriteByte(hMessage, 255);
|
||||
BfWriteByte(hMessage, 0);
|
||||
BfWriteFloat(hMessage, 0.1);
|
||||
BfWriteFloat(hMessage, 0.1);
|
||||
BfWriteFloat(hMessage, 7.0);
|
||||
BfWriteFloat(hMessage, 0.0);
|
||||
BfWriteString(hMessage, sBuffer);
|
||||
EndMessage();
|
||||
}
|
||||
}
|
||||
|
||||
if(GetEngineVersion() == Engine_CSGO)
|
||||
{
|
||||
int iSplits
|
||||
char sSplits[16][512];
|
||||
|
||||
if((iSplits = ExplodeString(sBuffer, "\n", sSplits, sizeof(sSplits), sizeof(sSplits[]))) != 0)
|
||||
{
|
||||
for (int iSplit; iSplit < iSplits; iSplit++)
|
||||
{
|
||||
PrintToChatAll(sSplits[iSplit]);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
PrintToChatAll(sBuffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public Action EventHook_PlayerDeath(Event hEvent, const char[] sEventName, bool bDontBroadcast)
|
||||
{
|
||||
if (!g_bZRLoaded)
|
||||
return Plugin_Continue;
|
||||
|
||||
int iClient = GetClientOfUserId(hEvent.GetInt("userid"));
|
||||
|
||||
if (!IsValidClient(iClient))
|
||||
return Plugin_Continue;
|
||||
|
||||
if (IsPlayerAlive(iClient) && !ZR_IsClientHuman(iClient))
|
||||
return Plugin_Continue;
|
||||
|
||||
int iPacked = (iClient<<16) | (GetEntProp(iClient, Prop_Send, "m_iAccount")&0xFFFF);
|
||||
|
||||
RequestFrame(RequestFrame_Callback, iPacked);
|
||||
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
void RequestFrame_Callback(int iPacked)
|
||||
{
|
||||
int iOldCash = iPacked&0xFFFF;
|
||||
int iClient = iPacked>>16;
|
||||
|
||||
if (!IsValidEntity(iClient) || !IsValidClient(iClient))
|
||||
return;
|
||||
|
||||
SetEntProp(iClient, Prop_Send, "m_iAccount", iOldCash);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public int SortBossHitsList(int[] elem1, int[] elem2, const int[][] array, Handle hndl)
|
||||
{
|
||||
if (elem1[1] > elem2[1]) return -1;
|
||||
if (elem1[1] < elem2[1]) return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
stock int IsValidClient(int client, bool nobots = true)
|
||||
{
|
||||
if (client <= 0 || client > MaxClients || !IsClientConnected(client) || (nobots && IsFakeClient(client)))
|
||||
return false;
|
||||
|
||||
return IsClientInGame(client);
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
this is used for the voice extension:
|
||||
https://git.unloze.com/UNLOZE/sm-ext-Voice/src/branch/CeltOpusTranscoder_2026
|
||||
|
||||
//the voice chat is now transcoded so both celt and opus work by sending each stream to the player that needs it.
|
||||
|
||||
the include belongs to the voice extension but is also put here extra
|
||||
@@ -0,0 +1,35 @@
|
||||
#if defined _voice_included
|
||||
#endinput
|
||||
#endif
|
||||
#define _voice_included
|
||||
|
||||
native bool IsClientTalking(int client);
|
||||
native bool SetClientNoSteam(int client, bool isNosteam);
|
||||
native bool SendCeltVoiceInit(int client);
|
||||
native bool ClientMutedOtherClient(int client, int otherclient, bool muted);
|
||||
|
||||
/**
|
||||
* Do not edit below this line!
|
||||
*/
|
||||
public Extension __ext_voice =
|
||||
{
|
||||
name = "Voice",
|
||||
file = "Voice.ext",
|
||||
#if defined AUTOLOAD_EXTENSIONS
|
||||
autoload = 1,
|
||||
#else
|
||||
autoload = 0,
|
||||
#endif
|
||||
#if defined REQUIRE_EXTENSIONS
|
||||
required = 1,
|
||||
#else
|
||||
required = 0,
|
||||
#endif
|
||||
};
|
||||
|
||||
#if !defined REQUIRE_EXTENSIONS
|
||||
public __ext_voice_SetNTVOptional()
|
||||
{
|
||||
MarkNativeAsOptional("IsClientTalking");
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,357 @@
|
||||
#include <sourcemod>
|
||||
#include <PlayerManager>
|
||||
#include <sdktools>
|
||||
#include <basecomm>
|
||||
#include <ccc>
|
||||
#include <clientprefs>
|
||||
#include <ripext>
|
||||
#tryinclude <zombiereloaded>
|
||||
#include <selfmute>
|
||||
#include <connect>
|
||||
#include <voice>
|
||||
|
||||
#pragma semicolon 1
|
||||
#pragma newdecls required
|
||||
|
||||
Handle g_hCheckMutes = null;
|
||||
Handle g_hCookieTorchMuted = null;
|
||||
|
||||
bool g_bWebclient[MAXPLAYERS + 1];
|
||||
bool g_bReRunWebClientName;
|
||||
bool g_bSkipInfection = false;
|
||||
|
||||
char g_sLastWebclientIp[64];
|
||||
|
||||
int g_iHttpFileCounter;
|
||||
|
||||
public Plugin myinfo =
|
||||
{
|
||||
name = "NoSteam CELT Voice override",
|
||||
author = "jenz",
|
||||
version = "1.5"
|
||||
};
|
||||
|
||||
public void OnPluginStart()
|
||||
{
|
||||
Format(g_sLastWebclientIp, sizeof(g_sLastWebclientIp), "");
|
||||
g_hCheckMutes = CreateTimer(2.0, check_mutes, _, TIMER_REPEAT);
|
||||
g_hCookieTorchMuted = RegClientCookie("torch_muted", "is torch muted", CookieAccess_Protected);
|
||||
}
|
||||
|
||||
public void OnPluginEnd()
|
||||
{
|
||||
if (g_hCheckMutes != null)
|
||||
{
|
||||
delete g_hCheckMutes;
|
||||
}
|
||||
}
|
||||
|
||||
public void set_nosteam_listen_override(int client)
|
||||
{
|
||||
for (int j = 1; j <= MaxClients; j++)
|
||||
{
|
||||
if (!IsValidClient(j) || client == j || IsFakeClient(j))
|
||||
continue;
|
||||
if (PM_IsPlayerSteam(j))
|
||||
{
|
||||
//the nosteam client is not allowed to hear the steam client because broadcastvoicedata would ear rape them.
|
||||
//LogMessage("%N has muted %N", client, j);
|
||||
SetListenOverride(client, j, Listen_No);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Action check_mutes(Handle timer, any data)
|
||||
{
|
||||
for (int i = 1; i <= MaxClients; i++)
|
||||
{
|
||||
if (!IsValidClient(i) || IsFakeClient(i))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (!PM_IsPlayerSteam(i))
|
||||
{
|
||||
set_nosteam_listen_override(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
//maybe this is a fix? maybe not.
|
||||
clear_steam_listen_override(i);
|
||||
}
|
||||
for (int j = 1; j <= MaxClients; j++)
|
||||
{
|
||||
if (!IsValidClient(j) || i == j || IsFakeClient(j))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
bool isMuted = BaseComm_IsClientMuted(j);
|
||||
if (!isMuted)
|
||||
{
|
||||
isMuted = SelfMute_IsIgnoring(i, j);
|
||||
}
|
||||
//PrintToChatAll("i: %N. j: %N. isMuted: %i", i, j, isMuted);
|
||||
ClientMutedOtherClient(i, j, isMuted);
|
||||
}
|
||||
if (g_bReRunWebClientName && g_bWebclient[i])
|
||||
{
|
||||
char dummy[64];
|
||||
OnClientAuthorized(i, dummy);
|
||||
g_bReRunWebClientName = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//sourceTV special case, index 0.
|
||||
for (int j = 1; j <= MaxClients; j++)
|
||||
{
|
||||
if (!IsValidClient(j) || IsFakeClient(j))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
//yeah its monkey work but server can probably handle it fine.
|
||||
char sBuffer[2];
|
||||
GetClientCookie(j, g_hCookieTorchMuted, sBuffer, sizeof(sBuffer));
|
||||
//PrintToChatAll("sBuffer: %s. j: %N", sBuffer, j);
|
||||
if(sBuffer[0] != '\0')
|
||||
{
|
||||
ClientMutedOtherClient(j, 0, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
ClientMutedOtherClient(j, 0, false);
|
||||
}
|
||||
}
|
||||
|
||||
//check if webclient has new IP every 2 seconds through endpoint.
|
||||
PollWebclientCurrentIp();
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
public void clear_steam_listen_override(int client)
|
||||
{
|
||||
for (int j = 1; j <= MaxClients; j++)
|
||||
{
|
||||
if (!IsValidClient(j) || client == j || IsFakeClient(j))
|
||||
continue;
|
||||
if (PM_IsPlayerSteam(j) && !BaseComm_IsClientMuted(j) && !SelfMute_IsIgnoring(client, j))
|
||||
{
|
||||
SetListenOverride(client, j, Listen_Default);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//AI slop
|
||||
public void SendWebclientNameToSignaling(const char[] webclientName)
|
||||
{
|
||||
char url[256];
|
||||
// Adjust port/path to match your signaling servers actual endpoint
|
||||
FormatEx(url, sizeof(url), "http://127.0.0.1:3000/webclient-name");
|
||||
|
||||
HTTPRequest hRequest = new HTTPRequest(url);
|
||||
|
||||
// Send the name as a POST body parameter
|
||||
hRequest.AppendFormParam("name", "%s", webclientName);
|
||||
hRequest.PostForm(OnWebclientNamePosted);
|
||||
}
|
||||
|
||||
public void OnWebclientNamePosted(HTTPResponse response, any value)
|
||||
{
|
||||
// Fire-and-forget: we dont care about the response. ripext closes the
|
||||
// request handle for us once this callback returns.
|
||||
}
|
||||
|
||||
public void OnMapStart()
|
||||
{
|
||||
g_bReRunWebClientName = true;
|
||||
}
|
||||
|
||||
public void OnClientDisconnect(int client)
|
||||
{
|
||||
g_bWebclient[client] = false;
|
||||
}
|
||||
|
||||
public void OnClientPostAdminCheck(int client)
|
||||
{
|
||||
g_bWebclient[client] = false;
|
||||
if (IsFakeClient(client))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
char sIP[32];
|
||||
GetClientIP(client, sIP, sizeof(sIP));
|
||||
|
||||
char allowed_ips[128];
|
||||
ConVar sv_set_steam_id_ips = FindConVar("sv_set_steam_id_ips");
|
||||
if (sv_set_steam_id_ips != null)
|
||||
{
|
||||
sv_set_steam_id_ips.GetString(allowed_ips, sizeof(allowed_ips));
|
||||
if (StrEqual(sIP, allowed_ips))
|
||||
{
|
||||
g_bWebclient[client] = true;
|
||||
g_bSkipInfection = GetConVarInt(FindConVar("zr_infect_mzombie_ratio")) > 10 ? true : false;
|
||||
|
||||
//this works fine with one webclient only existing.
|
||||
char webclientName[64];
|
||||
GetConVarString(FindConVar("sv_webclient_name"), webclientName, sizeof(webclientName));
|
||||
SetClientName(client, webclientName);
|
||||
SendWebclientNameToSignaling(webclientName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void OnClientAuthorized(int client, const char[] auth)
|
||||
{
|
||||
if (IsFakeClient(client))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SetClientNoSteam(client, false);
|
||||
if (!PM_IsPlayerSteam(client))
|
||||
{
|
||||
SetClientNoSteam(client, true);
|
||||
//LogMessage("here in onauthorized client: %N", client);
|
||||
//setting it too early does not work, so some delay needed.
|
||||
CreateTimer(10.0, Timer_SendVoiceInit, GetClientSerial(client));
|
||||
CreateTimer(20.0, Timer_SendVoiceInit, GetClientSerial(client));
|
||||
CreateTimer(30.0, Timer_SendVoiceInit, GetClientSerial(client));
|
||||
CreateTimer(40.0, Timer_SendVoiceInit, GetClientSerial(client));
|
||||
CreateTimer(50.0, Timer_SendVoiceInit, GetClientSerial(client));
|
||||
CreateTimer(60.0, Timer_SendVoiceInit, GetClientSerial(client));
|
||||
}
|
||||
}
|
||||
|
||||
public Action CCC_OnChatMessage(int client, int author, const char[] message)
|
||||
{
|
||||
if (!g_bWebclient[client])
|
||||
{
|
||||
return Plugin_Continue;
|
||||
}
|
||||
int index = FindCharInString(message, '"', false);
|
||||
int index1 = FindCharInString(message, ';', false);
|
||||
if (index != -1 || index1 != -1)
|
||||
{
|
||||
return Plugin_Handled;
|
||||
}
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
#if defined _zr_included
|
||||
public Action ZR_OnClientMotherZombieEligible(int client)
|
||||
{
|
||||
if (g_bWebclient[client] && g_bSkipInfection)
|
||||
{
|
||||
return Plugin_Handled;
|
||||
}
|
||||
return Plugin_Continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
public Action Timer_SendVoiceInit(Handle timer, int Serial)
|
||||
{
|
||||
int client;
|
||||
if ((client = GetClientFromSerial(Serial)) == 0)
|
||||
{
|
||||
return Plugin_Handled;
|
||||
}
|
||||
if (IsValidClient(client) && !PM_IsPlayerSteam(client))
|
||||
{
|
||||
SendCeltVoiceInit(client);
|
||||
}
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Polls the signaling server for the current webclient controller's real IP.
|
||||
* If the IP has changed since the last
|
||||
* check and a webclient player is currently connected, forces them to
|
||||
* reconnect with the new password via ClientCommand.
|
||||
*
|
||||
* ripext's HTTPResponse only exposes the body as decoded JSON, unlike
|
||||
* SteamWorks' GetHTTPResponseBodyCallback which handed back a plain char[].
|
||||
* Rather than change the signaling server's response format, we use
|
||||
* HTTPRequest.DownloadFile() to write the raw body to a temp file and read
|
||||
* it back as plain text, keeping the endpoint untouched.
|
||||
**/
|
||||
public void PollWebclientCurrentIp()
|
||||
{
|
||||
g_iHttpFileCounter++;
|
||||
|
||||
char sPath[PLATFORM_MAX_PATH];
|
||||
BuildPath(Path_SM, sPath, sizeof(sPath), "data/nosteamcelt_ip_%d.tmp", g_iHttpFileCounter);
|
||||
|
||||
HTTPRequest hRequest = new HTTPRequest("http://127.0.0.1:3000/webclient-current-ip");
|
||||
hRequest.DownloadFile(sPath, OnWebclientCurrentIpDownloaded, g_iHttpFileCounter);
|
||||
}
|
||||
|
||||
public void OnWebclientCurrentIpDownloaded(HTTPStatus status, any value)
|
||||
{
|
||||
char sPath[PLATFORM_MAX_PATH];
|
||||
BuildPath(Path_SM, sPath, sizeof(sPath), "data/nosteamcelt_ip_%d.tmp", value);
|
||||
|
||||
if (status != HTTPStatus_OK)
|
||||
{
|
||||
DeleteFile(sPath);
|
||||
return;
|
||||
}
|
||||
|
||||
char newIp[64];
|
||||
newIp[0] = '\0';
|
||||
|
||||
File hFile = OpenFile(sPath, "r");
|
||||
if (hFile != null)
|
||||
{
|
||||
hFile.ReadLine(newIp, sizeof(newIp));
|
||||
delete hFile;
|
||||
}
|
||||
DeleteFile(sPath);
|
||||
|
||||
TrimString(newIp);
|
||||
|
||||
if (newIp[0] == '\0')
|
||||
return;
|
||||
|
||||
if (StrEqual(newIp, g_sLastWebclientIp))
|
||||
return;
|
||||
|
||||
for (int i = 1; i <= MaxClients; i++)
|
||||
{
|
||||
if (IsValidClient(i) && g_bWebclient[i])
|
||||
{
|
||||
strcopy(g_sLastWebclientIp, sizeof(g_sLastWebclientIp), newIp);
|
||||
LogMessage("Forcing webclient reconnect for client %N with new IP: %s", i, newIp);
|
||||
ClientCommand(i, "retry");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public EConnect OnClientPreConnectEx(const char[] sName, char sPassword[255], const char[] sIP, const char[] sSteam32ID, char sRejectReason[255])
|
||||
{
|
||||
char allowedIp[64];
|
||||
ConVar sv_set_steam_id_ips = FindConVar("sv_set_steam_id_ips");
|
||||
if (sv_set_steam_id_ips != null)
|
||||
{
|
||||
sv_set_steam_id_ips.GetString(allowedIp, sizeof(allowedIp));
|
||||
//this is to handle that the connect extension receives the password parameter for the webclient as its real connecting IP.
|
||||
//works both for the client command retry and for the case that somebody presses the connect button in the clients Menu as the same password
|
||||
//is applied again.
|
||||
if (StrEqual(sIP, allowedIp) && strlen(g_sLastWebclientIp) > 0)
|
||||
{
|
||||
strcopy(sPassword, 255, g_sLastWebclientIp);
|
||||
LogMessage("Backfilled empty password for webclient with cached IP: %s", g_sLastWebclientIp);
|
||||
}
|
||||
}
|
||||
// ET_LowEvent takes the LOWEST value returned across all plugins hooking this forward.
|
||||
//this plugin returns 1 (Accept), meanwhile the reserved slot plugin still might return 0 (reject) or -1 (async) to overrule the decision from this return value.
|
||||
return k_OnClientPreConnectEx_Accept;
|
||||
}
|
||||
|
||||
stock bool IsValidClient(int client)
|
||||
{
|
||||
if (client > 0 && client <= MaxClients && IsClientConnected(client) && IsClientInGame(client))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,333 @@
|
||||
#include <sourcemod>
|
||||
#include <PlayerManager>
|
||||
#include <sdktools>
|
||||
#include <basecomm>
|
||||
#include <ccc>
|
||||
#include <clientprefs>
|
||||
#include <SteamWorks>
|
||||
#include <selfmute>
|
||||
#include <connect>
|
||||
#include <voice>
|
||||
|
||||
#pragma semicolon 1
|
||||
#pragma newdecls required
|
||||
|
||||
Handle g_hCheckMutes = null;
|
||||
Handle g_hCookieTorchMuted = null;
|
||||
|
||||
bool g_bWebclient[MAXPLAYERS + 1];
|
||||
bool g_bReRunWebClientName;
|
||||
|
||||
char g_sLastWebclientIp[64];
|
||||
|
||||
public Plugin myinfo =
|
||||
{
|
||||
name = "NoSteam CELT Voice override",
|
||||
author = "jenz",
|
||||
version = "1.5"
|
||||
};
|
||||
|
||||
public void OnPluginStart()
|
||||
{
|
||||
Format(g_sLastWebclientIp, sizeof(g_sLastWebclientIp), "");
|
||||
g_hCheckMutes = CreateTimer(2.0, check_mutes, _, TIMER_REPEAT);
|
||||
g_hCookieTorchMuted = RegClientCookie("torch_muted", "is torch muted", CookieAccess_Protected);
|
||||
}
|
||||
|
||||
public void OnPluginEnd()
|
||||
{
|
||||
if (g_hCheckMutes != null)
|
||||
{
|
||||
delete g_hCheckMutes;
|
||||
}
|
||||
}
|
||||
|
||||
public void set_nosteam_listen_override(int client)
|
||||
{
|
||||
for (int j = 1; j <= MaxClients; j++)
|
||||
{
|
||||
if (!IsValidClient(j) || client == j || IsFakeClient(j))
|
||||
continue;
|
||||
if (PM_IsPlayerSteam(j))
|
||||
{
|
||||
//the nosteam client is not allowed to hear the steam client because broadcastvoicedata would ear rape them.
|
||||
//LogMessage("%N has muted %N", client, j);
|
||||
SetListenOverride(client, j, Listen_No);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Action check_mutes(Handle timer, any data)
|
||||
{
|
||||
for (int i = 1; i <= MaxClients; i++)
|
||||
{
|
||||
if (!IsValidClient(i) || IsFakeClient(i))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (!PM_IsPlayerSteam(i))
|
||||
{
|
||||
set_nosteam_listen_override(i);
|
||||
}
|
||||
for (int j = 1; j <= MaxClients; j++)
|
||||
{
|
||||
if (!IsValidClient(j) || i == j || IsFakeClient(j))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
bool isMuted = BaseComm_IsClientMuted(j);
|
||||
if (!isMuted)
|
||||
{
|
||||
isMuted = SelfMute_IsIgnoring(i, j);
|
||||
}
|
||||
//PrintToChatAll("i: %N. j: %N. isMuted: %i", i, j, isMuted);
|
||||
ClientMutedOtherClient(i, j, isMuted);
|
||||
}
|
||||
if (g_bReRunWebClientName && g_bWebclient[i])
|
||||
{
|
||||
char dummy[64];
|
||||
OnClientAuthorized(i, dummy);
|
||||
g_bReRunWebClientName = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//sourceTV special case, index 0.
|
||||
for (int j = 1; j <= MaxClients; j++)
|
||||
{
|
||||
if (!IsValidClient(j) || IsFakeClient(j))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
//yeah its monkey work but server can probably handle it fine.
|
||||
char sBuffer[2];
|
||||
GetClientCookie(j, g_hCookieTorchMuted, sBuffer, sizeof(sBuffer));
|
||||
//PrintToChatAll("sBuffer: %s. j: %N", sBuffer, j);
|
||||
if(sBuffer[0] != '\0')
|
||||
{
|
||||
ClientMutedOtherClient(j, 0, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
ClientMutedOtherClient(j, 0, false);
|
||||
}
|
||||
}
|
||||
|
||||
//check if webclient has new IP every 2 seconds through endpoint.
|
||||
PollWebclientCurrentIp();
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
//AI slop
|
||||
public void SendWebclientNameToSignaling(const char[] webclientName)
|
||||
{
|
||||
char url[256];
|
||||
// Adjust port/path to match your signaling servers actual endpoint
|
||||
FormatEx(url, sizeof(url), "http://127.0.0.1:3000/webclient-name");
|
||||
|
||||
Handle hRequest = SteamWorks_CreateHTTPRequest(k_EHTTPMethodPOST, url);
|
||||
if (hRequest == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Send the name as a POST body parameter
|
||||
SteamWorks_SetHTTPRequestGetOrPostParameter(hRequest, "name", webclientName);
|
||||
|
||||
if (!SteamWorks_SetHTTPCallbacks(hRequest, OnWebclientNamePosted) ||
|
||||
!SteamWorks_SendHTTPRequest(hRequest))
|
||||
{
|
||||
delete hRequest;
|
||||
}
|
||||
}
|
||||
|
||||
public int OnWebclientNamePosted(Handle hRequest, bool bFailure, bool bRequestSuccessful,
|
||||
EHTTPStatusCode eStatusCode, any data)
|
||||
{
|
||||
// Fire-and-forget: we dont care about the response, just clean up.
|
||||
delete hRequest;
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void OnMapStart()
|
||||
{
|
||||
g_bReRunWebClientName = true;
|
||||
}
|
||||
|
||||
public void OnClientDisconnect(int client)
|
||||
{
|
||||
g_bWebclient[client] = false;
|
||||
}
|
||||
|
||||
public void OnClientPostAdminCheck(int client)
|
||||
{
|
||||
g_bWebclient[client] = false;
|
||||
if (IsFakeClient(client))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
char sIP[32];
|
||||
GetClientIP(client, sIP, sizeof(sIP));
|
||||
|
||||
char allowed_ips[128];
|
||||
ConVar sv_set_steam_id_ips = FindConVar("sv_set_steam_id_ips");
|
||||
if (sv_set_steam_id_ips != null)
|
||||
{
|
||||
sv_set_steam_id_ips.GetString(allowed_ips, sizeof(allowed_ips));
|
||||
if (StrEqual(sIP, allowed_ips))
|
||||
{
|
||||
g_bWebclient[client] = true;
|
||||
//this works fine with one webclient only existing.
|
||||
char webclientName[64];
|
||||
GetConVarString(FindConVar("sv_webclient_name"), webclientName, sizeof(webclientName));
|
||||
SetClientName(client, webclientName);
|
||||
SendWebclientNameToSignaling(webclientName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void OnClientAuthorized(int client, const char[] auth)
|
||||
{
|
||||
if (IsFakeClient(client))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SetClientNoSteam(client, false);
|
||||
if (!PM_IsPlayerSteam(client))
|
||||
{
|
||||
SetClientNoSteam(client, true);
|
||||
//LogMessage("here in onauthorized client: %N", client);
|
||||
//setting it too early does not work, so some delay needed.
|
||||
CreateTimer(10.0, Timer_SendVoiceInit, GetClientSerial(client));
|
||||
CreateTimer(20.0, Timer_SendVoiceInit, GetClientSerial(client));
|
||||
CreateTimer(30.0, Timer_SendVoiceInit, GetClientSerial(client));
|
||||
CreateTimer(40.0, Timer_SendVoiceInit, GetClientSerial(client));
|
||||
CreateTimer(50.0, Timer_SendVoiceInit, GetClientSerial(client));
|
||||
CreateTimer(60.0, Timer_SendVoiceInit, GetClientSerial(client));
|
||||
}
|
||||
}
|
||||
|
||||
public Action CCC_OnChatMessage(int client, int author, const char[] message)
|
||||
{
|
||||
if (!g_bWebclient[client])
|
||||
{
|
||||
return Plugin_Continue;
|
||||
}
|
||||
int index = FindCharInString(message, '"', false);
|
||||
int index1 = FindCharInString(message, ';', false);
|
||||
if (index != -1 || index1 != -1)
|
||||
{
|
||||
return Plugin_Handled;
|
||||
}
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
public Action Timer_SendVoiceInit(Handle timer, int Serial)
|
||||
{
|
||||
int client;
|
||||
if ((client = GetClientFromSerial(Serial)) == 0)
|
||||
{
|
||||
return Plugin_Handled;
|
||||
}
|
||||
if (IsValidClient(client))
|
||||
{
|
||||
SendCeltVoiceInit(client);
|
||||
}
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Polls the signaling server for the current webclient controller's real IP.
|
||||
* If the IP has changed since the last
|
||||
* check and a webclient player is currently connected, forces them to
|
||||
* reconnect with the new password via ClientCommand.
|
||||
**/
|
||||
public void PollWebclientCurrentIp()
|
||||
{
|
||||
Handle hRequest = SteamWorks_CreateHTTPRequest(k_EHTTPMethodGET, "http://127.0.0.1:3000/webclient-current-ip");
|
||||
if (hRequest == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!SteamWorks_SetHTTPCallbacks(hRequest, OnWebclientCurrentIpReceived) ||
|
||||
!SteamWorks_SendHTTPRequest(hRequest))
|
||||
{
|
||||
delete hRequest;
|
||||
}
|
||||
}
|
||||
|
||||
public int OnWebclientCurrentIpReceived(Handle hRequest, bool bFailure, bool bRequestSuccessful,
|
||||
EHTTPStatusCode eStatusCode, any data)
|
||||
{
|
||||
if (bFailure || !bRequestSuccessful || eStatusCode != k_EHTTPStatusCode200OK)
|
||||
{
|
||||
delete hRequest;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bodySize;
|
||||
bool gotSize = SteamWorks_GetHTTPResponseBodySize(hRequest, bodySize);
|
||||
|
||||
char newIp[64];
|
||||
newIp[0] = '\0';
|
||||
|
||||
if (gotSize && bodySize > 0 && bodySize < sizeof(newIp))
|
||||
{
|
||||
SteamWorks_GetHTTPResponseBodyData(hRequest, newIp, bodySize);
|
||||
}
|
||||
|
||||
delete hRequest;
|
||||
|
||||
if (newIp[0] == '\0')
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (StrEqual(newIp, g_sLastWebclientIp))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (int i = 1; i <= MaxClients; i++)
|
||||
{
|
||||
if (IsValidClient(i) && g_bWebclient[i])
|
||||
{
|
||||
strcopy(g_sLastWebclientIp, sizeof(g_sLastWebclientIp), newIp);
|
||||
LogMessage("Forcing webclient reconnect for client %N with new IP: %s", i, newIp);
|
||||
ClientCommand(i, "retry");
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public EConnect OnClientPreConnectEx(const char[] sName, char sPassword[255], const char[] sIP, const char[] sSteam32ID, char sRejectReason[255])
|
||||
{
|
||||
char allowedIp[64];
|
||||
ConVar sv_set_steam_id_ips = FindConVar("sv_set_steam_id_ips");
|
||||
if (sv_set_steam_id_ips != null)
|
||||
{
|
||||
sv_set_steam_id_ips.GetString(allowedIp, sizeof(allowedIp));
|
||||
//this is to handle that the connect extension receives the password parameter for the webclient as its real connecting IP.
|
||||
//works both for the client command retry and for the case that somebody presses the connect button in the clients Menu as the same password
|
||||
//is applied again.
|
||||
if (StrEqual(sIP, allowedIp) && strlen(g_sLastWebclientIp) > 0)
|
||||
{
|
||||
strcopy(sPassword, 255, g_sLastWebclientIp);
|
||||
LogMessage("Backfilled empty password for webclient with cached IP: %s", g_sLastWebclientIp);
|
||||
}
|
||||
}
|
||||
// ET_LowEvent takes the LOWEST value returned across all plugins hooking this forward.
|
||||
//this plugin returns 1 (Accept), meanwhile the reserved slot plugin still might return 0 (reject) or -1 (async) to overrule the decision from this return value.
|
||||
return k_OnClientPreConnectEx_Accept;
|
||||
}
|
||||
|
||||
stock bool IsValidClient(int client)
|
||||
{
|
||||
if (client > 0 && client <= MaxClients && IsClientConnected(client) && IsClientInGame(client))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{BLUE}{PLAYERTIER} {WHITE}{PLAYERTYPE} {GREEN}{NAME} {green}[{lightgreen}{STEAMID}{NOSTEAM}{green}] connected{COUNTRY}
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <multicolors>
|
||||
#include <clientprefs>
|
||||
#include <PlayerManager>
|
||||
#include <unloze_playtime>
|
||||
|
||||
#pragma newdecls required
|
||||
|
||||
@@ -16,6 +17,8 @@ Database g_hDatabase;
|
||||
Handle g_hCustomMessageFile;
|
||||
Handle g_hCustomMessageFile2;
|
||||
|
||||
static Handle g_hForwardConnectMessage;
|
||||
|
||||
bool g_bHideCsays[MAXPLAYERS + 1] = { false, ... };
|
||||
Handle g_hCookieHideCsays = null;
|
||||
|
||||
@@ -71,6 +74,17 @@ public void OnDatabaseConnect(Database db, const char[] error, any data)
|
||||
g_hDatabase = db;
|
||||
}
|
||||
|
||||
public APLRes AskPluginLoad2(Handle myself, bool late, char [] error, int err_max)
|
||||
{
|
||||
g_hForwardConnectMessage = CreateGlobalForward("GetPlayerConnectMessage", ET_Ignore, Param_String);
|
||||
return APLRes_Success;
|
||||
}
|
||||
|
||||
public void OnPluginEnd()
|
||||
{
|
||||
CloseHandle(g_hForwardConnectMessage);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
@@ -175,6 +189,7 @@ public int MenuHandler_MainMenu(Menu menu, MenuAction action, int client, int se
|
||||
delete menu;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
@@ -290,6 +305,189 @@ public Action Command_ResetJoinMsg(int client, int args)
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
|
||||
public void GetPlayerTier(int client, int tier)
|
||||
{
|
||||
Handle hFile = OpenFile(g_sDataFile, "r");
|
||||
static char sRawMsg[301];
|
||||
|
||||
if(hFile != INVALID_HANDLE)
|
||||
{
|
||||
ReadFileLine(hFile, sRawMsg, sizeof(sRawMsg));
|
||||
TrimString(sRawMsg);
|
||||
delete hFile;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogError("[SM] File not found! (configs/ConnectAnnounce/settings.txt)");
|
||||
return;
|
||||
}
|
||||
|
||||
static char sIP[16];
|
||||
static char sAuth[32];
|
||||
static char sCountry[32];
|
||||
static char sName[128];
|
||||
|
||||
GetClientAuthId(client, AuthId_Steam2, sAuth, sizeof(sAuth));
|
||||
GetClientName(client, sName, sizeof(sName));
|
||||
AdminId aid;
|
||||
|
||||
if(StrContains(sRawMsg, "{PLAYERTIER}"))
|
||||
{
|
||||
char s_tier[64];
|
||||
Format(s_tier, sizeof(s_tier), "Tier %i", tier);
|
||||
ReplaceString(sRawMsg, sizeof(sRawMsg), "{PLAYERTIER}", s_tier);
|
||||
}
|
||||
|
||||
if(StrContains(sRawMsg, "{PLAYERTYPE}"))
|
||||
{
|
||||
aid = GetUserAdmin(client);
|
||||
|
||||
if(GetAdminFlag(aid, Admin_Cheats) || GetAdminFlag(aid, Admin_Root))
|
||||
{
|
||||
ReplaceString(sRawMsg, sizeof(sRawMsg), "{PLAYERTYPE}", "Root-Admin");
|
||||
}
|
||||
else if(GetAdminFlag(aid, Admin_Generic))
|
||||
{
|
||||
ReplaceString(sRawMsg, sizeof(sRawMsg), "{PLAYERTYPE}", "Admin");
|
||||
}
|
||||
else if(GetAdminFlag(aid, Admin_Custom1))
|
||||
{
|
||||
ReplaceString(sRawMsg, sizeof(sRawMsg), "{PLAYERTYPE}", "VIP");
|
||||
}
|
||||
/*
|
||||
else if(GetAdminFlag(aid, Admin_Custom6))
|
||||
{
|
||||
ReplaceString(sRawMsg, sizeof(sRawMsg), "{PLAYERTYPE}", "Member");
|
||||
}
|
||||
*/
|
||||
else
|
||||
{
|
||||
ReplaceString(sRawMsg, sizeof(sRawMsg), "{PLAYERTYPE}", "Player");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
if(StrContains(sRawMsg, "{NOSTEAM}"))
|
||||
{
|
||||
if(!PM_IsPlayerSteam(client))
|
||||
ReplaceString(sRawMsg, sizeof(sRawMsg), "{NOSTEAM}", " <NoSteam>");
|
||||
else
|
||||
ReplaceString(sRawMsg, sizeof(sRawMsg), "{NOSTEAM}", "");
|
||||
}
|
||||
|
||||
if(StrContains(sRawMsg, "{STEAMID}"))
|
||||
{
|
||||
ReplaceString(sRawMsg, sizeof(sRawMsg), "{STEAMID}", sAuth);
|
||||
}
|
||||
|
||||
if(StrContains(sRawMsg, "{NAME}"))
|
||||
{
|
||||
ReplaceString(sRawMsg, sizeof(sRawMsg), "{NAME}", sName);
|
||||
}
|
||||
|
||||
if(StrContains(sRawMsg, "{COUNTRY}"))
|
||||
{
|
||||
if(GetClientIP(client, sIP, sizeof(sIP)) && GeoipCountry(sIP, sCountry, sizeof(sCountry)))
|
||||
{
|
||||
char sBuffer[128];
|
||||
Format(sBuffer, sizeof(sBuffer), " from %s", sCountry);
|
||||
ReplaceString(sRawMsg, sizeof(sRawMsg), "{COUNTRY}", sBuffer);
|
||||
}
|
||||
else
|
||||
ReplaceString(sRawMsg, sizeof(sRawMsg), "{COUNTRY}", "");
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
if (!CheckCommandAccess(client, "sm_joinmsg", ADMFLAG_CUSTOM1))
|
||||
{
|
||||
CPrintToChatAll(sRawMsg);
|
||||
Call_StartForward(g_hForwardConnectMessage);
|
||||
Call_PushString(sRawMsg);
|
||||
Call_Finish();
|
||||
return;
|
||||
}
|
||||
|
||||
delete g_hCustomMessageFile2;
|
||||
|
||||
g_hCustomMessageFile2 = CreateKeyValues("custom_messages");
|
||||
|
||||
if (!FileToKeyValues(g_hCustomMessageFile2, g_sCustomMessageFile))
|
||||
{
|
||||
SetFailState("[ConnectAnnounce] Config file missing!");
|
||||
return;
|
||||
}
|
||||
|
||||
KvRewind(g_hCustomMessageFile2);
|
||||
|
||||
char sBanned[16];
|
||||
char sFinalMessage[512];
|
||||
char sCustomMessage[256];
|
||||
|
||||
if (KvJumpToKey(g_hCustomMessageFile2, sAuth))
|
||||
{
|
||||
KvGetString(g_hCustomMessageFile2, "banned", sBanned, sizeof(sBanned), "");
|
||||
KvGetString(g_hCustomMessageFile2, "message", sCustomMessage, sizeof(sCustomMessage), "");
|
||||
if(StrEqual(sCustomMessage, "reset") || StrEqual(sBanned, "true"))
|
||||
{
|
||||
CPrintToChatAll(sRawMsg);
|
||||
Call_StartForward(g_hForwardConnectMessage);
|
||||
Call_PushString(sRawMsg);
|
||||
Call_Finish();
|
||||
|
||||
for(int i = 1; i <= MaxClients; i++)
|
||||
{
|
||||
if(IsClientInGame(i))
|
||||
{
|
||||
if(g_bHideCsays[i])
|
||||
continue;
|
||||
|
||||
PrintCenterText(i, "%s %s %s", "[VIP]", sName, "connected");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Format(sFinalMessage, sizeof(sFinalMessage), "%s %s", sRawMsg, sCustomMessage);
|
||||
CPrintToChatAll(sFinalMessage);
|
||||
Call_StartForward(g_hForwardConnectMessage);
|
||||
Call_PushString(sRawMsg);
|
||||
Call_Finish();
|
||||
|
||||
for(int i = 1; i <= MaxClients; i++)
|
||||
{
|
||||
if(IsClientInGame(i))
|
||||
{
|
||||
if(g_bHideCsays[i])
|
||||
continue;
|
||||
|
||||
CRemoveTags(sCustomMessage, 256);
|
||||
PrintCenterText(i, "%s %s %s %s", "[VIP]", sName, "connected", sCustomMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CPrintToChatAll(sRawMsg);
|
||||
Call_StartForward(g_hForwardConnectMessage);
|
||||
Call_PushString(sRawMsg);
|
||||
Call_Finish();
|
||||
for(int i = 1; i <= MaxClients; i++)
|
||||
{
|
||||
if(IsClientInGame(i))
|
||||
{
|
||||
if(g_bHideCsays[i])
|
||||
continue;
|
||||
|
||||
PrintCenterText(i, "%s %s %s", "[VIP]", sName, "connected");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void TQueryCB(Handle owner, Handle rs, const char[] error, any data)
|
||||
{
|
||||
int client = 0;
|
||||
@@ -338,7 +536,6 @@ public void TQueryCB(Handle owner, Handle rs, const char[] error, any data)
|
||||
|
||||
if(StrContains(sRawMsg, "{PLAYERTYPE}"))
|
||||
{
|
||||
|
||||
aid = GetUserAdmin(client);
|
||||
|
||||
if(GetAdminFlag(aid, Admin_Generic))
|
||||
@@ -492,7 +689,9 @@ public void OnClientPostAdminCheck(int client)
|
||||
char sServer[16];
|
||||
g_cvHlstatsServerName.GetString(sServer, sizeof(sServer));
|
||||
|
||||
/*
|
||||
char sQuery[512];
|
||||
Format(sQuery, sizeof(sQuery), "SELECT COUNT(*) AS 'rank' FROM hlstats_Players WHERE hlstats_Players.game = '%s' AND hideranking = 0 AND skill > (SELECT skill from hlstats_Players JOIN hlstats_PlayerUniqueIds ON hlstats_Players.playerId = hlstats_PlayerUniqueIds.playerId WHERE uniqueId = '%s' AND hlstats_PlayerUniqueIds.game = '%s')", sServer, sAuth, sServer);
|
||||
g_hDatabase.Query(TQueryCB, sQuery, GetClientUserId(client), DBPrio_High);
|
||||
*/
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
#if defined _connect_announce_included_
|
||||
#endinput
|
||||
#endif
|
||||
#define _connect_announce_included_
|
||||
|
||||
forward void GetPlayerConnectMessage(char[] message);
|
||||
@@ -0,0 +1,41 @@
|
||||
"Games"
|
||||
{
|
||||
"cstrike"
|
||||
{
|
||||
"Offsets"
|
||||
{
|
||||
// CBaseEntity::ForceVPhysicsCollide(CBaseEntity *pEntity). Same offsets as PropSpawner.
|
||||
"ForceVPhysicsCollide"
|
||||
{
|
||||
"linux" "160"
|
||||
"windows" "159"
|
||||
}
|
||||
|
||||
// CBasePlayer::PlayerUse(). Same offsets as PropSpawner.
|
||||
"PlayerUse"
|
||||
{
|
||||
"linux" "414"
|
||||
"windows" "413"
|
||||
}
|
||||
}
|
||||
|
||||
"Signatures"
|
||||
{
|
||||
// CCollisionEvent::ShouldCollide(IPhysicsObject *, IPhysicsObject *, void *, void *).
|
||||
// The vphysics collision filter, detoured for couch bans. Local symbol from .symtab,
|
||||
// so the server binary must be unstripped (as for LagCompensation).
|
||||
"CCollisionEvent::ShouldCollide"
|
||||
{
|
||||
"library" "server"
|
||||
"linux" "@_ZN15CCollisionEvent13ShouldCollideEP14IPhysicsObjectS1_PvS2_"
|
||||
}
|
||||
|
||||
// CBaseEntity::CollisionRulesChanged(). Makes vphysics re-ask ShouldCollide for cached pairs.
|
||||
"CBaseEntity::CollisionRulesChanged"
|
||||
{
|
||||
"library" "server"
|
||||
"linux" "@_ZN11CBaseEntity21CollisionRulesChangedEv"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,635 @@
|
||||
#include <sourcemod>
|
||||
#include <Couch>
|
||||
|
||||
#pragma semicolon 1
|
||||
#pragma newdecls required
|
||||
|
||||
#define PREFIX "\x01[SM] \x04"
|
||||
|
||||
// A player who keeps moving the couch the same way is reported at most once per this many seconds.
|
||||
#define LOG_REPEAT_INTERVAL 2.0
|
||||
|
||||
// How much each press in the push scale menu changes sm_couch_push_scale.
|
||||
#define PUSH_SCALE_STEP 0.25
|
||||
|
||||
// So commands fail with a message instead of an unbound-native error while the core is unloaded.
|
||||
bool g_bCoreLoaded;
|
||||
|
||||
// Admins who asked to see couch interactions. Per slot, so it survives map changes.
|
||||
bool g_bLogging[MAXPLAYERS + 1];
|
||||
|
||||
// Per player: what was last reported, and when the same thing may be reported again.
|
||||
CouchMethod g_iLastMethod[MAXPLAYERS + 1];
|
||||
char g_sLastWeapon[MAXPLAYERS + 1][64];
|
||||
float g_fNextReport[MAXPLAYERS + 1];
|
||||
|
||||
public Plugin myinfo =
|
||||
{
|
||||
name = "Couch Admin",
|
||||
author = "marisakirisame.net",
|
||||
description = "Admin commands and menu for the ZE couch",
|
||||
version = "2.0",
|
||||
url = "https://marisakirisame.net"
|
||||
};
|
||||
|
||||
public void OnPluginStart()
|
||||
{
|
||||
RegAdminCmd("sm_couch", Command_Couch, ADMFLAG_CHANGEMAP, "Opens the couch menu");
|
||||
RegAdminCmd("sm_spawncouch", Command_SpawnCouch, ADMFLAG_CHANGEMAP, "Spawns the couch where you are aiming");
|
||||
RegAdminCmd("sm_bringcouch", Command_BringCouch, ADMFLAG_CHANGEMAP, "Brings the couch to where you are aiming");
|
||||
RegAdminCmd("sm_gotocouch", Command_GotoCouch, ADMFLAG_CHANGEMAP, "Teleports you to the couch");
|
||||
RegAdminCmd("sm_removecouch", Command_RemoveCouch, ADMFLAG_CHANGEMAP, "Removes the couch");
|
||||
RegAdminCmd("sm_couchpistolonly", Command_PistolOnly, ADMFLAG_CHANGEMAP, "sm_couchpistolonly [0|1] - only pistols and knives move the couch; no argument toggles");
|
||||
RegAdminCmd("sm_couchlog", Command_Log, ADMFLAG_CHANGEMAP, "sm_couchlog [0|1] - show who moves the couch and how in your console, grenades in chat too; no argument toggles");
|
||||
}
|
||||
|
||||
// Game time restarts with the map.
|
||||
public void OnMapStart()
|
||||
{
|
||||
for (int i = 1; i <= MAXPLAYERS; i++)
|
||||
g_fNextReport[i] = 0.0;
|
||||
}
|
||||
|
||||
public void OnClientDisconnect(int client)
|
||||
{
|
||||
g_bLogging[client] = false;
|
||||
g_fNextReport[client] = 0.0;
|
||||
}
|
||||
|
||||
public void OnAllPluginsLoaded()
|
||||
{
|
||||
g_bCoreLoaded = LibraryExists("Couch");
|
||||
}
|
||||
|
||||
public void OnLibraryAdded(const char[] name)
|
||||
{
|
||||
if (StrEqual(name, "Couch"))
|
||||
g_bCoreLoaded = true;
|
||||
}
|
||||
|
||||
public void OnLibraryRemoved(const char[] name)
|
||||
{
|
||||
if (StrEqual(name, "Couch"))
|
||||
g_bCoreLoaded = false;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
// Commands
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
|
||||
public Action Command_Couch(int client, int args)
|
||||
{
|
||||
if (!RequireInGame(client) || !RequireCore(client))
|
||||
return Plugin_Handled;
|
||||
|
||||
ShowCouchMenu(client);
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
public Action Command_SpawnCouch(int client, int args)
|
||||
{
|
||||
if (!RequireInGame(client) || !RequireCore(client))
|
||||
return Plugin_Handled;
|
||||
|
||||
DoSpawn(client);
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
public Action Command_BringCouch(int client, int args)
|
||||
{
|
||||
if (!RequireInGame(client) || !RequireCore(client))
|
||||
return Plugin_Handled;
|
||||
|
||||
DoBring(client);
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
public Action Command_GotoCouch(int client, int args)
|
||||
{
|
||||
if (!RequireInGame(client) || !RequireCore(client))
|
||||
return Plugin_Handled;
|
||||
|
||||
DoGoto(client);
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
// Needs no position, so it also works from the server console.
|
||||
public Action Command_RemoveCouch(int client, int args)
|
||||
{
|
||||
if (!RequireCore(client))
|
||||
return Plugin_Handled;
|
||||
|
||||
DoRemove(client);
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
public Action Command_PistolOnly(int client, int args)
|
||||
{
|
||||
if (!RequireCore(client))
|
||||
return Plugin_Handled;
|
||||
|
||||
bool bEnable = !Couch_IsPistolOnly();
|
||||
if (args >= 1)
|
||||
bEnable = (GetCmdArgInt(1) != 0);
|
||||
|
||||
DoSetPistolOnly(client, bEnable);
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
public Action Command_Log(int client, int args)
|
||||
{
|
||||
if (!RequireInGame(client))
|
||||
return Plugin_Handled;
|
||||
|
||||
bool bEnable = !g_bLogging[client];
|
||||
if (args >= 1)
|
||||
bEnable = (GetCmdArgInt(1) != 0);
|
||||
|
||||
DoSetLogging(client, bEnable);
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
bool RequireInGame(int client)
|
||||
{
|
||||
if (client >= 1 && IsClientInGame(client))
|
||||
return true;
|
||||
|
||||
ReplyToCommand(client, "[SM] This command can only be used in-game.");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool RequireCore(int client)
|
||||
{
|
||||
if (g_bCoreLoaded)
|
||||
return true;
|
||||
|
||||
ReplyToCommand(client, "[SM] The Couch core plugin is not loaded.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
// Actions
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
|
||||
void DoSpawn(int client)
|
||||
{
|
||||
if (Couch_Spawn(client) == -1)
|
||||
{
|
||||
ReplyToCommand(client, "[SM] Could not create the couch.");
|
||||
return;
|
||||
}
|
||||
|
||||
ShowActivity2(client, PREFIX, "\x01Spawned the \x04couch\x01, it will respawn here each round until removed.");
|
||||
LogAction(client, -1, "\"%L\" spawned the couch.", client);
|
||||
}
|
||||
|
||||
void DoBring(int client)
|
||||
{
|
||||
if (!Couch_Bring(client))
|
||||
{
|
||||
ReplyToCommand(client, "[SM] There is no couch to bring. Use sm_spawncouch first.");
|
||||
return;
|
||||
}
|
||||
|
||||
ShowActivity2(client, PREFIX, "\x01Brought the \x04couch\x01.");
|
||||
LogAction(client, -1, "\"%L\" brought the couch.", client);
|
||||
}
|
||||
|
||||
void DoGoto(int client)
|
||||
{
|
||||
if (!IsPlayerAlive(client))
|
||||
{
|
||||
ReplyToCommand(client, "[SM] You must be alive to go to the couch.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Couch_Goto(client))
|
||||
{
|
||||
ReplyToCommand(client, "[SM] There is no couch to go to. Use sm_spawncouch first.");
|
||||
return;
|
||||
}
|
||||
|
||||
ShowActivity2(client, PREFIX, "\x01Teleported to the \x04couch\x01.");
|
||||
LogAction(client, -1, "\"%L\" teleported to the couch.", client);
|
||||
}
|
||||
|
||||
void DoRemove(int client)
|
||||
{
|
||||
if (!Couch_Remove())
|
||||
{
|
||||
ReplyToCommand(client, "[SM] There is no couch to remove.");
|
||||
return;
|
||||
}
|
||||
|
||||
ShowActivity2(client, PREFIX, "\x01Removed the \x04couch\x01.");
|
||||
LogAction(client, -1, "\"%L\" removed the couch.", client);
|
||||
}
|
||||
|
||||
void DoToggleEffect(int client)
|
||||
{
|
||||
bool bEnable = !Couch_IsEffectEnabled();
|
||||
Couch_SetEffectEnabled(bEnable);
|
||||
|
||||
ShowActivity2(client, PREFIX, "\x01Turned the couch effect \x04%s\x01.", bEnable ? "on" : "off");
|
||||
LogAction(client, -1, "\"%L\" turned the couch effect %s.", client, bEnable ? "on" : "off");
|
||||
}
|
||||
|
||||
// Announced to everyone so players know why their rifle stopped moving the couch.
|
||||
void DoSetPistolOnly(int client, bool bEnable)
|
||||
{
|
||||
Couch_SetPistolOnly(bEnable);
|
||||
|
||||
ShowActivity2(client, PREFIX, "\x01Turned couch pistol-only mode \x04%s\x01.", bEnable ? "on" : "off");
|
||||
LogAction(client, -1, "\"%L\" turned couch pistol-only mode %s.", client, bEnable ? "on" : "off");
|
||||
}
|
||||
|
||||
// Announced to everyone so players know to shoot the couch instead.
|
||||
void DoToggleUse(int client)
|
||||
{
|
||||
bool bAllow = !Couch_IsUseAllowed();
|
||||
Couch_SetUseAllowed(bAllow);
|
||||
|
||||
if (bAllow)
|
||||
ShowActivity2(client, PREFIX, "\x01Allowed pushing the \x04couch\x01 with USE.");
|
||||
else
|
||||
ShowActivity2(client, PREFIX, "\x01Disabled pushing the \x04couch\x01 with USE, it must be shot to move.");
|
||||
|
||||
LogAction(client, -1, "\"%L\" %s couch use.", client, bAllow ? "allowed" : "disallowed");
|
||||
}
|
||||
|
||||
// The tier cvars belong to the core plugin, so they are looked up by name. sName is "move" or "button".
|
||||
void DoChangeTier(int client, const char[] sName, int iDelta)
|
||||
{
|
||||
ConVar cvar = FindTierConVar(sName);
|
||||
if (cvar == null)
|
||||
return;
|
||||
|
||||
int iTier = cvar.IntValue + iDelta;
|
||||
if (iTier < 0)
|
||||
iTier = 0;
|
||||
|
||||
cvar.IntValue = iTier;
|
||||
|
||||
if (iTier == 0)
|
||||
{
|
||||
ShowActivity2(client, PREFIX, "\x01Turned off the couch \x04%s\x01 tier requirement.", sName);
|
||||
LogAction(client, -1, "\"%L\" turned off the couch %s tier requirement.", client, sName);
|
||||
}
|
||||
else
|
||||
{
|
||||
ShowActivity2(client, PREFIX, "\x01Set the couch \x04%s\x01 tier requirement to \x04%d\x01.", sName, iTier);
|
||||
LogAction(client, -1, "\"%L\" set the couch %s tier requirement to %d.", client, sName, iTier);
|
||||
}
|
||||
}
|
||||
|
||||
ConVar FindTierConVar(const char[] sName)
|
||||
{
|
||||
char sCvar[32];
|
||||
FormatEx(sCvar, sizeof(sCvar), "sm_couch_%s_tier", sName);
|
||||
return FindConVar(sCvar);
|
||||
}
|
||||
|
||||
// Also a core plugin cvar. Rounded to the step so repeated presses don't drift, and read back
|
||||
// afterwards so the announcement shows the value after the cvar's own bounds have clamped it.
|
||||
void DoSetPushScale(int client, float fScale)
|
||||
{
|
||||
ConVar cvar = FindConVar("sm_couch_push_scale");
|
||||
if (cvar == null)
|
||||
return;
|
||||
|
||||
fScale = RoundToNearest(fScale / PUSH_SCALE_STEP) * PUSH_SCALE_STEP;
|
||||
if (fScale < 0.0)
|
||||
fScale = 0.0;
|
||||
|
||||
cvar.FloatValue = fScale;
|
||||
fScale = cvar.FloatValue;
|
||||
|
||||
ShowActivity2(client, PREFIX, "\x01Set the couch \x04push scale\x01 to \x04%.2fx\x01.", fScale);
|
||||
LogAction(client, -1, "\"%L\" set the couch push scale to %.2f.", client, fScale);
|
||||
}
|
||||
|
||||
// Only affects what this admin sees, so it is neither announced nor logged.
|
||||
void DoSetLogging(int client, bool bEnable)
|
||||
{
|
||||
g_bLogging[client] = bEnable;
|
||||
|
||||
if (bEnable)
|
||||
PrintToChat(client, PREFIX ... "\x01Couch log \x04on\x01, see your console for who moves the couch and how. Grenades also show here.");
|
||||
else
|
||||
PrintToChat(client, PREFIX ... "\x01Couch log \x04off\x01.");
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
// Couch log
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
|
||||
public void Couch_OnCouchInteraction(int couch, int client, CouchMethod method, const char[] weapon)
|
||||
{
|
||||
if (!AnyoneLogging())
|
||||
return;
|
||||
|
||||
// Held fire and shotgun pellets would flood the console; a change of weapon or method is always shown.
|
||||
float fNow = GetGameTime();
|
||||
if (fNow < g_fNextReport[client] && method == g_iLastMethod[client] && StrEqual(weapon, g_sLastWeapon[client]))
|
||||
return;
|
||||
|
||||
g_iLastMethod[client] = method;
|
||||
strcopy(g_sLastWeapon[client], sizeof(g_sLastWeapon[]), weapon);
|
||||
g_fNextReport[client] = fNow + LOG_REPEAT_INTERVAL;
|
||||
|
||||
// Plain text: the console doesn't render chat colour codes.
|
||||
char sHow[96];
|
||||
switch (method)
|
||||
{
|
||||
case CouchMethod_Bullet:
|
||||
{
|
||||
if (weapon[0] != '\0')
|
||||
FormatEx(sHow, sizeof(sHow), "shot the couch with %s", weapon);
|
||||
else
|
||||
strcopy(sHow, sizeof(sHow), "shot the couch");
|
||||
}
|
||||
case CouchMethod_Knife: strcopy(sHow, sizeof(sHow), "knifed the couch");
|
||||
case CouchMethod_Grenade: strcopy(sHow, sizeof(sHow), "hit the couch with a grenade");
|
||||
case CouchMethod_Use: strcopy(sHow, sizeof(sHow), "pushed the couch with USE");
|
||||
default: FormatEx(sHow, sizeof(sHow), "moved the couch with %s", weapon[0] != '\0' ? weapon : "something");
|
||||
}
|
||||
|
||||
// Everything goes to the console; grenades are rare and knock the couch far, so they go to chat too.
|
||||
// The userid is there so it can go straight into sm_couchban.
|
||||
int userid = GetClientUserId(client);
|
||||
for (int i = 1; i <= MaxClients; i++)
|
||||
{
|
||||
if (!g_bLogging[i] || !IsClientInGame(i))
|
||||
continue;
|
||||
|
||||
PrintToConsole(i, "[Couch] %N (#%d) %s", client, userid, sHow);
|
||||
|
||||
if (method == CouchMethod_Grenade)
|
||||
PrintToChat(i, "\x01[Couch] \x04%N\x01 (#%d) hit the couch with a \x04grenade", client, userid);
|
||||
}
|
||||
}
|
||||
|
||||
bool AnyoneLogging()
|
||||
{
|
||||
for (int i = 1; i <= MaxClients; i++)
|
||||
{
|
||||
if (g_bLogging[i])
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
// Menu
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
|
||||
// Rebuilt on every display so the labels always reflect the current state.
|
||||
void ShowCouchMenu(int client)
|
||||
{
|
||||
bool bHasCouch = (Couch_GetCouch() != -1);
|
||||
bool bPlaced = Couch_IsPlaced();
|
||||
|
||||
Menu menu = new Menu(MenuHandler_Couch);
|
||||
menu.SetTitle("Couch");
|
||||
|
||||
// With a couch out, bring is the way to move it; without one there is nothing to bring.
|
||||
menu.AddItem("spawn", "Spawn couch at crosshair", bHasCouch ? ITEMDRAW_DISABLED : ITEMDRAW_DEFAULT);
|
||||
menu.AddItem("bring", "Bring couch to crosshair", bHasCouch ? ITEMDRAW_DEFAULT : ITEMDRAW_DISABLED);
|
||||
menu.AddItem("goto", "Teleport to couch", bHasCouch ? ITEMDRAW_DEFAULT : ITEMDRAW_DISABLED);
|
||||
|
||||
if (bHasCouch)
|
||||
menu.AddItem("remove", "Remove couch");
|
||||
else if (bPlaced)
|
||||
menu.AddItem("remove", "Remove couch (respawns next round)");
|
||||
else
|
||||
menu.AddItem("remove", "Remove couch", ITEMDRAW_DISABLED);
|
||||
|
||||
char sItem[64];
|
||||
FormatEx(sItem, sizeof(sItem), "Effect: %s", Couch_IsEffectEnabled() ? "ON" : "OFF");
|
||||
menu.AddItem("effect", sItem);
|
||||
|
||||
FormatEx(sItem, sizeof(sItem), "Pistol only: %s", Couch_IsPistolOnly() ? "ON" : "OFF");
|
||||
menu.AddItem("pistol", sItem);
|
||||
|
||||
FormatEx(sItem, sizeof(sItem), "Allow USE: %s", Couch_IsUseAllowed() ? "ON" : "OFF");
|
||||
menu.AddItem("use", sItem);
|
||||
|
||||
FormatEx(sItem, sizeof(sItem), "Couch logs (for me): %s", g_bLogging[client] ? "ON" : "OFF");
|
||||
menu.AddItem("log", sItem);
|
||||
|
||||
menu.AddItem("tiers", "Tier requirements");
|
||||
menu.AddItem("pushscale", "Push scale");
|
||||
|
||||
menu.ExitButton = true;
|
||||
menu.Display(client, MENU_TIME_FOREVER);
|
||||
}
|
||||
|
||||
public int MenuHandler_Couch(Menu menu, MenuAction action, int param1, int param2)
|
||||
{
|
||||
switch (action)
|
||||
{
|
||||
case MenuAction_Select:
|
||||
{
|
||||
int client = param1;
|
||||
|
||||
if (!g_bCoreLoaded)
|
||||
{
|
||||
RequireCore(client);
|
||||
return 0;
|
||||
}
|
||||
|
||||
char sInfo[16];
|
||||
menu.GetItem(param2, sInfo, sizeof(sInfo));
|
||||
|
||||
if (StrEqual(sInfo, "spawn"))
|
||||
DoSpawn(client);
|
||||
else if (StrEqual(sInfo, "bring"))
|
||||
DoBring(client);
|
||||
else if (StrEqual(sInfo, "goto"))
|
||||
DoGoto(client);
|
||||
else if (StrEqual(sInfo, "remove"))
|
||||
DoRemove(client);
|
||||
else if (StrEqual(sInfo, "effect"))
|
||||
DoToggleEffect(client);
|
||||
else if (StrEqual(sInfo, "pistol"))
|
||||
DoSetPistolOnly(client, !Couch_IsPistolOnly());
|
||||
else if (StrEqual(sInfo, "use"))
|
||||
DoToggleUse(client);
|
||||
else if (StrEqual(sInfo, "log"))
|
||||
DoSetLogging(client, !g_bLogging[client]);
|
||||
else if (StrEqual(sInfo, "tiers"))
|
||||
{
|
||||
ShowTierMenu(client);
|
||||
return 0;
|
||||
}
|
||||
else if (StrEqual(sInfo, "pushscale"))
|
||||
{
|
||||
ShowPushScaleMenu(client);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ShowCouchMenu(client);
|
||||
}
|
||||
|
||||
case MenuAction_End:
|
||||
{
|
||||
delete menu;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ShowTierMenu(int client)
|
||||
{
|
||||
Menu menu = new Menu(MenuHandler_Tier);
|
||||
menu.SetTitle("Couch - Tier requirements");
|
||||
|
||||
AddTierItems(menu, "move", "Move");
|
||||
AddTierItems(menu, "button", "Button");
|
||||
|
||||
menu.ExitBackButton = true;
|
||||
menu.ExitButton = true;
|
||||
menu.Display(client, MENU_TIME_FOREVER);
|
||||
}
|
||||
|
||||
// Adds "<label> tier: <value>", increase and decrease. Info strings are "+move", "-move" and so on.
|
||||
void AddTierItems(Menu menu, const char[] sName, const char[] sLabel)
|
||||
{
|
||||
ConVar cvar = FindTierConVar(sName);
|
||||
int iTier = (cvar == null) ? 0 : cvar.IntValue;
|
||||
|
||||
char sItem[64], sInfo[16];
|
||||
if (iTier > 0)
|
||||
FormatEx(sItem, sizeof(sItem), "%s tier: %d", sLabel, iTier);
|
||||
else
|
||||
FormatEx(sItem, sizeof(sItem), "%s tier: OFF", sLabel);
|
||||
menu.AddItem("", sItem, ITEMDRAW_DISABLED);
|
||||
|
||||
FormatEx(sInfo, sizeof(sInfo), "+%s", sName);
|
||||
FormatEx(sItem, sizeof(sItem), "Increase %s tier", sName);
|
||||
menu.AddItem(sInfo, sItem, (cvar == null) ? ITEMDRAW_DISABLED : ITEMDRAW_DEFAULT);
|
||||
|
||||
FormatEx(sInfo, sizeof(sInfo), "-%s", sName);
|
||||
FormatEx(sItem, sizeof(sItem), "Decrease %s tier", sName);
|
||||
menu.AddItem(sInfo, sItem, (iTier > 0) ? ITEMDRAW_DEFAULT : ITEMDRAW_DISABLED);
|
||||
}
|
||||
|
||||
public int MenuHandler_Tier(Menu menu, MenuAction action, int param1, int param2)
|
||||
{
|
||||
switch (action)
|
||||
{
|
||||
case MenuAction_Select:
|
||||
{
|
||||
int client = param1;
|
||||
|
||||
char sInfo[16];
|
||||
menu.GetItem(param2, sInfo, sizeof(sInfo));
|
||||
|
||||
DoChangeTier(client, sInfo[1], (sInfo[0] == '+') ? 1 : -1);
|
||||
ShowTierMenu(client);
|
||||
}
|
||||
|
||||
case MenuAction_Cancel:
|
||||
{
|
||||
if (param2 == MenuCancel_ExitBack)
|
||||
{
|
||||
if (g_bCoreLoaded)
|
||||
ShowCouchMenu(param1);
|
||||
else
|
||||
RequireCore(param1);
|
||||
}
|
||||
}
|
||||
|
||||
case MenuAction_End:
|
||||
{
|
||||
delete menu;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// The core plugin's default for sm_couch_push_scale, so the reset button follows it.
|
||||
float GetPushScaleDefault(ConVar cvar)
|
||||
{
|
||||
if (cvar == null)
|
||||
return 6.0;
|
||||
|
||||
char sDefault[16];
|
||||
cvar.GetDefault(sDefault, sizeof(sDefault));
|
||||
return StringToFloat(sDefault);
|
||||
}
|
||||
|
||||
void ShowPushScaleMenu(int client)
|
||||
{
|
||||
ConVar cvar = FindConVar("sm_couch_push_scale");
|
||||
float fScale = (cvar == null) ? 6.0 : cvar.FloatValue;
|
||||
|
||||
Menu menu = new Menu(MenuHandler_PushScale);
|
||||
menu.SetTitle("Couch - Push scale");
|
||||
|
||||
char sItem[64];
|
||||
FormatEx(sItem, sizeof(sItem), "Push scale: %.2fx", fScale);
|
||||
menu.AddItem("", sItem, ITEMDRAW_DISABLED);
|
||||
|
||||
FormatEx(sItem, sizeof(sItem), "Increase push scale (+%.2f)", PUSH_SCALE_STEP);
|
||||
menu.AddItem("+", sItem, (cvar == null) ? ITEMDRAW_DISABLED : ITEMDRAW_DEFAULT);
|
||||
|
||||
FormatEx(sItem, sizeof(sItem), "Decrease push scale (-%.2f)", PUSH_SCALE_STEP);
|
||||
menu.AddItem("-", sItem, (cvar != null && fScale > 0.0) ? ITEMDRAW_DEFAULT : ITEMDRAW_DISABLED);
|
||||
|
||||
float fDefault = GetPushScaleDefault(cvar);
|
||||
FormatEx(sItem, sizeof(sItem), "Reset push scale (%.2fx)", fDefault);
|
||||
menu.AddItem("reset", sItem, (cvar != null && fScale != fDefault) ? ITEMDRAW_DEFAULT : ITEMDRAW_DISABLED);
|
||||
|
||||
menu.ExitBackButton = true;
|
||||
menu.ExitButton = true;
|
||||
menu.Display(client, MENU_TIME_FOREVER);
|
||||
}
|
||||
|
||||
public int MenuHandler_PushScale(Menu menu, MenuAction action, int param1, int param2)
|
||||
{
|
||||
switch (action)
|
||||
{
|
||||
case MenuAction_Select:
|
||||
{
|
||||
int client = param1;
|
||||
|
||||
ConVar cvar = FindConVar("sm_couch_push_scale");
|
||||
if (cvar != null)
|
||||
{
|
||||
char sInfo[16];
|
||||
menu.GetItem(param2, sInfo, sizeof(sInfo));
|
||||
|
||||
if (StrEqual(sInfo, "+"))
|
||||
DoSetPushScale(client, cvar.FloatValue + PUSH_SCALE_STEP);
|
||||
else if (StrEqual(sInfo, "-"))
|
||||
DoSetPushScale(client, cvar.FloatValue - PUSH_SCALE_STEP);
|
||||
else if (StrEqual(sInfo, "reset"))
|
||||
DoSetPushScale(client, GetPushScaleDefault(cvar));
|
||||
}
|
||||
|
||||
ShowPushScaleMenu(client);
|
||||
}
|
||||
|
||||
case MenuAction_Cancel:
|
||||
{
|
||||
if (param2 == MenuCancel_ExitBack)
|
||||
{
|
||||
if (g_bCoreLoaded)
|
||||
ShowCouchMenu(param1);
|
||||
else
|
||||
RequireCore(param1);
|
||||
}
|
||||
}
|
||||
|
||||
case MenuAction_End:
|
||||
{
|
||||
delete menu;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,246 @@
|
||||
#include <sourcemod>
|
||||
#include <Couch>
|
||||
|
||||
#pragma semicolon 1
|
||||
#pragma newdecls required
|
||||
|
||||
#define PREFIX "\x01[SM] \x04"
|
||||
|
||||
// So commands fail with a message instead of an unbound-native error while the core is unloaded.
|
||||
bool g_bCoreLoaded;
|
||||
|
||||
// Account ids banned this map. This is the ban itself, so leaving and rejoining does not clear it.
|
||||
ArrayList g_alBannedAccounts;
|
||||
|
||||
// Slot mirror. Also carries bans of unauthenticated clients (account id 0), which last the session only.
|
||||
bool g_bBanned[MAXPLAYERS + 1];
|
||||
|
||||
public Plugin myinfo =
|
||||
{
|
||||
name = "Couch Ban",
|
||||
author = "marisakirisame.net",
|
||||
description = "Bans players from the ZE couch until the end of the map",
|
||||
version = "2.0",
|
||||
url = "https://marisakirisame.net"
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
// Plugin lifecycle
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
|
||||
public void OnPluginStart()
|
||||
{
|
||||
LoadTranslations("common.phrases"); // FindTarget's replies.
|
||||
|
||||
g_alBannedAccounts = new ArrayList();
|
||||
|
||||
RegAdminCmd("sm_couchban", Command_CouchBan, ADMFLAG_BAN, "sm_couchban <#userid|name> - bans a player from the couch until the end of the map");
|
||||
RegAdminCmd("sm_cban", Command_CouchBan, ADMFLAG_BAN, "Alias of sm_couchban");
|
||||
RegAdminCmd("sm_couchunban", Command_CouchUnban, ADMFLAG_BAN, "sm_couchunban <#userid|name> - lifts a couch ban");
|
||||
RegAdminCmd("sm_cunban", Command_CouchUnban, ADMFLAG_BAN, "Alias of sm_couchunban");
|
||||
}
|
||||
|
||||
public void OnAllPluginsLoaded()
|
||||
{
|
||||
g_bCoreLoaded = LibraryExists("Couch");
|
||||
|
||||
// After a reload of this plugin the core still knows who is blocked.
|
||||
if (!g_bCoreLoaded)
|
||||
return;
|
||||
|
||||
for (int i = 1; i <= MaxClients; i++)
|
||||
{
|
||||
if (!IsClientInGame(i) || !Couch_IsClientBlocked(i))
|
||||
continue;
|
||||
|
||||
g_bBanned[i] = true;
|
||||
RememberAccount(i);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnLibraryAdded(const char[] name)
|
||||
{
|
||||
if (!StrEqual(name, "Couch"))
|
||||
return;
|
||||
|
||||
g_bCoreLoaded = true;
|
||||
|
||||
// A core reload wiped its flags. A frame later so its natives are bound.
|
||||
RequestFrame(Frame_ReapplyBans);
|
||||
}
|
||||
|
||||
public void OnLibraryRemoved(const char[] name)
|
||||
{
|
||||
if (StrEqual(name, "Couch"))
|
||||
g_bCoreLoaded = false;
|
||||
}
|
||||
|
||||
void Frame_ReapplyBans(any data)
|
||||
{
|
||||
if (!g_bCoreLoaded)
|
||||
return;
|
||||
|
||||
for (int i = 1; i <= MaxClients; i++)
|
||||
{
|
||||
if (g_bBanned[i] && IsClientInGame(i))
|
||||
Couch_SetClientBlocked(i, true);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnMapStart()
|
||||
{
|
||||
for (int i = 1; i <= MAXPLAYERS; i++)
|
||||
g_bBanned[i] = false;
|
||||
}
|
||||
|
||||
public void OnMapEnd()
|
||||
{
|
||||
g_alBannedAccounts.Clear();
|
||||
}
|
||||
|
||||
// The account id stays on the list so a rejoin picks the ban back up.
|
||||
public void OnClientDisconnect(int client)
|
||||
{
|
||||
g_bBanned[client] = false;
|
||||
}
|
||||
|
||||
// Either "in game" or "authorized" can come last, so both are hooked.
|
||||
public void OnClientPutInServer(int client)
|
||||
{
|
||||
TryReapplyBan(client);
|
||||
}
|
||||
|
||||
public void OnClientAuthorized(int client, const char[] auth)
|
||||
{
|
||||
TryReapplyBan(client);
|
||||
}
|
||||
|
||||
void TryReapplyBan(int client)
|
||||
{
|
||||
if (g_bBanned[client] || !IsClientInGame(client))
|
||||
return;
|
||||
|
||||
int account = GetSteamAccountID(client);
|
||||
if (account == 0 || g_alBannedAccounts.FindValue(account) == -1)
|
||||
return;
|
||||
|
||||
g_bBanned[client] = true;
|
||||
|
||||
if (g_bCoreLoaded)
|
||||
Couch_SetClientBlocked(client, true);
|
||||
|
||||
PrintToChat(client, PREFIX ... "You are couch-banned\x01 until the end of the map.");
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
// Commands
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
|
||||
public Action Command_CouchBan(int client, int args)
|
||||
{
|
||||
if (!RequireCore(client))
|
||||
return Plugin_Handled;
|
||||
|
||||
if (args < 1)
|
||||
{
|
||||
ReplyToCommand(client, "[SM] Usage: sm_couchban <#userid|name>");
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
char sArg[MAX_TARGET_LENGTH];
|
||||
GetCmdArg(1, sArg, sizeof(sArg));
|
||||
|
||||
int target = FindTarget(client, sArg, true, true);
|
||||
if (target == -1)
|
||||
return Plugin_Handled;
|
||||
|
||||
if (g_bBanned[target])
|
||||
{
|
||||
ReplyToCommand(client, "[SM] %N is already couch-banned.", target);
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
DoBan(client, target);
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
public Action Command_CouchUnban(int client, int args)
|
||||
{
|
||||
if (!RequireCore(client))
|
||||
return Plugin_Handled;
|
||||
|
||||
if (args < 1)
|
||||
{
|
||||
ReplyToCommand(client, "[SM] Usage: sm_couchunban <#userid|name>");
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
char sArg[MAX_TARGET_LENGTH];
|
||||
GetCmdArg(1, sArg, sizeof(sArg));
|
||||
|
||||
int target = FindTarget(client, sArg, true, true);
|
||||
if (target == -1)
|
||||
return Plugin_Handled;
|
||||
|
||||
if (!g_bBanned[target])
|
||||
{
|
||||
ReplyToCommand(client, "[SM] %N is not couch-banned.", target);
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
DoUnban(client, target);
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
bool RequireCore(int client)
|
||||
{
|
||||
if (g_bCoreLoaded)
|
||||
return true;
|
||||
|
||||
ReplyToCommand(client, "[SM] The Couch core plugin is not loaded.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
// Actions
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
|
||||
void DoBan(int client, int target)
|
||||
{
|
||||
g_bBanned[target] = true;
|
||||
RememberAccount(target);
|
||||
Couch_SetClientBlocked(target, true);
|
||||
|
||||
// Announced to everyone so players know why the couch stopped reacting to someone.
|
||||
PrintToChatAll("\x01[SM] \x04%N\x01 couch-banned \x04%N\x01 until the end of the map.", client, target);
|
||||
LogAction(client, target, "\"%L\" couch-banned \"%L\" until the end of the map.", client, target);
|
||||
}
|
||||
|
||||
void DoUnban(int client, int target)
|
||||
{
|
||||
g_bBanned[target] = false;
|
||||
ForgetAccount(target);
|
||||
Couch_SetClientBlocked(target, false);
|
||||
|
||||
PrintToChatAll("\x01[SM] \x04%N\x01 removed couch-ban from \x04%N\x01.", client, target);
|
||||
LogAction(client, target, "\"%L\" removed couch-ban from \"%L\".", client, target);
|
||||
}
|
||||
|
||||
// An unauthenticated client has no account id, so their ban ends with the session.
|
||||
void RememberAccount(int client)
|
||||
{
|
||||
int account = GetSteamAccountID(client);
|
||||
if (account != 0 && g_alBannedAccounts.FindValue(account) == -1)
|
||||
g_alBannedAccounts.Push(account);
|
||||
}
|
||||
|
||||
void ForgetAccount(int client)
|
||||
{
|
||||
int account = GetSteamAccountID(client);
|
||||
if (account == 0)
|
||||
return;
|
||||
|
||||
int index = g_alBannedAccounts.FindValue(account);
|
||||
if (index != -1)
|
||||
g_alBannedAccounts.Erase(index);
|
||||
}
|
||||
@@ -0,0 +1,384 @@
|
||||
#include <sourcemod>
|
||||
#include <sdktools>
|
||||
#include <Couch>
|
||||
|
||||
#pragma semicolon 1
|
||||
#pragma newdecls required
|
||||
|
||||
// How often expired names are swept off the list. Only resends if a name actually dropped.
|
||||
#define HUD_EXPIRE_INTERVAL 0.25
|
||||
|
||||
// Longer names are cut so one can't eat the whole message.
|
||||
#define HUD_NAME_BYTES 20
|
||||
|
||||
// +use reports every tick it's held; the list is resent at most this often for it. Well under
|
||||
// sm_couch_hud_time's minimum, so the text never lapses while someone keeps pushing.
|
||||
#define HUD_USE_RESEND 0.1
|
||||
|
||||
// Put in front of the name of anyone whose grenade hit the couch recently.
|
||||
#define HUD_GRENADE_TAG "<G!> "
|
||||
|
||||
// A user message holds 255 bytes; HudMsg's fixed fields take 34 and the string its terminator.
|
||||
// Overflowing drops the whole message.
|
||||
#define HUD_MSG_BYTES 255
|
||||
#define HUD_MSG_HEADER_BYTES 34
|
||||
#define HUD_TEXT_MAX (HUD_MSG_BYTES - HUD_MSG_HEADER_BYTES - 1)
|
||||
|
||||
ConVar g_cvEnabled;
|
||||
ConVar g_cvTime;
|
||||
ConVar g_cvGrenadeTime;
|
||||
|
||||
Handle g_hHudSync;
|
||||
Handle g_hExpireTimer;
|
||||
|
||||
float g_fExpire[MAXPLAYERS + 1]; // GetGameTime() at which this client drops off the list.
|
||||
float g_fLastHit[MAXPLAYERS + 1]; // Ordering key: the most recent shooter is listed first.
|
||||
float g_fGrenadeUntil[MAXPLAYERS + 1]; // GetGameTime() at which this client loses the grenade tag.
|
||||
float g_fNextUseSend[MAXPLAYERS + 1]; // GetGameTime() before which this client's +use doesn't resend.
|
||||
|
||||
float g_fNextExpiry; // Soonest expiry or tag drop among the names currently listed.
|
||||
float g_fLastExpiry; // Latest expiry among them; the text is held until then.
|
||||
bool g_bFlushQueued; // A send is already scheduled for the next frame.
|
||||
bool g_bHudShown; // Clients currently have text up, so an empty list must clear it.
|
||||
|
||||
char g_sHudText[HUD_TEXT_MAX + 1];
|
||||
|
||||
public Plugin myinfo =
|
||||
{
|
||||
name = "Couch HUD",
|
||||
author = "marisakirisame.net",
|
||||
description = "Shows who is shooting the couch",
|
||||
version = "2.0",
|
||||
url = "https://marisakirisame.net"
|
||||
};
|
||||
|
||||
public void OnPluginStart()
|
||||
{
|
||||
g_hHudSync = CreateHudSynchronizer();
|
||||
|
||||
g_cvEnabled = CreateConVar("sm_couch_hud_enabled", "1", "Show the list of players shooting the couch.", FCVAR_NOTIFY, true, 0.0, true, 1.0);
|
||||
g_cvTime = CreateConVar("sm_couch_hud_time", "0.5", "How long a player stays on the list after their last hit or +use push, in seconds.", FCVAR_NOTIFY, true, 0.5, true, 30.0);
|
||||
g_cvGrenadeTime = CreateConVar("sm_couch_hud_time_grenade", "4.0", "How long a player stays on the list, tagged " ... HUD_GRENADE_TAG ... ", after their grenade hits the couch, in seconds.", FCVAR_NOTIFY, true, 0.5, true, 30.0);
|
||||
|
||||
g_cvEnabled.AddChangeHook(OnEnabledChanged);
|
||||
|
||||
AutoExecConfig(true, "CouchHud");
|
||||
}
|
||||
|
||||
public void OnMapStart()
|
||||
{
|
||||
// TIMER_FLAG_NO_MAPCHANGE already killed the timer.
|
||||
g_hExpireTimer = null;
|
||||
g_bFlushQueued = false;
|
||||
g_bHudShown = false;
|
||||
g_fNextExpiry = 0.0;
|
||||
g_fLastExpiry = 0.0;
|
||||
|
||||
for (int i = 1; i <= MAXPLAYERS; i++)
|
||||
{
|
||||
g_fExpire[i] = 0.0;
|
||||
g_fLastHit[i] = 0.0;
|
||||
g_fGrenadeUntil[i] = 0.0;
|
||||
g_fNextUseSend[i] = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
public void OnClientPutInServer(int client)
|
||||
{
|
||||
g_fExpire[client] = 0.0;
|
||||
g_fLastHit[client] = 0.0;
|
||||
g_fGrenadeUntil[client] = 0.0;
|
||||
g_fNextUseSend[client] = 0.0;
|
||||
}
|
||||
|
||||
public void OnClientDisconnect(int client)
|
||||
{
|
||||
bool bListed = (g_fExpire[client] > GetGameTime());
|
||||
|
||||
g_fExpire[client] = 0.0;
|
||||
g_fLastHit[client] = 0.0;
|
||||
g_fGrenadeUntil[client] = 0.0;
|
||||
g_fNextUseSend[client] = 0.0;
|
||||
|
||||
if (bListed)
|
||||
QueueFlush();
|
||||
}
|
||||
|
||||
public void OnEnabledChanged(ConVar convar, const char[] oldValue, const char[] newValue)
|
||||
{
|
||||
if (!convar.BoolValue)
|
||||
ResetAll();
|
||||
}
|
||||
|
||||
public void OnLibraryRemoved(const char[] name)
|
||||
{
|
||||
if (StrEqual(name, "Couch"))
|
||||
ResetAll();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
// Couch forwards
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
|
||||
public void Couch_OnCouchDamaged(int couch, int attacker, float damage)
|
||||
{
|
||||
if (!g_cvEnabled.BoolValue)
|
||||
return;
|
||||
|
||||
// Extended, never shortened, so gunfire right after a grenade doesn't cut its time short.
|
||||
float fNow = GetGameTime();
|
||||
g_fLastHit[attacker] = fNow;
|
||||
g_fExpire[attacker] = MaxFloat(g_fExpire[attacker], fNow + g_cvTime.FloatValue);
|
||||
|
||||
QueueFlush();
|
||||
}
|
||||
|
||||
// Hits fire just after Couch_OnCouchDamaged for the same hit, so the ordering is already updated.
|
||||
// +use never goes through damage, so it is listed from here.
|
||||
public void Couch_OnCouchInteraction(int couch, int client, CouchMethod method, const char[] weapon)
|
||||
{
|
||||
if (!g_cvEnabled.BoolValue)
|
||||
return;
|
||||
|
||||
float fNow = GetGameTime();
|
||||
|
||||
if (method == CouchMethod_Use)
|
||||
{
|
||||
g_fLastHit[client] = fNow;
|
||||
g_fExpire[client] = MaxFloat(g_fExpire[client], fNow + g_cvTime.FloatValue);
|
||||
|
||||
if (fNow < g_fNextUseSend[client])
|
||||
return;
|
||||
|
||||
g_fNextUseSend[client] = fNow + HUD_USE_RESEND;
|
||||
QueueFlush();
|
||||
return;
|
||||
}
|
||||
|
||||
if (method != CouchMethod_Grenade)
|
||||
return;
|
||||
|
||||
g_fGrenadeUntil[client] = fNow + g_cvGrenadeTime.FloatValue;
|
||||
g_fExpire[client] = MaxFloat(g_fExpire[client], g_fGrenadeUntil[client]);
|
||||
|
||||
QueueFlush();
|
||||
}
|
||||
|
||||
float MaxFloat(float a, float b)
|
||||
{
|
||||
return (a > b) ? a : b;
|
||||
}
|
||||
|
||||
public void Couch_OnCouchRemoved()
|
||||
{
|
||||
ResetAll();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
// Sending
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
|
||||
// Once per frame: every hit in a frame (all shotgun pellets, several shooters) lands here, but
|
||||
// only the first schedules a send.
|
||||
void QueueFlush()
|
||||
{
|
||||
if (g_bFlushQueued)
|
||||
return;
|
||||
|
||||
g_bFlushQueued = true;
|
||||
RequestFrame(Frame_Flush);
|
||||
}
|
||||
|
||||
void Frame_Flush(any data)
|
||||
{
|
||||
g_bFlushQueued = false;
|
||||
Flush();
|
||||
}
|
||||
|
||||
void Flush()
|
||||
{
|
||||
if (!g_cvEnabled.BoolValue || Couch_GetCouch() == -1)
|
||||
{
|
||||
ResetAll();
|
||||
return;
|
||||
}
|
||||
|
||||
if (BuildHudText(g_sHudText, sizeof(g_sHudText)) == 0)
|
||||
{
|
||||
ClearAll();
|
||||
return;
|
||||
}
|
||||
|
||||
Send();
|
||||
}
|
||||
|
||||
void Send()
|
||||
{
|
||||
// Held until the last name lapses, so the text leaves on its own if no update ever comes.
|
||||
// Names lapsing sooner are dropped by a resend from the expiry timer.
|
||||
SetHudTextParams(0.8, 0.2, g_fLastExpiry - GetGameTime(), 255, 255, 255, 255, 0, 0.0, 0.0, 0.0);
|
||||
|
||||
for (int i = 1; i <= MaxClients; i++)
|
||||
{
|
||||
if (IsClientInGame(i) && !IsFakeClient(i))
|
||||
ShowSyncHudText(i, g_hHudSync, "%s", g_sHudText);
|
||||
}
|
||||
|
||||
g_bHudShown = true;
|
||||
|
||||
// Started here rather than from the hit, so a tick between hit and send can't stop the timer.
|
||||
if (g_hExpireTimer == null)
|
||||
g_hExpireTimer = CreateTimer(HUD_EXPIRE_INTERVAL, Timer_Expire, _, TIMER_REPEAT | TIMER_FLAG_NO_MAPCHANGE);
|
||||
}
|
||||
|
||||
// Most recent shooters first. Returns how many players are listed, shown or not. Names are
|
||||
// packed until the message is full, then come off the bottom until a "+ N" line fits.
|
||||
int BuildHudText(char[] text, int maxlen)
|
||||
{
|
||||
float fNow = GetGameTime();
|
||||
bool bTaken[MAXPLAYERS + 1];
|
||||
int order[MAXPLAYERS + 1];
|
||||
int total = 0;
|
||||
|
||||
g_fNextExpiry = 0.0;
|
||||
g_fLastExpiry = 0.0;
|
||||
|
||||
// Selection sort, most recent hit first. The soonest expiry is tracked over everyone listed,
|
||||
// since a hidden player lapsing still changes the "+ N" count. A grenade tag dropping off a
|
||||
// name that stays listed changes the text too, so it counts as an expiry.
|
||||
for (;;)
|
||||
{
|
||||
int best = 0;
|
||||
|
||||
for (int i = 1; i <= MaxClients; i++)
|
||||
{
|
||||
if (bTaken[i] || g_fExpire[i] <= fNow || !IsClientInGame(i))
|
||||
continue;
|
||||
|
||||
if (best == 0 || g_fLastHit[i] > g_fLastHit[best])
|
||||
best = i;
|
||||
}
|
||||
|
||||
if (best == 0)
|
||||
break;
|
||||
|
||||
bTaken[best] = true;
|
||||
order[total++] = best;
|
||||
|
||||
float fChange = g_fExpire[best];
|
||||
if (g_fGrenadeUntil[best] > fNow && g_fGrenadeUntil[best] < fChange)
|
||||
fChange = g_fGrenadeUntil[best];
|
||||
|
||||
if (g_fNextExpiry == 0.0 || fChange < g_fNextExpiry)
|
||||
g_fNextExpiry = fChange;
|
||||
|
||||
if (g_fExpire[best] > g_fLastExpiry)
|
||||
g_fLastExpiry = g_fExpire[best];
|
||||
}
|
||||
|
||||
if (total == 0)
|
||||
{
|
||||
text[0] = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
strcopy(text, maxlen, "Shooting couch:");
|
||||
|
||||
// rowStart[n] is the text length before row n, so a row can be cut back off.
|
||||
int rowStart[MAXPLAYERS + 1];
|
||||
int rows = 0;
|
||||
|
||||
for (; rows < total; rows++)
|
||||
{
|
||||
char sName[MAX_NAME_LENGTH];
|
||||
GetClientName(order[rows], sName, sizeof(sName));
|
||||
TruncateUtf8(sName, HUD_NAME_BYTES);
|
||||
|
||||
char sLine[HUD_NAME_BYTES + sizeof(HUD_GRENADE_TAG) + 1];
|
||||
FormatEx(sLine, sizeof(sLine), "\n%s%s", (g_fGrenadeUntil[order[rows]] > fNow) ? HUD_GRENADE_TAG : "", sName);
|
||||
|
||||
int len = strlen(text);
|
||||
if (len + strlen(sLine) >= maxlen)
|
||||
break;
|
||||
|
||||
rowStart[rows] = len;
|
||||
StrCat(text, maxlen, sLine);
|
||||
}
|
||||
|
||||
// Overflow line. A longer count can need another byte, so the fit is rechecked after every drop.
|
||||
while (rows < total)
|
||||
{
|
||||
char sLine[16];
|
||||
FormatEx(sLine, sizeof(sLine), "\n+ %d", total - rows);
|
||||
|
||||
if (strlen(text) + strlen(sLine) < maxlen)
|
||||
{
|
||||
StrCat(text, maxlen, sLine);
|
||||
break;
|
||||
}
|
||||
|
||||
rows--;
|
||||
text[rowStart[rows]] = '\0';
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
// Cuts to at most maxBytes without leaving a partial multi-byte character on the end.
|
||||
void TruncateUtf8(char[] text, int maxBytes)
|
||||
{
|
||||
if (strlen(text) <= maxBytes)
|
||||
return;
|
||||
|
||||
int cut = maxBytes;
|
||||
|
||||
// Back up over continuation bytes so the whole character is dropped.
|
||||
while (cut > 0 && (text[cut] & 0xC0) == 0x80)
|
||||
cut--;
|
||||
|
||||
text[cut] = '\0';
|
||||
}
|
||||
|
||||
public Action Timer_Expire(Handle timer)
|
||||
{
|
||||
if (!g_bHudShown)
|
||||
{
|
||||
g_hExpireTimer = null;
|
||||
return Plugin_Stop;
|
||||
}
|
||||
|
||||
// Only resend when a name has lapsed.
|
||||
if (GetGameTime() >= g_fNextExpiry)
|
||||
Flush();
|
||||
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
void ClearAll()
|
||||
{
|
||||
if (!g_bHudShown)
|
||||
return;
|
||||
|
||||
for (int i = 1; i <= MaxClients; i++)
|
||||
{
|
||||
if (IsClientInGame(i) && !IsFakeClient(i))
|
||||
ClearSyncHud(i, g_hHudSync);
|
||||
}
|
||||
|
||||
g_bHudShown = false;
|
||||
}
|
||||
|
||||
// The expiry timer sees g_bHudShown clear on its next tick and stops itself, so this is safe
|
||||
// to call from inside it.
|
||||
void ResetAll()
|
||||
{
|
||||
for (int i = 1; i <= MAXPLAYERS; i++)
|
||||
{
|
||||
g_fExpire[i] = 0.0;
|
||||
g_fLastHit[i] = 0.0;
|
||||
g_fGrenadeUntil[i] = 0.0;
|
||||
g_fNextUseSend[i] = 0.0;
|
||||
}
|
||||
|
||||
ClearAll();
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
#if defined _Couch_included
|
||||
#endinput
|
||||
#endif
|
||||
|
||||
#define _Couch_included
|
||||
|
||||
// How a player moved the couch, passed to Couch_OnCouchInteraction.
|
||||
enum CouchMethod
|
||||
{
|
||||
CouchMethod_Bullet, // Gunfire; weapon is the gun, e.g. "ak47".
|
||||
CouchMethod_Knife, // weapon is "knife".
|
||||
CouchMethod_Grenade, // weapon is "hegrenade".
|
||||
CouchMethod_Use, // +use; weapon is empty.
|
||||
CouchMethod_Other // Any other damage credited to a player; weapon is the inflictor's classname.
|
||||
};
|
||||
|
||||
// The core defines COUCH_CORE; it creates the forwards and natives itself.
|
||||
#if !defined COUCH_CORE
|
||||
|
||||
/**
|
||||
* Called after the couch has been created, for admin spawns and round-start respawns.
|
||||
*
|
||||
* @param couch Entity index of the new couch.
|
||||
*/
|
||||
forward void Couch_OnCouchSpawned(int couch);
|
||||
|
||||
/**
|
||||
* Called once the couch entity is gone, for whatever reason. Couch_IsPlaced() tells whether
|
||||
* it will come back at the next round start.
|
||||
*/
|
||||
forward void Couch_OnCouchRemoved();
|
||||
|
||||
/**
|
||||
* Called for every hit the couch accepts from a player. Shotguns report once per pellet.
|
||||
*
|
||||
* @param couch Entity index of the couch.
|
||||
* @param attacker Client index of the player who dealt the damage.
|
||||
* @param damage Damage dealt by this hit.
|
||||
*/
|
||||
forward void Couch_OnCouchDamaged(int couch, int attacker, float damage);
|
||||
|
||||
/**
|
||||
* Called for every hit or +use the couch accepts from a player, saying how it was moved. Hits
|
||||
* fire alongside Couch_OnCouchDamaged, so shotguns report once per pellet.
|
||||
*
|
||||
* @param couch Entity index of the couch.
|
||||
* @param client Client index of the player.
|
||||
* @param method How the couch was moved.
|
||||
* @param weapon Weapon without its "weapon_" prefix, see CouchMethod.
|
||||
*/
|
||||
forward void Couch_OnCouchInteraction(int couch, int client, CouchMethod method, const char[] weapon);
|
||||
|
||||
/**
|
||||
* Called when a hit or +use from a blocked client is refused. Fires per pellet, so rate-limit anything you send.
|
||||
*
|
||||
* @param couch Entity index of the couch.
|
||||
* @param client Client index of the blocked player.
|
||||
*/
|
||||
forward void Couch_OnClientBlocked(int couch, int client);
|
||||
|
||||
/**
|
||||
* @return Entity index of the couch, or -1 if there is none.
|
||||
*/
|
||||
native int Couch_GetCouch();
|
||||
|
||||
/**
|
||||
* Whether an admin has placed a couch this map. While true the couch respawns at the placed
|
||||
* spot every round start.
|
||||
*
|
||||
* @return True if a placement is remembered.
|
||||
*/
|
||||
native bool Couch_IsPlaced();
|
||||
|
||||
/**
|
||||
* Spawns the couch where the client is aiming, replacing any existing couch. The spot becomes
|
||||
* the respawn point.
|
||||
*
|
||||
* @param client Client whose crosshair places the couch. Must be in game.
|
||||
* @return Entity index of the couch, or -1 on failure.
|
||||
*/
|
||||
native int Couch_Spawn(int client);
|
||||
|
||||
/**
|
||||
* Teleports the couch to where the client is aiming. The respawn point is left as it was.
|
||||
*
|
||||
* @param client Client whose crosshair places the couch. Must be in game.
|
||||
* @return True on success, false if there is no couch.
|
||||
*/
|
||||
native bool Couch_Bring(int client);
|
||||
|
||||
/**
|
||||
* Teleports the client onto the couch, or beside it when the top is blocked. The client keeps
|
||||
* their view angles and arrives with no velocity.
|
||||
*
|
||||
* @param client Client to teleport. Must be in game.
|
||||
* @return True on success, false if there is no couch.
|
||||
*/
|
||||
native bool Couch_Goto(int client);
|
||||
|
||||
/**
|
||||
* Removes the couch and forgets its placement.
|
||||
*
|
||||
* @return True if a couch or a placement was removed.
|
||||
*/
|
||||
native bool Couch_Remove();
|
||||
|
||||
/**
|
||||
* Whether the colour cycle and light are on. Mirrors sm_couch_effect.
|
||||
*/
|
||||
native bool Couch_IsEffectEnabled();
|
||||
|
||||
/**
|
||||
* Switches the colour cycle and light on or off. Writes sm_couch_effect.
|
||||
*/
|
||||
native void Couch_SetEffectEnabled(bool enabled);
|
||||
|
||||
/**
|
||||
* Whether only pistols and knives may move the couch. Mirrors sm_couch_pistol_only.
|
||||
*/
|
||||
native bool Couch_IsPistolOnly();
|
||||
|
||||
/**
|
||||
* Restricts couch movement to pistols and knives, or lifts the restriction. Writes sm_couch_pistol_only.
|
||||
*/
|
||||
native void Couch_SetPistolOnly(bool enabled);
|
||||
|
||||
/**
|
||||
* Whether players may push the couch by holding +use on it. Mirrors sm_allow_couch_use.
|
||||
*/
|
||||
native bool Couch_IsUseAllowed();
|
||||
|
||||
/**
|
||||
* Allows or forbids pushing the couch with +use; when forbidden it can only be shot. Writes sm_allow_couch_use.
|
||||
*/
|
||||
native void Couch_SetUseAllowed(bool allowed);
|
||||
|
||||
/**
|
||||
* Blocks or unblocks a client from the couch: their hits are refused and their body can't push
|
||||
* it. The flag is per slot and cleared on disconnect and map start; the caller owns persistence.
|
||||
*
|
||||
* @param client Client to block or unblock. Must be in game.
|
||||
* @param blocked True to block, false to lift the block.
|
||||
*/
|
||||
native void Couch_SetClientBlocked(int client, bool blocked);
|
||||
|
||||
/**
|
||||
* Whether the client is currently blocked from the couch.
|
||||
*
|
||||
* @param client Client index.
|
||||
* @return True if blocked.
|
||||
*/
|
||||
native bool Couch_IsClientBlocked(int client);
|
||||
|
||||
|
||||
public SharedPlugin __pl_Couch =
|
||||
{
|
||||
name = "Couch",
|
||||
file = "Couch.smx",
|
||||
|
||||
#if defined REQUIRE_PLUGIN
|
||||
required = 1
|
||||
#else
|
||||
required = 0
|
||||
#endif
|
||||
};
|
||||
|
||||
#if !defined REQUIRE_PLUGIN
|
||||
public void __pl_Couch_SetNTVOptional()
|
||||
{
|
||||
MarkNativeAsOptional("Couch_GetCouch");
|
||||
MarkNativeAsOptional("Couch_IsPlaced");
|
||||
MarkNativeAsOptional("Couch_Spawn");
|
||||
MarkNativeAsOptional("Couch_Bring");
|
||||
MarkNativeAsOptional("Couch_Goto");
|
||||
MarkNativeAsOptional("Couch_Remove");
|
||||
MarkNativeAsOptional("Couch_IsEffectEnabled");
|
||||
MarkNativeAsOptional("Couch_SetEffectEnabled");
|
||||
MarkNativeAsOptional("Couch_IsPistolOnly");
|
||||
MarkNativeAsOptional("Couch_SetPistolOnly");
|
||||
MarkNativeAsOptional("Couch_IsUseAllowed");
|
||||
MarkNativeAsOptional("Couch_SetUseAllowed");
|
||||
MarkNativeAsOptional("Couch_SetClientBlocked");
|
||||
MarkNativeAsOptional("Couch_IsClientBlocked");
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // COUCH_CORE
|
||||
@@ -2,9 +2,8 @@
|
||||
|
||||
#include <basecomm>
|
||||
#include <sourcemod>
|
||||
#include <SteamWorks>
|
||||
#include <ripext>
|
||||
#include <regex>
|
||||
#include <smjansson>
|
||||
#include <multicolors>
|
||||
#include <sdktools>
|
||||
#include <AFKManager>
|
||||
@@ -17,7 +16,8 @@
|
||||
#tryinclude <calladmin>
|
||||
#tryinclude <oryx>
|
||||
#tryinclude <entWatch_restrictions>
|
||||
#tryinclude <jenz_ban_detector>
|
||||
//#tryinclude <jenz_ban_detector>
|
||||
#tryinclude <handicap_trouble_maker>
|
||||
|
||||
#pragma newdecls required
|
||||
|
||||
@@ -28,6 +28,8 @@ ArrayList g_arrQueuedMessages = null;
|
||||
Handle g_hDataTimer = null;
|
||||
Handle g_hReplaceConfigFile = null;
|
||||
|
||||
static Handle g_hForwardDiscord;
|
||||
|
||||
UserMsg g_umSayText2 = INVALID_MESSAGE_ID;
|
||||
|
||||
bool g_bLoadedLate;
|
||||
@@ -58,7 +60,7 @@ public Plugin myinfo =
|
||||
public APLRes AskPluginLoad2(Handle hThis, bool bLate, char[] sError, int err_max)
|
||||
{
|
||||
g_bLoadedLate = bLate;
|
||||
|
||||
RegPluginLibrary("Discord_UNLOZE");
|
||||
return APLRes_Success;
|
||||
}
|
||||
|
||||
@@ -67,6 +69,9 @@ public void OnPluginStart()
|
||||
char sRegexErr[32];
|
||||
RegexError RegexErr;
|
||||
|
||||
g_hForwardDiscord = CreateGlobalForward("GetDiscordToStoat", ET_Ignore, Param_String, Param_String, Param_Cell);
|
||||
|
||||
|
||||
g_Regex_Clyde = CompileRegex(".*(clyde).*", PCRE_CASELESS, sRegexErr, sizeof(sRegexErr), RegexErr);
|
||||
|
||||
if (RegexErr != REGEX_ERROR_NONE)
|
||||
@@ -113,6 +118,7 @@ public void OnPluginEnd()
|
||||
{
|
||||
delete g_arrQueuedMessages;
|
||||
delete g_hDataTimer;
|
||||
CloseHandle(g_hForwardDiscord);
|
||||
|
||||
UnhookUserMessage(g_umSayText2, Hook_UserMessage, false);
|
||||
}
|
||||
@@ -132,6 +138,9 @@ public void OnAdminAFKTimeChanged(ConVar convar, const char[] oldValue, const ch
|
||||
g_iAdminAFKTime = g_cvAdminAFKTime.IntValue;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose: Steam player summary lookup (avatar) - now via ripext, JSON instead of VDF
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public void OnClientAuthorized(int client, const char[] sAuthID32)
|
||||
{
|
||||
if (IsFakeClient(client))
|
||||
@@ -142,19 +151,44 @@ public void OnClientAuthorized(int client, const char[] sAuthID32)
|
||||
if (!Steam32IDtoSteam64ID(sAuthID32, sAuthID64, sizeof(sAuthID64)))
|
||||
return;
|
||||
|
||||
static char sRequest[256];
|
||||
char sRequest[256];
|
||||
|
||||
FormatEx(sRequest, sizeof(sRequest), "http://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/?key=%s&steamids=%s&format=vdf", STEAM_API_KEY, sAuthID64);
|
||||
FormatEx(sRequest, sizeof(sRequest), "http://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/?key=%s&steamids=%s&format=json", STEAM_API_KEY, sAuthID64);
|
||||
|
||||
Handle hRequest = SteamWorks_CreateHTTPRequest(k_EHTTPMethodGET, sRequest);
|
||||
HTTPRequest req = new HTTPRequest(sRequest);
|
||||
req.Get(OnTransferComplete, GetClientSerial(client));
|
||||
}
|
||||
|
||||
if (!hRequest ||
|
||||
!SteamWorks_SetHTTPRequestContextValue(hRequest, client) ||
|
||||
!SteamWorks_SetHTTPCallbacks(hRequest, OnTransferComplete) ||
|
||||
!SteamWorks_SendHTTPRequest(hRequest))
|
||||
public void OnTransferComplete(HTTPResponse response, any serial)
|
||||
{
|
||||
int client = GetClientFromSerial(serial);
|
||||
if (!client) //Player disconnected.
|
||||
return;
|
||||
|
||||
if (response.Status != HTTPStatus_OK || response.Data == null)
|
||||
return;
|
||||
|
||||
APIWebResponse(view_as<JSONObject>(response.Data), client);
|
||||
}
|
||||
|
||||
public void APIWebResponse(JSONObject root, int client)
|
||||
{
|
||||
if (!root.HasKey("players"))
|
||||
return;
|
||||
|
||||
JSONArray players = view_as<JSONArray>(root.Get("players"));
|
||||
|
||||
if (players == null || !players.Length)
|
||||
{
|
||||
delete hRequest;
|
||||
delete players;
|
||||
return;
|
||||
}
|
||||
|
||||
JSONObject player = view_as<JSONObject>(players.Get(0));
|
||||
player.GetString("avatarfull", g_sAvatarURL[client], sizeof(g_sAvatarURL[]));
|
||||
|
||||
delete player;
|
||||
delete players;
|
||||
}
|
||||
|
||||
public Action Command_PrintToAdminChat(int args)
|
||||
@@ -190,6 +224,9 @@ public Action Command_PrintToAllChat(int args)
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose: Drains the rate-limit queue - now via ripext
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public Action Timer_DataProcessor(Handle hThis)
|
||||
{
|
||||
if (!g_bProcessingData)
|
||||
@@ -198,8 +235,6 @@ public Action Timer_DataProcessor(Handle hThis)
|
||||
if (g_iRatelimitRemaining == 0 && GetTime() < g_iRatelimitReset)
|
||||
return Plugin_Handled;
|
||||
|
||||
//PrintToServer("[Timer_DataProcessor] Array Length #1: %d", g_arrQueuedMessages.Length);
|
||||
|
||||
char sContent[1024];
|
||||
g_arrQueuedMessages.GetString(0, sContent, sizeof(sContent));
|
||||
g_arrQueuedMessages.Erase(0);
|
||||
@@ -211,28 +246,13 @@ public Action Timer_DataProcessor(Handle hThis)
|
||||
if (g_arrQueuedMessages.Length == 0)
|
||||
g_bProcessingData = false;
|
||||
|
||||
//PrintToServer("[Timer_DataProcessor] Array Length #2: %d", g_arrQueuedMessages.Length);
|
||||
JSONObject data = JSONObject.FromString(sContent);
|
||||
|
||||
//PrintToServer("%s | %s", sURL, sContent);
|
||||
HTTPRequest req = new HTTPRequest(sURL);
|
||||
req.Post(data, OnHTTPRequestCompleted);
|
||||
|
||||
Handle hRequest = SteamWorks_CreateHTTPRequest(k_EHTTPMethodPOST, sURL);
|
||||
delete data;
|
||||
|
||||
JSONObject RequestJSON = view_as<JSONObject>(json_load(sContent));
|
||||
|
||||
if (!hRequest ||
|
||||
!SteamWorks_SetHTTPRequestContextValue(hRequest, RequestJSON) ||
|
||||
!SteamWorks_SetHTTPCallbacks(hRequest, OnHTTPRequestCompleted) ||
|
||||
!SteamWorks_SetHTTPRequestRawPostBody(hRequest, "application/json", sContent, strlen(sContent)) ||
|
||||
!SteamWorks_SetHTTPRequestNetworkActivityTimeout(hRequest, 10) ||
|
||||
!SteamWorks_SendHTTPRequest(hRequest))
|
||||
{
|
||||
LogError("Discord SteamWorks_CreateHTTPRequest failed.");
|
||||
|
||||
delete RequestJSON;
|
||||
delete hRequest;
|
||||
|
||||
return Plugin_Handled;
|
||||
}
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
@@ -293,27 +313,11 @@ stock void Discord_MakeStringSafe(const char[] sOrigin, char[] sOut, int iOutSiz
|
||||
{
|
||||
if (sOrigin[i] < 0x20 && sOrigin[i] != 0x0)
|
||||
{
|
||||
//sOut[iCurIndex] = 0x20;
|
||||
//iCurIndex++;
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (sOrigin[i])
|
||||
{
|
||||
// case '"':
|
||||
// {
|
||||
// strcopy(sOut[iCurIndex], iOutSize, "\\u0022");
|
||||
// iCurIndex += 6;
|
||||
|
||||
// continue;
|
||||
// }
|
||||
// case '\\':
|
||||
// {
|
||||
// strcopy(sOut[iCurIndex], iOutSize, "\\u005C");
|
||||
// iCurIndex += 6;
|
||||
|
||||
// continue;
|
||||
// }
|
||||
case '@':
|
||||
{
|
||||
strcopy(sOut[iCurIndex], iOutSize, "@"); //@ + zero-width space
|
||||
@@ -351,102 +355,12 @@ stock void Discord_MakeStringSafe(const char[] sOrigin, char[] sOut, int iOutSiz
|
||||
}
|
||||
}
|
||||
|
||||
stock int OnTransferComplete(Handle hRequest, bool bFailure, bool bRequestSuccessful, EHTTPStatusCode eStatusCode, int client)
|
||||
{
|
||||
if (bFailure || !bRequestSuccessful || eStatusCode != k_EHTTPStatusCode200OK)
|
||||
{
|
||||
if (eStatusCode != k_EHTTPStatusCode429TooManyRequests)
|
||||
{
|
||||
LogError("SteamAPI HTTP Response failed: %d", eStatusCode);
|
||||
}
|
||||
|
||||
delete hRequest;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int iBodyLength;
|
||||
SteamWorks_GetHTTPResponseBodySize(hRequest, iBodyLength);
|
||||
|
||||
char[] sData = new char[iBodyLength];
|
||||
SteamWorks_GetHTTPResponseBodyData(hRequest, sData, iBodyLength);
|
||||
|
||||
delete hRequest;
|
||||
|
||||
APIWebResponse(sData, client);
|
||||
return 0;
|
||||
}
|
||||
|
||||
stock void APIWebResponse(const char[] sData, int client)
|
||||
{
|
||||
KeyValues kvResponse = new KeyValues("SteamAPIResponse");
|
||||
|
||||
if (!kvResponse.ImportFromString(sData, "SteamAPIResponse"))
|
||||
{
|
||||
//LogError("kvResponse.ImportFromString(\"SteamAPIResponse\") in APIWebResponse failed.");
|
||||
|
||||
delete kvResponse;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!kvResponse.JumpToKey("players"))
|
||||
{
|
||||
//LogError("kvResponse.JumpToKey(\"players\") in APIWebResponse failed.");
|
||||
|
||||
delete kvResponse;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!kvResponse.GotoFirstSubKey())
|
||||
{
|
||||
//LogError("kvResponse.GotoFirstSubKey() in APIWebResponse failed.");
|
||||
|
||||
delete kvResponse;
|
||||
return;
|
||||
}
|
||||
|
||||
kvResponse.GetString("avatarfull", g_sAvatarURL[client], sizeof(g_sAvatarURL[]));
|
||||
|
||||
delete kvResponse;
|
||||
}
|
||||
|
||||
stock void HTTPPostJSON(const char[] sURL, const char[] sText)
|
||||
{
|
||||
// if (g_iRatelimitRemaining > 0 && !g_bProcessingData && GetTime() < g_iRatelimitReset)
|
||||
// {
|
||||
Handle hRequest = SteamWorks_CreateHTTPRequest(k_EHTTPMethodPOST, sURL);
|
||||
|
||||
JSONObject RequestJSON = view_as<JSONObject>(json_load(sText));
|
||||
|
||||
if (!hRequest ||
|
||||
!SteamWorks_SetHTTPRequestContextValue(hRequest, RequestJSON) ||
|
||||
!SteamWorks_SetHTTPCallbacks(hRequest, OnHTTPRequestCompleted) ||
|
||||
!SteamWorks_SetHTTPRequestRawPostBody(hRequest, "application/json", sText, strlen(sText)) ||
|
||||
!SteamWorks_SetHTTPRequestNetworkActivityTimeout(hRequest, 15) ||
|
||||
!SteamWorks_SendHTTPRequest(hRequest))
|
||||
{
|
||||
LogError("Discord SteamWorks_CreateHTTPRequest failed.");
|
||||
|
||||
delete RequestJSON;
|
||||
delete hRequest;
|
||||
|
||||
return;
|
||||
}
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// g_arrQueuedMessages.PushString(sText);
|
||||
// g_arrQueuedMessages.PushString(sURL);
|
||||
// g_bProcessingData = true;
|
||||
// }
|
||||
|
||||
//delete hRequest;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose: Webhook POST helper - now via ripext
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
stock void Discord_POST(const char[] sURL, char[] sText, bool bUsingUsername=false, char[] sUsername=NULL_STRING, bool bUsingAvatar=false, char[] sAvatarURL=NULL_STRING, bool bSafe=true, bool bTimestamp=true)
|
||||
{
|
||||
//PrintToServer("[Discord_POST] Called with text: %s", sText);
|
||||
|
||||
JSONRootNode hJSONRoot = new JSONObject();
|
||||
JSONObject hJSONRoot = new JSONObject();
|
||||
|
||||
char sSafeText[4096];
|
||||
char sFinal[4096];
|
||||
@@ -456,13 +370,13 @@ stock void Discord_POST(const char[] sURL, char[] sText, bool bUsingUsername=fal
|
||||
TrimString(sUsername);
|
||||
|
||||
if (g_Regex_Clyde.Match(sUsername) > 0 || strlen(sUsername) < 2)
|
||||
(view_as<JSONObject>(hJSONRoot)).SetString("username", "Invalid Name");
|
||||
hJSONRoot.SetString("username", "Invalid Name");
|
||||
else
|
||||
(view_as<JSONObject>(hJSONRoot)).SetString("username", sUsername);
|
||||
hJSONRoot.SetString("username", sUsername);
|
||||
}
|
||||
|
||||
if (bUsingAvatar)
|
||||
(view_as<JSONObject>(hJSONRoot)).SetString("avatar_url", sAvatarURL);
|
||||
hJSONRoot.SetString("avatar_url", sAvatarURL);
|
||||
|
||||
if (bSafe)
|
||||
{
|
||||
@@ -481,102 +395,72 @@ stock void Discord_POST(const char[] sURL, char[] sText, bool bUsingUsername=fal
|
||||
Format(sSafeText, sizeof(sSafeText), "[ *%s* ] %s", sTime, sText);
|
||||
}
|
||||
|
||||
(view_as<JSONObject>(hJSONRoot)).SetString("content", sSafeText);
|
||||
(view_as<JSONObject>(hJSONRoot)).ToString(sFinal, sizeof(sFinal), 0);
|
||||
|
||||
//hJSONRoot.DumpToServer();
|
||||
|
||||
delete hJSONRoot;
|
||||
hJSONRoot.SetString("content", sSafeText);
|
||||
|
||||
if ((g_iRatelimitRemaining > 0 || GetTime() >= g_iRatelimitReset) && !g_bProcessingData)
|
||||
{
|
||||
//PrintToServer("[Discord_POST] Have allowances and not processing data");
|
||||
HTTPRequest req = new HTTPRequest(sURL);
|
||||
req.Post(hJSONRoot, OnHTTPRequestCompleted);
|
||||
|
||||
Handle hRequest = SteamWorks_CreateHTTPRequest(k_EHTTPMethodPOST, sURL);
|
||||
|
||||
JSONObject RequestJSON = view_as<JSONObject>(json_load(sFinal));
|
||||
|
||||
if (!hRequest ||
|
||||
!SteamWorks_SetHTTPRequestContextValue(hRequest, RequestJSON) ||
|
||||
!SteamWorks_SetHTTPCallbacks(hRequest, OnHTTPRequestCompleted) ||
|
||||
!SteamWorks_SetHTTPRequestRawPostBody(hRequest, "application/json", sFinal, strlen(sFinal)) ||
|
||||
!SteamWorks_SetHTTPRequestNetworkActivityTimeout(hRequest, 10) ||
|
||||
!SteamWorks_SendHTTPRequest(hRequest))
|
||||
{
|
||||
LogError("Discord SteamWorks_CreateHTTPRequest failed.");
|
||||
|
||||
delete RequestJSON;
|
||||
delete hRequest;
|
||||
|
||||
return;
|
||||
}
|
||||
delete hJSONRoot;
|
||||
}
|
||||
else
|
||||
{
|
||||
//PrintToServer("[Discord_POST] Have allowances? [%s] | Is processing data? [%s]", g_iRatelimitRemaining > 0 ? "YES":"NO", g_bProcessingData?"YES":"NO");
|
||||
hJSONRoot.ToString(sFinal, sizeof(sFinal), 0);
|
||||
delete hJSONRoot;
|
||||
|
||||
g_arrQueuedMessages.PushString(sFinal);
|
||||
g_arrQueuedMessages.PushString(sURL);
|
||||
g_bProcessingData = true;
|
||||
}
|
||||
|
||||
//delete hRequest; //nonono
|
||||
}
|
||||
|
||||
public int OnHTTPRequestCompleted(Handle hRequest, bool bFailure, bool bRequestSuccessful, EHTTPStatusCode eStatusCode, JSONObject RequestJSON)
|
||||
public void OnHTTPRequestCompleted(HTTPResponse response, any value)
|
||||
{
|
||||
if (bFailure || !bRequestSuccessful || (eStatusCode != k_EHTTPStatusCode200OK && eStatusCode != k_EHTTPStatusCode204NoContent))
|
||||
if (response.Status != HTTPStatus_OK && response.Status != HTTPStatus_NoContent)
|
||||
{
|
||||
if (eStatusCode != k_EHTTPStatusCode429TooManyRequests)
|
||||
LogError("Discord HTTP request failed: %d", eStatusCode);
|
||||
if (response.Status != HTTPStatus_TooManyRequests)
|
||||
LogError("Discord HTTP request failed: %d", response.Status);
|
||||
|
||||
if (eStatusCode == k_EHTTPStatusCode400BadRequest)
|
||||
if (response.Status == HTTPStatus_BadRequest && response.Data != null)
|
||||
{
|
||||
char sData[2048];
|
||||
|
||||
(view_as<JSONRootNode>(RequestJSON)).ToString(sData, sizeof(sData), 0);
|
||||
response.Data.ToString(sData, sizeof(sData), 0);
|
||||
|
||||
LogError("Malformed request? Dumping request data:\n%s", sData);
|
||||
}
|
||||
else if (eStatusCode == k_EHTTPStatusCode429TooManyRequests)
|
||||
else if (response.Status == HTTPStatus_TooManyRequests)
|
||||
{
|
||||
g_iRatelimitRemaining = 0;
|
||||
g_iRatelimitReset = GetTime() + 5;
|
||||
}
|
||||
|
||||
delete RequestJSON;
|
||||
delete hRequest;
|
||||
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
|
||||
static int iLastRatelimitRemaining = 0;
|
||||
static int iLastRatelimitReset = 0;
|
||||
char sTmp[32];
|
||||
bool bHeaderExists = SteamWorks_GetHTTPResponseHeaderValue(hRequest, "x-ratelimit-remaining", sTmp, sizeof(sTmp));
|
||||
bool bHeaderExists = response.GetHeader("x-ratelimit-remaining", sTmp, sizeof(sTmp));
|
||||
|
||||
if (!bHeaderExists)
|
||||
LogError("x-ratelimit-remaining header value could not be retrieved");
|
||||
|
||||
int iRatelimitRemaining = StringToInt(sTmp);
|
||||
|
||||
bHeaderExists = SteamWorks_GetHTTPResponseHeaderValue(hRequest, "x-ratelimit-reset", sTmp, sizeof(sTmp));
|
||||
bHeaderExists = response.GetHeader("x-ratelimit-reset", sTmp, sizeof(sTmp));
|
||||
|
||||
if (!bHeaderExists)
|
||||
LogError("x-ratelimit-reset header value could not be retrieved");
|
||||
|
||||
int iRatelimitReset = StringToInt(sTmp);
|
||||
|
||||
if (iRatelimitRemaining < iLastRatelimitRemaining || iRatelimitReset >= iLastRatelimitReset) //don't be fooled by different completion times
|
||||
if (iRatelimitRemaining < iLastRatelimitRemaining || iRatelimitReset >= iLastRatelimitReset) //dont be fooled by different completion times
|
||||
{
|
||||
g_iRatelimitRemaining = iRatelimitRemaining;
|
||||
g_iRatelimitReset = iRatelimitReset;
|
||||
}
|
||||
|
||||
//PrintToServer("limit: %d | remaining: %d || reset %d - now %d", g_iRatelimitLimit, g_iRatelimitRemaining, g_iRatelimitReset, GetTime());
|
||||
|
||||
delete RequestJSON;
|
||||
delete hRequest;
|
||||
return 0;
|
||||
}
|
||||
|
||||
stock bool IsValidClient(int client)
|
||||
@@ -613,6 +497,12 @@ public Action OnLogAction(Handle hSource, Identity ident, int client, int target
|
||||
else
|
||||
Discord_POST(DISCORD_ADMINLOGS_WEBHOOKURL, sFinal, true, sClientName, false, "", false);
|
||||
|
||||
Call_StartForward(g_hForwardDiscord);
|
||||
Call_PushString(sClientName);
|
||||
Call_PushString(sFinal);
|
||||
Call_PushCell(1);
|
||||
Call_Finish();
|
||||
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
@@ -632,6 +522,11 @@ public Action CommandListener_SmChat(int client, const char[] sCommand, int argc
|
||||
else
|
||||
Discord_POST(DISCORD_ADMINCHAT_WEBHOOKURL, sText, true, sUsername);
|
||||
|
||||
Call_StartForward(g_hForwardDiscord);
|
||||
Call_PushString(sUsername);
|
||||
Call_PushString(sText);
|
||||
Call_PushCell(6);
|
||||
Call_Finish();
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
@@ -656,6 +551,12 @@ public Action OnClientSayCommand(int client, const char[] sCommand, const char[]
|
||||
else
|
||||
Discord_POST(DISCORD_ADMINCHAT_WEBHOOKURL, sFinal, true, sUsername);
|
||||
|
||||
Call_StartForward(g_hForwardDiscord);
|
||||
Call_PushString(sUsername);
|
||||
Call_PushString(sFinal);
|
||||
Call_PushCell(6);
|
||||
Call_Finish();
|
||||
|
||||
if (!bAdmin)
|
||||
{
|
||||
//g_iReplyTargetSerial = GetClientSerial(client);
|
||||
@@ -674,7 +575,7 @@ public Action OnClientSayCommand(int client, const char[] sCommand, const char[]
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
public void BanDetectorPost(int client, const char[] detected_sourceban)
|
||||
public void BanDetectorPost1(int client, const char[] detected_sourceban)
|
||||
{
|
||||
char sClientName[MAX_NAME_LENGTH];
|
||||
char sClientID[21];
|
||||
@@ -687,11 +588,17 @@ public void BanDetectorPost(int client, const char[] detected_sourceban)
|
||||
if (!Steam32IDtoSteam64ID(sClientID, sAuthID64, sizeof(sAuthID64)))
|
||||
return;
|
||||
|
||||
Format(sMessage, sizeof(sMessage), "https://steamcommunity.com/profiles/%s\nName: %s\nSteamID: %s\nClient is ban avoiding the ban: https://bans.unloze.com/index.php?p=banlist&searchText=%s&Submit=Search ", sAuthID64, sClientName, sClientID, detected_sourceban);
|
||||
Format(sMessage, sizeof(sMessage), "https://steamcommunity.com/profiles/%s\nName: %s\nSteamID: %s\nMap: %s\nClient is ban avoiding the ban: https://bans.unloze.com/index.php?p=banlist&searchText=%s&Submit=Search ", sAuthID64, sClientName, sClientID, currentMap, detected_sourceban);
|
||||
if (g_sAvatarURL[client][0] != '\0')
|
||||
Discord_POST(DISCORD_BAN_DETECTOR, sMessage, true, sClientName, true, g_sAvatarURL[client], false);
|
||||
else
|
||||
Discord_POST(DISCORD_BAN_DETECTOR, sMessage, true, sClientName, false, "", false);
|
||||
|
||||
Call_StartForward(g_hForwardDiscord);
|
||||
Call_PushString(sClientName);
|
||||
Call_PushString(sMessage);
|
||||
Call_PushCell(4);
|
||||
Call_Finish();
|
||||
}
|
||||
|
||||
public void CallAdmin_OnReportPost(int client, int target, const char[] reason)
|
||||
@@ -710,7 +617,7 @@ public void CallAdmin_OnReportPost(int client, int target, const char[] reason)
|
||||
serverPort = CallAdmin_GetHostPort();
|
||||
CallAdmin_GetHostName(sServerName, sizeof(sServerName));
|
||||
|
||||
// Reporter wasn't a real client (initiated by a module)
|
||||
// Reporter wasnt a real client (initiated by a module)
|
||||
if (client == REPORTER_CONSOLE)
|
||||
{
|
||||
strcopy(sClientName, sizeof(sClientName), "Server/Console");
|
||||
@@ -753,6 +660,12 @@ public void CallAdmin_OnReportPost(int client, int target, const char[] reason)
|
||||
Discord_POST(DISCORD_CALLADMIN_WEBHOOKURL, sMessage, true, sUsername, true, g_sAvatarURL[client], false);
|
||||
else
|
||||
Discord_POST(DISCORD_CALLADMIN_WEBHOOKURL, sMessage, true, sUsername, false, "", false);
|
||||
|
||||
Call_StartForward(g_hForwardDiscord);
|
||||
Call_PushString(sUsername);
|
||||
Call_PushString(sMessage);
|
||||
Call_PushCell(5);
|
||||
Call_Finish();
|
||||
}
|
||||
|
||||
public void CallAdmin_OnReportHandled(int client, int id)
|
||||
@@ -772,30 +685,14 @@ public void CallAdmin_OnReportHandled(int client, int id)
|
||||
Discord_POST(DISCORD_CALLADMIN_WEBHOOKURL, sMessage, true, sUsername, true, g_sAvatarURL[client], false);
|
||||
else
|
||||
Discord_POST(DISCORD_CALLADMIN_WEBHOOKURL, sMessage, true, sUsername, false, "", false);
|
||||
|
||||
Call_StartForward(g_hForwardDiscord);
|
||||
Call_PushString(sUsername);
|
||||
Call_PushString(sMessage);
|
||||
Call_PushCell(5);
|
||||
Call_Finish();
|
||||
}
|
||||
//'
|
||||
/*
|
||||
public Action Oryx_OnTrigger(int client, int &level, char[] cheat)
|
||||
{
|
||||
char sUsername[MAX_NAME_LENGTH];
|
||||
GetClientName(client, sUsername, sizeof(sUsername));
|
||||
|
||||
char currentMap[64];
|
||||
GetCurrentMap(currentMap, sizeof(currentMap));
|
||||
|
||||
char sAuthID[32];
|
||||
GetClientAuthId(client, AuthId_Steam2, sAuthID, sizeof(sAuthID), false);
|
||||
char sMessage[1900];
|
||||
Format(sMessage, sizeof(sMessage), "```%s - Tick: %d``````NAME: %N\nSTEAMID: %s\nTRIGGER LEVEL: %i\n%s```", currentMap, GetGameTickCount(), client, sAuthID, level, cheat);
|
||||
|
||||
if (g_sAvatarURL[client][0] != '\0')
|
||||
Discord_POST(DISCORD_ANTIBHOPCHEAT_WEBHOOKURL, sMessage, true, sUsername, true, g_sAvatarURL[client], false);
|
||||
else
|
||||
Discord_POST(DISCORD_ANTIBHOPCHEAT_WEBHOOKURL, sMessage, true, sUsername, false, "", false);
|
||||
|
||||
return Plugin_Continue;
|
||||
}
|
||||
*/
|
||||
public void AntiBhopCheat_OnClientDetected(int client, char[] sReason, char[] sStats)
|
||||
{
|
||||
char sCurrentMap[64];
|
||||
@@ -820,9 +717,15 @@ public void AntiBhopCheat_OnClientDetected(int client, char[] sReason, char[] sS
|
||||
Discord_POST(DISCORD_ANTIBHOPCHEAT_WEBHOOKURL, sMessage, true, sUsername, true, g_sAvatarURL[client], false);
|
||||
else
|
||||
Discord_POST(DISCORD_ANTIBHOPCHEAT_WEBHOOKURL, sMessage, true, sUsername, false, "", false);
|
||||
|
||||
Call_StartForward(g_hForwardDiscord);
|
||||
Call_PushString(sUsername);
|
||||
Call_PushString(sMessage);
|
||||
Call_PushCell(2);
|
||||
Call_Finish();
|
||||
}
|
||||
|
||||
public void EW_OnClientRestricted(int client, int target, int length)
|
||||
public void EW_OnClientRestricted(int client, int target, int hours, int minutes)
|
||||
{
|
||||
char sCurrentMap[64];
|
||||
GetCurrentMap(sCurrentMap, sizeof(sCurrentMap));
|
||||
@@ -834,12 +737,12 @@ public void EW_OnClientRestricted(int client, int target, int length)
|
||||
Format(sDemoInfo, sizeof(sDemoInfo), "Not recording");
|
||||
|
||||
char sMessageTemp[1280]; // 128 x 10
|
||||
if (length == -1)
|
||||
if (hours == -1)
|
||||
Format(sMessageTemp, sizeof(sMessageTemp), "%L got temporarily restricted by %L", target, client);
|
||||
else if (length == 0)
|
||||
else if (hours == 0 && minutes == 0)
|
||||
Format(sMessageTemp, sizeof(sMessageTemp), "%L got permanently restricted by %L", target, client);
|
||||
else
|
||||
Format(sMessageTemp, sizeof(sMessageTemp), "%L got restricted by %L for %d minutes", target, client, length);
|
||||
Format(sMessageTemp, sizeof(sMessageTemp), "%L got restricted by %L for %d Hours and %d minutes playtime", target, client, hours, minutes);
|
||||
|
||||
char sMessage[1920]; // 128 x 15
|
||||
Format(sMessage, sizeof(sMessage), "[ %s ][ %s ]```%s```", sCurrentMap, sDemoInfo, sMessageTemp);
|
||||
@@ -851,8 +754,13 @@ public void EW_OnClientRestricted(int client, int target, int length)
|
||||
Discord_POST(DISCORD_ENTWATCH_WEBHOOKURL, sMessage, true, sUsername, true, g_sAvatarURL[target], false);
|
||||
else
|
||||
Discord_POST(DISCORD_ENTWATCH_WEBHOOKURL, sMessage, true, sUsername, false, "", false);
|
||||
}
|
||||
|
||||
Call_StartForward(g_hForwardDiscord);
|
||||
Call_PushString(sUsername);
|
||||
Call_PushString(sMessage);
|
||||
Call_PushCell(3);
|
||||
Call_Finish();
|
||||
}
|
||||
|
||||
public void EW_OnClientUnrestricted(int client, int target)
|
||||
{
|
||||
@@ -878,4 +786,10 @@ public void EW_OnClientUnrestricted(int client, int target)
|
||||
Discord_POST(DISCORD_ENTWATCH_WEBHOOKURL, sMessage, true, sUsername, true, g_sAvatarURL[target], false);
|
||||
else
|
||||
Discord_POST(DISCORD_ENTWATCH_WEBHOOKURL, sMessage, true, sUsername, false, "", false);
|
||||
|
||||
Call_StartForward(g_hForwardDiscord);
|
||||
Call_PushString(sUsername);
|
||||
Call_PushString(sMessage);
|
||||
Call_PushCell(3);
|
||||
Call_Finish();
|
||||
}
|
||||
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
#if defined _Discord_UNLOZE_Included
|
||||
#endinput
|
||||
#endif
|
||||
#define _Discord_UNLOZE_Included
|
||||
|
||||
/**
|
||||
|
||||
* @noreturn
|
||||
*/
|
||||
forward void GetDiscordToStoat(const char[] username, const char[] message, int type);
|
||||
|
||||
|
||||
/* Do not edit below this line */
|
||||
public SharedPlugin __pl_Discord_UNLOZE =
|
||||
{
|
||||
name = "Discord_UNLOZE",
|
||||
file = "Discord_UNLOZE.smx",
|
||||
#if defined REQUIRE_PLUGIN
|
||||
required = 1,
|
||||
#else
|
||||
required = 0,
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,296 @@
|
||||
#pragma semicolon 1
|
||||
|
||||
#include <ripext>
|
||||
#include <basecomm>
|
||||
#include <multicolors>
|
||||
#include <sourcemod>
|
||||
#include <Discord_UNLOZE>
|
||||
#include <connect_announce>
|
||||
|
||||
#define API_URL ""
|
||||
#define BOT_TOKEN ""
|
||||
|
||||
|
||||
int i_port = 0;
|
||||
|
||||
public Plugin myinfo =
|
||||
{
|
||||
name = "livechat stoat",
|
||||
author = "jenz",
|
||||
description = "",
|
||||
version = "1.2.0",
|
||||
url = "www.unloze.com"
|
||||
}
|
||||
|
||||
public void OnMapStart()
|
||||
{
|
||||
CreateTimer(5.0, SendMapNotificationToStoat);
|
||||
}
|
||||
|
||||
|
||||
public Action SendMapNotificationToStoat(Handle timer)
|
||||
{
|
||||
if (i_port == 27015)
|
||||
{
|
||||
char sText[1024];
|
||||
char sCurrentmap[64];
|
||||
GetCurrentMap(sCurrentmap, sizeof(sCurrentmap));
|
||||
int NumPlayers = GetClientCount(false);
|
||||
char serverIp[32];
|
||||
ConVar ip_cvar = FindConVar("ip");
|
||||
ip_cvar.GetString(serverIp, sizeof(serverIp));
|
||||
|
||||
Format(sText, sizeof(sText), "Map changed to: %s\nPlayerCount: %i/65\nQuick Join: %s:%i", sCurrentmap, NumPlayers, serverIp, i_port);
|
||||
SendToMapNotifications_endpoint(sText);
|
||||
}
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
void SendToMapNotifications_endpoint(const char[] msg) {
|
||||
HTTPRequest request = new HTTPRequest("");
|
||||
request.SetHeader("Content-Type", "application/json");
|
||||
|
||||
char content[1024];
|
||||
Format(content, sizeof(content), "**%s**", msg);
|
||||
|
||||
JSONObject data = new JSONObject();
|
||||
data.SetString("content", content);
|
||||
|
||||
request.Post(data, OnHTTPResponse);
|
||||
delete data;
|
||||
}
|
||||
|
||||
public void OnPluginStart()
|
||||
{
|
||||
i_port = GetConVarInt(FindConVar("hostport"));
|
||||
AddCommandListener(CommandListener_SmChat_stoat, "say");
|
||||
|
||||
RegServerCmd("sm_printtoallchat_stoat", Command_PrintToAllChat_stoat, "Stoat Integration");
|
||||
RegServerCmd("sm_printtoadminchat_stoat", Command_PrintToAdminChat_stoat, "Stoat Integration");
|
||||
}
|
||||
|
||||
public Action CommandListener_SmChat_stoat(int client, const char[] sCommand, int argc)
|
||||
{
|
||||
if (client <= 0)
|
||||
return Plugin_Continue;
|
||||
|
||||
if (!IsValidClient(client))
|
||||
{
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
char sText[256];
|
||||
char sUsername[32];
|
||||
|
||||
GetCmdArgString(sText, sizeof(sText));
|
||||
GetClientName(client, sUsername, sizeof(sUsername));
|
||||
|
||||
if (FindCharInString(sText, '!') > 0)
|
||||
{
|
||||
return Plugin_Continue;
|
||||
}
|
||||
if (FindCharInString(sText, '/') > 0)
|
||||
{
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
if (sText[0] == '/')
|
||||
{
|
||||
return Plugin_Continue;
|
||||
}
|
||||
if (sText[0] == '!')
|
||||
{
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
//LogMessage("sText: %s. sText[0]: %c", sText, sText[0]);
|
||||
|
||||
if (i_port == 27015)
|
||||
{
|
||||
SendToStoat(sUsername, sText, "");
|
||||
}
|
||||
else if (i_port == 27017)
|
||||
{
|
||||
SendToStoat(sUsername, sText, "");
|
||||
}
|
||||
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
void SendToStoat(const char[] name, const char[] msg, const char[] channel_id) {
|
||||
char formatted_api[1024];
|
||||
Format(formatted_api, sizeof(formatted_api), "%s%s%s", API_URL, channel_id, "/messages");
|
||||
|
||||
HTTPRequest request = new HTTPRequest(formatted_api);
|
||||
request.SetHeader("x-bot-token", BOT_TOKEN);
|
||||
request.SetHeader("Content-Type", "application/json");
|
||||
|
||||
char content[1024];
|
||||
Format(content, sizeof(content), "**%s**: %s", name, msg);
|
||||
|
||||
JSONObject data = new JSONObject();
|
||||
data.SetString("content", content);
|
||||
|
||||
request.Post(data, OnHTTPResponse);
|
||||
delete data;
|
||||
}
|
||||
|
||||
void OnHTTPResponse(HTTPResponse response, any value, const char[] error) {
|
||||
/*
|
||||
if (strlen(error) > 0)
|
||||
{
|
||||
LogError("HTTP request failed: %s", error);
|
||||
return;
|
||||
}
|
||||
|
||||
int status = response.Status;
|
||||
|
||||
if (status != HTTPStatus_OK && status != HTTPStatus_Created)
|
||||
{
|
||||
LogError("Unexpected HTTP status: %d", status);
|
||||
|
||||
// Optionally log the response body to see what the server returned
|
||||
JSONObject body = view_as<JSONObject>(response.Data);
|
||||
if (body != null)
|
||||
{
|
||||
char bodyStr[1024];
|
||||
body.ToString(bodyStr, sizeof(bodyStr));
|
||||
LogError("Response body: %s", bodyStr);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogMessage("Message sent successfully, status: %d", status);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
public Action Command_PrintToAllChat_stoat(int args)
|
||||
{
|
||||
char sArgs[1024];
|
||||
char sArgs2[1024];
|
||||
|
||||
GetCmdArg(1, sArgs, sizeof(sArgs));
|
||||
GetCmdArg(2, sArgs2, sizeof(sArgs2));
|
||||
|
||||
CPrintToChatAll("{azure}[Stoat] (ALL) %s: {white}%s", sArgs, sArgs2);
|
||||
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
public Action Command_PrintToAdminChat_stoat(int args)
|
||||
{
|
||||
char sArgs[1024];
|
||||
char sArgs2[1024];
|
||||
|
||||
GetCmdArg(1, sArgs, sizeof(sArgs));
|
||||
GetCmdArg(2, sArgs2, sizeof(sArgs2));
|
||||
|
||||
for(int i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (IsValidClient(i))
|
||||
{
|
||||
bool bAdmin = CheckCommandAccess(i, "", ADMFLAG_GENERIC, true);
|
||||
if (bAdmin)
|
||||
CPrintToChat(i, "{green}(Stoat ADMINS) %s: {yellow}%s", sArgs, sArgs2);
|
||||
}
|
||||
}
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
//ai SLOP
|
||||
void StripColorTags(char[] buffer, int maxlen)
|
||||
{
|
||||
int len = strlen(buffer);
|
||||
int writeIndex = 0;
|
||||
bool inside = false;
|
||||
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
if (buffer[i] == '{')
|
||||
{
|
||||
inside = true;
|
||||
continue;
|
||||
}
|
||||
else if (buffer[i] == '}')
|
||||
{
|
||||
inside = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!inside && writeIndex < maxlen - 1)
|
||||
{
|
||||
buffer[writeIndex] = buffer[i];
|
||||
writeIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
buffer[writeIndex] = '\0';
|
||||
}
|
||||
|
||||
public void GetPlayerConnectMessage(char[] message)
|
||||
{
|
||||
StripColorTags(message, strlen(message));
|
||||
if (i_port == 27015)
|
||||
{
|
||||
SendToStoat("", message, "");
|
||||
}
|
||||
else if (i_port == 27017)
|
||||
{
|
||||
SendToStoat("", message, "");
|
||||
}
|
||||
}
|
||||
|
||||
public void GetDiscordToStoat(const char[] username, const char[] message, int type)
|
||||
{
|
||||
//type = 1. ADMINLOGS
|
||||
//type = 2. ADMIN LOGS BHOPPING ZE
|
||||
//type = 3. AMDIN LOGS entwatch
|
||||
//type = 4. admin logs ze ban evasion
|
||||
//type = 5. Calladmin
|
||||
//type = 6. admin chat ze
|
||||
|
||||
if (i_port == 27017)
|
||||
{
|
||||
if (type == 1)
|
||||
{
|
||||
SendToStoat(username, message, "");
|
||||
}
|
||||
else if (type == 2)
|
||||
{
|
||||
SendToStoat(username, message, "");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == 1)
|
||||
{
|
||||
SendToStoat(username, message, "");
|
||||
}
|
||||
else if (type == 2)
|
||||
{
|
||||
SendToStoat(username, message, "");
|
||||
}
|
||||
else if (type == 3)
|
||||
{
|
||||
SendToStoat(username, message, "");
|
||||
}
|
||||
else if (type == 4)
|
||||
{
|
||||
SendToStoat(username, message, "");
|
||||
}
|
||||
else if (type == 5)
|
||||
{
|
||||
SendToStoat(username, message, "");
|
||||
}
|
||||
else if(type == 6)
|
||||
{
|
||||
SendToStoat(username, message, "");
|
||||
}
|
||||
}
|
||||
|
||||
stock bool IsValidClient(int client)
|
||||
{
|
||||
return (client > 0 && client <= MaxClients && IsClientInGame(client));
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,587 @@
|
||||
#include <sourcemod>
|
||||
#include <ripext>
|
||||
#include <geoip>
|
||||
|
||||
#define EVENTNOTIFIER_CORE
|
||||
#include <EventNotifier>
|
||||
|
||||
#pragma semicolon 1
|
||||
#pragma newdecls required
|
||||
|
||||
#define PLUGIN_VERSION "1.1"
|
||||
|
||||
#define TIMEZONE_FILE "configs/eventnotifier_timezones.cfg"
|
||||
#define TIMEZONE_NAME_LEN 64
|
||||
|
||||
enum struct TzPeriod
|
||||
{
|
||||
int start; // unix timestamp this offset applies from
|
||||
int offset; // seconds east of UTC
|
||||
char abbr[12];
|
||||
}
|
||||
|
||||
ConVar g_cvUrl;
|
||||
ConVar g_cvServer;
|
||||
ConVar g_cvInterval;
|
||||
ConVar g_cvFallbackZone;
|
||||
|
||||
ArrayList g_Events;
|
||||
Handle g_hRefreshTimer;
|
||||
bool g_bFetching;
|
||||
bool g_bEverFetched;
|
||||
|
||||
GlobalForward g_fwdOnEventsUpdated;
|
||||
|
||||
// Zone name -> ArrayList of TzPeriod, ascending by start.
|
||||
StringMap g_Timezones;
|
||||
char g_sClientZone[MAXPLAYERS + 1][TIMEZONE_NAME_LEN];
|
||||
|
||||
public Plugin myinfo =
|
||||
{
|
||||
name = "EventNotifier",
|
||||
author = "marisakirisame.net",
|
||||
description = "Fetches upcoming Unloze events and exposes them to other plugins",
|
||||
version = PLUGIN_VERSION,
|
||||
url = "https://marisakirisame.net"
|
||||
};
|
||||
|
||||
public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max)
|
||||
{
|
||||
CreateNative("EventNotifier_GetEventCount", Native_GetEventCount);
|
||||
CreateNative("EventNotifier_HasFetched", Native_HasFetched);
|
||||
CreateNative("EventNotifier_FindEventByNumber", Native_FindEventByNumber);
|
||||
CreateNative("EventNotifier_GetEvent", Native_GetEvent);
|
||||
CreateNative("EventNotifier_Refresh", Native_Refresh);
|
||||
CreateNative("EventNotifier_FormatClientTime", Native_FormatClientTime);
|
||||
RegPluginLibrary("EventNotifier");
|
||||
return APLRes_Success;
|
||||
}
|
||||
|
||||
public void OnPluginStart()
|
||||
{
|
||||
g_Events = new ArrayList(sizeof(EventInfo));
|
||||
g_fwdOnEventsUpdated = new GlobalForward("EventNotifier_OnEventsUpdated", ET_Ignore);
|
||||
|
||||
g_cvUrl = CreateConVar("sm_event_url", "https://marisakirisame.net/unloze-events.json",
|
||||
"Base URL of the Unloze events API (query parameters are appended automatically)");
|
||||
g_cvServer = CreateConVar("sm_event_server", "ze",
|
||||
"Value of the 'server' query parameter sent to the events API");
|
||||
g_cvInterval = CreateConVar("sm_event_refresh_interval", "1800",
|
||||
"How often to re-fetch the event list, in seconds", _, true, 30.0);
|
||||
g_cvFallbackZone = CreateConVar("sm_event_fallback_timezone", "UTC",
|
||||
"Timezone (IANA name, e.g. Europe/London) used for players whose timezone cannot be looked up");
|
||||
|
||||
g_cvUrl.AddChangeHook(OnSourceChanged);
|
||||
g_cvServer.AddChangeHook(OnSourceChanged);
|
||||
g_cvInterval.AddChangeHook(OnIntervalChanged);
|
||||
|
||||
AutoExecConfig(true, "EventNotifier");
|
||||
|
||||
RegAdminCmd("sm_event_refresh", Command_EventRefresh, ADMFLAG_GENERIC, "Re-fetches the event list now");
|
||||
|
||||
LoadTimezones();
|
||||
|
||||
// Late load: look up everyone already on the server.
|
||||
for (int client = 1; client <= MaxClients; client++)
|
||||
{
|
||||
if (IsClientConnected(client))
|
||||
OnClientConnected(client);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnClientConnected(int client)
|
||||
{
|
||||
g_sClientZone[client][0] = '\0';
|
||||
if (IsFakeClient(client))
|
||||
return;
|
||||
|
||||
char ip[32];
|
||||
if (GetClientIP(client, ip, sizeof(ip)) && !GeoipTimezone(ip, g_sClientZone[client], sizeof(g_sClientZone[])))
|
||||
g_sClientZone[client][0] = '\0';
|
||||
}
|
||||
|
||||
public void OnClientDisconnect(int client)
|
||||
{
|
||||
g_sClientZone[client][0] = '\0';
|
||||
}
|
||||
|
||||
public void OnConfigsExecuted()
|
||||
{
|
||||
RestartRefreshTimer();
|
||||
FetchEvents();
|
||||
}
|
||||
|
||||
public void OnSourceChanged(ConVar convar, const char[] oldValue, const char[] newValue)
|
||||
{
|
||||
FetchEvents();
|
||||
}
|
||||
|
||||
public void OnIntervalChanged(ConVar convar, const char[] oldValue, const char[] newValue)
|
||||
{
|
||||
RestartRefreshTimer();
|
||||
}
|
||||
|
||||
void RestartRefreshTimer()
|
||||
{
|
||||
delete g_hRefreshTimer;
|
||||
g_hRefreshTimer = CreateTimer(g_cvInterval.FloatValue, Timer_Refresh, _, TIMER_REPEAT);
|
||||
}
|
||||
|
||||
public Action Timer_Refresh(Handle timer)
|
||||
{
|
||||
FetchEvents();
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Natives
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
public any Native_GetEventCount(Handle plugin, int numParams)
|
||||
{
|
||||
return g_Events.Length;
|
||||
}
|
||||
|
||||
public any Native_HasFetched(Handle plugin, int numParams)
|
||||
{
|
||||
return g_bEverFetched;
|
||||
}
|
||||
|
||||
public any Native_FindEventByNumber(Handle plugin, int numParams)
|
||||
{
|
||||
return FindEventByNumber(GetNativeCell(1));
|
||||
}
|
||||
|
||||
public any Native_GetEvent(Handle plugin, int numParams)
|
||||
{
|
||||
int index = GetNativeCell(1);
|
||||
int size = GetNativeCell(3);
|
||||
|
||||
if (size < sizeof(EventInfo))
|
||||
return ThrowNativeError(SP_ERROR_NATIVE, "EventInfo buffer too small (%d < %d), recompile against the current EventNotifier.inc", size, sizeof(EventInfo));
|
||||
|
||||
if (index < 0 || index >= g_Events.Length)
|
||||
return false;
|
||||
|
||||
EventInfo ev;
|
||||
g_Events.GetArray(index, ev);
|
||||
SetNativeArray(2, ev, sizeof(EventInfo));
|
||||
return true;
|
||||
}
|
||||
|
||||
public any Native_Refresh(Handle plugin, int numParams)
|
||||
{
|
||||
FetchEvents();
|
||||
return 0;
|
||||
}
|
||||
|
||||
public any Native_FormatClientTime(Handle plugin, int numParams)
|
||||
{
|
||||
int client = GetNativeCell(1);
|
||||
if (client < 0 || client > MaxClients)
|
||||
return ThrowNativeError(SP_ERROR_NATIVE, "Invalid client index %d", client);
|
||||
|
||||
int maxlen = GetNativeCell(4);
|
||||
char[] buffer = new char[maxlen];
|
||||
FormatClientTime(client, GetNativeCell(2), buffer, maxlen, GetNativeCell(5));
|
||||
SetNativeString(3, buffer, maxlen);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Fetching
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
void FetchEvents()
|
||||
{
|
||||
if (g_bFetching)
|
||||
return;
|
||||
|
||||
char url[256];
|
||||
g_cvUrl.GetString(url, sizeof(url));
|
||||
if (!url[0])
|
||||
{
|
||||
LogError("sm_event_url is empty, cannot fetch events");
|
||||
return;
|
||||
}
|
||||
|
||||
char server[64];
|
||||
g_cvServer.GetString(server, sizeof(server));
|
||||
|
||||
HTTPRequest request = new HTTPRequest(url);
|
||||
request.AppendQueryParam("server", "%s", server);
|
||||
request.AppendQueryParam("future", "1");
|
||||
request.SetHeader("Accept", "application/json");
|
||||
request.Timeout = 20;
|
||||
|
||||
g_bFetching = true;
|
||||
request.Get(OnEventsFetched);
|
||||
}
|
||||
|
||||
public void OnEventsFetched(HTTPResponse response, any value, const char[] error)
|
||||
{
|
||||
g_bFetching = false;
|
||||
|
||||
if (error[0])
|
||||
{
|
||||
LogError("Event fetch failed: %s", error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (response.Status != HTTPStatus_OK)
|
||||
{
|
||||
LogError("Event fetch failed: HTTP %d", response.Status);
|
||||
return;
|
||||
}
|
||||
|
||||
JSON data = response.Data;
|
||||
if (data == null)
|
||||
{
|
||||
LogError("Event fetch failed: response was not valid JSON");
|
||||
return;
|
||||
}
|
||||
|
||||
JSONObject root = view_as<JSONObject>(data);
|
||||
if (!root.HasKey("events") || root.IsNull("events"))
|
||||
{
|
||||
LogError("Event fetch failed: response has no 'events' array");
|
||||
return;
|
||||
}
|
||||
|
||||
JSONArray events = view_as<JSONArray>(root.Get("events"));
|
||||
ArrayList parsed = new ArrayList(sizeof(EventInfo));
|
||||
|
||||
int count = events.Length;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
if (events.IsNull(i))
|
||||
continue;
|
||||
|
||||
JSONObject obj = view_as<JSONObject>(events.Get(i));
|
||||
EventInfo ev;
|
||||
ParseEvent(obj, ev);
|
||||
delete obj;
|
||||
|
||||
parsed.PushArray(ev);
|
||||
}
|
||||
delete events;
|
||||
|
||||
// Soonest first so "the next N events" means something regardless of API order.
|
||||
parsed.SortCustom(SortEventsByStart);
|
||||
|
||||
int previous = g_Events.Length;
|
||||
delete g_Events;
|
||||
g_Events = parsed;
|
||||
|
||||
if (!g_bEverFetched || previous != g_Events.Length)
|
||||
LogMessage("Fetched %d upcoming event(s)", g_Events.Length);
|
||||
g_bEverFetched = true;
|
||||
|
||||
Call_StartForward(g_fwdOnEventsUpdated);
|
||||
Call_Finish();
|
||||
}
|
||||
|
||||
// Ascending by start time, with unknown (0) start times last.
|
||||
int SortEventsByStart(int index1, int index2, Handle array, Handle hndl)
|
||||
{
|
||||
ArrayList list = view_as<ArrayList>(array);
|
||||
int a = list.Get(index1, EventInfo::start);
|
||||
int b = list.Get(index2, EventInfo::start);
|
||||
|
||||
if (a == b)
|
||||
return 0;
|
||||
if (a == 0)
|
||||
return 1;
|
||||
if (b == 0)
|
||||
return -1;
|
||||
return (a < b) ? -1 : 1;
|
||||
}
|
||||
|
||||
void ParseEvent(JSONObject obj, EventInfo ev)
|
||||
{
|
||||
char buf[64];
|
||||
|
||||
ev.number = (obj.HasKey("eventNumber") && !obj.IsNull("eventNumber")) ? obj.GetInt("eventNumber") : 0;
|
||||
|
||||
GetJsonString(obj, "name", ev.name, sizeof(ev.name));
|
||||
if (!ev.name[0])
|
||||
GetJsonString(obj, "title", ev.name, sizeof(ev.name));
|
||||
|
||||
GetJsonString(obj, "server", ev.server, sizeof(ev.server));
|
||||
GetJsonString(obj, "rewards", ev.rewards, sizeof(ev.rewards));
|
||||
GetJsonString(obj, "leader", ev.leader, sizeof(ev.leader));
|
||||
GetJsonString(obj, "kickers", ev.kickers, sizeof(ev.kickers));
|
||||
|
||||
ev.start = 0;
|
||||
if (GetJsonString(obj, "start", buf, sizeof(buf)))
|
||||
ev.start = ParseIso8601(buf);
|
||||
|
||||
ev.end = 0;
|
||||
if (GetJsonString(obj, "end", buf, sizeof(buf)))
|
||||
ev.end = ParseIso8601(buf);
|
||||
|
||||
ev.maps[0] = '\0';
|
||||
if (obj.HasKey("maps") && !obj.IsNull("maps"))
|
||||
{
|
||||
JSONArray maps = view_as<JSONArray>(obj.Get("maps"));
|
||||
int len = maps.Length;
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
char map[128];
|
||||
if (!maps.GetString(i, map, sizeof(map)) || !map[0])
|
||||
continue;
|
||||
|
||||
if (ev.maps[0])
|
||||
StrCat(ev.maps, sizeof(ev.maps), ", ");
|
||||
StrCat(ev.maps, sizeof(ev.maps), map);
|
||||
}
|
||||
delete maps;
|
||||
}
|
||||
}
|
||||
|
||||
bool GetJsonString(JSONObject obj, const char[] key, char[] buffer, int maxlen)
|
||||
{
|
||||
buffer[0] = '\0';
|
||||
if (!obj.HasKey(key) || obj.IsNull(key))
|
||||
return false;
|
||||
return obj.GetString(key, buffer, maxlen) && buffer[0] != '\0';
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Commands
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
public Action Command_EventRefresh(int client, int args)
|
||||
{
|
||||
if (g_bFetching)
|
||||
{
|
||||
ReplyToCommand(client, "[Events] A fetch is already in progress.");
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
FetchEvents();
|
||||
ReplyToCommand(client, "[Events] Refreshing the event list.");
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
int FindEventByNumber(int number)
|
||||
{
|
||||
int count = g_Events.Length;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
if (g_Events.Get(i, EventInfo::number) == number)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Timezones
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// Loads the zone table generated by tools/gen_timezones.py.
|
||||
void LoadTimezones()
|
||||
{
|
||||
g_Timezones = new StringMap();
|
||||
|
||||
char path[PLATFORM_MAX_PATH];
|
||||
BuildPath(Path_SM, path, sizeof(path), TIMEZONE_FILE);
|
||||
|
||||
KeyValues kv = new KeyValues("Timezones");
|
||||
if (!kv.ImportFromFile(path))
|
||||
{
|
||||
LogError("Could not read %s, all times will be shown in UTC", path);
|
||||
delete kv;
|
||||
return;
|
||||
}
|
||||
|
||||
if (kv.GotoFirstSubKey())
|
||||
{
|
||||
do
|
||||
{
|
||||
char zone[TIMEZONE_NAME_LEN];
|
||||
kv.GetSectionName(zone, sizeof(zone));
|
||||
|
||||
ArrayList periods = new ArrayList(sizeof(TzPeriod));
|
||||
if (kv.GotoFirstSubKey(false))
|
||||
{
|
||||
do
|
||||
{
|
||||
char key[16], value[32], parts[2][16];
|
||||
kv.GetSectionName(key, sizeof(key));
|
||||
kv.GetString(NULL_STRING, value, sizeof(value));
|
||||
if (ExplodeString(value, " ", parts, sizeof(parts), sizeof(parts[])) != 2)
|
||||
continue;
|
||||
|
||||
TzPeriod period;
|
||||
period.start = StringToInt(key);
|
||||
period.offset = StringToInt(parts[0]);
|
||||
strcopy(period.abbr, sizeof(period.abbr), parts[1]);
|
||||
periods.PushArray(period);
|
||||
} while (kv.GotoNextKey(false));
|
||||
kv.GoBack();
|
||||
}
|
||||
|
||||
if (periods.Length > 0)
|
||||
{
|
||||
periods.SortCustom(SortPeriodsByStart);
|
||||
g_Timezones.SetValue(zone, periods);
|
||||
}
|
||||
else
|
||||
{
|
||||
delete periods;
|
||||
}
|
||||
} while (kv.GotoNextKey());
|
||||
}
|
||||
delete kv;
|
||||
|
||||
LogMessage("Loaded %d timezone(s)", g_Timezones.Size);
|
||||
}
|
||||
|
||||
int SortPeriodsByStart(int index1, int index2, Handle array, Handle hndl)
|
||||
{
|
||||
ArrayList list = view_as<ArrayList>(array);
|
||||
int a = list.Get(index1, TzPeriod::start);
|
||||
int b = list.Get(index2, TzPeriod::start);
|
||||
return (a == b) ? 0 : ((a < b) ? -1 : 1);
|
||||
}
|
||||
|
||||
// Finds the offset and abbreviation in effect at stamp for the client's zone,
|
||||
// falling back to sm_event_fallback_timezone and then plain UTC.
|
||||
void GetClientZonePeriod(int client, int stamp, TzPeriod out)
|
||||
{
|
||||
ArrayList periods;
|
||||
if (client <= 0 || !g_sClientZone[client][0] || !g_Timezones.GetValue(g_sClientZone[client], periods))
|
||||
{
|
||||
char fallback[TIMEZONE_NAME_LEN];
|
||||
g_cvFallbackZone.GetString(fallback, sizeof(fallback));
|
||||
if (!g_Timezones.GetValue(fallback, periods))
|
||||
periods = null;
|
||||
}
|
||||
|
||||
if (periods == null)
|
||||
{
|
||||
out.start = 0;
|
||||
out.offset = 0;
|
||||
strcopy(out.abbr, sizeof(out.abbr), "UTC");
|
||||
return;
|
||||
}
|
||||
|
||||
periods.GetArray(0, out);
|
||||
int count = periods.Length;
|
||||
for (int i = 1; i < count && periods.Get(i, TzPeriod::start) <= stamp; i++)
|
||||
periods.GetArray(i, out);
|
||||
}
|
||||
|
||||
// Formats stamp in the client's timezone as "Thu 01 Oct 2026, 20:00 BST"
|
||||
// (or without the year). FormatTime can't be used, it only knows the server's zone.
|
||||
void FormatClientTime(int client, int stamp, char[] buffer, int maxlen, bool withYear)
|
||||
{
|
||||
static const char weekdays[][] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
|
||||
static const char months[][] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
|
||||
|
||||
TzPeriod period;
|
||||
GetClientZonePeriod(client, stamp, period);
|
||||
|
||||
int local = stamp + period.offset;
|
||||
int days = local / 86400;
|
||||
int secs = local % 86400;
|
||||
|
||||
int year, month, day;
|
||||
CivilFromDays(days, year, month, day);
|
||||
|
||||
// 1970-01-01 was a Thursday.
|
||||
int weekday = (days + 4) % 7;
|
||||
|
||||
if (withYear)
|
||||
Format(buffer, maxlen, "%s %02d %s %d, %02d:%02d %s", weekdays[weekday], day, months[month - 1], year, secs / 3600, (secs % 3600) / 60, period.abbr);
|
||||
else
|
||||
Format(buffer, maxlen, "%s %02d %s, %02d:%02d %s", weekdays[weekday], day, months[month - 1], secs / 3600, (secs % 3600) / 60, period.abbr);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Time helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// Parses dates into a UTC unix timestamp.
|
||||
// Returns 0 when the string cannot be parsed.
|
||||
int ParseIso8601(const char[] str)
|
||||
{
|
||||
int year, month, day, hour, minute, second;
|
||||
if (!ReadFixedInt(str, 0, 4, year) || str[4] != '-'
|
||||
|| !ReadFixedInt(str, 5, 2, month) || str[7] != '-'
|
||||
|| !ReadFixedInt(str, 8, 2, day))
|
||||
return 0;
|
||||
|
||||
int pos = 10;
|
||||
if (str[pos] == 'T' || str[pos] == ' ')
|
||||
{
|
||||
if (!ReadFixedInt(str, 11, 2, hour) || str[13] != ':' || !ReadFixedInt(str, 14, 2, minute))
|
||||
return 0;
|
||||
pos = 16;
|
||||
if (str[pos] == ':')
|
||||
{
|
||||
if (!ReadFixedInt(str, 17, 2, second))
|
||||
return 0;
|
||||
pos = 19;
|
||||
}
|
||||
if (str[pos] == '.')
|
||||
{
|
||||
pos++;
|
||||
while (IsCharNumeric(str[pos]))
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
|
||||
int offset = 0;
|
||||
if (str[pos] == '+' || str[pos] == '-')
|
||||
{
|
||||
int sign = (str[pos] == '-') ? -1 : 1;
|
||||
int offHour, offMinute;
|
||||
if (!ReadFixedInt(str, pos + 1, 2, offHour))
|
||||
return 0;
|
||||
int minPos = (str[pos + 3] == ':') ? pos + 4 : pos + 3;
|
||||
ReadFixedInt(str, minPos, 2, offMinute);
|
||||
offset = sign * (offHour * 3600 + offMinute * 60);
|
||||
}
|
||||
|
||||
int days = DaysFromCivil(year, month, day);
|
||||
return days * 86400 + hour * 3600 + minute * 60 + second - offset;
|
||||
}
|
||||
|
||||
bool ReadFixedInt(const char[] str, int pos, int digits, int &out)
|
||||
{
|
||||
out = 0;
|
||||
for (int i = 0; i < digits; i++)
|
||||
{
|
||||
if (!IsCharNumeric(str[pos + i]))
|
||||
return false;
|
||||
out = out * 10 + (str[pos + i] - '0');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Days since 1970-01-01 for a proper date.
|
||||
int DaysFromCivil(int y, int m, int d)
|
||||
{
|
||||
y -= (m <= 2) ? 1 : 0;
|
||||
int era = ((y >= 0) ? y : y - 399) / 400;
|
||||
int yoe = y - era * 400;
|
||||
int doy = (153 * (m + ((m > 2) ? -3 : 9)) + 2) / 5 + d - 1;
|
||||
int doe = yoe * 365 + yoe / 4 - yoe / 100 + doy;
|
||||
return era * 146097 + doe - 719468;
|
||||
}
|
||||
|
||||
// Inverse of DaysFromCivil: date for a day count since 1970-01-01.
|
||||
void CivilFromDays(int z, int &y, int &m, int &d)
|
||||
{
|
||||
z += 719468;
|
||||
int era = ((z >= 0) ? z : z - 146096) / 146097;
|
||||
int doe = z - era * 146097;
|
||||
int yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
|
||||
int doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
|
||||
int mp = (5 * doy + 2) / 153;
|
||||
d = doy - (153 * mp + 2) / 5 + 1;
|
||||
m = (mp < 10) ? mp + 3 : mp - 9;
|
||||
y = yoe + era * 400 + ((m <= 2) ? 1 : 0);
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
#include <sourcemod>
|
||||
#include <multicolors>
|
||||
#include <EventNotifier>
|
||||
|
||||
#pragma semicolon 1
|
||||
#pragma newdecls required
|
||||
|
||||
#define PLUGIN_VERSION "1.1"
|
||||
|
||||
// Shortest automatic announcement interval, in seconds. Values below this are
|
||||
// raised to it; 0 or below disables the timer entirely.
|
||||
#define MIN_ANNOUNCE_INTERVAL 60.0
|
||||
|
||||
// Visible characters kept from the event name in chat.
|
||||
#define ANNOUNCE_NAME_MAX 64
|
||||
// Visible characters allowed for the map list before " +N more" is appended.
|
||||
#define ANNOUNCE_MAPS_BUDGET 120
|
||||
|
||||
ConVar g_cvInterval;
|
||||
ConVar g_cvCount;
|
||||
|
||||
Handle g_hAnnounceTimer;
|
||||
int g_iRotation;
|
||||
|
||||
public Plugin myinfo =
|
||||
{
|
||||
name = "EventNotifier Announcements",
|
||||
author = "marisakirisame.net",
|
||||
description = "Periodically announces upcoming Unloze events in chat",
|
||||
version = PLUGIN_VERSION,
|
||||
url = "https://marisakirisame.net"
|
||||
};
|
||||
|
||||
public void OnPluginStart()
|
||||
{
|
||||
g_cvInterval = CreateConVar("sm_event_announce_interval", "120",
|
||||
"Seconds between event announcements in chat, -1 to disable automatic announcements (sm_event_announce still works)", _, true, -1.0);
|
||||
g_cvCount = CreateConVar("sm_event_announce_count", "3",
|
||||
"Rotate announcements through the next N upcoming events, one per announcement", _, true, 1.0, true, 20.0);
|
||||
|
||||
// CreateConVar keeps the old bounds when the cvar already exists (plugin reload),
|
||||
// so apply the lower bound explicitly.
|
||||
g_cvInterval.SetBounds(ConVarBound_Lower, true, -1.0);
|
||||
g_cvInterval.AddChangeHook(OnIntervalChanged);
|
||||
|
||||
AutoExecConfig(true, "EventNotifierAnnouncements");
|
||||
|
||||
RegAdminCmd("sm_event_announce", Command_Announce, ADMFLAG_GENERIC, "Posts the next event announcement in chat now");
|
||||
}
|
||||
|
||||
public void OnConfigsExecuted()
|
||||
{
|
||||
RestartAnnounceTimer();
|
||||
}
|
||||
|
||||
public void OnIntervalChanged(ConVar convar, const char[] oldValue, const char[] newValue)
|
||||
{
|
||||
RestartAnnounceTimer();
|
||||
}
|
||||
|
||||
void RestartAnnounceTimer()
|
||||
{
|
||||
delete g_hAnnounceTimer;
|
||||
|
||||
float interval = g_cvInterval.FloatValue;
|
||||
if (interval <= 0.0)
|
||||
return;
|
||||
|
||||
if (interval < MIN_ANNOUNCE_INTERVAL)
|
||||
interval = MIN_ANNOUNCE_INTERVAL;
|
||||
|
||||
g_hAnnounceTimer = CreateTimer(interval, Timer_Announce, _, TIMER_REPEAT);
|
||||
}
|
||||
|
||||
public Action Timer_Announce(Handle timer)
|
||||
{
|
||||
PostAnnouncement();
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
public Action Command_Announce(int client, int args)
|
||||
{
|
||||
if (PostAnnouncement())
|
||||
ReplyToCommand(client, "[Events] Announcement posted.");
|
||||
else
|
||||
ReplyToCommand(client, "[Events] No upcoming events to announce.");
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Announcement
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// Posts one event from the rotation window. Returns false when there is nothing to post.
|
||||
bool PostAnnouncement()
|
||||
{
|
||||
int count = EventNotifier_GetEventCount();
|
||||
if (count <= 0)
|
||||
return false;
|
||||
|
||||
int window = g_cvCount.IntValue;
|
||||
if (window > count)
|
||||
window = count;
|
||||
|
||||
int index = g_iRotation % window;
|
||||
g_iRotation = (index + 1) % window;
|
||||
|
||||
EventInfo ev;
|
||||
if (!EventNotifier_GetEvent(index, ev))
|
||||
return false;
|
||||
|
||||
char name[ANNOUNCE_NAME_MAX + 1];
|
||||
strcopy(name, sizeof(name), ev.name);
|
||||
if (!name[0])
|
||||
strcopy(name, sizeof(name), "Unnamed event");
|
||||
|
||||
if (ev.start <= 0)
|
||||
{
|
||||
CPrintToChatAll("{darkorange}[Events] {yellow}#%d: {lightgreen}%s {grey}date TBA", ev.number, name);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Each player gets the start time in their own timezone.
|
||||
bool started = GetTime() >= ev.start;
|
||||
for (int client = 1; client <= MaxClients; client++)
|
||||
{
|
||||
if (!IsClientInGame(client) || IsFakeClient(client))
|
||||
continue;
|
||||
|
||||
char when[48];
|
||||
EventNotifier_FormatClientTime(client, ev.start, when, sizeof(when), false);
|
||||
// CPrintToChatEx rather than CPrintToChat: the latter only accepts string format arguments.
|
||||
CPrintToChatEx(client, client, "{darkorange}[Events] {yellow}#%d: {lightgreen}%s %s %s", ev.number, name,
|
||||
started ? "{orchid}started" : "{white}starts{lime}", when);
|
||||
}
|
||||
}
|
||||
|
||||
if (ev.maps[0])
|
||||
{
|
||||
char mapsLine[ANNOUNCE_MAPS_BUDGET + 132];
|
||||
int more = BuildMapsLine(ev.maps, mapsLine, sizeof(mapsLine), ANNOUNCE_MAPS_BUDGET);
|
||||
if (more > 0)
|
||||
CPrintToChatAll("{darkorange}[Events] {white}Maps: {lightgreen}%s {grey}+%d more", mapsLine, more);
|
||||
else
|
||||
CPrintToChatAll("{darkorange}[Events] {white}Maps: {lightgreen}%s", mapsLine);
|
||||
}
|
||||
|
||||
CPrintToChatAll("{darkorange}[Events] {white}Type {lime}!event %d {white}for details and the full map list.", ev.number);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Copies ", "-separated items from maps into out while the visible length stays
|
||||
// within budget. Always emits at least the first item. Returns how many items
|
||||
// were left out (0 when everything fit).
|
||||
int BuildMapsLine(const char[] maps, char[] out, int maxlen, int budget)
|
||||
{
|
||||
// Count from the raw string so "+N more" is right even past the explode limit.
|
||||
int total = 1;
|
||||
for (int i = 0; maps[i]; i++)
|
||||
{
|
||||
if (maps[i] == ',' && maps[i + 1] == ' ')
|
||||
total++;
|
||||
}
|
||||
|
||||
char parts[32][128];
|
||||
int count = ExplodeString(maps, ", ", parts, sizeof(parts), sizeof(parts[]));
|
||||
|
||||
out[0] = '\0';
|
||||
int used = 0;
|
||||
int shown = 0;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
int add = strlen(parts[i]) + ((shown > 0) ? 2 : 0);
|
||||
if (shown > 0 && used + add > budget)
|
||||
break;
|
||||
|
||||
if (shown > 0)
|
||||
StrCat(out, maxlen, ", ");
|
||||
StrCat(out, maxlen, parts[i]);
|
||||
used += add;
|
||||
shown++;
|
||||
}
|
||||
|
||||
return total - shown;
|
||||
}
|
||||
@@ -0,0 +1,231 @@
|
||||
#include <sourcemod>
|
||||
#include <EventNotifier>
|
||||
|
||||
#pragma semicolon 1
|
||||
#pragma newdecls required
|
||||
|
||||
#define PLUGIN_VERSION "1.2"
|
||||
#define CHAT_PREFIX "\x04[Events]\x01"
|
||||
|
||||
#define PANEL_LINE_WIDTH 60
|
||||
|
||||
#define KEY_BACK 8
|
||||
#define KEY_EXIT 10
|
||||
|
||||
public Plugin myinfo =
|
||||
{
|
||||
name = "EventNotifier Menu",
|
||||
author = "marisakirisame.net",
|
||||
description = "Shows upcoming Unloze events in-game via sm_event",
|
||||
version = PLUGIN_VERSION,
|
||||
url = "https://marisakirisame.net"
|
||||
};
|
||||
|
||||
public void OnPluginStart()
|
||||
{
|
||||
RegConsoleCmd("sm_event", Command_EventMenu, "Shows upcoming Unloze events. Usage: sm_event [event number]");
|
||||
RegConsoleCmd("sm_events", Command_EventMenu, "Shows upcoming Unloze events. Usage: sm_events [event number]");
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Commands
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
public Action Command_EventMenu(int client, int args)
|
||||
{
|
||||
if (client <= 0 || !IsClientInGame(client))
|
||||
{
|
||||
ReplyToCommand(client, "This command can only be used in-game.");
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
if (EventNotifier_GetEventCount() == 0)
|
||||
{
|
||||
if (EventNotifier_HasFetched())
|
||||
PrintToChat(client, "%s There are no upcoming events.", CHAT_PREFIX);
|
||||
else
|
||||
PrintToChat(client, "%s The event list is not available right now, try again later.", CHAT_PREFIX);
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
if (args >= 1)
|
||||
{
|
||||
char arg[16];
|
||||
GetCmdArg(1, arg, sizeof(arg));
|
||||
int number = StringToInt(arg);
|
||||
int index = EventNotifier_FindEventByNumber(number);
|
||||
if (index == -1)
|
||||
{
|
||||
PrintToChat(client, "%s Event #%d is not in the upcoming list.", CHAT_PREFIX, number);
|
||||
return Plugin_Handled;
|
||||
}
|
||||
ShowEventDetails(client, index);
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
ShowEventList(client);
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Menus
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
void ShowEventList(int client)
|
||||
{
|
||||
Menu menu = new Menu(EventList_Handler);
|
||||
menu.SetTitle("Upcoming Unloze events");
|
||||
|
||||
int count = EventNotifier_GetEventCount();
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
EventInfo ev;
|
||||
if (!EventNotifier_GetEvent(i, ev))
|
||||
continue;
|
||||
|
||||
char info[16], display[EVENT_STR_LEN + 64];
|
||||
IntToString(ev.number, info, sizeof(info));
|
||||
Format(display, sizeof(display), "%d: %s", ev.number, ev.name);
|
||||
if (ev.start > 0)
|
||||
{
|
||||
char when[48];
|
||||
EventNotifier_FormatClientTime(client, ev.start, when, sizeof(when), false);
|
||||
Format(display, sizeof(display), "%s (%s)", display, when);
|
||||
}
|
||||
menu.AddItem(info, display);
|
||||
}
|
||||
|
||||
menu.ExitButton = true;
|
||||
menu.Display(client, MENU_TIME_FOREVER);
|
||||
}
|
||||
|
||||
public int EventList_Handler(Menu menu, MenuAction action, int param1, int param2)
|
||||
{
|
||||
switch (action)
|
||||
{
|
||||
case MenuAction_Select:
|
||||
{
|
||||
char info[16];
|
||||
menu.GetItem(param2, info, sizeof(info));
|
||||
|
||||
int index = EventNotifier_FindEventByNumber(StringToInt(info));
|
||||
if (index == -1)
|
||||
{
|
||||
PrintToChat(param1, "%s That event is no longer in the upcoming list.", CHAT_PREFIX);
|
||||
ShowEventList(param1);
|
||||
}
|
||||
else
|
||||
{
|
||||
ShowEventDetails(param1, index);
|
||||
}
|
||||
}
|
||||
case MenuAction_End:
|
||||
{
|
||||
delete menu;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ShowEventDetails(int client, int index)
|
||||
{
|
||||
EventInfo ev;
|
||||
if (!EventNotifier_GetEvent(index, ev))
|
||||
{
|
||||
PrintToChat(client, "%s That event is no longer in the upcoming list.", CHAT_PREFIX);
|
||||
return;
|
||||
}
|
||||
|
||||
char line[EVENT_STR_LEN + 32];
|
||||
Panel panel = new Panel();
|
||||
|
||||
Format(line, sizeof(line), "Event #%d: %s", ev.number, ev.name);
|
||||
panel.SetTitle(line);
|
||||
panel.DrawText(" ");
|
||||
|
||||
if (ev.start > 0)
|
||||
{
|
||||
char when[64];
|
||||
EventNotifier_FormatClientTime(client, ev.start, when, sizeof(when));
|
||||
Format(line, sizeof(line), "Date: %s", when);
|
||||
panel.DrawText(line);
|
||||
|
||||
EventNotifier_FormatRelativeTime(ev.start, ev.end, line, sizeof(line));
|
||||
panel.DrawText(line);
|
||||
}
|
||||
else
|
||||
{
|
||||
panel.DrawText("Date: unknown");
|
||||
}
|
||||
|
||||
Format(line, sizeof(line), "Server: %s", ev.server[0] ? ev.server : "unknown");
|
||||
panel.DrawText(line);
|
||||
|
||||
if (ev.maps[0])
|
||||
DrawWrappedList(panel, "Maps: ", ev.maps);
|
||||
else
|
||||
panel.DrawText("Maps: none listed");
|
||||
|
||||
Format(line, sizeof(line), "Rewards: %s", ev.rewards[0] ? ev.rewards : "none");
|
||||
panel.DrawText(line);
|
||||
Format(line, sizeof(line), "Leader: %s", ev.leader[0] ? ev.leader : "none");
|
||||
panel.DrawText(line);
|
||||
Format(line, sizeof(line), "Kickers: %s", ev.kickers[0] ? ev.kickers : "none");
|
||||
panel.DrawText(line);
|
||||
|
||||
panel.DrawText(" ");
|
||||
panel.CurrentKey = KEY_BACK;
|
||||
panel.DrawItem("Back");
|
||||
panel.CurrentKey = KEY_EXIT;
|
||||
panel.DrawItem("Exit");
|
||||
|
||||
panel.Send(client, EventDetails_Handler, MENU_TIME_FOREVER);
|
||||
delete panel;
|
||||
}
|
||||
|
||||
public int EventDetails_Handler(Menu menu, MenuAction action, int param1, int param2)
|
||||
{
|
||||
if (action == MenuAction_Select && param2 == KEY_BACK)
|
||||
{
|
||||
if (IsClientInGame(param1))
|
||||
Command_EventMenu(param1, 0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Draws "prefix a, b, c" and continues onto new indented lines when a line
|
||||
// would exceed PANEL_LINE_WIDTH characters, splitting only at the commas.
|
||||
void DrawWrappedList(Panel panel, const char[] prefix, const char[] list)
|
||||
{
|
||||
char parts[32][128];
|
||||
int count = ExplodeString(list, ", ", parts, sizeof(parts), sizeof(parts[]));
|
||||
|
||||
char line[PANEL_LINE_WIDTH + 160];
|
||||
strcopy(line, sizeof(line), prefix);
|
||||
bool lineHasItems = false;
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
char item[132];
|
||||
strcopy(item, sizeof(item), parts[i]);
|
||||
if (i < count - 1)
|
||||
StrCat(item, sizeof(item), ",");
|
||||
|
||||
if (lineHasItems && strlen(line) + 1 + strlen(item) > PANEL_LINE_WIDTH)
|
||||
{
|
||||
panel.DrawText(line);
|
||||
strcopy(line, sizeof(line), " ");
|
||||
lineHasItems = false;
|
||||
}
|
||||
else if (lineHasItems)
|
||||
{
|
||||
StrCat(line, sizeof(line), " ");
|
||||
}
|
||||
|
||||
StrCat(line, sizeof(line), item);
|
||||
lineHasItems = true;
|
||||
}
|
||||
|
||||
if (lineHasItems)
|
||||
panel.DrawText(line);
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
#if defined _EventNotifier_included
|
||||
#endinput
|
||||
#endif
|
||||
|
||||
#define _EventNotifier_included
|
||||
|
||||
#define EVENT_STR_LEN 128
|
||||
#define EVENT_MAPS_LEN 1024
|
||||
|
||||
enum struct EventInfo
|
||||
{
|
||||
int number;
|
||||
int start; // unix timestamp (UTC), 0 if unknown
|
||||
int end; // unix timestamp (UTC), 0 if unknown
|
||||
char name[EVENT_STR_LEN];
|
||||
char server[EVENT_STR_LEN];
|
||||
char rewards[EVENT_STR_LEN];
|
||||
char leader[EVENT_STR_LEN];
|
||||
char kickers[EVENT_STR_LEN];
|
||||
char maps[EVENT_MAPS_LEN]; // ", "-joined list
|
||||
}
|
||||
|
||||
#if !defined EVENTNOTIFIER_CORE
|
||||
|
||||
/**
|
||||
* Number of events in the last successful fetch (0 before the first fetch).
|
||||
*
|
||||
* @return Event count.
|
||||
*/
|
||||
native int EventNotifier_GetEventCount();
|
||||
|
||||
/**
|
||||
* Whether at least one fetch has succeeded since the plugin loaded.
|
||||
*
|
||||
* @return True once the event list has been fetched.
|
||||
*/
|
||||
native bool EventNotifier_HasFetched();
|
||||
|
||||
/**
|
||||
* Finds an event by its event number.
|
||||
*
|
||||
* @param number Event number.
|
||||
* @return Index into the event list, or -1 if not found.
|
||||
*/
|
||||
native int EventNotifier_FindEventByNumber(int number);
|
||||
|
||||
/**
|
||||
* Copies an event into the caller's EventInfo.
|
||||
*
|
||||
* @param index Index into the event list.
|
||||
* @param ev EventInfo to fill.
|
||||
* @param size Size of ev, must be sizeof(EventInfo).
|
||||
* @return True on success, false if index is out of range.
|
||||
* @error Buffer smaller than sizeof(EventInfo).
|
||||
*/
|
||||
native bool EventNotifier_GetEvent(int index, any[] ev, int size = sizeof(EventInfo));
|
||||
|
||||
/**
|
||||
* Starts a fetch of the event list now. No-op if a fetch is already running.
|
||||
*/
|
||||
native void EventNotifier_Refresh();
|
||||
|
||||
/**
|
||||
* Formats a timestamp in the client's own timezone (looked up via GeoIP),
|
||||
* e.g. "Thu 01 Oct 2026, 20:00 BST". Clients whose timezone is unknown get
|
||||
* sm_event_fallback_timezone.
|
||||
*
|
||||
* @param client Client index, 0 for the fallback timezone.
|
||||
* @param timestamp Unix timestamp (UTC).
|
||||
* @param buffer Destination buffer.
|
||||
* @param maxlen Size of buffer.
|
||||
* @param withYear Whether to include the year.
|
||||
* @error Invalid client index.
|
||||
*/
|
||||
native void EventNotifier_FormatClientTime(int client, int timestamp, char[] buffer, int maxlen, bool withYear = true);
|
||||
|
||||
/**
|
||||
* Called after every successful fetch, once the event list has been replaced.
|
||||
*/
|
||||
forward void EventNotifier_OnEventsUpdated();
|
||||
|
||||
#endif // EVENTNOTIFIER_CORE
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Display helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Formats a duration in seconds as "1d 2h 3m", "2h 3m" or "3m".
|
||||
*/
|
||||
stock void EventNotifier_FormatDuration(int seconds, char[] buffer, int maxlen)
|
||||
{
|
||||
int days = seconds / 86400;
|
||||
int hours = (seconds % 86400) / 3600;
|
||||
int minutes = (seconds % 3600) / 60;
|
||||
|
||||
if (days > 0)
|
||||
Format(buffer, maxlen, "%dd %dh %dm", days, hours, minutes);
|
||||
else if (hours > 0)
|
||||
Format(buffer, maxlen, "%dh %dm", hours, minutes);
|
||||
else
|
||||
Format(buffer, maxlen, "%dm", minutes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats an event's status relative to now as a "Status: ..." line.
|
||||
*/
|
||||
stock void EventNotifier_FormatRelativeTime(int start, int end, char[] buffer, int maxlen)
|
||||
{
|
||||
int now = GetTime();
|
||||
|
||||
if (end > 0 && now >= start && now < end)
|
||||
{
|
||||
strcopy(buffer, maxlen, "Status: In progress now!");
|
||||
return;
|
||||
}
|
||||
|
||||
char span[32];
|
||||
if (now >= start)
|
||||
{
|
||||
EventNotifier_FormatDuration(now - start, span, sizeof(span));
|
||||
Format(buffer, maxlen, "Status: Started %s ago", span);
|
||||
}
|
||||
else
|
||||
{
|
||||
EventNotifier_FormatDuration(start - now, span, sizeof(span));
|
||||
Format(buffer, maxlen, "Status: Starts in %s", span);
|
||||
}
|
||||
}
|
||||
|
||||
#if !defined EVENTNOTIFIER_CORE
|
||||
|
||||
public SharedPlugin __pl_EventNotifier =
|
||||
{
|
||||
name = "EventNotifier",
|
||||
file = "EventNotifier.smx",
|
||||
|
||||
#if defined REQUIRE_PLUGIN
|
||||
required = 1
|
||||
#else
|
||||
required = 0
|
||||
#endif
|
||||
};
|
||||
|
||||
#if !defined REQUIRE_PLUGIN
|
||||
public void __pl_EventNotifier_SetNTVOptional()
|
||||
{
|
||||
MarkNativeAsOptional("EventNotifier_GetEventCount");
|
||||
MarkNativeAsOptional("EventNotifier_HasFetched");
|
||||
MarkNativeAsOptional("EventNotifier_FindEventByNumber");
|
||||
MarkNativeAsOptional("EventNotifier_GetEvent");
|
||||
MarkNativeAsOptional("EventNotifier_Refresh");
|
||||
MarkNativeAsOptional("EventNotifier_FormatClientTime");
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // EVENTNOTIFIER_CORE
|
||||
@@ -0,0 +1,87 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Generates configs/eventnotifier_timezones.cfg from the system tzdata.
|
||||
|
||||
Each zone lists "<utc timestamp>" "<offset seconds> <abbreviation>" pairs; the
|
||||
"0" entry is the offset in effect before the first transition in range.
|
||||
Regenerate before END_YEAR runs out (32-bit timestamps end in 2038 anyway).
|
||||
"""
|
||||
|
||||
import re
|
||||
import zoneinfo
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from pathlib import Path
|
||||
|
||||
START_YEAR = 2025
|
||||
END_YEAR = 2037
|
||||
|
||||
OUT = Path(__file__).resolve().parent.parent / "configs" / "eventnotifier_timezones.cfg"
|
||||
|
||||
|
||||
def utc(ts):
|
||||
return datetime.fromtimestamp(ts, timezone.utc)
|
||||
|
||||
|
||||
def state(zone, ts):
|
||||
local = utc(ts).astimezone(zone)
|
||||
return int(local.utcoffset().total_seconds()), local.tzname()
|
||||
|
||||
|
||||
def abbreviation(name, offset):
|
||||
# tzdata uses numeric names like "+03" / "-0330" for zones without a real abbreviation.
|
||||
if name and re.fullmatch(r"[A-Za-z]+", name):
|
||||
return name
|
||||
sign = "+" if offset >= 0 else "-"
|
||||
hours, rem = divmod(abs(offset), 3600)
|
||||
minutes = rem // 60
|
||||
if hours == 0 and minutes == 0:
|
||||
return "UTC"
|
||||
return f"UTC{sign}{hours}:{minutes:02d}" if minutes else f"UTC{sign}{hours}"
|
||||
|
||||
|
||||
def transitions(zone):
|
||||
start = int(datetime(START_YEAR, 1, 1, tzinfo=timezone.utc).timestamp())
|
||||
end = int(datetime(END_YEAR, 12, 31, tzinfo=timezone.utc).timestamp())
|
||||
step = int(timedelta(days=1).total_seconds())
|
||||
|
||||
periods = [(0, *state(zone, start))]
|
||||
prev_ts, prev = start, periods[0][1:]
|
||||
ts = start + step
|
||||
while ts <= end:
|
||||
cur = state(zone, ts)
|
||||
if cur != prev:
|
||||
# Bisect to the first second of the new state.
|
||||
lo, hi = prev_ts, ts
|
||||
while hi - lo > 1:
|
||||
mid = (lo + hi) // 2
|
||||
if state(zone, mid) == prev:
|
||||
lo = mid
|
||||
else:
|
||||
hi = mid
|
||||
periods.append((hi, *state(zone, hi)))
|
||||
prev = cur
|
||||
prev_ts = ts
|
||||
ts += step
|
||||
return periods
|
||||
|
||||
|
||||
def main():
|
||||
lines = ["// Generated by tools/gen_timezones.py, do not edit by hand.", '"Timezones"', "{"]
|
||||
for name in sorted(zoneinfo.available_timezones()):
|
||||
if name.startswith(("posix/", "right/")) or name in ("Factory", "localtime"):
|
||||
continue
|
||||
zone = zoneinfo.ZoneInfo(name)
|
||||
lines.append(f'\t"{name}"')
|
||||
lines.append("\t{")
|
||||
for ts, offset, tzname in transitions(zone):
|
||||
lines.append(f'\t\t"{ts}"\t"{offset} {abbreviation(tzname, offset)}"')
|
||||
lines.append("\t}")
|
||||
lines.append("}")
|
||||
|
||||
OUT.parent.mkdir(parents=True, exist_ok=True)
|
||||
OUT.write_text("\n".join(lines) + "\n")
|
||||
print(f"Wrote {OUT}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -41,9 +41,21 @@ public void OnConVarQueryFinished(QueryCookie cookie, int client, ConVarQueryRes
|
||||
return;
|
||||
|
||||
if (result != ConVarQuery_Okay)
|
||||
LogError("Could not query ConVar \"cvarName\" for %N", client);
|
||||
LogError("Could not query ConVar fps_max for %N", client);
|
||||
|
||||
int len = strlen(cvarValue);
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
if (!IsCharNumeric(cvarValue[i]))
|
||||
{
|
||||
KickClient(client, "Invalid fps_max Format");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//fps_max 0 is probably acceptable
|
||||
if (StringToInt(cvarValue) == 0)
|
||||
return;
|
||||
if (StringToInt(cvarValue) < 58)
|
||||
KickClient(client, "Your fps_max is invalid, please set it to a higher value (>59)");
|
||||
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
HAFL-LIFE_2
|
||||
NoobDnoRaq
|
||||
IMBA
|
||||
ТАЩИР
|
||||
ebaw-kak-t34
|
||||
h4rdc0r3
|
||||
omfg]wiz
|
||||
GOdlikebAby
|
||||
SHITSHITSHIT
|
||||
-Impulse-
|
||||
SLAVA_UKRAINE
|
||||
4ik-pyk
|
||||
BigBoss
|
||||
lolkalalka
|
||||
muhahahahaha
|
||||
GFLClan.ru
|
||||
nide.gg
|
||||
vk.com/nide_css
|
||||
alex481516
|
||||
alahakbar
|
||||
HeadShot
|
||||
HolySHIT
|
||||
CRAY_ZEE
|
||||
mlpro
|
||||
Deadmemories
|
||||
MaGothic
|
||||
ILoveYouBaby
|
||||
nevazhno
|
||||
Tauren
|
||||
Calypso
|
||||
Hawk
|
||||
(1)HAFL-LIFE_2
|
||||
(1)NoobDnoRaq
|
||||
(1)Hawk
|
||||
(1)Tauren
|
||||
(1)mlpro
|
||||
(1)CRAY_ZEE
|
||||
(1)NF-CSS.RU
|
||||
(1)Calypso
|
||||
(1)BigBoss
|
||||
HAFL-LIFE_2
|
||||
NoobDnoRaq
|
||||
IMBA
|
||||
ТАЩИР
|
||||
ebaw-kak-t34
|
||||
h4rdc0r3
|
||||
omfg]wiz
|
||||
GOdlikebAby
|
||||
SHITSHITSHIT
|
||||
-Impulse-
|
||||
SLAVA_UKRAINE
|
||||
4ik-pyk
|
||||
BigBoss
|
||||
lolkalalka
|
||||
muhahahahaha
|
||||
cp.powerhub.ru
|
||||
NiDE.GG
|
||||
vk.com/nide_css
|
||||
alex481516
|
||||
alahakbar
|
||||
HeadShot
|
||||
HolySHIT
|
||||
CRAY_ZEE
|
||||
mlpro
|
||||
Deadmemories
|
||||
MaGothic
|
||||
ILoveYouBaby
|
||||
nevazhno
|
||||
Tauren
|
||||
Calypso
|
||||
Hawk
|
||||
(1)HAFL-LIFE_2
|
||||
(1)NoobDnoRaq
|
||||
(1)Hawk
|
||||
(1)Tauren
|
||||
(1)mlpro
|
||||
(1)CRAY_ZEE
|
||||
(1)NF-CSS.RU
|
||||
(1)Calypso
|
||||
(1)BigBoss
|
||||
@@ -1,114 +0,0 @@
|
||||
Dwelitram
|
||||
Gwyri
|
||||
Caredus
|
||||
Arerawia
|
||||
Vilali
|
||||
Astiwiel
|
||||
Vardonydd
|
||||
Ybaossa
|
||||
Umyk
|
||||
Nico50Pax
|
||||
Onand
|
||||
Thelian
|
||||
Nydaleth
|
||||
Chomarin
|
||||
Traedien
|
||||
Miev
|
||||
Kaaede
|
||||
Koamond
|
||||
TheRottenBenson
|
||||
BigLegend2017
|
||||
TRIGGEREDHarambexXx
|
||||
InPepe2016
|
||||
xXxMaster2012
|
||||
InBoixXx
|
||||
TheKopsing
|
||||
Cornelius
|
||||
Gustavo
|
||||
Bryant
|
||||
Winfred
|
||||
Nicolas
|
||||
Mitchel
|
||||
Dana
|
||||
Carrol
|
||||
Darell
|
||||
Ruben
|
||||
Jeromy
|
||||
Wade
|
||||
Scotty
|
||||
Salvatore
|
||||
Kory
|
||||
Don
|
||||
Morgan
|
||||
Kurtis
|
||||
Federico
|
||||
Darin
|
||||
css-ru
|
||||
aimbot
|
||||
lastkraftwagenfahrzeug
|
||||
edger
|
||||
clownface
|
||||
слово
|
||||
счастливый
|
||||
kara
|
||||
puta
|
||||
meow
|
||||
uncle sam
|
||||
FunBun
|
||||
Counter-Strike.Com.Ua
|
||||
For-css.Ru
|
||||
BOBO
|
||||
Z3r0
|
||||
ZeTo
|
||||
Sakharov
|
||||
Sache11
|
||||
Mr. Dogenberg
|
||||
Maus
|
||||
Magikarpet
|
||||
Miles
|
||||
magick
|
||||
James Lebron
|
||||
jiZZ
|
||||
Dobel
|
||||
THE POPE OF DOPE
|
||||
The Joker
|
||||
that guy over there
|
||||
Be happy :)
|
||||
KingKong
|
||||
Figger Nucker
|
||||
noname
|
||||
alexdu63
|
||||
I put babies in the microwave
|
||||
Tango!!
|
||||
faggot killer
|
||||
admin pidaras
|
||||
MESSI>RONALDO
|
||||
Gabe Newell
|
||||
acetylsalicylic acid
|
||||
Country-Steak: Sauce
|
||||
chlamydia harvester
|
||||
kyle_69
|
||||
Shaggy's dog
|
||||
rEVERSE
|
||||
Piment d'espelette
|
||||
FireHawk
|
||||
Sgt. Pepper
|
||||
krix.
|
||||
imPulse_^_
|
||||
Lardon
|
||||
CAPS LOCK
|
||||
$pussymoneyweed$
|
||||
Low5
|
||||
Bonerfart
|
||||
Shiny Thanos
|
||||
MARK NUTT
|
||||
Legless Runner
|
||||
Banana is a berry
|
||||
Gabriel
|
||||
El Moustachio
|
||||
James Bong
|
||||
Disco Fever
|
||||
Acacia
|
||||
Sugared
|
||||
ronald goddamn macdonald
|
||||
pizza is life
|
||||
@@ -0,0 +1,27 @@
|
||||
"FakeClientsTiers"
|
||||
{
|
||||
"0" "23"
|
||||
"3" "22"
|
||||
"6" "21"
|
||||
"9" "20"
|
||||
"12" "19"
|
||||
"15" "18"
|
||||
"18" "17"
|
||||
"21" "16"
|
||||
"24" "15"
|
||||
"27" "14"
|
||||
"30" "13"
|
||||
"33" "12"
|
||||
"36" "11"
|
||||
"39" "10"
|
||||
"42" "9"
|
||||
"45" "8"
|
||||
"48" "7"
|
||||
"51" "6"
|
||||
"54" "5"
|
||||
"55" "4"
|
||||
"57" "3"
|
||||
"60" "2"
|
||||
"61" "1"
|
||||
"62" "0"
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
"FakeClientsTiersTeam"
|
||||
{
|
||||
"0" "0"
|
||||
"10" "2"
|
||||
"14" "3"
|
||||
"18" "4"
|
||||
"22" "5"
|
||||
"26" "6"
|
||||
}
|
||||
@@ -1,28 +1,18 @@
|
||||
#include <sourcemod>
|
||||
#include <sdktools>
|
||||
#include <zombiereloaded>
|
||||
#include <BotTargeting>
|
||||
#include <cstrike>
|
||||
|
||||
#pragma semicolon 1
|
||||
#pragma newdecls required
|
||||
|
||||
#define MAXNAMES 500
|
||||
ArrayList g_hNames;
|
||||
ArrayList g_hClanNames;
|
||||
|
||||
bool g_bFakePopulation[MAXPLAYERS + 1];
|
||||
bool g_bMapEnded;
|
||||
|
||||
int g_iBaseLatency[MAXPLAYERS + 1];
|
||||
int g_iLatency[MAXPLAYERS + 1];
|
||||
int g_iPlayerCount;
|
||||
|
||||
int g_iAdminFakes;
|
||||
int g_iPopulation;
|
||||
|
||||
bool g_bCheckRequested;
|
||||
|
||||
bool g_bBlockInstantFakeConnects;
|
||||
bool g_bMapended;
|
||||
|
||||
Database g_hDatabase;
|
||||
Database g_hDatabase_hlstats;
|
||||
@@ -32,10 +22,10 @@ Database g_hDatabase_hlstats;
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public Plugin myinfo =
|
||||
{
|
||||
name = "ImprovedHitboxes", //camouflage
|
||||
name = "fakes",
|
||||
author = "Neon + Dogan + Botox",
|
||||
description = "Handle Hitboxes via Plugin",
|
||||
version = "5.3.1",
|
||||
version = "5.4.1",
|
||||
url = "https://steamcommunity.com/id/n3ontm"
|
||||
};
|
||||
|
||||
@@ -46,28 +36,19 @@ public void OnPluginStart()
|
||||
{
|
||||
RegAdminCmd("sm_debugfakes", Command_DebugFakes, ADMFLAG_GENERIC, "Shows the amount of fake-clients on server");
|
||||
RegAdminCmd("sm_fakes", Command_Fakes, ADMFLAG_GENERIC, "Shows the fake-clients on server");
|
||||
RegAdminCmd("sm_setfakes", Command_SetFakes, ADMFLAG_RCON, "Manually sets the amount of fake-clients");
|
||||
|
||||
int arraySize = ByteCountToCells(MAX_NAME_LENGTH);
|
||||
g_hNames = CreateArray(arraySize);
|
||||
g_hClanNames = CreateArray(arraySize);
|
||||
|
||||
g_iAdminFakes = -1;
|
||||
g_iPopulation = GetClientCount(false);
|
||||
for(int i = 1; i <= MaxClients; i++)
|
||||
{
|
||||
if(IsClientConnected(i) && IsFakeClient(i))
|
||||
g_iPopulation--;
|
||||
}
|
||||
g_bMapEnded = false;
|
||||
g_bBlockInstantFakeConnects = false;
|
||||
CreateTimer(40.0, BlockInstantFakeConnects, _, TIMER_REPEAT);
|
||||
CreateTimer(GetRandomFloat(20.0, 40.0), RunPopulationCheck, _, TIMER_REPEAT);
|
||||
CreateTimer(3.0, RandomizePing, _, TIMER_REPEAT);
|
||||
CreateTimer(150.0, RandomizeNames, _, TIMER_REPEAT);
|
||||
CreateTimer(600.0, RandomizeNames, _, TIMER_REPEAT);
|
||||
CreateTimer(80.0, CheckPopulation_interval, _, TIMER_REPEAT);
|
||||
|
||||
HookUserMessage(GetUserMessageId("SayText2"), UserMessage_SayText2, true);
|
||||
|
||||
RequestFrame(CheckPopulation);
|
||||
g_bMapended = false;
|
||||
|
||||
if (!g_hDatabase)
|
||||
{
|
||||
@@ -77,6 +58,7 @@ public void OnPluginStart()
|
||||
{
|
||||
Database.Connect(SQL_OnDatabaseConnect_hlstats, "hlstatsx");
|
||||
}
|
||||
g_iPlayerCount = 0;
|
||||
}
|
||||
|
||||
public void SQL_OnDatabaseConnect_hlstats(Database db, const char[] error, any data)
|
||||
@@ -93,7 +75,7 @@ public void SQL_OnDatabaseConnect_hlstats(Database db, const char[] error, any d
|
||||
public void randomize_clantags()
|
||||
{
|
||||
char sQuery[512];
|
||||
Format(sQuery, sizeof(sQuery), "select DISTINCT tag from unloze_stats.hlstats_Clans ORDER BY RAND() limit 500");
|
||||
Format(sQuery, sizeof(sQuery), "select DISTINCT tag from unloze_stats.hlstats_Clans ORDER BY RAND() limit 1000");
|
||||
g_hDatabase_hlstats.Query(SQL_OnQueryCompleted_hlstats, sQuery, DBPrio_High);
|
||||
}
|
||||
|
||||
@@ -111,7 +93,7 @@ public void SQL_OnDatabaseConnect(Database db, const char[] error, any data)
|
||||
public void randomize_names()
|
||||
{
|
||||
char sQuery[512];
|
||||
Format(sQuery, sizeof(sQuery), "select name from unloze_racetimer_css.random_names ORDER BY RAND() limit 500");
|
||||
Format(sQuery, sizeof(sQuery), "select name from unloze_racetimer_css.random_names ORDER BY RAND() limit 1000");
|
||||
g_hDatabase.Query(SQL_OnQueryCompleted, sQuery, DBPrio_High);
|
||||
}
|
||||
|
||||
@@ -159,20 +141,9 @@ public void SQL_OnQueryCompleted(Database db, DBResultSet results, const char[]
|
||||
delete results;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public void OnPluginEnd()
|
||||
public void OnMapEnd()
|
||||
{
|
||||
for(int i = 1; i <= MaxClients; i++)
|
||||
{
|
||||
if(g_bFakePopulation[i])
|
||||
{
|
||||
g_bFakePopulation[i] = false;
|
||||
g_iLatency[i] = 0;
|
||||
KickClient(i, "Disconnect by user.");
|
||||
}
|
||||
}
|
||||
g_bMapended = true;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
@@ -180,80 +151,174 @@ public void OnPluginEnd()
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public void OnMapStart()
|
||||
{
|
||||
g_hNames.Clear();
|
||||
g_hClanNames.Clear();
|
||||
if (!g_hDatabase)
|
||||
KeyValues kv = CreateKeyValues("ints_used_already");
|
||||
int fakes_needed = getFakesNeeded();
|
||||
for (float i = 0.0; i < fakes_needed;)
|
||||
{
|
||||
Database.Connect(SQL_OnDatabaseConnect, "racetimercss");
|
||||
int num = GetRandomInt(5, 45);
|
||||
char strnum[16];
|
||||
IntToString(num, strnum, sizeof(strnum));
|
||||
int found = kv.GetNum(strnum, -1);
|
||||
if (found != -1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
kv.SetNum(strnum, num);
|
||||
i += 1.0;
|
||||
|
||||
CreateTimer(float(num), addfakes);
|
||||
}
|
||||
else
|
||||
{
|
||||
randomize_names();
|
||||
}
|
||||
|
||||
if (!g_hDatabase_hlstats)
|
||||
{
|
||||
Database.Connect(SQL_OnDatabaseConnect_hlstats, "hlstatsx");
|
||||
}
|
||||
else
|
||||
{
|
||||
randomize_clantags();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
char sFile[PLATFORM_MAX_PATH];
|
||||
char sLine[MAX_NAME_LENGTH];
|
||||
BuildPath(Path_SM, sFile, sizeof(sFile), "configs/fakeclients_names.txt");
|
||||
Handle hFile = OpenFile(sFile, "r");
|
||||
|
||||
if(hFile != INVALID_HANDLE)
|
||||
{
|
||||
int iLine = 0;
|
||||
g_hNames.Clear();
|
||||
while (!IsEndOfFile(hFile))
|
||||
{
|
||||
if (!ReadFileLine(hFile, sLine, sizeof(sLine)) || iLine >= MAXNAMES)
|
||||
break;
|
||||
|
||||
TrimString(sLine);
|
||||
g_hNames.PushString(sLine);
|
||||
iLine++;
|
||||
}
|
||||
delete hFile;
|
||||
}
|
||||
else
|
||||
SetFailState("Could not open file: configs/fakeclients_names.txt");
|
||||
*/
|
||||
|
||||
g_bMapEnded = false;
|
||||
|
||||
g_bBlockInstantFakeConnects = false;
|
||||
delete kv;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public void OnMapEnd()
|
||||
public int getFakesNeededInTeam()
|
||||
{
|
||||
g_bMapEnded = true;
|
||||
g_iAdminFakes = -1;
|
||||
g_iPopulation = GetClientCount(false);
|
||||
//just moving them with CheckPopulation() over to a team as needed. we dont bother moving them back to spectate, mapchanges do that for us.
|
||||
char sPath[PLATFORM_MAX_PATH];
|
||||
BuildPath(Path_SM, sPath, sizeof(sPath), "configs/fakeclients_tiers_team.cfg");
|
||||
|
||||
KeyValues kv = new KeyValues("FakeClientsTiersTeam");
|
||||
if (!kv.ImportFromFile(sPath))
|
||||
{
|
||||
LogError("configs/fakeclients_tiers_team.cfg not found");
|
||||
delete kv;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!kv.GotoFirstSubKey(false))
|
||||
{
|
||||
LogError("configs/fakeclients_tiers_team.cfg is empty or malformed");
|
||||
delete kv;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int prev_bots = 0;
|
||||
do
|
||||
{
|
||||
char sKey[16], sVal[16];
|
||||
kv.GetSectionName(sKey, sizeof(sKey));
|
||||
kv.GetString(NULL_STRING, sVal, sizeof(sVal), "-1");
|
||||
|
||||
int bots = StringToInt(sVal);
|
||||
int players = StringToInt(sKey);
|
||||
if (players > g_iPlayerCount)
|
||||
{
|
||||
break;
|
||||
}
|
||||
prev_bots = bots;
|
||||
|
||||
} while (kv.GotoNextKey(false));
|
||||
|
||||
delete kv;
|
||||
//LogMessage("prev_bots: %i", prev_bots);
|
||||
return prev_bots;
|
||||
}
|
||||
|
||||
public int getFakesNeeded()
|
||||
{
|
||||
//copied from nide fakeclients plugin.
|
||||
char sPath[PLATFORM_MAX_PATH];
|
||||
BuildPath(Path_SM, sPath, sizeof(sPath), "configs/fakeclients_tiers.cfg");
|
||||
|
||||
KeyValues kv = new KeyValues("FakeClientsTiers");
|
||||
if (!kv.ImportFromFile(sPath))
|
||||
{
|
||||
LogError("configs/fakeclients_tiers.cfg not found — falling back to sm_fakeclients_players");
|
||||
delete kv;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!kv.GotoFirstSubKey(false))
|
||||
{
|
||||
LogError("configs/fakeclients_tiers.cfg is empty or malformed");
|
||||
delete kv;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int prev_bots = 0;
|
||||
do
|
||||
{
|
||||
char sKey[16], sVal[16];
|
||||
kv.GetSectionName(sKey, sizeof(sKey));
|
||||
kv.GetString(NULL_STRING, sVal, sizeof(sVal), "-1");
|
||||
|
||||
int bots = StringToInt(sVal);
|
||||
int players = StringToInt(sKey);
|
||||
if (players > g_iPlayerCount)
|
||||
{
|
||||
break;
|
||||
}
|
||||
prev_bots = bots;
|
||||
|
||||
} while (kv.GotoNextKey(false));
|
||||
|
||||
delete kv;
|
||||
//LogMessage("prev_bots: %i", prev_bots);
|
||||
return prev_bots;
|
||||
}
|
||||
|
||||
public Action addfakes(Handle timer)
|
||||
{
|
||||
int limit = getFakesNeeded();
|
||||
int fakes = 0;
|
||||
for(int i = 1; i <= MaxClients; i++)
|
||||
{
|
||||
if(IsClientConnected(i) && IsFakeClient(i))
|
||||
g_iPopulation--;
|
||||
if (IsClientConnected(i) && IsClientInGame(i) && IsClientAuthorized(i))
|
||||
{
|
||||
if (IsFakeClient(i) && !IsClientSourceTV(i))
|
||||
{
|
||||
fakes++;
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
if (nightTime())
|
||||
{
|
||||
limit /= 2;
|
||||
}
|
||||
*/
|
||||
|
||||
if (fakes < limit)
|
||||
{
|
||||
CreateFake();
|
||||
}
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
public Action CheckPopulation_interval(Handle timer)
|
||||
{
|
||||
int TimeLeft;
|
||||
if (GetMapTimeLeft(TimeLeft) && TimeLeft < 0)
|
||||
{
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
if (g_bMapended)
|
||||
{
|
||||
g_bMapended = false;
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
g_iPlayerCount = 0;
|
||||
for(int i = 1; i <= MaxClients; i++)
|
||||
{
|
||||
if (IsClientConnected(i) && IsClientInGame(i) && IsClientAuthorized(i))
|
||||
{
|
||||
if (!IsFakeClient(i) && !IsClientSourceTV(i))
|
||||
{
|
||||
g_iPlayerCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public Action BlockInstantFakeConnects(Handle timer)
|
||||
public Action RunPopulationCheck(Handle timer)
|
||||
{
|
||||
g_bBlockInstantFakeConnects = false;
|
||||
RequestFrame(CheckPopulation);
|
||||
|
||||
CheckPopulation();
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
@@ -264,7 +329,7 @@ public Action RandomizePing(Handle timer)
|
||||
{
|
||||
for(int i = 1; i <= MaxClients; i++)
|
||||
{
|
||||
if(g_bFakePopulation[i])
|
||||
if(IsClientConnected(i) && IsFakeClient(i) && !IsClientSourceTV(i))
|
||||
g_iLatency[i] = g_iBaseLatency[i] + GetRandomInt(-3, 3);
|
||||
}
|
||||
return Plugin_Continue;
|
||||
@@ -284,7 +349,7 @@ public Action RandomizeNames(Handle timer)
|
||||
ArrayList hClanNames = g_hClanNames.Clone();
|
||||
for(int i = 1; i <= MaxClients; i++)
|
||||
{
|
||||
if(g_bFakePopulation[i])
|
||||
if(IsClientConnected(i) && IsFakeClient(i) && !IsClientSourceTV(i))
|
||||
{
|
||||
int iRand = GetRandomInt(0, hNames.Length - 1);
|
||||
char sName[MAX_NAME_LENGTH];
|
||||
@@ -340,7 +405,7 @@ public Action UserMessage_SayText2(UserMsg msg_id, BfRead msg, const int[] playe
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
if(g_bFakePopulation[client])
|
||||
if(IsClientConnected(client) && IsFakeClient(client) && !IsClientSourceTV(client))
|
||||
return Plugin_Handled;
|
||||
|
||||
return Plugin_Continue;
|
||||
@@ -357,45 +422,20 @@ public Action Command_DebugFakes(int client, int argc)
|
||||
|
||||
for(int i = 1; i <= MaxClients; i++)
|
||||
{
|
||||
if (g_bFakePopulation[i])
|
||||
if (!IsClientConnected(i))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (IsFakeClient(i) && !IsClientSourceTV(i))
|
||||
{
|
||||
iFakes++;
|
||||
|
||||
if (g_bFakePopulation[i] && GetClientTeam(i) > 0)
|
||||
iFakesInTeam++;
|
||||
|
||||
if(IsClientConnected(i) && IsFakeClient(i))
|
||||
iPlayers--;
|
||||
}
|
||||
if (IsFakeClient(i) && GetClientTeam(i) > CS_TEAM_SPECTATOR)
|
||||
iFakesInTeam++;
|
||||
}
|
||||
|
||||
ReplyToCommand(client, "[SM] There are currently %d Fake-Clients, from which %d are in Spectate.", iFakes, iFakes - iFakesInTeam);
|
||||
ReplyToCommand(client, "[SM] Server Population at the end of the previous Map: %d.", g_iPopulation);
|
||||
ReplyToCommand(client, "[SM] Current Server Population: %d. Difference: %d.", iPlayers, iPlayers - g_iPopulation);
|
||||
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public Action Command_SetFakes(int client, int argc)
|
||||
{
|
||||
if (argc < 1)
|
||||
{
|
||||
ReplyToCommand(client, "[SM] Usage: sm_setfakes <amount of fakes>");
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
char sArgs[16];
|
||||
GetCmdArg(1, sArgs, sizeof(sArgs));
|
||||
|
||||
if (!StringToIntEx(sArgs, g_iAdminFakes))
|
||||
{
|
||||
ReplyToCommand(client, "[SM] Invalid value");
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
ReplyToCommand(client, "[SM] You set the amount of Fake-Clients to %d.", g_iAdminFakes);
|
||||
CheckPopulation();
|
||||
ReplyToCommand(client, "[SM] There are currently %d Fake-Clients, from which %d are in Spectate. Real Clients %d", iFakes, iFakes - iFakesInTeam, iPlayers);
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
@@ -411,7 +451,7 @@ public Action Command_Fakes(int client, int args)
|
||||
{
|
||||
if(IsClientInGame(i))
|
||||
{
|
||||
if(g_bFakePopulation[i])
|
||||
if(IsClientConnected(i) && IsFakeClient(i) && !IsClientSourceTV(i))
|
||||
{
|
||||
GetClientName(i, aBuf2, sizeof(aBuf2));
|
||||
StrCat(aBuf, sizeof(aBuf), aBuf2);
|
||||
@@ -431,18 +471,6 @@ public Action Command_Fakes(int client, int args)
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public void OnClientConnected(int client)
|
||||
{
|
||||
if (!g_bCheckRequested && !IsFakeClient(client))
|
||||
{
|
||||
RequestFrame(CheckPopulation);
|
||||
g_bCheckRequested = true;
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
@@ -450,225 +478,152 @@ public void OnClientDisconnect(int client)
|
||||
{
|
||||
if (client > 0)
|
||||
{
|
||||
if(g_bFakePopulation[client])
|
||||
if(IsFakeClient(client) && !IsClientSourceTV(client))
|
||||
{
|
||||
g_bFakePopulation[client] = false;
|
||||
g_iLatency[client] = 0;
|
||||
}
|
||||
|
||||
if (!g_bCheckRequested && !IsFakeClient(client))
|
||||
{
|
||||
RequestFrame(CheckPopulation);
|
||||
g_bCheckRequested = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Action repeatCheckPopulation(Handle timer)
|
||||
{
|
||||
CheckPopulation();
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public void CheckPopulation()
|
||||
{
|
||||
int fakes = 0;
|
||||
int last_fake = 0;
|
||||
for(int i = 1; i <= MaxClients; i++)
|
||||
{
|
||||
if (IsClientConnected(i) && IsClientInGame(i) && IsClientAuthorized(i))
|
||||
{
|
||||
if (IsFakeClient(i) && !IsClientSourceTV(i))
|
||||
{
|
||||
fakes++;
|
||||
last_fake = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
int fakes_needed = getFakesNeeded();
|
||||
/*
|
||||
if (nightTime())
|
||||
{
|
||||
fakes_needed /= 2;
|
||||
}
|
||||
*/
|
||||
|
||||
if (fakes > fakes_needed && last_fake != 0)
|
||||
{
|
||||
g_iLatency[last_fake] = 0;
|
||||
KickClient(last_fake, "Disconnect by user.");
|
||||
}
|
||||
else if (fakes < fakes_needed)
|
||||
{
|
||||
CreateFake();
|
||||
}
|
||||
|
||||
//moving fakes to a team
|
||||
int fakes_in_team = 0;
|
||||
for(int i = 1; i <= MaxClients; i++)
|
||||
{
|
||||
if (IsClientConnected(i) && IsClientInGame(i) && IsClientAuthorized(i))
|
||||
{
|
||||
if (IsFakeClient(i) && !IsClientSourceTV(i) && GetClientTeam(i) > CS_TEAM_SPECTATOR)
|
||||
{
|
||||
fakes_in_team++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (getFakesNeededInTeam() > fakes_in_team)
|
||||
{
|
||||
SetFakeToTeam();
|
||||
}
|
||||
}
|
||||
|
||||
public bool nightTime()
|
||||
{
|
||||
char sTime[8];
|
||||
FormatTime(sTime, sizeof(sTime), "%H%M");
|
||||
|
||||
int CurTime = StringToInt(sTime);
|
||||
int MinTime = 2300;
|
||||
int MaxTime = 400;
|
||||
|
||||
//Wrap around.
|
||||
CurTime = (CurTime <= MinTime) ? CurTime + 2400 : CurTime;
|
||||
MaxTime = (MaxTime <= MinTime) ? MaxTime + 2400 : MaxTime;
|
||||
|
||||
if ((MinTime <= CurTime <= MaxTime))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void SetFakeToTeam()
|
||||
{
|
||||
for(int i = 1; i <= MaxClients; i++)
|
||||
{
|
||||
if(IsClientConnected(i) && IsFakeClient(i) && !IsClientSourceTV(i) && GetClientTeam(i) <= CS_TEAM_SPECTATOR)
|
||||
{
|
||||
ChangeClientTeam(i, CS_TEAM_CT);
|
||||
FakeClientCommandEx(i, "joinclass");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void CreateFake()
|
||||
{
|
||||
if (g_hNames.Length == 0 || g_hClanNames.Length == 0)
|
||||
{
|
||||
CreateTimer(5.0, repeatCheckPopulation);
|
||||
return;
|
||||
}
|
||||
g_bCheckRequested = false;
|
||||
|
||||
if(g_bMapEnded)
|
||||
return;
|
||||
|
||||
int iPlayers = GetClientCount(false);
|
||||
|
||||
for(int i = 1; i <= MaxClients; i++)
|
||||
{
|
||||
if(IsClientConnected(i) && IsFakeClient(i))
|
||||
iPlayers--;
|
||||
}
|
||||
|
||||
int iFakes = 0;
|
||||
int iFakesInTeam = 0;
|
||||
|
||||
for(int i = 1; i <= MaxClients; i++)
|
||||
{
|
||||
if (g_bFakePopulation[i])
|
||||
iFakes++;
|
||||
}
|
||||
|
||||
int iFakesNeeded = 0;
|
||||
int iFakesInTeamNeeded = 0;
|
||||
|
||||
int max = 2;
|
||||
int min = 1;
|
||||
|
||||
if (iPlayers == 1)
|
||||
ArrayList hNames = g_hNames.Clone();
|
||||
char sName[MAX_NAME_LENGTH];
|
||||
for(int i = 1; i <= MaxClients; i++)
|
||||
{
|
||||
max = iPlayers * 5;
|
||||
min = 3;
|
||||
if(IsClientConnected(i) && IsFakeClient(i) && !IsClientSourceTV(i))
|
||||
{
|
||||
GetClientName(i, sName, sizeof(sName));
|
||||
int iPos = hNames.FindString(sName);
|
||||
if (iPos > -1)
|
||||
hNames.Erase(iPos);
|
||||
}
|
||||
}
|
||||
else if (iPlayers < 6)
|
||||
|
||||
int iRand = GetRandomInt(0, hNames.Length - 1);
|
||||
hNames.GetString(iRand, sName, sizeof(sName));
|
||||
delete hNames;
|
||||
|
||||
int iIndex = CreateFakeClient(sName);
|
||||
|
||||
if(iIndex < 1 || iIndex > MaxClients)
|
||||
return;
|
||||
|
||||
SetEntityFlags(iIndex, FL_CLIENT);
|
||||
DispatchKeyValue(iIndex, "classname", "player");
|
||||
DispatchSpawn(iIndex);
|
||||
|
||||
g_iBaseLatency[iIndex] = GetRandomInt(20, 110);
|
||||
g_iLatency[iIndex] = g_iBaseLatency[iIndex];
|
||||
|
||||
AdminId FakeAdmin = CreateAdmin();
|
||||
SetAdminFlag(FakeAdmin, Admin_Custom6, true);
|
||||
SetUserAdmin(iIndex, FakeAdmin, true);
|
||||
|
||||
if (GetRandomInt(0, 5) >= 3)
|
||||
{
|
||||
max = iPlayers * 5;
|
||||
min = iPlayers * 2;
|
||||
}
|
||||
else if (iPlayers < 11)
|
||||
{
|
||||
max = RoundToFloor(iPlayers * 1.5);
|
||||
min = iPlayers;
|
||||
}
|
||||
else if (iPlayers < 20)
|
||||
{
|
||||
max = iPlayers;
|
||||
min = RoundToFloor(iPlayers * 0.5);
|
||||
}
|
||||
else if (iPlayers < 26)
|
||||
{
|
||||
max = iPlayers;
|
||||
min = RoundToFloor(iPlayers * 0.3);
|
||||
}
|
||||
else if (iPlayers < 36)
|
||||
{
|
||||
max = RoundToFloor(iPlayers * 0.5);
|
||||
min = RoundToFloor(iPlayers * 0.3);
|
||||
ArrayList hClanNames = g_hClanNames.Clone();
|
||||
iRand = GetRandomInt(0, hClanNames.Length - 1);
|
||||
hClanNames.GetString(iRand, sName, sizeof(sName));
|
||||
delete hClanNames;
|
||||
CS_SetClientClanTag(iIndex, sName);
|
||||
}
|
||||
else
|
||||
{
|
||||
max = RoundToFloor(iPlayers * 0.3);
|
||||
min = RoundToFloor(iPlayers * 0.1);
|
||||
CS_SetClientClanTag(iIndex, "");
|
||||
}
|
||||
|
||||
iFakesNeeded = GetRandomInt(min, max);
|
||||
//iFakesNeeded = randomAmount - iPlayers;
|
||||
if (iFakesNeeded < 0 || iPlayers > 50)
|
||||
{
|
||||
iFakesNeeded = 0;
|
||||
iFakesInTeamNeeded = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
iFakesInTeamNeeded = iFakesNeeded / 3;
|
||||
}
|
||||
if (iFakesInTeamNeeded < 0)
|
||||
{
|
||||
iFakesInTeamNeeded = 0;
|
||||
}
|
||||
|
||||
if(g_iAdminFakes != -1)
|
||||
iFakesNeeded = g_iAdminFakes;
|
||||
|
||||
if (iFakes != iFakesNeeded)
|
||||
{
|
||||
while (iFakes < iFakesNeeded && !g_bBlockInstantFakeConnects)
|
||||
{
|
||||
ArrayList hNames = g_hNames.Clone();
|
||||
char sName[MAX_NAME_LENGTH];
|
||||
for(int i = 1; i <= MaxClients; i++)
|
||||
{
|
||||
if(g_bFakePopulation[i])
|
||||
{
|
||||
GetClientName(i, sName, sizeof(sName));
|
||||
int iPos = hNames.FindString(sName);
|
||||
if (iPos > -1)
|
||||
hNames.Erase(iPos);
|
||||
}
|
||||
}
|
||||
|
||||
int iRand = GetRandomInt(0, hNames.Length - 1);
|
||||
hNames.GetString(iRand, sName, sizeof(sName));
|
||||
delete hNames;
|
||||
|
||||
int iIndex = CreateFakeClient(sName);
|
||||
|
||||
if(iIndex < 1 || iIndex > MaxClients)
|
||||
return;
|
||||
|
||||
SetEntityFlags(iIndex, FL_CLIENT);
|
||||
DispatchKeyValue(iIndex, "classname", "player");
|
||||
DispatchSpawn(iIndex);
|
||||
|
||||
g_bFakePopulation[iIndex] = true;
|
||||
g_iBaseLatency[iIndex] = GetRandomInt(20, 110);
|
||||
g_iLatency[iIndex] = g_iBaseLatency[iIndex];
|
||||
|
||||
AdminId FakeAdmin = CreateAdmin();
|
||||
SetAdminFlag(FakeAdmin, Admin_Custom6, true);
|
||||
SetUserAdmin(iIndex, FakeAdmin, true);
|
||||
|
||||
if (GetRandomInt(0, 5) >= 3)
|
||||
{
|
||||
ArrayList hClanNames = g_hClanNames.Clone();
|
||||
iRand = GetRandomInt(0, hClanNames.Length - 1);
|
||||
hClanNames.GetString(iRand, sName, sizeof(sName));
|
||||
delete hClanNames;
|
||||
CS_SetClientClanTag(iIndex, sName);
|
||||
}
|
||||
else
|
||||
{
|
||||
CS_SetClientClanTag(iIndex, "");
|
||||
}
|
||||
iFakes++;
|
||||
|
||||
g_bBlockInstantFakeConnects = true;
|
||||
}
|
||||
|
||||
while (iFakes > iFakesNeeded)
|
||||
{
|
||||
for(int i = 1; i <= MaxClients; i++)
|
||||
{
|
||||
if(g_bFakePopulation[i])
|
||||
{
|
||||
g_bFakePopulation[i] = false;
|
||||
g_iLatency[i] = 0;
|
||||
KickClient(i, "Disconnect by user.");
|
||||
iFakes--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(int i = 1; i <= MaxClients; i++)
|
||||
{
|
||||
if (g_bFakePopulation[i] && GetClientTeam(i) >= CS_TEAM_T)
|
||||
iFakesInTeam++;
|
||||
}
|
||||
|
||||
if (iFakes == iFakesNeeded && iFakesInTeam != iFakesInTeamNeeded && g_iAdminFakes == -1)
|
||||
{
|
||||
while (iFakesInTeam < iFakesInTeamNeeded)
|
||||
{
|
||||
for(int i = 1; i <= MaxClients; i++)
|
||||
{
|
||||
if(g_bFakePopulation[i] && GetClientTeam(i) <= CS_TEAM_SPECTATOR)
|
||||
{
|
||||
ChangeClientTeam(i, CS_TEAM_CT);
|
||||
FakeClientCommandEx(i, "joinclass");
|
||||
iFakesInTeam++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (iFakesInTeam > iFakesInTeamNeeded)
|
||||
{
|
||||
for(int i = 1; i <= MaxClients; i++)
|
||||
{
|
||||
if(g_bFakePopulation[i] && GetClientTeam(i) >= CS_TEAM_T)
|
||||
{
|
||||
ChangeClientTeam(i, CS_TEAM_SPECTATOR);
|
||||
iFakesInTeam--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
@@ -678,7 +633,7 @@ public void OnGameFrame()
|
||||
{
|
||||
for(int i = 1; i <= MaxClients; i++)
|
||||
{
|
||||
if(g_bFakePopulation[i])
|
||||
if(IsClientConnected(i) && IsFakeClient(i) && !IsClientSourceTV(i))
|
||||
{
|
||||
int iResEnt = GetPlayerResourceEntity();
|
||||
|
||||
@@ -689,13 +644,3 @@ public void OnGameFrame()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
public Action ZR_OnClientMotherZombieEligible(int client)
|
||||
{
|
||||
if (g_bFakePopulation[client])
|
||||
return Plugin_Handled;
|
||||
|
||||
return Plugin_Continue;
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,480 @@
|
||||
#pragma semicolon 1
|
||||
#pragma newdecls required
|
||||
#include <sourcemod>
|
||||
#include <sdktools>
|
||||
#include <cstrike>
|
||||
|
||||
#define MAX_TIERS 32
|
||||
|
||||
ConVar g_hCount;
|
||||
ConVar g_hDelay;
|
||||
ConVar g_hUseTiers;
|
||||
ArrayList g_hNames;
|
||||
|
||||
int g_iTierThreshold[MAX_TIERS]; // real player count that triggers this tier
|
||||
int g_iTierMaxBots[MAX_TIERS]; // max bots allowed for this tier
|
||||
int g_iTierCount = 0;
|
||||
int g_iPendingBots = 0;
|
||||
|
||||
bool g_bUseTiers = false;
|
||||
|
||||
public Plugin myinfo = {
|
||||
name = "FakeClients",
|
||||
author = "Tsunami, .Rushaway",
|
||||
description = "Put fake clients in server with tier system",
|
||||
version = "3.0.0",
|
||||
url = "https://github.com/srcdslab/sm-plugin-FakeClients"
|
||||
}
|
||||
|
||||
public void OnPluginStart()
|
||||
{
|
||||
g_hCount = CreateConVar("sm_fakeclients_players", "8", "Fallback: number of bots when tier system is disabled", _, true, 0.0, true, 64.0);
|
||||
g_hDelay = CreateConVar("sm_fakeclients_delay", "120", "Delay after map change before fake clients join (seconds)", _, true, 0.0, true, 10000.0);
|
||||
g_hUseTiers = CreateConVar("sm_fakeclients_tiers", "0", "Use tier system from fakeclients_tiers.cfg (1 = enabled, 0 = disabled)", _, true, 0.0, true, 1.0);
|
||||
|
||||
g_bUseTiers = g_hUseTiers.BoolValue;
|
||||
g_hUseTiers.AddChangeHook(OnConVarChanged);
|
||||
|
||||
RegAdminCmd("sm_debugfakes", Command_DebugFakes, ADMFLAG_GENERIC, "Shows the amount of fake-clients on server");
|
||||
RegAdminCmd("sm_fakes", Command_Fakes, ADMFLAG_GENERIC, "Shows the fake-clients on server");
|
||||
|
||||
AutoExecConfig(true);
|
||||
}
|
||||
|
||||
public Action Command_DebugFakes(int client, int argc)
|
||||
{
|
||||
int iFakes = 0;
|
||||
int iFakesInTeam = 0;
|
||||
int iPlayers = GetClientCount(false);
|
||||
|
||||
for(int i = 1; i <= MaxClients; i++)
|
||||
{
|
||||
if (!IsClientConnected(i))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (IsFakeClient(i) || IsClientSourceTV(i))
|
||||
{
|
||||
iFakes++;
|
||||
iPlayers--;
|
||||
}
|
||||
if (IsFakeClient(i) && GetClientTeam(i) > CS_TEAM_SPECTATOR)
|
||||
iFakesInTeam++;
|
||||
|
||||
}
|
||||
|
||||
ReplyToCommand(client, "[SM] There are currently %d Fake-Clients, from which %d are in Spectate. Real Clients %d", iFakes, iFakes - iFakesInTeam, iPlayers);
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public Action Command_Fakes(int client, int args)
|
||||
{
|
||||
char aBuf[1024];
|
||||
char aBuf2[MAX_NAME_LENGTH];
|
||||
|
||||
for(int i = 1; i <= MaxClients; i++)
|
||||
{
|
||||
if(IsClientInGame(i))
|
||||
{
|
||||
if(IsClientConnected(i) && IsFakeClient(i) && !IsClientSourceTV(i))
|
||||
{
|
||||
GetClientName(i, aBuf2, sizeof(aBuf2));
|
||||
StrCat(aBuf, sizeof(aBuf), aBuf2);
|
||||
StrCat(aBuf, sizeof(aBuf), ", ");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(strlen(aBuf))
|
||||
{
|
||||
aBuf[strlen(aBuf) - 2] = 0;
|
||||
ReplyToCommand(client, "[SM] Fake-Clients online: %s", aBuf);
|
||||
}
|
||||
else
|
||||
ReplyToCommand(client, "[SM] Fake-Clients online: none");
|
||||
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
public void OnMapStart()
|
||||
{
|
||||
ParseNames();
|
||||
ParseTiers();
|
||||
CreateTimer(g_hDelay.FloatValue, Timer_CreateFakeClients, _, TIMER_FLAG_NO_MAPCHANGE);
|
||||
}
|
||||
|
||||
public void OnConVarChanged(ConVar hConVar, const char[] sOldValue, const char[] sNewValue)
|
||||
{
|
||||
if (hConVar == g_hUseTiers)
|
||||
{
|
||||
g_bUseTiers = g_hUseTiers.BoolValue;
|
||||
LogMessage("Tier system %s", g_bUseTiers ? "enabled" : "disabled");
|
||||
|
||||
if (g_bUseTiers)
|
||||
{
|
||||
g_iPendingBots = 0;
|
||||
|
||||
for (int i = 1; i <= MaxClients; i++)
|
||||
{
|
||||
if (!IsClientConnected(i) || !IsClientInGame(i))
|
||||
continue;
|
||||
|
||||
if (!IsFakeClient(i) || IsClientSourceTV(i))
|
||||
continue;
|
||||
|
||||
KickClient(i, "Client Disconnect");
|
||||
}
|
||||
|
||||
// Wait a moment for all bots to be kicked before trying to add new ones according to tiers.
|
||||
CreateTimer(1.0, Timer_CreateFakeClients, _, TIMER_FLAG_NO_MAPCHANGE);
|
||||
}
|
||||
else
|
||||
{
|
||||
AdjustFakeClientsToTier();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the target bot count based on the current number of real players.
|
||||
* Falls back to sm_fakeclients_players if tiers are disabled or not loaded.
|
||||
*/
|
||||
int GetTargetBotCount(int iRealPlayers)
|
||||
{
|
||||
if (!g_bUseTiers || g_iTierCount == 0)
|
||||
return g_hCount.IntValue;
|
||||
|
||||
// Walk tiers from highest threshold down, pick the first one that applies
|
||||
for (int t = g_iTierCount - 1; t >= 0; t--)
|
||||
{
|
||||
if (iRealPlayers >= g_iTierThreshold[t])
|
||||
return g_iTierMaxBots[t];
|
||||
}
|
||||
|
||||
// No tier matched (e.g. config missing threshold "0")
|
||||
LogMessage("Warning: no tier matched for %d real players, defaulting to 0 bots", iRealPlayers);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the clamped target bot count, accounting for available slots and
|
||||
* server over-capacity. Shared by AdjustFakeClientsToTier and OnClientPutInServer.
|
||||
*/
|
||||
int ComputeTarget(int iBots, int iRealPlayers, int iReservedSlots)
|
||||
{
|
||||
int iTarget = GetTargetBotCount(iRealPlayers);
|
||||
if (iTarget < 0)
|
||||
iTarget = 0;
|
||||
|
||||
// Bots currently being added already count as occupied slots
|
||||
int iEffectiveBots = iBots + g_iPendingBots;
|
||||
int iFreeSlots = MaxClients - (iRealPlayers + iEffectiveBots + iReservedSlots);
|
||||
|
||||
if (iFreeSlots < 0)
|
||||
{
|
||||
iTarget = iEffectiveBots + iFreeSlots;
|
||||
if (iTarget < 0)
|
||||
iTarget = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
int iMaxBotsBySlots = iEffectiveBots + iFreeSlots;
|
||||
if (iTarget > iMaxBotsBySlots)
|
||||
iTarget = iMaxBotsBySlots;
|
||||
}
|
||||
|
||||
return iTarget;
|
||||
}
|
||||
|
||||
/**
|
||||
* Kicks excess bots until the bot count reaches iTarget.
|
||||
* Never kicks real players or SourceTV.
|
||||
*/
|
||||
void KickExcessBots(int iBots, int iRealPlayers, int iTarget)
|
||||
{
|
||||
int iToKick = iBots - iTarget;
|
||||
int iKicked = 0;
|
||||
|
||||
for (int i = 1; i <= MaxClients && iKicked < iToKick; i++)
|
||||
{
|
||||
if (!IsClientConnected(i) || !IsClientInGame(i))
|
||||
continue;
|
||||
|
||||
// Safety: never kick real players or SourceTV
|
||||
if (!IsFakeClient(i) || IsClientSourceTV(i))
|
||||
continue;
|
||||
|
||||
LogMessage("Kicking fake client '%N' (slot %d) — bots:%d target:%d", i, i, iBots, iTarget);
|
||||
|
||||
KickClient(i, "Client Disconnect");
|
||||
iKicked++;
|
||||
}
|
||||
|
||||
if (iKicked > 0)
|
||||
LogMessage("Kicked %d bot(s) — real:%d bots:%d target:%d slots:%d", iKicked, iRealPlayers, iBots, iTarget, MaxClients);
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules staggered timers to add bots up to iToAdd, respecting free slots.
|
||||
*/
|
||||
void ScheduleBotsToAdd(int iToAdd, int iFreeSlots)
|
||||
{
|
||||
if (iFreeSlots <= 0 || iToAdd <= 0)
|
||||
return;
|
||||
|
||||
if (iToAdd > iFreeSlots)
|
||||
iToAdd = iFreeSlots;
|
||||
|
||||
// Keep a staggered cadence but add jitter so joins look less scripted.
|
||||
float fNextDelay = GetRandomFloat(0.4, 1.2);
|
||||
|
||||
for (int j = 0; j < iToAdd; j++)
|
||||
{
|
||||
g_iPendingBots++;
|
||||
CreateTimer(fNextDelay, Timer_CreateFakeClient, _, TIMER_FLAG_NO_MAPCHANGE);
|
||||
fNextDelay += GetRandomFloat(0.7, 1.9);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares current bot count against the target and either kicks excess bots
|
||||
* or schedules new ones to fill the gap.
|
||||
*/
|
||||
void AdjustFakeClientsToTier()
|
||||
{
|
||||
int iBots, iRealPlayers, iReservedSlots;
|
||||
CollectClientCounts(iBots, iRealPlayers, iReservedSlots);
|
||||
|
||||
int iTarget = ComputeTarget(iBots, iRealPlayers, iReservedSlots);
|
||||
int iFreeSlots = MaxClients - (iRealPlayers + iBots + iReservedSlots);
|
||||
|
||||
// Too many active bots -> kick extras
|
||||
if (iBots > iTarget)
|
||||
{
|
||||
g_iPendingBots = 0;
|
||||
KickExcessBots(iBots, iRealPlayers, iTarget);
|
||||
}
|
||||
// Active + pending bots are below target -> add missing bots
|
||||
else if ((iBots + g_iPendingBots) < iTarget)
|
||||
ScheduleBotsToAdd(iTarget - iBots - g_iPendingBots, iFreeSlots);
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects fake bots, real players and reserved slots in a single client loop.
|
||||
*/
|
||||
void CollectClientCounts(int &iBots, int &iRealPlayers, int &iReservedSlots)
|
||||
{
|
||||
iBots = 0;
|
||||
iRealPlayers = 0;
|
||||
bool bHasSourceTV = false;
|
||||
|
||||
for (int i = 1; i <= MaxClients; i++)
|
||||
{
|
||||
if (!IsClientConnected(i))
|
||||
continue;
|
||||
|
||||
if (IsClientSourceTV(i))
|
||||
{
|
||||
bHasSourceTV = true;
|
||||
continue; // SourceTV occupies its own slot in MaxClients — do not count it as a bot
|
||||
}
|
||||
|
||||
if (IsFakeClient(i))
|
||||
iBots++;
|
||||
else
|
||||
iRealPlayers++;
|
||||
}
|
||||
|
||||
// Reserve 1 free slot so a real player can always connect.
|
||||
// When SourceTV is active, reserve 1 extra (SourceTV's slot is already in MaxClients).
|
||||
iReservedSlots = bHasSourceTV ? 2 : 1;
|
||||
}
|
||||
|
||||
public void OnClientPutInServer(int client)
|
||||
{
|
||||
// Skip fake clients: bot additions are managed via timers.
|
||||
// Calling AdjustFakeClientsToTier on every bot join would cause cascading timers.
|
||||
if (!client || IsFakeClient(client))
|
||||
return;
|
||||
|
||||
// A real player joined: only kick excess bots, never schedule additions.
|
||||
// Scheduling here would race with the staggered timers already in flight.
|
||||
int iBots, iRealPlayers, iReservedSlots;
|
||||
CollectClientCounts(iBots, iRealPlayers, iReservedSlots);
|
||||
|
||||
int iTarget = ComputeTarget(iBots, iRealPlayers, iReservedSlots);
|
||||
if (iBots > iTarget)
|
||||
KickExcessBots(iBots, iRealPlayers, iTarget);
|
||||
}
|
||||
|
||||
public void OnClientDisconnect(int client)
|
||||
{
|
||||
// Ignore bot disconnects (caused by our own kicks) to avoid cascading timers.
|
||||
if (IsFakeClient(client))
|
||||
return;
|
||||
|
||||
CreateTimer(0.5, Timer_CreateFakeClients, _, TIMER_FLAG_NO_MAPCHANGE);
|
||||
}
|
||||
|
||||
public Action Timer_CreateFakeClient(Handle timer)
|
||||
{
|
||||
g_iPendingBots--;
|
||||
if (g_iPendingBots < 0)
|
||||
g_iPendingBots = 0;
|
||||
|
||||
int iBots, iRealPlayers, iReservedSlots;
|
||||
CollectClientCounts(iBots, iRealPlayers, iReservedSlots);
|
||||
|
||||
int iTarget = ComputeTarget(iBots, iRealPlayers, iReservedSlots);
|
||||
int iFreeSlots = MaxClients - (iRealPlayers + iBots + iReservedSlots);
|
||||
|
||||
// Recheck: tier may have changed since this timer was scheduled
|
||||
if (iFreeSlots <= 0 || iBots >= iTarget)
|
||||
return Plugin_Handled;
|
||||
|
||||
char sName[MAX_NAME_LENGTH];
|
||||
char sTarget[MAX_TARGET_LENGTH];
|
||||
int iTargets[MAXPLAYERS];
|
||||
bool bTN_Is_ML;
|
||||
|
||||
// Pick a random name, re-roll if it's already taken by a fake client.
|
||||
// Stop after trying all available names to prevent an infinite loop when
|
||||
// the name list is smaller than the number of bots (duplicates are allowed).
|
||||
// If no names are configured, CreateFakeClient will assign a default name based on the engine.
|
||||
int iNameCount = g_hNames.Length;
|
||||
if (iNameCount > 0)
|
||||
{
|
||||
g_hNames.GetString(GetRandomInt(0, iNameCount - 1), sName, sizeof(sName));
|
||||
|
||||
for (int iAttempt = 1; iAttempt < iNameCount; iAttempt++)
|
||||
{
|
||||
if (ProcessTargetString(sName, 0, iTargets, MAXPLAYERS, COMMAND_FILTER_NO_MULTI, sTarget, MAX_TARGET_LENGTH, bTN_Is_ML) != 1 || !IsFakeClient(iTargets[0]))
|
||||
break;
|
||||
|
||||
g_hNames.GetString(GetRandomInt(0, iNameCount - 1), sName, sizeof(sName));
|
||||
}
|
||||
}
|
||||
|
||||
CreateFakeClient(sName);
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
public Action Timer_CreateFakeClients(Handle timer)
|
||||
{
|
||||
AdjustFakeClientsToTier();
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads tier thresholds from configs/fakeclients_tiers.cfg. Max 32 tiers.
|
||||
* Format (KeyValues):
|
||||
*
|
||||
* "FakeClientsTiers"
|
||||
* {
|
||||
* "0" "12" // 0 real players → up to 12 bots
|
||||
* "5" "10" // 5+ real players → up to 10 bots
|
||||
* "15" "8" // And so on...
|
||||
* }
|
||||
*
|
||||
* Entries are sorted by threshold automatically, so order in the file
|
||||
* does not matter.
|
||||
*/
|
||||
stock void ParseTiers()
|
||||
{
|
||||
if (!g_bUseTiers)
|
||||
return;
|
||||
|
||||
g_iTierCount = 0;
|
||||
|
||||
char sPath[PLATFORM_MAX_PATH];
|
||||
BuildPath(Path_SM, sPath, sizeof(sPath), "configs/fakeclients_tiers.cfg");
|
||||
|
||||
KeyValues kv = new KeyValues("FakeClientsTiers");
|
||||
|
||||
if (!kv.ImportFromFile(sPath))
|
||||
{
|
||||
LogError("configs/fakeclients_tiers.cfg not found — falling back to sm_fakeclients_players");
|
||||
g_hUseTiers.BoolValue = false;
|
||||
delete kv;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!kv.GotoFirstSubKey(false))
|
||||
{
|
||||
LogError("configs/fakeclients_tiers.cfg is empty or malformed");
|
||||
g_hUseTiers.BoolValue = false;
|
||||
delete kv;
|
||||
return;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
if (g_iTierCount >= MAX_TIERS)
|
||||
break;
|
||||
|
||||
char sKey[16], sVal[16];
|
||||
kv.GetSectionName(sKey, sizeof(sKey));
|
||||
kv.GetString(NULL_STRING, sVal, sizeof(sVal), "-1");
|
||||
|
||||
int iMaxBots = StringToInt(sVal);
|
||||
if (iMaxBots < 0)
|
||||
continue; // skip malformed lines
|
||||
|
||||
g_iTierThreshold[g_iTierCount] = StringToInt(sKey);
|
||||
g_iTierMaxBots[g_iTierCount] = iMaxBots;
|
||||
g_iTierCount++;
|
||||
|
||||
} while (kv.GotoNextKey(false));
|
||||
|
||||
delete kv;
|
||||
|
||||
// Bubble sort tiers by threshold (ascending) so GetTargetBotCount() works correctly
|
||||
for (int i = 0; i < g_iTierCount - 1; i++)
|
||||
{
|
||||
for (int j = 0; j < g_iTierCount - 1 - i; j++)
|
||||
{
|
||||
if (g_iTierThreshold[j] > g_iTierThreshold[j + 1])
|
||||
{
|
||||
int tmp;
|
||||
tmp = g_iTierThreshold[j]; g_iTierThreshold[j] = g_iTierThreshold[j + 1]; g_iTierThreshold[j + 1] = tmp;
|
||||
tmp = g_iTierMaxBots[j]; g_iTierMaxBots[j] = g_iTierMaxBots[j + 1]; g_iTierMaxBots[j + 1] = tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LogMessage("Loaded %d tier(s) from fakeclients_tiers.cfg", g_iTierCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads bot display names from configs/fakeclients.txt (one name per line).
|
||||
*/
|
||||
stock void ParseNames()
|
||||
{
|
||||
delete g_hNames;
|
||||
g_hNames = new ArrayList(MAX_NAME_LENGTH);
|
||||
|
||||
char sBuffer[256];
|
||||
BuildPath(Path_SM, sBuffer, sizeof(sBuffer), "configs/fakeclients.txt");
|
||||
|
||||
File hConfig = OpenFile(sBuffer, "r");
|
||||
if (!hConfig)
|
||||
{
|
||||
LogError("configs/fakeclients.txt not found — using default engine name");
|
||||
return;
|
||||
}
|
||||
|
||||
while (hConfig.ReadLine(sBuffer, sizeof(sBuffer)))
|
||||
{
|
||||
TrimString(sBuffer);
|
||||
if (strlen(sBuffer) > 0)
|
||||
g_hNames.PushString(sBuffer);
|
||||
}
|
||||
|
||||
delete hConfig;
|
||||
|
||||
if (!g_hNames.Length)
|
||||
LogError("configs/fakeclients.txt is empty — using default engine name");
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma semicolon 1
|
||||
|
||||
#pragma newdecls required
|
||||
|
||||
#define PLUGIN_AUTHOR "Cloud Strife"
|
||||
#define PLUGIN_VERSION "1.0"
|
||||
|
||||
@@ -21,15 +23,17 @@ public Plugin myinfo =
|
||||
Handle g_CFuncRotating_StartForward = null;
|
||||
Handle g_CFuncRotating_UpdateSpeed = null;
|
||||
|
||||
stock float FloatMod(float num, float denom)
|
||||
/*
|
||||
float FloatMod(float num, float denom)
|
||||
{
|
||||
return num - denom * RoundToFloor(num / denom);
|
||||
}
|
||||
|
||||
stock float operator%(float oper1, float oper2)
|
||||
float operator%(float oper1, float oper2)
|
||||
{
|
||||
return FloatMod(oper1, oper2);
|
||||
}
|
||||
*/
|
||||
|
||||
// Set m_bStopAtStartPos to false
|
||||
public MRESReturn CFuncRotating_InputStartForward(int entity)
|
||||
|
||||
@@ -373,6 +373,8 @@ public Action Command_Rainbow(int client, int args)
|
||||
Frequency = StringToFloat(sArg);
|
||||
if(Frequency > 10.0)
|
||||
Frequency = 10.0;
|
||||
else if(Frequency < 0.01)
|
||||
Frequency = 0.01;
|
||||
}
|
||||
|
||||
if(!Frequency || (args < 1 && g_aRainbowFrequency[client]))
|
||||
@@ -401,7 +403,7 @@ void DisplayGlowColorMenu(int client)
|
||||
g_GlowColorsMenu.Display(client, MENU_TIME_FOREVER);
|
||||
}
|
||||
|
||||
public int MenuHandler_GlowColorsMenu(Menu menu, MenuAction action, int param1, int param2)
|
||||
public void MenuHandler_GlowColorsMenu(Menu menu, MenuAction action, int param1, int param2)
|
||||
{
|
||||
switch(action)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
// Angery nut
|
||||
materials/models/unloze/william/back.vmt
|
||||
materials/models/unloze/william/back.vtf
|
||||
materials/models/unloze/william/front.vmt
|
||||
materials/models/unloze/william/front.vtf
|
||||
models/unloze/william/angerynut.dx80.vtx
|
||||
models/unloze/william/angerynut.dx90.vtx
|
||||
models/unloze/william/angerynut.mdl
|
||||
models/unloze/william/angerynut.sw.vtx
|
||||
models/unloze/william/angerynut.vvd
|
||||
//
|
||||
//// Eevee
|
||||
models\unloze\grenades\eevee.dx80.vtx
|
||||
models\unloze\grenades\eevee.dx90.vtx
|
||||
models\unloze\grenades\eevee.mdl
|
||||
models\unloze\grenades\eevee.sw.vtx
|
||||
models\unloze\grenades\eevee.vvd
|
||||
materials\models\unloze\grenades\eevee\eevee_body.vtf
|
||||
materials\models\unloze\grenades\eevee\eevee_body_shiny.vtf
|
||||
materials\models\unloze\grenades\eevee\eevee_eye.vtf
|
||||
materials\models\unloze\grenades\eevee\eevee_eye_shiny.vtf
|
||||
materials\models\unloze\grenades\eevee\eevee_mouth.vtf
|
||||
materials\models\unloze\grenades\eevee\eevee_mouth_shiny.vtf
|
||||
materials\models\unloze\grenades\eevee\eievui.vmt
|
||||
materials\models\unloze\grenades\eevee\eievuieye.vmt
|
||||
materials\models\unloze\grenades\eevee\eievuieye_shiny.vmt
|
||||
materials\models\unloze\grenades\eevee\eievuimouth.vmt
|
||||
materials\models\unloze\grenades\eevee\eievuimouth_shiny.vmt
|
||||
materials\models\unloze\grenades\eevee\eievui_shiny.vmt
|
||||
//
|
||||
//// Holy Grenade
|
||||
models\unloze\grenades\holy_grenade.dx80.vtx
|
||||
models\unloze\grenades\holy_grenade.dx90.vtx
|
||||
models\unloze\grenades\holy_grenade.mdl
|
||||
models\unloze\grenades\holy_grenade.phy
|
||||
models\unloze\grenades\holy_grenade.sw.vtx
|
||||
models\unloze\grenades\holy_grenade.vvd
|
||||
materials\models\unloze\grenades\holy_grenade\holy_grenade.vmt
|
||||
materials\models\unloze\grenades\holy_grenade\holy_grenade.vtf
|
||||
//
|
||||
//// Lenny
|
||||
models\unloze\grenades\lenny.dx80.vtx
|
||||
models\unloze\grenades\lenny.dx90.vtx
|
||||
models\unloze\grenades\lenny.mdl
|
||||
models\unloze\grenades\lenny.sw.vtx
|
||||
models\unloze\grenades\lenny.vvd
|
||||
models\unloze\grenades\lennysad.dx80.vtx
|
||||
models\unloze\grenades\lennysad.dx90.vtx
|
||||
models\unloze\grenades\lennysad.mdl
|
||||
models\unloze\grenades\lennysad.sw.vtx
|
||||
models\unloze\grenades\lennysad.vvd
|
||||
materials\models\unloze\grenades\lenny\black.vmt
|
||||
//
|
||||
//// Pokeball
|
||||
models\unloze\grenades\pokeball.dx80.vtx
|
||||
models\unloze\grenades\pokeball.dx90.vtx
|
||||
models\unloze\grenades\pokeball.mdl
|
||||
models\unloze\grenades\pokeball.phy
|
||||
models\unloze\grenades\pokeball.sw.vtx
|
||||
models\unloze\grenades\pokeball.vvd
|
||||
materials\models\unloze\grenades\pokeball\pokeball.vmt
|
||||
materials\models\unloze\grenades\pokeball\pokeball.vtf
|
||||
materials\models\unloze\grenades\pokeball\pokeball_great.vmt
|
||||
materials\models\unloze\grenades\pokeball\pokeball_great.vtf
|
||||
materials\models\unloze\grenades\pokeball\pokeball_gs.vmt
|
||||
materials\models\unloze\grenades\pokeball\pokeball_gs.vtf
|
||||
materials\models\unloze\grenades\pokeball\pokeball_master.vmt
|
||||
materials\models\unloze\grenades\pokeball\pokeball_master.vtf
|
||||
materials\models\unloze\grenades\pokeball\pokeball_ultra.vmt
|
||||
materials\models\unloze\grenades\pokeball\pokeball_ultra.vtf
|
||||
//
|
||||
//// Soccer Ball
|
||||
materials/models/unloze/soccer/soccerball.vmt
|
||||
materials/models/unloze/soccer/soccerball_unloze.vtf
|
||||
models/unloze/soccer/soccerball.dx80.vtx
|
||||
models/unloze/soccer/soccerball.dx90.vtx
|
||||
models/unloze/soccer/soccerball.mdl
|
||||
models/unloze/soccer/soccerball.phy
|
||||
models/unloze/soccer/soccerball.sw.vtx
|
||||
models/unloze/soccer/soccerball.vvd
|
||||
@@ -0,0 +1,193 @@
|
||||
"Skins"
|
||||
{
|
||||
"0"
|
||||
{
|
||||
"Name" "Hula Doll"
|
||||
"ModelPath" "models/props_lab/huladoll.mdl"
|
||||
"Scale" "1.7"
|
||||
"Tier" "1"
|
||||
}
|
||||
"1"
|
||||
{
|
||||
"Name" "Banana"
|
||||
"ModelPath" "models/props/cs_italy/bananna.mdl"
|
||||
"Tier" "2"
|
||||
}
|
||||
"2"
|
||||
{
|
||||
"Name" "Melon"
|
||||
"ModelPath" "models/props_junk/watermelon01.mdl"
|
||||
"Scale" "0.7"
|
||||
"Tier" "2"
|
||||
}
|
||||
"3"
|
||||
{
|
||||
"Name" "Orange"
|
||||
"ModelPath" "models/props/cs_italy/orange.mdl"
|
||||
"Scale" "1.5"
|
||||
"Tier" "3"
|
||||
}
|
||||
"4"
|
||||
{
|
||||
"Name" "Creepy Doll"
|
||||
"ModelPath" "models/props_c17/doll01.mdl"
|
||||
"Scale" "0.8"
|
||||
"Tier" "3"
|
||||
}
|
||||
"5"
|
||||
{
|
||||
"Name" "Turtle"
|
||||
"ModelPath" "models/props/de_tides/vending_turtle.mdl"
|
||||
"Tier" "4"
|
||||
}
|
||||
"6"
|
||||
{
|
||||
"Name" "Goldfish"
|
||||
"ModelPath" "models/props/de_inferno/goldfish.mdl"
|
||||
"Tier" "5"
|
||||
}
|
||||
"7"
|
||||
{
|
||||
"Name" "River Fish"
|
||||
"ModelPath" "models/props/cs_militia/fishriver01.mdl"
|
||||
"Tier" "5"
|
||||
}
|
||||
"8"
|
||||
{
|
||||
"Name" "Snowman Head"
|
||||
"ModelPath" "models/props/cs_office/snowman_face.mdl"
|
||||
"Scale" "0.7"
|
||||
"Tier" "6"
|
||||
}
|
||||
"9"
|
||||
{
|
||||
"Name" "Glass Bottle"
|
||||
"ModelPath" "models/props/cs_militia/bottle01.mdl"
|
||||
"Tier" "7"
|
||||
}
|
||||
"10"
|
||||
{
|
||||
"Name" "Gift"
|
||||
"ModelPath" "models/items/cs_gift.mdl"
|
||||
"Scale" "0.6"
|
||||
"Tier" "8"
|
||||
}
|
||||
"11"
|
||||
{
|
||||
"Name" "Rollermine"
|
||||
"ModelPath" "models/roller_spikes.mdl"
|
||||
"VIP" "1"
|
||||
"Scale" "0.7"
|
||||
}
|
||||
"12"
|
||||
{
|
||||
"Name" "Helicopter Bomb"
|
||||
"ModelPath" "models/combine_helicopter/helicopter_bomb01.mdl"
|
||||
"VIP" "1"
|
||||
"Scale" "0.35"
|
||||
}
|
||||
"13"
|
||||
{
|
||||
"Name" "Explosive Barrel"
|
||||
"ModelPath" "models/props_c17/oildrum001_explosive.mdl"
|
||||
"VIP" "1"
|
||||
"Scale" "0.25"
|
||||
}
|
||||
"14"
|
||||
{
|
||||
"Name" "Skull"
|
||||
"ModelPath" "models/gibs/agibs.mdl"
|
||||
"VIP" "1"
|
||||
"Scale" "1.3"
|
||||
}
|
||||
"15"
|
||||
{
|
||||
"Name" "Angery Nut"
|
||||
"ModelPath" "models/unloze/william/angerynut.mdl"
|
||||
"VIP" "1"
|
||||
"Scale" "0.8"
|
||||
}
|
||||
"16"
|
||||
{
|
||||
"Name" "Holy Grenade"
|
||||
"ModelPath" "models/unloze/grenades/holy_grenade.mdl"
|
||||
"Scale" "0.6"
|
||||
"VIP" "1"
|
||||
}
|
||||
"17"
|
||||
{
|
||||
"Name" "Lenny Face"
|
||||
"ModelPath" "models/unloze/grenades/lenny.mdl"
|
||||
"VIP" "1"
|
||||
"Scale" "0.8"
|
||||
}
|
||||
"18"
|
||||
{
|
||||
"Name" "Lenny Sad"
|
||||
"ModelPath" "models/unloze/grenades/lennysad.mdl"
|
||||
"VIP" "1"
|
||||
"Scale" "0.8"
|
||||
}
|
||||
"19"
|
||||
{
|
||||
"Name" "Soccer Ball"
|
||||
"ModelPath" "models/unloze/soccer/soccerball.mdl"
|
||||
"VIP" "1"
|
||||
"Scale" "0.4"
|
||||
}
|
||||
"20"
|
||||
{
|
||||
"Name" "Eevee"
|
||||
"ModelPath" "models/unloze/grenades/eevee.mdl"
|
||||
"VIP" "1"
|
||||
"Scale" "0.5"
|
||||
}
|
||||
"21"
|
||||
{
|
||||
"Name" "Shiny Eevee"
|
||||
"ModelPath" "models/unloze/grenades/eevee.mdl"
|
||||
"VIP" "1"
|
||||
"Skin" "1"
|
||||
"Scale" "0.5"
|
||||
}
|
||||
"22"
|
||||
{
|
||||
"Name" "Poke Ball"
|
||||
"ModelPath" "models/unloze/grenades/pokeball.mdl"
|
||||
"VIP" "1"
|
||||
"Scale" "1.5"
|
||||
}
|
||||
"23"
|
||||
{
|
||||
"Name" "Great Ball"
|
||||
"ModelPath" "models/unloze/grenades/pokeball.mdl"
|
||||
"VIP" "1"
|
||||
"Skin" "1"
|
||||
"Scale" "1.5"
|
||||
}
|
||||
"24"
|
||||
{
|
||||
"Name" "Ultra Ball"
|
||||
"ModelPath" "models/unloze/grenades/pokeball.mdl"
|
||||
"VIP" "1"
|
||||
"Skin" "4"
|
||||
"Scale" "1.5"
|
||||
}
|
||||
"25"
|
||||
{
|
||||
"Name" "Master Ball"
|
||||
"ModelPath" "models/unloze/grenades/pokeball.mdl"
|
||||
"VIP" "1"
|
||||
"Skin" "3"
|
||||
"Scale" "1.5"
|
||||
}
|
||||
"26"
|
||||
{
|
||||
"Name" "GS Ball"
|
||||
"ModelPath" "models/unloze/grenades/pokeball.mdl"
|
||||
"VIP" "1"
|
||||
"Skin" "2"
|
||||
"Scale" "1.5"
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,416 @@
|
||||
#pragma semicolon 1
|
||||
#include <sourcemod>
|
||||
#include <clientprefs>
|
||||
#include <sdktools>
|
||||
#include <sdkhooks>
|
||||
#include <unloze_playtime>
|
||||
|
||||
#pragma newdecls required
|
||||
|
||||
#define MAX_SKINS 64
|
||||
|
||||
enum struct GrenadeSkin
|
||||
{
|
||||
char ModelPath[128];
|
||||
char Name[128];
|
||||
int VIP;
|
||||
int Tier;
|
||||
float Scale;
|
||||
int Skin;
|
||||
}
|
||||
|
||||
GrenadeSkin g_aGrenadeSkins[MAX_SKINS];
|
||||
int g_iSkinCount = 0;
|
||||
|
||||
int g_iGrenadeSkin[MAXPLAYERS + 1] = { -1, ... };
|
||||
bool g_bAdminChecked[MAXPLAYERS + 1];
|
||||
|
||||
Handle g_hCookie_GrenadeSkin;
|
||||
|
||||
public Plugin myinfo =
|
||||
{
|
||||
name = "Grenade Skins",
|
||||
description = "grenade skins but source code actually provided",
|
||||
author = "redone by claude ai mostly, slight human adjustments",
|
||||
version = "1.0",
|
||||
url = ""
|
||||
};
|
||||
|
||||
public void OnPluginStart()
|
||||
{
|
||||
RegConsoleCmd("sm_grenadeskin", Cmd_GrenadeSkinSettings);
|
||||
RegConsoleCmd("sm_grenadeskins", Cmd_GrenadeSkinSettings);
|
||||
|
||||
g_hCookie_GrenadeSkin = RegClientCookie("grenade_skin", "Selected grenade skin", CookieAccess_Protected);
|
||||
|
||||
SetCookieMenuItem(MenuHandler_CookieMenu, 0, "Grenade Skin");
|
||||
|
||||
for (int i = 1; i <= MaxClients; i++)
|
||||
{
|
||||
if (IsValidClient(i, true) && AreClientCookiesCached(i))
|
||||
{
|
||||
OnClientCookiesCached(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void OnMapStart()
|
||||
{
|
||||
LoadDownloads();
|
||||
LoadSkinsConfig();
|
||||
}
|
||||
|
||||
void LoadDownloads()
|
||||
{
|
||||
char sDownloadsFile[PLATFORM_MAX_PATH];
|
||||
BuildPath(Path_SM, sDownloadsFile, sizeof(sDownloadsFile), "configs/grenade_skins/downloads.txt");
|
||||
|
||||
File hFile = OpenFile(sDownloadsFile, "r");
|
||||
if (!hFile)
|
||||
{
|
||||
SetFailState("Could not open file: configs/grenade_skins/downloads.txt");
|
||||
}
|
||||
|
||||
char sLine[1024];
|
||||
while (hFile.ReadLine(sLine, sizeof(sLine)))
|
||||
{
|
||||
TrimString(sLine);
|
||||
|
||||
// Skip empty lines and comment lines (lines starting with "//" with no file extension).
|
||||
if (sLine[0] == '\0')
|
||||
continue;
|
||||
|
||||
if (StrContains(sLine, ".", false) == -1)
|
||||
continue;
|
||||
|
||||
if (FileExists(sLine, false, "GAME"))
|
||||
{
|
||||
AddFileToDownloadsTable(sLine);
|
||||
}
|
||||
}
|
||||
|
||||
delete hFile;
|
||||
}
|
||||
|
||||
void LoadSkinsConfig()
|
||||
{
|
||||
// Clear existing skins.
|
||||
for (int i = 0; i < MAX_SKINS; i++)
|
||||
{
|
||||
g_aGrenadeSkins[i].ModelPath[0] = '\0';
|
||||
g_aGrenadeSkins[i].Name[0] = '\0';
|
||||
g_aGrenadeSkins[i].VIP = 0;
|
||||
g_aGrenadeSkins[i].Tier = 0;
|
||||
g_aGrenadeSkins[i].Scale = 1.0;
|
||||
g_aGrenadeSkins[i].Skin = 0;
|
||||
}
|
||||
g_iSkinCount = 0;
|
||||
|
||||
char sConfigFile[PLATFORM_MAX_PATH];
|
||||
BuildPath(Path_SM, sConfigFile, sizeof(sConfigFile), "configs/grenade_skins/skins.cfg");
|
||||
|
||||
if (!FileExists(sConfigFile, false, "GAME"))
|
||||
{
|
||||
SetFailState("Could not find: \"%s\"", sConfigFile);
|
||||
}
|
||||
|
||||
KeyValues kv = new KeyValues("Skins");
|
||||
if (!kv.ImportFromFile(sConfigFile))
|
||||
{
|
||||
delete kv;
|
||||
SetFailState("ImportFromFile() failed!");
|
||||
}
|
||||
|
||||
if (!kv.GotoFirstSubKey(true))
|
||||
{
|
||||
delete kv;
|
||||
SetFailState("Unable to goto first sub key in: \"%s\"", sConfigFile);
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
do
|
||||
{
|
||||
if (i >= MAX_SKINS)
|
||||
{
|
||||
LogError("grenade_skins: skins.cfg has more than %d entries, ignoring the rest.", MAX_SKINS);
|
||||
break;
|
||||
}
|
||||
|
||||
kv.GetString("ModelPath", g_aGrenadeSkins[i].ModelPath, sizeof(g_aGrenadeSkins[].ModelPath), "error");
|
||||
if (StrEqual(g_aGrenadeSkins[i].ModelPath, "error", true))
|
||||
{
|
||||
SetFailState("Unable to read Path");
|
||||
}
|
||||
|
||||
kv.GetString("Name", g_aGrenadeSkins[i].Name, sizeof(g_aGrenadeSkins[].Name), "error");
|
||||
if (StrEqual(g_aGrenadeSkins[i].Name, "error", true))
|
||||
{
|
||||
SetFailState("Unable to read Name");
|
||||
}
|
||||
|
||||
g_aGrenadeSkins[i].VIP = kv.GetNum("VIP", 0);
|
||||
g_aGrenadeSkins[i].Tier = kv.GetNum("tier", 0);
|
||||
g_aGrenadeSkins[i].Scale = kv.GetFloat("Scale", 1.0);
|
||||
g_aGrenadeSkins[i].Skin = kv.GetNum("Skin", 0);
|
||||
|
||||
PrecacheModel(g_aGrenadeSkins[i].ModelPath, false);
|
||||
|
||||
i++;
|
||||
}
|
||||
while (kv.GotoNextKey(true));
|
||||
|
||||
g_iSkinCount = i;
|
||||
|
||||
delete kv;
|
||||
}
|
||||
|
||||
public void OnClientCookiesCached(int client)
|
||||
{
|
||||
char sBuffer[256];
|
||||
GetClientCookie(client, g_hCookie_GrenadeSkin, sBuffer, sizeof(sBuffer));
|
||||
|
||||
if (sBuffer[0])
|
||||
{
|
||||
g_iGrenadeSkin[client] = -1;
|
||||
|
||||
for (int i = 0; i < g_iSkinCount; i++)
|
||||
{
|
||||
if (StrEqual(g_aGrenadeSkins[i].ModelPath, sBuffer, true))
|
||||
{
|
||||
g_iGrenadeSkin[client] = i;
|
||||
|
||||
if (g_bAdminChecked[client] && !ClientHasSkinAccess(client, i))
|
||||
{
|
||||
g_iGrenadeSkin[client] = -1;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
g_iGrenadeSkin[client] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
public void OnClientDisconnect(int client)
|
||||
{
|
||||
g_iGrenadeSkin[client] = -1;
|
||||
g_bAdminChecked[client] = false;
|
||||
}
|
||||
|
||||
public void OnClientPostAdminCheck(int client)
|
||||
{
|
||||
g_bAdminChecked[client] = true;
|
||||
|
||||
if (AreClientCookiesCached(client) && g_iGrenadeSkin[client] != -1)
|
||||
{
|
||||
if (!ClientHasSkinAccess(client, g_iGrenadeSkin[client]))
|
||||
{
|
||||
g_iGrenadeSkin[client] = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a client has access to a given skin index,
|
||||
* taking both the legacy VIP flag and the new tier requirement into account.
|
||||
*/
|
||||
bool ClientHasSkinAccess(int client, int skinIndex)
|
||||
{
|
||||
if (g_aGrenadeSkins[skinIndex].VIP && !CheckCommandAccess(client, "", ADMFLAG_RESERVATION, false))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (g_aGrenadeSkins[skinIndex].Tier > 0 && GetPlayerTier_native(client) < g_aGrenadeSkins[skinIndex].Tier)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void OnEntitySpawned(int entity, const char[] sClassname)
|
||||
{
|
||||
if (StrContains(sClassname, "_projectile", false) == -1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int iOwner = GetEntPropEnt(entity, Prop_Send, "m_hOwnerEntity");
|
||||
|
||||
if (!IsValidClient(iOwner, true) || g_iGrenadeSkin[iOwner] == -1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int skinIndex = g_iGrenadeSkin[iOwner];
|
||||
|
||||
SetEntityRenderMode(entity, RENDER_TRANSALPHA);
|
||||
|
||||
float fNadeOrigin[3];
|
||||
GetEntPropVector(entity, Prop_Send, "m_vecOrigin", fNadeOrigin);
|
||||
|
||||
int iNadeProp = CreateEntityAtOrigin("prop_dynamic_override", fNadeOrigin);
|
||||
|
||||
DispatchKeyValue(iNadeProp, "disableshadows", "1");
|
||||
DispatchKeyValue(iNadeProp, "disablereceiveshadows", "1");
|
||||
DispatchKeyValue(iNadeProp, "DisableBoneFollowers", "1");
|
||||
DispatchKeyValue(iNadeProp, "solid", "0");
|
||||
DispatchKeyValue(iNadeProp, "model", g_aGrenadeSkins[skinIndex].ModelPath);
|
||||
|
||||
char sScale[32];
|
||||
FloatToString(g_aGrenadeSkins[skinIndex].Scale, sScale, sizeof(sScale));
|
||||
DispatchKeyValue(iNadeProp, "modelscale", sScale);
|
||||
|
||||
SpawnAndActivate(iNadeProp);
|
||||
ParentToEntity(iNadeProp, entity);
|
||||
|
||||
if (g_aGrenadeSkins[skinIndex].Skin > 0)
|
||||
{
|
||||
char sSkinBuf[16];
|
||||
IntToString(g_aGrenadeSkins[skinIndex].Skin, sSkinBuf, sizeof(sSkinBuf));
|
||||
SetVariantString(sSkinBuf);
|
||||
AcceptEntityInput(iNadeProp, "Skin");
|
||||
}
|
||||
}
|
||||
|
||||
public Action Cmd_GrenadeSkinSettings(int client, int args)
|
||||
{
|
||||
ShowSettingsMenu(client);
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
public void MenuHandler_CookieMenu(int client, CookieMenuAction action, any info, char[] buffer, int maxlen)
|
||||
{
|
||||
switch (action)
|
||||
{
|
||||
case CookieMenuAction_DisplayOption:
|
||||
{
|
||||
Format(buffer, maxlen, "Grenade Skin");
|
||||
}
|
||||
case CookieMenuAction_SelectOption:
|
||||
{
|
||||
ShowSettingsMenu(client);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ShowSettingsMenu(int client)
|
||||
{
|
||||
Menu menu = new Menu(MenuHandler_MainMenu);
|
||||
menu.SetTitle("Grenade Skin");
|
||||
|
||||
char sBuffer[128];
|
||||
Format(sBuffer, sizeof(sBuffer), "No Skin%s", (g_iGrenadeSkin[client] == -1) ? " (Selected)" : "");
|
||||
menu.AddItem("-1", sBuffer, (g_iGrenadeSkin[client] == -1) ? ITEMDRAW_DISABLED : ITEMDRAW_DEFAULT);
|
||||
|
||||
for (int i = 0; i < g_iSkinCount; i++)
|
||||
{
|
||||
bool bSelected = (i == g_iGrenadeSkin[client]);
|
||||
bool bHasAccess = ClientHasSkinAccess(client, i);
|
||||
bool bUnavailable = bSelected || !bHasAccess;
|
||||
|
||||
char sSuffix[32];
|
||||
sSuffix[0] = '\0';
|
||||
|
||||
if (!bHasAccess)
|
||||
{
|
||||
if (g_aGrenadeSkins[i].VIP && !CheckCommandAccess(client, "", ADMFLAG_RESERVATION, false))
|
||||
{
|
||||
Format(sSuffix, sizeof(sSuffix), " [VIP]");
|
||||
}
|
||||
else if (g_aGrenadeSkins[i].Tier > 0 && GetPlayerTier_native(client) < g_aGrenadeSkins[i].Tier)
|
||||
{
|
||||
Format(sSuffix, sizeof(sSuffix), " [Tier %i]", g_aGrenadeSkins[i].Tier);
|
||||
}
|
||||
}
|
||||
else if (bSelected)
|
||||
{
|
||||
Format(sSuffix, sizeof(sSuffix), " (Selected)");
|
||||
}
|
||||
|
||||
Format(sBuffer, sizeof(sBuffer), "%s%s", g_aGrenadeSkins[i].Name, sSuffix);
|
||||
|
||||
char sInfo[8];
|
||||
IntToString(i, sInfo, sizeof(sInfo));
|
||||
|
||||
menu.AddItem(sInfo, sBuffer, bUnavailable ? ITEMDRAW_DISABLED : ITEMDRAW_DEFAULT);
|
||||
}
|
||||
|
||||
menu.ExitBackButton = true;
|
||||
menu.Display(client, MENU_TIME_FOREVER);
|
||||
}
|
||||
|
||||
public int MenuHandler_MainMenu(Menu menu, MenuAction action, int client, int selection)
|
||||
{
|
||||
switch (action)
|
||||
{
|
||||
case MenuAction_Select:
|
||||
{
|
||||
char sInfo[8];
|
||||
menu.GetItem(selection, sInfo, sizeof(sInfo));
|
||||
|
||||
int chosen = StringToInt(sInfo);
|
||||
g_iGrenadeSkin[client] = chosen;
|
||||
|
||||
if (chosen == -1)
|
||||
{
|
||||
SetClientCookie(client, g_hCookie_GrenadeSkin, "");
|
||||
}
|
||||
else
|
||||
{
|
||||
SetClientCookie(client, g_hCookie_GrenadeSkin, g_aGrenadeSkins[chosen].ModelPath);
|
||||
}
|
||||
|
||||
ShowSettingsMenu(client);
|
||||
}
|
||||
case MenuAction_Cancel:
|
||||
{
|
||||
if (selection == MenuCancel_ExitBack)
|
||||
{
|
||||
// Re-open the cookie/settings menu if desired; original plugin
|
||||
// routed back into the cookie menu here.
|
||||
}
|
||||
}
|
||||
case MenuAction_End:
|
||||
{
|
||||
delete menu;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Helper stocks (ported from decompiled output)
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
stock bool IsValidClient(int client, bool noBots)
|
||||
{
|
||||
if (client <= 0 || client > MaxClients || !IsClientConnected(client) || (noBots && IsFakeClient(client)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return IsClientInGame(client);
|
||||
}
|
||||
|
||||
stock int CreateEntityAtOrigin(const char[] classname, const float origin[3])
|
||||
{
|
||||
int entity = CreateEntityByName(classname);
|
||||
TeleportEntity(entity, origin, NULL_VECTOR, NULL_VECTOR);
|
||||
return entity;
|
||||
}
|
||||
|
||||
stock void SpawnAndActivate(int entity)
|
||||
{
|
||||
DispatchSpawn(entity);
|
||||
ActivateEntity(entity);
|
||||
}
|
||||
|
||||
stock void ParentToEntity(int entity, int parent)
|
||||
{
|
||||
SetVariantString("!activator");
|
||||
AcceptEntityInput(entity, "SetParent", parent, parent);
|
||||
}
|
||||
@@ -25,7 +25,7 @@ bool g_bHitmarkerSound[MAXPLAYERS+1];
|
||||
int g_iHitmarkerSoundVolume[MAXPLAYERS+1] = {100,...};
|
||||
int g_iHitmarkerSkin[MAXPLAYERS+1];
|
||||
|
||||
#define MAXIMUMSKINS 6
|
||||
#define MAXIMUMSKINS 24
|
||||
char g_sHitmarkerSkins[MAXIMUMSKINS][PLATFORM_MAX_PATH];
|
||||
char g_sHitmarkerSkinsNames[MAXIMUMSKINS][PLATFORM_MAX_PATH];
|
||||
|
||||
@@ -38,6 +38,7 @@ Handle g_hCookie_HitmarkerSoundVolume;
|
||||
Handle g_hCookie_HitmarkerSkin;
|
||||
|
||||
ConVar g_hCVar_Debug;
|
||||
ConVar g_hCVar_HitmarkerEnabled;
|
||||
|
||||
bool g_bLate;
|
||||
|
||||
@@ -87,7 +88,9 @@ public void OnPluginStart()
|
||||
g_hCookie_HitmarkerSkin = RegClientCookie("hitmarker_skin", "", CookieAccess_Private);
|
||||
|
||||
g_hCVar_Debug = CreateConVar("sm_hitmarker_debug", "0", "", FCVAR_NONE, true, 0.0, true, 1.0);
|
||||
AutoExecConfig();
|
||||
g_hCVar_HitmarkerEnabled = CreateConVar("sm_hitmarker_enabled", "1", "", FCVAR_NONE, true, 0.0, true, 1.0);
|
||||
g_hCVar_HitmarkerEnabled.AddChangeHook(OnConVarChanged);
|
||||
AutoExecConfig(true);
|
||||
|
||||
RegConsoleCmd("sm_hm", OnHitmarkerSettings);
|
||||
RegConsoleCmd("sm_hitmarker", OnHitmarkerSettings);
|
||||
@@ -109,6 +112,14 @@ public void OnPluginStart()
|
||||
}
|
||||
}
|
||||
|
||||
public void OnConVarChanged(ConVar convar, const char[] oldValue, const char[] newValue)
|
||||
{
|
||||
if(g_hCVar_HitmarkerEnabled.BoolValue)
|
||||
CPrintToChatAll("{cyan}[Hitmarker] {white}has been allowed.");
|
||||
else
|
||||
CPrintToChatAll("{cyan}[Hitmarker] {white}has been disabled.");
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
@@ -120,6 +131,11 @@ public void OnMapStart()
|
||||
g_sHitmarkerSkinsNames[i] = "";
|
||||
}
|
||||
|
||||
if(!g_hCVar_HitmarkerEnabled.BoolValue)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
char sConfigFile[PLATFORM_MAX_PATH];
|
||||
BuildPath(Path_SM, sConfigFile, sizeof(sConfigFile), "configs/Hitmarkers.cfg");
|
||||
|
||||
@@ -184,6 +200,11 @@ public void OnMapStart()
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public void OnClientCookiesCached(int client)
|
||||
{
|
||||
if(!g_hCVar_HitmarkerEnabled.BoolValue)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
char sBuffer[PLATFORM_MAX_PATH];
|
||||
|
||||
GetClientCookie(client, g_hCookie_ShowBossHitmarker, sBuffer, sizeof(sBuffer));
|
||||
@@ -243,6 +264,10 @@ public void OnClientDisconnect(int client)
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public Action OnHitmarkerSettings(int client, int args)
|
||||
{
|
||||
if(!g_hCVar_HitmarkerEnabled.BoolValue)
|
||||
{
|
||||
return Plugin_Handled;
|
||||
}
|
||||
ShowSettingsMenu(client);
|
||||
return Plugin_Handled;
|
||||
}
|
||||
@@ -252,6 +277,10 @@ public Action OnHitmarkerSettings(int client, int args)
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public Action OnToggleBossHitmarker(int client, int args)
|
||||
{
|
||||
if(!g_hCVar_HitmarkerEnabled.BoolValue)
|
||||
{
|
||||
return Plugin_Handled;
|
||||
}
|
||||
ToggleBossHitmarker(client);
|
||||
return Plugin_Handled;
|
||||
}
|
||||
@@ -275,6 +304,10 @@ public void ToggleBossHitmarker(int client)
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public Action OnToggleZombieHitmarker(int client, int args)
|
||||
{
|
||||
if(!g_hCVar_HitmarkerEnabled.BoolValue)
|
||||
{
|
||||
return Plugin_Handled;
|
||||
}
|
||||
ToggleZombieHitmarker(client);
|
||||
return Plugin_Handled;
|
||||
}
|
||||
@@ -284,6 +317,10 @@ public Action OnToggleZombieHitmarker(int client, int args)
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public void ToggleZombieHitmarker(int client)
|
||||
{
|
||||
if(!g_hCVar_HitmarkerEnabled.BoolValue)
|
||||
{
|
||||
return;
|
||||
}
|
||||
g_bShowZombieHitmarker[client] = !g_bShowZombieHitmarker[client];
|
||||
SetClientCookie(client, g_hCookie_ShowZombieHitmarker, g_bShowZombieHitmarker[client] ? "1" : "");
|
||||
|
||||
@@ -298,6 +335,10 @@ public void ToggleZombieHitmarker(int client)
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public Action OnToggleHitmarkerSound(int client, int args)
|
||||
{
|
||||
if(!g_hCVar_HitmarkerEnabled.BoolValue)
|
||||
{
|
||||
return Plugin_Handled;
|
||||
}
|
||||
ToggleHitmarkerSound(client);
|
||||
return Plugin_Handled;
|
||||
}
|
||||
@@ -307,6 +348,10 @@ public Action OnToggleHitmarkerSound(int client, int args)
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public void ToggleHitmarkerSound(int client)
|
||||
{
|
||||
if(!g_hCVar_HitmarkerEnabled.BoolValue)
|
||||
{
|
||||
return;
|
||||
}
|
||||
g_bHitmarkerSound[client] = !g_bHitmarkerSound[client];
|
||||
SetClientCookie(client, g_hCookie_HitmarkerSound, g_bHitmarkerSound[client] ? "1" : "");
|
||||
|
||||
@@ -321,6 +366,10 @@ public void ToggleHitmarkerSound(int client)
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public void ToggleHitmarkerSoundVolume(int client)
|
||||
{
|
||||
if(!g_hCVar_HitmarkerEnabled.BoolValue)
|
||||
{
|
||||
return;
|
||||
}
|
||||
g_iHitmarkerSoundVolume[client] += 25;
|
||||
|
||||
if(g_iHitmarkerSoundVolume[client] > 100)
|
||||
@@ -339,6 +388,10 @@ public void ToggleHitmarkerSoundVolume(int client)
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public void ToggleHitmarkerSkin(int client)
|
||||
{
|
||||
if(!g_hCVar_HitmarkerEnabled.BoolValue)
|
||||
{
|
||||
return;
|
||||
}
|
||||
g_iHitmarkerSkin[client]++;
|
||||
|
||||
if (StrEqual(g_sHitmarkerSkins[g_iHitmarkerSkin[client]], ""))
|
||||
@@ -354,6 +407,10 @@ public void ToggleHitmarkerSkin(int client)
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public void ShowSettingsMenu(int client)
|
||||
{
|
||||
if(!g_hCVar_HitmarkerEnabled.BoolValue)
|
||||
{
|
||||
return;
|
||||
}
|
||||
Menu menu = new Menu(MenuHandler_MainMenu);
|
||||
|
||||
menu.SetTitle("Hitmarker Settings", client);
|
||||
@@ -434,6 +491,10 @@ public int MenuHandler_MainMenu(Menu menu, MenuAction action, int client, int se
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public void OnEntitySpawned(int Entity, const char[] sClassname)
|
||||
{
|
||||
if(!g_hCVar_HitmarkerEnabled.BoolValue)
|
||||
{
|
||||
return;
|
||||
}
|
||||
int ent = EntRefToEntIndex(Entity);
|
||||
|
||||
if (0 < ent < MAX_EDICTS)
|
||||
@@ -451,6 +512,10 @@ public void OnEntitySpawned(int Entity, const char[] sClassname)
|
||||
/*
|
||||
public void OnBossDamaged(CBoss Boss, CConfig Config, int client, float damage)
|
||||
{
|
||||
if(!g_hCVar_HitmarkerEnabled.BoolValue)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!IsValidClient(client))
|
||||
return;
|
||||
|
||||
@@ -475,6 +540,10 @@ public void OnBossDamaged(CBoss Boss, CConfig Config, int client, float damage)
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public MRESReturn Detour_OnTakeDamage(int entity, Handle hReturn, Handle hParams)
|
||||
{
|
||||
if(!g_hCVar_HitmarkerEnabled.BoolValue)
|
||||
{
|
||||
return MRES_Ignored;
|
||||
}
|
||||
//https://github.com/alliedmodders/hl2sdk/blob/css/game/shared/takedamageinfo.h#L115
|
||||
int iHealth = GetEntProp(entity, Prop_Data, "m_iHealth");
|
||||
int client = DHookGetParamObjectPtrVar(hParams, 1, 3*(3*4) + 0, ObjectValueType_Ehandle);
|
||||
@@ -514,6 +583,10 @@ public MRESReturn Detour_OnTakeDamage(int entity, Handle hReturn, Handle hParams
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public void OnClientHurt(Event hEvent, const char[] sEvent, bool bDontBroadcast)
|
||||
{
|
||||
if(!g_hCVar_HitmarkerEnabled.BoolValue)
|
||||
{
|
||||
return;
|
||||
}
|
||||
int client = GetClientOfUserId(hEvent.GetInt("attacker"));
|
||||
int victim = GetClientOfUserId(hEvent.GetInt("userid"));
|
||||
|
||||
@@ -544,6 +617,10 @@ public void OnClientHurt(Event hEvent, const char[] sEvent, bool bDontBroadcast)
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
void ShowOverlay(int client, float fTime = 0.3)
|
||||
{
|
||||
if(!g_hCVar_HitmarkerEnabled.BoolValue)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (g_bHitmarkerSound[client])
|
||||
{
|
||||
float fVolume = g_iHitmarkerSoundVolume[client] / 100.0;
|
||||
|
||||
@@ -177,6 +177,9 @@ public Action OnTakeDamage(int victim, int &attacker, int &inflictor, float &dam
|
||||
if(!IsPlayerAlive(victim))
|
||||
return Plugin_Continue;
|
||||
|
||||
if(!IsPlayerAlive(attacker))
|
||||
return Plugin_Continue;
|
||||
|
||||
if(!g_bKnifeBan)
|
||||
return Plugin_Continue;
|
||||
|
||||
@@ -552,7 +555,7 @@ public void OpenKnifebanlistMenu(int client)
|
||||
menu.Display(client, MENU_TIME_FOREVER);
|
||||
}
|
||||
|
||||
public int MenuHandler_MainMenu(Menu menu, MenuAction action, int client, int selection)
|
||||
public void MenuHandler_MainMenu(Menu menu, MenuAction action, int client, int selection)
|
||||
{
|
||||
switch(action)
|
||||
{
|
||||
@@ -644,7 +647,7 @@ public void OpenKnifebanlistOfTheClient(int client, char sBuffer[32])
|
||||
menuclient.Display(client, MENU_TIME_FOREVER);
|
||||
}
|
||||
|
||||
public int MenuHandler_ClientMenu(Menu menuclient, MenuAction action, int client, int selection)
|
||||
public void MenuHandler_ClientMenu(Menu menuclient, MenuAction action, int client, int selection)
|
||||
{
|
||||
switch(action)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,955 @@
|
||||
#pragma semicolon 1
|
||||
|
||||
#include <sourcemod>
|
||||
#include <sdktools>
|
||||
#include <sdkhooks>
|
||||
#include <cstrike>
|
||||
#include <multicolors>
|
||||
#include <zombiereloaded>
|
||||
|
||||
//2026: add playtime requirements for being unrestricted
|
||||
#include <unloze_playtime>
|
||||
|
||||
char g_sBanListFilePath[256];
|
||||
|
||||
Database g_hDatabase;
|
||||
|
||||
bool g_bKnifeBanned[MAXPLAYERS + 1];
|
||||
bool g_bKnifeBan;
|
||||
|
||||
ArrayList g_TempBanned;
|
||||
|
||||
KeyValues Banlist;
|
||||
|
||||
int g_hActiveWeapon;
|
||||
int g_iClientHours[MAXPLAYERS + 1];
|
||||
int g_iClientMinutes[MAXPLAYERS + 1];
|
||||
|
||||
public Plugin myinfo =
|
||||
{
|
||||
name = "KnifeBan",
|
||||
author = "Dogan",
|
||||
description = "Tools to handle Zombie Knifers in ZE",
|
||||
version = "2.0.0",
|
||||
url = ""
|
||||
}
|
||||
|
||||
public void OnPluginStart()
|
||||
{
|
||||
LoadTranslations("common.phrases");
|
||||
|
||||
ConVar cvar;
|
||||
HookConVarChange((cvar = CreateConVar("sm_knifeban_block", "1", "1 = Knifebanned clients are blocked, 0 = Knifebanned clients are not blocked", FCVAR_NONE, true, 0.0, true, 1.0)), g_cvKnifeBan);
|
||||
g_bKnifeBan = cvar.BoolValue;
|
||||
delete cvar;
|
||||
|
||||
AutoExecConfig(true, "plugin.KnifeBan");
|
||||
|
||||
RegAdminCmd("sm_knifeban", Command_Knifeban, ADMFLAG_BAN, "Knifebans a client");
|
||||
RegAdminCmd("sm_knifeunban", Command_Knifeunban, ADMFLAG_BAN, "Knifeunbans a client");
|
||||
RegConsoleCmd("sm_knifestatus", Command_Knifestatus, "Returns the knifestatus of a client");
|
||||
RegConsoleCmd("sm_knifebanlist", Command_Knifebanlist, "Returns the knifebanlist of all clients currently on the server");
|
||||
|
||||
g_hActiveWeapon = FindSendPropInfo("CBaseCombatCharacter", "m_hActiveWeapon");
|
||||
if(g_hActiveWeapon == -1)
|
||||
SetFailState("Couldnt find CBaseCombatCharacter::m_hActiveWeapon");
|
||||
|
||||
CreateTimer(5.0, CheckKnifeBans, _, TIMER_REPEAT);
|
||||
CreateTimer(300.0, CheckKnifeBansFromWeb, _, TIMER_REPEAT); //check every 5 minutes if something new was done on webpannel
|
||||
|
||||
g_TempBanned = new ArrayList();
|
||||
|
||||
Banlist = new KeyValues("banlist");
|
||||
BuildPath(Path_SM, g_sBanListFilePath, sizeof(g_sBanListFilePath), "configs/knifeban/banlist.cfg");
|
||||
Banlist.ImportFromFile(g_sBanListFilePath);
|
||||
if(!FileExists(g_sBanListFilePath))
|
||||
SetFailState("[KnifeBan] Config file missing!");
|
||||
|
||||
|
||||
for(int i = 1; i <= MaxClients; i++)
|
||||
{
|
||||
if(IsClientInGame(i))
|
||||
OnClientPostAdminCheck(i);
|
||||
}
|
||||
|
||||
if (!g_hDatabase)
|
||||
{
|
||||
Database.Connect(SQL_OnDatabaseConnect, "unloze_knifeban_web");
|
||||
}
|
||||
}
|
||||
|
||||
public void OnMapStart()
|
||||
{
|
||||
if (!g_hDatabase)
|
||||
{
|
||||
Database.Connect(SQL_OnDatabaseConnect, "unloze_knifeban_web");
|
||||
}
|
||||
}
|
||||
|
||||
public void SQL_OnDatabaseConnect(Database db, const char[] error, any data)
|
||||
{
|
||||
if(!db || strlen(error))
|
||||
{
|
||||
LogError("Database error: %s", error);
|
||||
return;
|
||||
}
|
||||
g_hDatabase = db;
|
||||
}
|
||||
|
||||
public void OnPluginEnd()
|
||||
{
|
||||
Banlist.Rewind();
|
||||
if(!Banlist.ExportToFile(g_sBanListFilePath))
|
||||
SetFailState("[KnifeBan] Config file missing!");
|
||||
}
|
||||
|
||||
public void g_cvKnifeBan(ConVar convar, const char[] oldValue, const char[] newValue)
|
||||
{
|
||||
g_bKnifeBan = convar.BoolValue;
|
||||
}
|
||||
|
||||
public void OnClientPutInServer(int client)
|
||||
{
|
||||
//now knifebanned people cant knife fakeclients forward.
|
||||
if (IsFakeClient(client))
|
||||
{
|
||||
SDKHook(client, SDKHook_OnTakeDamage, OnTakeDamage);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnClientPostAdminCheck(int client)
|
||||
{
|
||||
//autism check
|
||||
if (!IsFakeClient(client))
|
||||
{
|
||||
SDKHook(client, SDKHook_OnTakeDamage, OnTakeDamage);
|
||||
}
|
||||
|
||||
char sAuth[32];
|
||||
GetClientAuthId(client, AuthId_Steam2, sAuth, sizeof(sAuth));
|
||||
|
||||
Banlist.Rewind();
|
||||
|
||||
if(Banlist.JumpToKey(sAuth))
|
||||
{
|
||||
int length = Banlist.GetNum("duration");
|
||||
int length_minutes = Banlist.GetNum("duration_minutes");
|
||||
int time = Banlist.GetNum("time");
|
||||
int time_minutes = Banlist.GetNum("time_minutes", 0);
|
||||
|
||||
if(length == -1)
|
||||
g_bKnifeBanned[client] = true;
|
||||
else if(length > 0 || length_minutes > 0)
|
||||
CheckIfClientIsStillKnifeBanned(sAuth, client, time, time_minutes, length, length_minutes);
|
||||
}
|
||||
|
||||
int accountid = GetSteamAccountID(client);
|
||||
if(accountid != 0)
|
||||
{
|
||||
int index = g_TempBanned.FindValue(accountid);
|
||||
if(index != -1)
|
||||
g_bKnifeBanned[client] = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void OnClientDisconnect(int client)
|
||||
{
|
||||
g_iClientHours[client] = 0;
|
||||
g_iClientMinutes[client] = 0;
|
||||
g_bKnifeBanned[client] = false;
|
||||
}
|
||||
|
||||
public void OnMapEnd()
|
||||
{
|
||||
g_TempBanned.Clear();
|
||||
}
|
||||
|
||||
public Action CheckKnifeBansFromWeb(Handle timer)
|
||||
{
|
||||
char sQuery[512];
|
||||
Format(sQuery, sizeof(sQuery), "select client_steamid, web_addban_required, web_unban_required, web_ban_delete, web_edit_required, time_played_start, length, krcb.admin_name from `KbRestrict_CurrentBans` krcb where krcb.web_addban_required = 1 or krcb.web_unban_required = 1 or krcb.web_ban_delete = 1 or krcb.web_edit_required = 1");
|
||||
g_hDatabase.Query(SQL_OnQueryCompletedWebKbans, sQuery);
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
public void SQL_OnQueryCompletedWebKbans(Database db, DBResultSet results, const char[] error, int iSerial)
|
||||
{
|
||||
if (!db || strlen(error))
|
||||
{
|
||||
delete results;
|
||||
LogError("Query error 3: %s", error);
|
||||
return;
|
||||
}
|
||||
while (results.RowCount && results.FetchRow())
|
||||
{
|
||||
char sSID[64];
|
||||
results.FetchString(0, sSID, sizeof(sSID));
|
||||
int web_addban_required = results.FetchInt(1);
|
||||
int web_unban_required = results.FetchInt(2);
|
||||
int web_ban_delete = results.FetchInt(3);
|
||||
int web_edit_required = results.FetchInt(4);
|
||||
int time_played_start = results.FetchInt(5);
|
||||
int length = results.FetchInt(6);
|
||||
char admin_name[255];
|
||||
results.FetchString(7, admin_name, sizeof(admin_name));
|
||||
|
||||
char sQuery[512];
|
||||
if (web_addban_required)
|
||||
{
|
||||
file_addban(sSID, time_played_start, length, admin_name);
|
||||
Format(sQuery, sizeof(sQuery), "update `KbRestrict_CurrentBans` set web_addban_required = 0 where client_steamid = '%s' and web_addban_required = 1", sSID);
|
||||
g_hDatabase.Query(SQL_FinishedQuery, sQuery);
|
||||
}
|
||||
else if (web_unban_required)
|
||||
{
|
||||
file_unban(sSID);
|
||||
Format(sQuery, sizeof(sQuery), "update `KbRestrict_CurrentBans` set web_unban_required = 0 where client_steamid = '%s' and web_unban_required = 1", sSID);
|
||||
g_hDatabase.Query(SQL_FinishedQuery, sQuery);
|
||||
}
|
||||
else if (web_ban_delete)
|
||||
{
|
||||
file_unban(sSID);
|
||||
Format(sQuery, sizeof(sQuery), "delete from `KbRestrict_CurrentBans` where client_steamid = '%s' and web_ban_delete = 1", sSID);
|
||||
g_hDatabase.Query(SQL_FinishedQuery, sQuery);
|
||||
}
|
||||
else if (web_edit_required)
|
||||
{
|
||||
file_edit_ban(sSID, length);
|
||||
Format(sQuery, sizeof(sQuery), "update `KbRestrict_CurrentBans` set web_edit_required = 0 where client_steamid = '%s' and web_edit_required = 1", sSID);
|
||||
g_hDatabase.Query(SQL_FinishedQuery, sQuery);
|
||||
}
|
||||
}
|
||||
delete results;
|
||||
}
|
||||
|
||||
public void file_edit_ban(char sAuth[64], int length)
|
||||
{
|
||||
int length_hours = length / 60;
|
||||
int length_minutes = length % 60;
|
||||
|
||||
if(Banlist.JumpToKey(sAuth))
|
||||
{
|
||||
Banlist.SetNum("duration", length_hours);
|
||||
Banlist.SetNum("duration_minutes", length_minutes);
|
||||
Banlist.Rewind();
|
||||
Banlist.ExportToFile(g_sBanListFilePath);
|
||||
}
|
||||
}
|
||||
|
||||
public void file_unban(char sAuth[64])
|
||||
{
|
||||
if(Banlist.JumpToKey(sAuth))
|
||||
{
|
||||
int history = Banlist.GetNum("history");
|
||||
Banlist.SetNum("history", history - 1);
|
||||
Banlist.SetNum("time", 0);
|
||||
Banlist.SetNum("time_minutes", 0);
|
||||
Banlist.SetNum("duration", 0);
|
||||
Banlist.SetNum("duration_minutes", 0);
|
||||
Banlist.SetString("admin", "");
|
||||
Banlist.Rewind();
|
||||
Banlist.ExportToFile(g_sBanListFilePath);
|
||||
}
|
||||
}
|
||||
|
||||
public void file_addban(char sAuth[64], int time_played_start, int length, char sAdmin[255])
|
||||
{
|
||||
int time = time_played_start / 60;
|
||||
int time_minutes = time_played_start & 60;
|
||||
|
||||
int length_hours = length / 60;
|
||||
int length_minutes = length % 60;
|
||||
|
||||
if( length_hours || length_minutes)
|
||||
{
|
||||
if(Banlist.JumpToKey(sAuth, true))
|
||||
{
|
||||
int history = Banlist.GetNum("history");
|
||||
Banlist.SetNum("history", history + 1);
|
||||
Banlist.SetNum("time", time);
|
||||
Banlist.SetNum("time_minutes", time_minutes);
|
||||
Banlist.SetNum("duration", length_hours);
|
||||
Banlist.SetNum("duration_minutes", length_minutes);
|
||||
Banlist.SetString("admin", sAdmin);
|
||||
Banlist.Rewind();
|
||||
Banlist.ExportToFile(g_sBanListFilePath);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(Banlist.JumpToKey(sAuth, true))
|
||||
{
|
||||
int history = Banlist.GetNum("history");
|
||||
Banlist.SetNum("history", history + 1);
|
||||
Banlist.SetNum("time", time);
|
||||
Banlist.SetNum("time_minutes", time_minutes);
|
||||
Banlist.SetNum("duration", -1);
|
||||
Banlist.SetNum("duration_minutes", -1);
|
||||
Banlist.SetString("admin", sAdmin);
|
||||
Banlist.Rewind();
|
||||
Banlist.ExportToFile(g_sBanListFilePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Action CheckKnifeBans(Handle timer)
|
||||
{
|
||||
for(int i = 1; i <= MaxClients; i++)
|
||||
{
|
||||
if(!IsClientInGame(i))
|
||||
continue;
|
||||
|
||||
if(g_bKnifeBanned[i])
|
||||
{
|
||||
char sAuth[32];
|
||||
GetClientAuthId(i, AuthId_Steam2, sAuth, sizeof(sAuth));
|
||||
|
||||
Banlist.Rewind();
|
||||
|
||||
int length;
|
||||
int length_minutes;
|
||||
int time;
|
||||
int time_minutes;
|
||||
if(Banlist.JumpToKey(sAuth))
|
||||
{
|
||||
length = Banlist.GetNum("duration");
|
||||
length_minutes = Banlist.GetNum("duration_minutes");
|
||||
time = Banlist.GetNum("time");
|
||||
time_minutes = Banlist.GetNum("time_minutes", 0);
|
||||
}
|
||||
|
||||
if(length > 0 || length_minutes > 0)
|
||||
CheckIfClientIsStillKnifeBanned(sAuth, i, time, time_minutes, length, length_minutes);
|
||||
}
|
||||
}
|
||||
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
public void CheckIfClientIsStillKnifeBanned(char sAuth[32], int client, int time, int time_minutes, int length, int length_minutes)
|
||||
{
|
||||
int timesinceknifeban = g_iClientHours[client] - time;
|
||||
int timesinceknifeban_minutes = g_iClientMinutes[client] - time_minutes;
|
||||
|
||||
if (timesinceknifeban_minutes < 0)
|
||||
{
|
||||
timesinceknifeban_minutes += 60;
|
||||
timesinceknifeban--;
|
||||
}
|
||||
//PrintToChatAll("timesinceknifeban: %i. length: %i. timesinceknifeban_minutes: %i. length_minutes: %i", timesinceknifeban, length, timesinceknifeban_minutes, length_minutes);
|
||||
if (timesinceknifeban < length)
|
||||
{
|
||||
g_bKnifeBanned[client] = true;
|
||||
}
|
||||
else if (timesinceknifeban <= length && timesinceknifeban_minutes < length_minutes)
|
||||
{
|
||||
g_bKnifeBanned[client] = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_bKnifeBanned[client] = false;
|
||||
Banlist.Rewind();
|
||||
|
||||
if(Banlist.JumpToKey(sAuth))
|
||||
{
|
||||
Banlist.SetNum("time", 0);
|
||||
Banlist.SetNum("time_minutes", 0);
|
||||
Banlist.SetNum("duration", 0);
|
||||
Banlist.SetNum("duration_minutes", 0);
|
||||
Banlist.SetString("admin", "");
|
||||
Banlist.Rewind();
|
||||
Banlist.ExportToFile(g_sBanListFilePath);
|
||||
update_knife_unban_mysql(client);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Action OnTakeDamage(int victim, int &attacker, int &inflictor, float &damage, int &damagetype, int &weapon, float damageForce[3], float damagePosition[3], int damagecustom)
|
||||
{
|
||||
if(attacker < 1 || attacker > MaxClients)
|
||||
return Plugin_Continue;
|
||||
|
||||
if(!IsPlayerAlive(victim))
|
||||
return Plugin_Continue;
|
||||
|
||||
if(!IsPlayerAlive(attacker))
|
||||
return Plugin_Continue;
|
||||
|
||||
if(!g_bKnifeBan)
|
||||
return Plugin_Continue;
|
||||
|
||||
if(!g_bKnifeBanned[attacker])
|
||||
return Plugin_Continue;
|
||||
|
||||
if(!ZR_IsClientZombie(victim))
|
||||
return Plugin_Continue;
|
||||
|
||||
if(!ZR_IsClientHuman(attacker))
|
||||
return Plugin_Continue;
|
||||
|
||||
char sWeapon[32];
|
||||
int iWeapon = GetEntDataEnt2(attacker, g_hActiveWeapon);
|
||||
if(iWeapon != INVALID_ENT_REFERENCE)
|
||||
GetEdictClassname(iWeapon, sWeapon, sizeof(sWeapon));
|
||||
else
|
||||
return Plugin_Continue;
|
||||
|
||||
if(!StrEqual(sWeapon, "weapon_knife") || damagetype & DMG_BLAST)
|
||||
return Plugin_Continue;
|
||||
|
||||
CPrintToChat(attacker, "\x07%s[KnifeBan] \x07%sYou are currently knifebanned.", "E01B5D", "F16767");
|
||||
damage = 0.0;
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
public Action Command_Knifeban(int client, int args)
|
||||
{
|
||||
if(!GetCmdArgs())
|
||||
{
|
||||
CReplyToCommand(client, "\x07%s[KnifeBan] \x07%sUsage: sm_knifeban <#userid/name> [duration]", "E01B5D", "F16767");
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
char sArguments[2][32];
|
||||
GetCmdArg(1, sArguments[0], sizeof(sArguments[]));
|
||||
GetCmdArg(2, sArguments[1], sizeof(sArguments[]));
|
||||
|
||||
int target;
|
||||
if((target = FindTarget(client, sArguments[0], true)) == -1)
|
||||
return Plugin_Handled;
|
||||
|
||||
int length;
|
||||
int length_hours;
|
||||
int length_minutes;
|
||||
|
||||
if(GetCmdArgs() >= 2)
|
||||
{
|
||||
length = StringToInt(sArguments[1]);
|
||||
|
||||
if(length < 0)
|
||||
{
|
||||
CReplyToCommand(client, "\x07%s[KnifeBan] \x07%sThe given knifeban duration is invalid.", "E01B5D", "F16767");
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
length_hours = length / 60;
|
||||
length_minutes = length % 60;
|
||||
}
|
||||
|
||||
if(g_bKnifeBanned[target])
|
||||
{
|
||||
CReplyToCommand(client, "\x07%s[KnifeBan] \x07%sThe given client is already knifebanned.", "E01B5D", "F16767");
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
g_bKnifeBanned[target] = true;
|
||||
int time = g_iClientHours[target];
|
||||
int time_minutes = g_iClientMinutes[target];
|
||||
|
||||
char sAdmin[32];
|
||||
GetClientName(client, sAdmin, sizeof(sAdmin));
|
||||
if(client == 0)
|
||||
sAdmin = "Console";
|
||||
|
||||
char sAuth[32];
|
||||
GetClientAuthId(target, AuthId_Steam2, sAuth, sizeof(sAuth));
|
||||
|
||||
Banlist.Rewind();
|
||||
|
||||
if(GetCmdArgs() >= 2)
|
||||
{
|
||||
if(length_hours || length_minutes)
|
||||
{
|
||||
CPrintToChatAll("\x07%s[KnifeBan] \x07%s%N\x07%s knifebanned \x07%s%N\x07%s for \x07%s%d\x07%s hours and \x07%s%d\x07%s minutes Playtime.", "E01B5D", "EDEDED", client, "F16767", "EDEDED", target, "F16767", "EDEDED", length_hours, "F16767", "EDEDED", length_minutes, "F16767");
|
||||
LogAction(client, target, "%L knifebanned %L for %d hours and %d minutes.", client, target, length_hours, length_minutes);
|
||||
if(Banlist.JumpToKey(sAuth, true))
|
||||
{
|
||||
int history = Banlist.GetNum("history");
|
||||
Banlist.SetNum("history", history + 1);
|
||||
Banlist.SetNum("time", time);
|
||||
Banlist.SetNum("time_minutes", time_minutes);
|
||||
Banlist.SetNum("duration", length_hours);
|
||||
Banlist.SetNum("duration_minutes", length_minutes);
|
||||
Banlist.SetString("admin", sAdmin);
|
||||
Banlist.Rewind();
|
||||
Banlist.ExportToFile(g_sBanListFilePath);
|
||||
|
||||
insert_knifeban_mysql(client, target, length_hours, length_minutes, time, time_minutes);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CPrintToChatAll("\x07%s[KnifeBan] \x07%s%N\x07%s knifebanned \x07%s%N\x07%s permanently.", "E01B5D", "EDEDED", client, "F16767", "EDEDED", target, "F16767");
|
||||
LogAction(client, target, "%L knifebanned %L permanently.", client, target);
|
||||
if(Banlist.JumpToKey(sAuth, true))
|
||||
{
|
||||
int history = Banlist.GetNum("history");
|
||||
Banlist.SetNum("history", history + 1);
|
||||
Banlist.SetNum("time", time);
|
||||
Banlist.SetNum("time_minutes", time_minutes);
|
||||
Banlist.SetNum("duration", -1);
|
||||
Banlist.SetNum("duration_minutes", -1);
|
||||
Banlist.SetString("admin", sAdmin);
|
||||
Banlist.Rewind();
|
||||
Banlist.ExportToFile(g_sBanListFilePath);
|
||||
|
||||
insert_knifeban_mysql(client, target, -1, -1, time, time_minutes);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CPrintToChatAll("\x07%s[KnifeBan] \x07%s%N\x07%s knifebanned \x07%s%N\x07%s temporarily.", "E01B5D", "EDEDED", client, "F16767", "EDEDED", target, "F16767");
|
||||
LogAction(client, target, "%L knifebanned %L temporarily.", client, target);
|
||||
if(Banlist.JumpToKey(sAuth, true))
|
||||
{
|
||||
int history = Banlist.GetNum("history");
|
||||
Banlist.SetNum("history", history + 1);
|
||||
Banlist.SetNum("time", time);
|
||||
Banlist.SetNum("time_minutes", time_minutes);
|
||||
Banlist.SetString("admin", sAdmin);
|
||||
Banlist.Rewind();
|
||||
Banlist.ExportToFile(g_sBanListFilePath);
|
||||
int accountid = GetSteamAccountID(target);
|
||||
g_TempBanned.Push(accountid);
|
||||
}
|
||||
}
|
||||
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
public void insert_knifeban_mysql(int admin, int knife_banned_player, int length_hours, int length_minutes, int time_start_playertime_hours, int time_start_playertime_minutes)
|
||||
{
|
||||
char sNameAdmin[MAX_NAME_LENGTH];
|
||||
GetClientName(admin, sNameAdmin, sizeof(sNameAdmin));
|
||||
int size2 = 2 * strlen(sNameAdmin) + 1;
|
||||
char[] sEscapedNameAdmin = new char[size2 + 1];
|
||||
g_hDatabase.Escape(sNameAdmin, sEscapedNameAdmin, size2 + 1);
|
||||
char sSIDAdmin[64];
|
||||
GetClientAuthId(admin, AuthId_Steam2, sSIDAdmin, sizeof(sSIDAdmin));
|
||||
|
||||
|
||||
char sNameClient[MAX_NAME_LENGTH];
|
||||
GetClientName(knife_banned_player, sNameClient, sizeof(sNameClient));
|
||||
size2 = 2 * strlen(sNameClient) + 1;
|
||||
char[] sEscapedNameClient = new char[size2 + 1];
|
||||
g_hDatabase.Escape(sNameClient, sEscapedNameClient, size2 + 1);
|
||||
char sSIDClient[64];
|
||||
GetClientAuthId(knife_banned_player, AuthId_Steam2, sSIDClient, sizeof(sSIDClient));
|
||||
char sIP[32];
|
||||
GetClientIP(knife_banned_player, sIP, sizeof(sIP));
|
||||
|
||||
char currentMap[64];
|
||||
GetCurrentMap(currentMap, sizeof(currentMap));
|
||||
|
||||
char sQuery[512];
|
||||
//length is assumed to be minutes.
|
||||
int length = (length_hours * 60) + length_minutes;
|
||||
if (length_hours == -1)
|
||||
{
|
||||
length = 0;
|
||||
}
|
||||
|
||||
int playtime_start_client = (time_start_playertime_hours * 60) + time_start_playertime_minutes;
|
||||
int playtime_end_client = playtime_start_client + length;
|
||||
|
||||
if (length_hours == -1)
|
||||
{
|
||||
playtime_end_client = -1;
|
||||
playtime_start_client = -1;
|
||||
}
|
||||
|
||||
Format(sQuery, sizeof(sQuery), "INSERT INTO `KbRestrict_CurrentBans` (`client_name`, `client_steamid`, `client_ip`, `admin_name`, `admin_steamid`, `reason`, `map`, `length`, `time_played_start`, `time_played_end`, `is_expired`, `is_removed`, `admin_name_removed`, `admin_steamid_removed`, `time_stamp_removed`, `reason_removed`) VALUES ('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%i', '%i', '%i', 0, 0, ' ', ' ', 0, ' ')", sEscapedNameClient, sSIDClient, sIP, sEscapedNameAdmin, sSIDAdmin, "get fucked", currentMap, length, playtime_start_client, playtime_end_client);
|
||||
g_hDatabase.Query(SQL_FinishedQuery, sQuery, _, DBPrio_Normal);
|
||||
}
|
||||
|
||||
public void SQL_FinishedQuery(Database db, DBResultSet results, const char[] error, any data)
|
||||
{
|
||||
if (!db || strlen(error))
|
||||
{
|
||||
LogError("Query error 3: %s", error);
|
||||
}
|
||||
delete results;
|
||||
}
|
||||
|
||||
public Action Command_Knifeunban(int client, int args)
|
||||
{
|
||||
if(!GetCmdArgs())
|
||||
{
|
||||
CReplyToCommand(client, "\x07%s[KnifeBan] \x07%sUsage: sm_knifeunban <#userid/name>", "E01B5D", "F16767");
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
char sArguments[1][32];
|
||||
GetCmdArg(1, sArguments[0], sizeof(sArguments[]));
|
||||
|
||||
int target;
|
||||
if((target = FindTarget(client, sArguments[0], true)) == -1)
|
||||
return Plugin_Handled;
|
||||
|
||||
if(!g_bKnifeBanned[target])
|
||||
{
|
||||
CReplyToCommand(client, "\x07%s[KnifeBan] \x07%sThe given client is not knifebanned.", "E01B5D", "F16767");
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
g_bKnifeBanned[target] = false;
|
||||
Banlist.Rewind();
|
||||
|
||||
char sAuth[32];
|
||||
GetClientAuthId(target, AuthId_Steam2, sAuth, sizeof(sAuth));
|
||||
|
||||
if(Banlist.JumpToKey(sAuth))
|
||||
{
|
||||
int history = Banlist.GetNum("history");
|
||||
Banlist.SetNum("history", history - 1);
|
||||
Banlist.SetNum("time", 0);
|
||||
Banlist.SetNum("time_minutes", 0);
|
||||
Banlist.SetNum("duration", 0);
|
||||
Banlist.SetNum("duration_minutes", 0);
|
||||
Banlist.SetString("admin", "");
|
||||
Banlist.Rewind();
|
||||
Banlist.ExportToFile(g_sBanListFilePath);
|
||||
update_knife_unban_mysql(target);
|
||||
}
|
||||
|
||||
int accountid = GetSteamAccountID(target);
|
||||
if(accountid != 0)
|
||||
{
|
||||
int index = g_TempBanned.FindValue(accountid);
|
||||
if(index != -1)
|
||||
g_TempBanned.Erase(index);
|
||||
}
|
||||
|
||||
CPrintToChatAll("\x07%s[KnifeBan] \x07%s%N\x07%s knifeunbanned \x07%s%N\x07%s.", "E01B5D", "EDEDED", client, "F16767", "EDEDED", target, "F16767");
|
||||
LogAction(client, target, "%L knifeunbanned %L.", client, target);
|
||||
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
public void update_knife_unban_mysql(int knife_unbanned_client)
|
||||
{
|
||||
char sSIDClient[64];
|
||||
GetClientAuthId(knife_unbanned_client, AuthId_Steam2, sSIDClient, sizeof(sSIDClient));
|
||||
|
||||
char sQuery[512];
|
||||
Format(sQuery, sizeof(sQuery), "update `KbRestrict_CurrentBans` set is_expired = 1 where client_steamid = '%s' and is_expired = 0", sSIDClient);
|
||||
g_hDatabase.Query(SQL_FinishedQuery, sQuery, _, DBPrio_Normal);
|
||||
}
|
||||
|
||||
public Action Command_Knifestatus(int client, int args)
|
||||
{
|
||||
if(CheckCommandAccess(client, "", ADMFLAG_BAN) && GetCmdArgs())
|
||||
{
|
||||
char sArguments[1][32];
|
||||
GetCmdArg(1, sArguments[0], sizeof(sArguments[]));
|
||||
|
||||
int target;
|
||||
if((target = FindTarget(client, sArguments[0], true)) == -1)
|
||||
return Plugin_Handled;
|
||||
|
||||
char sAuth[32];
|
||||
GetClientAuthId(target, AuthId_Steam2, sAuth, sizeof(sAuth));
|
||||
int accountid = GetSteamAccountID(target);
|
||||
int index = g_TempBanned.FindValue(accountid);
|
||||
|
||||
Banlist.Rewind();
|
||||
int length_hours;
|
||||
int length_minutes;
|
||||
|
||||
int time;
|
||||
int time_minutes;
|
||||
|
||||
int history;
|
||||
if(Banlist.JumpToKey(sAuth))
|
||||
{
|
||||
length_hours = Banlist.GetNum("duration");
|
||||
length_minutes = Banlist.GetNum("duration_minutes");
|
||||
|
||||
time = Banlist.GetNum("time");
|
||||
time_minutes = Banlist.GetNum("time_minutes");
|
||||
|
||||
history = Banlist.GetNum("history");
|
||||
}
|
||||
|
||||
CReplyToCommand(client, "\x07%s[KnifeBan] \x07%s%N\x07%s has been knifebanned \x07%s%d\x07%s times before.", "E01B5D", "EDEDED", target, "F16767", "EDEDED", history, "F16767");
|
||||
|
||||
if(g_bKnifeBanned[target] && index != -1)
|
||||
{
|
||||
CReplyToCommand(client, "\x07%s[KnifeBan] \x07%s%N\x07%s is currently temporarily knifebanned.", "E01B5D", "EDEDED", target, "F16767");
|
||||
return Plugin_Handled;
|
||||
}
|
||||
else if(g_bKnifeBanned[target] && length_hours == -1)
|
||||
{
|
||||
CReplyToCommand(client, "\x07%s[KnifeBan] \x07%s%N\x07%s is currently permanently knifebanned.", "E01B5D", "EDEDED", target, "F16767");
|
||||
return Plugin_Handled;
|
||||
}
|
||||
else if(g_bKnifeBanned[target] && (length_hours > 0 || length_minutes > 0))
|
||||
{
|
||||
char sTimeRemaining[64];
|
||||
int timesinceknifeban = g_iClientHours[target] - time;
|
||||
int timesinceknifeban_minutes = g_iClientMinutes[target] - time_minutes;
|
||||
|
||||
if (timesinceknifeban_minutes < 0)
|
||||
{
|
||||
timesinceknifeban_minutes += 60;
|
||||
timesinceknifeban--;
|
||||
}
|
||||
|
||||
int iTimeRemaining = length_hours - timesinceknifeban;
|
||||
int iTimeRemaining_minutes = length_minutes - timesinceknifeban_minutes;
|
||||
if (iTimeRemaining_minutes < 0)
|
||||
{
|
||||
iTimeRemaining_minutes += 60;
|
||||
iTimeRemaining--;
|
||||
}
|
||||
|
||||
Format(sTimeRemaining, sizeof(sTimeRemaining), "%d Hours and %d minutes playtime", iTimeRemaining, iTimeRemaining_minutes);
|
||||
CReplyToCommand(client, "\x07%s[KnifeBan] \x07%s%N\x07%s is currently knifebanned for another \x07%s%s\x07%s.", "E01B5D", "EDEDED", target, "F16767", "EDEDED", sTimeRemaining, "F16767");
|
||||
return Plugin_Handled;
|
||||
}
|
||||
else
|
||||
{
|
||||
CReplyToCommand(client, "\x07%s[KnifeBan] \x07%s%N\x07%s is currently not knifebanned.", "E01B5D", "EDEDED", target, "F16767");
|
||||
return Plugin_Handled;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
char sAuth[32];
|
||||
GetClientAuthId(client, AuthId_Steam2, sAuth, sizeof(sAuth));
|
||||
int accountid = GetSteamAccountID(client);
|
||||
int index = g_TempBanned.FindValue(accountid);
|
||||
|
||||
Banlist.Rewind();
|
||||
int length_hours;
|
||||
int length_minutes;
|
||||
|
||||
int time;
|
||||
int time_minutes;
|
||||
if(Banlist.JumpToKey(sAuth))
|
||||
{
|
||||
length_hours = Banlist.GetNum("duration");
|
||||
length_minutes = Banlist.GetNum("duration_minutes");
|
||||
|
||||
time = Banlist.GetNum("time");
|
||||
time_minutes = Banlist.GetNum("time_minutes");
|
||||
}
|
||||
|
||||
if(g_bKnifeBanned[client] && index != -1)
|
||||
{
|
||||
CReplyToCommand(client, "\x07%s[KnifeBan] \x07%sYou are currently temporarily knifebanned.", "E01B5D", "F16767");
|
||||
return Plugin_Handled;
|
||||
}
|
||||
else if(g_bKnifeBanned[client] && length_hours == -1)
|
||||
{
|
||||
CReplyToCommand(client, "\x07%s[KnifeBan] \x07%sYou are currently permanently knifebanned.", "E01B5D", "F16767");
|
||||
return Plugin_Handled;
|
||||
}
|
||||
else if(g_bKnifeBanned[client] && (length_hours > 0 || length_minutes > 0))
|
||||
{
|
||||
char sTimeRemaining[64];
|
||||
int timesinceknifeban = g_iClientHours[client] - time;
|
||||
int timesinceknifeban_minutes = g_iClientMinutes[client] - time_minutes;
|
||||
|
||||
if (timesinceknifeban_minutes < 0)
|
||||
{
|
||||
timesinceknifeban_minutes += 60;
|
||||
timesinceknifeban--;
|
||||
}
|
||||
|
||||
int iTimeRemaining = length_hours - timesinceknifeban;
|
||||
int iTimeRemaining_minutes = length_minutes - timesinceknifeban_minutes;
|
||||
if (iTimeRemaining_minutes < 0)
|
||||
{
|
||||
iTimeRemaining_minutes += 60;
|
||||
iTimeRemaining--;
|
||||
}
|
||||
|
||||
Format(sTimeRemaining, sizeof(sTimeRemaining), "%d Hours and %d minutes playtime", iTimeRemaining, iTimeRemaining_minutes);
|
||||
CReplyToCommand(client, "\x07%s[KnifeBan] \x07%sYou are currently knifebanned for another \x07%s%s\x07%s.", "E01B5D", "F16767", "EDEDED", sTimeRemaining, "F16767");
|
||||
return Plugin_Handled;
|
||||
}
|
||||
else
|
||||
{
|
||||
CReplyToCommand(client, "\x07%s[KnifeBan] \x07%sYou are currently not knifebanned.", "E01B5D", "F16767");
|
||||
return Plugin_Handled;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Action Command_Knifebanlist(int client, int args)
|
||||
{
|
||||
char aBuf[1024];
|
||||
char aBuf2[MAX_NAME_LENGTH];
|
||||
|
||||
for(int i = 1; i <= MaxClients; i++)
|
||||
{
|
||||
if(IsClientInGame(i) && !IsFakeClient(i))
|
||||
{
|
||||
if(g_bKnifeBanned[i])
|
||||
{
|
||||
GetClientName(i, aBuf2, sizeof(aBuf2));
|
||||
StrCat(aBuf, sizeof(aBuf), aBuf2);
|
||||
StrCat(aBuf, sizeof(aBuf), ", ");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(strlen(aBuf))
|
||||
{
|
||||
aBuf[strlen(aBuf) - 2] = 0;
|
||||
CReplyToCommand(client, "\x07%s[KnifeBan] \x07%sCurrently knifebanned clients: \x07%s%s", "E01B5D", "F16767", "EDEDED", aBuf);
|
||||
}
|
||||
else
|
||||
CReplyToCommand(client, "\x07%s[KnifeBan] \x07%sCurrently knifebanned clients: \x07%snone", "E01B5D", "F16767", "EDEDED");
|
||||
|
||||
if(CheckCommandAccess(client, "", ADMFLAG_BAN))
|
||||
OpenKnifebanlistMenu(client);
|
||||
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
public void OpenKnifebanlistMenu(int client)
|
||||
{
|
||||
if(client == 0)
|
||||
return;
|
||||
|
||||
Menu menu = new Menu(MenuHandler_MainMenu);
|
||||
|
||||
menu.SetTitle("Currently knifebanned clients:", client);
|
||||
|
||||
char sBuffer[32];
|
||||
int iMenuItem;
|
||||
char sMenuItem[8];
|
||||
|
||||
for(int i = 1; i <= MaxClients; i++)
|
||||
{
|
||||
if(IsClientInGame(i) && !IsFakeClient(i))
|
||||
{
|
||||
if(g_bKnifeBanned[i])
|
||||
{
|
||||
GetClientName(i, sBuffer, sizeof(sBuffer));
|
||||
IntToString(iMenuItem, sMenuItem, sizeof(sMenuItem));
|
||||
menu.AddItem(sMenuItem, sBuffer);
|
||||
iMenuItem++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
menu.Display(client, MENU_TIME_FOREVER);
|
||||
}
|
||||
|
||||
public void MenuHandler_MainMenu(Menu menu, MenuAction action, int client, int selection)
|
||||
{
|
||||
switch(action)
|
||||
{
|
||||
case(MenuAction_Select):
|
||||
{
|
||||
char sMenuItem[8];
|
||||
char sBuffer[32];
|
||||
int flag;
|
||||
menu.GetItem(selection, sMenuItem, sizeof(sMenuItem), flag, sBuffer, sizeof(sBuffer));
|
||||
OpenKnifebanlistOfTheClient(client, sBuffer);
|
||||
}
|
||||
case(MenuAction_End):
|
||||
{
|
||||
delete menu;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void OpenKnifebanlistOfTheClient(int client, char sBuffer[32])
|
||||
{
|
||||
Menu menuclient = new Menu(MenuHandler_ClientMenu);
|
||||
|
||||
char sTitle[64] = "Knifebanned client: ";
|
||||
|
||||
StrCat(sTitle, sizeof(sTitle), sBuffer);
|
||||
|
||||
int target;
|
||||
char sName[32];
|
||||
|
||||
for(int i = 1; i <= MaxClients; i++)
|
||||
{
|
||||
if(IsClientInGame(i) && !IsFakeClient(i) && g_bKnifeBanned[i])
|
||||
{
|
||||
GetClientName(i, sName, sizeof(sName));
|
||||
|
||||
if(StrEqual(sName, sBuffer))
|
||||
{
|
||||
target = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
char sAuth[32];
|
||||
GetClientAuthId(target, AuthId_Steam2, sAuth, sizeof(sAuth));
|
||||
|
||||
Banlist.Rewind();
|
||||
|
||||
int length;
|
||||
int length_minutes;
|
||||
|
||||
int history;
|
||||
char sAdmin[32];
|
||||
if(Banlist.JumpToKey(sAuth))
|
||||
{
|
||||
length = Banlist.GetNum("duration");
|
||||
length_minutes = Banlist.GetNum("duration_minutes");
|
||||
|
||||
history = Banlist.GetNum("history");
|
||||
Banlist.GetString("admin", sAdmin, sizeof(sAdmin));
|
||||
}
|
||||
|
||||
menuclient.SetTitle(sTitle, client);
|
||||
|
||||
char sInfo[256];
|
||||
Format(sInfo, sizeof(sInfo), "Banned by: %s", sAdmin);
|
||||
|
||||
menuclient.AddItem("0", sInfo);
|
||||
|
||||
if(length > 0)
|
||||
Format(sInfo, sizeof(sInfo), "Duration: %d Hours and %d minutes playtime", length, length_minutes);
|
||||
else if(length == -1)
|
||||
Format(sInfo, sizeof(sInfo), "Duration: Permanently");
|
||||
else
|
||||
Format(sInfo, sizeof(sInfo), "Duration: Temporarly");
|
||||
|
||||
menuclient.AddItem("1", sInfo);
|
||||
Format(sInfo, sizeof(sInfo), "Ban History: %d times", history);
|
||||
|
||||
menuclient.AddItem("2", sInfo);
|
||||
|
||||
menuclient.AddItem("3", "Back to List");
|
||||
|
||||
menuclient.Display(client, MENU_TIME_FOREVER);
|
||||
}
|
||||
|
||||
public void MenuHandler_ClientMenu(Menu menuclient, MenuAction action, int client, int selection)
|
||||
{
|
||||
switch(action)
|
||||
{
|
||||
case(MenuAction_Select):
|
||||
{
|
||||
switch(selection)
|
||||
{
|
||||
case(4): OpenKnifebanlistMenu(client);
|
||||
}
|
||||
}
|
||||
case(MenuAction_End):
|
||||
{
|
||||
delete menuclient;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose: 2026 getting the ingame play time of the players. called with OnClientPostAdminCheck() forward and timer every 10 minutes
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public void GetPlayerHoursServer(int client, int hours, int minutes)
|
||||
{
|
||||
g_iClientHours[client] = hours;
|
||||
g_iClientMinutes[client] = minutes;
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
-- knifeban_web.KbRestrict_CurrentBans definition
|
||||
|
||||
CREATE TABLE `KbRestrict_CurrentBans` (
|
||||
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`client_name` varchar(64) NOT NULL,
|
||||
`client_steamid` varchar(64) NOT NULL,
|
||||
`client_ip` varchar(64) NOT NULL,
|
||||
`admin_name` varchar(64) NOT NULL,
|
||||
`admin_steamid` varchar(64) NOT NULL,
|
||||
`reason` varchar(255) NOT NULL,
|
||||
`map` varchar(64) NOT NULL,
|
||||
`length` int(11) NOT NULL,
|
||||
`time_stamp_start` datetime DEFAULT current_timestamp(),
|
||||
`time_played_start` int(11) NOT NULL,
|
||||
`time_played_end` int(11) NOT NULL,
|
||||
`is_expired` int(11) NOT NULL,
|
||||
`is_removed` int(11) NOT NULL,
|
||||
`time_stamp_removed` int(20) NOT NULL,
|
||||
`admin_name_removed` varchar(255) NOT NULL,
|
||||
`admin_steamid_removed` varchar(255) NOT NULL,
|
||||
`reason_removed` varchar(255) NOT NULL,
|
||||
`web_unban_required` tinyint(1) NOT NULL DEFAULT 0,
|
||||
`web_addban_required` tinyint(1) NOT NULL DEFAULT 0,
|
||||
`web_ban_delete` tinyint(1) NOT NULL DEFAULT 0,
|
||||
`web_edit_required` tinyint(1) NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_steamid_search` (`client_steamid`),
|
||||
KEY `idx_ip_search` (`client_ip`),
|
||||
KEY `idx_admin_steamid` (`admin_steamid`),
|
||||
KEY `idx_status` (`is_expired`,`is_removed`),
|
||||
KEY `idx_steamid_ip_status` (`client_steamid`,`client_ip`,`is_expired`,`is_removed`),
|
||||
KEY `idx_web_unban_required` (`web_unban_required`),
|
||||
KEY `idx_web_addban_required` (`web_addban_required`),
|
||||
KEY `idx_web_ban_delete` (`web_ban_delete`),
|
||||
KEY `idx_web_edit_required` (`web_edit_required`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS knifeban_web.KbRestrict_weblogs (
|
||||
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`message` varchar(200) NOT NULL,
|
||||
`admin_name` varchar(255) NOT NULL,
|
||||
`admin_steamid` varchar(64) NOT NULL,
|
||||
`client_name` varchar(255) NOT NULL,
|
||||
`client_steamid` varchar(64) NOT NULL,
|
||||
`time_stamp` int(20) NOT NULL,
|
||||
PRIMARY KEY(`id`));
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <dhooks>
|
||||
#include <clientprefs>
|
||||
#include <multicolors>
|
||||
#include <mapchooser_extended>
|
||||
|
||||
#define SetBit(%1,%2) ((%1)[(%2) >> 5] |= (1 << ((%2) & 31)))
|
||||
#define ClearBit(%1,%2) ((%1)[(%2) >> 5] &= ~(1 << ((%2) & 31)))
|
||||
@@ -18,7 +19,7 @@ public Plugin myinfo =
|
||||
name = "LagCompensation",
|
||||
author = "BotoX, Vauff, .Rushaway, maxime1907",
|
||||
description = "",
|
||||
version = "1.0.6",
|
||||
version = "1.2.0",
|
||||
url = ""
|
||||
};
|
||||
|
||||
@@ -26,9 +27,10 @@ bool g_bLateLoad = false;
|
||||
bool g_bHasPhysHooks = true;
|
||||
bool g_bHasOnEntitySpawned = false;
|
||||
bool g_bCheckPing = false;
|
||||
bool g_bMapDisabled = false;
|
||||
int g_iCompenseEntityClass;
|
||||
|
||||
// Dont change this.
|
||||
// Don't change this.
|
||||
#define MAX_EDICTS 2048
|
||||
#define FSOLID_FORCE_WORLD_ALIGNED 0x0040
|
||||
#define FSOLID_ROOT_PARENT_ALIGNED 0x0100
|
||||
@@ -104,6 +106,7 @@ enum struct EntityLagData
|
||||
int iNotMoving;
|
||||
bool bRestore;
|
||||
bool bLateKill;
|
||||
char sClassname[64];
|
||||
}
|
||||
|
||||
LagRecord g_aaLagRecords[MAX_ENTITIES][MAX_RECORDS];
|
||||
@@ -111,6 +114,12 @@ EntityLagData g_aEntityLagData[MAX_ENTITIES];
|
||||
int g_iNumEntities = 0;
|
||||
bool g_bCleaningUp = true;
|
||||
|
||||
// When more than sm_lagcomp_max_entities entities are lag compensation at the same time we switch off lag compensation
|
||||
//enabled again on next round or through command
|
||||
bool g_bLagCompSuspended = false; // nothing new gets compensated
|
||||
bool g_bReleasePending = false; // the tracked entities still have to be released
|
||||
int g_iMaxEntities = MAX_ENTITIES;
|
||||
|
||||
// Cache
|
||||
int g_iGameTick;
|
||||
float g_fTickInterval;
|
||||
@@ -159,22 +168,25 @@ int g_aaFilterClientSolidTouch[((MAXPLAYERS + 1) * MAX_EDICTS) / 32];
|
||||
int g_aBlockTriggerMoved[MAX_EDICTS / 32];
|
||||
int g_aBlacklisted[MAX_EDICTS / 32];
|
||||
|
||||
Handle g_hCookie_EnableLagComp;
|
||||
Handle g_hCookie_LagCompMessages;
|
||||
bool g_bEnableLagComp[MAXPLAYERS + 1];
|
||||
Handle g_hCookie_LagCompSettings;
|
||||
bool g_bDisableLagComp[MAXPLAYERS + 1];
|
||||
bool g_bPlayerLaserCompensated[MAXPLAYERS + 1];
|
||||
bool g_bLagCompMessages[MAXPLAYERS + 1];
|
||||
bool g_bSettingsTouched[MAXPLAYERS + 1];
|
||||
int g_iDisableLagComp[MAXPLAYERS + 1];
|
||||
ConVar g_cvCompenseEntityClass;
|
||||
ConVar g_cvCheckUserPing;
|
||||
ConVar g_cvMinimumPing;
|
||||
ConVar g_cvMaxEntities;
|
||||
|
||||
public void OnPluginStart()
|
||||
{
|
||||
LoadTranslations("common.phrases");
|
||||
|
||||
g_cvCompenseEntityClass = CreateConVar("sm_lagcomp_entityclass", "0", "Only lagcompensate: [0 = All | 1 = func_physbox only]", _, true, 0.0, true, 1.0);
|
||||
g_cvCheckUserPing = CreateConVar("sm_lagcomp_checkuserping", "0", "Check users ping before lagcompensating them [0 = Disabled | 1 = Enabled]", _, true, 0.0, true, 1.0);
|
||||
g_cvMinimumPing = CreateConVar("sm_lagcomp_ping", "150", "Minimum ping threshold to apply lagcomp on a client", _, true, 0.0);
|
||||
g_cvCheckUserPing = CreateConVar("sm_lagcomp_checkuserping", "0", "Check user's ping before lagcompensating them [0 = Disabled | 1 = Enabled]", _, true, 0.0, true, 1.0);
|
||||
g_cvMinimumPing = CreateConVar("sm_lagcomp_ping", "180", "Minimum ping threshold to apply lagcomp on a client", _, true, 0.0);
|
||||
g_cvMaxEntities = CreateConVar("sm_lagcomp_max_entities", "30", "Maximum entities lag compensated at the same time. Going over it disables lag compensation for everyone until the next round. [1-256]", _, true, 1.0, true, float(MAX_ENTITIES));
|
||||
|
||||
AutoExecConfig(true);
|
||||
|
||||
@@ -345,19 +357,21 @@ public void OnPluginStart()
|
||||
HookConVarChange(g_cvCompenseEntityClass, OnConVarChanged);
|
||||
HookConVarChange(g_cvCheckUserPing, OnConVarChanged);
|
||||
HookConVarChange(g_cvMinimumPing, OnConVarChanged);
|
||||
HookConVarChange(g_cvMaxEntities, OnConVarChanged);
|
||||
|
||||
|
||||
g_hCookie_EnableLagComp = RegClientCookie("enable_lagcomp", "Client LagComp Status", CookieAccess_Private);
|
||||
g_hCookie_LagCompMessages = RegClientCookie("lagcomp_messages", "Print LagComp messages in chat", CookieAccess_Private);
|
||||
RegConsoleCmd("sm_lagcomp", OnToggleLagCompSettings);
|
||||
g_hCookie_LagCompSettings = RegClientCookie("lagcomp_settings", "LagCompensation Settings", CookieAccess_Private);
|
||||
RegConsoleCmd("sm_lagcomp", OnLagCompSettings);
|
||||
RegConsoleCmd("sm_0ping", OnToggleLagCompSettings);
|
||||
RegConsoleCmd("sm_0pinglasers", OnToggleLagCompLasersSettings);
|
||||
RegConsoleCmd("sm_checklag", CheckLagComp);
|
||||
SetCookieMenuItem(MenuHandler_CookieMenu, 0, "LagCompensation");
|
||||
|
||||
CreateTimer(0.1, DisableLagCompTimer, _, TIMER_REPEAT);
|
||||
|
||||
RegAdminCmd("sm_unlag", Command_AddLagCompensation, ADMFLAG_ROOT, "sm_unlag <entidx>");
|
||||
RegAdminCmd("sm_unlag", Command_AddLagCompensation, ADMFLAG_ROOT, "sm_unlag <entidx> [late]");
|
||||
RegAdminCmd("sm_lagged", Command_CheckLagCompensated, ADMFLAG_ROOT, "sm_lagged");
|
||||
RegAdminCmd("sm_lagcomp_enable", Command_EnableLagCompensation, ADMFLAG_GENERIC, "Re-enables lag compensation after it was disabled for going over sm_lagcomp_max_entities");
|
||||
|
||||
FilterClientSolidTouch(g_aaFilterClientSolidTouch, true);
|
||||
BlockTriggerMoved(g_aBlockTriggerMoved, true);
|
||||
@@ -405,6 +419,7 @@ public void OnConfigsExecuted()
|
||||
g_iCompenseEntityClass = GetConVarInt(g_cvCompenseEntityClass);
|
||||
g_bCheckPing = GetConVarBool(g_cvCheckUserPing);
|
||||
g_iMinPing = GetConVarInt(g_cvMinimumPing);
|
||||
g_iMaxEntities = GetConVarInt(g_cvMaxEntities);
|
||||
}
|
||||
|
||||
void OnConVarChanged(ConVar convar, const char[] oldValue, const char[] newValue)
|
||||
@@ -415,10 +430,16 @@ void OnConVarChanged(ConVar convar, const char[] oldValue, const char[] newValue
|
||||
g_bCheckPing = GetConVarBool(convar);
|
||||
else if (convar == g_cvMinimumPing)
|
||||
g_iMinPing = GetConVarInt(convar);
|
||||
else if (convar == g_cvMaxEntities)
|
||||
g_iMaxEntities = GetConVarInt(convar);
|
||||
}
|
||||
|
||||
public void OnMapStart()
|
||||
{
|
||||
char map[128];
|
||||
GetCurrentMap(map, sizeof(map));
|
||||
g_bMapDisabled = GetMapLagCompensationDisabled(map);
|
||||
|
||||
bool bLate = g_bLateLoad;
|
||||
|
||||
DHookGamerules(g_hEndGameFrame, true);
|
||||
@@ -486,18 +507,27 @@ public void OnMapEnd()
|
||||
|
||||
public void OnClientConnected(int client)
|
||||
{
|
||||
g_bEnableLagComp[client] = false;
|
||||
// Must match the defaults in ParseClientCookie() for clients whose cookies never cache.
|
||||
g_bDisableLagComp[client] = false;
|
||||
g_bPlayerLaserCompensated[client] = true;
|
||||
g_bLagCompMessages[client] = false;
|
||||
g_bSettingsTouched[client] = false;
|
||||
g_iDisableLagComp[client] = 0;
|
||||
g_aLerpTicks[client] = 0;
|
||||
}
|
||||
|
||||
public void OnClientCookiesCached(int client)
|
||||
{
|
||||
char sBuffer[16];
|
||||
GetClientCookie(client, g_hCookie_EnableLagComp, sBuffer, sizeof(sBuffer));
|
||||
g_bEnableLagComp[client] = StringToInt(sBuffer) != 0; //this wording should make more sense, if the cookie exists its true to enable it.
|
||||
GetClientCookie(client, g_hCookie_LagCompMessages, sBuffer, sizeof(sBuffer));
|
||||
g_bLagCompMessages[client] = StringToInt(sBuffer) != 0;
|
||||
// A toggle made before the cookies arrived wins, so flush it instead of
|
||||
// overwriting the client's choice with the stored value.
|
||||
if(g_bSettingsTouched[client])
|
||||
{
|
||||
SaveLagCompSettings(client);
|
||||
return;
|
||||
}
|
||||
|
||||
// Load all settings from single cookie
|
||||
ParseClientCookie(client, g_bDisableLagComp[client], g_bPlayerLaserCompensated[client], g_bLagCompMessages[client]);
|
||||
}
|
||||
|
||||
public void OnClientSettingsChanged(int client)
|
||||
@@ -511,7 +541,10 @@ public void OnClientSettingsChanged(int client)
|
||||
|
||||
public void OnClientDisconnect(int client)
|
||||
{
|
||||
g_bEnableLagComp[client] = false;
|
||||
g_bDisableLagComp[client] = false;
|
||||
g_bPlayerLaserCompensated[client] = true;
|
||||
g_bLagCompMessages[client] = false;
|
||||
g_bSettingsTouched[client] = false;
|
||||
g_iDisableLagComp[client] = 0;
|
||||
}
|
||||
|
||||
@@ -529,8 +562,9 @@ public void OnEntityCreated(int entity, const char[] classname)
|
||||
DHookEntity(g_hAcceptInput, true, entity);
|
||||
}
|
||||
|
||||
if (!g_bHasOnEntitySpawned && (!strncmp(classname, "func_physbox", 12, false) || (g_iCompenseEntityClass == 0 && strcmp(classname, "trigger_hurt", false) == 0 ||
|
||||
strcmp(classname, "trigger_push", false) == 0 || strcmp(classname, "trigger_teleport", false) == 0)))
|
||||
if (!g_bHasOnEntitySpawned && (!strncmp(classname, "func_physbox", 12, false) ||
|
||||
(g_iCompenseEntityClass == 0 && (strcmp(classname, "trigger_hurt", false) == 0 ||
|
||||
strcmp(classname, "trigger_push", false) == 0 || strcmp(classname, "trigger_teleport", false) == 0))))
|
||||
{
|
||||
SDKHook(entity, SDKHook_SpawnPost, OnSDKHookEntitySpawnPost);
|
||||
}
|
||||
@@ -570,7 +604,7 @@ public MRESReturn Hook_AcceptInput(int entity, Handle hReturn, Handle hParams)
|
||||
if(iCaller <= 0 || iCaller >= MAX_EDICTS)
|
||||
return MRES_Ignored;
|
||||
|
||||
// Dont lagcompensate anything that has a game_ui button in their hierarchy.
|
||||
// Don't lagcompensate anything that has a game_ui button in their hierarchy.
|
||||
BlacklistFamily(iCaller);
|
||||
|
||||
return MRES_Ignored;
|
||||
@@ -623,12 +657,16 @@ void BlacklistChildren(int parent)
|
||||
|
||||
bool CheckEntityForLagComp(int entity, const char[] classname, bool bRecursive=false, bool bGoodParents=false)
|
||||
{
|
||||
if(entity < 0 || entity > MAX_EDICTS)
|
||||
if(entity < 0 || entity >= MAX_EDICTS)
|
||||
return false;
|
||||
|
||||
if(!IsValidEntity(entity))
|
||||
return false;
|
||||
|
||||
// Disabled for the rest of the round.
|
||||
if(g_bLagCompSuspended)
|
||||
return false;
|
||||
|
||||
if(g_aLagCompensated[entity] != -1)
|
||||
return false;
|
||||
|
||||
@@ -668,7 +706,7 @@ bool CheckEntityForLagComp(int entity, const char[] classname, bool bRecursive=f
|
||||
return false;
|
||||
}
|
||||
|
||||
if(CheckBit(g_aBlacklisted, entity))
|
||||
if(CheckBit(g_aBlacklisted, iParent))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -695,6 +733,14 @@ bool CheckEntityForLagComp(int entity, const char[] classname, bool bRecursive=f
|
||||
if(!bGoodParents)
|
||||
return false;
|
||||
|
||||
// One more than allowed: switch lag compensation off until the next round.
|
||||
//switch lag comp off every round if the map is not allowed to use it
|
||||
if(g_iNumEntities >= g_iMaxEntities || g_bMapDisabled)
|
||||
{
|
||||
SuspendLagCompensation();
|
||||
return false;
|
||||
}
|
||||
|
||||
if(AddEntityForLagCompensation(entity, bTrigger))
|
||||
{
|
||||
if(bTrigger)
|
||||
@@ -736,7 +782,7 @@ public void OnEntityDestroyed(int entity)
|
||||
if(g_bCleaningUp)
|
||||
return;
|
||||
|
||||
if(entity < 0 || entity > MAX_EDICTS)
|
||||
if(entity < 0 || entity >= MAX_EDICTS)
|
||||
return;
|
||||
|
||||
ClearBit(g_aBlacklisted, entity);
|
||||
@@ -758,7 +804,7 @@ public MRESReturn Detour_OnUTIL_Remove(Handle hParams)
|
||||
return MRES_Ignored;
|
||||
|
||||
int entity = DHookGetParamObjectPtrVar(hParams, 1, g_iNetworkableOuter, ObjectValueType_CBaseEntityPtr);
|
||||
if(entity < 0 || entity > MAX_EDICTS)
|
||||
if(entity < 0 || entity >= MAX_EDICTS)
|
||||
return MRES_Ignored;
|
||||
|
||||
int iIndex = g_aLagCompensated[entity];
|
||||
@@ -785,9 +831,32 @@ public MRESReturn Detour_OnRestartRound()
|
||||
{
|
||||
g_bCleaningUp = true;
|
||||
|
||||
ReleaseAllEntities();
|
||||
|
||||
for(int i = 0; i < sizeof(g_aBlacklisted); i++)
|
||||
g_aBlacklisted[i] = 0;
|
||||
|
||||
// New round, lag compensation is back on.
|
||||
g_bLagCompSuspended = false;
|
||||
|
||||
g_bCleaningUp = false;
|
||||
return MRES_Ignored;
|
||||
}
|
||||
|
||||
// Stops compensating everything that is currently tracked.
|
||||
void ReleaseAllEntities()
|
||||
{
|
||||
g_bReleasePending = false;
|
||||
|
||||
for(int i = 0; i < g_iNumEntities; i++)
|
||||
{
|
||||
int iEntity = g_aEntityLagData[i].iEntity;
|
||||
bool bValid = IsValidEntity(iEntity);
|
||||
|
||||
// Should never be rewound at this point, but never leave an entity in the past.
|
||||
if(g_aEntityLagData[i].bRestore && bValid)
|
||||
RestoreEntityFromRecord(iEntity, g_aaLagRecords[i][g_aEntityLagData[i].iRecordIndex]);
|
||||
g_aEntityLagData[i].bRestore = false;
|
||||
|
||||
g_aLagCompensated[iEntity] = -1;
|
||||
ClearBit(g_aBlockTriggerTouchPlayers, iEntity);
|
||||
@@ -798,22 +867,39 @@ public MRESReturn Detour_OnRestartRound()
|
||||
ClearBit(g_aaFilterClientSolidTouch, client * MAX_EDICTS + iEntity);
|
||||
}
|
||||
|
||||
if(g_aEntityLagData[i].iDeleted)
|
||||
if(g_aEntityLagData[i].iDeleted && bValid)
|
||||
{
|
||||
if(IsValidEntity(iEntity))
|
||||
RemoveEdict(iEntity);
|
||||
ClearBit(g_aBlacklisted, iEntity);
|
||||
RemoveEdict(iEntity);
|
||||
}
|
||||
|
||||
g_aEntityLagData[i].iEntity = INVALID_ENT_REFERENCE;
|
||||
}
|
||||
|
||||
for(int i = 0; i < sizeof(g_aBlacklisted); i++)
|
||||
g_aBlacklisted[i] = 0;
|
||||
|
||||
g_iNumEntities = 0;
|
||||
}
|
||||
|
||||
g_bCleaningUp = false;
|
||||
return MRES_Ignored;
|
||||
void ReleaseAllEntitiesNow()
|
||||
{
|
||||
bool bWasCleaningUp = g_bCleaningUp;
|
||||
g_bCleaningUp = true;
|
||||
ReleaseAllEntities();
|
||||
g_bCleaningUp = bWasCleaningUp;
|
||||
}
|
||||
|
||||
void SuspendLagCompensation()
|
||||
{
|
||||
if(g_bLagCompSuspended)
|
||||
return;
|
||||
|
||||
g_bLagCompSuspended = true;
|
||||
g_bReleasePending = true;
|
||||
|
||||
for(int client = 1; client <= MaxClients; client++)
|
||||
{
|
||||
if(IsClientInGame(client) && !IsFakeClient(client) && !g_bDisableLagComp[client])
|
||||
CPrintToChat(client, "%s LagCompensation has been {red}disabled{default} until the next round, too many entities needed to be compensated.", PREFIX);
|
||||
}
|
||||
}
|
||||
|
||||
// https://developer.valvesoftware.com/wiki/Logic_measure_movement
|
||||
@@ -858,8 +944,8 @@ public MRESReturn Detour_OnFrameUpdatePostEntityThink()
|
||||
for(int i = 0; i < g_iNumEntities; i++)
|
||||
{
|
||||
// Dont make the entity check untouch in FrameUpdatePostEntityThink.
|
||||
// If the player didnt get simulated in the current frame then
|
||||
// they didnt have a chance to touch this entity.
|
||||
// If the player didn't get simulated in the current frame then
|
||||
// they didn't have a chance to touch this entity.
|
||||
// Hence the touchlink could be broken and we only let the player check untouch.
|
||||
int EFlags = GetEntData(g_aEntityLagData[i].iEntity, g_iEFlags);
|
||||
EFlags &= ~EFL_CHECK_UNTOUCH;
|
||||
@@ -877,7 +963,10 @@ public void OnRunThinkFunctions(bool simulating)
|
||||
|
||||
public Action OnPlayerRunCmd(int client, int &buttons, int &impulse, float vel[3], float angles[3], int &weapon, int &subtype, int &cmdnum, int &tickcount, int &seed, int mouse[2])
|
||||
{
|
||||
if(!IsPlayerAlive(client) || IsFakeClient(client))
|
||||
if(!IsClientInGame(client) || !IsPlayerAlive(client) || IsFakeClient(client))
|
||||
return Plugin_Continue;
|
||||
|
||||
if(g_bLagCompSuspended)
|
||||
return Plugin_Continue;
|
||||
|
||||
int iTargetTick = tickcount - g_aLerpTicks[client];
|
||||
@@ -928,6 +1017,13 @@ public Action OnPlayerRunCmd(int client, int &buttons, int &impulse, float vel[3
|
||||
continue;
|
||||
|
||||
int iRecord = iDelta;
|
||||
|
||||
if(!g_bPlayerLaserCompensated[client] &&
|
||||
(strcmp(g_aEntityLagData[i].sClassname, "trigger_hurt", false) == 0 ||
|
||||
strcmp(g_aEntityLagData[i].sClassname, "trigger_push", false) == 0 ||
|
||||
strcmp(g_aEntityLagData[i].sClassname, "trigger_teleport", false) == 0))
|
||||
iRecord = 0;
|
||||
|
||||
if(iRecord >= g_aEntityLagData[i].iNumRecords)
|
||||
iRecord = g_aEntityLagData[i].iNumRecords - 1;
|
||||
|
||||
@@ -958,6 +1054,13 @@ public void OnPostPlayerThinkFunctions()
|
||||
|
||||
public MRESReturn Hook_EndGameFrame()
|
||||
{
|
||||
// Lag compensation got disabled this frame, let go of everything.
|
||||
if(g_bReleasePending)
|
||||
{
|
||||
ReleaseAllEntitiesNow();
|
||||
return MRES_Ignored;
|
||||
}
|
||||
|
||||
for(int i = 0; i < g_iNumEntities; i++)
|
||||
{
|
||||
if(g_aEntityLagData[i].iDeleted)
|
||||
@@ -1112,6 +1215,10 @@ bool AddEntityForLagCompensation(int iEntity, bool bLateKill)
|
||||
if(g_bCleaningUp)
|
||||
return false;
|
||||
|
||||
// sm_unlag takes an arbitrary index from the console, so validate before indexing anything.
|
||||
if(iEntity < 0 || iEntity >= MAX_EDICTS || !IsValidEntity(iEntity))
|
||||
return false;
|
||||
|
||||
if(g_aLagCompensated[iEntity] != -1)
|
||||
return false;
|
||||
|
||||
@@ -1144,6 +1251,7 @@ bool AddEntityForLagCompensation(int iEntity, bool bLateKill)
|
||||
g_aEntityLagData[i].iNotMoving = MAX_RECORDS;
|
||||
g_aEntityLagData[i].bRestore = false;
|
||||
g_aEntityLagData[i].bLateKill = bLateKill;
|
||||
g_aEntityLagData[i].sClassname = sClassname;
|
||||
|
||||
if(bLateKill)
|
||||
{
|
||||
@@ -1226,6 +1334,7 @@ void EntityLagData_Copy(EntityLagData obj, const EntityLagData other)
|
||||
obj.iNotMoving = other.iNotMoving;
|
||||
obj.bRestore = other.bRestore;
|
||||
obj.bLateKill = other.bLateKill;
|
||||
obj.sClassname = other.sClassname;
|
||||
}
|
||||
|
||||
void LagRecord_Copy(LagRecord obj, const LagRecord other)
|
||||
@@ -1253,6 +1362,59 @@ bool CompareVectors(const float vec1[3], const float vec2[3])
|
||||
return vec1[0] == vec2[0] && vec1[1] == vec2[1] && vec1[2] == vec2[2];
|
||||
}
|
||||
|
||||
void SaveLagCompSettings(int client)
|
||||
{
|
||||
// Remember that this client chose something, so OnClientCookiesCached
|
||||
// flushes it rather than loading over the top of it.
|
||||
g_bSettingsTouched[client] = true;
|
||||
|
||||
// Cookie reads/writes are meaningless until the cookies arrive; we get called back then.
|
||||
if(!AreClientCookiesCached(client))
|
||||
return;
|
||||
|
||||
// Read current cookie values
|
||||
bool currentDisableLagComp, currentPlayerLaserCompensated, currentLagCompMessages;
|
||||
ParseClientCookie(client, currentDisableLagComp, currentPlayerLaserCompensated, currentLagCompMessages);
|
||||
|
||||
// Check if values have changed
|
||||
bool disableChanged = g_bDisableLagComp[client] != currentDisableLagComp;
|
||||
bool laserChanged = g_bPlayerLaserCompensated[client] != currentPlayerLaserCompensated;
|
||||
bool messagesChanged = g_bLagCompMessages[client] != currentLagCompMessages;
|
||||
|
||||
// If no values have changed, no need to save
|
||||
if (!disableChanged && !laserChanged && !messagesChanged) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise, save the chain format
|
||||
char sBuffer[8];
|
||||
Format(sBuffer, sizeof(sBuffer), "%d%d%d",
|
||||
g_bDisableLagComp[client] ? 1 : 0,
|
||||
g_bPlayerLaserCompensated[client] ? 1 : 0,
|
||||
g_bLagCompMessages[client] ? 1 : 0);
|
||||
SetClientCookie(client, g_hCookie_LagCompSettings, sBuffer);
|
||||
}
|
||||
|
||||
void ParseClientCookie(int client, bool &disableLagComp, bool &playerLaserCompensated, bool &lagCompMessages)
|
||||
{
|
||||
char buffer[16];
|
||||
GetClientCookie(client, g_hCookie_LagCompSettings, buffer, sizeof(buffer));
|
||||
|
||||
// Parse chain format: disable|laser|messages
|
||||
if (strlen(buffer) >= 3)
|
||||
{
|
||||
disableLagComp = (buffer[0] == '1');
|
||||
playerLaserCompensated = (buffer[1] == '1');
|
||||
lagCompMessages = (buffer[2] == '1');
|
||||
}
|
||||
else
|
||||
{
|
||||
// Default values
|
||||
disableLagComp = false;
|
||||
playerLaserCompensated = true;
|
||||
lagCompMessages = false;
|
||||
}
|
||||
}
|
||||
|
||||
public Action Command_AddLagCompensation(int client, int argc)
|
||||
{
|
||||
@@ -1274,11 +1436,43 @@ public Action Command_AddLagCompensation(int client, int argc)
|
||||
late = view_as<bool>(StringToInt(sArgs));
|
||||
}
|
||||
|
||||
if(g_bLagCompSuspended)
|
||||
{
|
||||
ReplyToCommand(client, "[SM] LagCompensation is disabled until the next round, use sm_lagcomp_enable first.");
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
AddEntityForLagCompensation(entity, late);
|
||||
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
public Action Command_EnableLagCompensation(int client, int argc)
|
||||
{
|
||||
if(!g_bLagCompSuspended)
|
||||
{
|
||||
ReplyToCommand(client, "[SM] LagCompensation is already enabled.");
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
// Let go of whatever was tracked before it got disabled, so only
|
||||
// entities that spawn from now on are compensated.
|
||||
if(g_bReleasePending)
|
||||
ReleaseAllEntitiesNow();
|
||||
|
||||
g_bLagCompSuspended = false;
|
||||
|
||||
ReplyToCommand(client, "[SM] LagCompensation has been re-enabled.");
|
||||
|
||||
for(int i = 1; i <= MaxClients; i++)
|
||||
{
|
||||
if(IsClientInGame(i) && !IsFakeClient(i) && !g_bDisableLagComp[i])
|
||||
CPrintToChat(i, "%s LagCompensation has been {green}re-enabled{default} by an admin.", PREFIX);
|
||||
}
|
||||
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
public Action Command_CheckLagCompensated(int client, int argc)
|
||||
{
|
||||
if(argc == 0)
|
||||
@@ -1296,6 +1490,9 @@ public Action Command_CheckLagCompensated(int client, int argc)
|
||||
PrintToConsole(client, "%2d. #%d %s \"%s\" (#%d)", i, g_aEntityLagData[i].iEntity, sClassname, sTargetname, iHammerID);
|
||||
}
|
||||
|
||||
PrintToConsole(client, "Active: %d / %d%s", g_iNumEntities, g_iMaxEntities,
|
||||
g_bLagCompSuspended ? " | DISABLED until next round (over the limit)" : "");
|
||||
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
@@ -1355,9 +1552,9 @@ stock void PrintToBoth(const char[] format, any ...)
|
||||
|
||||
for(int client = 1; client <= MaxClients; client++)
|
||||
{
|
||||
if (IsClientInGame(client) && g_bLagCompMessages[client])
|
||||
if (IsClientInGame(client) && g_bLagCompMessages[client] && CheckCommandAccess(client, "sm_lagged", ADMFLAG_ROOT))
|
||||
{
|
||||
PrintToChat(client, "%s", buffer);
|
||||
PrintToConsole(client, "%s", buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1370,22 +1567,25 @@ public Action DisableLagCompTimer(Handle timer)
|
||||
continue;
|
||||
|
||||
// https://github.com/ValveSoftware/source-sdk-2013/blob/master/sp/src/game/server/util.cpp#L747
|
||||
// Lag compensation only kicks in above the ping threshold, so ramp it off below it.
|
||||
if (g_bCheckPing)
|
||||
{
|
||||
int iAvgPing = RoundFloat(GetClientAvgLatency(client, NetFlow_Outgoing) * 1000);
|
||||
|
||||
if (iAvgPing > g_iMinPing)
|
||||
if (iAvgPing <= g_iMinPing)
|
||||
{
|
||||
g_iDisableLagComp[client]++;
|
||||
return Plugin_Continue;
|
||||
if(g_iDisableLagComp[client] < MAX_RECORDS)
|
||||
g_iDisableLagComp[client]++;
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if(!g_bEnableLagComp[client] && g_iDisableLagComp[client] < MAX_RECORDS)
|
||||
if(g_bDisableLagComp[client] && g_iDisableLagComp[client] < MAX_RECORDS)
|
||||
{
|
||||
g_iDisableLagComp[client]++;
|
||||
}
|
||||
else if(g_bEnableLagComp[client] && g_iDisableLagComp[client] > 0)
|
||||
else if(!g_bDisableLagComp[client] && g_iDisableLagComp[client] > 0)
|
||||
{
|
||||
g_iDisableLagComp[client]--;
|
||||
}
|
||||
@@ -1406,33 +1606,55 @@ public Action OnToggleLagCompSettings(int client, int args)
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
public Action OnToggleLagCompLasersSettings(int client, int args)
|
||||
{
|
||||
ToggleLagCompLasers(client);
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
public void ToggleLagCompSettings(int client)
|
||||
{
|
||||
if(!client)
|
||||
if(!client || !IsClientInGame(client) || IsFakeClient(client))
|
||||
return;
|
||||
|
||||
g_bEnableLagComp[client] = !g_bEnableLagComp[client];
|
||||
SetClientCookie(client, g_hCookie_EnableLagComp, g_bEnableLagComp[client] ? "1" : "");
|
||||
g_bDisableLagComp[client] = !g_bDisableLagComp[client];
|
||||
SaveLagCompSettings(client);
|
||||
|
||||
CPrintToChat(client, "%s LagCompensation has been %s.", PREFIX, g_bEnableLagComp[client] ? "enabled" : "disabled");
|
||||
CPrintToChat(client, "%s LagCompensation has been %s.", PREFIX, g_bDisableLagComp[client] ? "disabled" : "enabled");
|
||||
|
||||
if(!g_bDisableLagComp[client] && g_bLagCompSuspended)
|
||||
CPrintToChat(client, "%s It is currently {red}disabled{default} for everyone until the next round (too many entities).", PREFIX);
|
||||
}
|
||||
|
||||
public void ToggleLagCompLasers(int client)
|
||||
{
|
||||
if(!client || !IsClientInGame(client) || IsFakeClient(client))
|
||||
return;
|
||||
|
||||
g_bPlayerLaserCompensated[client] = !g_bPlayerLaserCompensated[client];
|
||||
SaveLagCompSettings(client);
|
||||
|
||||
CPrintToChat(client, "%s LagCompensation for Lasers has been %s.", PREFIX, g_bPlayerLaserCompensated[client] ? "enabled" : "disabled");
|
||||
if (g_bCheckPing && g_bPlayerLaserCompensated[client])
|
||||
CPrintToChat(client, "%s It will take effect if your real ping is higher than %dms.", PREFIX, g_iMinPing);
|
||||
}
|
||||
|
||||
public void ToggleLagCompMessages(int client)
|
||||
{
|
||||
if(!client)
|
||||
if(!client || !IsClientInGame(client) || IsFakeClient(client))
|
||||
return;
|
||||
|
||||
g_bLagCompMessages[client] = !g_bLagCompMessages[client];
|
||||
SetClientCookie(client, g_hCookie_LagCompMessages, g_bLagCompMessages[client] ? "1" : "");
|
||||
SaveLagCompSettings(client);
|
||||
|
||||
CPrintToChat(client, "%s LagCompensation messages have been %s.", PREFIX, g_bLagCompMessages[client] ? "enabled" : "disabled");
|
||||
}
|
||||
|
||||
public Action CheckLagComp(int client, int args)
|
||||
{
|
||||
if (args > 1)
|
||||
if (args > 1 || (args == 0 && !client))
|
||||
{
|
||||
CPrintToChat(client, "%s Usage sm_checklag <client>", PREFIX);
|
||||
CReplyToCommand(client, "%s Usage: sm_checklag <client>", PREFIX);
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
@@ -1455,7 +1677,19 @@ public Action CheckLagComp(int client, int args)
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
CPrintToChat(client, "%s LagCompensation is %s for {olive}%N", PREFIX, g_bEnableLagComp[target] ? sEnable : "disabled", target);
|
||||
CReplyToCommand(client, "%s LagCompensation is %s for {olive}%N", PREFIX, g_bDisableLagComp[target] ? "disabled" : sEnable, target);
|
||||
CReplyToCommand(client, "%s Laser compensation: %s", PREFIX, g_bPlayerLaserCompensated[target] ? "enabled" : "disabled");
|
||||
|
||||
if(g_bLagCompSuspended)
|
||||
CReplyToCommand(client, "%s Server status: {red}disabled{default} for everyone until the next round (more than %d entities needed to be compensated).", PREFIX, g_iMaxEntities);
|
||||
else
|
||||
CReplyToCommand(client, "%s Server status: {green}active{default}.", PREFIX);
|
||||
|
||||
// g_iDisableLagComp also ramps from the ping threshold, so report it when they haven't opted out.
|
||||
if(!g_bDisableLagComp[target] && g_iDisableLagComp[target] > 0)
|
||||
CReplyToCommand(client, "%s Currently reduced: %d of %d ticks skipped%s.", PREFIX,
|
||||
g_iDisableLagComp[target], MAX_RECORDS,
|
||||
g_bCheckPing ? " (ping at or below the threshold ramps this up)" : "");
|
||||
|
||||
return Plugin_Handled;
|
||||
}
|
||||
@@ -1463,15 +1697,20 @@ public Action CheckLagComp(int client, int args)
|
||||
public void ShowSettingsMenu(int client)
|
||||
{
|
||||
Menu menu = new Menu(MenuHandler_MainMenu);
|
||||
menu.SetTitle("LagCompensation Settings", client);
|
||||
if(g_bLagCompSuspended)
|
||||
menu.SetTitle("LagCompensation Settings\nCurrently disabled until the next round (too many entities)");
|
||||
else
|
||||
menu.SetTitle("LagCompensation Settings");
|
||||
menu.ExitBackButton = true;
|
||||
|
||||
char buffer[128], msg_buffer[128];
|
||||
Format(buffer, sizeof(buffer), "LagCompensation: %s", g_bEnableLagComp[client] ? "Enabled" : "Disabled");
|
||||
char buffer[128], buffer_lasers[128], msg_buffer[128];
|
||||
Format(buffer, sizeof(buffer), "LagCompensation: %s", g_bDisableLagComp[client] ? "Disabled" : "Enabled");
|
||||
Format(buffer_lasers, sizeof(buffer_lasers), "LagCompensation for Lasers: %s", g_bPlayerLaserCompensated[client] ? "Enabled" : "Disabled");
|
||||
Format(msg_buffer, sizeof(msg_buffer), "LagCompensation Messages: %s", g_bLagCompMessages[client] ? "Enabled" : "Disabled");
|
||||
|
||||
menu.AddItem("0", buffer);
|
||||
menu.AddItem("1", msg_buffer);
|
||||
menu.AddItem("1", buffer_lasers);
|
||||
menu.AddItem("2", msg_buffer, CheckCommandAccess(client, "sm_lagged", ADMFLAG_ROOT) ? ITEMDRAW_DEFAULT : ITEMDRAW_DISABLED);
|
||||
|
||||
menu.Display(client, MENU_TIME_FOREVER);
|
||||
}
|
||||
@@ -1482,7 +1721,7 @@ public void MenuHandler_CookieMenu(int client, CookieMenuAction action, any info
|
||||
{
|
||||
case(CookieMenuAction_DisplayOption):
|
||||
{
|
||||
Format(buffer, maxlen, "LagCompensation", client);
|
||||
strcopy(buffer, maxlen, "LagCompensation");
|
||||
}
|
||||
case(CookieMenuAction_SelectOption):
|
||||
{
|
||||
@@ -1500,7 +1739,8 @@ public int MenuHandler_MainMenu(Menu menu, MenuAction action, int client, int se
|
||||
switch(selection)
|
||||
{
|
||||
case(0): ToggleLagCompSettings(client);
|
||||
case(1): ToggleLagCompMessages(client);
|
||||
case(1): ToggleLagCompLasers(client);
|
||||
case(2): ToggleLagCompMessages(client);
|
||||
}
|
||||
|
||||
ShowSettingsMenu(client);
|
||||
|
||||
@@ -1030,6 +1030,13 @@ public void LeaderMenu(int client)
|
||||
AddMenuItem(menu, "ping", "Ping Menu");
|
||||
AddMenuItem(menu, "beacon", "Toggle Beacon");
|
||||
|
||||
// RCON-only options
|
||||
if (CheckCommandAccess(client, "", ADMFLAG_RCON, false))
|
||||
{
|
||||
AddMenuItem(menu, "addleader", "[RCON] Add Leader to File");
|
||||
AddMenuItem(menu, "removeleader", "[RCON] Remove Leader from File");
|
||||
}
|
||||
|
||||
SetMenuExitButton(menu, true);
|
||||
DisplayMenu(menu, client, MENU_TIME_FOREVER);
|
||||
}
|
||||
@@ -1046,6 +1053,23 @@ public int LeaderMenu_Handler(Handle menu, MenuAction action, int client, int po
|
||||
char info[32];
|
||||
GetMenuItem(menu, position, info, sizeof(info));
|
||||
|
||||
if(StrEqual(info, "addleader"))
|
||||
{
|
||||
if(CheckCommandAccess(client, "", ADMFLAG_RCON, false))
|
||||
{
|
||||
AddLeaderMenu(client);
|
||||
}
|
||||
}
|
||||
|
||||
if(StrEqual(info, "removeleader"))
|
||||
{
|
||||
if(CheckCommandAccess(client, "", ADMFLAG_RCON, false))
|
||||
{
|
||||
RemoveLeaderMenu(client);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(StrEqual(info, "resign"))
|
||||
{
|
||||
RemoveLeader(client);
|
||||
@@ -1070,7 +1094,260 @@ public int LeaderMenu_Handler(Handle menu, MenuAction action, int client, int po
|
||||
delete menu;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose: RCON - shows connected players to add to the leaders file
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public void AddLeaderMenu(int client)
|
||||
{
|
||||
Handle menu = CreateMenu(AddLeaderMenu_Handler);
|
||||
SetMenuTitle(menu, "Select a player to ADD as Leader");
|
||||
|
||||
char info[12], display[MAX_NAME_LENGTH];
|
||||
bool any = false;
|
||||
|
||||
for(int i = 1; i <= MaxClients; i++)
|
||||
{
|
||||
if(IsClientInGame(i) && !IsFakeClient(i))
|
||||
{
|
||||
any = true;
|
||||
IntToString(GetClientUserId(i), info, sizeof(info));
|
||||
GetClientName(i, display, sizeof(display));
|
||||
AddMenuItem(menu, info, display);
|
||||
}
|
||||
}
|
||||
|
||||
if(!any)
|
||||
{
|
||||
AddMenuItem(menu, "", "No players connected", ITEMDRAW_DISABLED);
|
||||
}
|
||||
|
||||
SetMenuExitBackButton(menu, true);
|
||||
SetMenuExitButton(menu, true);
|
||||
DisplayMenu(menu, client, MENU_TIME_FOREVER);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public int AddLeaderMenu_Handler(Handle menu, MenuAction action, int client, int position)
|
||||
{
|
||||
if(action == MenuAction_Select)
|
||||
{
|
||||
if(!CheckCommandAccess(client, "", ADMFLAG_RCON, false))
|
||||
{
|
||||
PrintToChat(client, "[SM] You no longer have access to this.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
char info[12];
|
||||
GetMenuItem(menu, position, info, sizeof(info));
|
||||
|
||||
int target = GetClientOfUserId(StringToInt(info));
|
||||
if(target == 0)
|
||||
{
|
||||
PrintToChat(client, "[SM] That player is no longer available.");
|
||||
}
|
||||
else
|
||||
{
|
||||
AddLeaderToFile(client, target);
|
||||
}
|
||||
|
||||
LeaderMenu(client);
|
||||
}
|
||||
else if(action == MenuAction_End)
|
||||
{
|
||||
delete menu;
|
||||
}
|
||||
else if(action == MenuAction_Cancel && position == MenuCancel_ExitBack)
|
||||
{
|
||||
LeaderMenu(client);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
//AI slop
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose: RCON - shows connected players to remove from the leaders file
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public void RemoveLeaderMenu(int client)
|
||||
{
|
||||
Handle menu = CreateMenu(RemoveLeaderMenu_Handler);
|
||||
SetMenuTitle(menu, "Select a player to REMOVE as Leader");
|
||||
|
||||
char info[12], display[MAX_NAME_LENGTH];
|
||||
bool any = false;
|
||||
|
||||
for(int i = 1; i <= MaxClients; i++)
|
||||
{
|
||||
if(IsClientInGame(i) && !IsFakeClient(i))
|
||||
{
|
||||
any = true;
|
||||
IntToString(GetClientUserId(i), info, sizeof(info));
|
||||
GetClientName(i, display, sizeof(display));
|
||||
AddMenuItem(menu, info, display);
|
||||
}
|
||||
}
|
||||
|
||||
if(!any)
|
||||
{
|
||||
AddMenuItem(menu, "", "No players connected", ITEMDRAW_DISABLED);
|
||||
}
|
||||
|
||||
SetMenuExitBackButton(menu, true);
|
||||
SetMenuExitButton(menu, true);
|
||||
DisplayMenu(menu, client, MENU_TIME_FOREVER);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public int RemoveLeaderMenu_Handler(Handle menu, MenuAction action, int client, int position)
|
||||
{
|
||||
if(action == MenuAction_Select)
|
||||
{
|
||||
if(!CheckCommandAccess(client, "", ADMFLAG_RCON, false))
|
||||
{
|
||||
PrintToChat(client, "[SM] You no longer have access to this.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
char info[12];
|
||||
GetMenuItem(menu, position, info, sizeof(info));
|
||||
|
||||
int target = GetClientOfUserId(StringToInt(info));
|
||||
if(target == 0)
|
||||
{
|
||||
PrintToChat(client, "[SM] That player is no longer available.");
|
||||
}
|
||||
else
|
||||
{
|
||||
RemoveLeaderFromFile(client, target);
|
||||
}
|
||||
|
||||
LeaderMenu(client);
|
||||
}
|
||||
else if(action == MenuAction_End)
|
||||
{
|
||||
delete menu;
|
||||
}
|
||||
else if(action == MenuAction_Cancel && position == MenuCancel_ExitBack)
|
||||
{
|
||||
LeaderMenu(client);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose: Appends a targets SteamID2 to the leaders file with their name as a comment
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public void AddLeaderToFile(int admin, int target)
|
||||
{
|
||||
char sAuth[64], sName[MAX_NAME_LENGTH];
|
||||
if(!GetClientAuthId(target, AuthId_Steam2, sAuth, sizeof(sAuth)))
|
||||
{
|
||||
PrintToChat(admin, "[SM] Could not resolve a SteamID for that player yet, try again in a moment.");
|
||||
return;
|
||||
}
|
||||
GetClientName(target, sName, sizeof(sName));
|
||||
|
||||
if(IsPossibleLeader(target))
|
||||
{
|
||||
PrintToChat(admin, "[SM] %N is already in the leaders file.", target);
|
||||
return;
|
||||
}
|
||||
|
||||
File fFile = OpenFile(g_sDataFile, "a");
|
||||
if(!fFile)
|
||||
{
|
||||
PrintToChat(admin, "[SM] Failed to open the leaders file for writing.");
|
||||
return;
|
||||
}
|
||||
|
||||
fFile.WriteLine("\"%s\" //%s", sAuth, sName);
|
||||
fFile.Close();
|
||||
|
||||
UpdateLeaders();
|
||||
|
||||
PrintToChatAll("[SM] %N added %N to the leaders list.", admin, target);
|
||||
LogAction(admin, target, "\"%L\" added \"%L\" to the leaders file", admin, target);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose: Removes a targets SteamID2 line from the leaders file, if present
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public void RemoveLeaderFromFile(int admin, int target)
|
||||
{
|
||||
char sAuth[64];
|
||||
if(!GetClientAuthId(target, AuthId_Steam2, sAuth, sizeof(sAuth)))
|
||||
{
|
||||
PrintToChat(admin, "[SM] Could not resolve a SteamID for that player yet, try again in a moment.");
|
||||
return;
|
||||
}
|
||||
|
||||
File fRead = OpenFile(g_sDataFile, "rt");
|
||||
if(!fRead)
|
||||
{
|
||||
PrintToChat(admin, "[SM] Failed to open the leaders file for reading.");
|
||||
return;
|
||||
}
|
||||
|
||||
ArrayList lines = new ArrayList(ByteCountToCells(255));
|
||||
char line[255];
|
||||
bool found = false;
|
||||
|
||||
while(!fRead.EndOfFile() && fRead.ReadLine(line, sizeof(line)))
|
||||
{
|
||||
// Only skip the first line that actually contains this SteamID (inside the quotes)
|
||||
if(!found && StrContains(line, sAuth) != -1)
|
||||
{
|
||||
found = true;
|
||||
continue;
|
||||
}
|
||||
lines.PushString(line);
|
||||
}
|
||||
fRead.Close();
|
||||
|
||||
if(!found)
|
||||
{
|
||||
delete lines;
|
||||
PrintToChat(admin, "[SM] %N was not found in the leaders file.", target);
|
||||
return;
|
||||
}
|
||||
|
||||
File fWrite = OpenFile(g_sDataFile, "wt");
|
||||
if(!fWrite)
|
||||
{
|
||||
delete lines;
|
||||
PrintToChat(admin, "[SM] Failed to open the leaders file for writing.");
|
||||
return;
|
||||
}
|
||||
|
||||
int count = lines.Length;
|
||||
for(int i = 0; i < count; i++)
|
||||
{
|
||||
lines.GetString(i, line, sizeof(line));
|
||||
fWrite.WriteLine(line);
|
||||
}
|
||||
fWrite.Close();
|
||||
delete lines;
|
||||
|
||||
UpdateLeaders();
|
||||
|
||||
PrintToChatAll("[SM] %N removed %N from the leaders list.", admin, target);
|
||||
LogAction(admin, target, "\"%L\" removed \"%L\" from the leaders file", admin, target);
|
||||
|
||||
// If the person removed is the current leader, kick them out of the role too
|
||||
if(target == leaderClient)
|
||||
{
|
||||
RemoveLeader(target);
|
||||
PrintToChatAll("[SM] %N is no longer a valid leader and has been removed from the leader role.", target);
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
@@ -1191,6 +1468,7 @@ public int SpriteMenu_Handler(Handle menu, MenuAction action, int client, int po
|
||||
LeaderMenu(client);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
@@ -1320,6 +1598,7 @@ public int PingMenu_Handler(Handle menu, MenuAction action, int client, int posi
|
||||
LeaderMenu(client);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
@@ -1353,6 +1632,7 @@ public Action Event_PlayerDeath(Handle event, char[] name, bool dontBroadcast)
|
||||
PrintToChatAll("[SM] The leader has died!");
|
||||
RemoveLeader(client);
|
||||
}
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
@@ -1407,6 +1687,13 @@ public Action Event_RoundEnd(Handle event, char[] name, bool dontBroadcast)
|
||||
|
||||
KillAllBeacons();
|
||||
//KillAllPings();
|
||||
|
||||
//maybe a good idea to reset them on round end to prevent random props from being killed that could have the same index.
|
||||
pingEntity0 = -1;
|
||||
pingEntity1 = -1;
|
||||
pingEntity2 = -1;
|
||||
pingEntity3 = -1;
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
@@ -1457,6 +1744,7 @@ public int Native_CurrentLeader(Handle plugin, int numParams)
|
||||
public int Native_SetLeader(Handle plugin, int numParams)
|
||||
{
|
||||
SetLeader(GetNativeCell(1));
|
||||
return 0;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
|
||||
+101
-15
@@ -7,16 +7,7 @@
|
||||
#pragma semicolon 1
|
||||
#pragma newdecls required
|
||||
|
||||
public Plugin myinfo =
|
||||
{
|
||||
name = "MakoVoteSystem",
|
||||
author = "Neon",
|
||||
description = "MakoVoteSystem",
|
||||
version = "1.1",
|
||||
url = "https://steamcommunity.com/id/n3ontm"
|
||||
}
|
||||
|
||||
#define NUMBEROFSTAGES 6
|
||||
#define NUMBEROFSTAGES 7
|
||||
|
||||
bool g_bVoteFinished = true;
|
||||
bool g_bIsRevote = false;
|
||||
@@ -24,13 +15,15 @@ bool g_bStartVoteNextRound = false;
|
||||
bool g_bPlayedZM = false;
|
||||
|
||||
bool g_bOnCooldown[NUMBEROFSTAGES];
|
||||
static char g_sStageName[NUMBEROFSTAGES][32] = {"Extreme 2", "Extreme 2 (Heal + Ultima)", "Extreme 3 (ZED)", "Extreme 3 (Hellz)", "Race Mode", "Zombie Mode"};
|
||||
static char g_sStageName[NUMBEROFSTAGES][32] = {"Extreme 2 (Heal + Ultima)", "Extreme 3 (ZED)", "Extreme 3 (Hellz)", "Race Mode", "Zombie Mode", "Extreme 4 RMZS", "Ext2 Special Lasers"};
|
||||
int g_Winnerstage;
|
||||
|
||||
Handle g_VoteMenu = INVALID_HANDLE;
|
||||
Handle g_StageList = INVALID_HANDLE;
|
||||
Handle g_CountdownTimer = INVALID_HANDLE;
|
||||
|
||||
bool g_bCorrectMap = false;
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
@@ -57,6 +50,7 @@ public void OnPluginStart()
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public void OnMapStart()
|
||||
{
|
||||
g_bCorrectMap = false;
|
||||
VerifyMap();
|
||||
|
||||
g_bStartVoteNextRound = false;
|
||||
@@ -76,17 +70,22 @@ public Action VerifyMap()
|
||||
|
||||
if (!StrEqual(currentMap, "ze_ffvii_mako_reactor_v5_3", false))
|
||||
{
|
||||
//plugin unloading can make map switches very slow
|
||||
/*
|
||||
char sFilename[256];
|
||||
GetPluginFilename(INVALID_HANDLE, sFilename, sizeof(sFilename));
|
||||
|
||||
ServerCommand("sm plugins unload %s", sFilename);
|
||||
*/
|
||||
}
|
||||
else
|
||||
{
|
||||
g_bCorrectMap = true;
|
||||
PrecacheSound("#unloze/Pendulum - Witchcraft.mp3", true);
|
||||
|
||||
AddFileToDownloadsTable("sound/unloze/Pendulum - Witchcraft.mp3");
|
||||
}
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
@@ -94,6 +93,10 @@ public Action VerifyMap()
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public void OnEntitySpawned(int iEntity, const char[] sClassname)
|
||||
{
|
||||
if (!g_bCorrectMap)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!IsValidEntity(iEntity) || g_bVoteFinished)
|
||||
return;
|
||||
|
||||
@@ -109,6 +112,10 @@ public void OnEntitySpawned(int iEntity, const char[] sClassname)
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public void OnRoundStart(Event hEvent, const char[] sEvent, bool bDontBroadcast)
|
||||
{
|
||||
if (!g_bCorrectMap)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (g_bStartVoteNextRound)
|
||||
{
|
||||
g_bVoteFinished = false;
|
||||
@@ -160,9 +167,12 @@ public void OnRoundStart(Event hEvent, const char[] sEvent, bool bDontBroadcast)
|
||||
if (iButton5 != INVALID_ENT_REFERENCE)
|
||||
AcceptEntityInput(iButton5, "Lock");
|
||||
|
||||
int iCounter = FindEntityByTargetname(INVALID_ENT_REFERENCE, "LevelCounter", "math_counter");
|
||||
//its now a permanent entity so really dont kill it.
|
||||
/*
|
||||
int iCounter = FindEntityByTargetname(INVALID_ENT_REFERENCE, "LevelCounter", "info_target");
|
||||
if (iCounter != INVALID_ENT_REFERENCE)
|
||||
AcceptEntityInput(iCounter, "Kill");
|
||||
*/
|
||||
|
||||
int iFilter = FindEntityByTargetname(INVALID_ENT_REFERENCE, "humanos", "filter_activator_team");
|
||||
if (iFilter != INVALID_ENT_REFERENCE)
|
||||
@@ -245,6 +255,10 @@ public void OnRoundStart(Event hEvent, const char[] sEvent, bool bDontBroadcast)
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public void GenerateArray()
|
||||
{
|
||||
if (!g_bCorrectMap)
|
||||
{
|
||||
return;
|
||||
}
|
||||
int iBlockSize = ByteCountToCells(PLATFORM_MAX_PATH);
|
||||
g_StageList = CreateArray(iBlockSize);
|
||||
|
||||
@@ -270,6 +284,11 @@ public void GenerateArray()
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public Action Command_StartVote(int args)
|
||||
{
|
||||
if (!g_bCorrectMap)
|
||||
{
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
int iCurrentStage = GetCurrentStage();
|
||||
|
||||
if (iCurrentStage > 0)
|
||||
@@ -302,6 +321,10 @@ public Action Command_StartVote(int args)
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public Action StartVote(Handle timer)
|
||||
{
|
||||
if (!g_bCorrectMap)
|
||||
{
|
||||
return Plugin_Handled;
|
||||
}
|
||||
static int iCountDown = 5;
|
||||
PrintCenterTextAll("[MakoVote] Starting Vote in %ds", iCountDown);
|
||||
|
||||
@@ -312,6 +335,7 @@ public Action StartVote(Handle timer)
|
||||
g_CountdownTimer = INVALID_HANDLE;
|
||||
InitiateVote();
|
||||
}
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
@@ -319,6 +343,10 @@ public Action StartVote(Handle timer)
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public void InitiateVote()
|
||||
{
|
||||
if (!g_bCorrectMap)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if(IsVoteInProgress())
|
||||
{
|
||||
CPrintToChatAll("{green}[Mako Vote] {white}Another vote is currently in progress, retrying again in 5s.");
|
||||
@@ -347,7 +375,7 @@ public void InitiateVote()
|
||||
}
|
||||
}
|
||||
|
||||
SetMenuOptionFlags(g_VoteMenu, MENUFLAG_BUTTON_NOVOTE);
|
||||
//SetMenuOptionFlags(g_VoteMenu, MENUFLAG_BUTTON_NOVOTE);
|
||||
SetMenuTitle(g_VoteMenu, "What stage to play next?");
|
||||
SetVoteResultCallback(g_VoteMenu, Handler_SettingsVoteFinished);
|
||||
VoteMenuToAll(g_VoteMenu, 20);
|
||||
@@ -358,6 +386,10 @@ public void InitiateVote()
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public int Handler_MakoVoteMenu(Handle menu, MenuAction action, int param1, int param2)
|
||||
{
|
||||
if (!g_bCorrectMap)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
switch(action)
|
||||
{
|
||||
case MenuAction_End:
|
||||
@@ -385,10 +417,15 @@ public int MenuHandler_NotifyPanel(Menu hMenu, MenuAction iAction, int iParam1,
|
||||
case MenuAction_Select, MenuAction_Cancel:
|
||||
delete hMenu;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void Handler_SettingsVoteFinished(Handle menu, int num_votes, int num_clients, const int[][] client_info, int num_items, const int[][] item_info)
|
||||
{
|
||||
if (!g_bCorrectMap)
|
||||
{
|
||||
return;
|
||||
}
|
||||
int highest_votes = item_info[0][VOTEINFO_ITEM_VOTES];
|
||||
int required_percent = 60;
|
||||
int required_votes = RoundToCeil(float(num_votes) * float(required_percent) / 100);
|
||||
@@ -416,6 +453,10 @@ public void Handler_SettingsVoteFinished(Handle menu, int num_votes, int num_cli
|
||||
|
||||
public void Handler_VoteFinishedGeneric(Handle menu, int num_votes, int num_clients, const int[][] client_info, int num_items, const int[][] item_info)
|
||||
{
|
||||
if (!g_bCorrectMap)
|
||||
{
|
||||
return;
|
||||
}
|
||||
g_bVoteFinished = true;
|
||||
char sWinner[128];
|
||||
GetMenuItem(menu, item_info[0][VOTEINFO_ITEM_INDEX], sWinner, sizeof(sWinner));
|
||||
@@ -429,7 +470,42 @@ public void Handler_VoteFinishedGeneric(Handle menu, int num_votes, int num_clie
|
||||
g_Winnerstage = i;
|
||||
}
|
||||
|
||||
ServerCommand("sm_stage %d", (g_Winnerstage + 4));
|
||||
//ServerCommand("sm_stage %d", (g_Winnerstage + 4));
|
||||
char winner_stage[64];
|
||||
if (StrEqual(sWinner, "Extreme 3 (Hellz)"))
|
||||
{
|
||||
Format(winner_stage, sizeof(winner_stage), "hellz");
|
||||
}
|
||||
else if (StrEqual(sWinner, "Extreme 2"))
|
||||
{
|
||||
Format(winner_stage, sizeof(winner_stage), "extreme2");
|
||||
}
|
||||
else if (StrEqual(sWinner, "Extreme 2 (Heal + Ultima)"))
|
||||
{
|
||||
Format(winner_stage, sizeof(winner_stage), "ultiheal");
|
||||
}
|
||||
else if (StrEqual(sWinner, "Extreme 3 (ZED)"))
|
||||
{
|
||||
Format(winner_stage, sizeof(winner_stage), "zombieden");
|
||||
}
|
||||
else if (StrEqual(sWinner, "Race Mode"))
|
||||
{
|
||||
Format(winner_stage, sizeof(winner_stage), "race");
|
||||
}
|
||||
else if (StrEqual(sWinner, "Zombie Mode"))
|
||||
{
|
||||
Format(winner_stage, sizeof(winner_stage), "zm");
|
||||
}
|
||||
else if (StrEqual(sWinner, "Extreme 4 RMZS"))
|
||||
{
|
||||
Format(winner_stage, sizeof(winner_stage), "rmzs");
|
||||
}
|
||||
else if (StrEqual(sWinner, "Ext2 Special Lasers"))
|
||||
{
|
||||
Format(winner_stage, sizeof(winner_stage), "laser");
|
||||
}
|
||||
|
||||
ServerCommand("sm_stage %s", winner_stage);
|
||||
|
||||
float fDelay = 3.0;
|
||||
CS_TerminateRound(fDelay, CSRoundEnd_GameStart, false);
|
||||
@@ -437,7 +513,13 @@ public void Handler_VoteFinishedGeneric(Handle menu, int num_votes, int num_clie
|
||||
|
||||
public int GetCurrentStage()
|
||||
{
|
||||
int iLevelCounterEnt = FindEntityByTargetname(INVALID_ENT_REFERENCE, "LevelCounter", "math_counter");
|
||||
if (!g_bCorrectMap)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
//int iLevelCounterEnt = FindEntityByTargetname(INVALID_ENT_REFERENCE, "LevelCounter", "math_counter");
|
||||
//due to the stripper its now an info_target and not math_counter.
|
||||
int iLevelCounterEnt = FindEntityByTargetname(INVALID_ENT_REFERENCE, "LevelCounter", "info_target");
|
||||
|
||||
int offset = FindDataMapInfo(iLevelCounterEnt, "m_OutValue");
|
||||
int iCounterVal = RoundFloat(GetEntDataFloat(iLevelCounterEnt, offset));
|
||||
@@ -463,6 +545,10 @@ public int GetCurrentStage()
|
||||
|
||||
public int FindEntityByTargetname(int entity, const char[] sTargetname, const char[] sClassname)
|
||||
{
|
||||
if (!g_bCorrectMap)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if(sTargetname[0] == '#') // HammerID
|
||||
{
|
||||
int HammerID = StringToInt(sTargetname[1]);
|
||||
|
||||
@@ -37,7 +37,7 @@ public void OnPluginStart()
|
||||
ConVar cvar;
|
||||
HookConVarChange((cvar = CreateConVar("sm_admin_afk_time", "30", "Time in seconds until an admin is considered as AFK.")), Cvar_AdminAFKTime);
|
||||
g_iAdminAFKTime = cvar.IntValue;
|
||||
HookConVarChange((cvar = CreateConVar("sm_selfextend_amount", "2", "Amount of sm_selfextend's allowed.")), Cvar_SelfExtendsAmount);
|
||||
HookConVarChange((cvar = CreateConVar("sm_selfextend_amount", "2", "Amount of sm_selfextends allowed.")), Cvar_SelfExtendsAmount);
|
||||
g_iSelfMaxExtendsAmount = cvar.IntValue;
|
||||
HookConVarChange((cvar = CreateConVar("sm_selfextend_time", "10", "How long to extend in minutes when sm_selfextend passes through.")), Cvar_SelfExtendsTime);
|
||||
g_iSelfExtendsTime = cvar.IntValue;
|
||||
@@ -175,24 +175,14 @@ public Action Command_DisplayActiveAdmins(int client, int args)
|
||||
|
||||
public Action Command_SelfExtend(int client, int args)
|
||||
{
|
||||
int iAdminActivity = 0;
|
||||
for(int i = 1; i <= MaxClients; i++)
|
||||
if(g_bActiveAdmin[i])
|
||||
iAdminActivity++;
|
||||
|
||||
if(GetExtendsLeft() > 0)
|
||||
{
|
||||
ReplyToCommand(client, "[SM] Not available because not all regular extends have been depleted.");
|
||||
return Plugin_Handled;
|
||||
}
|
||||
if(iAdminActivity > 0)
|
||||
{
|
||||
ReplyToCommand(client, "[SM] Not available because there is atleast one active Admin who can extend. Please ask the Admins.");
|
||||
return Plugin_Handled;
|
||||
}
|
||||
if(!g_bSelfExtendsAllowed)
|
||||
{
|
||||
ReplyToCommand(client, "[SM] Not available because it's still too early in the map.");
|
||||
ReplyToCommand(client, "[SM] Not available because its still too early in the map.");
|
||||
return Plugin_Handled;
|
||||
}
|
||||
if(g_iSelfMaxExtendsAmount <= g_iSelfExtends)
|
||||
@@ -201,14 +191,34 @@ public Action Command_SelfExtend(int client, int args)
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
int iPlayers;
|
||||
int iSelfExtendsPlayers;
|
||||
int iPlayersNeeded;
|
||||
|
||||
for(int i = 1; i <= MaxClients; i++)
|
||||
{
|
||||
if(IsValidClient(i))
|
||||
iPlayers++;
|
||||
|
||||
if(g_bSelfExtends[i])
|
||||
iSelfExtendsPlayers++;
|
||||
}
|
||||
iPlayersNeeded = RoundToFloor(float(iPlayers) * g_fSelfExtendsRatio);
|
||||
|
||||
if (iPlayersNeeded == 0)
|
||||
{
|
||||
iPlayersNeeded = 1;
|
||||
}
|
||||
if(!g_bSelfExtends[client])
|
||||
{
|
||||
g_bSelfExtends[client] = true;
|
||||
PrintToChatAll("[SM] %N wants to self-extend the map.", client);
|
||||
PrintToChatAll("[SM] %N wants to self-extend the map. %i/%i", client, iSelfExtendsPlayers + 1, iPlayersNeeded);
|
||||
CheckRatio();
|
||||
}
|
||||
else
|
||||
ReplyToCommand(client, "[SM] You have already voted to self-extend the map.");
|
||||
{
|
||||
ReplyToCommand(client, "[SM] You have already voted to self-extend the map. %i/%i", iSelfExtendsPlayers, iPlayersNeeded);
|
||||
}
|
||||
|
||||
return Plugin_Handled;
|
||||
}
|
||||
@@ -233,7 +243,7 @@ public void CheckRatio()
|
||||
|
||||
iPlayersNeeded = RoundToFloor(float(iPlayers) * g_fSelfExtendsRatio);
|
||||
|
||||
if(iSelfExtendsPlayers >= iPlayersNeeded && iPlayersNeeded != 0)
|
||||
if(iSelfExtendsPlayers >= iPlayersNeeded && iSelfExtendsPlayers > 0)
|
||||
{
|
||||
PrintToChatAll("[SM] Enough Players voted to self-extend the map. Adding %d minutes to the timelimit.", g_iSelfExtendsTime);
|
||||
LogMessage("Enough Players voted to self-extend the map. Adding %d minutes to the timelimit.", g_iSelfExtendsTime);
|
||||
@@ -243,7 +253,6 @@ public void CheckRatio()
|
||||
for(int j; j <= MaxClients; j++)
|
||||
g_bSelfExtends[j] = false;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
ConVar g_hCvar_BlockSpoof;
|
||||
ConVar g_hCvar_BlockAdmin;
|
||||
ConVar g_hCvar_BlockVoice;
|
||||
ConVar g_hCvar_AuthenticationTime;
|
||||
|
||||
/* DATABASE */
|
||||
Database g_hDatabase;
|
||||
@@ -53,7 +52,6 @@ public void OnPluginStart()
|
||||
g_hCvar_BlockSpoof = CreateConVar("sm_manager_block_spoof", "1", "Kick unauthenticated people that join with known steamids.", FCVAR_NONE, true, 0.0, true, 1.0);
|
||||
g_hCvar_BlockAdmin = CreateConVar("sm_manager_block_admin", "1", "Should unauthenticated people be blocked from admin?", FCVAR_NONE, true, 0.0, true, 1.0);
|
||||
g_hCvar_BlockVoice = CreateConVar("sm_manager_block_voice", "1", "Should unauthenticated people be blocked from voice?", FCVAR_NONE, true, 0.0, true, 1.0);
|
||||
g_hCvar_AuthenticationTime = CreateConVar("sm_manager_authentication_time", "15", "Time in seconds after which a client needs to be assigned to a SteamID", FCVAR_NONE, true, 1.0);
|
||||
|
||||
AddMultiTargetFilter("@steam", Filter_Steam, "Steam Players", false);
|
||||
AddMultiTargetFilter("@nosteam", Filter_NoSteam, "No-Steam Players", false);
|
||||
@@ -229,6 +227,7 @@ public bool Filter_NoSteam(const char[] sPattern, Handle hClients)
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public EConnect OnClientPreConnectEx(const char[] sName, char sPassword[255], const char[] sIP, const char[] sSteam32ID, char sRejectReason[255])
|
||||
{
|
||||
//LogMessage("OnClientPreConnectEx sName: %s. sRejectReason: %s. sSteam32ID: %s", sName, sRejectReason, sSteam32ID);
|
||||
char sAuthID[32];
|
||||
for(int client = 1; client <= MaxClients; client++)
|
||||
{
|
||||
@@ -257,14 +256,6 @@ public EConnect OnClientPreConnectEx(const char[] sName, char sPassword[255], co
|
||||
return k_OnClientPreConnectEx_Accept;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public void OnClientPutInServer(int client)
|
||||
{
|
||||
//CreateTimer(g_hCvar_AuthenticationTime.FloatValue, CheckAuth, GetClientSerial(client), TIMER_FLAG_NO_MAPCHANGE);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
@@ -272,8 +263,22 @@ public void OnClientAuthorized(int client, const char[] sAuthID)
|
||||
{
|
||||
char sAddress[16];
|
||||
GetClientIP(client, sAddress, sizeof(sAddress));
|
||||
static char sCountry[32];
|
||||
//if its the webclient its not a malicious steam ID steal, just pure coincidence if it were to hit a real steam players steamID.
|
||||
char allowed_ips[128];
|
||||
ConVar sv_set_steam_id_ips = FindConVar("sv_set_steam_id_ips");
|
||||
if (sv_set_steam_id_ips != null)
|
||||
{
|
||||
sv_set_steam_id_ips.GetString(allowed_ips, sizeof(allowed_ips));
|
||||
if (StrEqual(sAddress, allowed_ips))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//just kicking all nosteamers from algeria. not very cool but so be it.
|
||||
/*
|
||||
static char sCountry[32];
|
||||
if (GeoipCountry(sAddress, sCountry, sizeof(sCountry)) && StrEqual(sCountry, "Algeria", false))
|
||||
{
|
||||
if (!SteamClientAuthenticatedEx(sAuthID))
|
||||
@@ -283,6 +288,7 @@ public void OnClientAuthorized(int client, const char[] sAuthID)
|
||||
return;
|
||||
}
|
||||
}
|
||||
*/
|
||||
if(!g_hCvar_BlockSpoof.BoolValue || !g_hDatabase)
|
||||
return;
|
||||
|
||||
@@ -331,61 +337,6 @@ public void OnClientPostAdminCheck(int client)
|
||||
|
||||
char sAuthID[32];
|
||||
GetClientAuthId(client, AuthId_Steam2, sAuthID, sizeof(sAuthID), false);
|
||||
|
||||
/*
|
||||
if(!SteamClientAuthenticatedEx(sAuthID))
|
||||
{
|
||||
LogMessage("%L was not authenticated with steam, muting client.", client);
|
||||
BaseComm_SetClientMute(client, true);
|
||||
return;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public Action CheckAuth(Handle timer, int data)
|
||||
{
|
||||
int client;
|
||||
if ((client = GetClientFromSerial(data)) == 0)
|
||||
return Plugin_Stop;
|
||||
|
||||
char sAuthID[32];
|
||||
if(!GetClientAuthId(client, AuthId_Steam2, sAuthID, sizeof(sAuthID), true))
|
||||
{
|
||||
LogMessage("%L could not be assigned to a SteamID, kicking client.", client);
|
||||
//KickClient(client, "Invalid STEAMID");
|
||||
if (IsValidClient(client))
|
||||
{
|
||||
Panel hNotifyPanel = new Panel(GetMenuStyleHandle(MenuStyle_Radio));
|
||||
hNotifyPanel.DrawItem("WARNING: Your SteamID is Invalid: Reconnecting you to the server in 8 Seconds.", ITEMDRAW_RAWLINE);
|
||||
hNotifyPanel.DrawItem("", ITEMDRAW_SPACER);
|
||||
hNotifyPanel.DrawItem("IMPORTANT: You may disconnect, reconnect if necessary!", ITEMDRAW_RAWLINE);
|
||||
hNotifyPanel.Send(client, MenuHandler_NotifyPanel, 8);
|
||||
delete hNotifyPanel;
|
||||
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
PrintToChat(client, "WARNING: Your SteamID is Invalid: Reconnecting you to the server in 8 Seconds.");
|
||||
}
|
||||
|
||||
CreateTimer(8.0, forceClientRetry, GetClientSerial(client));
|
||||
}
|
||||
}
|
||||
return Plugin_Stop;
|
||||
}
|
||||
|
||||
public Action forceClientRetry(Handle timer, int data)
|
||||
{
|
||||
int client;
|
||||
if ((client = GetClientFromSerial(data)) == 0)
|
||||
return Plugin_Stop;
|
||||
if (IsValidClient(client))
|
||||
{
|
||||
ClientCommand(client, "retry");
|
||||
}
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
stock bool IsValidClient(int client)
|
||||
@@ -395,16 +346,6 @@ stock bool IsValidClient(int client)
|
||||
return false;
|
||||
}
|
||||
|
||||
int MenuHandler_NotifyPanel(Menu hMenu, MenuAction iAction, int iParam1, int iParam2)
|
||||
{
|
||||
switch (iAction)
|
||||
{
|
||||
case MenuAction_Select, MenuAction_Cancel:
|
||||
delete hMenu;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
@@ -471,18 +412,13 @@ public void SQL_OnQueryCompleted(Database db, DBResultSet results, const char[]
|
||||
{
|
||||
if(StrEqual(sAddress, sResultAddress, false))
|
||||
{
|
||||
LogMessage("%L tried to join with a legitimate steamid while not authenticated with steam. Allowing connection, IPs match. (Known: %s)", client, sAddress);
|
||||
LogMessage("%N tried to join with a legitimate steamid while not authenticated with steam. Allowing connection, IPs match. (Known: %s)", client, sAddress);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogAction(client, -1, "\"%L\" tried to join with a legitimate steamid while not authenticated with steam. Refusing connection, IPs dont match. (Known: %s | Current: %s)", client, sResultAddress, sAddress);
|
||||
static char sCountry[32];
|
||||
//we were directly got a problem with an algerian. this is just a specific issue with a specific solution.
|
||||
if (GeoipCountry(sAddress, sCountry, sizeof(sCountry)) && StrEqual(sCountry, "Algeria", false))
|
||||
{
|
||||
KickClient(client, "Trying to join with a legitimate steamid while not authenticated with steam.");
|
||||
}
|
||||
LogMessage("%N tried to join with a legitimate steamid while not authenticated with steam. Refusing connection, IPs dont match. (Known: %s | Current: %s)", client, sResultAddress, sAddress);
|
||||
KickClient(client, "Trying to join with a legitimate steamid while not authenticated with steam.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,13 +18,13 @@ native bool PM_IsPlayerSteam(int client);
|
||||
* Retrieve clients usertype.
|
||||
*
|
||||
* @param client The client index.
|
||||
* @param type The buffer to write to.
|
||||
* @param maxlength The maximum buffer length.
|
||||
* @param type The integer to write to (1 = Steam, 0 = NoSteam).
|
||||
*
|
||||
* @return True on success, false otherwise.
|
||||
* @error Invalid client index, not connected or fake client.
|
||||
*/
|
||||
native bool PM_GetPlayerType(int client, char[] type, int maxlength);
|
||||
//native bool PM_GetPlayerType(int client, int type);
|
||||
native bool PM_GetPlayerType(int client, char[] buffer, int maxlength);
|
||||
|
||||
/**
|
||||
* Retrieve clients globally unique identifier (GUID).
|
||||
@@ -40,7 +40,7 @@ native int PM_GetPlayerGUID(int client);
|
||||
public SharedPlugin __pl_PlayerManager =
|
||||
{
|
||||
name = "PlayerManager",
|
||||
file = "PlayerManager_Connect.smx",
|
||||
file = "PlayerManager.smx",
|
||||
|
||||
#if defined REQUIRE_PLUGIN
|
||||
required = 1
|
||||
@@ -50,10 +50,10 @@ public SharedPlugin __pl_PlayerManager =
|
||||
};
|
||||
|
||||
#if !defined REQUIRE_PLUGIN
|
||||
public void __pl_PlayerManager_SetNTVOptional()
|
||||
{
|
||||
MarkNativeAsOptional("PM_IsPlayerSteam");
|
||||
MarkNativeAsOptional("PM_GetPlayerType");
|
||||
MarkNativeAsOptional("PM_GetPlayerGUID");
|
||||
}
|
||||
public void __pl_PlayerManager_SetNTVOptional()
|
||||
{
|
||||
MarkNativeAsOptional("PM_IsPlayerSteam");
|
||||
MarkNativeAsOptional("PM_GetPlayerType");
|
||||
MarkNativeAsOptional("PM_GetPlayerGUID");
|
||||
}
|
||||
#endif
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,20 @@
|
||||
"Games"
|
||||
{
|
||||
"cstrike"
|
||||
{
|
||||
"Offsets"
|
||||
{
|
||||
"PlayerUse"
|
||||
{
|
||||
"linux" "414"
|
||||
"windows" "413"
|
||||
}
|
||||
|
||||
"ForceVPhysicsCollide"
|
||||
{
|
||||
"linux" "160"
|
||||
"windows" "159"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,10 @@
|
||||
#pragma semicolon 1
|
||||
#include <sourcemod>
|
||||
#include <sdktools>
|
||||
#include <sdkhooks>
|
||||
#include <dhooks>
|
||||
#include <cstrike>
|
||||
#include <unloze_playtime>
|
||||
|
||||
public Plugin myinfo =
|
||||
{
|
||||
@@ -16,9 +20,67 @@ Menu g_RotationMenu = null;
|
||||
Menu g_PhysicMenu = null;
|
||||
Menu g_DynamicMenu = null;
|
||||
Menu g_NpcMenu = null;
|
||||
Menu g_ColorMenu = null;
|
||||
|
||||
KeyValues g_Models;
|
||||
|
||||
ConVar g_cvNotifyShot;
|
||||
ConVar g_cvNotifyTimer;
|
||||
ConVar g_cvUseMode;
|
||||
ConVar g_cvPropShootTier;
|
||||
ConVar g_cvButtonPressTier;
|
||||
|
||||
ArrayList g_aSpawnedProps; // Tracks entity indices of props spawned by this plugin
|
||||
ArrayList g_aNotifyList;
|
||||
|
||||
int g_aPlayerPropColor[MAXPLAYERS + 1][3];
|
||||
|
||||
Handle g_PlayerUseSetup = INVALID_HANDLE;
|
||||
Handle g_ForceVPhysicsCollideSetup = INVALID_HANDLE;
|
||||
|
||||
enum struct NotifyListData
|
||||
{
|
||||
int client;
|
||||
Handle timer;
|
||||
}
|
||||
|
||||
enum
|
||||
{
|
||||
PropColors_Default,
|
||||
PropColors_Black,
|
||||
PropColors_Red,
|
||||
PropColors_Purple,
|
||||
PropColors_Blue,
|
||||
PropColors_Green,
|
||||
PropColors_Yellow,
|
||||
PropColors_Orange,
|
||||
PropColors_Rainbow,
|
||||
PropColors_Count
|
||||
}
|
||||
|
||||
char ColorToString[PropColors_Count][] = {
|
||||
"Default",
|
||||
"Black",
|
||||
"Red",
|
||||
"Purple",
|
||||
"Blue",
|
||||
"Green",
|
||||
"Yellow",
|
||||
"Orange",
|
||||
"Rainbow"
|
||||
};
|
||||
|
||||
int ColorToRGB[PropColors_Count][3] = {
|
||||
{255, 255, 255},
|
||||
{0, 0, 0},
|
||||
{255, 0, 0},
|
||||
{128, 0, 128},
|
||||
{0, 0, 255},
|
||||
{0, 255, 0},
|
||||
{255, 255, 0},
|
||||
{255, 165, 0},
|
||||
{256, 256, 256}
|
||||
};
|
||||
|
||||
public void OnPluginStart()
|
||||
{
|
||||
@@ -41,7 +103,74 @@ public void OnPluginStart()
|
||||
}
|
||||
g_Models.Rewind(); // Go back to the root node
|
||||
|
||||
GameData gd = new GameData("PropSpawner");
|
||||
if (gd == null)
|
||||
{
|
||||
LogError("PropSpawner: Missing gamedata file");
|
||||
}
|
||||
else
|
||||
{
|
||||
int PlayerUseOffset = gd.GetOffset("PlayerUse");
|
||||
if (PlayerUseOffset == -1)
|
||||
{
|
||||
LogError("PropSpawner: Missing \"PlayerUse\" offset in gamedata file");
|
||||
}
|
||||
else
|
||||
{
|
||||
g_PlayerUseSetup = DHookCreate(PlayerUseOffset, HookType_Entity, ReturnType_Void, ThisPointer_CBaseEntity, PlayerUse_Hook);
|
||||
}
|
||||
|
||||
int ForceVPhysicsCollideOffset = gd.GetOffset("ForceVPhysicsCollide");
|
||||
if (ForceVPhysicsCollideOffset == -1)
|
||||
{
|
||||
LogError("PropSpawner: Missing \"ForceVPhysicsCollide\" offset in gamedata file");
|
||||
}
|
||||
else
|
||||
{
|
||||
g_ForceVPhysicsCollideSetup = DHookCreate(ForceVPhysicsCollideOffset, HookType_Entity, ReturnType_Bool, ThisPointer_CBaseEntity, ForceVPhysicsCollide_Hook);
|
||||
DHookAddParam(g_ForceVPhysicsCollideSetup, HookParamType_CBaseEntity);
|
||||
}
|
||||
|
||||
delete gd;
|
||||
}
|
||||
|
||||
RegAdminCmd("sm_propspawner", Command_MainMenu, ADMFLAG_CHANGEMAP); //g
|
||||
RegAdminCmd("sm_setpropcolor", Command_SetPropColor, ADMFLAG_CHANGEMAP, "Set PropSpawner color by RGB value, or \"rainbow\" for that.");
|
||||
|
||||
g_cvNotifyShot = CreateConVar("sm_propspawner_notify_shot", "0", "If set to 1, announces in chat when a plugin-spawned prop is shot.", FCVAR_NOTIFY, true, 0.0, true, 2.0);
|
||||
g_cvNotifyTimer = CreateConVar("sm_propspawner_notify_shot_timer", "0.5", "How long a player stays in the \"shooting couch\" list.");
|
||||
g_cvUseMode = CreateConVar("sm_propspawner_use_mode", "0", "What should happen when a prop is +use'd. " ...
|
||||
"0 = Normal. 1 = Disallow CTs moving plugin spawned props. 2 = Add player to notify list.");
|
||||
g_cvPropShootTier = CreateConVar("sm_propspawner_tier_requirement", "-1", "Tier needed for players to be able to move PropSpawner props.");
|
||||
g_cvButtonPressTier = CreateConVar("sm_propspawner_button_tier_requirement", "-1", "Tier needed for players to be able to press buttons.");
|
||||
AutoExecConfig(true, "PropSpawner");
|
||||
|
||||
g_aSpawnedProps = new ArrayList();
|
||||
g_aNotifyList = new ArrayList();
|
||||
}
|
||||
|
||||
public void OnEntityCreated(int entity, const char[] classname)
|
||||
{
|
||||
if (StrEqual(classname, "func_clip_vphysics") && g_ForceVPhysicsCollideSetup != INVALID_HANDLE)
|
||||
{
|
||||
DHookEntity(g_ForceVPhysicsCollideSetup, false, entity);
|
||||
}
|
||||
else if (StrEqual(classname, "func_button") && g_cvButtonPressTier.IntValue > -1)
|
||||
{
|
||||
SDKHook(entity, SDKHook_Use, OnButtonUse);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnEntityDestroyed(int entity)
|
||||
{
|
||||
if (entity <= 0 || g_aSpawnedProps == null)
|
||||
return;
|
||||
|
||||
int index = g_aSpawnedProps.FindValue(entity);
|
||||
if (index != -1)
|
||||
{
|
||||
g_aSpawnedProps.Erase(index);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnMapStart()
|
||||
@@ -53,6 +182,7 @@ public void OnMapStart()
|
||||
g_PhysicMenu = BuildModelsMenu("physic");
|
||||
g_DynamicMenu = BuildModelsMenu("dynamic");
|
||||
g_NpcMenu = BuildModelsMenu("npc");
|
||||
g_ColorMenu = BuildColorMenu();
|
||||
}
|
||||
|
||||
public void OnMapEnd()
|
||||
@@ -94,6 +224,7 @@ Menu BuildMainMenu(){
|
||||
menu.AddItem("menu_prop_dynamic", "Dynamic/Static Props");
|
||||
menu.AddItem("menu_prop_physic", "Physic Props");
|
||||
menu.AddItem("menu_prop_npc", "NPCs");
|
||||
menu.AddItem("menu_prop_color", "Choose Color");
|
||||
menu.ExitButton = true;
|
||||
return menu;
|
||||
}
|
||||
@@ -144,6 +275,20 @@ Menu BuildModelsMenu(char[] modelType){
|
||||
return menu;
|
||||
}
|
||||
|
||||
Menu BuildColorMenu()
|
||||
{
|
||||
Menu menu = new Menu(ColorHandler);
|
||||
SetMenuTitle(menu, "Choose Prop Color");
|
||||
for (int i = 0; i < PropColors_Count; i++)
|
||||
{
|
||||
menu.AddItem(ColorToString[i], ColorToString[i]);
|
||||
}
|
||||
|
||||
menu.ExitBackButton = true;
|
||||
menu.ExitButton = true;
|
||||
return menu;
|
||||
}
|
||||
|
||||
/* ----------------------------------- */
|
||||
|
||||
public Action Command_MainMenu(int client, int args)
|
||||
@@ -152,6 +297,35 @@ public Action Command_MainMenu(int client, int args)
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
public Action Command_SetPropColor(int client, int args)
|
||||
{
|
||||
if (args < 3)
|
||||
{
|
||||
if (args != 0)
|
||||
{
|
||||
char buf[32];
|
||||
GetCmdArg(1, buf, sizeof(buf));
|
||||
if (StrEqual(buf, "rainbow", false))
|
||||
{
|
||||
g_aPlayerPropColor[client][0] = 256;
|
||||
ReplyToCommand(client, "\x01[SM] Changed color to \x04Rainbow\x01.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ReplyToCommand(client, "Usage: sm_setpropcolor <r> <g> <b>");
|
||||
}
|
||||
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
int r = g_aPlayerPropColor[client][0] = GetCmdArgInt(1) % 256;
|
||||
int g = g_aPlayerPropColor[client][1] = GetCmdArgInt(2) % 256;
|
||||
int b = g_aPlayerPropColor[client][2] = GetCmdArgInt(3) % 256;
|
||||
ReplyToCommand(client, "\x01[SM] Changed color to \x07%02x%02x%02x%d %d %d\x01.", r, g, b, r, g, b);
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
public int MainMenuHandler(Menu menu, MenuAction action, int client, int param2)
|
||||
{
|
||||
char info[32];
|
||||
@@ -175,8 +349,12 @@ public int MainMenuHandler(Menu menu, MenuAction action, int client, int param2)
|
||||
else if(StrEqual(info, "menu_prop_npc")){
|
||||
g_NpcMenu.Display(client, MENU_TIME_FOREVER);
|
||||
}
|
||||
else if(StrEqual(info, "menu_prop_color")){
|
||||
g_ColorMenu.Display(client, MENU_TIME_FOREVER);
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public RotationHandler(Menu menu, MenuAction action, int client, int param2)
|
||||
@@ -302,6 +480,37 @@ public NpcHandler(Menu menu, MenuAction action, int client, int param2)
|
||||
}
|
||||
}
|
||||
|
||||
public ColorHandler(Menu menu, MenuAction action, int client, int param2)
|
||||
{
|
||||
char info[32];
|
||||
menu.GetItem(param2, info, sizeof(info));
|
||||
switch (action)
|
||||
{
|
||||
case MenuAction_Select:
|
||||
{
|
||||
int colorIdx = -1;
|
||||
for (int i = 0; i < PropColors_Count; i++)
|
||||
{
|
||||
if (StrEqual(ColorToString[i], info))
|
||||
{
|
||||
colorIdx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (colorIdx != -1)
|
||||
{
|
||||
g_aPlayerPropColor[client] = ColorToRGB[colorIdx];
|
||||
ReplyToCommand(client, "\x01[SM] Changed color to \x04%s\x01.", info);
|
||||
g_MainMenu.Display(client, MENU_TIME_FOREVER);
|
||||
}
|
||||
}
|
||||
|
||||
case MenuAction_Cancel:
|
||||
if (param2 == MenuCancel_ExitBack)
|
||||
g_MainMenu.Display(client, MENU_TIME_FOREVER);
|
||||
}
|
||||
}
|
||||
|
||||
bool isAProp(char[] classname){
|
||||
if(StrEqual(classname, "prop_physics") || StrEqual(classname, "prop_physics_override") || StrEqual(classname, "prop_dynamic") || StrEqual(classname, "prop_dynamic_override") || StrEqual(classname, "prop_physics_multiplayer") || StrEqual(classname, "prop_dynamic_ornament") || StrEqual(classname, "prop_static")){
|
||||
@@ -405,7 +614,7 @@ void prop_any_create(int client)
|
||||
char sModelExplodeRadius[11];
|
||||
int iModelExplode = g_Models.GetNum("explode",0); // if "explode" is not set, then it's 0
|
||||
|
||||
g_Models.GetString("health", sModelHealth, sizeof(sModelHealth),"0"); // if "health" is not set, then it's 0
|
||||
g_Models.GetString("health", sModelHealth, sizeof(sModelHealth),"0"); // if "health" is not set, then its 0
|
||||
g_Models.GetString("explodedamage", sModelExplodeDamage, sizeof(sModelExplodeDamage),"1");
|
||||
g_Models.GetString("exploderadius", sModelExplodeRadius, sizeof(sModelExplodeRadius),"1");
|
||||
|
||||
@@ -485,6 +694,323 @@ void prop_any_create(int client)
|
||||
AcceptEntityInput(prop, "TurnOn", prop, prop, 0);
|
||||
AcceptEntityInput(prop, "EnableCollision");
|
||||
|
||||
// Hook the prop so we can detect when it gets shot/damaged
|
||||
if (g_cvNotifyShot.BoolValue || g_cvPropShootTier.IntValue > -1)
|
||||
{
|
||||
SetEntityFlags(prop, FL_CLIENT); //Now youre thinking with portals
|
||||
SetEntProp(prop, Prop_Data, "m_iTeamNum", CS_TEAM_CT); // So it works with filtered triggers
|
||||
SDKHook(prop, SDKHook_OnTakeDamage, OnPropTakeDamage);
|
||||
}
|
||||
|
||||
g_aSpawnedProps.Push(prop);
|
||||
|
||||
SetEntityRenderMode(prop, RENDER_TRANSCOLOR);
|
||||
if (g_aPlayerPropColor[client][0] == 256) // Rainbow
|
||||
{
|
||||
CreateTimer(0.2, OnRainbowTimer, EntIndexToEntRef(prop), TIMER_REPEAT);
|
||||
ApplyRainbow(prop, 1.0);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetEntityRenderColor(prop, g_aPlayerPropColor[client][0], g_aPlayerPropColor[client][1], g_aPlayerPropColor[client][2], 255);
|
||||
}
|
||||
|
||||
ShowActivity2(client, "\x01[SM] \x04", "\x01Spawned \x04%s\x01.", sModelName);
|
||||
LogAction(client, -1, "\"%L\" Spawned \"%s\".", client, sModelName);
|
||||
}
|
||||
|
||||
/* ----------------------------------- */
|
||||
|
||||
public Action OnPropTakeDamage(int victim, int &attacker, int &inflictor, float &damage, int &damagetype, int &weapon, float damageForce[3], float damagePosition[3], int damagecustom)
|
||||
{
|
||||
if (attacker < 1 || attacker > MaxClients || !IsClientInGame(attacker))
|
||||
return Plugin_Continue;
|
||||
|
||||
if (GetClientTeam(attacker) == CS_TEAM_CT && GetPlayerTier_native(attacker) < g_cvPropShootTier.IntValue)
|
||||
return Plugin_Handled;
|
||||
|
||||
if (!g_cvNotifyShot.BoolValue)
|
||||
return Plugin_Continue;
|
||||
|
||||
AddToNotifyList(attacker);
|
||||
DisplayNotifyList();
|
||||
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
char g_szNotifyText[80];
|
||||
|
||||
void DisplayNotifyList()
|
||||
{
|
||||
if (g_cvNotifyShot.IntValue == 2)
|
||||
return;
|
||||
|
||||
static int tick = 0;
|
||||
|
||||
strcopy(g_szNotifyText, sizeof(g_szNotifyText), "Shooting Couch:\n");
|
||||
|
||||
for (int i = g_aNotifyList.Length - 1; i >= 0; i--)
|
||||
{
|
||||
NotifyListData buf;
|
||||
g_aNotifyList.GetArray(i, buf, sizeof(NotifyListData));
|
||||
|
||||
if (!IsClientInGame(buf.client))
|
||||
{
|
||||
if (buf.timer != INVALID_HANDLE)
|
||||
KillTimer(buf.timer);
|
||||
|
||||
g_aNotifyList.Erase(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
char clientName[32];
|
||||
GetClientName(buf.client, clientName, sizeof(clientName));
|
||||
StrCat(g_szNotifyText, sizeof(g_szNotifyText), clientName);
|
||||
StrCat(g_szNotifyText, sizeof(g_szNotifyText), "\n");
|
||||
}
|
||||
|
||||
if (tick != GetGameTickCount())
|
||||
{
|
||||
// Wait a frame since other players might still shoot the couch this frame
|
||||
RequestFrame(DisplayNotifyList2);
|
||||
tick = GetGameTickCount();
|
||||
}
|
||||
}
|
||||
|
||||
void OnNotifyTimer(Handle timer, any data)
|
||||
{
|
||||
int client = view_as<int>(data);
|
||||
int idx = FindInNotifyList(client);
|
||||
if (idx != -1)
|
||||
g_aNotifyList.Erase(idx);
|
||||
|
||||
if (g_aNotifyList.Length)
|
||||
DisplayNotifyList();
|
||||
}
|
||||
|
||||
int FindInNotifyList(int client)
|
||||
{
|
||||
for (int i = 0; i < g_aNotifyList.Length; i++)
|
||||
{
|
||||
NotifyListData buf;
|
||||
g_aNotifyList.GetArray(i, buf, sizeof(NotifyListData));
|
||||
if (buf.client == client)
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public void OnClientPutInServer(int client)
|
||||
{
|
||||
g_aPlayerPropColor[client] = {255, 255, 255};
|
||||
if (g_PlayerUseSetup != INVALID_HANDLE)
|
||||
{
|
||||
DHookEntity(g_PlayerUseSetup, false, client);
|
||||
DHookEntity(g_PlayerUseSetup, true, client, INVALID_FUNCTION, PlayerUse_HookPost);
|
||||
}
|
||||
}
|
||||
|
||||
void OnRainbowTimer(Handle timer, any data)
|
||||
{
|
||||
int prop = EntRefToEntIndex(view_as<int>(data));
|
||||
if (prop == -1)
|
||||
{
|
||||
KillTimer(timer);
|
||||
return;
|
||||
}
|
||||
|
||||
ApplyRainbow(prop, 1.0);
|
||||
}
|
||||
|
||||
// Copied from GlowColors
|
||||
void ApplyRainbow(int ent, float Frequency)
|
||||
{
|
||||
float i = GetGameTime();
|
||||
|
||||
int Red = RoundFloat(Sine(Frequency * i + 0.0) * 127.0 + 128.0);
|
||||
int Green = RoundFloat(Sine(Frequency * i + 2.0943951) * 127.0 + 128.0);
|
||||
int Blue = RoundFloat(Sine(Frequency * i + 4.1887902) * 127.0 + 128.0);
|
||||
|
||||
SetEntityRenderColor(ent, Red, Green, Blue, 255);
|
||||
}
|
||||
|
||||
void AddToNotifyList(int client)
|
||||
{
|
||||
if (GetClientTeam(client) != CS_TEAM_CT)
|
||||
return;
|
||||
|
||||
// Just in case the buffer overflow in net message issue doesn't go away with the other fixes
|
||||
if (g_cvNotifyShot.IntValue == 2)
|
||||
{
|
||||
char clientName[32];
|
||||
GetClientName(client, clientName, sizeof(clientName));
|
||||
PrintToConsoleAll("[SM] %s is shooting the couch.", clientName);
|
||||
return;
|
||||
}
|
||||
|
||||
Handle timer = CreateTimer(g_cvNotifyTimer.FloatValue, OnNotifyTimer, client);
|
||||
int idx = FindInNotifyList(client);
|
||||
if (idx == -1)
|
||||
{
|
||||
NotifyListData data;
|
||||
data.client = client;
|
||||
data.timer = timer;
|
||||
g_aNotifyList.PushArray(data, sizeof(NotifyListData));
|
||||
}
|
||||
else
|
||||
{
|
||||
NotifyListData buf;
|
||||
g_aNotifyList.GetArray(idx, buf, sizeof(NotifyListData));
|
||||
if (buf.timer != INVALID_HANDLE)
|
||||
KillTimer(buf.timer);
|
||||
|
||||
buf.timer = timer;
|
||||
g_aNotifyList.SetArray(idx, buf, sizeof(NotifyListData));
|
||||
}
|
||||
}
|
||||
|
||||
ConVar sv_pushaway_force = null;
|
||||
float g_flPushawayRestore = 0.0;
|
||||
|
||||
MRESReturn PlayerUse_Hook(int pThis)
|
||||
{
|
||||
static ConVar sv_turbophysics = null;
|
||||
if (!sv_pushaway_force)
|
||||
sv_pushaway_force = FindConVar("sv_pushaway_force");
|
||||
|
||||
if (!sv_turbophysics)
|
||||
sv_turbophysics = FindConVar("sv_turbophysics");
|
||||
|
||||
if (!sv_pushaway_force || !sv_turbophysics)
|
||||
return MRES_Ignored;
|
||||
|
||||
if (!sv_turbophysics.BoolValue)
|
||||
return MRES_Ignored;
|
||||
|
||||
if (g_cvUseMode.IntValue != 1 && g_cvUseMode.IntValue != 2 && g_cvPropShootTier.IntValue <= -1)
|
||||
return MRES_Ignored;
|
||||
|
||||
// ZMs can always push it
|
||||
if (GetClientTeam(pThis) != CS_TEAM_CT)
|
||||
return MRES_Ignored;
|
||||
|
||||
if ((GetClientButtons(pThis) & IN_USE) == 0)
|
||||
return MRES_Ignored;
|
||||
|
||||
float eyePos[3];
|
||||
float eyeAng[3];
|
||||
float eyeFwd[3];
|
||||
float endPos[3];
|
||||
GetClientEyePosition(pThis, eyePos);
|
||||
GetClientEyeAngles(pThis, eyeAng);
|
||||
GetAngleVectors(eyeAng, eyeFwd, NULL_VECTOR, NULL_VECTOR);
|
||||
|
||||
endPos[0] = eyePos[0] + (eyeFwd[0] * 96.0);
|
||||
endPos[1] = eyePos[1] + (eyeFwd[1] * 96.0);
|
||||
endPos[2] = eyePos[2] + (eyeFwd[2] * 96.0);
|
||||
|
||||
Handle tr = TR_TraceRayFilterEx(eyePos, endPos, MASK_SOLID, RayType_EndPoint, UsePushFilter, 0, TRACE_ENTITIES_ONLY);
|
||||
|
||||
if (TR_DidHit(tr))
|
||||
{
|
||||
int entHit = TR_GetEntityIndex(tr);
|
||||
|
||||
if (GetEntityMoveType(entHit) == MOVETYPE_VPHYSICS && g_aSpawnedProps.FindValue(entHit) != -1)
|
||||
{
|
||||
int mode = g_cvUseMode.IntValue;
|
||||
if (GetPlayerTier_native(pThis) < g_cvPropShootTier.IntValue)
|
||||
mode = 1;
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
// Prevent the prop from being moved
|
||||
case 1:
|
||||
{
|
||||
g_flPushawayRestore = sv_pushaway_force.FloatValue;
|
||||
sv_pushaway_force.FloatValue = 0.0;
|
||||
}
|
||||
|
||||
// Add to the shooting couch list
|
||||
case 2:
|
||||
{
|
||||
if (g_cvNotifyShot.BoolValue)
|
||||
{
|
||||
AddToNotifyList(pThis);
|
||||
DisplayNotifyList();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CloseHandle(tr);
|
||||
return MRES_Handled;
|
||||
}
|
||||
|
||||
MRESReturn PlayerUse_HookPost(int pThis)
|
||||
{
|
||||
if (sv_pushaway_force && g_flPushawayRestore != 0.0)
|
||||
{
|
||||
sv_pushaway_force.FloatValue = g_flPushawayRestore;
|
||||
g_flPushawayRestore = 0.0;
|
||||
}
|
||||
|
||||
return MRES_Handled;
|
||||
}
|
||||
|
||||
bool UsePushFilter(int entity, int contentsMask)
|
||||
{
|
||||
// Checking if the entity has a physics object
|
||||
int offs = FindDataMapInfo(entity, "m_pPhysicsObject");
|
||||
int pt1 = LoadFromAddress(GetEntityAddress(entity) + view_as<Address>(offs), NumberType_Int32);
|
||||
int pt2 = view_as<int>(Address_PointerSize) == 8 ? LoadFromAddress(GetEntityAddress(entity) + view_as<Address>(offs + 4), NumberType_Int32) : 0;
|
||||
if (pt1 == 0 && pt2 == 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
MRESReturn ForceVPhysicsCollide_Hook(int pThis, DHookReturn hReturn, DHookParam hParams)
|
||||
{
|
||||
int ent = hParams.Get(1);
|
||||
if (GetEntityMoveType(ent) == MOVETYPE_VPHYSICS && g_aSpawnedProps.FindValue(ent) != -1)
|
||||
{
|
||||
hReturn.Value = false;
|
||||
return MRES_Supercede;
|
||||
}
|
||||
|
||||
return MRES_Ignored;
|
||||
}
|
||||
|
||||
void DisplayNotifyList2()
|
||||
{
|
||||
static int channel = -1;
|
||||
static float lastDisplayTime = 0.0;
|
||||
|
||||
// Change the channel every once in a while, to avoid conflicts with the map's game_text entities
|
||||
// (not really sure if sourcemod accounts for them when using -1 as channel but hopefully it does)
|
||||
if ((GetEngineTime() - lastDisplayTime) > (g_cvNotifyTimer.FloatValue * 2.0))
|
||||
channel = -1;
|
||||
|
||||
SetHudTextParams(0.8, 0.2, g_cvNotifyTimer.FloatValue, 255, 255, 255, 255, 0, 0.0, 0.0, 0.0);
|
||||
for (int i = 1; i <= MaxClients; i++)
|
||||
{
|
||||
if (IsClientInGame(i))
|
||||
channel = ShowHudText(i, channel, g_szNotifyText);
|
||||
}
|
||||
|
||||
lastDisplayTime = GetEngineTime();
|
||||
}
|
||||
|
||||
// To prevent people from early triggering on couch events. Not every map uses buttons for triggers however.
|
||||
Action OnButtonUse(int entity, int activator, int caller, UseType type, float value)
|
||||
{
|
||||
if (activator < 1 || activator > MaxClients || !IsClientInGame(activator))
|
||||
return Plugin_Continue;
|
||||
|
||||
if (GetClientTeam(activator) == CS_TEAM_CT && GetPlayerTier_native(activator) < g_cvButtonPressTier.IntValue)
|
||||
return Plugin_Handled;
|
||||
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
@@ -1,23 +1,38 @@
|
||||
#pragma semicolon 1
|
||||
|
||||
//Yes, this should be a convar. No, I cant be bothered to do the convar hooks.
|
||||
#define DEFAULT_BAN 60
|
||||
|
||||
#include <sourcemod>
|
||||
#include <clientprefs>
|
||||
|
||||
#pragma newdecls required
|
||||
|
||||
bool g_bLateLoad = false;
|
||||
bool g_bProtoBuf;
|
||||
bool g_bBlocked[MAXPLAYERS + 1];
|
||||
|
||||
int g_iBanTime[MAXPLAYERS + 1]; //CAREFUL! 0 ON THIS VARIABLE MEANS NOT BANNED, BUT 0 FROM COOKIE OUTPUT IS AN ERROR!
|
||||
Cookie g_CookieBanTime = null;
|
||||
|
||||
//bool g_bBlocked[MAXPLAYERS + 1];
|
||||
|
||||
int g_iMessageClient = -1;
|
||||
|
||||
public Plugin myinfo =
|
||||
{
|
||||
name = "RadioMute",
|
||||
description = "Very simple plugin to block players from using the in-game radio in supported games.",
|
||||
author = "Obus",
|
||||
version = "1.0.1",
|
||||
description = "(No longer that) Very simple plugin to block players from using the in-game radio in supported games.",
|
||||
author = "Obus, Pan32",
|
||||
version = "1.1.0",
|
||||
url = ""
|
||||
}
|
||||
|
||||
public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max)
|
||||
{
|
||||
g_bLateLoad = late;
|
||||
return APLRes_Success;
|
||||
}
|
||||
|
||||
public void OnPluginStart()
|
||||
{
|
||||
LoadTranslations("common.phrases");
|
||||
@@ -35,31 +50,46 @@ public void OnPluginStart()
|
||||
if (SendAudio == INVALID_MESSAGE_ID)
|
||||
SetFailState("This game does not support the \"SendAudio\" UserMessage.");
|
||||
|
||||
g_CookieBanTime = new Cookie("radiomute_ban_time", "Stores the unix epoch time in which a players ban expires", CookieAccess_Private);
|
||||
|
||||
RegAdminCmd("sm_radiomute", Command_RadioMute, ADMFLAG_BAN, "Block a client from using the in-game radio.");
|
||||
RegAdminCmd("sm_radiounmute", Command_RadioUnmute, ADMFLAG_BAN, "Unblock a client from using the in-game radio.");
|
||||
|
||||
HookUserMessage(RadioText, _hkRadioText, true);
|
||||
HookUserMessage(SendAudio, _hkSendAudio, true);
|
||||
|
||||
if (g_bLateLoad)
|
||||
{
|
||||
for (int i = 1; i <= MaxClients; i++)
|
||||
{
|
||||
if (IsClientConnected(i) && IsClientInGame(i))
|
||||
{
|
||||
OnClientCookiesCached(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void OnMapEnd()
|
||||
{
|
||||
for (int i = 1; i <= MaxClients; i++)
|
||||
{
|
||||
g_bBlocked[i] = false;
|
||||
//g_bBlocked[i] = false;
|
||||
g_iBanTime[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void OnClientDisconnect(int client)
|
||||
{
|
||||
g_bBlocked[client] = false;
|
||||
//g_bBlocked[client] = false;
|
||||
g_iBanTime[client] = 0;
|
||||
}
|
||||
|
||||
public Action Command_RadioMute(int client, int argc)
|
||||
{
|
||||
if (argc < 1)
|
||||
{
|
||||
ReplyToCommand(client, "[SM] Usage: sm_radiomute <target>");
|
||||
ReplyToCommand(client, "[SM] Usage: sm_radiomute <target> <time>");
|
||||
|
||||
return Plugin_Handled;
|
||||
}
|
||||
@@ -79,13 +109,47 @@ public Action Command_RadioMute(int client, int argc)
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
int iBanTime = DEFAULT_BAN;
|
||||
if (argc > 1)
|
||||
{
|
||||
GetCmdArg(2, sArgs, sizeof(sArgs));
|
||||
//BUGBUG Not checking for string to int errors, as people are used to 0 for perm and 0 is also the error value
|
||||
iBanTime = StringToInt(sArgs);
|
||||
if (iBanTime < 0)
|
||||
{
|
||||
ReplyToCommand(client, "[SM] Invalid ban time");
|
||||
}
|
||||
|
||||
if (iBanTime == 0)
|
||||
{
|
||||
//Does this technically violate the previous check? Yes, but the banned value is this. Tought.
|
||||
iBanTime = -1;
|
||||
}
|
||||
}
|
||||
|
||||
char sBanTimeStamp[64];
|
||||
int iBanTimeStamp;
|
||||
|
||||
if (iBanTime == -1) //Permanent ban
|
||||
{
|
||||
iBanTimeStamp = -1;
|
||||
Format(sBanTimeStamp, sizeof(sBanTimeStamp), "-1");
|
||||
}
|
||||
else //Timestamp + time
|
||||
{
|
||||
iBanTimeStamp = GetTime() + MinsToSecs(iBanTime);
|
||||
Format(sBanTimeStamp, sizeof(sBanTimeStamp), "%d", iBanTimeStamp);
|
||||
}
|
||||
|
||||
for (int i = 0; i < iTargetCount; i++)
|
||||
{
|
||||
g_bBlocked[iTargets[i]] = true;
|
||||
g_CookieBanTime.Set(iTargets[i], sBanTimeStamp);
|
||||
g_iBanTime[iTargets[i]] = iBanTimeStamp;
|
||||
//g_bBlocked[iTargets[i]] = true;
|
||||
}
|
||||
|
||||
ShowActivity2(client, "\x01[SM] \x04", "\x01Radio muted \x04%s\x01", sTargetName);
|
||||
LogAction(client, -1, "\"%L\" radio muted \"%s\"", client, sTargetName);
|
||||
LogMessage("\"%L\" radio muted \"%s\" for %d minutes.", client, sTargetName, iBanTime);
|
||||
|
||||
return Plugin_Handled;
|
||||
}
|
||||
@@ -116,11 +180,14 @@ public Action Command_RadioUnmute(int client, int argc)
|
||||
|
||||
for (int i = 0; i < iTargetCount; i++)
|
||||
{
|
||||
g_bBlocked[iTargets[i]] = false;
|
||||
g_CookieBanTime.Set(iTargets[i], "");
|
||||
g_iBanTime[iTargets[i]] = 0;
|
||||
//g_bBlocked[iTargets[i]] = false;
|
||||
}
|
||||
|
||||
ShowActivity2(client, "\x01[SM] \x04", "\x01Radio unmuted \x04%s\x01", sTargetName);
|
||||
LogAction(client, -1, "\"%L\" radio unmuted \"%s\"", client, sTargetName);
|
||||
//Originally LogAction, but for some reason its not logging, so LogMessage it is
|
||||
LogMessage("\"%L\" radio unmuted \"%s\"", client, sTargetName);
|
||||
|
||||
return Plugin_Handled;
|
||||
}
|
||||
@@ -137,7 +204,8 @@ public Action _hkRadioText(UserMsg msg_id, Handle bf, const int[] players, int p
|
||||
g_iMessageClient = BfReadByte(bf);
|
||||
}
|
||||
|
||||
if (g_bBlocked[g_iMessageClient])
|
||||
//if (g_bBlocked[g_iMessageClient])
|
||||
if (IsRadioBanned(g_iMessageClient))
|
||||
{
|
||||
return Plugin_Handled;
|
||||
}
|
||||
@@ -160,7 +228,8 @@ public Action _hkSendAudio(UserMsg msg_id, Handle bf, const int[] players, int p
|
||||
if (strncmp(sSound[6], "lock", 4, false) == 0)
|
||||
return Plugin_Continue;
|
||||
|
||||
if (g_bBlocked[g_iMessageClient])
|
||||
//if (g_bBlocked[g_iMessageClient])
|
||||
if (IsRadioBanned(g_iMessageClient))
|
||||
{
|
||||
g_iMessageClient = -1;
|
||||
|
||||
@@ -171,3 +240,46 @@ public Action _hkSendAudio(UserMsg msg_id, Handle bf, const int[] players, int p
|
||||
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
public void OnClientCookiesCached(int client)
|
||||
{
|
||||
//Get cookie
|
||||
char sBanTime[64];
|
||||
g_CookieBanTime.Get(client, sBanTime, sizeof(sBanTime));
|
||||
if (StrEqual(sBanTime, "")) //If string is empty, no ban ongoing.
|
||||
{
|
||||
g_iBanTime[client] = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
int epochTime = StringToInt(sBanTime);
|
||||
if (epochTime == 0) //0 Means the string to int errored out, log and empty out the string
|
||||
{
|
||||
char steamId[32];
|
||||
GetClientAuthId(client, AuthId_Steam2, steamId, sizeof(steamId));
|
||||
LogError("Failed to interpret correctly the ban time length from cookie for user %N (%s)", client, steamId);
|
||||
g_CookieBanTime.Set(client, "");
|
||||
g_iBanTime[client] = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (epochTime < GetTime()) //Player is no longer banned
|
||||
{
|
||||
g_CookieBanTime.Set(client, "");
|
||||
g_iBanTime[client] = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
g_iBanTime[client] = epochTime;
|
||||
}
|
||||
|
||||
//If this turns out to be very expensive, we can convert it back to boolean and banned players will only get unbanned on reconnect or mapchange. But this would work as an ongoing thing.
|
||||
public bool IsRadioBanned(int client)
|
||||
{
|
||||
return (g_iBanTime[client] == -1 || g_iBanTime[client] >= GetTime());
|
||||
}
|
||||
|
||||
public int MinsToSecs(int mins)
|
||||
{
|
||||
return mins*60;
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
#pragma semicolon 1
|
||||
|
||||
#include <sourcemod>
|
||||
#include <sdktools>
|
||||
#include <sdkhooks>
|
||||
#include <clientprefs>
|
||||
#include <multicolors>
|
||||
|
||||
#pragma newdecls required
|
||||
|
||||
static const char entityList[][] = { "env_entity_igniter", "env_explosion", "env_fade", "env_fire", "env_hudhint", \
|
||||
"env_physexplosion", "env_sprite", "env_sun", \
|
||||
"func_breakable", "func_brush", "func_bomb_target", "func_button", "func_door", \
|
||||
"func_dustmotes", "func_physbox_multiplayer", "info_overlay", "info_particle_system", \
|
||||
"info_player_counterterrorist", "info_player_terrorist", "infodecal", \
|
||||
"item_assaultsuit", "item_defuser", "item_kevlar", \
|
||||
"light", "light_environment", "light_spot", "move_rope", "path_track", "planted_c4", \
|
||||
"point_viewcontrol", "shadow_control", "vgui_screen", "func_tracktrain", "worldspawn", \
|
||||
"func_tanktrain"};
|
||||
|
||||
bool bEnabled[MAXPLAYERS + 1];
|
||||
|
||||
int iDistance[MAXPLAYERS + 1];
|
||||
|
||||
float g_fClientOrigin[MAXPLAYERS + 1][3];
|
||||
|
||||
public void OnPluginStart()
|
||||
{
|
||||
// Run the cache timer every 0.5 seconds globally
|
||||
CreateTimer(0.5, Timer_CacheOrigins, _, TIMER_REPEAT);
|
||||
}
|
||||
|
||||
public Action Timer_CacheOrigins(Handle timer)
|
||||
{
|
||||
for (int i = 1; i <= MaxClients; i++)
|
||||
{
|
||||
if (IsClientConnected(i) && IsClientInGame(i) && !IsFakeClient(i))
|
||||
{
|
||||
GetClientAbsOrigin(i, g_fClientOrigin[i]);
|
||||
}
|
||||
}
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
public Plugin myinfo =
|
||||
{
|
||||
name = "Render Distance Control",
|
||||
author = "null138 & (ty ZombieFeyk), modified for webclient on unloze",
|
||||
description = "Sets entities render distance and etc for players",
|
||||
version = "3.0.5",
|
||||
url = "https://steamcommunity.com/id/null138/"
|
||||
}
|
||||
|
||||
public void OnClientAuthorized(int client, const char[] auth)
|
||||
{
|
||||
bEnabled[client] = false;
|
||||
iDistance[client] = 800;
|
||||
|
||||
char sIP[32];
|
||||
GetClientIP(client, sIP, sizeof(sIP));
|
||||
|
||||
char allowed_ips[128];
|
||||
ConVar sv_set_steam_id_ips = FindConVar("sv_set_steam_id_ips");
|
||||
sv_set_steam_id_ips.GetString(allowed_ips, sizeof(allowed_ips));
|
||||
if (StrEqual(sIP, allowed_ips))
|
||||
{
|
||||
bEnabled[client] = true;
|
||||
iDistance[client] = 1200;
|
||||
}
|
||||
}
|
||||
|
||||
public void OnEntityCreated(int entity, const char[] classname)
|
||||
{
|
||||
bool hook = false;
|
||||
|
||||
if (!strncmp(classname, "prop_", 5))
|
||||
{
|
||||
hook = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
for(int i; i < sizeof(entityList); i++)
|
||||
{
|
||||
if(!strcmp(classname, entityList[i]))
|
||||
{
|
||||
hook = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hook)
|
||||
{
|
||||
SDKHook(entity, SDKHook_SetTransmit, DoTransmit);
|
||||
}
|
||||
}
|
||||
|
||||
public Action DoTransmit(int entity, int client)
|
||||
{
|
||||
if (IsFakeClient(client) || !bEnabled[client])
|
||||
return Plugin_Continue;
|
||||
|
||||
if (GetEdictFlags(entity) & FL_EDICT_ALWAYS)
|
||||
SetEdictFlags(entity, GetEdictFlags(entity) ^ FL_EDICT_ALWAYS);
|
||||
|
||||
// Get Entity Origin
|
||||
static float entVec[3];
|
||||
if (HasEntProp(entity, Prop_Send, "m_vecOrigin"))
|
||||
{
|
||||
GetEntPropVector(entity, Prop_Send, "m_vecOrigin", entVec);
|
||||
}
|
||||
else
|
||||
{
|
||||
return Plugin_Continue; // If it doesnt have an origin, let it render safely
|
||||
}
|
||||
|
||||
// Use GetVectorDistance with UseSquared=true (avoids an expensive Square Root calculation)
|
||||
float maxDist = float(iDistance[client]);
|
||||
float distanceSq = ((g_fClientOrigin[client][0] - entVec[0]) * (g_fClientOrigin[client][0] - entVec[0])) +
|
||||
((g_fClientOrigin[client][1] - entVec[1]) * (g_fClientOrigin[client][1] - entVec[1])) +
|
||||
((g_fClientOrigin[client][2] - entVec[2]) * (g_fClientOrigin[client][2] - entVec[2]));
|
||||
|
||||
if (distanceSq > (maxDist * maxDist))
|
||||
{
|
||||
return Plugin_Handled; // Out of range, hide it from the software renderer!
|
||||
}
|
||||
|
||||
return Plugin_Continue;
|
||||
}
|
||||
@@ -97,8 +97,10 @@ public void OnClientDisconnect(int client)
|
||||
public EConnect OnClientPreConnectEx(const char[] sName, char sPassword[255], const char[] sIP, const char[] sSteam32ID, char sRejectReason[255])
|
||||
{
|
||||
// Server not full, nothing to do...
|
||||
if(GetClientCount(false) < MaxClients)
|
||||
if (GetClientCount(false) < MaxClients)
|
||||
{
|
||||
return k_OnClientPreConnectEx_Accept;
|
||||
}
|
||||
|
||||
// Try to get precached admin id.
|
||||
AdminId admin = FindAdminByIdentity(AUTHMETHOD_STEAM, sSteam32ID);
|
||||
@@ -108,14 +110,13 @@ public EConnect OnClientPreConnectEx(const char[] sName, char sPassword[255], co
|
||||
if(admin != INVALID_ADMIN_ID && GetAdminFlag(admin, Admin_Reservation))
|
||||
{
|
||||
Immunity = GetAdminImmunityLevel(admin);
|
||||
|
||||
if(!KickValidClient(sName, sSteam32ID, admin, Immunity))
|
||||
{
|
||||
Format(sRejectReason, sizeof(sRejectReason), "No reserved slot available, sorry.");
|
||||
return k_OnClientPreConnectEx_Reject;
|
||||
}
|
||||
else
|
||||
return k_OnClientPreConnectEx_Accept;
|
||||
return k_OnClientPreConnectEx_Accept;
|
||||
}
|
||||
|
||||
if(g_Plugin_Break && Break_IsBreakActive())
|
||||
@@ -131,6 +132,7 @@ public EConnect OnClientPreConnectEx(const char[] sName, char sPassword[255], co
|
||||
pack.WriteCell(Immunity);
|
||||
pack.WriteString(sName);
|
||||
|
||||
LogMessage("here before AsyncHasSteamIDReservedSlot: sName: %s. sSteam32ID: %s", sName, sSteam32ID);
|
||||
AsyncHasSteamIDReservedSlot(sSteam32ID, AsyncHasSteamIDReservedSlotCallback, pack);
|
||||
return k_OnClientPreConnectEx_Async;
|
||||
}
|
||||
@@ -145,6 +147,7 @@ public void AsyncHasSteamIDReservedSlotCallback(const char[] sSteam32ID, int Res
|
||||
// Slot free'd up while waiting or doesn't have a reserved slot?
|
||||
if(GetClientCount(false) < MaxClients || Result <= 0)
|
||||
{
|
||||
LogMessage("here in return: sSteam32ID: %s. Result: %i", sSteam32ID, Result);
|
||||
delete pack;
|
||||
ClientPreConnectEx(sSteam32ID, k_OnClientPreConnectEx_Accept, "");
|
||||
return;
|
||||
@@ -162,10 +165,15 @@ public void AsyncHasSteamIDReservedSlotCallback(const char[] sSteam32ID, int Res
|
||||
if(Result > Immunity)
|
||||
Immunity = Result;
|
||||
|
||||
LogMessage("here 2: sName: %s. sSteam32ID: %s. Result: %i", sName, sSteam32ID, Result);
|
||||
if(!KickValidClient(sName, sSteam32ID, admin, Immunity))
|
||||
{
|
||||
ClientPreConnectEx(sSteam32ID, k_OnClientPreConnectEx_Reject, "No reserved slot available, sorry.");
|
||||
}
|
||||
else
|
||||
ClientPreConnectEx(sSteam32ID, k_OnClientPreConnectEx_Accept, "");
|
||||
{
|
||||
ClientPreConnectEx(sSteam32ID, k_OnClientPreConnectEx_Accept, "");
|
||||
}
|
||||
}
|
||||
|
||||
stock bool KickValidClient(const char[] sName, const char[] sSteam32ID, AdminId admin, int Immunity)
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
#if defined _AsyncSocket_included
|
||||
#endinput
|
||||
#endif
|
||||
#define _AsyncSocket_included
|
||||
|
||||
typedef AsyncSocketConnectCallback = function void(AsyncSocket socket);
|
||||
|
||||
typedef AsyncSocketErrorCallback = function void(AsyncSocket socket, int error, const char[] errorName);
|
||||
|
||||
typedef AsyncSocketDataCallback = function void(AsyncSocket socket, const char[] data, const int size);
|
||||
|
||||
methodmap AsyncSocket < Handle {
|
||||
public native AsyncSocket();
|
||||
|
||||
public native bool Connect(const char[] host, const int port);
|
||||
|
||||
public native bool Listen(const char[] host, const int port);
|
||||
|
||||
public native bool Write(const char[] data, int length = -1);
|
||||
|
||||
public bool WriteNull(const char[] data)
|
||||
{
|
||||
return this.Write(data, strlen(data) + 1);
|
||||
}
|
||||
|
||||
public native bool SetConnectCallback(AsyncSocketConnectCallback callback);
|
||||
|
||||
public native bool SetErrorCallback(AsyncSocketErrorCallback callback);
|
||||
|
||||
public native bool SetDataCallback(AsyncSocketDataCallback callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Do not edit below this line!
|
||||
*/
|
||||
public Extension __ext_AsyncSocket =
|
||||
{
|
||||
name = "AsyncSocket",
|
||||
file = "AsyncSocket.ext",
|
||||
#if defined AUTOLOAD_EXTENSIONS
|
||||
autoload = 1,
|
||||
#else
|
||||
autoload = 0,
|
||||
#endif
|
||||
#if defined REQUIRE_EXTENSIONS
|
||||
required = 1,
|
||||
#else
|
||||
required = 0,
|
||||
#endif
|
||||
};
|
||||
|
||||
#if !defined REQUIRE_EXTENSIONS
|
||||
public __ext_AsyncSocket_SetNTVOptional()
|
||||
{
|
||||
MarkNativeAsOptional("AsyncSocket.AsyncSocket");
|
||||
MarkNativeAsOptional("AsyncSocket.Connect");
|
||||
MarkNativeAsOptional("AsyncSocket.Listen");
|
||||
MarkNativeAsOptional("AsyncSocket.Write");
|
||||
MarkNativeAsOptional("AsyncSocket.SetConnectCallback");
|
||||
MarkNativeAsOptional("AsyncSocket.SetErrorCallback");
|
||||
MarkNativeAsOptional("AsyncSocket.SetDataCallback");
|
||||
}
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,6 @@
|
||||
#if defined _selfmute_included_
|
||||
#endinput
|
||||
#endif
|
||||
#define _selfmute_included_
|
||||
|
||||
native bool SelfMute_IsIgnoring(int client, int target);
|
||||
@@ -25,7 +25,8 @@ public void SourceTV_OnSpectatorPutInServer(int client)
|
||||
|
||||
SourceTV_GetClientName(client, sName, sizeof(sName));
|
||||
|
||||
if(SourceTV_GetClientIP(client, sIP, sizeof(sIP)) && GeoipCountry(sIP, sCountry, sizeof(sCountry)))
|
||||
SourceTV_GetClientIP(client, sIP, sizeof(sIP));
|
||||
if (strlen(sIP) != 0 && GeoipCountry(sIP, sCountry, sizeof(sCountry)))
|
||||
PrintToChatAll("\x04[\x03SourceTV\x04] %s connected from %s", sName, sCountry);
|
||||
else
|
||||
PrintToChatAll("\x04[\x03SourceTV\x04] %s connected", sName);
|
||||
|
||||
@@ -2072,6 +2072,7 @@ stock CreateBlock(client, targetId = 0, length = -1, type, const String:sReason[
|
||||
decl target_list[MAXPLAYERS], target_count, bool:tn_is_ml, String:target_name[MAX_NAME_LENGTH];
|
||||
new String:reason[256];
|
||||
new bool:skipped = false;
|
||||
new bool:unverified = false;
|
||||
|
||||
// checking args
|
||||
if (targetId)
|
||||
@@ -2167,9 +2168,10 @@ stock CreateBlock(client, targetId = 0, length = -1, type, const String:sReason[
|
||||
if (!g_bPlayerStatus[target])
|
||||
{
|
||||
// The target has not been blocks verify. It must be completed before you can block anyone.
|
||||
ReplyToCommand(client, "%s%t", PREFIX, "Player Comms Not Verified");
|
||||
skipped = true;
|
||||
continue; // skip
|
||||
//ReplyToCommand(client, "%s%t", PREFIX, "Player Comms Not Verified");
|
||||
//skipped = true;
|
||||
//continue; // skip
|
||||
unverified = true;
|
||||
}
|
||||
|
||||
switch (type)
|
||||
@@ -2251,7 +2253,7 @@ stock CreateBlock(client, targetId = 0, length = -1, type, const String:sReason[
|
||||
}
|
||||
}
|
||||
}
|
||||
if (target_count == 1 && !skipped)
|
||||
if (target_count == 1 && !skipped && !unverified)
|
||||
SavePunishment(client, target_list[0], type, length, reason);
|
||||
if (target_count > 1 || !skipped)
|
||||
ShowActivityToServer(client, type, length, reason, target_name, tn_is_ml);
|
||||
|
||||
@@ -5,6 +5,9 @@
|
||||
#include <clientprefs>
|
||||
#include <cstrike>
|
||||
|
||||
//add some hour requirement for being allowed to spray
|
||||
#include <unloze_playtime>
|
||||
|
||||
#undef REQUIRE_PLUGIN
|
||||
#include <adminmenu>
|
||||
#define REQUIRE_PLUGIN
|
||||
@@ -35,6 +38,7 @@ ConVar g_cvarUseProximityCheck = null;
|
||||
ConVar g_cvarSendSpraysToConnectingClients = null;
|
||||
ConVar g_cvarUsePersistentSprays = null;
|
||||
ConVar g_cvarMaxSprayLifetime = null;
|
||||
ConVar g_cvarSprayTierRequired = null;
|
||||
|
||||
int g_iNSFWDecalIndex;
|
||||
int g_iHiddenDecalIndex;
|
||||
@@ -85,7 +89,7 @@ public Plugin myinfo =
|
||||
{
|
||||
name = "Spray Manager",
|
||||
description = "A plugin to help manage player sprays.",
|
||||
author = "Obus",
|
||||
author = "Obus blabla",
|
||||
version = "2.1.1",
|
||||
url = ""
|
||||
}
|
||||
@@ -156,6 +160,8 @@ public void OnPluginStart()
|
||||
|
||||
g_cvarMaxSprayLifetime = CreateConVar("sm_spraymanager_maxspraylifetime", "2", "If not using persistent sprays, remove sprays after their global lifetime (in rounds) exceeds this number");
|
||||
|
||||
g_cvarSprayTierRequired = CreateConVar("sm_spraymanager_tier_required", "1", "Using the unloze play time plugin to set a minimum tier for allowing players access to spraying.");
|
||||
|
||||
AutoExecConfig(true, "plugin.SprayManager");
|
||||
|
||||
g_hTraceTimer = CreateTimer(0.25, Timer_PerformPlayerTraces, _, TIMER_REPEAT);
|
||||
@@ -2150,7 +2156,7 @@ public Action Command_ForceSFW(int client, int argc)
|
||||
return Plugin_Handled;
|
||||
|
||||
AdminForceSpraySFW(iTarget);
|
||||
PrintToChat(client, "\x01\x04[SprayManager]\x01 Marked \x04%N\x01's spray as SFW.", iTarget);
|
||||
PrintToChat(client, "\x01\x04[SprayManager]\x01 Marked \x04%N\x01 spray as SFW.", iTarget);
|
||||
|
||||
return Plugin_Handled;
|
||||
}
|
||||
@@ -2165,7 +2171,7 @@ public Action Command_ForceSFW(int client, int argc)
|
||||
continue;
|
||||
|
||||
AdminForceSpraySFW(i);
|
||||
PrintToChat(client, "\x01\x04[SprayManager]\x01 Marked \x04%N\x01's spray as SFW.", i);
|
||||
PrintToChat(client, "\x01\x04[SprayManager]\x01 Marked \x04%N\x01s spray as SFW.", i);
|
||||
|
||||
return Plugin_Handled;
|
||||
}
|
||||
@@ -2260,6 +2266,15 @@ public Action HookDecal(const char[] sTEName, const int[] iClients, int iNumClie
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
//check for an arbitrary amount of hours before allowing spraying
|
||||
int clientTier = GetPlayerTier_native(client);
|
||||
int TierRequired = g_cvarSprayTierRequired.IntValue;
|
||||
if (TierRequired > clientTier)
|
||||
{
|
||||
PrintToChat(client, "\x01\x04[SprayManager]\x01 You cannot spray because you dont have reached tier %i yet. (You have tier \x04%i)", TierRequired, clientTier);
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
if (g_fNextSprayTime[client] > GetGameTime())
|
||||
return Plugin_Handled;
|
||||
|
||||
|
||||
@@ -115,7 +115,8 @@ public void CheckStage()
|
||||
if(StrEqual(sMethod, "counter"))
|
||||
{
|
||||
char sCounter[64];
|
||||
if(!g_Config.GetString("counter", sCounter, sizeof(sCounter)))
|
||||
g_Config.GetString("counter", sCounter, sizeof(sCounter), "not found");
|
||||
if (StrEqual(sCounter, "not found"))
|
||||
{
|
||||
LogError("Could not find \"counter\"");
|
||||
return;
|
||||
@@ -172,7 +173,8 @@ public void CheckStage()
|
||||
bHasDiffCounter = true;
|
||||
|
||||
char sDiffCounter[64];
|
||||
if(!g_Config.GetString("counter", sDiffCounter, sizeof(sDiffCounter)))
|
||||
g_Config.GetString("counter", sDiffCounter, sizeof(sDiffCounter), "not found");
|
||||
if (StrEqual(sDiffCounter, "not found"))
|
||||
{
|
||||
LogError("Could not find \"diffcounter\"");
|
||||
return;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
//
|
||||
//====================================================================================================
|
||||
#include <sourcemod>
|
||||
#include <SteamWorks>
|
||||
#include <ripext>
|
||||
#include <unloze>
|
||||
#include <UNLOZE.secret> //#define UNLOZE_APIKEY here
|
||||
#include <cstrike>
|
||||
@@ -131,68 +131,71 @@ public void OnClientAuthorized(int client, const char[] sSteamID32)
|
||||
FormatEx(sRequest, sizeof(sRequest), "https://unloze.com/api/private_api.php?api_key=%s&steam_id=%s", UNLOZE_APIKEY, sSteamID64);
|
||||
|
||||
int iSerial = GetClientSerial(client);
|
||||
Handle hRequest = SteamWorks_CreateHTTPRequest(k_EHTTPMethodGET, sRequest);
|
||||
if (!hRequest ||
|
||||
!SteamWorks_SetHTTPCallbacks(hRequest, OnClientAuthorized_OnTransferComplete) ||
|
||||
!SteamWorks_SetHTTPRequestContextValue(hRequest, iSerial) ||
|
||||
!SteamWorks_SendHTTPRequest(hRequest))
|
||||
{
|
||||
delete hRequest;
|
||||
}
|
||||
|
||||
char sPath[PLATFORM_MAX_PATH];
|
||||
BuildPath(Path_SM, sPath, sizeof(sPath), "data/unloze_forum_%d.tmp", iSerial);
|
||||
|
||||
HTTPRequest hRequest = new HTTPRequest(sRequest);
|
||||
hRequest.DownloadFile(sPath, OnClientAuthorized_OnDownloaded, iSerial);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Purpose: ripexts HTTPResponse only exposes the body as decoded JSON, unlike
|
||||
// SteamWorks' GetHTTPResponseBodyCallback which handed back a plain char[].
|
||||
// Rather than change private_api.php's response format, we use
|
||||
// HTTPRequest.DownloadFile() to write the raw "GROUP\r\nNAME" body to a temp
|
||||
// file and parse it back exactly like the original SteamWorks version did.
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public int OnClientAuthorized_OnTransferComplete(Handle hRequest, bool bFailure, bool bSuccessful, EHTTPStatusCode eStatusCode, int iSerial)
|
||||
public void OnClientAuthorized_OnDownloaded(HTTPStatus status, any iSerial)
|
||||
{
|
||||
char sPath[PLATFORM_MAX_PATH];
|
||||
BuildPath(Path_SM, sPath, sizeof(sPath), "data/unloze_forum_%d.tmp", iSerial);
|
||||
|
||||
int client = GetClientFromSerial(iSerial);
|
||||
|
||||
if (!client) //Player disconnected.
|
||||
{
|
||||
delete hRequest;
|
||||
return 0;
|
||||
DeleteFile(sPath);
|
||||
return;
|
||||
}
|
||||
|
||||
if (bFailure || !bSuccessful || eStatusCode != k_EHTTPStatusCode200OK)
|
||||
if (status != HTTPStatus_OK)
|
||||
{
|
||||
DeleteFile(sPath);
|
||||
G_bResponseFailed[client] = true;
|
||||
|
||||
if (G_bPreAdminChecked[client])
|
||||
NotifyPostAdminCheck(client);
|
||||
|
||||
delete hRequest;
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
|
||||
SteamWorks_GetHTTPResponseBodyCallback(hRequest, OnClientAuthorized_OnTransferResponse, iSerial);
|
||||
return 0;
|
||||
}
|
||||
char sGroup[64], sName[32];
|
||||
sGroup[0] = '\0';
|
||||
sName[0] = '\0';
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public int OnClientAuthorized_OnTransferResponse(char[] sData, int iSerial)
|
||||
{
|
||||
char splitData[2][32];
|
||||
|
||||
int client = GetClientFromSerial(iSerial);
|
||||
|
||||
if (!client) //Player disconnected.
|
||||
return 0;
|
||||
|
||||
TrimString(sData);
|
||||
StripQuotes(sData);
|
||||
|
||||
LogMessage("reached sData with status 200: %s", sData);
|
||||
ExplodeString(sData, "\r\n", splitData, 2, sizeof(splitData[]));
|
||||
|
||||
if(strlen(splitData[1]) > 0)
|
||||
strcopy(G_sName[client], sizeof(G_sName[]), splitData[1]);
|
||||
|
||||
if(!StrEqual(splitData[0], "NOGROUP"))
|
||||
File hFile = OpenFile(sPath, "r");
|
||||
if (hFile != null)
|
||||
{
|
||||
strcopy(G_sGroup[client], sizeof(G_sGroup[]), splitData[0]);
|
||||
hFile.ReadLine(sGroup, sizeof(sGroup));
|
||||
hFile.ReadLine(sName, sizeof(sName));
|
||||
delete hFile;
|
||||
}
|
||||
DeleteFile(sPath);
|
||||
|
||||
TrimString(sGroup);
|
||||
StripQuotes(sGroup);
|
||||
TrimString(sName);
|
||||
StripQuotes(sName);
|
||||
|
||||
LogMessage("reached sData with status 200: %s", sGroup);
|
||||
|
||||
if (strlen(sName) > 0)
|
||||
strcopy(G_sName[client], sizeof(G_sName[]), sName);
|
||||
|
||||
if (!StrEqual(sGroup, "NOGROUP"))
|
||||
{
|
||||
strcopy(G_sGroup[client], sizeof(G_sGroup[]), sGroup);
|
||||
|
||||
G_bResponsePassed[client] = true;
|
||||
|
||||
@@ -201,7 +204,6 @@ public int OnClientAuthorized_OnTransferResponse(char[] sData, int iSerial)
|
||||
}
|
||||
else
|
||||
G_bResponseFailed[client] = true; //users with just a forum name did not pass the VIP check! so the response "failed" (but we store their forum name for later!)
|
||||
return 0;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
@@ -295,40 +297,39 @@ public int Native_AsyncHasSteamIDReservedSlot(Handle plugin, int numParams)
|
||||
hDataPack.WriteCell(plugin);
|
||||
hDataPack.WriteCell(data);
|
||||
|
||||
Handle hRequest = SteamWorks_CreateHTTPRequest(k_EHTTPMethodGET, sRequest);
|
||||
if (!hRequest ||
|
||||
!SteamWorks_SetHTTPCallbacks(hRequest, Native_AsyncHasSteamIDReservedSlot_OnTransferComplete) ||
|
||||
!SteamWorks_SetHTTPRequestContextValue(hRequest, hDataPack) ||
|
||||
!SteamWorks_SendHTTPRequest(hRequest))
|
||||
{
|
||||
delete hRequest;
|
||||
}
|
||||
// The DataPack handle value is unique among currently-open handles, so it
|
||||
// doubles as a collision-free key for the temp file this specific
|
||||
// in-flight request will use.
|
||||
char sPath[PLATFORM_MAX_PATH];
|
||||
BuildPath(Path_SM, sPath, sizeof(sPath), "data/unloze_reslot_%d.tmp", view_as<int>(hDataPack));
|
||||
|
||||
HTTPRequest hRequest = new HTTPRequest(sRequest);
|
||||
hRequest.DownloadFile(sPath, Native_AsyncHasSteamIDReservedSlot_OnDownloaded, hDataPack);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Purpose: same DownloadFile-to-tempfile approach as OnClientAuthorized_OnDownloaded
|
||||
// above - keeps private_api.php's raw "GROUP\r\nNAME" text response untouched.
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public int Native_AsyncHasSteamIDReservedSlot_OnTransferComplete(Handle hRequest, bool bFailure, bool bSuccessful, EHTTPStatusCode eStatusCode, DataPack hDataPack)
|
||||
public void Native_AsyncHasSteamIDReservedSlot_OnDownloaded(HTTPStatus status, DataPack hDataPack)
|
||||
{
|
||||
if (bFailure || !bSuccessful || eStatusCode != k_EHTTPStatusCode200OK)
|
||||
char sPath[PLATFORM_MAX_PATH];
|
||||
BuildPath(Path_SM, sPath, sizeof(sPath), "data/unloze_reslot_%d.tmp", view_as<int>(hDataPack));
|
||||
|
||||
char sData[32] = "NOGROUP";
|
||||
|
||||
if (status == HTTPStatus_OK)
|
||||
{
|
||||
char sData[32] = "NOGROUP";
|
||||
Native_AsyncHasSteamIDReservedSlot_OnTransferResponse(sData, hDataPack);
|
||||
|
||||
delete hRequest;
|
||||
return 0;
|
||||
File hFile = OpenFile(sPath, "r");
|
||||
if (hFile != null)
|
||||
{
|
||||
hFile.ReadLine(sData, sizeof(sData));
|
||||
delete hFile;
|
||||
}
|
||||
}
|
||||
DeleteFile(sPath);
|
||||
|
||||
SteamWorks_GetHTTPResponseBodyCallback(hRequest, Native_AsyncHasSteamIDReservedSlot_OnTransferResponse, hDataPack);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public int Native_AsyncHasSteamIDReservedSlot_OnTransferResponse(char[] sData, DataPack hDataPack)
|
||||
{
|
||||
hDataPack.Reset();
|
||||
|
||||
char sSteamID32[32];
|
||||
@@ -343,7 +344,7 @@ public int Native_AsyncHasSteamIDReservedSlot_OnTransferResponse(char[] sData, D
|
||||
any data;
|
||||
data = hDataPack.ReadCell();
|
||||
|
||||
static char splitData[32];
|
||||
char splitData[32];
|
||||
|
||||
TrimString(sData);
|
||||
StripQuotes(sData);
|
||||
@@ -363,7 +364,6 @@ public int Native_AsyncHasSteamIDReservedSlot_OnTransferResponse(char[] sData, D
|
||||
Call_Finish();
|
||||
|
||||
delete hDataPack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
#include <sdktools>
|
||||
#include <multicolors>
|
||||
#include <zombiereloaded>
|
||||
#include <loghelper>
|
||||
|
||||
#pragma semicolon 1
|
||||
#pragma newdecls required
|
||||
|
||||
/* CONVARS */
|
||||
ConVar g_hCVar_Delay;
|
||||
ConVar g_hCVar_KnifemadnessEnabled;
|
||||
|
||||
/* BOOLS */
|
||||
bool g_bClientKnifed[MAXPLAYERS+1];
|
||||
@@ -32,6 +32,9 @@ public Plugin myinfo =
|
||||
public void OnPluginStart()
|
||||
{
|
||||
g_hCVar_Delay = CreateConVar("sm_knife_madness_kill_delay", "3", "Delay before ZMs die after being knifed by a human.", 0, true, 0.0);
|
||||
g_hCVar_KnifemadnessEnabled = CreateConVar("sm_knifemadness_enabled", "0", "", FCVAR_NONE, true, 0.0, true, 1.0);
|
||||
g_hCVar_KnifemadnessEnabled.AddChangeHook(OnConVarChanged);
|
||||
AutoExecConfig(true);
|
||||
|
||||
HookEvent("player_spawn", OnClientSpawn, EventHookMode_Post);
|
||||
HookEvent("player_death", OnClientDeath, EventHookMode_Pre);
|
||||
@@ -39,11 +42,23 @@ public void OnPluginStart()
|
||||
HookEvent("player_team", OnClientTeam, EventHookMode_Pre);
|
||||
}
|
||||
|
||||
public void OnConVarChanged(ConVar convar, const char[] oldValue, const char[] newValue)
|
||||
{
|
||||
if(g_hCVar_KnifemadnessEnabled.BoolValue)
|
||||
CPrintToChatAll("{cyan}[Knifemadness] {white}has been allowed.");
|
||||
else
|
||||
CPrintToChatAll("{cyan}[Knifemadness] {white}has been disabled.");
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public void OnClientSpawn(Event hEvent, const char[] sEvent, bool bDontBroadcast)
|
||||
{
|
||||
if(!g_hCVar_KnifemadnessEnabled.BoolValue)
|
||||
{
|
||||
return;
|
||||
}
|
||||
int client = GetClientOfUserId(hEvent.GetInt("userid"));
|
||||
|
||||
g_bClientKnifed[client] = false;
|
||||
@@ -58,6 +73,10 @@ public void OnClientSpawn(Event hEvent, const char[] sEvent, bool bDontBroadcast
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public Action OnClientDeath(Event hEvent, const char[] sEvent, bool bDontBroadcast)
|
||||
{
|
||||
if(!g_hCVar_KnifemadnessEnabled.BoolValue)
|
||||
{
|
||||
return Plugin_Continue;
|
||||
}
|
||||
int client = GetClientOfUserId(hEvent.GetInt("userid"));
|
||||
|
||||
if (g_bSupressDeath[client])
|
||||
@@ -74,6 +93,10 @@ public Action OnClientDeath(Event hEvent, const char[] sEvent, bool bDontBroadca
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public void OnClientHurt(Event hEvent, const char[] sEvent, bool bDontBroadcast)
|
||||
{
|
||||
if(!g_hCVar_KnifemadnessEnabled.BoolValue)
|
||||
{
|
||||
return;
|
||||
}
|
||||
int attacker = GetClientOfUserId(hEvent.GetInt("attacker"));
|
||||
int victim = GetClientOfUserId(hEvent.GetInt("userid"));
|
||||
|
||||
@@ -107,12 +130,16 @@ public void OnClientHurt(Event hEvent, const char[] sEvent, bool bDontBroadcast)
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public void OnClientTeam(Event hEvent, const char[] sEvent, bool bDontBroadcast)
|
||||
{
|
||||
if(!g_hCVar_KnifemadnessEnabled.BoolValue)
|
||||
{
|
||||
return;
|
||||
}
|
||||
int client = GetClientOfUserId(hEvent.GetInt("userid"));
|
||||
|
||||
if (g_bClientKnifed[client])
|
||||
{
|
||||
g_bClientKnifed[client] = false;
|
||||
LogPlayerEvent(client, "triggered", "switch_to_spec");
|
||||
//LogPlayerEvent(client, "triggered", "switch_to_spec");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,6 +148,10 @@ public void OnClientTeam(Event hEvent, const char[] sEvent, bool bDontBroadcast)
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public void ZR_OnClientInfected(int client, int attacker, bool motherInfect, bool respawnOverride, bool respawn)
|
||||
{
|
||||
if(!g_hCVar_KnifemadnessEnabled.BoolValue)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!IsValidClient(attacker))
|
||||
return;
|
||||
|
||||
@@ -137,6 +168,10 @@ public void ZR_OnClientInfected(int client, int attacker, bool motherInfect, boo
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public Action KillZM(Handle timer, DataPack pack)
|
||||
{
|
||||
if(!g_hCVar_KnifemadnessEnabled.BoolValue)
|
||||
{
|
||||
return Plugin_Continue;
|
||||
}
|
||||
int attacker = 0;
|
||||
int client = 0;
|
||||
|
||||
@@ -145,21 +180,21 @@ public Action KillZM(Handle timer, DataPack pack)
|
||||
client = GetClientOfUserId(pack.ReadCell());
|
||||
|
||||
if (client == 0)
|
||||
return;
|
||||
return Plugin_Continue;
|
||||
|
||||
if (!(IsValidClient(client, false) && IsPlayerAlive(client) && ZR_IsClientZombie(client) && g_bClientKnifed[client]))
|
||||
return;
|
||||
return Plugin_Continue;
|
||||
|
||||
g_bSupressDeath[client] = true;
|
||||
ForcePlayerSuicide(client);
|
||||
g_bClientKnifed[client] = false;
|
||||
|
||||
if (!(IsValidClient(attacker, false)))
|
||||
return;
|
||||
return Plugin_Continue;
|
||||
|
||||
Event hEvent = CreateEvent("player_death");
|
||||
if (hEvent == null)
|
||||
return;
|
||||
return Plugin_Continue;
|
||||
|
||||
hEvent.SetInt("userid", GetClientUserId(client));
|
||||
hEvent.SetInt("attacker", GetClientUserId(attacker));
|
||||
@@ -168,6 +203,7 @@ public Action KillZM(Handle timer, DataPack pack)
|
||||
hEvent.SetInt("dominated", 0);
|
||||
hEvent.SetInt("revenge", 0);
|
||||
hEvent.Fire();
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
@@ -190,6 +226,10 @@ public void QueryClient(int client)
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public void OnConVarQueryFinished(QueryCookie cookie, int client, ConVarQueryResult result, const char[] cvarName, const char[] cvarValue, int iSerial)
|
||||
{
|
||||
if(!g_hCVar_KnifemadnessEnabled.BoolValue)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (GetClientFromSerial(iSerial) != client)
|
||||
return;
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#include <sourcemod>
|
||||
#include <SteamWorks>
|
||||
#include <ripext>
|
||||
#include <multicolors>
|
||||
#include <json>
|
||||
|
||||
#undef REQUIRE_PLUGIN
|
||||
#tryinclude <PlayerManager>
|
||||
@@ -103,9 +102,19 @@ public void OnClientAuthorized(int client, const char[] auth)
|
||||
char sIP[32];
|
||||
GetClientIP(client, sIP, sizeof(sIP));
|
||||
|
||||
char allowed_ips[128];
|
||||
ConVar sv_set_steam_id_ips = FindConVar("sv_set_steam_id_ips");
|
||||
if (sv_set_steam_id_ips != null)
|
||||
{
|
||||
sv_set_steam_id_ips.GetString(allowed_ips, sizeof(allowed_ips));
|
||||
if (StrEqual(sIP, allowed_ips))
|
||||
{
|
||||
return; //its the webclient, the internal docker IP obviously cannot be found by VPN checker.
|
||||
}
|
||||
}
|
||||
|
||||
char sQuery[512];
|
||||
Format(sQuery, sizeof(sQuery), "SELECT * FROM ip_table WHERE ip='%s'", sIP);
|
||||
|
||||
g_hDatabase.Query(SQL_OnQueryCompleted, sQuery, GetClientSerial(client), DBPrio_Low);
|
||||
}
|
||||
|
||||
@@ -151,72 +160,63 @@ public void SQL_OnQueryCompleted(Database db, DBResultSet results, const char[]
|
||||
GetClientIP(client, sIP, sizeof(sIP));
|
||||
|
||||
char sRequest[256];
|
||||
FormatEx(sRequest, sizeof(sRequest), "use a url here", sIP, APIKEY);
|
||||
FormatEx(sRequest, sizeof(sRequest), "use a url here", sIP, APIKEY);
|
||||
//PrintToConsoleAll(sRequest);
|
||||
|
||||
Handle hRequest = SteamWorks_CreateHTTPRequest(k_EHTTPMethodGET, sRequest);
|
||||
if (!hRequest ||
|
||||
!SteamWorks_SetHTTPCallbacks(hRequest, OnTransferComplete) ||
|
||||
!SteamWorks_SetHTTPRequestContextValue(hRequest, iSerial) ||
|
||||
!SteamWorks_SendHTTPRequest(hRequest))
|
||||
{
|
||||
delete hRequest;
|
||||
}
|
||||
HTTPRequest hRequest = new HTTPRequest(sRequest);
|
||||
hRequest.Get(OnTransferComplete, iSerial);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public int OnTransferComplete(Handle hRequest, bool bFailure, bool bSuccessful, EHTTPStatusCode eStatusCode, int iSerial)
|
||||
public void OnTransferComplete(HTTPResponse response, any iSerial)
|
||||
{
|
||||
int client = GetClientFromSerial(iSerial);
|
||||
if (!client) //Player disconnected.
|
||||
{
|
||||
delete hRequest;
|
||||
return 0;
|
||||
}
|
||||
return;
|
||||
|
||||
if (bFailure || !bSuccessful || eStatusCode != k_EHTTPStatusCode200OK)
|
||||
if (response.Status != HTTPStatus_OK || response.Data == null)
|
||||
{
|
||||
delete hRequest;
|
||||
g_bStatus[client] = STATUS_ERROR;
|
||||
LogError("Request-Error: %d", eStatusCode);
|
||||
return 0;
|
||||
LogError("Request-Error: %d", response.Status);
|
||||
return;
|
||||
}
|
||||
|
||||
SteamWorks_GetHTTPResponseBodyCallback(hRequest, OnTransferResponse, iSerial);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public int OnTransferResponse(char[] sData, int iSerial)
|
||||
{
|
||||
int client = GetClientFromSerial(iSerial);
|
||||
if (!client) //Player disconnected.
|
||||
return 0;
|
||||
|
||||
char sIP[32];
|
||||
GetClientIP(client, sIP, sizeof(sIP));
|
||||
|
||||
JSON_Object obj = json_decode(sData);
|
||||
// ripext already decodes the JSON body for us as a native JSONObject; no
|
||||
JSONObject obj = view_as<JSONObject>(response.Data);
|
||||
|
||||
char sStatus[32];
|
||||
obj.GetString("status", sStatus, sizeof(sStatus));
|
||||
if (!obj.GetString("status", sStatus, sizeof(sStatus)))
|
||||
sStatus[0] = '\0';
|
||||
|
||||
if (!StrEqual(sStatus, "ok") && !StrEqual(sStatus, "warning"))
|
||||
{
|
||||
char sMessage[256];
|
||||
obj.GetString("message", sMessage, sizeof(sMessage));
|
||||
if (!obj.GetString("message", sMessage, sizeof(sMessage)))
|
||||
sMessage[0] = '\0';
|
||||
LogError("API-Response: %s: %s", sStatus, sMessage);
|
||||
g_bStatus[client] = STATUS_ERROR;
|
||||
json_cleanup_and_delete(obj);
|
||||
return 0;
|
||||
delete obj;
|
||||
return;
|
||||
}
|
||||
|
||||
JSON_Object ipobj = obj.GetObject(sIP);
|
||||
if (!obj.HasKey(sIP))
|
||||
{
|
||||
LogError("API-Response: no entry for IP %s", sIP);
|
||||
g_bStatus[client] = STATUS_ERROR;
|
||||
delete obj;
|
||||
return;
|
||||
}
|
||||
|
||||
JSONObject ipobj = view_as<JSONObject>(obj.Get(sIP));
|
||||
char sProxy[16];
|
||||
ipobj.GetString("proxy", sProxy, sizeof(sProxy));
|
||||
if (!ipobj.GetString("proxy", sProxy, sizeof(sProxy)))
|
||||
sProxy[0] = '\0';
|
||||
|
||||
if (StrEqual(sProxy, "no"))
|
||||
g_bStatus[client] = STATUS_SAFE;
|
||||
else
|
||||
@@ -238,10 +238,8 @@ public int OnTransferResponse(char[] sData, int iSerial)
|
||||
Format(sQuery, sizeof(sQuery), "INSERT INTO ip_table (ip, type, last_check) VALUES ('%s', '%d', '%d') ON DUPLICATE KEY UPDATE type='%d', last_check='%d';", sIP, g_bStatus[client], iCurrentTime, g_bStatus[client], iCurrentTime);
|
||||
g_hDatabase.Query(SQL_OnQueryCompleted, sQuery, _, DBPrio_Low);
|
||||
|
||||
//https://github.com/clugg/sm-json/blob/master/addons/sourcemod/scripting/json_test.sp#L446
|
||||
//as far as i can tell i just need to call json_cleanup_and_delete() on the most outer json object, all its children should get cleaned and deleted as well.
|
||||
json_cleanup_and_delete(obj);
|
||||
return 0;
|
||||
delete ipobj;
|
||||
delete obj;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
@@ -405,3 +403,4 @@ public void OnLibraryRemoved(const char[] sName)
|
||||
if (strcmp(sName, "PlayerManager", false) == 0)
|
||||
g_bPMLoaded = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -87,16 +87,21 @@ public void OnConVarChanged(ConVar convar, const char[] oldValue, const char[] n
|
||||
|
||||
public void OnMapStart()
|
||||
{
|
||||
if(g_hTimer != INVALID_HANDLE && CloseHandle(g_hTimer))
|
||||
if(g_hTimer != INVALID_HANDLE)
|
||||
{
|
||||
CloseHandle(g_hTimer);
|
||||
g_hTimer = INVALID_HANDLE;
|
||||
|
||||
}
|
||||
g_hTimer = CreateTimer(TIMER_INTERVAL, Timer_CleanupWeapons, INVALID_HANDLE, TIMER_REPEAT);
|
||||
}
|
||||
|
||||
public void OnMapEnd()
|
||||
{
|
||||
if(g_hTimer != INVALID_HANDLE && CloseHandle(g_hTimer))
|
||||
if(g_hTimer != INVALID_HANDLE)
|
||||
{
|
||||
CloseHandle(g_hTimer);
|
||||
g_hTimer = INVALID_HANDLE;
|
||||
}
|
||||
}
|
||||
|
||||
public void OnClientPutInServer(int client)
|
||||
|
||||
@@ -0,0 +1,458 @@
|
||||
#include <clientprefs>
|
||||
#include <multicolors>
|
||||
#include <sdkhooks>
|
||||
#include <sourcemod>
|
||||
#include <zombiereloaded>
|
||||
#include <unloze_playtime>
|
||||
#include <TransmitManager>
|
||||
#include <leader>
|
||||
#include <entWatch_core>
|
||||
|
||||
/* BOOLS */
|
||||
bool g_bHasSomebodyToHide[MAXPLAYERS + 1];
|
||||
bool g_bHasTobeVisible[MAXPLAYERS + 1];
|
||||
bool g_bHidePlayers[MAXPLAYERS + 1][MAXPLAYERS + 1];
|
||||
|
||||
/* INTEGERS */
|
||||
int g_iHideRange[MAXPLAYERS+1];
|
||||
int g_iLeader = 0;
|
||||
int g_iTierHide = 0;
|
||||
|
||||
/* HANDLES */
|
||||
Handle g_hTimer;
|
||||
|
||||
/* CONVARS */
|
||||
ConVar g_hCVar_HideEnabled;
|
||||
|
||||
/* COOKIES */
|
||||
Handle g_hCookie_HideRange;
|
||||
|
||||
#define DISABLED -1
|
||||
#define SHORT_RANGE 120
|
||||
#define MEDIUM_RANGE 250
|
||||
#define LONG_RANGE 500
|
||||
|
||||
int SHORT = SHORT_RANGE * SHORT_RANGE;
|
||||
int MEDIUM = MEDIUM_RANGE * MEDIUM_RANGE;
|
||||
int LONG = LONG_RANGE * LONG_RANGE;
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public Plugin myinfo =
|
||||
{
|
||||
name = "Hide Teammates with Transmit extension",
|
||||
author = "jenz, a bunch of re-used code from Neon. credits to pan for fixing it. and thx to zacade",
|
||||
description = "A plugin that can !hide teammates with individual distances",
|
||||
version = "1.0.0",
|
||||
url = "www.unloze.com"
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public void OnPluginStart()
|
||||
{
|
||||
g_hCVar_HideEnabled = CreateConVar("sm_hide_enabled", "1", "", FCVAR_NONE, true, 0.0, true, 1.0);
|
||||
g_hCVar_HideEnabled.AddChangeHook(OnConVarChanged);
|
||||
AutoExecConfig(true);
|
||||
|
||||
g_hCookie_HideRange = RegClientCookie("hide_range_transmitext", "", CookieAccess_Private);
|
||||
|
||||
RegConsoleCmd("sm_hide", OnHideSettings, "Hiding near humans");
|
||||
|
||||
for(int client = 1; client <= MaxClients; client++)
|
||||
{
|
||||
if(IsValidClient(client))
|
||||
{
|
||||
OnClientPutInServer(client);
|
||||
|
||||
if(AreClientCookiesCached(client))
|
||||
OnClientCookiesCached(client);
|
||||
}
|
||||
|
||||
}
|
||||
SetCookieMenuItem(MenuHandler_CookieMenu, 0, "Hide");
|
||||
g_hTimer = CreateTimer(1.0, UpdateHide, INVALID_HANDLE, TIMER_REPEAT);
|
||||
g_iTierHide = FindTierForCommandOverride("sm_hide");
|
||||
}
|
||||
|
||||
public void OnPluginEnd()
|
||||
{
|
||||
if (g_hTimer != INVALID_HANDLE)
|
||||
delete g_hTimer;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public void OnConVarChanged(ConVar convar, const char[] oldValue, const char[] newValue)
|
||||
{
|
||||
for(int client = 1; client <= MaxClients; client++)
|
||||
{
|
||||
g_bHasSomebodyToHide[client] = false;
|
||||
for(int target = 1; target <= MaxClients; target++)
|
||||
{
|
||||
g_bHidePlayers[client][target] = false;
|
||||
}
|
||||
}
|
||||
|
||||
if(g_hCVar_HideEnabled.BoolValue)
|
||||
CPrintToChatAll("{cyan}[Hide] {white}has been allowed.");
|
||||
else
|
||||
CPrintToChatAll("{cyan}[Hide] {white}has been disabled.");
|
||||
}
|
||||
|
||||
public Action UpdateHide(Handle timer)
|
||||
{
|
||||
if (!g_hCVar_HideEnabled.BoolValue)
|
||||
return Plugin_Continue;
|
||||
|
||||
g_iLeader = Leader_CurrentLeader();
|
||||
|
||||
for (int client = 1; client <= MaxClients; client++)
|
||||
{
|
||||
if (!IsValidClient(client) || IsClientSourceTV(client) || IsFakeClient(client))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (g_iHideRange[client] == DISABLED) //client just disabled hide right now. the bool will pass once until they enabled hide again.
|
||||
{
|
||||
if (g_bHasSomebodyToHide[client] && TransmitManager_IsEntityHooked(client))
|
||||
{
|
||||
for (int target = 1; target <= MaxClients; target++)
|
||||
{
|
||||
if (IsValidClient(target) && client != target && TransmitManager_IsEntityHooked(target))
|
||||
{
|
||||
TransmitManager_SetEntityState(target, client, true); //client can see all players again
|
||||
g_bHidePlayers[client][target] = false;
|
||||
}
|
||||
}
|
||||
g_bHasSomebodyToHide[client] = false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
//lazy fix for invisible zm
|
||||
if (TransmitManager_IsEntityHooked(client))
|
||||
{
|
||||
for (int target = 1; target <= MaxClients; target++)
|
||||
{
|
||||
if (IsValidClient(target) && client != target && g_bHidePlayers[client][target]
|
||||
&& IsPlayerAlive(target) && ZR_IsClientZombie(target) && TransmitManager_IsEntityHooked(target))
|
||||
{
|
||||
g_bHidePlayers[client][target] = false;
|
||||
TransmitManager_SetEntityState(target, client, true); //client can see player again because the player is a zm
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ClientIsAlive = IsPlayerAlive(client);
|
||||
bool ClientIsHuman = false;
|
||||
if (ClientIsAlive)
|
||||
{
|
||||
ClientIsHuman = ZR_IsClientHuman(client);
|
||||
}
|
||||
bool ClientHasItem = EW_ClientHasItem(client);
|
||||
|
||||
//player is leader, has an item, is not alive or is not human, set the transmit state once via the bool so all can see the player.
|
||||
if (g_iLeader == client || ClientHasItem || !ClientIsAlive || !ClientIsHuman)
|
||||
{
|
||||
if (!g_bHasTobeVisible[client] && TransmitManager_IsEntityHooked(client))
|
||||
{
|
||||
for (int target = 1; target <= MaxClients; target++)
|
||||
{
|
||||
if (IsValidClient(target) && client != target && TransmitManager_IsEntityHooked(target))
|
||||
{
|
||||
//all must see the leader, the zombie and the item user.
|
||||
TransmitManager_SetEntityState(client, target, true); //all can see the player again
|
||||
|
||||
//if the player is dead or not human they must see all players again
|
||||
if (!ClientIsAlive || !ClientIsHuman)
|
||||
{
|
||||
TransmitManager_SetEntityState(target, client, true); //client can see all players again
|
||||
}
|
||||
}
|
||||
}
|
||||
g_bHasTobeVisible[client] = true;
|
||||
}
|
||||
//but the human leader and human item users may still want to hide the remaining human players.
|
||||
if (ClientIsAlive && ClientIsHuman && (g_iLeader == client || ClientHasItem))
|
||||
{
|
||||
DoTheTargetCheck(client);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
g_bHasTobeVisible[client] = false;
|
||||
|
||||
//all the remaining checks for clients with hide enabled that are alive humans and not leaders or item users.
|
||||
DoTheTargetCheck(client);
|
||||
}
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
public void DoTheTargetCheck(int client)
|
||||
{
|
||||
float fOriginClient[3];
|
||||
float fOriginTarget[3];
|
||||
GetClientAbsOrigin(client, fOriginClient);
|
||||
g_bHasSomebodyToHide[client] = false;
|
||||
|
||||
for (int target = 1; target <= MaxClients; target++)
|
||||
{
|
||||
if (!IsValidClient(target) || IsClientSourceTV(target))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
//the target is not the leader, has no item, is alive and is a human. so consider him for the distance check.
|
||||
if (g_iLeader != target && !EW_ClientHasItem(target) && IsPlayerAlive(target) && ZR_IsClientHuman(target) && target != client)
|
||||
{
|
||||
GetClientAbsOrigin(target, fOriginTarget);
|
||||
if (GetVectorDistance(fOriginTarget, fOriginClient, true) <= float(g_iHideRange[client]))
|
||||
{
|
||||
//we only have to check the hook when the state of the hidePlayers boolean changed
|
||||
if (!g_bHidePlayers[client][target] && TransmitManager_IsEntityHooked(client) && TransmitManager_IsEntityHooked(target))
|
||||
{
|
||||
//the client here cannot be a fakeclient. thats already checked.
|
||||
TransmitManager_SetEntityState(target, client, false); //the player is hiding the teammate due to distance
|
||||
g_bHidePlayers[client][target] = true;
|
||||
//LogMessage("client: %N is %s. target: %N is %s", client, ZR_IsClientHuman(client) ? "Human" : "Zombie", target, ZR_IsClientHuman(target) ? "Human" : "Zombie");
|
||||
}
|
||||
g_bHasSomebodyToHide[client] = true; //theres at least one target to hide for the player, the teammate
|
||||
}
|
||||
else
|
||||
{
|
||||
//we only have to check the hook when the state of the hidePlayers boolean changed
|
||||
if (g_bHidePlayers[client][target] && TransmitManager_IsEntityHooked(client) && TransmitManager_IsEntityHooked(target))
|
||||
{
|
||||
TransmitManager_SetEntityState(target, client, true); //the player is seeing the teammate due to distance
|
||||
g_bHidePlayers[client][target] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Action OnHideSettings(int client, int args)
|
||||
{
|
||||
ShowSettingsMenu(client);
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public void ShowSettingsMenu(int client)
|
||||
{
|
||||
Menu menu = new Menu(MenuHandler_MainMenu);
|
||||
|
||||
char titlemsg[256];
|
||||
Format(titlemsg, sizeof(titlemsg), "Hide");
|
||||
menu.SetTitle(titlemsg, client);
|
||||
|
||||
char sBuffer[128];
|
||||
|
||||
Format(sBuffer, sizeof(sBuffer), "Disabled%s", (g_iHideRange[client] == DISABLED) ? " [Selected]" : "");
|
||||
menu.AddItem("", sBuffer, (g_iHideRange[client] == DISABLED));
|
||||
|
||||
Format(sBuffer, sizeof(sBuffer), "Short Range%s", (g_iHideRange[client] == SHORT) ? " [Selected]" : "");
|
||||
menu.AddItem("", sBuffer, (g_iHideRange[client] == SHORT));
|
||||
|
||||
Format(sBuffer, sizeof(sBuffer), "Medium Range%s", (g_iHideRange[client] == MEDIUM) ? " [Selected]" : "");
|
||||
menu.AddItem("", sBuffer, (g_iHideRange[client] == MEDIUM));
|
||||
|
||||
Format(sBuffer, sizeof(sBuffer), "Long Range%s", (g_iHideRange[client] == LONG) ? " [Selected]" : "");
|
||||
menu.AddItem("", sBuffer, (g_iHideRange[client] == LONG));
|
||||
|
||||
if(!g_hCVar_HideEnabled.BoolValue)
|
||||
menu.AddItem("", "Warning: Hide is currently disabled on the server", ITEMDRAW_DISABLED);
|
||||
|
||||
menu.ExitBackButton = true;
|
||||
|
||||
menu.Display(client, MENU_TIME_FOREVER);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public int MenuHandler_MainMenu(Menu menu, MenuAction action, int client, int selection)
|
||||
{
|
||||
switch(action)
|
||||
{
|
||||
case(MenuAction_Select):
|
||||
{
|
||||
switch(selection)
|
||||
{
|
||||
case(0):
|
||||
{
|
||||
g_iHideRange[client] = DISABLED
|
||||
}
|
||||
case(1):
|
||||
{
|
||||
g_iHideRange[client] = SHORT
|
||||
}
|
||||
case(2):
|
||||
{
|
||||
g_iHideRange[client] = MEDIUM
|
||||
}
|
||||
case(3):
|
||||
{
|
||||
g_iHideRange[client] = LONG
|
||||
}
|
||||
}
|
||||
char sBuffer[16];
|
||||
Format(sBuffer, sizeof(sBuffer), "%d", g_iHideRange[client]);
|
||||
SetClientCookie(client, g_hCookie_HideRange, sBuffer);
|
||||
ShowSettingsMenu(client);
|
||||
}
|
||||
case(MenuAction_Cancel):
|
||||
{
|
||||
ShowCookieMenu(client);
|
||||
}
|
||||
case(MenuAction_End):
|
||||
{
|
||||
delete menu;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
stock int FindTierForCommandOverride(const char[] command)
|
||||
{
|
||||
char sPath[PLATFORM_MAX_PATH];
|
||||
BuildPath(Path_SM, sPath, sizeof(sPath), "configs/admin_groups.cfg");
|
||||
|
||||
KeyValues kv = new KeyValues("Groups");
|
||||
if (!kv.ImportFromFile(sPath))
|
||||
{
|
||||
delete kv;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// collect tier group names first
|
||||
char groupNames[32][64];
|
||||
int groupCount = 0;
|
||||
|
||||
if (kv.GotoFirstSubKey())
|
||||
{
|
||||
do
|
||||
{
|
||||
char sName[64];
|
||||
kv.GetSectionName(sName, sizeof(sName));
|
||||
if (StrContains(sName, "tier", false) == 0)
|
||||
{
|
||||
strcopy(groupNames[groupCount], 64, sName);
|
||||
groupCount++;
|
||||
}
|
||||
}
|
||||
while (kv.GotoNextKey() && groupCount < 32);
|
||||
}
|
||||
|
||||
int foundTier = -1;
|
||||
|
||||
for (int i = 0; i < groupCount; i++)
|
||||
{
|
||||
kv.Rewind();
|
||||
|
||||
if (!kv.JumpToKey(groupNames[i]))
|
||||
continue;
|
||||
|
||||
if (!kv.JumpToKey("Overrides"))
|
||||
continue;
|
||||
|
||||
if (!kv.GotoFirstSubKey(false))
|
||||
continue;
|
||||
|
||||
do
|
||||
{
|
||||
char overrideCmd[64];
|
||||
kv.GetSectionName(overrideCmd, sizeof(overrideCmd));
|
||||
|
||||
if (StrEqual(overrideCmd, command, false))
|
||||
{
|
||||
char tierStr[8];
|
||||
strcopy(tierStr, sizeof(tierStr), groupNames[i][4]); // skip "tier"
|
||||
foundTier = StringToInt(tierStr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (kv.GotoNextKey(false));
|
||||
|
||||
if (foundTier != -1)
|
||||
break;
|
||||
}
|
||||
|
||||
delete kv;
|
||||
return foundTier;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public void MenuHandler_CookieMenu(int client, CookieMenuAction action, any info, char[] buffer, int maxlen)
|
||||
{
|
||||
int client_tier = GetPlayerTier_native(client);
|
||||
if (g_iTierHide > client_tier)
|
||||
{
|
||||
return;
|
||||
}
|
||||
switch(action)
|
||||
{
|
||||
case(CookieMenuAction_DisplayOption):
|
||||
{
|
||||
Format(buffer, maxlen, "Hide", client);
|
||||
}
|
||||
case(CookieMenuAction_SelectOption):
|
||||
{
|
||||
ShowSettingsMenu(client);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void OnClientDisconnect(int client)
|
||||
{
|
||||
g_iHideRange[client] = 0;
|
||||
g_bHasSomebodyToHide[client] = false;
|
||||
g_bHasTobeVisible[client] = false;
|
||||
for(int target = 1; target <= MaxClients; target++)
|
||||
{
|
||||
g_bHidePlayers[client][target] = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void OnClientPutInServer(int client)
|
||||
{
|
||||
if(!g_hCVar_HideEnabled.BoolValue)
|
||||
return;
|
||||
|
||||
g_bHasTobeVisible[client] = false;
|
||||
if (!IsClientSourceTV(client))
|
||||
{
|
||||
TransmitManager_AddEntityHooks(client);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnClientCookiesCached(int client)
|
||||
{
|
||||
char sBuffer[16];
|
||||
GetClientCookie(client, g_hCookie_HideRange, sBuffer, sizeof(sBuffer));
|
||||
|
||||
if (sBuffer[0])
|
||||
{
|
||||
g_iHideRange[client] = StringToInt(sBuffer);
|
||||
if (g_iHideRange[client] != SHORT && g_iHideRange[client] != MEDIUM && g_iHideRange[client] != LONG)
|
||||
g_iHideRange[client] = DISABLED;
|
||||
}
|
||||
else
|
||||
g_iHideRange[client] = DISABLED;
|
||||
}
|
||||
|
||||
|
||||
stock bool IsValidClient(int client)
|
||||
{
|
||||
if (client > 0 && client <= MaxClients && IsClientConnected(client) && IsClientInGame(client))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
@@ -101,7 +101,7 @@ public void OnPluginStart()
|
||||
|
||||
if (SayText2 == INVALID_MESSAGE_ID)
|
||||
{
|
||||
SetFailState("This game doesn't support SayText2 user messages.");
|
||||
SetFailState("This game doesnt support SayText2 user messages.");
|
||||
}
|
||||
|
||||
HookUserMessage(SayText2, Hook_UserMessage, true);
|
||||
@@ -333,11 +333,11 @@ bool ForceColor(int client, char Key[64])
|
||||
SetColor(SID, Key, sCol, -1, true);
|
||||
|
||||
if (!strcmp(Key, "namecolor"))
|
||||
CPrintToChat(client, "{green}[{red}C{green}C{blue}C{green}-ADMIN]{default} Successfully set {green}%N's{default} name color to: \x07%s#%s{default}!", iTarget, sCol[1], sCol[1]);
|
||||
CPrintToChat(client, "{green}[{red}C{green}C{blue}C{green}-ADMIN]{default} Successfully set {green}%Ns{default} name color to: \x07%s#%s{default}!", iTarget, sCol[1], sCol[1]);
|
||||
else if (!strcmp(Key, "tagcolor"))
|
||||
CPrintToChat(client, "{green}[{red}C{green}C{blue}C{green}-ADMIN]{default} Successfully set {green}%N's{default} tag color to: \x07%s#%s{default}!", iTarget, sCol[1], sCol[1]);
|
||||
CPrintToChat(client, "{green}[{red}C{green}C{blue}C{green}-ADMIN]{default} Successfully set {green}%Ns{default} tag color to: \x07%s#%s{default}!", iTarget, sCol[1], sCol[1]);
|
||||
else
|
||||
CPrintToChat(client, "{green}[{red}C{green}C{blue}C{green}-ADMIN]{default} Successfully set {green}%N's{default} text color to: \x07%s#%s{default}!", iTarget, sCol[1], sCol[1]);
|
||||
CPrintToChat(client, "{green}[{red}C{green}C{blue}C{green}-ADMIN]{default} Successfully set {green}%Ns{default} text color to: \x07%s#%s{default}!", iTarget, sCol[1], sCol[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2543,7 +2543,7 @@ public void OnClientPostAdminCheck(int client)
|
||||
Call_Finish();
|
||||
}
|
||||
|
||||
public Action Hook_UserMessage(UserMsg msg_id, Handle bf, const players[], int playersNum, bool reliable, bool init)
|
||||
public Action Hook_UserMessage(UserMsg msg_id, Handle bf, const int[] players, int playersNum, bool reliable, bool init)
|
||||
{
|
||||
char sAuthorTag[64];
|
||||
g_msgAuthor = BfReadByte(bf);
|
||||
@@ -2701,11 +2701,11 @@ public Action Event_PlayerSay(Handle event, const char[] name, bool dontBroadcas
|
||||
{
|
||||
if (g_msgAuthor == -1 || GetClientOfUserId(GetEventInt(event, "userid")) != g_msgAuthor)
|
||||
{
|
||||
return;
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
if (strlen(g_msgText) == 0)
|
||||
return;
|
||||
return Plugin_Handled;
|
||||
|
||||
int[] players = new int[MaxClients + 1];
|
||||
int playersNum = 0;
|
||||
@@ -2718,7 +2718,7 @@ public Action Event_PlayerSay(Handle event, const char[] name, bool dontBroadcas
|
||||
{
|
||||
if (IsClientInGame(client) && GetClientTeam(client) == team)
|
||||
{
|
||||
if(!g_Ignored[client * (MAXPLAYERS + 1) + g_msgAuthor] && MessageForward(client, g_msgAuthor, g_msgText))
|
||||
if(!g_Ignored[client * (MAXPLAYERS + 1) + g_msgAuthor] && MessageForward(client, g_msgAuthor, g_msgText) )
|
||||
players[playersNum++] = client;
|
||||
}
|
||||
}
|
||||
@@ -2729,7 +2729,7 @@ public Action Event_PlayerSay(Handle event, const char[] name, bool dontBroadcas
|
||||
{
|
||||
if (IsClientInGame(client))
|
||||
{
|
||||
if(!g_Ignored[client * (MAXPLAYERS + 1) + g_msgAuthor] && MessageForward(client, g_msgAuthor, g_msgText))
|
||||
if(!g_Ignored[client * (MAXPLAYERS + 1) + g_msgAuthor] && MessageForward(client, g_msgAuthor, g_msgText) )
|
||||
players[playersNum++] = client;
|
||||
}
|
||||
}
|
||||
@@ -2738,7 +2738,7 @@ public Action Event_PlayerSay(Handle event, const char[] name, bool dontBroadcas
|
||||
if (!playersNum)
|
||||
{
|
||||
g_msgAuthor = -1;
|
||||
return;
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
Handle SayText2 = StartMessage("SayText2", players, playersNum, USERMSG_RELIABLE | USERMSG_BLOCKHOOKS);
|
||||
@@ -2759,6 +2759,7 @@ public Action Event_PlayerSay(Handle event, const char[] name, bool dontBroadcas
|
||||
}
|
||||
|
||||
g_msgAuthor = -1;
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
// 888b 888 d8888 88888888888 8888888 888 888 8888888888 .d8888b.
|
||||
|
||||
@@ -29,7 +29,7 @@ public Plugin myinfo =
|
||||
author = "Neon",
|
||||
description = "",
|
||||
version = "1.0.0",
|
||||
url = "https://steamcommunity.com/id/n3ontm"
|
||||
url = "https://steamcommunity.com/id/n3ontm"
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
@@ -40,14 +40,16 @@ public void OnPluginStart()
|
||||
HookEvent("round_start", Event_RoundStart, EventHookMode_Pre);
|
||||
}
|
||||
|
||||
public Action Event_RoundStart(Event event, const char[] name, bool dontBroadcast)
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public void Event_RoundStart(Event event, const char[] name, bool dontBroadcast)
|
||||
{
|
||||
for (int i = 0; i < sizeof(g_bDropped); i++)
|
||||
{
|
||||
g_bDropped[i] = false;
|
||||
g_bActive[i] = false;
|
||||
}
|
||||
return Plugin_Continue;
|
||||
for (int i = 0; i < sizeof(g_bDropped); i++)
|
||||
{
|
||||
g_bDropped[i] = false;
|
||||
g_bActive[i] = false;
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
@@ -142,7 +144,7 @@ public Action Timer_Beacon(Handle timer, any index)
|
||||
return Plugin_Stop;
|
||||
}
|
||||
|
||||
CConfig cfg = item.dConfig ;
|
||||
CConfig cfg = item.dConfig;
|
||||
|
||||
if (cfg.iMode == 2 && item.iTimesUsed >= cfg.iMaxUses)
|
||||
{
|
||||
@@ -151,7 +153,7 @@ public Action Timer_Beacon(Handle timer, any index)
|
||||
}
|
||||
|
||||
float fOrigin[3];
|
||||
GetEntPropVector(item.iWeapon, Prop_Send, "m_vecOrigin", fOrigin)
|
||||
GetEntPropVector(item.iWeapon, Prop_Send, "m_vecOrigin", fOrigin);
|
||||
|
||||
char sColor[32];
|
||||
item.dConfig.GetColor(sColor, sizeof(sColor));
|
||||
@@ -165,10 +167,8 @@ public Action Timer_Beacon(Handle timer, any index)
|
||||
iVecColor[2] = ((iColor >> 0) & 0xFF);
|
||||
iVecColor[3] = 255
|
||||
|
||||
|
||||
TE_SetupBeamRingPoint(fOrigin, 5.0, 50.0, g_iBeamSprite, g_iHaloSprite, 0, 10, 0.6, 6.0, 0.5, iVecColor, 10, 0);
|
||||
TE_SendToAll();
|
||||
|
||||
return Plugin_Continue;
|
||||
|
||||
}
|
||||
|
||||
@@ -354,7 +354,7 @@ stock bool RegisterExistingItem(CConfig config, int entity, int type)
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
stock bool RegisterItemEntity(CItem item, int entity, int type)
|
||||
{
|
||||
if (Entity_IsValid(entity))
|
||||
if (Entity_IsValid(entity) && !item.bDestroyed)
|
||||
{
|
||||
int iOwner = Entity_GetOwner(entity);
|
||||
int iParent = Entity_GetParent(entity);
|
||||
@@ -415,18 +415,21 @@ public void OnEntityDestroyed(int entity)
|
||||
{
|
||||
item.iClient = INVALID_ENT_REFERENCE;
|
||||
item.iWeapon = INVALID_ENT_REFERENCE;
|
||||
item.bDestroyed = true;
|
||||
|
||||
return;
|
||||
}
|
||||
else if (item.bButton && item.iButton == entity)
|
||||
{
|
||||
item.iButton = INVALID_ENT_REFERENCE;
|
||||
item.bDestroyed = true;
|
||||
|
||||
return;
|
||||
}
|
||||
else if (item.bTrigger && item.iTrigger == entity)
|
||||
{
|
||||
item.iTrigger = INVALID_ENT_REFERENCE;
|
||||
item.bDestroyed = true;
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -749,7 +752,7 @@ public int Native_GetItemData(Handle hPlugin, int numParams)
|
||||
{
|
||||
int index = GetNativeCell(1);
|
||||
|
||||
if ((index < 0) || (index > g_hArray_Items.Length))
|
||||
if ((index < 0) || (index >= g_hArray_Items.Length))
|
||||
{
|
||||
return ThrowNativeError(SP_ERROR_INDEX, "Item index %d is invalid.", index);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,800 @@
|
||||
//====================================================================================================
|
||||
//
|
||||
// Name: [entWatch] Restrictions
|
||||
// Author: zaCade & Prometheum
|
||||
// Description: Handle the restrictions of [entWatch]
|
||||
//
|
||||
//====================================================================================================
|
||||
#include <multicolors>
|
||||
|
||||
#pragma newdecls required
|
||||
|
||||
#include <sdktools>
|
||||
#include <sourcemod>
|
||||
#include <clientprefs>
|
||||
#include <entWatch_core>
|
||||
#include <entWatch_helpers>
|
||||
|
||||
//2026: add playtime requirements for being unrestricted
|
||||
#include <unloze_playtime>
|
||||
|
||||
Database g_hDatabase;
|
||||
|
||||
/* FORWARDS */
|
||||
Handle g_hFwd_OnClientRestricted;
|
||||
Handle g_hFwd_OnClientUnrestricted;
|
||||
|
||||
/* COOKIES */
|
||||
Handle g_hCookie_RestrictIssued;
|
||||
Handle g_hCookie_RestrictIssued_minutes;
|
||||
|
||||
Handle g_hCookie_RestrictExpire;
|
||||
Handle g_hCookie_RestrictExpire_minutes;
|
||||
|
||||
Handle g_hCookie_RestrictLength;
|
||||
Handle g_hCookie_RestrictLength_minutes;
|
||||
|
||||
/* BOOLEANS */
|
||||
bool g_bRestrictedTemp[MAXPLAYERS+1];
|
||||
bool g_bDontSpamMsg[MAXPLAYERS+1];
|
||||
bool g_bActiveEban[MAXPLAYERS+1];
|
||||
|
||||
/* INTERGERS */
|
||||
int g_iRestrictIssued[MAXPLAYERS+1];
|
||||
int g_iRestrictIssued_minutes[MAXPLAYERS+1];
|
||||
|
||||
int g_iRestrictLength[MAXPLAYERS+1];
|
||||
int g_iRestrictLength_minutes[MAXPLAYERS+1];
|
||||
|
||||
int g_iRestrictExpire[MAXPLAYERS+1];
|
||||
int g_iRestrictExpire_minutes[MAXPLAYERS+1];
|
||||
|
||||
int g_iClientHours[MAXPLAYERS + 1];
|
||||
int g_iClientMinutes[MAXPLAYERS + 1];
|
||||
|
||||
/* STRINGMAPS */
|
||||
StringMap g_hTrie_Storage;
|
||||
|
||||
/* CONVARS */
|
||||
ConVar g_cvarItemTierRequirement = null;
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public Plugin myinfo =
|
||||
{
|
||||
name = "[entWatch] Restrictions",
|
||||
author = "zaCade & Prometheum",
|
||||
description = "Handle the restrictions of [entWatch]",
|
||||
version = "4.0.1"
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public APLRes AskPluginLoad2(Handle hMyself, bool bLate, char[] sError, int errorSize)
|
||||
{
|
||||
CreateNative("EW_ClientRestrict", Native_ClientRestrict);
|
||||
CreateNative("EW_ClientUnrestrict", Native_ClientUnrestrict);
|
||||
CreateNative("EW_ClientRestricted", Native_ClientRestricted);
|
||||
|
||||
RegPluginLibrary("entWatch-restrictions");
|
||||
return APLRes_Success;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public void OnPluginStart()
|
||||
{
|
||||
LoadTranslations("common.phrases");
|
||||
LoadTranslations("entWatch.restrictions.phrases");
|
||||
|
||||
g_hFwd_OnClientRestricted = CreateGlobalForward("EW_OnClientRestricted", ET_Ignore, Param_Cell, Param_Cell, Param_Cell, Param_Cell);
|
||||
g_hFwd_OnClientUnrestricted = CreateGlobalForward("EW_OnClientUnrestricted", ET_Ignore, Param_Cell, Param_Cell);
|
||||
|
||||
g_hCookie_RestrictIssued = RegClientCookie("EW_RestrictIssued", "", CookieAccess_Private);
|
||||
g_hCookie_RestrictIssued_minutes = RegClientCookie("EW_RestrictIssued_minutes", "", CookieAccess_Private);
|
||||
|
||||
g_hCookie_RestrictExpire = RegClientCookie("EW_RestrictExpire", "", CookieAccess_Private);
|
||||
g_hCookie_RestrictExpire_minutes = RegClientCookie("EW_RestrictExpire_minutes", "", CookieAccess_Private);
|
||||
|
||||
g_hCookie_RestrictLength = RegClientCookie("EW_RestrictLength", "", CookieAccess_Private);
|
||||
g_hCookie_RestrictLength_minutes = RegClientCookie("EW_RestrictLength_minutes", "", CookieAccess_Private);
|
||||
|
||||
g_hTrie_Storage = new StringMap();
|
||||
|
||||
RegAdminCmd("sm_eban", Command_ClientRestrict, ADMFLAG_BAN);
|
||||
RegAdminCmd("sm_eunban", Command_ClientUnrestrict, ADMFLAG_UNBAN);
|
||||
|
||||
RegConsoleCmd("sm_restrictions", Command_DisplayRestrictions);
|
||||
RegConsoleCmd("sm_ebanlist", Command_DisplayRestrictions);
|
||||
RegConsoleCmd("sm_status", Command_DisplayStatus);
|
||||
|
||||
for (int client = 1; client <= MaxClients; client++)
|
||||
{
|
||||
if (IsClientConnected(client))
|
||||
OnClientPutInServer(client);
|
||||
|
||||
if (AreClientCookiesCached(client))
|
||||
OnClientCookiesCached(client);
|
||||
}
|
||||
|
||||
if (!g_hDatabase)
|
||||
{
|
||||
Database.Connect(SQL_OnDatabaseConnect, "unloze_entwatch_web");
|
||||
}
|
||||
CreateTimer(5.0, CheckIfEntwatchBanExpired, _, TIMER_REPEAT);
|
||||
|
||||
g_cvarItemTierRequirement = CreateConVar("sm_entwatch_tier_requirement", "1", "Using the unloze play time plugin to set a tier for allowing players access to items.");
|
||||
}
|
||||
|
||||
//could probably fail at detecting when a client finally gets entwatch unbanned but risk is pretty small as they need the ingame playtime to accomplish it.
|
||||
public Action CheckIfEntwatchBanExpired(Handle timer)
|
||||
{
|
||||
for (int client = 1; client <= MaxClients; client++)
|
||||
{
|
||||
if (Client_IsValid(client) && IsClientInGame(client))
|
||||
{
|
||||
//we only need to check for bans with durations. temporary and permanent bans dont need to be updated as expired obviusly
|
||||
if (g_iRestrictIssued[client] && g_iRestrictExpire[client] >= g_iClientHours[client] && g_iClientHours[client] > 0) //assuring playtime is set for client
|
||||
{
|
||||
//example: client restricted until 1335 hours and currently has 1320
|
||||
if (g_iRestrictExpire[client] > g_iClientHours[client])
|
||||
{
|
||||
g_bActiveEban[client] = true;
|
||||
continue;
|
||||
}
|
||||
//example client restricted until 1335 hours and 45 minutes. currently has 1335 hours and 20 minutes.
|
||||
if (g_iRestrictExpire_minutes[client] > g_iClientMinutes[client])
|
||||
{
|
||||
g_bActiveEban[client] = true;
|
||||
continue;
|
||||
}
|
||||
if (g_bActiveEban[client])
|
||||
{
|
||||
g_bActiveEban[client] = false;
|
||||
update_entwatchban_mysql_is_expired(client);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public void OnMapStart()
|
||||
{
|
||||
g_hTrie_Storage.Clear();
|
||||
if (!g_hDatabase)
|
||||
{
|
||||
Database.Connect(SQL_OnDatabaseConnect, "unloze_entwatch_web");
|
||||
}
|
||||
}
|
||||
|
||||
public void SQL_OnDatabaseConnect(Database db, const char[] error, any data)
|
||||
{
|
||||
if(!db || strlen(error))
|
||||
{
|
||||
LogError("Database error: %s", error);
|
||||
return;
|
||||
}
|
||||
g_hDatabase = db;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public void OnClientPutInServer(int client)
|
||||
{
|
||||
char sAddress[32];
|
||||
GetClientIP(client, sAddress, sizeof(sAddress));
|
||||
|
||||
g_bDontSpamMsg[client] = false;
|
||||
g_bActiveEban[client] = false;
|
||||
bool bRestrictedTemp;
|
||||
if (g_hTrie_Storage.GetValue(sAddress, bRestrictedTemp))
|
||||
{
|
||||
g_bRestrictedTemp[client] = bRestrictedTemp;
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public void OnClientCookiesCached(int client)
|
||||
{
|
||||
g_iRestrictIssued[client] = GetClientCookieInt(client, g_hCookie_RestrictIssued);
|
||||
g_iRestrictIssued_minutes[client] = GetClientCookieInt(client, g_hCookie_RestrictIssued_minutes);
|
||||
|
||||
g_iRestrictExpire[client] = GetClientCookieInt(client, g_hCookie_RestrictExpire);
|
||||
g_iRestrictExpire_minutes[client] = GetClientCookieInt(client, g_hCookie_RestrictExpire_minutes);
|
||||
|
||||
g_iRestrictLength[client] = GetClientCookieInt(client, g_hCookie_RestrictLength);
|
||||
g_iRestrictLength_minutes[client] = GetClientCookieInt(client, g_hCookie_RestrictLength_minutes);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public void OnClientDisconnect(int client)
|
||||
{
|
||||
if (g_bRestrictedTemp[client])
|
||||
{
|
||||
char sAddress[32];
|
||||
GetClientIP(client, sAddress, sizeof(sAddress));
|
||||
|
||||
g_hTrie_Storage.SetArray(sAddress, g_bRestrictedTemp[client], true);
|
||||
}
|
||||
|
||||
g_bDontSpamMsg[client] = false;
|
||||
g_bRestrictedTemp[client] = false;
|
||||
g_bActiveEban[client] = false;
|
||||
|
||||
g_iRestrictIssued[client] = 0;
|
||||
g_iRestrictIssued_minutes[client] = 0;
|
||||
|
||||
g_iRestrictExpire[client] = 0;
|
||||
g_iRestrictExpire_minutes[client] = 0;
|
||||
|
||||
g_iRestrictLength[client] = 0;
|
||||
g_iRestrictLength_minutes[client] = 0;
|
||||
|
||||
g_iClientHours[client] = 0;
|
||||
g_iClientMinutes[client] = 0;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public Action Command_ClientRestrict(int client, int args)
|
||||
{
|
||||
if (!GetCmdArgs())
|
||||
{
|
||||
CReplyToCommand(client, "\x07%s[entWatch] \x07%sUsage: sm_eban <#userid/name> [duration]", "E01B5D", "F16767");
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
char sArguments[2][32];
|
||||
GetCmdArg(1, sArguments[0], sizeof(sArguments[]));
|
||||
GetCmdArg(2, sArguments[1], sizeof(sArguments[]));
|
||||
|
||||
int target;
|
||||
if ((target = FindTarget(client, sArguments[0], true)) == -1)
|
||||
return Plugin_Handled;
|
||||
|
||||
if (GetCmdArgs() >= 2)
|
||||
{
|
||||
int length = StringToInt(sArguments[1]);
|
||||
|
||||
if (ClientRestrict(client, target, length))
|
||||
{
|
||||
if (length == -1)
|
||||
{
|
||||
CPrintToChatAll("\x07%s[entWatch] \x07%s%N\x07%s restricted \x07%s%N\x07%s temporarily.", "E01B5D", "EDEDED", client, "F16767", "EDEDED", target, "F16767");
|
||||
LogAction(client, target, "%L restricted %L temporarily.", client, target);
|
||||
}
|
||||
else if (length > 0)
|
||||
{
|
||||
int hours = length / 60;
|
||||
int minutes = length % 60;
|
||||
|
||||
CPrintToChatAll("\x07%s[entWatch] \x07%s%N\x07%s restricted \x07%s%N\x07%s for \x07%s%d\x07%s hours and \x07%s%d\x07%s minutes Playtime.", "E01B5D", "EDEDED", client, "F16767", "EDEDED", target, "F16767", "EDEDED", hours, "F16767", "EDEDED", minutes, "F16767");
|
||||
LogAction(client, target, "%L restricted %L for %d hours and %d minutes playtime.", client, target, hours, minutes);
|
||||
}
|
||||
else
|
||||
{
|
||||
CPrintToChatAll("\x07%s[entWatch] \x07%s%N\x07%s restricted \x07%s%N\x07%s permanently.", "E01B5D", "EDEDED", client, "F16767", "EDEDED", target, "F16767");
|
||||
LogAction(client, target, "%L restricted %L permanently.", client, target);
|
||||
}
|
||||
insert_entwatchban_mysql(client, target);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ClientRestrict(client, target, -1))
|
||||
{
|
||||
CPrintToChatAll("\x07%s[entWatch] \x07%s%N\x07%s restricted \x07%s%N\x07%s temporarily.", "E01B5D", "EDEDED", client, "F16767", "EDEDED", target, "F16767");
|
||||
LogAction(client, target, "%L restricted %L temporarily.", client, target);
|
||||
}
|
||||
}
|
||||
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public Action Command_ClientUnrestrict(int client, int args)
|
||||
{
|
||||
if (!GetCmdArgs())
|
||||
{
|
||||
CReplyToCommand(client, "\x07%s[entWatch] \x07%sUsage: sm_eunban <#userid/name>", "E01B5D", "F16767");
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
char sArguments[1][32];
|
||||
GetCmdArg(1, sArguments[0], sizeof(sArguments[]));
|
||||
|
||||
int target;
|
||||
if ((target = FindTarget(client, sArguments[0], true)) == -1)
|
||||
return Plugin_Handled;
|
||||
|
||||
if (ClientUnrestrict(client, target))
|
||||
{
|
||||
CPrintToChatAll("\x07%s[entWatch] \x07%s%N\x07%s unrestricted \x07%s%N\x07%s.", "E01B5D", "EDEDED", client, "F16767", "EDEDED", target, "F16767");
|
||||
LogAction(client, target, "%L unrestricted %L.", client, target);
|
||||
}
|
||||
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public Action Command_DisplayRestrictions(int client, int args)
|
||||
{
|
||||
char aBuf[1024];
|
||||
char aBuf_time_requirement[1024];
|
||||
char aBuf2[MAX_NAME_LENGTH];
|
||||
char aBuf2_time_requirement[MAX_NAME_LENGTH];
|
||||
|
||||
for (int i = 1; i <= MaxClients; i++)
|
||||
{
|
||||
if (IsClientInGame(i) && !IsFakeClient(i))
|
||||
{
|
||||
if (ClientRestricted(i))
|
||||
{
|
||||
int client_tier = GetPlayerTier_native(i);
|
||||
if (client_tier < g_cvarItemTierRequirement.IntValue)
|
||||
{
|
||||
GetClientName(i, aBuf2_time_requirement, sizeof(aBuf2_time_requirement));
|
||||
StrCat(aBuf_time_requirement, sizeof(aBuf_time_requirement), aBuf2_time_requirement);
|
||||
StrCat(aBuf_time_requirement, sizeof(aBuf_time_requirement), ", ");
|
||||
}
|
||||
else
|
||||
{
|
||||
GetClientName(i, aBuf2, sizeof(aBuf2));
|
||||
StrCat(aBuf, sizeof(aBuf), aBuf2);
|
||||
StrCat(aBuf, sizeof(aBuf), ", ");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (strlen(aBuf))
|
||||
{
|
||||
aBuf[strlen(aBuf) - 2] = 0;
|
||||
CReplyToCommand(client, "\x07%s[entWatch] \x07%sCurrently restricted clients: \x07%s%s", "E01B5D", "F16767", "EDEDED", aBuf);
|
||||
}
|
||||
else
|
||||
CReplyToCommand(client, "\x07%s[entWatch] \x07%sCurrently restricted clients: \x07%snone", "E01B5D", "F16767", "EDEDED");
|
||||
|
||||
if (strlen(aBuf_time_requirement))
|
||||
{
|
||||
aBuf_time_requirement[strlen(aBuf_time_requirement) - 2] = 0;
|
||||
CReplyToCommand(client, "\x07%s[entWatch] \x07%sCurrent clients with not high enough Tier: \x07%s%s", "E01B5D", "F16767", "EDEDED", aBuf_time_requirement);
|
||||
}
|
||||
else
|
||||
CReplyToCommand(client, "\x07%s[entWatch] \x07%sCurrent clients with not high enough Tier: \x07%snone", "E01B5D", "F16767", "EDEDED");
|
||||
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public Action Command_DisplayStatus(int client, int args)
|
||||
{
|
||||
if (CheckCommandAccess(client, "", ADMFLAG_BAN) && GetCmdArgs())
|
||||
{
|
||||
char sArguments[1][32];
|
||||
GetCmdArg(1, sArguments[0], sizeof(sArguments[]));
|
||||
|
||||
int target;
|
||||
if ((target = FindTarget(client, sArguments[0], true)) == -1)
|
||||
return Plugin_Handled;
|
||||
|
||||
if (!AreClientCookiesCached(target))
|
||||
{
|
||||
CReplyToCommand(client, "\x07%s[entWatch] \x07%s%N\x07%s their cookies are still loading.", "E01B5D", "EDEDED", target, "F16767");
|
||||
return Plugin_Handled;
|
||||
}
|
||||
else if (g_bRestrictedTemp[target])
|
||||
{
|
||||
CReplyToCommand(client, "\x07%s[entWatch] \x07%s%N\x07%s is currently temporarily restricted.", "E01B5D", "EDEDED", target, "F16767");
|
||||
return Plugin_Handled;
|
||||
}
|
||||
else if (g_iRestrictIssued[target] && g_iRestrictExpire[target] == 0)
|
||||
{
|
||||
CReplyToCommand(client, "\x07%s[entWatch] \x07%s%N\x07%s is currently permanently restricted.", "E01B5D", "EDEDED", target, "F16767");
|
||||
return Plugin_Handled;
|
||||
}
|
||||
else if (g_iRestrictIssued[target] && g_iRestrictExpire[target] >= g_iClientHours[target])
|
||||
{
|
||||
//example: 1335 hours and 40 minutes restriction expires. player at 1335 hours and 50 minutes is no longer restricted.
|
||||
if (g_iRestrictExpire[target] == g_iClientHours[target] && g_iRestrictExpire_minutes[target] < g_iClientMinutes[target])
|
||||
{
|
||||
CReplyToCommand(client, "\x07%s[entWatch] \x07%s%N\x07%s is currently not restricted.", "E01B5D", "EDEDED", target, "F16767");
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
//its just hours and minutes now
|
||||
char sTimeRemaining[64];
|
||||
int iTimeRemaining_hours = g_iRestrictExpire[target] - g_iClientHours[target];
|
||||
int iTimeRemaining_minutes = g_iRestrictExpire_minutes[target] - g_iClientMinutes[target];
|
||||
if (iTimeRemaining_minutes < 0)
|
||||
{
|
||||
iTimeRemaining_minutes += 60;
|
||||
iTimeRemaining_hours--;
|
||||
}
|
||||
|
||||
Format(sTimeRemaining, sizeof(sTimeRemaining), "%d Hours and %d minutes Playtime", iTimeRemaining_hours, iTimeRemaining_minutes);
|
||||
|
||||
CReplyToCommand(client, "\x07%s[entWatch] \x07%s%N\x07%s is currently restricted for another \x07%s%s\x07%s.", "E01B5D", "EDEDED", target, "F16767", "EDEDED", sTimeRemaining, "F16767");
|
||||
return Plugin_Handled;
|
||||
}
|
||||
else
|
||||
{
|
||||
CReplyToCommand(client, "\x07%s[entWatch] \x07%s%N\x07%s is currently not restricted.", "E01B5D", "EDEDED", target, "F16767");
|
||||
return Plugin_Handled;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!AreClientCookiesCached(client))
|
||||
{
|
||||
CReplyToCommand(client, "\x07%s[entWatch] \x07%sYour cookies are still loading.", "E01B5D", "F16767");
|
||||
return Plugin_Handled;
|
||||
}
|
||||
else if (g_bRestrictedTemp[client])
|
||||
{
|
||||
CReplyToCommand(client, "\x07%s[entWatch] \x07%sYou are currently temporarily restricted.", "E01B5D", "F16767");
|
||||
return Plugin_Handled;
|
||||
}
|
||||
else if (g_iRestrictIssued[client] && g_iRestrictExpire[client] == 0)
|
||||
{
|
||||
CReplyToCommand(client, "\x07%s[entWatch] \x07%sYou are currently permanently restricted.", "E01B5D", "F16767");
|
||||
return Plugin_Handled;
|
||||
}
|
||||
else if (g_iRestrictIssued[client] && g_iRestrictExpire[client] >= g_iClientHours[client])
|
||||
{
|
||||
//example: 1335 hours and 40 minutes restriction expires. player at 1335 hours and 50 minutes is no longer restricted.
|
||||
if (g_iRestrictExpire[client] == g_iClientHours[client] && g_iRestrictExpire_minutes[client] < g_iClientMinutes[client])
|
||||
{
|
||||
CReplyToCommand(client, "\x07%s[entWatch] \x07%sYou are currently not restricted.", "E01B5D", "F16767");
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
//its just hours and minutes now
|
||||
char sTimeRemaining[64];
|
||||
int iTimeRemaining_hours = g_iRestrictExpire[client] - g_iClientHours[client];
|
||||
int iTimeRemaining_minutes = g_iRestrictExpire_minutes[client] - g_iClientMinutes[client];
|
||||
if (iTimeRemaining_minutes < 0)
|
||||
{
|
||||
iTimeRemaining_minutes += 60;
|
||||
iTimeRemaining_hours--;
|
||||
}
|
||||
|
||||
Format(sTimeRemaining, sizeof(sTimeRemaining), "%d Hours and %d minutes Playtime", iTimeRemaining_hours, iTimeRemaining_minutes);
|
||||
|
||||
CReplyToCommand(client, "\x07%s[entWatch] \x07%sYou are currently restricted for another \x07%s%s\x07%s.", "E01B5D", "F16767", "EDEDED", sTimeRemaining, "F16767");
|
||||
return Plugin_Handled;
|
||||
}
|
||||
else
|
||||
{
|
||||
CReplyToCommand(client, "\x07%s[entWatch] \x07%sYou are currently not restricted.", "E01B5D", "F16767");
|
||||
return Plugin_Handled;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public Action EW_OnClientItemCanPickup(int client, int index)
|
||||
{
|
||||
return ClientRestricted(client)?Plugin_Handled:Plugin_Continue;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public Action EW_OnClientItemCanActivate(int client, int index)
|
||||
{
|
||||
return ClientRestricted(client)?Plugin_Handled:Plugin_Continue;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
stock bool ClientRestrict(int client, int target, int length)
|
||||
{
|
||||
if (!Client_IsValid(client) || !Client_IsValid(target) || !AreClientCookiesCached(target) || ClientRestricted(target))
|
||||
{
|
||||
if (Client_IsValid(client) && Client_IsValid(target) && ClientRestricted(target))
|
||||
{
|
||||
CPrintToChat(client, "\x07%s[entWatch] \x07%s The player \x07%s%N\x07%s is already restricted.", "E01B5D", "EDEDED", "EDEDED", target, "F16767");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (length == -1)
|
||||
{
|
||||
g_bRestrictedTemp[target] = true;
|
||||
}
|
||||
else if (length == 0)
|
||||
{
|
||||
//permanently
|
||||
g_bRestrictedTemp[target] = false;
|
||||
g_iRestrictIssued[target] = g_iClientHours[target];
|
||||
g_iRestrictExpire[target] = 0;
|
||||
g_iRestrictLength[target] = 0;
|
||||
g_iRestrictLength_minutes[target] = 0;
|
||||
|
||||
SetClientCookieInt(target, g_hCookie_RestrictIssued, GetTime());
|
||||
SetClientCookieInt(target, g_hCookie_RestrictExpire, 0);
|
||||
SetClientCookieInt(target, g_hCookie_RestrictLength, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
//2026 edit. we now use ingame play time of the client instead of real time.
|
||||
int hours_length = length / 60;
|
||||
int minutes_length = length % 60;
|
||||
|
||||
g_bRestrictedTemp[target] = false;
|
||||
|
||||
g_iRestrictIssued[target] = g_iClientHours[target];
|
||||
g_iRestrictIssued_minutes[target] = g_iClientMinutes[target];
|
||||
|
||||
g_iRestrictExpire[target] = g_iClientHours[target] + hours_length;
|
||||
int rounded_minutes = g_iClientMinutes[target] + minutes_length;
|
||||
if (rounded_minutes > 60)
|
||||
{
|
||||
rounded_minutes -= 60;
|
||||
g_iRestrictExpire[target]++;
|
||||
}
|
||||
g_iRestrictExpire_minutes[target] = rounded_minutes;
|
||||
|
||||
g_iRestrictLength[target] = hours_length;
|
||||
g_iRestrictLength_minutes[target] = minutes_length;
|
||||
|
||||
SetClientCookieInt(target, g_hCookie_RestrictIssued, g_iRestrictIssued[target]);
|
||||
SetClientCookieInt(target, g_hCookie_RestrictIssued_minutes, g_iRestrictIssued_minutes[target]);
|
||||
|
||||
SetClientCookieInt(target, g_hCookie_RestrictExpire, g_iRestrictExpire[target]);
|
||||
SetClientCookieInt(target, g_hCookie_RestrictExpire_minutes, g_iRestrictExpire_minutes[target]);
|
||||
|
||||
SetClientCookieInt(target, g_hCookie_RestrictLength, g_iRestrictLength[target]);
|
||||
SetClientCookieInt(target, g_hCookie_RestrictLength_minutes, g_iRestrictLength_minutes[target]);
|
||||
}
|
||||
|
||||
Call_StartForward(g_hFwd_OnClientRestricted);
|
||||
Call_PushCell(client);
|
||||
Call_PushCell(target);
|
||||
if (length == -1 || length == 0)
|
||||
{
|
||||
//its temporarily or permanently
|
||||
Call_PushCell(length);
|
||||
Call_PushCell(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
//its certain amount of hours and minutes
|
||||
Call_PushCell(g_iRestrictLength[target]);
|
||||
Call_PushCell(g_iRestrictLength_minutes[target]);
|
||||
}
|
||||
Call_Finish();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
stock bool ClientUnrestrict(int client, int target)
|
||||
{
|
||||
if (!Client_IsValid(client) || !Client_IsValid(target) || !AreClientCookiesCached(target) || !ClientRestricted(target))
|
||||
return false;
|
||||
|
||||
g_bRestrictedTemp[target] = false;
|
||||
|
||||
g_iRestrictIssued[target] = 0;
|
||||
g_iRestrictIssued_minutes[target] = 0;
|
||||
|
||||
g_iRestrictExpire[target] = 0;
|
||||
g_iRestrictExpire_minutes[target] = 0;
|
||||
|
||||
g_iRestrictLength[target] = 0;
|
||||
g_iRestrictLength_minutes[target] = 0;
|
||||
|
||||
SetClientCookieInt(target, g_hCookie_RestrictIssued, 0);
|
||||
SetClientCookieInt(target, g_hCookie_RestrictIssued_minutes, 0);
|
||||
|
||||
SetClientCookieInt(target, g_hCookie_RestrictExpire, 0);
|
||||
SetClientCookieInt(target, g_hCookie_RestrictExpire_minutes, 0);
|
||||
|
||||
SetClientCookieInt(target, g_hCookie_RestrictLength, 0);
|
||||
SetClientCookieInt(target, g_hCookie_RestrictLength_minutes, 0);
|
||||
|
||||
update_entwatchban_mysql_is_expired(target);
|
||||
|
||||
Call_StartForward(g_hFwd_OnClientUnrestricted);
|
||||
Call_PushCell(client);
|
||||
Call_PushCell(target);
|
||||
Call_Finish();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
stock bool ClientRestricted(int client)
|
||||
{
|
||||
if (!Client_IsValid(client))
|
||||
return false;
|
||||
|
||||
//Block them when loading cookies..
|
||||
if (!AreClientCookiesCached(client))
|
||||
return true;
|
||||
|
||||
//Temporary restriction.
|
||||
if (g_bRestrictedTemp[client])
|
||||
return true;
|
||||
|
||||
//Permanent restriction.
|
||||
if (g_iRestrictIssued[client] && g_iRestrictExpire[client] == 0)
|
||||
return true;
|
||||
|
||||
//Normal restriction.
|
||||
if (g_iRestrictIssued[client] && g_iRestrictExpire[client] >= g_iClientHours[client])
|
||||
{
|
||||
//example: client restricted until 1335 hours and currently has 1320
|
||||
if (g_iRestrictExpire[client] > g_iClientHours[client])
|
||||
{
|
||||
return true;
|
||||
}
|
||||
//example client restricted until 1335 hours and 45 minutes. currently has 1335 hours and 20 minutes.
|
||||
if (g_iRestrictExpire_minutes[client] > g_iClientMinutes[client])
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
//tier requirement for picking up items
|
||||
int client_tier = GetPlayerTier_native(client);
|
||||
if (client_tier < g_cvarItemTierRequirement.IntValue)
|
||||
{
|
||||
if (!g_bDontSpamMsg[client])
|
||||
{
|
||||
CPrintToChat(client, "\x07%s[entWatch] \x07%s You need to reach Tier \x07%s%i\x07%s for picking up items.", "E01B5D", "EDEDED", "EDEDED", g_cvarItemTierRequirement.IntValue, "EDEDED");
|
||||
g_bDontSpamMsg[client] = true;
|
||||
CreateTimer(1.0, allow_message_again, GetClientSerial(client));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public int Native_ClientRestrict(Handle hPlugin, int numParams)
|
||||
{
|
||||
return ClientRestrict(GetNativeCell(1), GetNativeCell(2), GetNativeCell(3));
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public int Native_ClientUnrestrict(Handle hPlugin, int numParams)
|
||||
{
|
||||
return ClientUnrestrict(GetNativeCell(1), GetNativeCell(2));
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public int Native_ClientRestricted(Handle hPlugin, int numParams)
|
||||
{
|
||||
return ClientRestricted(GetNativeCell(1));
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
stock void SetClientCookieInt(int client, Handle hCookie, int value)
|
||||
{
|
||||
char sValue[32];
|
||||
IntToString(value, sValue, sizeof(sValue));
|
||||
|
||||
SetClientCookie(client, hCookie, sValue);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
stock int GetClientCookieInt(int client, Handle hCookie)
|
||||
{
|
||||
char sValue[32];
|
||||
GetClientCookie(client, hCookie, sValue, sizeof(sValue));
|
||||
|
||||
return StringToInt(sValue);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose: 2026 getting the ingame play time of the players. called with OnClientPostAdminCheck() forward and timer every 10 minutes
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public void GetPlayerHoursServer(int client, int hours, int minutes)
|
||||
{
|
||||
g_iClientHours[client] = hours;
|
||||
g_iClientMinutes[client] = minutes;
|
||||
}
|
||||
|
||||
public void update_entwatchban_mysql_is_expired(int entwatch_banned_player)
|
||||
{
|
||||
char sSIDClient[64];
|
||||
GetClientAuthId(entwatch_banned_player, AuthId_Steam2, sSIDClient, sizeof(sSIDClient));
|
||||
|
||||
char sQuery[512];
|
||||
Format(sQuery, sizeof(sQuery), "update `EntWatch_Current_Eban` set is_expired = 1 where client_steamid = '%s' and is_expired = 0", sSIDClient);
|
||||
g_hDatabase.Query(SQL_FinishedQuery, sQuery, _, DBPrio_Normal);
|
||||
}
|
||||
|
||||
public void insert_entwatchban_mysql(int admin, int entwatch_banned_player)
|
||||
{
|
||||
char sNameAdmin[MAX_NAME_LENGTH];
|
||||
GetClientName(admin, sNameAdmin, sizeof(sNameAdmin));
|
||||
int size2 = 2 * strlen(sNameAdmin) + 1;
|
||||
char[] sEscapedNameAdmin = new char[size2 + 1];
|
||||
g_hDatabase.Escape(sNameAdmin, sEscapedNameAdmin, size2 + 1);
|
||||
char sSIDAdmin[64];
|
||||
GetClientAuthId(admin, AuthId_Steam2, sSIDAdmin, sizeof(sSIDAdmin));
|
||||
|
||||
|
||||
char sNameClient[MAX_NAME_LENGTH];
|
||||
GetClientName(entwatch_banned_player, sNameClient, sizeof(sNameClient));
|
||||
size2 = 2 * strlen(sNameClient) + 1;
|
||||
char[] sEscapedNameClient = new char[size2 + 1];
|
||||
g_hDatabase.Escape(sNameClient, sEscapedNameClient, size2 + 1);
|
||||
char sSIDClient[64];
|
||||
GetClientAuthId(entwatch_banned_player, AuthId_Steam2, sSIDClient, sizeof(sSIDClient));
|
||||
|
||||
int playtime_start_client = (g_iRestrictIssued[entwatch_banned_player] * 60) + g_iRestrictIssued_minutes[entwatch_banned_player];
|
||||
int playtime_end_client = (g_iRestrictExpire[entwatch_banned_player] * 60) + g_iRestrictExpire_minutes[entwatch_banned_player];
|
||||
|
||||
int length = (g_iRestrictLength[entwatch_banned_player] * 60) + g_iRestrictLength_minutes[entwatch_banned_player];
|
||||
|
||||
char sQuery[512];
|
||||
Format(sQuery, sizeof(sQuery), "INSERT INTO `EntWatch_Current_Eban` (`client_name`, `client_steamid`, `admin_name`, `admin_steamid`, `duration`, `reason`, `time_played_start`, `time_played_end`) VALUES ('%s', '%s', '%s', '%s', '%i', '%s', '%i', '%i')", sEscapedNameClient, sSIDClient, sEscapedNameAdmin, sSIDAdmin, length, "get fucked", playtime_start_client, playtime_end_client);
|
||||
g_hDatabase.Query(SQL_FinishedQuery, sQuery, _, DBPrio_Normal);
|
||||
}
|
||||
|
||||
public void SQL_FinishedQuery(Database db, DBResultSet results, const char[] error, any data)
|
||||
{
|
||||
if (!db || strlen(error))
|
||||
{
|
||||
LogError("Query error 3: %s", error);
|
||||
}
|
||||
delete results;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public Action allow_message_again(Handle hTimer, int Serial)
|
||||
{
|
||||
int client;
|
||||
if ((client = GetClientFromSerial(Serial)) == 0)
|
||||
{
|
||||
return Plugin_Handled;
|
||||
}
|
||||
if (Client_IsValid(client))
|
||||
{
|
||||
g_bDontSpamMsg[client] = false;
|
||||
}
|
||||
return Plugin_Handled;
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
//====================================================================================================
|
||||
//
|
||||
// Name: [entWatch] UseAnywhere
|
||||
// Author: marisakirisame.net
|
||||
// Description: Allow the owner of an item to activate it with their USE key anywhere
|
||||
//
|
||||
//====================================================================================================
|
||||
#include <multicolors>
|
||||
|
||||
#pragma newdecls required
|
||||
|
||||
#include <sourcemod>
|
||||
#include <sdkhooks>
|
||||
#include <sdktools>
|
||||
#include <clientprefs>
|
||||
#include <entWatch_core>
|
||||
#include <entWatch_helpers>
|
||||
|
||||
/* BOOLS */
|
||||
bool g_bDisabled[MAXPLAYERS+1];
|
||||
|
||||
/* COOKIES */
|
||||
Handle g_hCookie_Disabled;
|
||||
|
||||
/* INTEGERS */
|
||||
int g_iLastButtons[MAXPLAYERS+1];
|
||||
int g_iActivateTick[MAXPLAYERS+1];
|
||||
int g_iActivateButton[MAXPLAYERS+1];
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public Plugin myinfo =
|
||||
{
|
||||
name = "[entWatch] UseAnywhere",
|
||||
author = "marisakirisame.net",
|
||||
description = "Allow the owner of an item to activate it with their USE key anywhere",
|
||||
version = "4.0.0"
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public void OnPluginStart()
|
||||
{
|
||||
g_hCookie_Disabled = RegClientCookie("EW_UseAnywhereDisabled", "", CookieAccess_Private);
|
||||
|
||||
RegConsoleCmd("sm_euseanywhere", Command_ToggleUseAnywhere);
|
||||
|
||||
for (int client = 1; client <= MaxClients; client++)
|
||||
{
|
||||
if (!IsClientInGame(client) || IsFakeClient(client))
|
||||
continue;
|
||||
|
||||
OnClientPutInServer(client);
|
||||
|
||||
if (AreClientCookiesCached(client))
|
||||
OnClientCookiesCached(client);
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public void OnClientPutInServer(int client)
|
||||
{
|
||||
g_bDisabled[client] = false;
|
||||
g_iLastButtons[client] = 0;
|
||||
g_iActivateTick[client] = 0;
|
||||
g_iActivateButton[client] = INVALID_ENT_REFERENCE;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public void OnClientCookiesCached(int client)
|
||||
{
|
||||
g_bDisabled[client] = GetClientCookieBool(client, g_hCookie_Disabled);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public Action Command_ToggleUseAnywhere(int client, int args)
|
||||
{
|
||||
if (!Client_IsValid(client))
|
||||
return Plugin_Handled;
|
||||
|
||||
g_bDisabled[client] = !g_bDisabled[client];
|
||||
|
||||
if (g_bDisabled[client])
|
||||
{
|
||||
SetClientCookieBool(client, g_hCookie_Disabled, true);
|
||||
|
||||
CReplyToCommand(client, "\x07%s[entWatch] \x07%sYou will now have to aim at the item's trigger to use it.", "E01B5D", "F16767");
|
||||
return Plugin_Handled;
|
||||
}
|
||||
else
|
||||
{
|
||||
SetClientCookieBool(client, g_hCookie_Disabled, false);
|
||||
|
||||
CReplyToCommand(client, "\x07%s[entWatch] \x07%sYou can now use your item by pressing your USE key anywhere.", "E01B5D", "F16767");
|
||||
return Plugin_Handled;
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose: Activate the clients item on +use, without having to aim at its button.
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public Action OnPlayerRunCmd(int client, int &buttons)
|
||||
{
|
||||
bool bPressed = view_as<bool>((buttons & IN_USE) && !(g_iLastButtons[client] & IN_USE));
|
||||
|
||||
g_iLastButtons[client] = buttons;
|
||||
|
||||
if (!bPressed || g_bDisabled[client] || !IsPlayerAlive(client))
|
||||
return Plugin_Continue;
|
||||
|
||||
int button = FindClientItemButton(client);
|
||||
|
||||
if (button == INVALID_ENT_REFERENCE)
|
||||
return Plugin_Continue;
|
||||
|
||||
// Fires the buttons Use input, which runs the core's SDKHook_Use handler as if the
|
||||
// client had aimed at it, so every cooldown, restriction and message is handled there.
|
||||
AcceptEntityInput(button, "Use", client, client);
|
||||
|
||||
// Set after the input, so the press above is not blocked by its own guard.
|
||||
g_iActivateTick[client] = GetGameTickCount();
|
||||
g_iActivateButton[client] = button;
|
||||
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose: Ignore the engines own +use if we already fired this button for the client this tick,
|
||||
// so aiming at the button while pressing USE cannot activate the item twice.
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public Action EW_OnClientItemCanActivate(int client, int index)
|
||||
{
|
||||
if (g_iActivateTick[client] != GetGameTickCount())
|
||||
return Plugin_Continue;
|
||||
|
||||
CItem item = EW_GetItemData(index);
|
||||
|
||||
if (item.bButton && item.iButton == g_iActivateButton[client])
|
||||
return Plugin_Handled;
|
||||
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose: Get the button of the clients item, but only if they carry exactly one.
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
stock int FindClientItemButton(int client)
|
||||
{
|
||||
int iFound = -1;
|
||||
|
||||
for (int index; index < EW_GetItemCount(); index++)
|
||||
{
|
||||
CItem item = EW_GetItemData(index);
|
||||
|
||||
if (!(item.bClient && item.iClient == client))
|
||||
continue;
|
||||
|
||||
if (iFound != -1)
|
||||
return INVALID_ENT_REFERENCE;
|
||||
|
||||
iFound = index;
|
||||
}
|
||||
|
||||
if (iFound != -1)
|
||||
{
|
||||
CItem itemFound = EW_GetItemData(iFound);
|
||||
|
||||
if (itemFound.bButton && Entity_IsValid(itemFound.iButton))
|
||||
return itemFound.iButton;
|
||||
}
|
||||
|
||||
return INVALID_ENT_REFERENCE;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
stock void SetClientCookieBool(int client, Handle hCookie, bool value)
|
||||
{
|
||||
char sValue[32];
|
||||
IntToString(view_as<int>(value), sValue, sizeof(sValue));
|
||||
|
||||
SetClientCookie(client, hCookie, sValue);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
stock bool GetClientCookieBool(int client, Handle hCookie)
|
||||
{
|
||||
char sValue[32];
|
||||
GetClientCookie(client, hCookie, sValue, sizeof(sValue));
|
||||
|
||||
return view_as<bool>(StringToInt(sValue));
|
||||
}
|
||||
@@ -22,6 +22,8 @@ methodmap CItem < Basic
|
||||
|
||||
myclass.SetFloat("flWait", 0.0);
|
||||
|
||||
myclass.SetBool("bDestroyed", false);
|
||||
|
||||
return view_as<CItem>(myclass);
|
||||
}
|
||||
|
||||
@@ -124,6 +126,18 @@ methodmap CItem < Basic
|
||||
}
|
||||
}
|
||||
|
||||
property bool bDestroyed
|
||||
{
|
||||
public get()
|
||||
{
|
||||
return this.GetBool("bDestroyed");
|
||||
}
|
||||
public set(bool value)
|
||||
{
|
||||
this.SetBool("bDestroyed", value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
property bool bClient
|
||||
{
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
#if defined entWatch_restrictions_included
|
||||
#endinput
|
||||
#endif
|
||||
|
||||
#define entWatch_restrictions_included
|
||||
|
||||
public SharedPlugin __pl_entWatch_restrictions =
|
||||
{
|
||||
name = "entWatch-restrictions",
|
||||
file = "entWatch-restrictions.smx",
|
||||
|
||||
#if defined REQUIRE_PLUGIN
|
||||
required = 1
|
||||
#else
|
||||
required = 0
|
||||
#endif
|
||||
};
|
||||
|
||||
#if !defined REQUIRE_PLUGIN
|
||||
public void __pl_entWatch_restrictions_SetNTVOptional()
|
||||
{
|
||||
MarkNativeAsOptional("EW_ClientRestrict");
|
||||
MarkNativeAsOptional("EW_ClientUnrestrict");
|
||||
MarkNativeAsOptional("EW_ClientRestricted");
|
||||
}
|
||||
#endif
|
||||
|
||||
native bool EW_ClientRestrict(int client, int target, int length);
|
||||
native bool EW_ClientUnrestrict(int client, int target);
|
||||
native bool EW_ClientRestricted(int client);
|
||||
|
||||
forward void EW_OnClientRestricted(int client, int target, int hours, int minutes);
|
||||
forward void EW_OnClientUnrestricted(int client, int target);
|
||||
@@ -0,0 +1,38 @@
|
||||
-- entwatchban_web.EntWatch_Current_Eban definition
|
||||
|
||||
CREATE TABLE `EntWatch_Current_Eban` (
|
||||
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`client_name` varchar(32) NOT NULL,
|
||||
`client_steamid` varchar(64) NOT NULL,
|
||||
`admin_name` varchar(32) NOT NULL,
|
||||
`admin_steamid` varchar(64) NOT NULL,
|
||||
`duration` int(10) unsigned NOT NULL,
|
||||
`time_stamp_start` datetime DEFAULT current_timestamp(),
|
||||
`reason` varchar(64) DEFAULT NULL,
|
||||
`reason_unban` varchar(64) DEFAULT NULL,
|
||||
`admin_name_unban` varchar(32) DEFAULT NULL,
|
||||
`admin_steamid_unban` varchar(64) DEFAULT NULL,
|
||||
`timestamp_unban` int(11) DEFAULT NULL,
|
||||
`time_played_start` int(11) NOT NULL DEFAULT -1,
|
||||
`time_played_end` int(11) NOT NULL DEFAULT -1,
|
||||
`is_expired` tinyint(1) NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_steamid_search` (`client_steamid`,`admin_steamid`),
|
||||
KEY `idx_expiry_sort` (`time_stamp_start`,`duration`),
|
||||
KEY `idx_server_expiry` (`duration`,`time_stamp_start`),
|
||||
KEY `idx_is_expired` (`is_expired`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
|
||||
|
||||
-- entwatchban_web.web_logs definition
|
||||
|
||||
CREATE TABLE `web_logs` (
|
||||
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`message` varchar(255) NOT NULL,
|
||||
`admin_name` varchar(32) NOT NULL,
|
||||
`admin_steamid` varchar(64) NOT NULL,
|
||||
`client_name` varchar(32) NOT NULL,
|
||||
`client_steamid` varchar(64) NOT NULL,
|
||||
`time_stamp` int(10) unsigned NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
@@ -0,0 +1,33 @@
|
||||
"Games"
|
||||
{
|
||||
"cstrike"
|
||||
{
|
||||
"Signatures"
|
||||
{
|
||||
"CBasePlayer::InputSetFogController"
|
||||
{
|
||||
"library" "server"
|
||||
"windows" "\x55\x8B\xEC\x56\x8B\xF1\x8B\x4D\x08\x83\xC1\x08\x83\x79\x10\x02\x75\x2A\x8B\x01\xBA\x2A\x2A\x2A\x2A\x85\xC0\x0F\x45\xD0\xEB\x2A\xE8\x2A\x2A\x2A\x2A\x8B\xD0\x6A\x00"
|
||||
"linux" "@_ZN11CBasePlayer21InputSetFogControllerER11inputdata_t"
|
||||
}
|
||||
}
|
||||
|
||||
"Functions"
|
||||
{
|
||||
"CBasePlayer::InputSetFogController"
|
||||
{
|
||||
"signature" "CBasePlayer::InputSetFogController"
|
||||
"callconv" "thiscall"
|
||||
"return" "void"
|
||||
"this" "entity"
|
||||
"arguments"
|
||||
{
|
||||
"inputdata"
|
||||
{
|
||||
"type" "objectptr"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,278 @@
|
||||
#include <sourcemod>
|
||||
#include <sdktools>
|
||||
#include <dhooks>
|
||||
|
||||
public Plugin myinfo =
|
||||
{
|
||||
name = "Render Distance Limiter",
|
||||
author = "Dysphie, repurposed for unloze webclient using some AI",
|
||||
description = "",
|
||||
version = "",
|
||||
url = ""
|
||||
};
|
||||
|
||||
int g_iClientFogEntity[MAXPLAYERS + 1] = { INVALID_ENT_REFERENCE, ... };
|
||||
float g_OverrideDist[MAXPLAYERS + 1] = { -1.0, ... };
|
||||
bool bEnabled[MAXPLAYERS + 1];
|
||||
|
||||
float g_fClientEstimatedFPS[MAXPLAYERS + 1];
|
||||
|
||||
// New tracking arrays for your global variables section
|
||||
float g_fLastStableDist[MAXPLAYERS + 1] = { -1.0, ... };
|
||||
float g_fDistCooldown[MAXPLAYERS + 1] = { 0.0, ... };
|
||||
|
||||
int g_iLastCmdNum[MAXPLAYERS + 1];
|
||||
float g_fFPSIntervalStart[MAXPLAYERS + 1];
|
||||
int g_iCmdsInInterval[MAXPLAYERS + 1];
|
||||
|
||||
public void OnPluginStart()
|
||||
{
|
||||
SetupDetours();
|
||||
CreateTimer(0.5, Timer_printFPS, _, TIMER_REPEAT);
|
||||
}
|
||||
|
||||
//ai generated
|
||||
public Action OnPlayerRunCmd(int client, int &buttons, int &impulse, float vel[3], float angles[3], int &weapon, int &subtype, int &cmdnum, int &tickcount, int &seed, int mouse[2])
|
||||
{
|
||||
if (IsFakeClient(client) || !bEnabled[client])
|
||||
return Plugin_Continue;
|
||||
|
||||
// Check if this is a brand new user command execution frame
|
||||
if (cmdnum != g_iLastCmdNum[client])
|
||||
{
|
||||
g_iLastCmdNum[client] = cmdnum;
|
||||
g_iCmdsInInterval[client]++;
|
||||
}
|
||||
|
||||
float fCurrentTime = GetEngineTime();
|
||||
float fTimeElapsed = fCurrentTime - g_fFPSIntervalStart[client];
|
||||
|
||||
// Evaluate the true input frequency every 0.5 seconds
|
||||
if (fTimeElapsed >= 0.5)
|
||||
{
|
||||
if (g_fFPSIntervalStart[client] > 0.0)
|
||||
{
|
||||
// Raw calculated input framerate based on sequential command progression
|
||||
float fCalculatedFPS = float(g_iCmdsInInterval[client]) / fTimeElapsed;
|
||||
|
||||
// FAST-DROP BYPASS: If the client just lost more than 25% of their frame rate instantly
|
||||
// skip the dampening math and force the estimation down immediately.
|
||||
if (fCalculatedFPS < (g_fClientEstimatedFPS[client] * 0.75))
|
||||
{
|
||||
g_fClientEstimatedFPS[client] = fCalculatedFPS; // Hard-drop the value
|
||||
}
|
||||
else
|
||||
{
|
||||
// Smoothly recover/climb when frame rates are stable or increasing
|
||||
g_fClientEstimatedFPS[client] = (g_fClientEstimatedFPS[client] * 0.8) + (fCalculatedFPS * 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
// Reset interval buckets
|
||||
g_fFPSIntervalStart[client] = fCurrentTime;
|
||||
g_iCmdsInInterval[client] = 0;
|
||||
}
|
||||
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
//ai generated
|
||||
public Action Timer_printFPS(Handle timer)
|
||||
{
|
||||
for (int i = 1; i <= MaxClients; i++)
|
||||
{
|
||||
if (IsClientConnected(i) && IsClientInGame(i) && !IsFakeClient(i) && bEnabled[i])
|
||||
{
|
||||
float fCurrentFPS = g_fClientEstimatedFPS[i] - 4.0;
|
||||
float currentDist = g_OverrideDist[i];
|
||||
float targetDist = currentDist;
|
||||
|
||||
// 1. HARD CRASH: FPS has tanked down to unplayable levels.
|
||||
if (fCurrentFPS <= 25.0)
|
||||
{
|
||||
// If we have a cached, proven stable distance, use it immediately!
|
||||
if (g_fLastStableDist[i] != -1.0 && g_fLastStableDist[i] < currentDist)
|
||||
{
|
||||
targetDist = g_fLastStableDist[i];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Emergency fallback steps if we dont have a cached history yet
|
||||
if (currentDist == -1.0 || currentDist > 1200.0)
|
||||
{
|
||||
targetDist = 1200.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
targetDist = 600.0;
|
||||
}
|
||||
}
|
||||
|
||||
// Penalize the cooldown heavily because we just choked on rendering
|
||||
g_fDistCooldown[i] = GetEngineTime() + 15.0;
|
||||
}
|
||||
|
||||
// 2. WARNING ZONE: Framerate is beginning to dip
|
||||
else if (fCurrentFPS <= 38.0)
|
||||
{
|
||||
if (currentDist == -1.0 || currentDist > 2000.0)
|
||||
{
|
||||
targetDist = 2000.0;
|
||||
g_fDistCooldown[i] = GetEngineTime() + 10.0;
|
||||
}
|
||||
}
|
||||
|
||||
// 3. RECOVERY ZONE: Performance is solid. Lets see if we can open up the view.
|
||||
else if (fCurrentFPS >= 48.0)
|
||||
{
|
||||
if (currentDist != -1.0)
|
||||
{
|
||||
// Since the game is running flawlessly at this specific distance,
|
||||
// we update our safety cache to remember this specific float.
|
||||
g_fLastStableDist[i] = currentDist;
|
||||
|
||||
// Only venture out if our time barrier has expired
|
||||
if (GetEngineTime() >= g_fDistCooldown[i])
|
||||
{
|
||||
if (currentDist == 600.0) targetDist = 1200.0;
|
||||
else if (currentDist == 1200.0) targetDist = 2000.0;
|
||||
else if (currentDist == 2000.0) targetDist = 4000.0;
|
||||
else if (currentDist == 4000.0) targetDist = -1.0;
|
||||
|
||||
// Give the client a test window to sample the new distance
|
||||
g_fDistCooldown[i] = GetEngineTime() + 8.0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the client is running fine at infinite distance (-1.0),
|
||||
// clear the history out so we can recalculate from a clean slate later
|
||||
g_fLastStableDist[i] = -1.0;
|
||||
}
|
||||
}
|
||||
|
||||
if (targetDist != currentDist)
|
||||
{
|
||||
ApplyFogValues(i, targetDist);
|
||||
}
|
||||
}
|
||||
}
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
void ApplyFogValues(int client, float dist)
|
||||
{
|
||||
g_OverrideDist[client] = dist;
|
||||
int fog = EntRefToEntIndex(g_iClientFogEntity[client]);
|
||||
|
||||
// Create the persistent entity once if it vanished or hasnt been created yet
|
||||
if (fog == -1 || !IsValidEntity(fog))
|
||||
{
|
||||
fog = CreateEntityByName("env_fog_controller");
|
||||
if (fog != -1)
|
||||
{
|
||||
DispatchSpawn(fog);
|
||||
g_iClientFogEntity[client] = EntIndexToEntRef(fog);
|
||||
}
|
||||
}
|
||||
|
||||
if (fog != -1 && IsValidEntity(fog))
|
||||
{
|
||||
if (dist == -1.0)
|
||||
{
|
||||
// Back to map defaults safely
|
||||
SetEntProp(fog, Prop_Send, "m_fog.enable", 0);
|
||||
SetEntPropFloat(fog, Prop_Send, "m_fog.farz", 0.0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Apply strict culling boundaries explicitly
|
||||
SetEntProp(fog, Prop_Send, "m_fog.enable", 1);
|
||||
SetEntPropFloat(fog, Prop_Send, "m_fog.farz", dist);
|
||||
SetEntPropFloat(fog, Prop_Send, "m_fog.end", dist);
|
||||
SetEntPropFloat(fog, Prop_Send, "m_fog.maxdensity", 1.0);
|
||||
}
|
||||
|
||||
// Keep the entity assigned cleanly inside data tables
|
||||
SetFogController(client, fog);
|
||||
|
||||
// Single fire state changes to replicate out smoothly
|
||||
ChangeEdictState(client, FindSendPropInfo("CBasePlayer", "m_hCtrl"));
|
||||
}
|
||||
}
|
||||
|
||||
public void OnClientDisconnect(int client)
|
||||
{
|
||||
// Clean up entity allocation to prevent lingering map errors
|
||||
int fog = EntRefToEntIndex(g_iClientFogEntity[client]);
|
||||
if (fog != -1 && IsValidEntity(fog))
|
||||
{
|
||||
RemoveEntity(fog);
|
||||
}
|
||||
|
||||
g_iClientFogEntity[client] = INVALID_ENT_REFERENCE;
|
||||
g_OverrideDist[client] = -1.0;
|
||||
bEnabled[client] = false;
|
||||
}
|
||||
|
||||
void SetupDetours()
|
||||
{
|
||||
GameData gamedata = new GameData("player-fog.games");
|
||||
if (!gamedata) {
|
||||
SetFailState("Missing gamedata file");
|
||||
}
|
||||
|
||||
DynamicDetour detour = DynamicDetour.FromConf(gamedata, "CBasePlayer::InputSetFogController");
|
||||
if (!detour)
|
||||
SetFailState("Failed to find signature CBasePlayer::InputSetFogController");
|
||||
detour.Enable(Hook_Pre, Detour_InputSetFogController);
|
||||
delete detour;
|
||||
}
|
||||
|
||||
MRESReturn Detour_InputSetFogController(int client, DHookParam params)
|
||||
{
|
||||
RequestFrame(Frame_FogControllerChanged, GetClientSerial(client));
|
||||
return MRES_Ignored;
|
||||
}
|
||||
|
||||
void FogControllerChanged(int client)
|
||||
{
|
||||
if (g_OverrideDist[client] != -1.0) {
|
||||
ApplyFogValues(client, g_OverrideDist[client]);
|
||||
}
|
||||
}
|
||||
|
||||
void Frame_FogControllerChanged(int clientSerial)
|
||||
{
|
||||
int client = GetClientFromSerial(clientSerial);
|
||||
if (client && IsClientInGame(client))
|
||||
{
|
||||
FogControllerChanged(client);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnClientAuthorized(int client, const char[] auth)
|
||||
{
|
||||
bEnabled[client] = false;
|
||||
g_OverrideDist[client] = -1.0;
|
||||
g_iClientFogEntity[client] = INVALID_ENT_REFERENCE;
|
||||
|
||||
char sIP[32];
|
||||
GetClientIP(client, sIP, sizeof(sIP));
|
||||
|
||||
char allowed_ips[128];
|
||||
ConVar sv_set_steam_id_ips = FindConVar("sv_set_steam_id_ips");
|
||||
if (sv_set_steam_id_ips != null)
|
||||
{
|
||||
sv_set_steam_id_ips.GetString(allowed_ips, sizeof(allowed_ips));
|
||||
if (StrEqual(sIP, allowed_ips))
|
||||
{
|
||||
bEnabled[client] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SetFogController(int client, int fog)
|
||||
{
|
||||
SetEntPropEnt(client, Prop_Data, "m_hCtrl", fog);
|
||||
}
|
||||
@@ -20,7 +20,7 @@ methodmap AsyncSocket < Handle {
|
||||
|
||||
public bool WriteNull(const char[] data)
|
||||
{
|
||||
this.Write(data, strlen(data) + 1);
|
||||
return this.Write(data, strlen(data) + 1);
|
||||
}
|
||||
|
||||
public native bool SetConnectCallback(AsyncSocketConnectCallback callback);
|
||||
|
||||
@@ -1052,9 +1052,9 @@ methodmap JSONRootNode < JSONValue {
|
||||
return view_as<JSONRootNode>(json_load_file(path));
|
||||
}
|
||||
|
||||
public static JSONRootNode Pack(const char[] packString, ArrayList params) {
|
||||
return JSONValue_ByPack(packString, params);
|
||||
}
|
||||
public static JSONRootNode Pack(const char[] packString, ArrayList params) {
|
||||
return view_as<JSONRootNode>(JSONValue_ByPack(packString, params));
|
||||
}
|
||||
|
||||
public void DumpToServer() {
|
||||
char sMsg[4096];
|
||||
|
||||
@@ -78,6 +78,11 @@ forward void OnMapVoteStart();
|
||||
*/
|
||||
forward void OnMapVoteEnd(const char[] map);
|
||||
|
||||
/**
|
||||
just send the steamID, vote choice and voteweight of the client as a forward
|
||||
**/
|
||||
|
||||
forward void OnMapVoteClientResult(char[] steamID, char[] vote_choice, int voteweight);
|
||||
/**
|
||||
* Is a map on the current game's official list?
|
||||
* This should be treated as informative only.
|
||||
@@ -85,6 +90,7 @@ forward void OnMapVoteEnd(const char[] map);
|
||||
* @param map Name of map to check
|
||||
* @return true if it's on the list of official maps for this game
|
||||
*/
|
||||
|
||||
native bool IsMapOfficial(const char[] map);
|
||||
|
||||
/**
|
||||
@@ -108,13 +114,23 @@ native CanNominateResult CanNominate();
|
||||
native bool ExcludeMap(const char[] map, int cooldown = 0, int mode = 0);
|
||||
// Cooldown in seconds
|
||||
native bool ExcludeMapTime(const char[] map, int cooldown = 0, int mode = 0);
|
||||
// cooldown based on mysql table rows listing
|
||||
native bool ExcludeMapListingPriority(const char[] map, int index);
|
||||
|
||||
native int GetMapCooldown(const char[] map);
|
||||
native int GetMapCooldownTime(const char[] map); // in unix time
|
||||
native int GetMapCooldownTime2(const char[] map); // in unix time
|
||||
native int GetMapLowPopHelpCount(const char[] map);
|
||||
native int GetMapLaserFag(const char[] map);
|
||||
native int GetMapTrails(const char[] map);
|
||||
native int GetMapTickRate(const char[] map);
|
||||
native int GetMapLagCompensationDisabled(const char[] map);
|
||||
native int GetMapMinTime(const char[] map);
|
||||
native int GetMapMaxTime(const char[] map);
|
||||
native int GetMapMinPlayers(const char[] map);
|
||||
native int GetMapMaxPlayers(const char[] map);
|
||||
native int GetMapMinHoursAvg(const char[] map);
|
||||
native int GetExcludedMapListingPriority(const char[] map);
|
||||
|
||||
// 0 = Okay
|
||||
// >0 = Minutes till Okay
|
||||
|
||||
@@ -0,0 +1,195 @@
|
||||
#pragma semicolon 1
|
||||
#define PLUGIN_AUTHOR "jenz"
|
||||
#define PLUGIN_VERSION "1.0"
|
||||
#include <sourcemod>
|
||||
#include <cstrike>
|
||||
#include <mapchooser_extended>
|
||||
|
||||
int g_iPlayerCount_time_track;
|
||||
int g_iTopMapsToMarkWithCooldown;
|
||||
int g_iUnmarkCooldowns;
|
||||
int g_iCurrentPlayerCount;
|
||||
int g_iMapStartTime;
|
||||
Database g_hDatabase;
|
||||
|
||||
public Plugin myinfo =
|
||||
{
|
||||
name = "map_tracker_cooldown",
|
||||
author = PLUGIN_AUTHOR,
|
||||
description = "assigns cooldowns on maps and tracks their played time on the server",
|
||||
version = PLUGIN_VERSION,
|
||||
url = "www.unloze.com"
|
||||
};
|
||||
|
||||
public void OnPluginStart()
|
||||
{
|
||||
if (!g_hDatabase)
|
||||
{
|
||||
Database.Connect(SQL_OnDatabaseConnect, "unloze_playtimestats");
|
||||
}
|
||||
|
||||
ConVar cvar;
|
||||
HookConVarChange((cvar = CreateConVar("mce_min_players_track_time", "20", "Playercount in full number required for tracking minutes spend on a map.")), Cvar_playersCount);
|
||||
g_iPlayerCount_time_track = cvar.IntValue;
|
||||
delete cvar;
|
||||
|
||||
ConVar cvar1;
|
||||
HookConVarChange((cvar1 = CreateConVar("mce_top_ordered_maps_on_cooldown", "30", "These highest listed maps will receive the cooldown boolean")), Cvar_TopMarkedWithCooldown);
|
||||
g_iTopMapsToMarkWithCooldown = cvar1.IntValue;
|
||||
delete cvar1;
|
||||
|
||||
ConVar cvar2;
|
||||
HookConVarChange((cvar2 = CreateConVar("mce_UnmarkCooldowns", "120", "How far down the list does a map need to be for getting unmarked again")), Cvar_UnmarkedCooldown);
|
||||
g_iUnmarkCooldowns = cvar2.IntValue;
|
||||
delete cvar2;
|
||||
|
||||
g_iCurrentPlayerCount = GetClientCount(false);
|
||||
CreateTimer(30.0, CheckPlayerCount, _, TIMER_REPEAT);
|
||||
}
|
||||
|
||||
public Action CheckPlayerCount(Handle timer)
|
||||
{
|
||||
g_iCurrentPlayerCount = GetClientCount(false);
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
public void Cvar_playersCount(ConVar convar, const char[] oldValue, const char[] newValue)
|
||||
{
|
||||
g_iPlayerCount_time_track = convar.IntValue;
|
||||
}
|
||||
|
||||
public void Cvar_TopMarkedWithCooldown(ConVar convar, const char[] oldValue, const char[] newValue)
|
||||
{
|
||||
g_iTopMapsToMarkWithCooldown = convar.IntValue;
|
||||
}
|
||||
|
||||
public void Cvar_UnmarkedCooldown(ConVar convar, const char[] oldValue, const char[] newValue)
|
||||
{
|
||||
g_iUnmarkCooldowns = convar.IntValue;
|
||||
}
|
||||
|
||||
public void SQL_OnDatabaseConnect(Database db, const char[] error, any data)
|
||||
{
|
||||
if(!db || strlen(error))
|
||||
{
|
||||
LogError("Database error: %s", error);
|
||||
return;
|
||||
}
|
||||
g_hDatabase = db;
|
||||
UpdateTopListedMapsWithCooldown();
|
||||
}
|
||||
|
||||
public void OnMapStart()
|
||||
{
|
||||
int i_port = GetConVarInt(FindConVar("hostport"));
|
||||
if (i_port != 27019 && i_port != 27015)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
g_iMapStartTime = GetTime();
|
||||
if (!g_hDatabase)
|
||||
{
|
||||
Database.Connect(SQL_OnDatabaseConnect, "unloze_playtimestats");
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateTopListedMapsWithCooldown();
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateTopListedMapsWithCooldown()
|
||||
{
|
||||
//just set every map in top g_iTopMapsToMarkWithCooldown on cooldown
|
||||
char sQuery[512];
|
||||
Format(sQuery, sizeof(sQuery), "update unloze_playtimestats.map_minutes_played set on_cooldown = true where mapname in ( SELECT mapname FROM (select mmp.mapname from unloze_playtimestats.map_minutes_played mmp order by mmp.minutes desc limit %i) as top) and on_cooldown is false", g_iTopMapsToMarkWithCooldown);
|
||||
g_hDatabase.Query(SQL_FinishedQuery1, sQuery, _, DBPrio_High);
|
||||
}
|
||||
|
||||
public void SQL_FinishedQuery1(Database db, DBResultSet results, const char[] error, DataPack data)
|
||||
{
|
||||
delete results;
|
||||
if (!db || strlen(error))
|
||||
{
|
||||
LogError("Query error: %s", error);
|
||||
}
|
||||
delete data;
|
||||
UpdateUnmarkCooldown();
|
||||
}
|
||||
|
||||
public void UpdateUnmarkCooldown()
|
||||
{
|
||||
//if map in listing fell below g_iUnmarkCooldowns then set the bool to false again.
|
||||
char sQuery[512];
|
||||
Format(sQuery, sizeof(sQuery), "update unloze_playtimestats.map_minutes_played set on_cooldown = false where mapname not in ( SELECT mapname FROM (select mmp.mapname from unloze_playtimestats.map_minutes_played mmp order by mmp.minutes desc limit %i) as top) and on_cooldown is true", g_iUnmarkCooldowns);
|
||||
g_hDatabase.Query(SQL_FinishedQuery2, sQuery, _, DBPrio_High);
|
||||
}
|
||||
|
||||
public void SQL_FinishedQuery2(Database db, DBResultSet results, const char[] error, DataPack data)
|
||||
{
|
||||
delete results;
|
||||
if (!db || strlen(error))
|
||||
{
|
||||
LogError("Query error: %s", error);
|
||||
}
|
||||
delete data;
|
||||
SelectMapsToCooldownForMapChooser();
|
||||
}
|
||||
|
||||
public void SelectMapsToCooldownForMapChooser()
|
||||
{
|
||||
char sQuery[512];
|
||||
Format(sQuery, sizeof(sQuery), "select mapname, map_list_index from (select mapname, ROW_NUMBER() OVER(ORDER BY minutes DESC) as map_list_index, mmp.on_cooldown from unloze_playtimestats.map_minutes_played mmp) as targets where on_cooldown");
|
||||
g_hDatabase.Query(SQL_FinishedQuerySelectMaps, sQuery, _, DBPrio_High);
|
||||
}
|
||||
|
||||
public void SQL_FinishedQuerySelectMaps(Database db, DBResultSet results, const char[] error, DataPack data)
|
||||
{
|
||||
if (!db || strlen(error))
|
||||
{
|
||||
LogError("Query error in SQL_FinishedQuerySelectMaps: %s", error);
|
||||
delete results;
|
||||
return;
|
||||
}
|
||||
|
||||
while (results.RowCount && results.FetchRow())
|
||||
{
|
||||
char mapname[256];
|
||||
results.FetchString(0, mapname, sizeof(mapname));
|
||||
int map_list_index = results.FetchInt(1);
|
||||
ExcludeMapListingPriority(mapname, g_iUnmarkCooldowns - map_list_index);
|
||||
}
|
||||
delete results;
|
||||
}
|
||||
|
||||
public void OnMapEnd()
|
||||
{
|
||||
int i_port = GetConVarInt(FindConVar("hostport"));
|
||||
if (i_port != 27019 && i_port != 27015)
|
||||
{
|
||||
return;
|
||||
}
|
||||
int elapsed = GetTime() - g_iMapStartTime;
|
||||
int minutes = RoundToFloor(elapsed / 60.0);
|
||||
|
||||
char Mapname[128];
|
||||
GetCurrentMap(Mapname, sizeof(Mapname));
|
||||
//LogMessage("minutes: %i. map: %s", minutes, Mapname);
|
||||
|
||||
if (g_iCurrentPlayerCount > g_iPlayerCount_time_track)
|
||||
{
|
||||
char sQuery[512];
|
||||
Format(sQuery, sizeof(sQuery), "INSERT INTO `map_minutes_played` (`mapname`, `minutes`, `updated_on`) VALUES ('%s', '%i', NOW()) ON DUPLICATE KEY UPDATE `minutes` = `minutes` + '%i', `updated_on` = NOW()", Mapname, minutes, minutes);
|
||||
g_hDatabase.Query(SQL_FinishedQuery, sQuery, _, DBPrio_Low);
|
||||
}
|
||||
}
|
||||
|
||||
public void SQL_FinishedQuery(Database db, DBResultSet results, const char[] error, DataPack data)
|
||||
{
|
||||
delete results;
|
||||
if (!db || strlen(error))
|
||||
{
|
||||
LogError("Query error: %s", error);
|
||||
}
|
||||
delete data;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
+689
-351
File diff suppressed because it is too large
Load Diff
@@ -54,9 +54,6 @@ public Plugin myinfo =
|
||||
url = "https://forums.alliedmods.net/showthread.php?t=156974"
|
||||
};
|
||||
|
||||
Handle g_Cvar_ExcludeOld = INVALID_HANDLE;
|
||||
Handle g_Cvar_ExcludeCurrent = INVALID_HANDLE;
|
||||
|
||||
Handle g_MapList = INVALID_HANDLE;
|
||||
Handle g_AdminMapList = INVALID_HANDLE;
|
||||
Menu g_MapMenu;
|
||||
@@ -64,14 +61,6 @@ Menu g_AdminMapMenu;
|
||||
int g_mapFileSerial = -1;
|
||||
int g_AdminMapFileSerial = -1;
|
||||
|
||||
#define MAPSTATUS_ENABLED (1<<0)
|
||||
#define MAPSTATUS_DISABLED (1<<1)
|
||||
#define MAPSTATUS_EXCLUDE_CURRENT (1<<2)
|
||||
#define MAPSTATUS_EXCLUDE_PREVIOUS (1<<3)
|
||||
#define MAPSTATUS_EXCLUDE_NOMINATED (1<<4)
|
||||
|
||||
Handle g_mapTrie;
|
||||
|
||||
// Nominations Extended Convars
|
||||
Handle g_Cvar_MarkCustomMaps = INVALID_HANDLE;
|
||||
Handle g_Cvar_NominateDelay = INVALID_HANDLE;
|
||||
@@ -82,6 +71,9 @@ Handle g_Cvar_VIPTimeframe = INVALID_HANDLE;
|
||||
Handle g_Cvar_VIPTimeframeMinTime = INVALID_HANDLE;
|
||||
Handle g_Cvar_VIPTimeframeMaxTime = INVALID_HANDLE;
|
||||
|
||||
KeyValues g_Config;
|
||||
|
||||
|
||||
int g_Player_NominationDelay[MAXPLAYERS+1];
|
||||
int g_NominationDelay;
|
||||
|
||||
@@ -100,8 +92,6 @@ public void OnPluginStart()
|
||||
g_MapList = CreateArray(arraySize);
|
||||
g_AdminMapList = CreateArray(arraySize);
|
||||
|
||||
g_Cvar_ExcludeOld = CreateConVar("sm_nominate_excludeold", "1", "Specifies if the current map should be excluded from the Nominations list", 0, true, 0.00, true, 1.0);
|
||||
g_Cvar_ExcludeCurrent = CreateConVar("sm_nominate_excludecurrent", "1", "Specifies if the MapChooser excluded maps should also be excluded from Nominations", 0, true, 0.00, true, 1.0);
|
||||
g_Cvar_InitialDelay = CreateConVar("sm_nominate_initialdelay", "60.0", "Time in seconds before first Nomination can be made", 0, true, 0.00);
|
||||
g_Cvar_NominateDelay = CreateConVar("sm_nominate_delay", "3.0", "Delay between nominations", 0, true, 0.00, true, 60.00);
|
||||
|
||||
@@ -125,8 +115,6 @@ public void OnPluginStart()
|
||||
// Nominations Extended cvars
|
||||
CreateConVar("ne_version", MCE_VERSION, "Nominations Extended Version", FCVAR_SPONLY|FCVAR_NOTIFY|FCVAR_DONTRECORD);
|
||||
|
||||
g_mapTrie = CreateTrie();
|
||||
|
||||
//DB
|
||||
if (!g_dDatabase)
|
||||
{
|
||||
@@ -142,6 +130,26 @@ public void OnMapStart()
|
||||
//we have too many dbs so i am just re-using racetimercss
|
||||
Database.Connect(SQL_OnDatabaseConnect, "racetimercss");
|
||||
}
|
||||
|
||||
if(g_Config)
|
||||
delete g_Config;
|
||||
|
||||
char sConfigFile[PLATFORM_MAX_PATH];
|
||||
BuildPath(Path_SM, sConfigFile, sizeof(sConfigFile), "configs/mapchooser_extended.cfg");
|
||||
if(!FileExists(sConfigFile))
|
||||
{
|
||||
LogMessage("Could not find config: \"%s\"", sConfigFile);
|
||||
return;
|
||||
}
|
||||
|
||||
g_Config = new KeyValues("mapchooser_extended");
|
||||
if(!g_Config.ImportFromFile(sConfigFile))
|
||||
{
|
||||
delete g_Config;
|
||||
LogMessage("ImportFromFile() failed!");
|
||||
return;
|
||||
}
|
||||
g_Config.Rewind();
|
||||
}
|
||||
|
||||
public void SQL_OnDatabaseConnect(Database db, const char[] error, any data)
|
||||
@@ -310,7 +318,6 @@ public void OnConfigsExecuted()
|
||||
|
||||
g_NominationDelay = GetTime() + GetConVarInt(g_Cvar_InitialDelay);
|
||||
|
||||
UpdateMapTrie();
|
||||
UpdateMapMenus();
|
||||
}
|
||||
|
||||
@@ -320,61 +327,6 @@ void UpdateMapMenus()
|
||||
g_AdminMapMenu = BuildAdminMapMenu("");
|
||||
}
|
||||
|
||||
void UpdateMapTrie()
|
||||
{
|
||||
static char map[PLATFORM_MAX_PATH];
|
||||
static char currentMap[PLATFORM_MAX_PATH];
|
||||
ArrayList excludeMaps;
|
||||
|
||||
if(GetConVarBool(g_Cvar_ExcludeOld))
|
||||
{
|
||||
excludeMaps = CreateArray(ByteCountToCells(PLATFORM_MAX_PATH));
|
||||
GetExcludeMapList(excludeMaps);
|
||||
}
|
||||
|
||||
if(GetConVarBool(g_Cvar_ExcludeCurrent))
|
||||
GetCurrentMap(currentMap, sizeof(currentMap));
|
||||
|
||||
ClearTrie(g_mapTrie);
|
||||
|
||||
for(int i = 0; i < GetArraySize(g_MapList); i++)
|
||||
{
|
||||
int status = MAPSTATUS_ENABLED;
|
||||
|
||||
GetArrayString(g_MapList, i, map, sizeof(map));
|
||||
|
||||
if(GetConVarBool(g_Cvar_ExcludeCurrent))
|
||||
{
|
||||
if(StrEqual(map, currentMap))
|
||||
status = MAPSTATUS_DISABLED|MAPSTATUS_EXCLUDE_CURRENT;
|
||||
}
|
||||
|
||||
/* Dont bother with this check if the current map check passed */
|
||||
if(GetConVarBool(g_Cvar_ExcludeOld) && status == MAPSTATUS_ENABLED)
|
||||
{
|
||||
if(FindStringInArray(excludeMaps, map) != -1)
|
||||
status = MAPSTATUS_DISABLED|MAPSTATUS_EXCLUDE_PREVIOUS;
|
||||
}
|
||||
|
||||
SetTrieValue(g_mapTrie, map, status);
|
||||
}
|
||||
|
||||
if(excludeMaps)
|
||||
delete excludeMaps;
|
||||
}
|
||||
|
||||
public void OnNominationRemoved(const char[] map, int client)
|
||||
{
|
||||
int status;
|
||||
/* Is the map in our list? */
|
||||
if(!GetTrieValue(g_mapTrie, map, status))
|
||||
return;
|
||||
/* Was the map disabled due to being nominated */
|
||||
if((status & MAPSTATUS_EXCLUDE_NOMINATED) != MAPSTATUS_EXCLUDE_NOMINATED)
|
||||
return;
|
||||
SetTrieValue(g_mapTrie, map, MAPSTATUS_ENABLED);
|
||||
}
|
||||
|
||||
public Action Command_Addmap(int client, int args)
|
||||
{
|
||||
if(args == 0)
|
||||
@@ -398,69 +350,6 @@ public Action Command_Addmap(int client, int args)
|
||||
AttemptAdminNominate(client, mapname);
|
||||
return Plugin_Handled;
|
||||
}
|
||||
/*
|
||||
int Cooldown1 = GetMapCooldownTime(mapname);
|
||||
if(Cooldown1 > GetTime())
|
||||
{
|
||||
int Seconds = Cooldown1 - GetTime();
|
||||
CPrintToChat(client, "[NE] %t", "Map Cooldown Time Error", Seconds / 3600, (Seconds % 3600) / 60);
|
||||
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
if(!CheckCommandAccess(client, "sm_nominate_ignore", ADMFLAG_KICK, true) )
|
||||
{
|
||||
bool RestrictionsActive = AreRestrictionsActive();
|
||||
|
||||
int status;
|
||||
if(GetTrieValue(g_mapTrie, mapname, status))
|
||||
{
|
||||
if((status & MAPSTATUS_DISABLED) == MAPSTATUS_DISABLED)
|
||||
{
|
||||
if((status & MAPSTATUS_EXCLUDE_CURRENT) == MAPSTATUS_EXCLUDE_CURRENT)
|
||||
CPrintToChat(client, "[NE] %t", "Cant Nominate Current Map");
|
||||
|
||||
if(RestrictionsActive && (status & MAPSTATUS_EXCLUDE_PREVIOUS) == MAPSTATUS_EXCLUDE_PREVIOUS)
|
||||
{
|
||||
int Cooldown = GetMapCooldown(mapname);
|
||||
CPrintToChat(client, "[NE] %t (%d)", "Map in Exclude List", Cooldown);
|
||||
}
|
||||
|
||||
if((status & MAPSTATUS_EXCLUDE_NOMINATED) == MAPSTATUS_EXCLUDE_NOMINATED)
|
||||
CPrintToChat(client, "[NE] %t", "Map Already Nominated");
|
||||
|
||||
return Plugin_Handled;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int TimeRestriction = GetMapTimeRestriction(mapname);
|
||||
if(RestrictionsActive && TimeRestriction)
|
||||
{
|
||||
CPrintToChat(client, "[NE] %t", "Map Nominate Time Error", TimeRestriction / 60, TimeRestriction % 60);
|
||||
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
int AverageHourRestricted = GetAveragePlayerTimeOnServerMapRestriction(mapname);
|
||||
if (RestrictionsActive && AverageHourRestricted > 0)
|
||||
{
|
||||
PrintToChat(client, "%s requires +%i hours average. Use sm_houravg to check average.", mapname, AverageHourRestricted);
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
int PlayerRestriction = GetMapPlayerRestriction(mapname);
|
||||
if(RestrictionsActive && PlayerRestriction)
|
||||
{
|
||||
if(PlayerRestriction < 0)
|
||||
CPrintToChat(client, "[NE] %t", "Map Nominate MinPlayers Error", PlayerRestriction * -1);
|
||||
else
|
||||
CPrintToChat(client, "[NE] %t", "Map Nominate MaxPlayers Error", PlayerRestriction);
|
||||
|
||||
return Plugin_Handled;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
NominateResult result = NominateMap(mapname, true, 0);
|
||||
|
||||
@@ -472,8 +361,6 @@ public Action Command_Addmap(int client, int args)
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
SetTrieValue(g_mapTrie, mapname, MAPSTATUS_DISABLED|MAPSTATUS_EXCLUDE_NOMINATED);
|
||||
|
||||
CReplyToCommand(client, "%t", "Map Inserted", mapname);
|
||||
LogAction(client, -1, "\"%L\" inserted map \"%s\".", client, mapname);
|
||||
|
||||
@@ -548,18 +435,9 @@ public Action Command_AddExclude(int client, int args)
|
||||
mode = StringToInt(buffer);
|
||||
}
|
||||
|
||||
int status;
|
||||
if(!GetTrieValue(g_mapTrie, mapname, status))
|
||||
{
|
||||
ReplyToCommand(client, "[NE] %t", "Map was not found", mapname);
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
ShowActivity(client, "Excluded map \"%s\" from nomination", mapname);
|
||||
LogAction(client, -1, "\"%L\" excluded map \"%s\" from nomination", client, mapname);
|
||||
|
||||
SetTrieValue(g_mapTrie, mapname, MAPSTATUS_DISABLED|MAPSTATUS_EXCLUDE_PREVIOUS);
|
||||
|
||||
// native call to mapchooser_extended
|
||||
ExcludeMap(mapname, cooldown, mode);
|
||||
|
||||
@@ -592,8 +470,7 @@ public Action Command_AddExcludeTime(int client, int args)
|
||||
mode = StringToInt(buffer);
|
||||
}
|
||||
|
||||
int status;
|
||||
if(!GetTrieValue(g_mapTrie, mapname, status))
|
||||
if (FindStringInArray(g_MapList, mapname) == -1)
|
||||
{
|
||||
ReplyToCommand(client, "[NE] %t", "Map was not found", mapname);
|
||||
return Plugin_Handled;
|
||||
@@ -668,48 +545,36 @@ public Action Command_Nominate(int client, int args)
|
||||
static char mapname[PLATFORM_MAX_PATH];
|
||||
GetCmdArg(1, mapname, sizeof(mapname));
|
||||
|
||||
int status;
|
||||
if(!GetTrieValue(g_mapTrie, mapname, status))
|
||||
if (FindStringInArray(g_MapList, mapname) == -1)
|
||||
{
|
||||
//CPrintToChat(client, "%t", "Map was not found", mapname);
|
||||
ReplyToCommand(client, "Map was not found: %s", mapname);
|
||||
AttemptNominate(client, mapname);
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
int Cooldown1 = GetMapCooldownTime(mapname);
|
||||
if(Cooldown1 > GetTime())
|
||||
//yeah admins and leaders cant bypass these restrictions, maybe the admins really are the problem sometimes for skipping restrictions.
|
||||
int Index_Cooldown = GetExcludedMapListingPriority(mapname);
|
||||
if (Index_Cooldown > 0)
|
||||
{
|
||||
int Seconds = Cooldown1 - GetTime();
|
||||
CPrintToChat(client, "[NE] %t", "Map Cooldown Time Error", Seconds / 3600, (Seconds % 3600) / 60);
|
||||
|
||||
CPrintToChat(client, "[NE] Map is on Cooldown for being overplayed. List Posistion: %i", Index_Cooldown);
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
//July 2024 edit: any person who is potential leader can just skip all cooldowns and map restrictions. same for admins.
|
||||
if(!CheckCommandAccess(client, "sm_nominate_ignore", ADMFLAG_KICK, true) && !Leader_Is(client))
|
||||
//October 2026: admins can just skip all cooldowns and map restrictions
|
||||
if(!CheckCommandAccess(client, "sm_nominate_ignore", ADMFLAG_KICK, true) /* && !Leader_Is(client)*/ )
|
||||
{
|
||||
bool RestrictionsActive = AreRestrictionsActive();
|
||||
|
||||
if((status & MAPSTATUS_DISABLED) == MAPSTATUS_DISABLED)
|
||||
//restrictions not being active now also skips all the cooldowns.
|
||||
int Cooldown1 = GetMapCooldownTime(mapname);
|
||||
if(RestrictionsActive && Cooldown1 > GetTime())
|
||||
{
|
||||
if((status & MAPSTATUS_EXCLUDE_CURRENT) == MAPSTATUS_EXCLUDE_CURRENT)
|
||||
ReplyToCommand(client, "Cant Nominate Current Map.");
|
||||
//CPrintToChat(client, "[NE] %t", "Cant Nominate Current Map");
|
||||
|
||||
if(RestrictionsActive && (status & MAPSTATUS_EXCLUDE_PREVIOUS) == MAPSTATUS_EXCLUDE_PREVIOUS)
|
||||
{
|
||||
int Cooldown = GetMapCooldown(mapname);
|
||||
CPrintToChat(client, "[NE] %t (%d)", "Map in Exclude List", Cooldown);
|
||||
}
|
||||
|
||||
if((status & MAPSTATUS_EXCLUDE_NOMINATED) == MAPSTATUS_EXCLUDE_NOMINATED)
|
||||
CPrintToChat(client, "[NE] %t", "Map Already Nominated");
|
||||
int Seconds = Cooldown1 - GetTime();
|
||||
CPrintToChat(client, "[NE] %t", "Map Cooldown Time Error", Seconds / 3600, (Seconds % 3600) / 60);
|
||||
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
|
||||
bool VIPRestriction = GetMapVIPRestriction(mapname, client);
|
||||
if(RestrictionsActive && VIPRestriction)
|
||||
{
|
||||
@@ -744,6 +609,32 @@ public Action Command_Nominate(int client, int args)
|
||||
return Plugin_Handled;
|
||||
}
|
||||
}
|
||||
|
||||
//2026 march 20th: if map already is admin or leader nominated we dont allow it being nominated again
|
||||
int arraySize = ByteCountToCells(PLATFORM_MAX_PATH);
|
||||
ArrayList MapList = CreateArray(arraySize);
|
||||
ArrayList OwnerList = CreateArray();
|
||||
GetNominatedMapList(MapList, OwnerList);
|
||||
|
||||
for (int j = 0; j < GetArraySize(MapList); j++)
|
||||
{
|
||||
char mapIterated[128];
|
||||
GetArrayString(MapList, j, mapIterated, sizeof(mapIterated));
|
||||
if (StrEqual(mapname, mapIterated))
|
||||
{
|
||||
int owner = GetArrayCell(OwnerList, j);
|
||||
if (!owner || Leader_Is(owner))
|
||||
{
|
||||
delete MapList;
|
||||
delete OwnerList;
|
||||
PrintToChat(client, "The map is already nominated by admin or leader.");
|
||||
return Plugin_Handled;
|
||||
}
|
||||
}
|
||||
}
|
||||
delete MapList;
|
||||
delete OwnerList;
|
||||
|
||||
NominateResult result = NominateMap(mapname, false, client);
|
||||
|
||||
if (result == Nominate_InvalidMap)
|
||||
@@ -762,21 +653,17 @@ public Action Command_Nominate(int client, int args)
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
/* Map was nominated! - Disable the menu item and update the trie */
|
||||
|
||||
//SetTrieValue(g_mapTrie, mapname, MAPSTATUS_DISABLED|MAPSTATUS_EXCLUDE_NOMINATED);
|
||||
|
||||
static char name[MAX_NAME_LENGTH];
|
||||
GetClientName(client, name, sizeof(name));
|
||||
|
||||
int nominate_worth = GetPlayerWorthRTV_boost_(client);
|
||||
if(result == Nominate_Added)
|
||||
{
|
||||
int nominate_worth = GetPlayerWorthRTV_boost_(client);
|
||||
PrintToChatAll("[NE] %t", "Map Nominated", name, mapname, nominate_worth);
|
||||
PrintToChatAll("[NE] %N has nominated %s (%i Nomination Boost).", client, mapname, nominate_worth);
|
||||
}
|
||||
else if(result == Nominate_Replaced)
|
||||
{
|
||||
PrintToChatAll("[NE] %t", "Map Nomination Changed", name, mapname);
|
||||
PrintToChatAll("[NE] %N has changed their nomination to %s (%i Nomination Boost).", client, mapname, nominate_worth);
|
||||
}
|
||||
|
||||
LogMessage("%s nominated %s", name, mapname);
|
||||
@@ -836,19 +723,45 @@ public int Handler_NominateListMenu(Menu menu, MenuAction action, int param1, in
|
||||
return 0;
|
||||
}
|
||||
|
||||
void SetNominateMenuTitle(Menu menu, int client)
|
||||
{
|
||||
int nominate_worth = GetPlayerWorthRTV_boost_(client);
|
||||
if (Leader_Is(client))
|
||||
SetMenuTitle(menu, "Nominate Map: (%i Nomination Boost Leader)", 999);
|
||||
else
|
||||
SetMenuTitle(menu, "Nominate Map: (%i Nomination Boost)", nominate_worth);
|
||||
}
|
||||
|
||||
void AttemptNominate(int client, const char[] filter = "")
|
||||
{
|
||||
if(!client)
|
||||
return;
|
||||
|
||||
Menu menu = g_MapMenu;
|
||||
menu = BuildMapMenu(filter, client);
|
||||
if (filter[0])
|
||||
{
|
||||
// preserves the existing behavior for the "map wasnt found, try a substring search" fallback
|
||||
menu = BuildMapMenu(filter, client);
|
||||
}
|
||||
else
|
||||
{
|
||||
menu = BuildCategoryMenu(client);
|
||||
}
|
||||
|
||||
int nominate_worth = GetPlayerWorthRTV_boost_(client);
|
||||
SetMenuTitle(menu, "%T", "Nominate Title", client, nominate_worth);
|
||||
SetNominateMenuTitle(menu, client);
|
||||
DisplayMenu(menu, client, MENU_TIME_FOREVER);
|
||||
}
|
||||
|
||||
public void GetMapCategory(const char[] mapname, char[] output_category, int maxlen)
|
||||
{
|
||||
strcopy(output_category, maxlen, "Uncategorized");
|
||||
if (g_Config && g_Config.JumpToKey(mapname))
|
||||
{
|
||||
g_Config.GetString("MapCategory", output_category, maxlen, "Uncategorized");
|
||||
g_Config.Rewind();
|
||||
}
|
||||
}
|
||||
|
||||
void AttemptAdminNominate(int client, const char[] filter = "")
|
||||
{
|
||||
if(!client)
|
||||
@@ -858,7 +771,7 @@ void AttemptAdminNominate(int client, const char[] filter = "")
|
||||
if(filter[0])
|
||||
menu = BuildAdminMapMenu(filter);
|
||||
|
||||
SetMenuTitle(menu, "%T", "Nominate Title", client, 999);
|
||||
SetMenuTitle(menu, "Nominate Map: (%i Nomination Boost Admin)", 999);
|
||||
DisplayMenu(menu, client, MENU_TIME_FOREVER);
|
||||
}
|
||||
|
||||
@@ -915,12 +828,12 @@ bool PopulateNominateListMenu(Menu menu, int client, const char[] filter = "")
|
||||
}
|
||||
|
||||
StringMap sm = new StringMap();
|
||||
StringMap sm_special_noms = new StringMap(); //storing leaders and admin noms.
|
||||
static char map[PLATFORM_MAX_PATH];
|
||||
static char display[PLATFORM_MAX_PATH];
|
||||
for(int i = 0; i < GetArraySize(MapList); i++)
|
||||
{
|
||||
GetArrayString(MapList, i, map, sizeof(map));
|
||||
|
||||
if(!filter[0] || StrContains(map, filter, false) != -1)
|
||||
{
|
||||
//admin nominations must have most votes shrug
|
||||
@@ -929,14 +842,15 @@ bool PopulateNominateListMenu(Menu menu, int client, const char[] filter = "")
|
||||
int nominate_count_for_particular_map = 0;
|
||||
sm.GetValue(map, nominate_count_for_particular_map);
|
||||
//if its console its admin nomination. if its nominated by leader it will also go to the vote just.
|
||||
if (!owner)
|
||||
if (!owner || Leader_Is(owner))
|
||||
{
|
||||
nominate_count_for_particular_map = 1999;
|
||||
sm_special_noms.SetValue(map, owner); //admin noms and leaders are unique per each map, so cant be overwritten.
|
||||
}
|
||||
else
|
||||
{
|
||||
int nominate_worth = GetPlayerWorthRTV_boost_(owner);
|
||||
//atm everybody just has equal voting again, so this is just += 1
|
||||
//2026 march: the boosting can now range from 1 to 2. so for most its +=1 and for a few +=2.
|
||||
nominate_count_for_particular_map += nominate_worth;
|
||||
}
|
||||
sm.SetValue(map, nominate_count_for_particular_map, true);
|
||||
@@ -962,22 +876,26 @@ bool PopulateNominateListMenu(Menu menu, int client, const char[] filter = "")
|
||||
{
|
||||
int nominate_count_for_particular_map = 0;
|
||||
sm.GetValue(map, nominate_count_for_particular_map);
|
||||
|
||||
int leader_or_admin_map_owner = -1;
|
||||
sm_special_noms.GetValue(map, leader_or_admin_map_owner);
|
||||
strcopy(display, sizeof(display), map);
|
||||
|
||||
bool VIPRestriction = GetMapVIPRestriction(map);
|
||||
if((VIPRestriction) && AreRestrictionsActive())
|
||||
Format(display, sizeof(display), "%s (%T)", display, "VIP Nomination", client);
|
||||
|
||||
int owner = GetArrayCell(OwnerList, i);
|
||||
char spelling[8];
|
||||
|
||||
Format(spelling, sizeof(spelling), nominate_count_for_particular_map == 1 ? "Vote" : "Votes");
|
||||
|
||||
if(!owner)
|
||||
Format(display, sizeof(display), "%s (Admin)", display);
|
||||
if (leader_or_admin_map_owner != -1)
|
||||
{
|
||||
if(!leader_or_admin_map_owner)
|
||||
Format(display, sizeof(display), "%s (Admin)", display);
|
||||
else
|
||||
Format(display, sizeof(display), "%s (Leader)", display);
|
||||
}
|
||||
else
|
||||
Format(display, sizeof(display), "%s (%i %s)", display, nominate_count_for_particular_map, spelling);
|
||||
|
||||
AddMenuItem(menu, map, display);
|
||||
}
|
||||
}
|
||||
@@ -986,15 +904,87 @@ bool PopulateNominateListMenu(Menu menu, int client, const char[] filter = "")
|
||||
delete OwnerList;
|
||||
delete SortedList;
|
||||
delete sm;
|
||||
delete sm_special_noms;
|
||||
delete sm_snapshot;
|
||||
return true;
|
||||
}
|
||||
|
||||
Menu BuildMapMenu(const char[] filter, int client)
|
||||
Menu BuildCategoryMenu(int client)
|
||||
{
|
||||
Menu menu = CreateMenu(Handler_CategorySelectMenu, MENU_ACTIONS_DEFAULT);
|
||||
|
||||
//2023 edit to allow hiding/unhiding unavailable maps
|
||||
char hiding_unavailable_maps[MAX_NAME_LENGTH];
|
||||
if (g_bClientsIgnoring[client])
|
||||
Format(hiding_unavailable_maps, sizeof(hiding_unavailable_maps), "Show all unavailable maps");
|
||||
else
|
||||
Format(hiding_unavailable_maps, sizeof(hiding_unavailable_maps), "Hide all unavailable maps");
|
||||
AddMenuItem(menu, hiding_unavailable_maps, hiding_unavailable_maps);
|
||||
|
||||
ArrayList categories = CreateArray(ByteCountToCells(64));
|
||||
static char map[PLATFORM_MAX_PATH];
|
||||
static char category[64];
|
||||
|
||||
for (int i = 0; i < GetArraySize(g_MapList); i++)
|
||||
{
|
||||
GetArrayString(g_MapList, i, map, sizeof(map));
|
||||
GetMapCategory(map, category, sizeof(category));
|
||||
|
||||
if (FindStringInArray(categories, category) == -1)
|
||||
PushArrayString(categories, category);
|
||||
}
|
||||
|
||||
SortADTArray(categories, Sort_Ascending, Sort_String);
|
||||
|
||||
for (int i = 0; i < GetArraySize(categories); i++)
|
||||
{
|
||||
GetArrayString(categories, i, category, sizeof(category));
|
||||
AddMenuItem(menu, category, category);
|
||||
}
|
||||
|
||||
AddMenuItem(menu, "Full Map list", "Full Map list");
|
||||
delete categories;
|
||||
|
||||
SetMenuExitButton(menu, true);
|
||||
return menu;
|
||||
}
|
||||
|
||||
public int Handler_CategorySelectMenu(Menu menu, MenuAction action, int param1, int param2)
|
||||
{
|
||||
switch(action)
|
||||
{
|
||||
case MenuAction_End:
|
||||
{
|
||||
delete menu;
|
||||
}
|
||||
case MenuAction_Select:
|
||||
{
|
||||
static char item[64];
|
||||
GetMenuItem(menu, param2, item, sizeof(item));
|
||||
|
||||
if (StrEqual(item, "Hide all unavailable maps") || StrEqual(item, "Show all unavailable maps"))
|
||||
{
|
||||
sql_insert_update_hiding_unavailable(param1);
|
||||
Menu refreshed = BuildCategoryMenu(param1);
|
||||
SetNominateMenuTitle(refreshed, param1);
|
||||
DisplayMenu(refreshed, param1, MENU_TIME_FOREVER);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Menu submenu = BuildMapMenu("", param1, item);
|
||||
SetNominateMenuTitle(submenu, param1);
|
||||
DisplayMenu(submenu, param1, MENU_TIME_FOREVER);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
Menu BuildMapMenu(const char[] filter, int client, const char[] category = "")
|
||||
{
|
||||
Menu menu = CreateMenu(Handler_MapSelectMenu, MENU_ACTIONS_DEFAULT|MenuAction_DrawItem|MenuAction_DisplayItem);
|
||||
|
||||
static char map[PLATFORM_MAX_PATH * 2];
|
||||
static char mapCategory[64];
|
||||
|
||||
int arraySize = ByteCountToCells(PLATFORM_MAX_PATH);
|
||||
ArrayList MapList = CreateArray(arraySize);
|
||||
@@ -1013,7 +1003,18 @@ Menu BuildMapMenu(const char[] filter, int client)
|
||||
for(int j = 0; j < GetArraySize(MapList); j++)
|
||||
{
|
||||
int owner = GetArrayCell(OwnerList, j);
|
||||
if (client == owner)
|
||||
//display if normal nomination, leader nomination or admin nomination
|
||||
if (!owner)
|
||||
{
|
||||
GetArrayString(MapList, j, map, sizeof(map));
|
||||
sm.SetValue(map, 2);
|
||||
}
|
||||
else if (Leader_Is(owner))
|
||||
{
|
||||
GetArrayString(MapList, j, map, sizeof(map));
|
||||
sm.SetValue(map, 3);
|
||||
}
|
||||
else if (client == owner)
|
||||
{
|
||||
GetArrayString(MapList, j, map, sizeof(map));
|
||||
sm.SetValue(map, 1);
|
||||
@@ -1021,41 +1022,44 @@ Menu BuildMapMenu(const char[] filter, int client)
|
||||
}
|
||||
}
|
||||
|
||||
//2023 edit to allow hiding/unhiding unavailable maps
|
||||
char hiding_unavailable_maps[MAX_NAME_LENGTH];
|
||||
if (g_bClientsIgnoring[client])
|
||||
{
|
||||
Format(hiding_unavailable_maps, sizeof(hiding_unavailable_maps), "Show all unavailable maps");
|
||||
}
|
||||
else
|
||||
{
|
||||
Format(hiding_unavailable_maps, sizeof(hiding_unavailable_maps), "Hide all unavailable maps");
|
||||
}
|
||||
AddMenuItem(menu, hiding_unavailable_maps, hiding_unavailable_maps);
|
||||
|
||||
for(int i = 0; i < GetArraySize(g_MapList); i++)
|
||||
{
|
||||
GetArrayString(g_MapList, i, map, sizeof(map));
|
||||
|
||||
if (category[0] && !StrEqual(category, "Full Map list"))
|
||||
{
|
||||
//only shows maps that are in this actual category.
|
||||
GetMapCategory(map, mapCategory, sizeof(mapCategory));
|
||||
if (!StrEqual(mapCategory, category))
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!filter[0] || StrContains(map, filter, false) != -1)
|
||||
{
|
||||
if (client != 0 && nominated_maps)
|
||||
{
|
||||
int map_present = 0;
|
||||
sm.GetValue(map, map_present);
|
||||
// 1 -> nominated by client. 2 -> nominated by admin. 3 -> nominated by leader
|
||||
if (map_present == 1)
|
||||
{
|
||||
//PrintToChatAll("client %N here. map: %s", client, map);
|
||||
StrCat(map, sizeof(map), " (Nominated)");
|
||||
}
|
||||
else if (map_present == 2)
|
||||
{
|
||||
StrCat(map, sizeof(map), " (Admin Nominated)");
|
||||
}
|
||||
else if (map_present == 3)
|
||||
{
|
||||
StrCat(map, sizeof(map), " (Leader Nominated)");
|
||||
}
|
||||
}
|
||||
if (g_bClientsIgnoring[client])
|
||||
{
|
||||
if (GetMapCooldownTime(map) > GetTime())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if(AreRestrictionsActive() && (
|
||||
GetMapCooldownTime(map) > GetTime() ||
|
||||
GetExcludedMapListingPriority(map) > 0 ||
|
||||
GetMapTimeRestriction(map) ||
|
||||
GetMapPlayerRestriction(map) ||
|
||||
GetAveragePlayerTimeOnServerMapRestriction(map) > 0 ||
|
||||
@@ -1072,7 +1076,10 @@ Menu BuildMapMenu(const char[] filter, int client)
|
||||
delete OwnerList;
|
||||
delete sm;
|
||||
|
||||
SetMenuExitButton(menu, true);
|
||||
if (category[0])
|
||||
SetMenuExitBackButton(menu, true);
|
||||
else
|
||||
SetMenuExitButton(menu, true);
|
||||
|
||||
return menu;
|
||||
}
|
||||
@@ -1119,46 +1126,42 @@ public int Handler_MapSelectMenu(Menu menu, MenuAction action, int param1, int p
|
||||
delete menu;
|
||||
}
|
||||
}
|
||||
case MenuAction_Cancel:
|
||||
{
|
||||
if (param2 == MenuCancel_ExitBack)
|
||||
{
|
||||
Menu back = BuildCategoryMenu(param1);
|
||||
SetNominateMenuTitle(back, param1);
|
||||
DisplayMenu(back, param1, MENU_TIME_FOREVER);
|
||||
}
|
||||
}
|
||||
case MenuAction_Select:
|
||||
{
|
||||
//2023-03-22 edit since client sometimes is 0 which fucks up mapchooser
|
||||
if (param1 > 0 && param1 <= MaxClients && IsClientConnected(param1) && IsClientInGame(param1))
|
||||
{
|
||||
if(g_Player_NominationDelay[param1] > GetTime())
|
||||
if (g_Player_NominationDelay[param1] > GetTime())
|
||||
{
|
||||
PrintToChat(param1, "[NE] Please wait %d seconds before you can nominate again", g_Player_NominationDelay[param1] - GetTime());
|
||||
DisplayMenuAtItem(menu, param1, GetMenuSelectionPosition(), MENU_TIME_FOREVER);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char map[PLATFORM_MAX_PATH];
|
||||
char name[MAX_NAME_LENGTH];
|
||||
GetMenuItem(menu, param2, map, sizeof(map));
|
||||
|
||||
if (StrEqual(map, "Hide all unavailable maps"))
|
||||
{
|
||||
PrintToChat(param1, "Hiding all unavailable maps from the nomination list");
|
||||
sql_insert_update_hiding_unavailable(param1);
|
||||
return 0;
|
||||
}
|
||||
if (StrEqual(map, "Show all unavailable maps"))
|
||||
{
|
||||
PrintToChat(param1, "Displaying all unavailable maps from the nomination list");
|
||||
sql_insert_update_hiding_unavailable(param1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
GetClientName(param1, name, MAX_NAME_LENGTH);
|
||||
|
||||
if (GetMapCooldownTime(map) > GetTime())
|
||||
int Index_Cooldown = GetExcludedMapListingPriority(map);
|
||||
if (Index_Cooldown > 0)
|
||||
{
|
||||
PrintToChat(param1, "[NE] You cant nominate this map right now.");
|
||||
PrintToChat(param1, "[NE] You cant nominate this map right now. Overplayed Position: %i", Index_Cooldown);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(!CheckCommandAccess(param1, "sm_nominate_ignore", ADMFLAG_KICK, true) && !Leader_Is(param1))
|
||||
if (!CheckCommandAccess(param1, "sm_nominate_ignore", ADMFLAG_KICK, true) /*&& !Leader_Is(param1)*/ )
|
||||
{
|
||||
if(AreRestrictionsActive() && (
|
||||
GetMapCooldownTime(map) > GetTime() ||
|
||||
GetMapTimeRestriction(map) ||
|
||||
GetMapPlayerRestriction(map) ||
|
||||
GetAveragePlayerTimeOnServerMapRestriction(map) > 0 ||
|
||||
@@ -1188,59 +1191,79 @@ public int Handler_MapSelectMenu(Menu menu, MenuAction action, int param1, int p
|
||||
PrintToChat(param1, "[NE] %t", "Max Nominations");
|
||||
return 0;
|
||||
}
|
||||
int nominate_worth = GetPlayerWorthRTV_boost_(param1);
|
||||
|
||||
//SetTrieValue(g_mapTrie, map, MAPSTATUS_DISABLED|MAPSTATUS_EXCLUDE_NOMINATED);
|
||||
|
||||
if(result == Nominate_Added)
|
||||
if (result == Nominate_Added)
|
||||
{
|
||||
int nominate_worth = GetPlayerWorthRTV_boost_(param1);
|
||||
PrintToChatAll("[NE] %t", "Map Nominated", name, map, nominate_worth);
|
||||
if (Leader_Is(param1))
|
||||
{
|
||||
PrintToChatAll("[NE] %N has nominated %s (%i Nomination Boost Leader).", param1, map, 999);
|
||||
}
|
||||
else
|
||||
{
|
||||
PrintToChatAll("[NE] %N has nominated %s (%i Nomination Boost).", param1, map, nominate_worth);
|
||||
}
|
||||
}
|
||||
else if(result == Nominate_Replaced)
|
||||
{
|
||||
PrintToChatAll("[NE] %t", "Map Nomination Changed", name, map);
|
||||
if (Leader_Is(param1))
|
||||
{
|
||||
PrintToChatAll("[NE] %N has changed their nomination to %s (%i Nomination Boost Leader).", param1, map, 999);
|
||||
}
|
||||
else
|
||||
{
|
||||
PrintToChatAll("[NE] %N has changed their nomination to %s (%i Nomination Boost).", param1, map, nominate_worth);
|
||||
}
|
||||
}
|
||||
|
||||
LogMessage("%s nominated %s", name, map);
|
||||
//LogMessage("%s nominated %s", name, map);
|
||||
g_Player_NominationDelay[param1] = GetTime() + GetConVarInt(g_Cvar_NominateDelay);
|
||||
}
|
||||
}
|
||||
|
||||
case MenuAction_DrawItem:
|
||||
{
|
||||
static char map[PLATFORM_MAX_PATH];
|
||||
GetMenuItem(menu, param2, map, sizeof(map));
|
||||
char map_before_fix[PLATFORM_MAX_PATH];
|
||||
GetMenuItem(menu, param2, map_before_fix, sizeof(map_before_fix));
|
||||
|
||||
int status;
|
||||
if(GetTrieValue(g_mapTrie, map, status))
|
||||
char map[PLATFORM_MAX_PATH];
|
||||
if (SplitString(map_before_fix, " ", map, sizeof(map)) == -1)
|
||||
{
|
||||
if((status & MAPSTATUS_DISABLED) == MAPSTATUS_DISABLED)
|
||||
Format(map, sizeof(map), map_before_fix);
|
||||
}
|
||||
|
||||
//2026 march 20th: if map already is admin or leader nominated we itemdraw disable
|
||||
int arraySize = ByteCountToCells(PLATFORM_MAX_PATH);
|
||||
ArrayList MapList = CreateArray(arraySize);
|
||||
ArrayList OwnerList = CreateArray();
|
||||
GetNominatedMapList(MapList, OwnerList);
|
||||
|
||||
for (int j = 0; j < GetArraySize(MapList); j++)
|
||||
{
|
||||
char mapIterated[128];
|
||||
GetArrayString(MapList, j, mapIterated, sizeof(mapIterated));
|
||||
if (StrEqual(map, mapIterated))
|
||||
{
|
||||
if((status & MAPSTATUS_EXCLUDE_CURRENT) == MAPSTATUS_EXCLUDE_CURRENT)
|
||||
{
|
||||
return ITEMDRAW_DISABLED;
|
||||
}
|
||||
|
||||
if(AreRestrictionsActive() && (status & MAPSTATUS_EXCLUDE_PREVIOUS) == MAPSTATUS_EXCLUDE_PREVIOUS)
|
||||
{
|
||||
return ITEMDRAW_DISABLED;
|
||||
}
|
||||
|
||||
if((status & MAPSTATUS_EXCLUDE_NOMINATED) == MAPSTATUS_EXCLUDE_NOMINATED)
|
||||
int owner = GetArrayCell(OwnerList, j);
|
||||
if (!owner || Leader_Is(owner))
|
||||
{
|
||||
delete MapList;
|
||||
delete OwnerList;
|
||||
return ITEMDRAW_DISABLED;
|
||||
}
|
||||
}
|
||||
}
|
||||
delete MapList;
|
||||
delete OwnerList;
|
||||
|
||||
if (GetMapCooldownTime(map) > GetTime())
|
||||
int Index_Cooldown = GetExcludedMapListingPriority(map);
|
||||
if (Index_Cooldown > 0)
|
||||
{
|
||||
return ITEMDRAW_DISABLED;
|
||||
}
|
||||
|
||||
if(!CheckCommandAccess(param1, "sm_nominate_ignore", ADMFLAG_KICK, true) && !Leader_Is(param1))
|
||||
{
|
||||
if(AreRestrictionsActive() && (
|
||||
GetMapCooldownTime(map) > GetTime() ||
|
||||
GetMapTimeRestriction(map) ||
|
||||
GetMapPlayerRestriction(map) ||
|
||||
GetAveragePlayerTimeOnServerMapRestriction(map) > 0 ||
|
||||
@@ -1292,34 +1315,15 @@ public int Handler_MapSelectMenu(Menu menu, MenuAction action, int param1, int p
|
||||
Format(buffer, sizeof(buffer), "%s (%T)", buffer, "VIP Restriction", param1);
|
||||
}
|
||||
|
||||
int status;
|
||||
if(GetTrieValue(g_mapTrie, map, status))
|
||||
int Index_Cooldown = GetExcludedMapListingPriority(map);
|
||||
if (Index_Cooldown > 0)
|
||||
{
|
||||
if((status & MAPSTATUS_DISABLED) == MAPSTATUS_DISABLED)
|
||||
{
|
||||
if((status & MAPSTATUS_EXCLUDE_CURRENT) == MAPSTATUS_EXCLUDE_CURRENT)
|
||||
{
|
||||
Format(display, sizeof(display), "%s (%T)", buffer, "Current Map", param1);
|
||||
return RedrawMenuItem(display);
|
||||
}
|
||||
|
||||
if(RestrictionsActive && (status & MAPSTATUS_EXCLUDE_PREVIOUS) == MAPSTATUS_EXCLUDE_PREVIOUS)
|
||||
{
|
||||
int Cooldown = GetMapCooldown(map);
|
||||
Format(display, sizeof(display), "%s (%T %d)", buffer, "Recently Played", param1, Cooldown);
|
||||
return RedrawMenuItem(display);
|
||||
}
|
||||
|
||||
if((status & MAPSTATUS_EXCLUDE_NOMINATED) == MAPSTATUS_EXCLUDE_NOMINATED)
|
||||
{
|
||||
Format(display, sizeof(display), "%s (%T)", buffer, "Nominated", param1);
|
||||
return RedrawMenuItem(display);
|
||||
}
|
||||
}
|
||||
Format(display, sizeof(display), "%s (Overplayed pos: %i)", map, Index_Cooldown);
|
||||
return RedrawMenuItem(display);
|
||||
}
|
||||
|
||||
int Cooldown = GetMapCooldownTime(map);
|
||||
if(Cooldown > GetTime())
|
||||
if(RestrictionsActive && Cooldown > GetTime())
|
||||
{
|
||||
int Seconds = Cooldown - GetTime();
|
||||
char time[16];
|
||||
@@ -1404,13 +1408,6 @@ stock bool IsNominateAllowed(int client)
|
||||
CReplyToCommand(client, "[NE] %t", "Next Map", map);
|
||||
return false;
|
||||
}
|
||||
/*
|
||||
case CanNominate_No_VoteFull:
|
||||
{
|
||||
CReplyToCommand(client, "[NE] %t", "Max Nominations");
|
||||
return false;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -1434,6 +1431,7 @@ public int Handler_AdminMapSelectMenu(Menu menu, MenuAction action, int param1,
|
||||
{
|
||||
if(AreRestrictionsActive() && (
|
||||
GetMapCooldownTime(map) > GetTime() || //this one is fine cause its admin nomination.
|
||||
GetExcludedMapListingPriority(map) > 0 ||
|
||||
GetMapTimeRestriction(map) ||
|
||||
GetMapPlayerRestriction(map) ||
|
||||
GetAveragePlayerTimeOnServerMapRestriction(map) > 0 ||
|
||||
@@ -1453,8 +1451,6 @@ public int Handler_AdminMapSelectMenu(Menu menu, MenuAction action, int param1,
|
||||
return 0;
|
||||
}
|
||||
|
||||
SetTrieValue(g_mapTrie, map, MAPSTATUS_DISABLED|MAPSTATUS_EXCLUDE_NOMINATED);
|
||||
|
||||
PrintToChat(param1, "[NE] %t", "Map Inserted", map);
|
||||
LogAction(param1, -1, "\"%L\" inserted map \"%s\".", param1, map);
|
||||
|
||||
@@ -1525,7 +1521,6 @@ public int Native_PushMapIntoNominationPool(Handle plugin, int numArgs)
|
||||
ShiftArrayUp(g_MapList, 0);
|
||||
SetArrayString(g_MapList, 0, map);
|
||||
|
||||
UpdateMapTrie();
|
||||
UpdateMapMenus();
|
||||
|
||||
return 0;
|
||||
@@ -1549,7 +1544,6 @@ public int Native_PushMapsIntoNominationPool(Handle plugin, int numArgs)
|
||||
|
||||
delete maps;
|
||||
|
||||
UpdateMapTrie();
|
||||
UpdateMapMenus();
|
||||
|
||||
return 0;
|
||||
@@ -1566,7 +1560,6 @@ public int Native_RemoveMapFromNominationPool(Handle plugin, int numArgs)
|
||||
if ((idx = FindStringInArray(g_MapList, map)) != -1)
|
||||
RemoveFromArray(g_MapList, idx);
|
||||
|
||||
UpdateMapTrie();
|
||||
UpdateMapMenus();
|
||||
|
||||
return 0;
|
||||
@@ -1589,7 +1582,6 @@ public int Native_RemoveMapsFromNominationPool(Handle plugin, int numArgs)
|
||||
|
||||
delete maps;
|
||||
|
||||
UpdateMapTrie();
|
||||
UpdateMapMenus();
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -201,6 +201,10 @@ public int Native_GetRtvPercentageForClient(Handle plugin, int numParams)
|
||||
|
||||
UpdateRTV();
|
||||
int player_worth_rtv = GetPlayerWorthRTV_boost_(client);
|
||||
if (player_worth_rtv < 1)
|
||||
{
|
||||
player_worth_rtv = 1;
|
||||
}
|
||||
return player_worth_rtv;
|
||||
}
|
||||
|
||||
@@ -209,7 +213,7 @@ void update_g_VotesNeeded()
|
||||
int iVoters = 0;
|
||||
for (int i=1; i<=MaxClients; i++)
|
||||
{
|
||||
if (IsClientInGame(i) && !IsFakeClient(i) && !is_bot_player[i])
|
||||
if (IsClientInGame(i) && !IsFakeClient(i) && !is_bot_player[i] && PM_IsPlayerSteam(i))
|
||||
{
|
||||
if (GetClientIdleTime(i) >= g_Cvar_AFKTime.IntValue)
|
||||
continue;
|
||||
@@ -245,11 +249,15 @@ void UpdateRTV()
|
||||
public int get_voted_rtv()
|
||||
{
|
||||
int Votes = 0;
|
||||
for (int i = 0; i < MaxClients; i++)
|
||||
for (int i = 0; i <= MaxClients; i++)
|
||||
{
|
||||
if (IsValidClient(i) && g_Voted[i])
|
||||
{
|
||||
int player_worth_boost = GetPlayerWorthRTV_boost_(i);
|
||||
if (player_worth_boost < 1)
|
||||
{
|
||||
player_worth_boost = 1;
|
||||
}
|
||||
Votes += player_worth_boost;
|
||||
}
|
||||
}
|
||||
@@ -311,11 +319,11 @@ void AttemptRTV(int client)
|
||||
return;
|
||||
}
|
||||
update_g_VotesNeeded();
|
||||
int Votes = get_voted_rtv();
|
||||
|
||||
if (g_Voted[client])
|
||||
{
|
||||
ReplyToCommand(client, "[RTVE] %t", "Already Voted", Votes, g_VotesNeeded, GetPlayerWorthRTV_boost_(client));
|
||||
int Votes = get_voted_rtv();
|
||||
ReplyToCommand(client, "[RTVE] You have already voted to Rock the Vote. (%i/%i Votes. (%i RTV boost))", Votes, g_VotesNeeded, GetPlayerWorthRTV_boost_(client));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -324,9 +332,9 @@ void AttemptRTV(int client)
|
||||
|
||||
g_Voted[client] = true;
|
||||
|
||||
Votes = get_voted_rtv();
|
||||
int Votes = get_voted_rtv();
|
||||
|
||||
PrintToChatAll("[RTVE] %t", "RTV Requested", name, Votes, g_VotesNeeded, GetPlayerWorthRTV_boost_(client));
|
||||
PrintToChatAll("[RTVE] %s wants to rock the vote. (%i/%i RTV reached) (%i RTV boost).", name, Votes, g_VotesNeeded, GetPlayerWorthRTV_boost_(client));
|
||||
|
||||
if (Votes >= g_VotesNeeded)
|
||||
{
|
||||
@@ -481,7 +489,7 @@ public Action Command_DebugRTV(int client, int args)
|
||||
|
||||
for (int i=1; i<=MaxClients; i++)
|
||||
{
|
||||
if (IsClientInGame(i) && !IsFakeClient(i) && !is_bot_player[i])
|
||||
if (IsClientInGame(i) && !IsFakeClient(i) && !is_bot_player[i] && PM_IsPlayerSteam(i))
|
||||
{
|
||||
if (GetClientIdleTime(i) >= g_Cvar_AFKTime.IntValue)
|
||||
continue;
|
||||
|
||||
@@ -234,9 +234,10 @@ public MRESReturn TryPlayerMove_Dhook(Address pThis, Handle hReturn, Handle hPar
|
||||
Address pFirstTrace = DHookGetParam(hParams, 2);
|
||||
|
||||
DHookSetReturn(hReturn, TryPlayerMove(view_as<CGameMovement>(pThis), view_as<Vector>(pFirstDest), view_as<CGameTrace>(pFirstTrace)));
|
||||
return MRES_Supercede;
|
||||
}
|
||||
return MRES_Ignored;
|
||||
|
||||
return MRES_Supercede;
|
||||
}
|
||||
|
||||
int TryPlayerMove(CGameMovement pThis, Vector pFirstDest, CGameTrace pFirstTrace)
|
||||
|
||||
@@ -0,0 +1,189 @@
|
||||
#pragma semicolon 1
|
||||
#define PLUGIN_AUTHOR "jenz"
|
||||
#define PLUGIN_VERSION "1.0"
|
||||
#include <sourcemod>
|
||||
|
||||
Database g_hDatabase;
|
||||
char g_cTimeRecords[18][256];
|
||||
|
||||
public Plugin myinfo =
|
||||
{
|
||||
name = "unloze top activity",
|
||||
author = PLUGIN_AUTHOR,
|
||||
description = "listing top activity on ze",
|
||||
version = PLUGIN_VERSION,
|
||||
url = "www.unloze.com"
|
||||
};
|
||||
|
||||
public void OnPluginStart()
|
||||
{
|
||||
if (!g_hDatabase)
|
||||
{
|
||||
Database.Connect(SQL_OnDatabaseConnect, "unloze_playtimestats");
|
||||
}
|
||||
RegConsoleCmd("sm_activity", Command_topactivity, "retrieve top activity on ze for the week");
|
||||
RegConsoleCmd("sm_myactivity", Command_myactivity, "retrieve your own activity on ze for the week");
|
||||
CreateTimer(1800.0, update_top_activity, _, TIMER_REPEAT);
|
||||
update_top_activity_ze();
|
||||
}
|
||||
|
||||
public Action update_top_activity(Handle timer)
|
||||
{
|
||||
update_top_activity_ze();
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
public void update_top_activity_ze()
|
||||
{
|
||||
if (!g_hDatabase)
|
||||
{
|
||||
Database.Connect(SQL_OnDatabaseConnect, "unloze_playtimestats");
|
||||
return;
|
||||
}
|
||||
char sQuery[1024];
|
||||
Format(sQuery, sizeof(sQuery), "select pt.steam_id, pt.player_name, SUM(pt.ze_time) - pa.ze_time_start_week AS time_this_week from unloze_playtimestats.player_activity_ze pa inner join unloze_playtimestats.player_time pt on pa.steamid = pt.steam_id where pa.steamid not in ('STEAM_0:0:518094602', 'STEAM_0:0:204398871', 'STEAM_0:1:60189040', 'STEAM_0:0:610560766') GROUP BY pt.steam_id ORDER BY time_this_week DESC limit 15");
|
||||
g_hDatabase.Query(SQL_top_activity, sQuery, DBPrio_Low);
|
||||
}
|
||||
|
||||
public void SQL_top_activity(Database db, DBResultSet results, const char[] error, int iSerial)
|
||||
{
|
||||
if (!db || strlen(error))
|
||||
{
|
||||
delete results;
|
||||
LogError("Query error: %s", error);
|
||||
return;
|
||||
}
|
||||
int counter = 0;
|
||||
int counter_menu = 1;
|
||||
Format(g_cTimeRecords[counter], sizeof(g_cTimeRecords[]), "Top activity: 5 days VIP");
|
||||
counter++;
|
||||
while (results.RowCount && results.FetchRow())
|
||||
{
|
||||
char sBuffer[528];
|
||||
char sName[MAX_NAME_LENGTH];
|
||||
results.FetchString(1, sName, sizeof(sName));
|
||||
float time_this_week = results.FetchFloat(2);
|
||||
|
||||
int hours_server = RoundToFloor((time_this_week / 60) / 60);
|
||||
int minutes_server = RoundToFloor((time_this_week / 60) % 60);
|
||||
|
||||
Format(sBuffer, sizeof(sBuffer), "%i %s %d Hours %d Minutes", counter_menu, sName, hours_server, minutes_server);
|
||||
Format(g_cTimeRecords[counter], sizeof(g_cTimeRecords[]), sBuffer);
|
||||
|
||||
counter++;
|
||||
counter_menu++;
|
||||
if (counter == 6)
|
||||
{
|
||||
Format(g_cTimeRecords[counter], sizeof(g_cTimeRecords[]), "Top activity: 3 days VIP");
|
||||
counter++;
|
||||
}
|
||||
if (counter == 11)
|
||||
{
|
||||
Format(g_cTimeRecords[counter], sizeof(g_cTimeRecords[]), "Top activity: 1 day VIP");
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
delete results;
|
||||
}
|
||||
|
||||
public void SQL_OnDatabaseConnect(Database db, const char[] error, any data)
|
||||
{
|
||||
if(!db || strlen(error))
|
||||
{
|
||||
LogError("Database error: %s", error);
|
||||
return;
|
||||
}
|
||||
g_hDatabase = db;
|
||||
|
||||
char sQuery[1024];
|
||||
Format(sQuery, sizeof(sQuery), "select pt.steam_id, pt.player_name, SUM(pt.ze_time) - pa.ze_time_start_week AS time_this_week from unloze_playtimestats.player_activity_ze pa inner join unloze_playtimestats.player_time pt on pa.steamid = pt.steam_id where pa.steamid not in ('STEAM_0:0:518094602', 'STEAM_0:0:204398871', 'STEAM_0:1:60189040', 'STEAM_0:0:610560766') GROUP BY pt.steam_id ORDER BY time_this_week DESC limit 15");
|
||||
g_hDatabase.Query(SQL_top_activity, sQuery, DBPrio_Low);
|
||||
}
|
||||
|
||||
public Action Command_myactivity(int client, int args)
|
||||
{
|
||||
if (!g_hDatabase)
|
||||
{
|
||||
Database.Connect(SQL_OnDatabaseConnect, "unloze_playtimestats");
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
char sSID[64];
|
||||
GetClientAuthId(client, AuthId_Steam2, sSID, sizeof(sSID));
|
||||
|
||||
char sQuery[1024];
|
||||
Format(sQuery, sizeof(sQuery), "select pt.steam_id, pt.player_name, SUM(pt.ze_time) - pa.ze_time_start_week AS time_this_week from unloze_playtimestats.player_activity_ze pa inner join unloze_playtimestats.player_time pt on pa.steamid = pt.steam_id where pa.steamid = '%s' GROUP BY pt.steam_id ORDER BY time_this_week DESC limit 15", sSID);
|
||||
g_hDatabase.Query(SQL_my_activity, sQuery, GetClientSerial(client));
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
public void SQL_my_activity(Database db, DBResultSet results, const char[] error, int iSerial)
|
||||
{
|
||||
if (!db || strlen(error))
|
||||
{
|
||||
delete results;
|
||||
LogError("Query error: %s", error);
|
||||
return;
|
||||
}
|
||||
int client;
|
||||
if ((client = GetClientFromSerial(iSerial)) == 0)
|
||||
{
|
||||
delete results;
|
||||
return;
|
||||
}
|
||||
|
||||
char sTitle[64];
|
||||
Format(sTitle, sizeof(sTitle), "[UNLOZE your weekly activity ZE]");
|
||||
Menu menu = new Menu(MenuHandler1);
|
||||
menu.SetTitle(sTitle);
|
||||
|
||||
if (results.RowCount && results.FetchRow())
|
||||
{
|
||||
char sBuffer[528];
|
||||
char sName[MAX_NAME_LENGTH];
|
||||
results.FetchString(1, sName, sizeof(sName));
|
||||
float time_this_week = results.FetchFloat(2);
|
||||
|
||||
int hours_server = RoundToFloor((time_this_week / 60) / 60);
|
||||
int minutes_server = RoundToFloor((time_this_week / 60) % 60);
|
||||
|
||||
Format(sBuffer, sizeof(sBuffer), "%s %d Hours %d Minutes", sName, hours_server, minutes_server);
|
||||
menu.AddItem("-1", sBuffer, ITEMDRAW_DISABLED);
|
||||
}
|
||||
delete results;
|
||||
|
||||
menu.ExitButton = true;
|
||||
menu.Display(client, 0);
|
||||
}
|
||||
|
||||
public Action Command_topactivity(int client, int args)
|
||||
{
|
||||
if (!g_hDatabase)
|
||||
{
|
||||
Database.Connect(SQL_OnDatabaseConnect, "unloze_playtimestats");
|
||||
return Plugin_Handled;
|
||||
}
|
||||
char sTitle[64];
|
||||
Format(sTitle, sizeof(sTitle), "[UNLOZE weekly activity ZE]");
|
||||
Menu menu = new Menu(MenuHandler1);
|
||||
menu.SetTitle(sTitle);
|
||||
for (int i = 0; i < sizeof(g_cTimeRecords); i++)
|
||||
{
|
||||
menu.AddItem("-1", g_cTimeRecords[i], ITEMDRAW_DISABLED);
|
||||
}
|
||||
menu.ExitButton = true;
|
||||
menu.Display(client, 0);
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
public int MenuHandler1(Menu menu, MenuAction action, int param1, int param2)
|
||||
{
|
||||
switch(action)
|
||||
{
|
||||
case MenuAction_End:
|
||||
{
|
||||
delete menu;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,215 @@
|
||||
"Trails"
|
||||
{
|
||||
"Dollars"
|
||||
{
|
||||
"material" "materials/sprites/store/trails/dollars.vmt"
|
||||
"tier" "9"
|
||||
}
|
||||
|
||||
"Donuts"
|
||||
{
|
||||
"material" "materials/sprites/store/trails/donuts.vmt"
|
||||
"tier" "9"
|
||||
}
|
||||
|
||||
"Double Rainbow"
|
||||
{
|
||||
"material" "materials/sprites/store/trails/doublerainbow.vmt"
|
||||
"tier" "10"
|
||||
}
|
||||
|
||||
"Dragon"
|
||||
{
|
||||
"material" "materials/sprites/store/trails/dragon.vmt"
|
||||
"tier" "10"
|
||||
}
|
||||
"8Bit Mushroom"
|
||||
{
|
||||
"material" "materials/sprites/store/trails/8bitmushroom.vmt"
|
||||
"tier" "11"
|
||||
}
|
||||
"Arrow Rainbow"
|
||||
{
|
||||
"material" "materials/sprites/store/trails/arrowrainbow.vmt"
|
||||
"tier" "11"
|
||||
}
|
||||
|
||||
"Arrows"
|
||||
{
|
||||
"material" "materials/sprites/store/trails/arrows.vmt"
|
||||
"tier" "11"
|
||||
}
|
||||
|
||||
"Assasin"
|
||||
{
|
||||
"material" "materials/sprites/store/trails/assasin.vmt"
|
||||
"tier" "11"
|
||||
}
|
||||
|
||||
"Awesome"
|
||||
{
|
||||
"material" "materials/sprites/store/trails/awesome.vmt"
|
||||
"tier" "11"
|
||||
"VIP" "1"
|
||||
}
|
||||
"8Bit Pow"
|
||||
{
|
||||
"material" "materials/sprites/store/trails/8bitpow.vmt"
|
||||
"tier" "11"
|
||||
"VIP" "1"
|
||||
}
|
||||
"Banknote"
|
||||
{
|
||||
"material" "materials/sprites/store/trails/banknote.vmt"
|
||||
"tier" "12"
|
||||
}
|
||||
|
||||
"Barcalona"
|
||||
{
|
||||
"material" "materials/sprites/store/trails/barcalona.vmt"
|
||||
"tier" "12"
|
||||
}
|
||||
|
||||
"Batman"
|
||||
{
|
||||
"material" "materials/sprites/store/trails/batman.vmt"
|
||||
"tier" "12"
|
||||
}
|
||||
|
||||
"Battlefield"
|
||||
{
|
||||
"material" "materials/sprites/store/trails/battlefield.vmt"
|
||||
"tier" "12"
|
||||
}
|
||||
|
||||
"Beer Mug"
|
||||
{
|
||||
"material" "materials/sprites/store/trails/beermug.vmt"
|
||||
"tier" "12"
|
||||
"VIP" "1"
|
||||
}
|
||||
|
||||
"Blue Lightning"
|
||||
{
|
||||
"material" "materials/sprites/store/trails/bluelightning.vmt"
|
||||
"tier" "13"
|
||||
}
|
||||
|
||||
"BMW Logo"
|
||||
{
|
||||
"material" "materials/sprites/store/trails/bmwlogo.vmt"
|
||||
"tier" "13"
|
||||
}
|
||||
|
||||
"Bom Bomb"
|
||||
{
|
||||
"material" "materials/sprites/store/trails/bombomb.vmt"
|
||||
"tier" "13"
|
||||
}
|
||||
|
||||
"Boo"
|
||||
{
|
||||
"material" "materials/sprites/store/trails/boo.vmt"
|
||||
"tier" "13"
|
||||
"VIP" "1"
|
||||
}
|
||||
|
||||
"Canada Flag"
|
||||
{
|
||||
"material" "materials/sprites/store/trails/canadaflag.vmt"
|
||||
"tier" "14"
|
||||
}
|
||||
|
||||
"Cereal-guy"
|
||||
{
|
||||
"material" "materials/sprites/store/trails/cereal-guy.vmt"
|
||||
"tier" "14"
|
||||
}
|
||||
|
||||
"Challenge"
|
||||
{
|
||||
"material" "materials/sprites/store/trails/challenge.vmt"
|
||||
"tier" "14"
|
||||
}
|
||||
"Coca-Cola"
|
||||
{
|
||||
"material" "materials/sprites/store/trails/cocacola.vmt"
|
||||
"tier" "14"
|
||||
}
|
||||
|
||||
"Cookies"
|
||||
{
|
||||
"material" "materials/sprites/store/trails/cookies.vmt"
|
||||
"tier" "15"
|
||||
}
|
||||
|
||||
"Cracked Beam"
|
||||
{
|
||||
"material" "materials/sprites/store/trails/crackedbeam.vmt"
|
||||
"tier" "15"
|
||||
}
|
||||
|
||||
"Creed"
|
||||
{
|
||||
"material" "materials/sprites/store/trails/creed.vmt"
|
||||
"tier" "15"
|
||||
}
|
||||
|
||||
"Creeper"
|
||||
{
|
||||
"material" "materials/sprites/store/trails/creeper.vmt"
|
||||
"tier" "15"
|
||||
}
|
||||
|
||||
"Crown"
|
||||
{
|
||||
"material" "materials/sprites/store/trails/crown.vmt"
|
||||
"tier" "15"
|
||||
}
|
||||
|
||||
"Danish Flag"
|
||||
{
|
||||
"material" "materials/sprites/store/trails/danishflag.vmt"
|
||||
"VIP" "1"
|
||||
}
|
||||
|
||||
"Dark Knight"
|
||||
{
|
||||
"material" "materials/sprites/store/trails/darkknight.vmt"
|
||||
"VIP" "1"
|
||||
}
|
||||
|
||||
"Coca-Cola 2"
|
||||
{
|
||||
"material" "materials/sprites/store/trails/cocacola2.vmt"
|
||||
"VIP" "1"
|
||||
}
|
||||
"Bullet Bill"
|
||||
{
|
||||
"material" "materials/sprites/store/trails/bulletbill.vmt"
|
||||
"VIP" "1"
|
||||
}
|
||||
|
||||
"Biohazard"
|
||||
{
|
||||
"material" "materials/sprites/store/trails/biohazard.vmt"
|
||||
"VIP" "1"
|
||||
}
|
||||
"Apple"
|
||||
{
|
||||
"material" "materials/sprites/store/trails/apple.vmt"
|
||||
"VIP" "1"
|
||||
}
|
||||
"Airforce"
|
||||
{
|
||||
"material" "materials/sprites/store/trails/airforce.vmt"
|
||||
"VIP" "1"
|
||||
}
|
||||
|
||||
"Airforce 2"
|
||||
{
|
||||
"material" "materials/sprites/store/trails/airforce2.vmt"
|
||||
"VIP" "1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,478 @@
|
||||
#pragma semicolon 1
|
||||
#define PLUGIN_AUTHOR "jenz"
|
||||
#define PLUGIN_VERSION "1.0"
|
||||
|
||||
#include <unloze_playtime>
|
||||
#include <clientprefs>
|
||||
#include <mapchooser_extended>
|
||||
#include <zombiereloaded>
|
||||
#include <sdkhooks>
|
||||
#include <sdktools>
|
||||
|
||||
bool g_bTrailsEnabled;
|
||||
bool g_bRoundStartSpawnTrails;
|
||||
int g_iSpriteModel[MAXPLAYERS + 1] = {-1, ...};
|
||||
|
||||
int g_iClientTrail[MAXPLAYERS + 1] = { -1, ... }; // index into g_hTrails, -1 = none
|
||||
|
||||
float g_fTrailsLifeTime = 1.0;
|
||||
int g_iTrailsStartWidth = 10;
|
||||
int g_iTrailsEndWidth = 6;
|
||||
|
||||
Handle g_hCookie_trailName;
|
||||
Handle g_hCookie_trailHide;
|
||||
|
||||
bool g_bHideTrails[MAXPLAYERS + 1];
|
||||
|
||||
public Plugin myinfo =
|
||||
{
|
||||
name = "unloze trails",
|
||||
author = PLUGIN_AUTHOR,
|
||||
description = "adds trails based on tier, vip and maps",
|
||||
version = PLUGIN_VERSION,
|
||||
url = "www.unloze.com"
|
||||
};
|
||||
|
||||
enum struct TrailData
|
||||
{
|
||||
char name[128];
|
||||
char material[512];
|
||||
int tier;
|
||||
int vip;
|
||||
}
|
||||
|
||||
ArrayList g_hTrails;
|
||||
|
||||
public void OnPluginStart()
|
||||
{
|
||||
RegConsoleCmd("sm_trails", Command_Trail, "The Trails menu");
|
||||
RegConsoleCmd("sm_trail", Command_Trail, "The Trails menu");
|
||||
|
||||
HookEvent("round_start", Event_RoundStart, EventHookMode_PostNoCopy);
|
||||
HookEvent("round_end", Event_RoundEnd, EventHookMode_PostNoCopy);
|
||||
HookEvent("player_death", PlayerDeath);
|
||||
|
||||
g_hCookie_trailName = RegClientCookie("unloze_trail", "Select your trail", CookieAccess_Protected);
|
||||
g_hCookie_trailHide = RegClientCookie("unloze_trail_hide", "Hide trails", CookieAccess_Protected);
|
||||
SetCookieMenuItem(MenuHandler_CookieMenu, 0, "Unloze Trails");
|
||||
|
||||
g_hTrails = new ArrayList(sizeof(TrailData));
|
||||
|
||||
|
||||
ConVar cvar;
|
||||
HookConVarChange((cvar = CreateConVar("sm_unloze_trails_lifetime", "1.0", "Lifetime of trails")), Cvar_TrailsLifeTime);
|
||||
g_fTrailsLifeTime = cvar.FloatValue;
|
||||
delete cvar;
|
||||
|
||||
ConVar cvar1;
|
||||
HookConVarChange((cvar1 = CreateConVar("sm_unloze_trails_start_width", "10", "Start width of trails")), Cvar_TrailsStartWidth);
|
||||
g_iTrailsStartWidth = cvar1.IntValue;
|
||||
delete cvar1;
|
||||
|
||||
ConVar cvar2;
|
||||
HookConVarChange((cvar2 = CreateConVar("sm_unloze_trails_end_width", "6", "End width of trails")), Cvar_TrailsEndWidth);
|
||||
g_iTrailsEndWidth = cvar2.IntValue;
|
||||
delete cvar2;
|
||||
}
|
||||
|
||||
public void Cvar_TrailsEndWidth(ConVar convar, const char[] oldValue, const char[] newValue)
|
||||
{
|
||||
g_iTrailsEndWidth = convar.IntValue;
|
||||
}
|
||||
|
||||
public void Cvar_TrailsStartWidth(ConVar convar, const char[] oldValue, const char[] newValue)
|
||||
{
|
||||
g_iTrailsStartWidth = convar.IntValue;
|
||||
}
|
||||
|
||||
public void Cvar_TrailsLifeTime(ConVar convar, const char[] oldValue, const char[] newValue)
|
||||
{
|
||||
g_fTrailsLifeTime = convar.FloatValue;
|
||||
}
|
||||
|
||||
public void MenuHandler_CookieMenu(int client, CookieMenuAction action, any info, char[] buffer, int maxlen)
|
||||
{
|
||||
switch (action)
|
||||
{
|
||||
case CookieMenuAction_DisplayOption:
|
||||
{
|
||||
Format(buffer, maxlen, "Trails");
|
||||
}
|
||||
case CookieMenuAction_SelectOption:
|
||||
{
|
||||
if (!g_bTrailsEnabled)
|
||||
{
|
||||
PrintToChat(client, "[Trails] Trails are disabled on this map.");
|
||||
return;
|
||||
}
|
||||
OpenTrailsMenu(client);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void PlayerDeath(Handle:event,const String:name[],bool:dontBroadcast)
|
||||
{
|
||||
int client = GetClientOfUserId(GetEventInt(event, "userid"));
|
||||
KillTrail(client);
|
||||
}
|
||||
|
||||
public void Event_RoundEnd(Handle event, const char[] name, bool dontBroadcast)
|
||||
{
|
||||
if (!g_bTrailsEnabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
for (int client = 1; client <= MAXPLAYERS; client++)
|
||||
{
|
||||
KillTrail(client);
|
||||
}
|
||||
}
|
||||
|
||||
public void Event_RoundStart(Handle event, const char[] name, bool dontBroadcast)
|
||||
{
|
||||
if (!g_bTrailsEnabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
g_bRoundStartSpawnTrails = false;
|
||||
//From Round start we wait 5 seconds before spawning trails so map has time to load its edicts for the round.
|
||||
CreateTimer(5.0, allow_spawning_trails);
|
||||
}
|
||||
|
||||
public Action allow_spawning_trails(Handle hTimer)
|
||||
{
|
||||
g_bRoundStartSpawnTrails = true;
|
||||
for (int client = 1; client <= MAXPLAYERS; client++)
|
||||
{
|
||||
if (IsValidClient(client) && g_iClientTrail[client] != -1 && IsPlayerAlive(client) && ZR_IsClientHuman(client))
|
||||
{
|
||||
SpawnTrail(client);
|
||||
}
|
||||
}
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
public Action Command_Trail(int client, int args)
|
||||
{
|
||||
if (!g_bTrailsEnabled)
|
||||
{
|
||||
PrintToChat(client, "[Trails] Trails are disabled on this map.");
|
||||
return Plugin_Handled;
|
||||
}
|
||||
OpenTrailsMenu(client);
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
void OpenTrailsMenu(int client)
|
||||
{
|
||||
Menu menu = new Menu(MenuHandler_Trails);
|
||||
menu.SetTitle("Trails Menu");
|
||||
|
||||
if (g_bHideTrails[client])
|
||||
{
|
||||
menu.AddItem("-1", "Unhide Trails");
|
||||
}
|
||||
else
|
||||
{
|
||||
menu.AddItem("-1", "Hide Trails");
|
||||
}
|
||||
|
||||
int count = g_hTrails.Length;
|
||||
int clientTier = GetPlayerTier_native(client);
|
||||
bool vipAccess = CheckCommandAccess(client, "", ADMFLAG_RESERVATION, false);
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
TrailData trail;
|
||||
g_hTrails.GetArray(i, trail);
|
||||
|
||||
char info[128];
|
||||
IntToString(i, info, sizeof(info));
|
||||
|
||||
if (trail.tier > clientTier)
|
||||
{
|
||||
char displayName[128];
|
||||
Format(displayName, sizeof(displayName), "%s (Tier %d)", trail.name, trail.tier);
|
||||
menu.AddItem(info, displayName, ITEMDRAW_DISABLED);
|
||||
}
|
||||
else if (trail.vip && !vipAccess)
|
||||
{
|
||||
char displayName[128];
|
||||
Format(displayName, sizeof(displayName), "%s (VIP)", trail.name);
|
||||
menu.AddItem(info, displayName, ITEMDRAW_DISABLED);
|
||||
}
|
||||
else
|
||||
{
|
||||
menu.AddItem(info, trail.name);
|
||||
}
|
||||
}
|
||||
|
||||
menu.ExitButton = true;
|
||||
menu.Display(client, MENU_TIME_FOREVER);
|
||||
}
|
||||
|
||||
public int MenuHandler_Trails(Menu menu, MenuAction action, int param1, int param2)
|
||||
{
|
||||
switch (action)
|
||||
{
|
||||
case MenuAction_Select:
|
||||
{
|
||||
char info[8];
|
||||
menu.GetItem(param2, info, sizeof(info));
|
||||
int index = StringToInt(info);
|
||||
g_iClientTrail[param1] = index;
|
||||
|
||||
if (index == -1)
|
||||
{
|
||||
g_bHideTrails[param1] = !g_bHideTrails[param1];
|
||||
PrintToChat(param1, "Trails are now %s", g_bHideTrails[param1]? "Hidden": "Unhidden");
|
||||
SetClientCookie(param1, g_hCookie_trailName, "");
|
||||
KillTrail(param1);
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
TrailData trail;
|
||||
g_hTrails.GetArray(index, trail);
|
||||
PrintToChat(param1, "[Trails] Trail set to: %s", trail.name);
|
||||
SetClientCookie(param1, g_hCookie_trailName, trail.name);
|
||||
if (IsPlayerAlive(param1) && ZR_IsClientHuman(param1))
|
||||
{
|
||||
SpawnTrail(param1);
|
||||
}
|
||||
else
|
||||
{
|
||||
PrintToChat(param1, "Not Applying trail right now since you are not alive as human.");
|
||||
}
|
||||
}
|
||||
|
||||
//OpenTrailsMenu(param1); // reopen
|
||||
}
|
||||
case MenuAction_End:
|
||||
{
|
||||
delete menu;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public OnMapEnd()
|
||||
{
|
||||
for (int client = 1; client <= MAXPLAYERS; client++)
|
||||
{
|
||||
KillTrail(client);
|
||||
}
|
||||
|
||||
for (int client = 1; client <= MAXPLAYERS; client++)
|
||||
{
|
||||
g_iSpriteModel[client] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
public void OnClientDisconnect(client)
|
||||
{
|
||||
g_bHideTrails[client] = false;
|
||||
KillTrail(client);
|
||||
g_iClientTrail[client] = -1;
|
||||
}
|
||||
|
||||
public void OnClientCookiesCached(int client)
|
||||
{
|
||||
char sBuffer[512];
|
||||
GetClientCookie(client, g_hCookie_trailHide, sBuffer, sizeof(sBuffer));
|
||||
|
||||
if (sBuffer[0])
|
||||
{
|
||||
g_bHideTrails[client] = StringToInt(sBuffer) != 0 ? true : false;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_bHideTrails[client] = false;
|
||||
}
|
||||
|
||||
GetClientCookie(client, g_hCookie_trailName, sBuffer, sizeof(sBuffer));
|
||||
if (sBuffer[0])
|
||||
{
|
||||
g_iClientTrail[client] = -1;
|
||||
int count = g_hTrails.Length;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
TrailData trail;
|
||||
g_hTrails.GetArray(i, trail);
|
||||
if (StrEqual(trail.name, sBuffer, true))
|
||||
{
|
||||
g_iClientTrail[client] = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
g_iClientTrail[client] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
public void OnClientPostAdminCheck(int client)
|
||||
{
|
||||
g_iSpriteModel[client] = -1;
|
||||
}
|
||||
|
||||
public void OnMapStart()
|
||||
{
|
||||
char Mapname[128];
|
||||
GetCurrentMap(Mapname, sizeof(Mapname));
|
||||
g_bTrailsEnabled = GetMapTrails(Mapname) != 0 ? true : false;
|
||||
LoadTrails();
|
||||
}
|
||||
|
||||
public void LoadTrails()
|
||||
{
|
||||
g_hTrails.Clear();
|
||||
char path[PLATFORM_MAX_PATH];
|
||||
BuildPath(Path_SM, path, sizeof(path), "configs/unloze_trails.cfg");
|
||||
|
||||
KeyValues kv = new KeyValues("Trails");
|
||||
if (!kv.ImportFromFile(path))
|
||||
{
|
||||
LogError("[trails] Could not load %s", path);
|
||||
delete kv;
|
||||
return;
|
||||
}
|
||||
|
||||
// Enter the root "Trails" section, then iterate its children
|
||||
if (kv.GotoFirstSubKey())
|
||||
{
|
||||
do
|
||||
{
|
||||
TrailData trail;
|
||||
kv.GetSectionName(trail.name, sizeof(trail.name));
|
||||
kv.GetString("material", trail.material, sizeof(trail.material), "");
|
||||
trail.tier = kv.GetNum("tier", 0);
|
||||
trail.vip = kv.GetNum("VIP", 0);
|
||||
g_hTrails.PushArray(trail);
|
||||
|
||||
PrecacheModel(trail.material, false);
|
||||
AddFileToDownloadsTable(trail.material);
|
||||
|
||||
char vtf_download[512];
|
||||
strcopy(vtf_download, sizeof(vtf_download), trail.material);
|
||||
|
||||
//modifying last two characters so its .vtf
|
||||
int len = strlen(vtf_download);
|
||||
vtf_download[len - 2] = 't';
|
||||
vtf_download[len - 1] = 'f';
|
||||
AddFileToDownloadsTable(vtf_download);
|
||||
}
|
||||
while (kv.GotoNextKey());
|
||||
}
|
||||
delete kv;
|
||||
}
|
||||
|
||||
public void ZR_OnClientInfected(int client, int attacker, bool motherInfect, bool respawnOverride, bool respawn)
|
||||
{
|
||||
KillTrail(client);
|
||||
}
|
||||
|
||||
public void KillTrail(int client)
|
||||
{
|
||||
if (g_iSpriteModel[client] > MaxClients && IsValidEdict(g_iSpriteModel[client]))
|
||||
{
|
||||
SDKUnhook(g_iSpriteModel[client], SDKHook_SetTransmit, Hook_TrailShouldHide);
|
||||
AcceptEntityInput(g_iSpriteModel[client], "kill");
|
||||
}
|
||||
g_iSpriteModel[client] = -1;
|
||||
}
|
||||
|
||||
public void SpawnTrail(int client)
|
||||
{
|
||||
KillTrail(client);
|
||||
if (!g_bRoundStartSpawnTrails)
|
||||
{
|
||||
PrintToChat(client, "Cant spawn trails this early in the round.");
|
||||
return;
|
||||
}
|
||||
|
||||
//we got 2048 edicts in total
|
||||
int edict_limit = 1700;
|
||||
int edict_count = GetEntityCount();
|
||||
if (edict_count > edict_limit)
|
||||
{
|
||||
PrintToChat(client, "Not spawning your trail due to edict limitations");
|
||||
return;
|
||||
}
|
||||
|
||||
g_iSpriteModel[client] = CreateEntityByName("env_spritetrail");
|
||||
|
||||
if (g_iSpriteModel[client] != -1)
|
||||
{
|
||||
DispatchKeyValueFloat(g_iSpriteModel[client], "lifetime", g_fTrailsLifeTime);
|
||||
char startwidth[16];
|
||||
Format(startwidth, sizeof(startwidth), "%i", g_iTrailsStartWidth);
|
||||
DispatchKeyValue(g_iSpriteModel[client], "startwidth", startwidth);
|
||||
|
||||
char endwidth[16];
|
||||
Format(endwidth, sizeof(endwidth), "%i", g_iTrailsEndWidth);
|
||||
DispatchKeyValue(g_iSpriteModel[client], "endwidth", endwidth);
|
||||
|
||||
TrailData trail;
|
||||
g_hTrails.GetArray(g_iClientTrail[client], trail);
|
||||
DispatchKeyValue(g_iSpriteModel[client], "spritename", trail.material);
|
||||
DispatchKeyValue(g_iSpriteModel[client], "renderamt", "255");
|
||||
|
||||
DispatchKeyValue(g_iSpriteModel[client], "rendercolor", "255 255 255 255");
|
||||
DispatchKeyValue(g_iSpriteModel[client], "rendermode", "1");
|
||||
|
||||
// We give the name for our entities here
|
||||
char buffer[1024];
|
||||
Format(buffer, sizeof(buffer), "unloze_trails_%d", g_iSpriteModel[client]);
|
||||
DispatchKeyValue(g_iSpriteModel[client], "targetname", buffer);
|
||||
|
||||
DispatchSpawn(g_iSpriteModel[client]);
|
||||
|
||||
float dest_vector[3];
|
||||
dest_vector[0] = 0.0;
|
||||
dest_vector[1] = 0.0;
|
||||
dest_vector[2] = 5.0;
|
||||
|
||||
float or[3];
|
||||
float ang[3];
|
||||
float fForward[3];
|
||||
float fRight[3];
|
||||
float fUp[3];
|
||||
|
||||
GetClientAbsOrigin(client, or);
|
||||
GetClientAbsAngles(client, ang);
|
||||
|
||||
GetAngleVectors(ang, fForward, fRight, fUp);
|
||||
|
||||
or[0] += fRight[0]*dest_vector[0] + fForward[0]*dest_vector[1] + fUp[0]*dest_vector[2];
|
||||
or[1] += fRight[1]*dest_vector[0] + fForward[1]*dest_vector[1] + fUp[1]*dest_vector[2];
|
||||
or[2] += fRight[2]*dest_vector[0] + fForward[2]*dest_vector[1] + fUp[2]*dest_vector[2];
|
||||
|
||||
TeleportEntity(g_iSpriteModel[client], or, NULL_VECTOR, NULL_VECTOR);
|
||||
|
||||
SetVariantString("!activator");
|
||||
AcceptEntityInput(g_iSpriteModel[client], "SetParent", client);
|
||||
SetEntPropFloat(g_iSpriteModel[client], Prop_Send, "m_flTextureRes", 0.05);
|
||||
SetEntPropEnt(g_iSpriteModel[client], Prop_Send, "m_hOwnerEntity", client);
|
||||
|
||||
SDKHook(g_iSpriteModel[client], SDKHook_SetTransmit, Hook_TrailShouldHide);
|
||||
}
|
||||
}
|
||||
|
||||
public Action Hook_TrailShouldHide(int entity, int client)
|
||||
{
|
||||
if (g_bHideTrails[client])
|
||||
{
|
||||
return Plugin_Handled;
|
||||
}
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
stock bool IsValidClient(int client)
|
||||
{
|
||||
if (client > 0 && client <= MaxClients && IsClientConnected(client) && IsClientInGame(client))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
Reference in New Issue
Block a user