· 5 years ago · Mar 18, 2020, 01:02 PM
1#include <a_samp>
2#include <a_mysql>
3#include <foreach>
4#include <mSelection>
5#include <streamer>
6
7//Configuring MySQL Stuff to set up the connection
8
9#define MYSQL_HOST "localhost" // Change this to your MySQL Remote IP or "localhost".
10#define MYSQL_USER "root" // Change this to your MySQL Database username.
11#define MYSQL_PASS "password" // Change this to your MySQL Database password.
12#define MYSQL_DATABASE "san_andreas" // Change this to your MySQL Database name.
13
14// Well, don't just enter random information and expect it to work, information
15// Should be valid and working fine.
16
17#define DIALOG_REGISTER (0)
18#define DIALOG_LOGIN (1)
19
20// Make sure the dialog IDs above do not match any dialog ID you're using in your
21// gamemode otherwise they won't do their job properly.
22
23main( )
24{
25
26}
27
28
29new MySQL: Database, Corrupt_Check[MAX_PLAYERS];
30
31//Creating an enumerator to store player's data for further use (below).
32
33//==============================================================================
34
35enum ENUM_PLAYER_DATA
36{
37 ID,
38 Name[25],
39
40 Password[65],
41 Salt[11],
42
43 PasswordFails,
44
45 Kills,
46 Deaths,
47
48 pSkin,
49
50 Score,
51 Cash,
52
53 Cache: Player_Cache,
54 bool:LoggedIn
55}
56
57new pInfo[MAX_PLAYERS][ENUM_PLAYER_DATA];
58new skinlist = mS_INVALID_LISTID;
59
60//==============================================================================
61//==============================================================================
62
63
64
65
66public OnGameModeInit()
67{
68 skinlist = LoadModelSelectionMenu("skins.txt");
69 new MySQLOpt: option_id = mysql_init_options();
70 mysql_set_option(option_id, AUTO_RECONNECT, true);
71 Database = mysql_connect(MYSQL_HOST, MYSQL_USER, MYSQL_PASS, MYSQL_DATABASE, option_id);
72
73 if(Database == MYSQL_INVALID_HANDLE || mysql_errno(Database) != 0)
74 {
75 print("I couldn't connect to the MySQL server, closing.");
76
77 SendRconCommand("exit");
78 return 1;
79 }
80
81 print("I have connected to the MySQL server."); // If the given MySQL details were all okay, this message prints to the log.
82
83 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), `KILLS` mediumint(7), `CASH` mediumint(7) NOT NULL DEFAULT '0',`DEATHS` mediumint(7) NOT NULL DEFAULT '0',`PSKIN` mediumint(7) NOT NULL DEFAULT '0', PRIMARY KEY (`ID`), UNIQUE KEY `USERNAME` (`USERNAME`))");
84
85 return 1;
86}
87
88public OnGameModeExit()
89{
90
91 foreach(new i: Player)
92 {
93 if(IsPlayerConnected(i)) // Checking if the players stored in "i" are connected.
94 {
95 OnPlayerDisconnect(i, 1); // We do that so players wouldn't lose their data upon server's close.
96 }
97 }
98 mysql_close(Database); // Closing the database.
99
100 return 1;
101}
102
103public OnPlayerConnect(playerid)
104{
105 new DB_Query[115];
106 //Resetting player information.
107 pInfo[playerid][Kills] = 0;
108 pInfo[playerid][Deaths] = 0;
109 pInfo[playerid][PasswordFails] = 0;
110
111 GetPlayerName(playerid, pInfo[playerid][Name], MAX_PLAYER_NAME); // Getting the player's name.
112 Corrupt_Check[playerid]++;
113
114 mysql_format(Database, DB_Query, sizeof(DB_Query), "SELECT * FROM `PLAYERS` WHERE `USERNAME` = '%e' LIMIT 1", pInfo[playerid][Name]);
115 mysql_tquery(Database, DB_Query, "OnPlayerDataCheck", "ii", playerid, Corrupt_Check[playerid]);
116 return 1;
117}
118
119public OnPlayerDisconnect(playerid, reason)
120{
121 Corrupt_Check[playerid]++;
122
123 new DB_Query[256];
124 //Running a query to save the player's data using the stored stuff.
125 mysql_format(Database, DB_Query, sizeof(DB_Query), "UPDATE `PLAYERS` SET `SCORE` = %d, `CASH` = %d, `KILLS` = %d, `DEATHS` = %d WHERE `ID` = %d LIMIT 1",
126 pInfo[playerid][Score], pInfo[playerid][Cash], pInfo[playerid][Kills], pInfo[playerid][Deaths], pInfo[playerid][ID]);
127 mysql_tquery(Database, DB_Query);
128
129 if(cache_is_valid(pInfo[playerid][Player_Cache])) //Checking if the player's cache ID is valid.
130 {
131 cache_delete(pInfo[playerid][Player_Cache]); // Deleting the cache.
132 pInfo[playerid][Player_Cache] = MYSQL_INVALID_CACHE; // Setting the stored player Cache as invalid.
133 }
134
135 pInfo[playerid][LoggedIn] = false;
136 print("OnPlayerDisconnect has been called."); // Sending message once OnPlayerDisconnect is called.
137 return 1;
138}
139
140public OnPlayerSpawn(playerid)
141{
142 SetPlayerSkin(playerid, pInfo[playerid][pSkin]);
143}
144
145
146public OnPlayerDeath(playerid, killerid, reason)
147{
148 // Increase death if self-death or got killed!
149 pInfo[playerid][Deaths]++;
150
151 if(killerid != INVALID_PLAYER_ID) // Checking if the killer of the player is valid.
152 {
153 //Increasing the kills of the killer and the deaths of the player.
154 pInfo[killerid][Kills]++;
155 }
156 return 1;
157}
158
159public OnPlayerRequestSpawn(playerid)
160{
161 if(pInfo[playerid][LoggedIn] == false) return 0; // Ignoring the request incase player isn't logged in.
162 return 1;
163}
164
165public OnDialogResponse(playerid, dialogid, response, listitem, inputtext[])
166{
167 switch (dialogid)
168 {
169 case DIALOG_LOGIN:
170 {
171
172 if(!response) return Kick(playerid);
173
174 new Salted_Key[65];
175 SHA256_PassHash(inputtext, pInfo[playerid][Salt], Salted_Key, 65);
176
177 if(strcmp(Salted_Key, pInfo[playerid][Password]) == 0)
178 {
179 // Now, password should be correct as well as the strings
180 // Matched with each other, so nothing is wrong until now.
181
182 // We will activate the cache of player to make use of it e.g.
183 // Retrieve their data.
184
185 cache_set_active(pInfo[playerid][Player_Cache]);
186
187 // Okay, we are retrieving the information now..
188 cache_get_value_int(0, "ID", pInfo[playerid][ID]);
189
190 cache_get_value_int(0, "KILLS", pInfo[playerid][Kills]);
191 cache_get_value_int(0, "DEATHS", pInfo[playerid][Deaths]);
192
193 cache_get_value_int(0, "SCORE", pInfo[playerid][Score]);
194 cache_get_value_int(0, "CASH", pInfo[playerid][Cash]);
195 cache_get_value_int(0, "PSKIN", pInfo[playerid][Score]);
196
197 SetPlayerScore(playerid, pInfo[playerid][Score]);
198
199 ResetPlayerMoney(playerid);
200 GivePlayerMoney(playerid, pInfo[playerid][Cash]);
201
202 // So, we have successfully retrieved data? Now deactivating the cache.
203
204 cache_delete(pInfo[playerid][Player_Cache]);
205 pInfo[playerid][Player_Cache] = MYSQL_INVALID_CACHE;
206
207 pInfo[playerid][LoggedIn] = true;
208 SendClientMessage(playerid, 0x00FF00FF, "Logged in to the account.");
209 }
210 else
211 {
212 new String[150];
213
214 pInfo[playerid][PasswordFails] += 1;
215 printf("%s has been failed to login. (%d)", pInfo[playerid][Name], pInfo[playerid][PasswordFails]);
216 // Printing the message that someone has failed to login to his account.
217
218 if (pInfo[playerid][PasswordFails] >= 3) // If the fails exceeded the limit we kick the player.
219 {
220 format(String, sizeof(String), "%s has been kicked Reason: {FF0000}(%d/3) Login fails.", pInfo[playerid][Name], pInfo[playerid][PasswordFails]);
221 SendClientMessageToAll(0x969696FF, String);
222 Kick(playerid);
223 }
224 else
225 {
226 // If the player didn't exceed the limits we send him a message that the password is wrong.
227 format(String, sizeof(String), "Wrong password, you have %d out of 3 tries.", pInfo[playerid][PasswordFails]);
228 SendClientMessage(playerid, 0xFF0000FF, String);
229
230 format(String, sizeof(String), "{FFFFFF}Welcome back, %s.\n\n{0099FF}This account is already registered.\n\
231 {0099FF}Please, input your password below to proceed to the game.\n\n", pInfo[playerid][Name]);
232 ShowPlayerDialog(playerid, DIALOG_LOGIN, DIALOG_STYLE_PASSWORD, "Login System", String, "Login", "Leave");
233 }
234 }
235 }
236 case DIALOG_REGISTER:
237 {
238 if(!response) return Kick(playerid);
239 if(strlen(inputtext) <= 5 || strlen(inputtext) > 60)
240 {
241 // If the password length is less than or equal to 5 and more than 60
242 // It repeats the process and shows error message as seen below.
243
244 SendClientMessage(playerid, 0x969696FF, "Invalid password length, should be 5 - 60.");
245
246 new String[150];
247
248 format(String, sizeof(String), "{FFFFFF}Welcome %s.\n\n{0099FF}This account is not registered.\n\
249 {0099FF}Please, input your password below to proceed.\n\n", pInfo[playerid][Name]);
250 ShowPlayerDialog(playerid, DIALOG_REGISTER, DIALOG_STYLE_PASSWORD, "Registration System", String, "Register", "Leave");
251
252 }
253 else
254 {
255
256 // Salting the player's password using SHA256 for a better security.
257
258 for (new i = 0; i < 10; i++)
259 {
260 pInfo[playerid][Salt][i] = random(79) + 47;
261 }
262
263 pInfo[playerid][Salt][10] = 0;
264 SHA256_PassHash(inputtext, pInfo[playerid][Salt], pInfo[playerid][Password], 65);
265
266 // Show Skin Selection
267 SendClientMessage(playerid, 0x00FF00FF, "Please select a skin!");
268 ShowModelSelectionMenu(playerid, skinlist, "Pick A Skin");
269 }
270 }
271 }
272 return 1;
273}
274
275forward OnPlayerDataCheck(playerid, corrupt_check);
276public OnPlayerDataCheck(playerid, corrupt_check)
277{
278 if (corrupt_check != Corrupt_Check[playerid]) return Kick(playerid);
279 // You'd have asked already what's corrput_check and how it'd benefit me?
280 // Well basically MySQL query takes long, incase a player leaves while its not proceeded
281 // With ID 1 for example, then another player comes as ID 1 it'll basically corrupt the data
282 // So, once the query is done, the player will have the wrong data assigned for himself.
283
284 new String[150];
285
286 if(cache_num_rows() > 0)
287 {
288 // If the player exists, everything is okay and nothing is wrongly detected
289 // The player's password and Saltion key gets stored as seen below
290 // So we won't have to get a headache just to match player's password.
291
292 cache_get_value(0, "PASSWORD", pInfo[playerid][Password], 65);
293 cache_get_value(0, "SALT", pInfo[playerid][Salt], 11);
294
295 pInfo[playerid][Player_Cache] = cache_save();
296 // ^ Storing the cache ID of the player for further use later.
297
298 format(String, sizeof(String), "{FFFFFF}Welcome back, %s.\n\n{0099FF}This account is already registered.\n\
299 {0099FF}Please, input your password below to proceed to the game.\n\n", pInfo[playerid][Name]);
300 ShowPlayerDialog(playerid, DIALOG_LOGIN, DIALOG_STYLE_PASSWORD, "Login System", String, "Login", "Leave");
301 }
302 else
303 {
304 format(String, sizeof(String), "{FFFFFF}Welcome %s.\n\n{0099FF}This account is not registered.\n\
305 {0099FF}Please, input your password below to proceed to the game.\n\n", pInfo[playerid][Name]);
306 ShowPlayerDialog(playerid, DIALOG_REGISTER, DIALOG_STYLE_PASSWORD, "Registration System", String, "Register", "Leave");
307 }
308 return 1;
309}
310
311//==============================================================================
312
313forward OnPlayerRegister(playerid);
314public OnPlayerRegister(playerid)
315{
316 // This gets called only when the player registers a new account.
317 SendClientMessage(playerid, 0x00FF00FF, "You are now registered and has been logged in.");
318
319
320 // Player Logged in!
321 pInfo[playerid][LoggedIn] = true;
322
323 //SetSpawnInfo(playerid, 0, , 2098.5088,1159.1156,11.6484, 65.2418, 0, 0, 0, 0, 0, 0 );
324 //SpawnPlayer(playerid);
325 return 1;
326}
327
328
329public OnPlayerModelSelection(playerid, response, listid, modelid)
330{
331 if(listid == skinlist)
332 {
333 if(response)
334 {
335 SetPlayerSkin(playerid, modelid); // set player skin
336 pInfo[playerid][pSkin] = modelid;
337
338 new DB_Query[225];
339
340 mysql_format(Database, DB_Query, sizeof(DB_Query), "INSERT INTO `PLAYERS` (`USERNAME`, `PASSWORD`, `SALT`, `SCORE`, `KILLS`, `CASH`, `DEATHS`, `PSKIN`)\
341 VALUES ('%e', '%s', '%e', '20', '0', '5000', '0', %d)", pInfo[playerid][Name], pInfo[playerid][Password], pInfo[playerid][Salt],pInfo[playerid][pSkin]);
342
343 mysql_tquery(Database, DB_Query, "OnPlayerRegister", "d", playerid);
344
345
346
347 }
348 else if(!response && !pInfo[playerid][LoggedIn])
349 {
350 SendClientMessage(playerid, 0x00FF00FF, "Please select a skin!");
351 ShowModelSelectionMenu(playerid, skinlist, "Pick A Skin");
352 }
353 return 1;
354 }
355 return 1;
356}
357
358//==============================================================================
359
360
361
362
363
364
365
366// End of script //