· 7 years ago · Oct 05, 2018, 06:42 PM
1
2
3#include <a_samp>
4#include <a_mysql>
5#include <foreach>
6#include <zcmd>
7#include <sscanf2>
8
9
10////////////////////////////////////////
11// DEFINES
12/////////////////////////////////////
13
14//shortcuts
15#define SCM SendClientMessage
16#define SCMTA SendClientMessageToAll
17#define pid playerid
18#define PlayerInfo pInfo
19
20#define COLOR_RED "{FF0000}"
21#define COLOR_ACHAT "{0099BF}"
22
23
24#define MYSQL_HOST "localhost" // Change this to your MySQL Remote IP or "localhost".
25#define MYSQL_USER "root" // Change this to your MySQL Database username.
26#define MYSQL_PASS "" // Change this to your MySQL Database password.
27#define MYSQL_DATABASE "RPG" // Change this to your MySQL Database name.
28
29
30#define DIALOG_REGISTER (0)
31#define DIALOG_LOGIN (1)
32
33
34
35////////////////////////////////////////////////////
36// GLOBAL VARS
37///////////////////////////////////////////
38
39
40
41new MySQL: Database, Corrupt_Check[MAX_PLAYERS];
42
43
44enum ENUM_PLAYER_DATA
45{
46 ID,
47 Name[25],
48
49 Password[65],
50 Salt[11],
51
52 PasswordFails,
53
54 pAdmin,
55 pHelper,
56
57 Score,
58 Cash,
59
60 Cache: Player_Cache,
61 bool:LoggedIn
62}
63
64new pInfo[MAX_PLAYERS][ENUM_PLAYER_DATA];
65//==============================================================================
66
67///////////////////////////////////
68// STOCKS
69/////////////////////////////////
70
71stock getName(playerid)
72{
73 new name[MAX_PLAYER_NAME];
74 GetPlayerName(playerid, name, sizeof(name));
75 return name;
76}
77
78
79//////////////////////////////////////
80// PUBLICS
81////////////////////////////////////
82
83
84
85forward Permission(pid);
86public Permission(pid)
87{
88 return SendClientMessage(pid, -1, "[Error]: You are not authorized for this action!");
89}
90
91
92forward Ussage(pid, string[]);
93public Ussage(pid, string[])
94{
95 new str[128];
96 format(str, sizeof(str), "Please use: %s", string);
97 SendClientMessage(pid, -1, str);
98 return 1;
99}
100
101forward SendOOC(sender[], msg[]);
102public SendOOC(sender[], msg[])
103{
104 new output[256];
105 format(output, sizeof(output), "(( %s: %s ))", sender, msg);
106 SCMTA(-1, output);
107}
108
109forward ABC(msg[]);
110public ABC(msg[])
111{
112 for(new i = 0; i < MAX_PLAYERS; i++)
113 {
114 if(PlayerInfo[i][pAdmin] >= 1 || IsPlayerAdmin(i))
115 {
116 SCM(i,-1,msg);
117 }
118 }
119
120}
121
122forward public OnPlayerDataCheck(playerid, corrupt_check);
123public OnPlayerDataCheck(playerid, corrupt_check)
124{
125 if (corrupt_check != Corrupt_Check[playerid]) return Kick(playerid);
126 // You'd have asked already what's corrput_check and how it'd benefit me?
127 // Well basically MySQL query takes long, incase a player leaves while its not proceeded
128 // With ID 1 for example, then another player comes as ID 1 it'll basically corrupt the data
129 // So, once the query is done, the player will have the wrong data assigned for himself.
130
131 new String[150];
132
133 if(cache_num_rows() > 0)
134 {
135 // If the player exists, everything is okay and nothing is wrongly detected
136 // The player's password and Saltion key gets stored as seen below
137 // So we won't have to get a headache just to match player's password.
138
139 cache_get_value(0, "PASSWORD", pInfo[playerid][Password], 65);
140 cache_get_value(0, "SALT", pInfo[playerid][Salt], 11);
141
142 pInfo[playerid][Player_Cache] = cache_save();
143 // ^ Storing the cache ID of the player for further use later.
144
145 format(String, sizeof(String), "{FFFFFF}Welcome back, %s.\n\n{0099FF}This account is already registered.\n\
146 {0099FF}Please, input your password below to proceed to the game.\n\n", pInfo[playerid][Name]);
147 ShowPlayerDialog(playerid, DIALOG_LOGIN, DIALOG_STYLE_PASSWORD, "Login System", String, "Login", "Leave");
148 }
149 else
150 {
151 format(String, sizeof(String), "{FFFFFF}Welcome %s.\n\n{0099FF}This account is not registered.\n\
152 {0099FF}Please, input your password below to proceed to the game.\n\n", pInfo[playerid][Name]);
153 ShowPlayerDialog(playerid, DIALOG_REGISTER, DIALOG_STYLE_PASSWORD, "Registration System", String, "Register", "Leave");
154 }
155 return 1;
156}
157
158forward public OnPlayerRegister(playerid);
159public OnPlayerRegister(playerid)
160{
161 // This gets called only when the player registers a new account.
162 SendClientMessage(playerid, 0x00FF00FF, "You are now registered and has been logged in.");
163 pInfo[playerid][LoggedIn] = true;
164 return 1;
165}
166
167
168
169
170////////////////////////////////////
171// EVENTS
172//////////////////////////////////
173
174main()
175{
176
177}
178
179public OnGameModeInit()
180{
181 new MySQLOpt: option_id = mysql_init_options();
182 mysql_set_option(option_id, AUTO_RECONNECT, true); // We will set that option to automatically reconnect on timeouts.
183
184 Database = mysql_connect(MYSQL_HOST, MYSQL_USER, MYSQL_PASS, MYSQL_DATABASE, option_id); // Setting up the "Database" handle on the given MySQL details above.
185
186 if(Database == MYSQL_INVALID_HANDLE || mysql_errno(Database) != 0) // Checking if the database connection is invalid to shutdown.
187 {
188 print("I couldn't connect to the MySQL server, closing."); // Printing a message to the log.
189
190 SendRconCommand("exit"); // Sending console command to shut down server.
191 return 1;
192 }
193
194 print("I have connected to the MySQL server."); // If the given MySQL details were all okay, this message prints to the log.
195
196 // Now, we will set up the information table of the player's information.
197
198 mysql_tquery(Database, "CREATE TABLE IF NOT EXISTS `PLAYERS` (`ID` int(11) NOT NULL AUTO_INCREMENT,`USERNAME` varchar(24) NOT NULL,`PASSWORD` char(65) NOT NULL,`SALT` char(11) NOT NULL,`SCORE` mediumint(7), `pAdmin` mediumint(7), `CASH` mediumint(7) NOT NULL DEFAULT '0',`pHelper` mediumint(7) NOT NULL DEFAULT '0', PRIMARY KEY (`ID`), UNIQUE KEY `USERNAME` (`USERNAME`))");
199
200 // So, this code is probably the only one which you haven't understood.
201 // Well, we firstly create a table only if not existing in the database which is "USERS".
202 // We create "ID" and set it as a primary key with auto increment to use it in retrieving information and many more uses.
203 // We create "USERNAME" and set it as a unique key, the USERNAME stores every player's name in the database so you can
204 // Control the players in offline mode and when a player leaves everything storted like kills, deaths, password and Saltion key
205 // Wouldn't be lost upon server's close or player's disconnection.
206 // We store kills, deaths, score and cash as written above so they might be useful for further use.
207
208 return 1;
209}
210
211public OnGameModeExit()
212{
213 foreach(new i: Player)
214 {
215 if(IsPlayerConnected(i)) // Checking if the players stored in "i" are connected.
216 {
217 OnPlayerDisconnect(i, 1); // We do that so players wouldn't lose their data upon server's close.
218 }
219 }
220
221 mysql_close(Database); // Closing the database.
222 return 1;
223}
224
225public OnPlayerConnect(playerid)
226{
227 new DB_Query[115];
228
229 //Resetting player information.
230 pInfo[playerid][pAdmin] = 0;
231 pInfo[playerid][pHelper] = 0;
232 pInfo[playerid][PasswordFails] = 0;
233
234 GetPlayerName(playerid, pInfo[playerid][Name], MAX_PLAYER_NAME); // Getting the player's name.
235 Corrupt_Check[playerid]++;
236
237 mysql_format(Database, DB_Query, sizeof(DB_Query), "SELECT * FROM `PLAYERS` WHERE `USERNAME` = '%e' LIMIT 1", pInfo[playerid][Name]);
238 mysql_tquery(Database, DB_Query, "OnPlayerDataCheck", "ii", playerid, Corrupt_Check[playerid]);
239 return 1;
240}
241
242public OnPlayerDisconnect(playerid, reason)
243{
244 Corrupt_Check[playerid]++;
245
246 new DB_Query[256];
247 //Running a query to save the player's data using the stored stuff.
248 mysql_format(Database, DB_Query, sizeof(DB_Query), "UPDATE `PLAYERS` SET `SCORE` = %d, `CASH` = %d, `pAdmin` = %d, `pHelper` = %d WHERE `ID` = %d LIMIT 1",
249 pInfo[playerid][Score], pInfo[playerid][Cash], pInfo[playerid][pAdmin], pInfo[playerid][pHelper], pInfo[playerid][ID]);
250
251 mysql_tquery(Database, DB_Query);
252
253 if(cache_is_valid(pInfo[playerid][Player_Cache])) //Checking if the player's cache ID is valid.
254 {
255 cache_delete(pInfo[playerid][Player_Cache]); // Deleting the cache.
256 pInfo[playerid][Player_Cache] = MYSQL_INVALID_CACHE; // Setting the stored player Cache as invalid.
257 }
258
259 pInfo[playerid][LoggedIn] = false;
260 print("OnPlayerDisconnect has been called."); // Sending message once OnPlayerDisconnect is called.
261 return 1;
262}
263
264public OnPlayerDeath(playerid, killerid, reason)
265{
266
267 return 1;
268}
269
270public OnPlayerRequestSpawn(playerid)
271{
272 if(pInfo[playerid][LoggedIn] == false) return 0; // Ignoring the request incase player isn't logged in.
273 return 1;
274}
275
276public OnDialogResponse(playerid, dialogid, response, listitem, inputtext[])
277{
278 switch (dialogid)
279 {
280 case DIALOG_LOGIN:
281 {
282 if(!response) return Kick(playerid);
283
284 new Salted_Key[65];
285 SHA256_PassHash(inputtext, pInfo[playerid][Salt], Salted_Key, 65);
286
287 if(strcmp(Salted_Key, pInfo[playerid][Password]) == 0)
288 {
289 // Now, password should be correct as well as the strings
290 // Matched with each other, so nothing is wrong until now.
291
292 // We will activate the cache of player to make use of it e.g.
293 // Retrieve their data.
294
295 cache_set_active(pInfo[playerid][Player_Cache]);
296
297 // Okay, we are retrieving the information now..
298 cache_get_value_int(0, "ID", pInfo[playerid][ID]);
299
300 cache_get_value_int(0, "pAdmin", pInfo[playerid][pAdmin]);
301 cache_get_value_int(0, "pHelper", pInfo[playerid][pHelper]);
302
303 cache_get_value_int(0, "SCORE", pInfo[playerid][Score]);
304 cache_get_value_int(0, "CASH", pInfo[playerid][Cash]);
305
306 SetPlayerScore(playerid, pInfo[playerid][Score]);
307
308 ResetPlayerMoney(playerid);
309 GivePlayerMoney(playerid, pInfo[playerid][Cash]);
310
311 // So, we have successfully retrieved data? Now deactivating the cache.
312
313 cache_delete(pInfo[playerid][Player_Cache]);
314 pInfo[playerid][Player_Cache] = MYSQL_INVALID_CACHE;
315
316 pInfo[playerid][LoggedIn] = true;
317 SendClientMessage(playerid, 0x00FF00FF, "Logged in to the account.");
318 }
319 else
320 {
321 new String[150];
322
323 pInfo[playerid][PasswordFails] += 1;
324 printf("%s has been failed to login. (%d)", pInfo[playerid][Name], pInfo[playerid][PasswordFails]);
325 // Printing the message that someone has failed to login to his account.
326
327 if (pInfo[playerid][PasswordFails] >= 3) // If the fails exceeded the limit we kick the player.
328 {
329 format(String, sizeof(String), "%s has been kicked Reason: {FF0000}(%d/3) Login fails.", pInfo[playerid][Name], pInfo[playerid][PasswordFails]);
330 SendClientMessageToAll(0x969696FF, String);
331 Kick(playerid);
332 }
333 else
334 {
335 // If the player didn't exceed the limits we send him a message that the password is wrong.
336 format(String, sizeof(String), "Wrong password, you have %d out of 3 tries.", pInfo[playerid][PasswordFails]);
337 SendClientMessage(playerid, 0xFF0000FF, String);
338
339 format(String, sizeof(String), "{FFFFFF}Welcome back, %s.\n\n{0099FF}This account is already registered.\n\
340 {0099FF}Please, input your password below to proceed to the game.\n\n", pInfo[playerid][Name]);
341 ShowPlayerDialog(playerid, DIALOG_LOGIN, DIALOG_STYLE_PASSWORD, "Login System", String, "Login", "Leave");
342 }
343 }
344 }
345 case DIALOG_REGISTER:
346 {
347 if(!response) return Kick(playerid);
348
349 if(strlen(inputtext) <= 5 || strlen(inputtext) > 60)
350 {
351 // If the password length is less than or equal to 5 and more than 60
352 // It repeats the process and shows error message as seen below.
353
354 SendClientMessage(playerid, 0x969696FF, "Invalid password length, should be 5 - 60.");
355
356 new String[150];
357
358 format(String, sizeof(String), "{FFFFFF}Welcome %s.\n\n{0099FF}This account is not registered.\n\
359 {0099FF}Please, input your password below to proceed.\n\n", pInfo[playerid][Name]);
360 ShowPlayerDialog(playerid, DIALOG_REGISTER, DIALOG_STYLE_PASSWORD, "Registration System", String, "Register", "Leave");
361 }
362 else
363 {
364
365 // Salting the player's password using SHA256 for a better security.
366
367 for (new i = 0; i < 10; i++)
368 {
369 pInfo[playerid][Salt][i] = random(79) + 47;
370 }
371
372 pInfo[playerid][Salt][10] = 0;
373 SHA256_PassHash(inputtext, pInfo[playerid][Salt], pInfo[playerid][Password], 65);
374
375 new DB_Query[225];
376
377 // Storing player's information if everything goes right.
378 mysql_format(Database, DB_Query, sizeof(DB_Query), "INSERT INTO `PLAYERS` (`USERNAME`, `PASSWORD`, `SALT`, `SCORE`, `pAdmin`, `CASH`, `pHelper`)\
379 VALUES ('%e', '%s', '%e', '20', '0', '0', '0')", pInfo[playerid][Name], pInfo[playerid][Password], pInfo[playerid][Salt]);
380 mysql_tquery(Database, DB_Query, "OnPlayerRegister", "d", playerid);
381 }
382 }
383 }
384 return 1;
385}
386
387public OnPlayerClickMap(pid, Float:fX, Float:fY, Float:fZ)
388{
389 SetPlayerPos(pid, fX, fY, fZ+3);
390}
391
392
393
394///////////////////////////////////////////
395// CMDS
396/////////////////////////////////////////
397
398CMD:help(pid, params[])
399{
400 SCM(pid, -1, "-------------- Commands --------------");
401 SCM(pid, -1, "/help; /admins; ");
402 return 1;
403}
404
405CMD:admins(pid, params[])
406{
407 SCM(pid, -1, "----------- Admin Lists -----------");
408 new str[128];
409 for(new i = 0; i < MAX_PLAYERS; i++)
410 {
411
412 if(PlayerInfo[i][pAdmin] >= 1)
413 {
414 format(str, sizeof(str), "Admin Level %d: %s(%d)", PlayerInfo[i][pAdmin], getName(i), i);
415 }
416 else if(IsPlayerAdmin(i))
417 {
418 format(str, sizeof(str), "Admin RCON: %s(%d)", getName(i), i);
419 }
420
421 }
422 return 1;
423}
424
425
426//admin 1
427CMD:ah(pid, params[])
428{
429 if(PlayerInfo[pid][pAdmin] < 1)
430 return Permission(pid);
431 if(PlayerInfo[pid][pAdmin] >= 1)
432 {//For Admin Level 1
433 SCM(pid, -1, "-------------------------- Admin Commands --------------------------");
434 SCM(pid, -1, "Admin 1: /a(dmin)h(elp); /c(lear)c(hat); /kick; /ban; /mute; /unmute;");
435 SCM(pid, -1, "Admin 1: /goto; /gethere; /o(oc); /a(dminchat); /freeze; /unfreeze;");
436 SCM(pid, -1, "Admin 1: /area; /spec(tate);");
437 }
438
439 return 1;
440}
441
442CMD:freeze(pid, params[])
443{
444 if(IsPlayerAdmin(pid) || PlayerInfo[pid][pAdmin] >= 1)
445 {
446 new tid;
447 if(sscanf(params, "u", tid)) return Ussage(pid, "/freeze <Player ID/Name>");
448 else
449 {
450 TogglePlayerControllable(tid, 0);
451 return 1;
452 }
453 }
454 else
455 return Permission(pid);
456}
457
458CMD:unfreeze(pid, params[])
459{
460 if(IsPlayerAdmin(pid) || PlayerInfo[pid][pAdmin] >= 1)
461 {
462 new tid;
463 if(sscanf(params, "u", tid)) return Ussage(pid, "/unfreeze <Player ID/Name>");
464 else
465 {
466 TogglePlayerControllable(tid, 1);
467 return 1;
468 }
469 }
470 else
471 return Permission(pid);
472}
473
474CMD:a(pid, params[])
475{
476 if(IsPlayerAdmin(pid) || PlayerInfo[pid][pAdmin] >= 1)
477 {
478 new str[128];
479 if(sscanf(params, "s", str)) return Ussage(pid, "/a(dminchat) <Message>");
480 else
481 {
482 new buff[200];
483 format(buff, sizeof(buff), ""COLOR_ACHAT"Admin %d - %s: %s", PlayerInfo[pid][pAdmin], getName(pid), str);
484 ABC(buff);
485 return 1;
486 }
487 }
488 else
489 return Permission(pid);
490}
491
492CMD:adminchat(pid, params[])
493{
494 return cmd_a(pid, params);
495}
496
497CMD:o(pid, params[])
498{
499 if(IsPlayerAdmin(pid) || PlayerInfo[pid][pAdmin] >= 1)
500 {
501 new str[200];
502 if(sscanf(params, "s", str)) return Ussage(pid, "/o(oc) <Public Message>");
503 else
504 {
505 SendOOC(getName(pid), str);
506 return 1;
507 }
508 }
509 else
510 return Permission(pid);
511}
512
513CMD:ooc(pid, params[])
514{
515 return cmd_o(pid, params);
516}
517
518
519CMD:goto(pid, params[])
520{
521 if(IsPlayerAdmin(pid) || PlayerInfo[pid][pAdmin] >= 1)
522 {
523 new tid;
524 if(sscanf(params, "u", tid)) return Ussage(pid, "/goto <Player ID/Name>");
525 else
526 {
527 new Float:tx, Float:ty, Float:tz;
528 GetPlayerPos(tid, tx, ty, tz);
529
530 if(IsPlayerInAnyVehicle(pid) && GetPlayerVehicleSeat(pid) == 0)
531 {
532 new vid = GetPlayerVehicleID(pid);
533 SetVehiclePos(vid, tx, ty, tz+3);
534 PutPlayerInVehicle(pid, vid, 0);
535 }
536 else
537 {
538 SetPlayerPos(pid, tx, ty, tz);
539 }
540
541 return 1;
542 }
543 }
544 else
545 return Permission(pid);
546}
547
548CMD:gethere(pid, params[])
549{
550 if(IsPlayerAdmin(pid) || PlayerInfo[pid][pAdmin] >= 1)
551 {
552 new tid;
553 if(sscanf(params, "u", tid)) return Ussage(pid, "/gethere <Player ID/Name>");
554 else
555 {
556 new Float:tx, Float:ty, Float:tz;
557 GetPlayerPos(pid, tx, ty, tz);
558 SetPlayerPos(tid, tx, ty, tz);
559
560 return 1;
561 }
562 }
563 else
564 return Permission(pid);
565}
566
567
568
569
570CMD:kick(pid, params[])
571{
572 if(IsPlayerAdmin(pid) || PlayerInfo[pid][pAdmin] >= 1)
573 {
574 new tid, res;
575 if(sscanf(params, "us", tid, res)) return Ussage(pid, "/kick <Player ID/Name> <Reason>");
576 {
577 new string[128];
578 format(string, sizeof(string), COLOR_RED"AdmLog: %s was kicked by admin %s. | Reason: %s", getName(tid), getName(pid), res);
579 SCMTA(-1, string);
580 Kick(pid);
581 return 1;
582 }
583 }
584 else return Permission(pid);
585}
586
587CMD:ahelp(pid, params[])
588{
589 return cmd_ah(pid, params);
590}
591
592CMD:adminhelp(pid, params[])
593{
594 return cmd_ah(pid, params);
595}
596
597CMD:cc(pid, params[])
598{
599 if(PlayerInfo[pid][pAdmin] >= 1)
600 {
601 for(new i; i < 120; i++)
602 {
603 SCMTA(-1, "");
604 }
605 SCMTA(-1, "Chat-ul a fost sters.");
606 return 1;
607 }
608 else
609 return Permission(pid);
610}
611
612
613CMD:clearchat(pid, params[])
614{
615 return cmd_cc(pid, params);
616}
617
618
619
620
621//admin 2
622
623
624
625//admin 3
626
627
628//admin 4
629
630
631
632
633
634//Admin 5
635CMD:makeadmin(pid, params[])
636{
637 if(IsPlayerAdmin(pid))
638 {
639 new tid, alevel;
640 if(sscanf(params, "ud", tid, alevel)) return Ussage(pid, "/makeadmin <Player ID/Name> <Admin Level 0-5>");
641 else
642 {
643 new string[128];
644 if(alevel > 0 && alevel <= 5)
645 {
646 format(string, sizeof(string), "%s a fost promovat la nivelul de admin %d.", getName(pid), alevel);
647 SendOOC("System", string);
648
649 format(string, sizeof(string), "Ai fost promovat la nivelul de admin %d", alevel);
650 SCM(tid, -1, string);
651
652 PlayerInfo[tid][pAdmin] = alevel;
653 }
654 else if(alevel == 0)
655 {
656 SCM(tid, -1, "Nu mai ai permisiuni de admin!");
657 SCM(pid, -1, "Actiunea a fost realizata cu succes");
658 PlayerInfo[tid][pAdmin] = 0;
659 }
660 else
661 return Ussage(pid, "/makeadmin <Player ID/Name> <Admin Level 0-5>");
662
663 return 1;
664 }
665
666
667 }
668 else
669 return Permission(pid);
670}