· 7 years ago · Oct 21, 2018, 08:04 PM
1// This is a comment
2// uncomment the line below if you want to write a filterscript
3//#define FILTERSCRIPT
4
5#include <a_samp>
6
7#include <a_mysql>
8#include <foreach>
9
10#define MYSQL_HOST "79.98.24.174" // Change this to your MySQL Remote IP or "localhost".
11#define MYSQL_USER "reward_samp" // Change this to your MySQL Database username.
12#define MYSQL_PASS "reward_samp" // Change this to your MySQL Database password.
13#define MYSQL_DATABASE "xJLUKi3du" // Change this to your MySQL Database name.
14#define DIALOG_REGISTER (0)
15#define DIALOG_LOGIN (1)
16
17new
18 MySQL: Database, Corrupt_Check[MAX_PLAYERS];
19
20enum ENUM_PLAYER_DATA
21{
22 ID,
23 Name[25],
24
25 Password[65],
26 Salt[11],
27
28 PasswordFails,
29
30 Score,
31 Cash,
32
33 Cache: Player_Cache,
34 bool:LoggedIn
35}
36
37new pInfo[MAX_PLAYERS][ENUM_PLAYER_DATA];
38
39
40
41main()
42{
43 print("\n----------------------------------");
44 print(" Blank Gamemode by your name here");
45 print("----------------------------------\n");
46}
47
48
49
50public OnGameModeInit()
51{
52 new MySQLOpt: option_id = mysql_init_options();
53 mysql_set_option(option_id, AUTO_RECONNECT, true); // We will set that option to automatically reconnect on timeouts.
54
55 Database = mysql_connect(MYSQL_HOST, MYSQL_USER, MYSQL_PASS, MYSQL_DATABASE, option_id); // Setting up the "Database" handle on the given MySQL details above.
56
57 if(Database == MYSQL_INVALID_HANDLE || mysql_errno(Database) != 0) // Checking if the database connection is invalid to shutdown.
58 {
59 print("I couldn't connect to the MySQL server, closing."); // Printing a message to the log.
60
61 SendRconCommand("exit"); // Sending console command to shut down server.
62 return 1;
63 }
64
65 print("I have connected to the MySQL server."); // If the given MySQL details were all okay, this message prints to the log.
66
67 // Now, we will set up the information table of the player's information.
68
69 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),`CASH` mediumint(7) NOT NULL DEFAULT '0', PRIMARY KEY (`ID`), UNIQUE KEY `USERNAME` (`USERNAME`))");
70
71 SetGameModeText("Blank Script");
72 AddPlayerClass(0, 1958.3783, 1343.1572, 15.3746, 269.1425, 0, 0, 0, 0, 0, 0);
73 return 1;
74}
75
76public OnGameModeExit()
77{
78 foreach(new i: Player)
79 {
80 if(IsPlayerConnected(i)) // Checking if the players stored in "i" are connected.
81 {
82 OnPlayerDisconnect(i, 1); // We do that so players wouldn't lose their data upon server's close.
83 }
84 }
85
86 mysql_close(Database); // Closing the database.
87 return 1;
88}
89
90public OnPlayerRequestClass(playerid, classid)
91{
92 SetPlayerPos(playerid, 1958.3783, 1343.1572, 15.3746);
93 SetPlayerCameraPos(playerid, 1958.3783, 1343.1572, 15.3746);
94 SetPlayerCameraLookAt(playerid, 1958.3783, 1343.1572, 15.3746);
95 return 1;
96}
97
98public OnPlayerConnect(playerid)
99{
100 new DB_Query[115];
101
102 //Resetting player information.
103 pInfo[playerid][PasswordFails] = 0;
104
105 GetPlayerName(playerid, pInfo[playerid][Name], MAX_PLAYER_NAME); // Getting the player's name.
106 Corrupt_Check[playerid]++;
107
108 mysql_format(Database, DB_Query, sizeof(DB_Query), "SELECT * FROM `PLAYERS` WHERE `USERNAME` = '%e' LIMIT 1", pInfo[playerid][Name]);
109 mysql_tquery(Database, DB_Query, "OnPlayerDataCheck", "ii", playerid, Corrupt_Check[playerid]);
110
111 return 1;
112}
113
114public OnPlayerDisconnect(playerid, reason)
115{
116 Corrupt_Check[playerid]++;
117
118 new DB_Query[256];
119 //Running a query to save the player's data using the stored stuff.
120 mysql_format(Database, DB_Query, sizeof(DB_Query), "UPDATE `PLAYERS` SET `SCORE` = %d, `CASH` = %d WHERE `ID` = %d LIMIT 1",
121 pInfo[playerid][Score], pInfo[playerid][Cash], pInfo[playerid][ID]);
122
123 mysql_tquery(Database, DB_Query);
124
125 if(cache_is_valid(pInfo[playerid][Player_Cache])) //Checking if the player's cache ID is valid.
126 {
127 cache_delete(pInfo[playerid][Player_Cache]); // Deleting the cache.
128 pInfo[playerid][Player_Cache] = MYSQL_INVALID_CACHE; // Setting the stored player Cache as invalid.
129 }
130
131 pInfo[playerid][LoggedIn] = false;
132 print("OnPlayerDisconnect has been called."); // Sending message once OnPlayerDisconnect is called.
133
134 return 1;
135}
136
137public OnPlayerSpawn(playerid)
138{
139 return 1;
140}
141
142public OnPlayerDeath(playerid, killerid, reason)
143{
144 return 1;
145}
146
147public OnVehicleSpawn(vehicleid)
148{
149 return 1;
150}
151
152public OnVehicleDeath(vehicleid, killerid)
153{
154 return 1;
155}
156
157public OnPlayerText(playerid, text[])
158{
159 return 1;
160}
161
162public OnPlayerCommandText(playerid, cmdtext[])
163{
164 if (strcmp("/mycommand", cmdtext, true, 10) == 0)
165 {
166 // Do something here
167 return 1;
168 }
169 return 0;
170}
171
172public OnPlayerEnterVehicle(playerid, vehicleid, ispassenger)
173{
174 return 1;
175}
176
177public OnPlayerExitVehicle(playerid, vehicleid)
178{
179 return 1;
180}
181
182public OnPlayerStateChange(playerid, newstate, oldstate)
183{
184 return 1;
185}
186
187public OnPlayerEnterCheckpoint(playerid)
188{
189 return 1;
190}
191
192public OnPlayerLeaveCheckpoint(playerid)
193{
194 return 1;
195}
196
197public OnPlayerEnterRaceCheckpoint(playerid)
198{
199 return 1;
200}
201
202public OnPlayerLeaveRaceCheckpoint(playerid)
203{
204 return 1;
205}
206
207public OnRconCommand(cmd[])
208{
209 return 1;
210}
211
212public OnPlayerRequestSpawn(playerid)
213{
214 return 1;
215}
216
217public OnObjectMoved(objectid)
218{
219 return 1;
220}
221
222public OnPlayerObjectMoved(playerid, objectid)
223{
224 return 1;
225}
226
227public OnPlayerPickUpPickup(playerid, pickupid)
228{
229 return 1;
230}
231
232public OnVehicleMod(playerid, vehicleid, componentid)
233{
234 return 1;
235}
236
237public OnVehiclePaintjob(playerid, vehicleid, paintjobid)
238{
239 return 1;
240}
241
242public OnVehicleRespray(playerid, vehicleid, color1, color2)
243{
244 return 1;
245}
246
247public OnPlayerSelectedMenuRow(playerid, row)
248{
249 return 1;
250}
251
252public OnPlayerExitedMenu(playerid)
253{
254 return 1;
255}
256
257public OnPlayerInteriorChange(playerid, newinteriorid, oldinteriorid)
258{
259 return 1;
260}
261
262public OnPlayerKeyStateChange(playerid, newkeys, oldkeys)
263{
264 return 1;
265}
266
267public OnRconLoginAttempt(ip[], password[], success)
268{
269 return 1;
270}
271
272public OnPlayerUpdate(playerid)
273{
274 return 1;
275}
276
277public OnPlayerStreamIn(playerid, forplayerid)
278{
279 return 1;
280}
281
282public OnPlayerStreamOut(playerid, forplayerid)
283{
284 return 1;
285}
286
287public OnVehicleStreamIn(vehicleid, forplayerid)
288{
289 return 1;
290}
291
292public OnVehicleStreamOut(vehicleid, forplayerid)
293{
294 return 1;
295}
296
297public OnDialogResponse(playerid, dialogid, response, listitem, inputtext[])
298{
299 switch (dialogid)
300 {
301 case DIALOG_LOGIN:
302 {
303 if(!response) return Kick(playerid);
304
305 new Salted_Key[65];
306 SHA256_PassHash(inputtext, pInfo[playerid][Salt], Salted_Key, 65);
307
308 if(strcmp(Salted_Key, pInfo[playerid][Password]) == 0)
309 {
310 // Now, password should be correct as well as the strings
311 // Matched with each other, so nothing is wrong until now.
312
313 // We will activate the cache of player to make use of it e.g.
314 // Retrieve their data.
315
316 cache_set_active(pInfo[playerid][Player_Cache]);
317
318 // Okay, we are retrieving the information now..
319 cache_get_value_int(0, "ID", pInfo[playerid][ID]);
320
321 cache_get_value_int(0, "SCORE", pInfo[playerid][Score]);
322 cache_get_value_int(0, "CASH", pInfo[playerid][Cash]);
323
324 SetPlayerScore(playerid, pInfo[playerid][Score]);
325
326 ResetPlayerMoney(playerid);
327 GivePlayerMoney(playerid, pInfo[playerid][Cash]);
328
329 // So, we have successfully retrieved data? Now deactivating the cache.
330
331 cache_delete(pInfo[playerid][Player_Cache]);
332 pInfo[playerid][Player_Cache] = MYSQL_INVALID_CACHE;
333
334 pInfo[playerid][LoggedIn] = true;
335 SendClientMessage(playerid, 0x00FF00FF, "Logged in to the account.");
336 }
337 else
338 {
339 new String[150];
340
341 pInfo[playerid][PasswordFails] += 1;
342 printf("%s has been failed to login. (%d)", pInfo[playerid][Name], pInfo[playerid][PasswordFails]);
343 // Printing the message that someone has failed to login to his account.
344
345 if (pInfo[playerid][PasswordFails] >= 3) // If the fails exceeded the limit we kick the player.
346 {
347 format(String, sizeof(String), "%s has been kicked Reason: {FF0000}(%d/3) Login fails.", pInfo[playerid][Name], pInfo[playerid][PasswordFails]);
348 SendClientMessageToAll(0x969696FF, String);
349 Kick(playerid);
350 }
351 else
352 {
353 // If the player didn't exceed the limits we send him a message that the password is wrong.
354 format(String, sizeof(String), "Wrong password, you have %d out of 3 tries.", pInfo[playerid][PasswordFails]);
355 SendClientMessage(playerid, 0xFF0000FF, String);
356
357 format(String, sizeof(String), "{FFFFFF}Welcome back, %s.\n\n{0099FF}This account is already registered.\n\
358 {0099FF}Please, input your password below to proceed to the game.\n\n", pInfo[playerid][Name]);
359 ShowPlayerDialog(playerid, DIALOG_LOGIN, DIALOG_STYLE_PASSWORD, "Login System", String, "Login", "Leave");
360 }
361 }
362 }
363 case DIALOG_REGISTER:
364 {
365 if(!response) return Kick(playerid);
366
367 if(strlen(inputtext) <= 5 || strlen(inputtext) > 60)
368 {
369 // If the password length is less than or equal to 5 and more than 60
370 // It repeats the process and shows error message as seen below.
371
372 SendClientMessage(playerid, 0x969696FF, "Invalid password length, should be 5 - 60.");
373
374 new String[150];
375
376 format(String, sizeof(String), "{FFFFFF}Welcome %s.\n\n{0099FF}This account is not registered.\n\
377 {0099FF}Please, input your password below to proceed.\n\n", pInfo[playerid][Name]);
378 ShowPlayerDialog(playerid, DIALOG_REGISTER, DIALOG_STYLE_PASSWORD, "Registration System", String, "Register", "Leave");
379 }
380 else
381 {
382
383 // Salting the player's password using SHA256 for a better security.
384
385 for (new i = 0; i < 10; i++)
386 {
387 pInfo[playerid][Salt][i] = random(79) + 47;
388 }
389
390 pInfo[playerid][Salt][10] = 0;
391 SHA256_PassHash(inputtext, pInfo[playerid][Salt], pInfo[playerid][Password], 65);
392
393 new DB_Query[225];
394
395 // Storing player's information if everything goes right.
396 mysql_format(Database, DB_Query, sizeof(DB_Query), "INSERT INTO `PLAYERS` (`USERNAME`, `PASSWORD`, `SALT`, `SCORE`, `CASH`)\
397 VALUES ('%e', '%s', '%e', '20', '0')", pInfo[playerid][Name], pInfo[playerid][Password], pInfo[playerid][Salt]);
398 mysql_tquery(Database, DB_Query, "OnPlayerRegister", "d", playerid);
399 }
400 }
401 }
402 return 1;
403}
404
405public OnPlayerClickPlayer(playerid, clickedplayerid, source)
406{
407 return 1;
408}
409forward public OnPlayerDataCheck(playerid, corrupt_check);
410public OnPlayerDataCheck(playerid, corrupt_check)
411{
412 if (corrupt_check != Corrupt_Check[playerid]) return Kick(playerid);
413 // You'd have asked already what's corrput_check and how it'd benefit me?
414 // Well basically MySQL query takes long, incase a player leaves while its not proceeded
415 // With ID 1 for example, then another player comes as ID 1 it'll basically corrupt the data
416 // So, once the query is done, the player will have the wrong data assigned for himself.
417
418 new String[150];
419
420 if(cache_num_rows() > 0)
421 {
422 // If the player exists, everything is okay and nothing is wrongly detected
423 // The player's password and Saltion key gets stored as seen below
424 // So we won't have to get a headache just to match player's password.
425
426 cache_get_value(0, "PASSWORD", pInfo[playerid][Password], 65);
427 cache_get_value(0, "SALT", pInfo[playerid][Salt], 11);
428
429 pInfo[playerid][Player_Cache] = cache_save();
430 // ^ Storing the cache ID of the player for further use later.
431
432 format(String, sizeof(String), "{FFFFFF}Welcome back, %s.\n\n{0099FF}This account is already registered.\n\
433 {0099FF}Please, input your password below to proceed to the game.\n\n", pInfo[playerid][Name]);
434 ShowPlayerDialog(playerid, DIALOG_LOGIN, DIALOG_STYLE_PASSWORD, "Login System", String, "Login", "Leave");
435 }
436 else
437 {
438 format(String, sizeof(String), "{FFFFFF}Welcome %s.\n\n{0099FF}This account is not registered.\n\
439 {0099FF}Please, input your password below to proceed to the game.\n\n", pInfo[playerid][Name]);
440 ShowPlayerDialog(playerid, DIALOG_REGISTER, DIALOG_STYLE_PASSWORD, "Registration System", String, "Register", "Leave");
441 }
442 return 1;
443}