· 9 years ago · Dec 20, 2016, 02:08 PM
1#include <a_samp>
2
3#undef MAX_PLAYERS
4#define MAX_PLAYERS 20
5#include <a_mysql>
6
7
8#define MYSQL_HOST "127.0.0.1"
9#define MYSQL_USER "port_2762"
10#define MYSQL_PASSWORD "l58stwd0mg"
11#define MYSQL_DATABASE "port_2762"
12
13#define SECONDS_TO_LOGIN 30
14
15#define DEFAULT_POS_X -2393.2209
16#define DEFAULT_POS_Y -588.9634
17#define DEFAULT_POS_Z 132.7478
18#define DEFAULT_POS_A 125.8995
19
20
21new MySQL: g_SQL;
22
23enum E_PLAYERS
24{
25 ID,
26 Name[MAX_PLAYER_NAME],
27 Password[65],
28 Salt[17],
29 Kills,
30 Level,
31 Deaths,
32 Float: X_Pos,
33 Float: Y_Pos,
34 Float: Z_Pos,
35 Float: A_Pos,
36 Interior,
37 Cache: Cache_ID,
38 bool: IsLoggedIn,
39 LoginAttempts,
40 LoginTimer
41};
42new Player[MAX_PLAYERS][E_PLAYERS];
43new g_MysqlRaceCheck[MAX_PLAYERS];
44// dialog data
45enum
46{
47 DIALOG_UNUSED,
48 DIALOG_LOGIN,
49 DIALOG_REGISTER
50};
51main() {}
52public OnGameModeInit()
53{
54 new MySQLOpt: option_id = mysql_init_options();
55 mysql_set_option(option_id, AUTO_RECONNECT, true); // it automatically reconnects when loosing connection to mysql server
56 g_SQL = mysql_connect(MYSQL_HOST, MYSQL_USER, MYSQL_PASSWORD, MYSQL_DATABASE, option_id); // AUTO_RECONNECT is enabled for this connection handle only
57 if (g_SQL == MYSQL_INVALID_HANDLE || mysql_errno(g_SQL) != 0)
58 {
59 print("MySQL connection failed. Server is shutting down.");
60 SendRconCommand("exit"); // close the server if there is no connection
61 return 1;
62 }
63 print("MySQL connection is successful.");
64
65 // if the table has been created, the "SetupPlayerTable" function does not have any purpose so you may remove it completely
66 SetupPlayerTable();
67 return 1;
68}
69public OnGameModeExit()
70{
71 // save all player data before closing connection
72 for (new i = 0, j = GetPlayerPoolSize(); i <= j; i++) // GetPlayerPoolSize function was added in 0.3.7 version and gets the highest playerid currently in use on the server
73 {
74 if (IsPlayerConnected(i))
75 {
76 // reason is set to 1 for normal 'Quit'
77 OnPlayerDisconnect(i, 1);
78 }
79 }
80 mysql_close(g_SQL);
81 return 1;
82}
83public OnPlayerConnect(playerid)
84{
85 SendDeathMessage(playerid, playerid, 201);
86 new pname[MAX_PLAYER_NAME], string[25 + MAX_PLAYER_NAME];
87 GetPlayerName(playerid, pname, sizeof(pname));
88 format(string, sizeof(string), "{FFAF00}[INFO] {FFFFFF}%s has joined United City", pname);
89 SendClientMessageToAll(-1, string);
90
91
92 g_MysqlRaceCheck[playerid]++;
93
94 static const empty_player[E_PLAYERS];
95 Player[playerid] = empty_player;
96 GetPlayerName(playerid, Player[playerid][Name], MAX_PLAYER_NAME);
97
98 new query[103];
99 mysql_format(g_SQL, query, sizeof query, "SELECT * FROM `players` WHERE `username` = '%e' LIMIT 1", Player[playerid][Name]);
100 mysql_tquery(g_SQL, query, "OnPlayerDataLoaded", "dd", playerid, g_MysqlRaceCheck[playerid]);
101 return 1;
102}
103public OnPlayerDisconnect(playerid, reason)
104{
105 SendDeathMessage(playerid, playerid, 200);
106 new pname[MAX_PLAYER_NAME], string[39 + MAX_PLAYER_NAME];
107 GetPlayerName(playerid, pname, sizeof(pname));
108 switch(reason)
109 {
110 case 0: format(string, sizeof(string), "{FFAF00}[INFO] {FFFFFF}%s has left United City [Time Out]", pname);
111 case 1: format(string, sizeof(string), "{FFAF00}[INFO] {FFFFFF}%s has left United City [Leaving]", pname);
112 case 2: format(string, sizeof(string), "{FFAF00}[INFO] {FFFFFF}%s has left United City [Kicked]", pname);
113 }
114 SendClientMessageToAll(-1, string);
115
116 g_MysqlRaceCheck[playerid]++;
117 UpdatePlayerData(playerid, reason);
118 // if the player was kicked (either wrong password or taking too long) during the login part, remove the data from the memory
119 if (cache_is_valid(Player[playerid][Cache_ID]))
120 {
121 cache_delete(Player[playerid][Cache_ID]);
122 Player[playerid][Cache_ID] = MYSQL_INVALID_CACHE;
123 }
124 // if the player was kicked before the time expires (30 seconds), kill the timer
125 if (Player[playerid][LoginTimer])
126 {
127 KillTimer(Player[playerid][LoginTimer]);
128 Player[playerid][LoginTimer] = 0;
129 }
130 // sets "IsLoggedIn" to false when the player disconnects, it prevents from saving the player data twice when "gmx" is used
131 Player[playerid][IsLoggedIn] = false;
132 return 1;
133}
134public OnPlayerSpawn(playerid)
135{
136 // spawn the player to their last saved position
137 SetPlayerInterior(playerid, Player[playerid][Interior]);
138 SetPlayerPos(playerid, Player[playerid][X_Pos], Player[playerid][Y_Pos], Player[playerid][Z_Pos]);
139 SetPlayerFacingAngle(playerid, Player[playerid][A_Pos]);
140
141 SetCameraBehindPlayer(playerid);
142 return 1;
143}
144public OnPlayerDeath(playerid, killerid, reason)
145{
146 SendDeathMessage(killerid, playerid, reason);
147 UpdatePlayerDeaths(playerid);
148 UpdatePlayerKills(killerid);
149 return 1;
150}
151public OnPlayerRequestClass(playerid,classid)
152{
153 ClearAnimations(playerid);
154 ApplyAnimation(playerid, "ROB_BANK", "CAT_Safe_Rob", 1, 1, 0, 0, 0, 0, 1);
155 ApplyAnimation(playerid, "BOMBER", "BOM_PLANT_LOOP", 4.0, 1, 0, 0, 0, -1);
156 ClearAnimations(playerid);
157
158 new Float:spawnX, Float:spawnY, Float:spawnZ;
159 spawnX = -2495.3347;
160 spawnY = -699.1564;
161 spawnZ = 160.3984;
162 ClearAnimations(playerid);
163 SetPlayerInterior(playerid, 0);
164 SetPlayerPos(playerid, spawnX, spawnY, spawnZ);
165 SetPlayerCameraPos(playerid, spawnX + 5.0, spawnY +3.0, spawnZ + 2.00);
166 SetPlayerCameraLookAt(playerid, spawnX, spawnY, spawnZ);
167 SetPlayerFacingAngle(playerid, -90.0000);
168
169 return 1;
170}
171
172public OnDialogResponse(playerid, dialogid, response, listitem, inputtext[])
173{
174 switch (dialogid)
175 {
176 case DIALOG_UNUSED: return 1; // Useful for dialogs that contain only information and we do nothing depending on whether they responded or not
177 case DIALOG_LOGIN:
178 {
179 if (!response) return Kick(playerid);
180 new hashed_pass[65];
181 SHA256_PassHash(inputtext, Player[playerid][Salt], hashed_pass, 65);
182 if (strcmp(hashed_pass, Player[playerid][Password]) == 0)
183 {
184 //correct password, spawn the player
185 ShowPlayerDialog(playerid, DIALOG_UNUSED, DIALOG_STYLE_MSGBOX, "Login", "You have been successfully logged in.", "Okay", "");
186 // sets the specified cache as the active cache so we can retrieve the rest player data
187 cache_set_active(Player[playerid][Cache_ID]);
188 AssignPlayerData(playerid);
189 // remove the active cache from memory and unsets the active cache as well
190 cache_delete(Player[playerid][Cache_ID]);
191 Player[playerid][Cache_ID] = MYSQL_INVALID_CACHE;
192 KillTimer(Player[playerid][LoginTimer]);
193 Player[playerid][LoginTimer] = 0;
194 Player[playerid][IsLoggedIn] = true;
195 // spawn the player to their last saved position after login
196 SetSpawnInfo(playerid, NO_TEAM, 0, Player[playerid][X_Pos], Player[playerid][Y_Pos], Player[playerid][Z_Pos], Player[playerid][A_Pos], 0, 0, 0, 0, 0, 0);
197 SpawnPlayer(playerid);
198 }
199 else
200 {
201 Player[playerid][LoginAttempts]++;
202 if (Player[playerid][LoginAttempts] >= 3)
203 {
204 ShowPlayerDialog(playerid, DIALOG_UNUSED, DIALOG_STYLE_MSGBOX, "Login", "You have mistyped your password too often (3 times).", "Okay", "");
205 DelayedKick(playerid);
206 }
207 else ShowPlayerDialog(playerid, DIALOG_LOGIN, DIALOG_STYLE_PASSWORD, "Login", "Wrong password!\nPlease enter your password in the field below:", "Login", "Abort");
208 }
209 }
210 case DIALOG_REGISTER:
211 {
212 if (!response) return Kick(playerid);
213 if (strlen(inputtext) <= 5) return ShowPlayerDialog(playerid, DIALOG_REGISTER, DIALOG_STYLE_PASSWORD, "Registration", "Your password must be longer than 5 characters!\nPlease enter your password in the field below:", "Register", "Abort");
214 // 16 random characters from 33 to 126 (in ASCII) for the salt
215 for (new i = 0; i < 16; i++) Player[playerid][Salt][i] = random(94) + 33;
216 SHA256_PassHash(inputtext, Player[playerid][Salt], Player[playerid][Password], 65);
217 new query[221];
218 mysql_format(g_SQL, query, sizeof query, "INSERT INTO `players` (`username`, `password`, `salt`) VALUES ('%e', '%s', '%e')", Player[playerid][Name], Player[playerid][Password], Player[playerid][Salt]);
219 mysql_tquery(g_SQL, query, "OnPlayerRegister", "d", playerid);
220 }
221 default: return 0; // dialog ID was not found, search in other scripts
222 }
223 return 1;
224}
225//-----------------------------------------------------
226forward OnPlayerDataLoaded(playerid, race_check);
227public OnPlayerDataLoaded(playerid, race_check)
228{
229
230 if (race_check != g_MysqlRaceCheck[playerid]) return Kick(playerid);
231 new string[115];
232 if(cache_num_rows() > 0)
233 {
234 // we store the password and the salt so we can compare the password the player inputs
235 // and save the rest so we won't have to execute another query later
236 cache_get_value(0, "password", Player[playerid][Password], 65);
237 cache_get_value(0, "salt", Player[playerid][Salt], 17);
238 // saves the active cache in the memory and returns an cache-id to access it for later use
239 Player[playerid][Cache_ID] = cache_save();
240 format(string, sizeof string, "This account (%s) is registered. Please login by entering your password in the field below:", Player[playerid][Name]);
241 ShowPlayerDialog(playerid, DIALOG_LOGIN, DIALOG_STYLE_PASSWORD, "Login", string, "Login", "Abort");
242 // from now on, the player has 30 seconds to login
243 Player[playerid][LoginTimer] = SetTimerEx("OnLoginTimeout", SECONDS_TO_LOGIN * 1000, false, "d", playerid);
244 }
245 else
246 {
247 format(string, sizeof string, "Welcome %s, you can register by entering your password in the field below:", Player[playerid][Name]);
248 ShowPlayerDialog(playerid, DIALOG_REGISTER, DIALOG_STYLE_PASSWORD, "Registration", string, "Register", "Abort");
249 }
250 return 1;
251}
252forward OnLoginTimeout(playerid);
253public OnLoginTimeout(playerid)
254{
255 // reset the variable that stores the timerid
256 Player[playerid][LoginTimer] = 0;
257
258 ShowPlayerDialog(playerid, DIALOG_UNUSED, DIALOG_STYLE_MSGBOX, "Login", "You have been kicked for taking too long to login successfully to your account.", "Okay", "");
259 DelayedKick(playerid);
260 return 1;
261}
262forward OnPlayerRegister(playerid);
263public OnPlayerRegister(playerid)
264{
265 // retrieves the ID generated for an AUTO_INCREMENT column by the sent query
266 Player[playerid][ID] = cache_insert_id();
267 ShowPlayerDialog(playerid, DIALOG_UNUSED, DIALOG_STYLE_MSGBOX, "Registration", "Account successfully registered, you have been automatically logged in.", "Okay", "");
268 Player[playerid][IsLoggedIn] = true;
269 Player[playerid][X_Pos] = DEFAULT_POS_X;
270 Player[playerid][Y_Pos] = DEFAULT_POS_Y;
271 Player[playerid][Z_Pos] = DEFAULT_POS_Z;
272 Player[playerid][A_Pos] = DEFAULT_POS_A;
273
274 SetSpawnInfo(playerid, NO_TEAM, 0, Player[playerid][X_Pos], Player[playerid][Y_Pos], Player[playerid][Z_Pos], Player[playerid][A_Pos], 0, 0, 0, 0, 0, 0);
275 SpawnPlayer(playerid);
276 return 1;
277}
278forward _KickPlayerDelayed(playerid);
279public _KickPlayerDelayed(playerid)
280{
281 Kick(playerid);
282 return 1;
283}
284//-----------------------------------------------------
285AssignPlayerData(playerid)
286{
287 cache_get_value_int(0, "id", Player[playerid][ID]);
288
289 cache_get_value_int(0, "kills", Player[playerid][Kills]);
290 cache_get_value_int(0, "deaths", Player[playerid][Deaths]);
291
292 cache_get_value_float(0, "x", Player[playerid][X_Pos]);
293 cache_get_value_float(0, "y", Player[playerid][Y_Pos]);
294 cache_get_value_float(0, "z", Player[playerid][Z_Pos]);
295 cache_get_value_float(0, "angle", Player[playerid][A_Pos]);
296 cache_get_value_int(0, "interior", Player[playerid][Interior]);
297 return 1;
298}
299DelayedKick(playerid, time = 500)
300{
301 SetTimerEx("_KickPlayerDelayed", time, false, "d", playerid);
302 return 1;
303}
304SetupPlayerTable()
305{
306 mysql_tquery(g_SQL, "CREATE TABLE IF NOT EXISTS `players` (`id` int(11) NOT NULL AUTO_INCREMENT,`username` varchar(24) NOT NULL,`password` char(64) NOT NULL,`salt` char(16) NOT NULL,`kills` mediumint(8) NOT NULL DEFAULT '0',`deaths` mediumint(8) NOT NULL DEFAULT '0',`x` float NOT NULL DEFAULT '0',`y` float NOT NULL DEFAULT '0',`z` float NOT NULL DEFAULT '0',`angle` float NOT NULL DEFAULT '0',`interior` tinyint(3) NOT NULL DEFAULT '0', PRIMARY KEY (`id`), UNIQUE KEY `username` (`username`))");
307 return 1;
308}
309UpdatePlayerData(playerid, reason)
310{
311 if (Player[playerid][IsLoggedIn] == false) return 0;
312 // if the client crashed, it's not possible to get the player's position in OnPlayerDisconnect callback
313 // so we will use the last saved position (in case of a player who registered and crashed/kicked, the position will be the default spawn point)
314 if (reason == 1)
315 {
316 GetPlayerPos(playerid, Player[playerid][X_Pos], Player[playerid][Y_Pos], Player[playerid][Z_Pos]);
317 GetPlayerFacingAngle(playerid, Player[playerid][A_Pos]);
318 }
319
320 new query[145];
321 mysql_format(g_SQL, query, sizeof query, "UPDATE `players` SET `x` = %f, `y` = %f, `z` = %f, `angle` = %f, `interior` = %d WHERE `id` = %d LIMIT 1", Player[playerid][X_Pos], Player[playerid][Y_Pos], Player[playerid][Z_Pos], Player[playerid][A_Pos], GetPlayerInterior(playerid), Player[playerid][ID]);
322 mysql_tquery(g_SQL, query);
323 return 1;
324}
325UpdatePlayerDeaths(playerid)
326{
327 if (Player[playerid][IsLoggedIn] == false) return 0;
328
329 Player[playerid][Deaths]++;
330
331 new query[70];
332 mysql_format(g_SQL, query, sizeof query, "UPDATE `players` SET `deaths` = %d WHERE `id` = %d LIMIT 1", Player[playerid][Deaths], Player[playerid][ID]);
333 mysql_tquery(g_SQL, query);
334 return 1;
335}
336UpdatePlayerKills(killerid)
337{
338 // we must check before if the killer wasn't valid (connected) player to avoid run time error 4
339 if (killerid == INVALID_PLAYER_ID) return 0;
340 if (Player[killerid][IsLoggedIn] == false) return 0;
341
342 Player[killerid][Kills]++;
343
344 new query[70];
345 mysql_format(g_SQL, query, sizeof query, "UPDATE `players` SET `kills` = %d WHERE `id` = %d LIMIT 1", Player[killerid][Deaths], Player[killerid][ID]);
346 mysql_tquery(g_SQL, query);
347 return 1;
348}