· 6 years ago · Oct 07, 2019, 12:40 AM
1#include <sourcemod>
2
3#define Version "2.2.1"
4
5typedef VIPSelectedCallback = function void(int caller, const char[] name, const char[] steamId, int duration, any additionalData);
6typedef VIPCheckedCallback = function void(int vipClient, bool expired);
7
8Database connection;
9ConVar authTypeConVar;
10AuthIdType authType = AuthId_Engine;
11
12Handle onAddVIPForward;
13Handle onRemoveVIPForward;
14Handle onDurationChangedForward;
15
16public Plugin myinfo = {
17 name = "VIP-Manager",
18 author = "Shadow_Man",
19 description = "Manage VIPs on your server",
20 version = Version,
21 url = "http://cf-server.pfweb.eu"
22};
23
24public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, err_max)
25{
26 RegPluginLibrary("VIP-Manager");
27
28 return APLRes_Success;
29}
30
31public void OnPluginStart()
32{
33 CreateConVar("sm_vipm_version", Version, "Version of VIP-Manager", FCVAR_PLUGIN | FCVAR_SPONLY);
34 authTypeConVar = CreateConVar("sm_vipm_authid_format", "engine", "Sets which SteamId format should be used.\nEngine (default) | Steam2 | Steam3 | Steam64", FCVAR_PLUGIN);
35
36 RegAdminCmd("sm_vipm", Cmd_PrintHelp, ADMFLAG_ROOT, "Lists all commands.");
37 RegAdminCmd("sm_vipm_add", Cmd_AddVIP, ADMFLAG_ROOT, "Add a VIP.");
38 RegAdminCmd("sm_vipm_rm", Cmd_RemoveVIP, ADMFLAG_ROOT, "Remove a VIP.");
39 RegAdminCmd("sm_vipm_time", Cmd_ChangeVIPDuration, ADMFLAG_ROOT, "Change the duration for a VIP.");
40 RegAdminCmd("sm_vipm_check", Cmd_CheckForExpiredVIPs, ADMFLAG_ROOT, "Check for expired VIPs.");
41
42 onAddVIPForward = CreateGlobalForward("OnVIPAdded", ET_Ignore, Param_Cell, Param_String, Param_String, Param_Cell);
43 onRemoveVIPForward = CreateGlobalForward("OnVIPRemoved", ET_Ignore, Param_Cell, Param_String, Param_String, Param_String);
44 onDurationChangedForward = CreateGlobalForward("OnVIPDurationChanged", ET_Ignore, Param_Cell, Param_String, Param_String, Param_String, Param_Cell, Param_Cell);
45
46 ConnectToDatabase();
47}
48
49public void OnConfigsExecuted()
50{
51 ParseAuthIdFormat();
52}
53
54void ParseAuthIdFormat()
55{
56 char type[16];
57 authTypeConVar.GetString(type, sizeof(type));
58
59 if(StrEqual(type, "steam2", false))
60 authType = AuthId_Steam2;
61 else if(StrEqual(type, "steam3", false))
62 authType = AuthId_Steam3;
63 else if(StrEqual(type, "steam64", false))
64 authType = AuthId_SteamID64;
65 else
66 authType = AuthId_Engine;
67}
68
69public Action Cmd_PrintHelp(int client, int args)
70{
71 ReplyToCommand(client, "sm_vipm | Lists all commands.");
72 ReplyToCommand(client, "sm_vipm_add <\"name\"> <minutes> [\"SteamId\"] | Add a VIP. If SteamID is give, it will be used.");
73 ReplyToCommand(client, "sm_vipm_rm <\"name\"> | Remove a VIP");
74 ReplyToCommand(client, "sm_vipm_time <set|add|sub> <\"name\"> <minutes> | Change the duration for a VIP.");
75 ReplyToCommand(client, "sm_vipm_check | Checks for expired VIPs.");
76
77 return Plugin_Handled;
78}
79
80public Action Cmd_AddVIP(int client, int args)
81{
82 if(connection == null) {
83 ReplyToCommand(client, "There is currently no connection to the SQL server");
84 return Plugin_Handled;
85 }
86
87 if(args < 2) {
88 ReplyToCommand(client, "Usage: sm_vipm_add <\"name\"> <minutes> [\"SteamId\"]");
89 return Plugin_Handled;
90 }
91
92 char name[MAX_NAME_LENGTH];
93 char steamId[64];
94
95 if(args == 2) {
96 char searchName[MAX_NAME_LENGTH];
97 GetCmdArg(1, searchName, sizeof(searchName));
98
99 if(!SearchClient(searchName, name, sizeof(name), steamId, sizeof(steamId))) {
100 ReplyToCommand(client, "Can't find client '%s'", searchName);
101 return Plugin_Handled;
102 }
103 }
104 else {
105 GetCmdArg(1, name, sizeof(name));
106 GetCmdArg(3, steamId, sizeof(steamId));
107 }
108
109 char durationString[16];
110 GetCmdArg(2, durationString, sizeof(durationString));
111
112 AddVIP(client, name, steamId, StringToInt(durationString));
113
114 return Plugin_Handled;
115}
116
117void AddVIP(int caller, const char[] name, const char[] steamId, int duration)
118{
119 int len = strlen(name) * 2 + 1;
120 char[] escapedName = new char[len];
121 connection.Escape(name, escapedName, len);
122
123 len = strlen(steamId) * 2 + 1;
124 char[] escapedSteamId = new char[len];
125 connection.Escape(steamId, escapedSteamId, len);
126
127 if(duration < -1)
128 duration = -1;
129
130 DataPack pack = new DataPack();
131 pack.WriteCell(caller);
132 pack.WriteString(name);
133 pack.WriteString(steamId);
134 pack.WriteCell(duration);
135 pack.Reset();
136
137 char query[512];
138 Format(query, sizeof(query), "INSERT INTO vips (steamId, name, duration) VALUES ('%s', '%s', %i);", escapedSteamId, escapedName, duration);
139 connection.Query(AddVIPCallback, query, pack);
140}
141
142public void AddVIPCallback(Database db, DBResultSet result, char[] error, any data)
143{
144 DataPack pack = view_as<DataPack>(data);
145 int caller = pack.ReadCell();
146
147 if(result == null) {
148 LogError("Error while adding VIP! Error: %s", error);
149 ReplyClient(caller, "Can't add VIP! %s", error);
150
151 delete pack;
152 return;
153 }
154
155 char name[MAX_NAME_LENGTH];
156 pack.ReadString(name, sizeof(name));
157
158 char steamId[64];
159 pack.ReadString(steamId, sizeof(steamId));
160
161 int duration = pack.ReadCell();
162
163 delete pack;
164
165 int vipClient = FindPlayer(name);
166 if(AddVIPToAdminCache(vipClient))
167 ReplyClient(caller, "Successfully added '%s' as a VIP for %i minutes!", name, duration);
168 else
169 ReplyClient(caller, "Added '%s' as a VIP in database, but can't added VIP in admin cache!", name);
170
171 Call_StartForward(onAddVIPForward);
172 Call_PushCell(caller);
173 Call_PushString(name);
174 Call_PushString(steamId);
175 Call_PushCell(duration);
176 Call_Finish();
177}
178
179bool AddVIPToAdminCache(int client)
180{
181 if(client < 1 || !IsClientConnected(client) || ClientIsAdmin(client)) {
182 NotifyPostAdminCheck(client);
183 return false;
184 }
185
186 char steamId[64];
187 GetClientAuthId(client, authType, steamId, sizeof(steamId));
188
189 GroupId group = FindAdmGroup("VIP");
190 if(group == INVALID_GROUP_ID) {
191 PrintToServer("[VIP-Manager] Couldn't found group 'VIP'! Please create a group called 'VIP'.");
192
193 NotifyPostAdminCheck(client);
194 return false;
195 }
196
197 AdminId admin = CreateAdmin();
198 AdminInheritGroup(admin, group);
199 if(!BindAdminIdentity(admin, AUTHMETHOD_STEAM, steamId)) {
200 RemoveAdmin(admin);
201
202 NotifyPostAdminCheck(client);
203 return false;
204 }
205
206 RunAdminCacheChecks(client);
207 NotifyPostAdminCheck(client);
208 return true;
209}
210
211public Action Cmd_RemoveVIP(int client, int args)
212{
213 if(connection == null) {
214 ReplyToCommand(client, "There is currently no connection to the SQL server");
215 return Plugin_Handled;
216 }
217
218 if(args < 1) {
219 ReplyToCommand(client, "Usage: sm_vipm_rm <\"name\">");
220 return Plugin_Handled;
221 }
222
223 char searchTerm[MAX_NAME_LENGTH];
224 GetCmdArg(1, searchTerm, sizeof(searchTerm));
225
226 SearchVIPByName(client, searchTerm, RemoveVIPByCommand);
227
228 return Plugin_Handled;
229}
230
231void SearchVIPByName(int caller, const char[] searchTerm, VIPSelectedCallback callback, any additionalData = 0)
232{
233 char query[128];
234 Format(query, sizeof(query), "SELECT * FROM vips WHERE name LIKE '%%%s%%';", searchTerm);
235
236 DataPack pack = new DataPack();
237 pack.WriteCell(caller);
238 pack.WriteString(searchTerm);
239 pack.WriteFunction(callback);
240 pack.WriteCell(additionalData);
241 pack.Reset();
242
243 connection.Query(VIPSearchingResult, query, pack);
244}
245
246public void VIPSearchingResult(Database db, DBResultSet result, char[] error, any data)
247{
248 DataPack pack = view_as<DataPack>(data);
249 int caller = pack.ReadCell();
250
251 if(result == null) {
252 LogError("Error while selecting VIP! Error: %s", error);
253 ReplyClient(caller, "Can't select VIP! %s", error);
254
255 delete pack;
256 return;
257 }
258
259 char searchTerm[MAX_NAME_LENGTH];
260 pack.ReadString(searchTerm, sizeof(searchTerm));
261
262 if(result.RowCount == 0) {
263 ReplyClient(caller, "Can't find a VIP with the name '%s'!", searchTerm);
264
265 delete pack;
266 return;
267 }
268 else if(result.RowCount > 1) {
269 ReplyClient(caller, "Found more than one VIP with the name '%s'! Please specify the name more accurately!", searchTerm);
270
271 delete pack;
272 return;
273 }
274
275 result.FetchRow();
276
277 char name[MAX_NAME_LENGTH];
278 result.FetchString(1, name, sizeof(name));
279
280 char steamId[64];
281 result.FetchString(0, steamId, sizeof(steamId));
282
283 int duration = result.FetchInt(3);
284
285 Call_StartFunction(null, pack.ReadFunction());
286 Call_PushCell(caller);
287 Call_PushString(name);
288 Call_PushString(steamId);
289 Call_PushCell(duration);
290 Call_PushCell(pack.ReadCell());
291 Call_Finish();
292
293 delete pack;
294}
295
296public void RemoveVIPByCommand(int caller, const char[] name, const char[] steamId, int duration, any nothing)
297{
298 char adminName[MAX_NAME_LENGTH];
299 GetClientName(caller, adminName, sizeof(adminName));
300
301 char reason[256];
302 Format(reason, sizeof(reason), "Removed by admin '%s'", adminName);
303
304 RemoveVIP(caller, name, steamId, reason);
305}
306
307void RemoveVIP(int caller, const char[] name, const char[] steamId, const char[] reason)
308{
309 int len = strlen(steamId) * 2 + 1;
310 char[] escapedSteamId = new char[len];
311 connection.Escape(steamId, escapedSteamId, len);
312
313 char query[128];
314 Format(query, sizeof(query), "DELETE FROM vips WHERE steamId = '%s';", escapedSteamId);
315
316 DataPack pack = new DataPack();
317 pack.WriteCell(caller);
318 pack.WriteString(name);
319 pack.WriteString(steamId);
320 pack.WriteString(reason);
321 pack.Reset();
322
323 connection.Query(CallbackRemoveVIP, query, pack);
324}
325
326public void CallbackRemoveVIP(Database db, DBResultSet result, char[] error, any data)
327{
328 DataPack pack = view_as<DataPack>(data);
329 int caller = pack.ReadCell();
330
331 if(result == null) {
332 LogError("Error while removing VIP! Error: %s", error);
333 ReplyClient(caller, "Can't remove VIP! %s", error);
334
335 delete pack;
336 return;
337 }
338
339 char name[MAX_NAME_LENGTH];
340 pack.ReadString(name, sizeof(name));
341
342 char steamId[64];
343 pack.ReadString(steamId, sizeof(steamId));
344
345 char reason[256];
346 pack.ReadString(reason, sizeof(reason));
347
348 delete pack;
349
350 RemoveVIPFromAdminCache(steamId);
351
352 Call_StartForward(onRemoveVIPForward);
353 Call_PushCell(caller);
354 Call_PushString(name);
355 Call_PushString(steamId);
356 Call_PushString(reason);
357 Call_Finish();
358
359 ReplyClient(caller, "Removed VIP %s(%s)! Reason: %s", name, steamId, reason);
360}
361
362void RemoveVIPFromAdminCache(char[] steamId)
363{
364 AdminId admin = FindAdminByIdentity(AUTHMETHOD_STEAM, steamId);
365 if(AdminInheritFromGroupVIP(admin))
366 RemoveAdmin(admin);
367}
368
369bool AdminInheritFromGroupVIP(AdminId admin)
370{
371 if(admin == INVALID_ADMIN_ID)
372 return false;
373
374 int groupCount = GetAdminGroupCount(admin);
375 for(int groupNumber = 0; groupNumber < groupCount; groupNumber++) {
376 char groupName[8];
377 GetAdminGroup(admin, groupNumber, groupName, sizeof(groupName));
378
379 if(StrEqual(groupName, "VIP", false))
380 return true;
381 }
382
383 return false;
384}
385
386public Action Cmd_ChangeVIPDuration(int client, int args)
387{
388 if(args != 3) {
389 ReplyToCommand(client, "Usage: sm_vipm_time <set|add|sub> <\"name\"> <minutes>");
390 return Plugin_Handled;
391 }
392
393 char searchTerm[MAX_NAME_LENGTH];
394 GetCmdArg(2, searchTerm, sizeof(searchTerm));
395
396 char minutesString[8];
397 GetCmdArg(3, minutesString, sizeof(minutesString));
398
399 int minutes = StringToInt(minutesString);
400 if(minutes < 0)
401 minutes *= -1;
402
403 char mode[8];
404 GetCmdArg(1, mode, sizeof(mode));
405
406 if(StrEqual(mode, "set", false))
407 SearchVIPByName(client, searchTerm, SetVIPDuration, minutes);
408 else if(StrEqual(mode, "add", false))
409 SearchVIPByName(client, searchTerm, AddVIPDuration, minutes);
410 else if(StrEqual(mode, "sub", false))
411 SearchVIPByName(client, searchTerm, SubVIPDuration, minutes);
412 else
413 ReplyToCommand(client, "Unknown mode '%s'! Please use 'set', 'add' or 'sub'.", mode);
414
415 return Plugin_Handled;
416}
417
418public void SetVIPDuration(int caller, const char[] name, const char[] steamId, int duration, any newDuration)
419{
420 ChangeVIPDuration(caller, name, steamId, "set", duration, newDuration);
421}
422
423public void AddVIPDuration(int caller, const char[] name, const char[] steamId, int duration, any durationToAdd)
424{
425 int newDuration = duration + durationToAdd;
426 ChangeVIPDuration(caller, name, steamId, "add", duration, newDuration);
427}
428
429public void SubVIPDuration(int caller, const char[] name, const char[] steamId, int duration, any durationToSub)
430{
431 int newDuration = duration - durationToSub;
432 ChangeVIPDuration(caller, name, steamId, "sub", duration, newDuration);
433}
434
435void ChangeVIPDuration(int caller, const char[] name, const char[] steamId, const char[] mode, int oldDuration, int newDuration)
436{
437 char query[128];
438 Format(query, sizeof(query), "UPDATE vips SET duration = %i WHERE steamId = '%s'", newDuration, steamId);
439
440 DataPack pack = new DataPack();
441 pack.WriteCell(caller);
442 pack.WriteString(name);
443 pack.WriteString(steamId);
444 pack.WriteString(mode);
445 pack.WriteCell(oldDuration);
446 pack.WriteCell(newDuration);
447 pack.Reset();
448
449 connection.Query(CallbackChangeTime, query, pack);
450}
451
452public void CallbackChangeTime(Database db, DBResultSet result, char[] error, any data)
453{
454 DataPack pack = view_as<DataPack>(data);
455 int caller = pack.ReadCell();
456
457 if(result == null) {
458 LogError("Error while manipulate VIP time! Error: %s", error);
459 ReplyClient(caller, "Can't change time for VIP! %s", error);
460
461 delete pack;
462 return;
463 }
464
465 char name[MAX_NAME_LENGTH];
466 pack.ReadString(name, sizeof(name));
467
468 char steamId[64];
469 pack.ReadString(steamId, sizeof(steamId));
470
471 char mode[8];
472 pack.ReadString(mode, sizeof(mode));
473
474 int oldDuration = pack.ReadCell();
475 int newDuration = pack.ReadCell();
476
477 delete pack;
478
479 Call_StartForward(onDurationChangedForward);
480 Call_PushCell(caller);
481 Call_PushString(name);
482 Call_PushString(steamId);
483 Call_PushString(mode);
484 Call_PushCell(oldDuration);
485 Call_PushCell(newDuration);
486 Call_Finish();
487
488 ReplyClient(caller, "Changed time for VIP '%s' from %i to %i minutes!", name, oldDuration, newDuration);
489}
490
491public Action Cmd_CheckForExpiredVIPs(int client, int args)
492{
493 DataPack pack = new DataPack();
494 pack.WriteCell(client);
495 pack.Reset();
496
497 char query[128];
498 if(DriverIsSQLite())
499 Format(query, sizeof(query), "SELECT * FROM vips WHERE (strftime('%%s', joindate, duration || ' minutes') - strftime('%%s', 'now')) < 0 AND duration >= 0;");
500 else
501 Format(query, sizeof(query), "SELECT * FROM vips WHERE TIMEDIFF(DATE_ADD(joindate, INTERVAL duration MINUTE), NOW()) < 0 AND duration >= 0;");
502
503 connection.Query(CallbackCheckForExpiredVIPs, query, pack);
504 return Plugin_Handled;
505}
506
507public void CallbackCheckForExpiredVIPs(Database db, DBResultSet result, char[] error, any data)
508{
509 DataPack pack = view_as<DataPack>(data);
510 int caller = pack.ReadCell();
511
512 delete pack;
513
514 if(result == null) {
515 LogError("Error while checking VIPs! Error: %s", error);
516 ReplyClient(caller, "Can't check VIPs! %s", error);
517 return;
518 }
519
520 if(result.RowCount <= 0) {
521 ReplyClient(caller, "No VIP is expired.");
522 return;
523 }
524
525 while(result.FetchRow()) {
526 char name[MAX_NAME_LENGTH];
527 result.FetchString(1, name, sizeof(name));
528
529 char steamId[64];
530 result.FetchString(0, steamId, sizeof(steamId));
531
532 RemoveVIP(caller, name, steamId, "Time expired!");
533 }
534
535 ReplyClient(caller, "Removed all expired VIPs!");
536}
537
538void ConnectToDatabase()
539{
540 if(SQL_CheckConfig("vip-manager"))
541 Database.Connect(CallbackConnect, "vip-manager");
542 else
543 Database.Connect(CallbackConnect, "default");
544}
545
546public void CallbackConnect(Database db, char[] error, any data)
547{
548 if(db == null)
549 LogError("Can't connect to server. Error: %s", error);
550
551 connection = db;
552 CreateTableIfNotExists();
553}
554
555void CreateTableIfNotExists()
556{
557 connection.Query(CallbackCreateTable, "CREATE TABLE IF NOT EXISTS vips (steamId VARCHAR(64) PRIMARY KEY, name VARCHAR(64) NOT NULL, joindate TIMESTAMP DEFAULT CURRENT_TIMESTAMP, duration INT(11) NOT NULL);");
558}
559
560public void CallbackCreateTable(Database db, DBResultSet result, char[] error, any data)
561{
562 if(result == null)
563 LogError("Error while creating table! Error: %s", error);
564}
565
566public Action OnClientPreAdminCheck(int client)
567{
568 if(connection == null)
569 return Plugin_Continue;
570
571 CheckVIP(client, VIPCheckedSuccessfully);
572 return Plugin_Handled;
573}
574
575void CheckVIP(int vipClient, VIPCheckedCallback callback)
576{
577 if(vipClient < 1 || !IsClientConnected(vipClient))
578 return;
579
580 char steamId[64];
581 GetClientAuthId(vipClient, authType, steamId, sizeof(steamId));
582
583 int len = strlen(steamId) * 2 + 1;
584 char[] escapedSteamId = new char[len];
585 connection.Escape(steamId, escapedSteamId, len);
586
587 char query[196];
588 if(DriverIsSQLite())
589 Format(query, sizeof(query), "SELECT (strftime('%%s', joindate, duration || ' minutes') - strftime('%%s', 'now')) < 0 AS expired FROM vips WHERE steamId = '%s' AND duration >= 0;", escapedSteamId);
590 else
591 Format(query, sizeof(query), "SELECT TIMEDIFF(DATE_ADD(joindate, INTERVAL duration MINUTE), NOW()) < 0 AS expired FROM vips WHERE steamId = '%s' AND duration >= 0;", escapedSteamId);
592
593 DataPack pack = new DataPack();
594 pack.WriteCell(vipClient);
595 pack.WriteFunction(callback);
596 pack.Reset();
597
598 connection.Query(CallbackCheckVIP, query, pack, DBPrio_High);
599}
600
601public void CallbackCheckVIP(Database db, DBResultSet result, char[] error, any data)
602{
603 if(result == null) {
604 LogError("Error while checking VIP! Error: %s", error);
605 return;
606 }
607 else if(result.RowCount <= 0)
608 return;
609
610 DataPack pack = view_as<DataPack>(data);
611 int vipClient = pack.ReadCell();
612
613 result.FetchRow();
614 bool expired = view_as<bool>(result.FetchInt(0));
615
616 Call_StartFunction(null, pack.ReadFunction());
617 Call_PushCell(vipClient);
618 Call_PushCell(expired);
619 Call_Finish();
620
621 delete pack;
622}
623
624public void VIPCheckedSuccessfully(int vipClient, bool expired)
625{
626 if(expired)
627 RemoveVIPByExpiration(vipClient);
628 else
629 FetchVIP(vipClient);
630}
631
632void FetchVIP(int vipClient)
633{
634 if(vipClient < 1 || !IsClientConnected(vipClient) || ClientIsAdmin(vipClient))
635 return;
636
637 char steamId[64];
638 GetClientAuthId(vipClient, authType, steamId, sizeof(steamId));
639
640 int len = strlen(steamId) * 2 + 1;
641 char[] escapedSteamId = new char[len];
642 connection.Escape(steamId, escapedSteamId, len);
643
644 char query[128];
645 Format(query, sizeof(query), "SELECT duration FROM vips WHERE steamId = '%s';", escapedSteamId);
646 connection.Query(CallbackFetchVIP, query, vipClient, DBPrio_High);
647}
648
649public void CallbackFetchVIP(Database db, DBResultSet result, char[] error, any data)
650{
651 int vipClient = data;
652
653 if(result == null) {
654 LogError("Error while fetching VIP! Error: %s", error);
655 return;
656 }
657
658 if(result.RowCount != 1)
659 return;
660
661 AddVIPToAdminCache(vipClient);
662}
663
664void RemoveVIPByExpiration(int vipClient)
665{
666 char name[MAX_NAME_LENGTH];
667 GetClientName(vipClient, name, sizeof(name));
668
669 char steamId[64];
670 GetClientAuthId(vipClient, authType, steamId, sizeof(steamId));
671
672 RemoveVIP(0, name, steamId, "Time expired");
673}
674
675public int OnRebuildAdminCache(AdminCachePart part)
676{
677 if(part == AdminCache_Admins)
678 CheckAvailableVIPs();
679}
680
681void CheckAvailableVIPs()
682{
683 for(int i = 1; i < MaxClients; i++)
684 CheckVIP(i, VIPCheckedSuccessfully);
685}
686
687void ReplyClient(int client, const char[] format, any ...)
688{
689 int len = strlen(format) + 256;
690 char[] message = new char[len];
691 VFormat(message, len, format, 3);
692
693 if(client == 0)
694 PrintToServer(message);
695 else
696 PrintToChat(client, message);
697}
698
699bool DriverIsSQLite()
700{
701 DBDriver driver = connection.Driver;
702 char identifier[64];
703 driver.GetIdentifier(identifier, sizeof(identifier));
704
705 return StrEqual(identifier, "sqlite");
706}
707
708bool SearchClient(const char[] search, char[] name, nameLength, char[] steamId, steamIdLength)
709{
710 int client = FindPlayer(search);
711 if(client == -1)
712 return false;
713
714 GetClientName(client, name, nameLength);
715 GetClientAuthId(client, authType, steamId, steamIdLength);
716 return true;
717}
718
719int FindPlayer(const char[] searchTerm)
720{
721 for(int client = 1; client < MaxClients; client++) {
722 if(ClientNameContainsString(client, searchTerm))
723 return client;
724 }
725
726 return -1;
727}
728
729bool ClientNameContainsString(int client, const char[] str)
730{
731 if(client < 1 || !IsClientConnected(client))
732 return false;
733
734 char playerName[MAX_NAME_LENGTH];
735 GetClientName(client, playerName, sizeof(playerName));
736
737 return StrContains(playerName, str, false) > -1;
738}
739
740bool ClientIsAdmin(int client)
741{
742 return GetUserAdmin(client) != INVALID_ADMIN_ID;
743}