· 11 years ago · Jun 24, 2015, 04:22 PM
1
2 * Nazwa: Register & Login
3 * Autor: l0nger (aka AXV) <l0nger.programmer@gmail.com | l0nger@infinity-gaming.net>
4 * Wersja: 1.1.132011
5 *
6 * Wymagane technologie baz danych: MySQL
7 * Wymagane wtyczki: sscanf 2.8.1
8 * (C) <2013>, <l0nger.programmer@gmail.com | pawno.pl>
9**/
10
11#pragma dynamic 4096
12#pragma compress 1
13
14// naglowek SAMP
15#include a_samp
16
17#define ALREADY_REDEFINED_MAXPLAYERS
18
19#if defined ALREADY_REDEFINED_MAXPLAYERS
20 #undef MAX_PLAYERS
21 #define MAX_PLAYERS (5) // Prosze tutaj wpisac liczbe slotow serwera
22#endif
23
24// naglowki technologiczne
25#include sscanf // Y_Less > 2.8.1
26#include mysql // StrickenKid > 2.1.1
27
28// makrorozwieniecia
29#define outputChatMsg(%1,%2,%3) SendClientMessage(%1, %2, %3)
30#define hidePlayerDialog(%1) ShowPlayerDialog(%1, -1, NULL, {NULL}, {NULL}, {NULL}, {NULL})
31#define ResetVariablesInEnum(%0,%1) for(new __rvine; %1:__rvine != %1; __rvine++) %0[%1:__rvine]=0
32
33// Dane do polaczenia z MySQL
34#define SQL_HOSTNAME "localhost" // host
35#define SQL_USERNAME "username" // uzytkownik
36#define SQL_PASSWORD "password" // haslo
37#define SQL_DATABASE "database" // baza danych
38
39// Prefiks tabel
40#define PREFIX_DB "px_"
41
42// Zmienne stale, wyliczeniowe i nie tylko...
43const MAX_LOGIN_ATTEMPTS = (5); // maksymalnie 5 prob logowan
44const NULL = (0); // miejsce zerowe!
45
46enum {
47 dialog_info = 0,
48 dialog_login,
49 dialog_register
50};
51
52enum E_PLAYER_DATA {
53 /* bool */
54 bool:loggedIn,
55
56 /* chars */
57 nickname[24],
58 ipv4[16],
59
60 /* int */
61 timeOnline,
62 score,
63 money,
64 kills,
65 deaths,
66 suicides,
67 loginAttempts // max. 5 prob
68};
69
70new
71 PlayerData[MAX_PLAYERS][E_PLAYER_DATA],
72 MySQL:hMySQL;
73
74// Funkcje publiczne
75public OnFilterScriptInit() {
76 hMySQL = mysql_init(LOG_ONLY_ERRORS);
77 new mysqlHandle = mysql_connect(SQL_HOSTNAME, SQL_USERNAME, SQL_PASSWORD, SQL_DATABASE, hMySQL, true);
78
79 if(mysqlHandle) {
80 CreateDatabase();
81 print(" [REG&LOG]: Polaczono z baza danych!");
82 } else print(" [REG&LOG]: Nie mozna nawiazac polaczenia z baza danych! Sprawdz dane konfiguracyjne."), SendRconCommand("exit");
83
84 print(" Skrypt Register & Login - zaladowany poprawnie!");
85 return 1;
86}
87
88public OnFilterScriptExit() {
89 mysql_close(hMySQL);
90 return 1;
91}
92
93public OnPlayerConnect(playerid) {
94 ResetVariablesInEnum(PlayerData[playerid], E_PLAYER_DATA);
95
96 PlayerData[playerid][timeOnline]=gettime();
97 GetPlayerName(playerid, PlayerData[playerid][nickname], 24);
98 GetPlayerIp(playerid, PlayerData[playerid][ipv4], 16);
99
100 new
101 s_buf[120+MAX_PLAYER_NAME];
102
103 if(PlayerExistsInDatabase(playerid)) {
104 format(s_buf, sizeof(s_buf), "Witaj, %s!\nTwoje konto jest zarejestrowane.\nProszę wpisać hasło w poniższe okno, a następnie kliknąć przycisk 'ZALOGUJ'", PlayerData[playerid][nickname]);
105 ShowPlayerDialog(playerid, dialog_login, DIALOG_STYLE_PASSWORD, "Panel > Logowanie", s_buf, "Zaloguj", "Opusc");
106 } else {
107 format(s_buf, sizeof(s_buf), "Witaj, %s!\nTwoje konto nie jest zarejestrowane.\nProszę wpisać hasło w poniższe okno, a następnie kliknąć przycisk 'REJESTRUJ'", PlayerData[playerid][nickname]);
108 ShowPlayerDialog(playerid, dialog_register, DIALOG_STYLE_PASSWORD, "Panel > Rejestracja", s_buf, "Rejestruj", "");
109 }
110 return 1;
111}
112
113public OnPlayerDisconnect(playerid, reason) {
114 SavePlayerData(playerid, .leaving=1);
115}
116
117public OnPlayerDeath(playerid, killerid, reason) {
118 if(killerid != INVALID_PLAYER_ID) {
119 PlayerData[killerid][kills]++;
120 PlayerData[playerid][deaths]++;
121 } else {
122 PlayerData[playerid][deaths]++;
123 PlayerData[playerid][suicides]++;
124 }
125}
126
127public OnDialogResponse(playerid, dialogid, response, listitem, inputtext[]) {
128 static
129 s_buf[120+MAX_PLAYER_NAME];
130
131 switch(dialogid) {
132 case dialog_info: return 1;
133 case dialog_login: {
134 if(!response) {
135 outputChatMsg(playerid, -1, "Anulowales logowanie.");
136 hidePlayerDialog(playerid);
137 kickPlayer(playerid);
138 return 0;
139 }
140
141 if(!OnPlayerLogin(playerid, inputtext)) {
142 format(s_buf, sizeof(s_buf), "Witaj, %s!\nTwoje konto jest zarejestrowane.\nProszę wpisać hasło w poniższe okno, a następnie kliknąć przycisk 'ZALOGUJ'", PlayerData[playerid][nickname]);
143 ShowPlayerDialog(playerid, dialog_login, DIALOG_STYLE_PASSWORD, "Panel > Logowanie", s_buf, "Zaloguj", "Opusc");
144 }
145 }
146 case dialog_register: {
147 if(!response) return 1;
148
149 if(!OnPlayerRegister(playerid, inputtext)) {
150 format(s_buf, sizeof(s_buf), "Witaj, %s!\nTwoje konto nie jest zarejestrowane.\nProszę wpisać hasło w poniższe okno, a następnie kliknąć przycisk 'REJESTRUJ'", PlayerData[playerid][nickname]);
151 ShowPlayerDialog(playerid, dialog_register, DIALOG_STYLE_PASSWORD, "Panel > Rejestracja", s_buf, "Rejestruj", "");
152 }
153 }
154 }
155 return 1;
156}
157
158/**
159 * @name: OnPlayerLogin
160 * @desc: Funkcja sprawdza, czy haslo jest poprawne - jezeli tak - nastepuje wczytanie danych i wywolanie spawnu gracza
161 * @params: playerid=id gracza, password=wpisywane haslo w GUI
162 * @returns: brak
163 **/
164stock OnPlayerLogin(playerid, password[]) {
165 if(!password[0] || password[0] && !password[1]) {
166 PlayerData[playerid][loginAttempts]++;
167 return false;
168 }
169
170 // -- SPRAWDZANIE POPRAWNOSCI HASLA --
171 for(new i, j = strlen(password); i<j; i++) {
172 switch(password[i]) {
173 case 'a' .. 'z', 'A' .. 'Z', '0' .. '9', ';', '!', '@', '#', '$', '^', '&', '*', '(', ')': continue;
174 default: return false;
175 }
176 }
177
178 new
179 esc_password[32], // 25+7 na znaki escapowane
180 esc_nickname[24+5], // to co wyzej
181 s_buf[160];
182
183 mysql_real_escape_string(PlayerData[playerid][nickname], esc_nickname);
184 mysql_real_escape_string(password, esc_password);
185 format(s_buf, sizeof(s_buf), "SELECT (password = SHA1(CONCAT(salt, SHA1(CONCAT(salt, '%s'))))) AS valid FROM "PREFIX_DB"players WHERE nickname = '%s' LIMIT 1;", esc_password, esc_nickname);
186 printf(s_buf);
187 mysql_query(s_buf);
188 mysql_store_result();
189 new valid=mysql_fetch_int();
190 mysql_free_result();
191
192 if(!valid) {
193 format(s_buf, sizeof(s_buf), "proba zalogowania sie na konto %s", PlayerData[playerid][nickname]);
194 RegisterLogWithIP(PlayerData[playerid][ipv4], s_buf);
195
196 if(++PlayerData[playerid][loginAttempts]>=MAX_LOGIN_ATTEMPTS-1) {
197 format(s_buf, sizeof(s_buf), "proba zalogowania sie na konto %s - wyrzucony", PlayerData[playerid][nickname]);
198 RegisterLogWithIP(PlayerData[playerid][ipv4], s_buf);
199 hidePlayerDialog(playerid);
200 kickPlayer(playerid);
201 outputChatMsg(playerid, -1, "Wykorzystales limit prob zalogowan na konto. {ff0000}Zostales wyrzucony.");
202 return false;
203 }
204 outputChatMsg(playerid, -1, "Podane haslo jest nieprawidlowe.");
205 return false;
206 } else {
207 format(s_buf, sizeof(s_buf), "zalogowano sie na konto %s", PlayerData[playerid][nickname]);
208 RegisterLogWithIP(PlayerData[playerid][ipv4], s_buf);
209 PlayerData[playerid][loggedIn]=true;
210 }
211
212 format(s_buf, sizeof(s_buf), "UPDATE "PREFIX_DB"players SET is_online=1, ts_activelast=NOW(), address_ip='%s', logons=logons+1 WHERE nickname='%s';", PlayerData[playerid][ipv4], PlayerData[playerid][nickname]);
213 mysql_query(s_buf);
214
215 FetchPlayerData(playerid);
216 format(s_buf, sizeof(s_buf), "%s! Jest to Twoja %d wizyta na tym serwerze.", PlayerData[playerid][nickname], GetPlayerIntegerData(playerid, "logons"));
217 outputChatMsg(playerid, -1, s_buf);
218
219 SpawnPlayer(playerid);
220 return true;
221}
222
223/**
224 * @name: OnPlayerRegister
225 * @desc: Funkcja, ma za zadanie zarejestrowac gracza do bazy danych. Funkcja takze ma za zadanie sprawdzac znaki w hasle, czy nie sa nieprawidlowe
226 * @params: playerid=id gracza, password=wpisywane haslo w GUI
227 * @returns: false=jezeli haslo bedzie puste, lub nieprawidlowe, true=jezeli proces rejestracji przebiegnie prawidlowo
228 **/
229stock OnPlayerRegister(playerid, const password[]) {
230 if(!password[0] || password[0] && !password[1]) return false;
231
232 // -- SPRAWDZANIE POPRAWNOSCI HASLA --
233 for(new i, j = strlen(password); i<j; i++) {
234 switch(password[i]) {
235 case 'a' .. 'z', 'A' .. 'Z', '0' .. '9', ';', '!', '@', '#', '$', '^', '&', '*', '(', ')': continue;
236 case ':', '<', '>', '/', '[', ']': continue;
237 default: return false;
238 }
239 }
240
241 new
242 s_salt[5],
243 esc_password[32], // 25+7 na znaki escapowane
244 esc_nickname[24+5], // to co wyzej
245 s_buf[250];
246
247 // -- GENEROWANIE SOLI --
248 new i_salt;
249 while(i_salt<sizeof(s_salt)-1) {
250 s_salt[i_salt]=random(2)?(random(26)+(random(2)?'a':'A')):(random(10)+'0');
251 i_salt++;
252 }
253 print(s_salt);
254
255 mysql_real_escape_string(password, esc_password);
256 mysql_real_escape_string(PlayerData[playerid][nickname], esc_nickname);
257 format(s_buf, sizeof(s_buf), "INSERT INTO "PREFIX_DB"players (nickname, salt, password, address_ip, ts_register, ts_activelast) VALUES ('%s', '%s', SHA1(CONCAT('%s', SHA1(CONCAT('%s', '%s')))), '%s', NOW(), NOW());", esc_nickname, s_salt, s_salt, s_salt, esc_password, PlayerData[playerid][ipv4]);
258 if(mysql_query(s_buf) || !mysql_affected_rows()) {
259 format(s_buf, sizeof(s_buf), "proba rejestracji konta: %s - nieudana, blad z polaczeniem mysql", PlayerData[playerid][nickname]);
260 RegisterLogWithIP(PlayerData[playerid][ipv4], s_buf);
261 outputChatMsg(playerid, -1, "Wystapil blad podczas wysylania formularza danych. Sprobuj ponownie!");
262 hidePlayerDialog(playerid);
263 kickPlayer(playerid);
264 return false;
265 }
266
267 format(s_buf, sizeof(s_buf), "zarejestrowano konto: %s", PlayerData[playerid][nickname]);
268 RegisterLogWithIP(PlayerData[playerid][ipv4], s_buf);
269
270 format(s_buf, sizeof(s_buf), "Zarejestrowales konto: %s! Twoje haslo: %s - zapamietaj je do przyszlego logowania!", PlayerData[playerid][nickname], password);
271 outputChatMsg(playerid, -1, s_buf);
272
273 PlayerData[playerid][loggedIn]=true;
274 SpawnPlayer(playerid);
275 return true;
276}
277
278/**
279 * @name: RegisterLogWithIP
280 * @desc: Zapisywanie danych do logu rejestracji i logowan, lacznie z adresem ip
281 * @params: ivp4=IP gracza, desc=opis przebiegu logu
282 * @returns: brak
283 **/
284stock RegisterLogWithIP(const address_ip[], const desc[]) {
285 new
286 s_buf[160];
287
288 format(s_buf, sizeof(s_buf), "INSERT INTO "PREFIX_DB"logins_logs (ts, address_ip, opis) VALUES (NOW(), '%s', '%s');", address_ip, desc);
289 mysql_query(s_buf);
290 printf(" [REG&LOG]: Zarejestrowano zdarzenie id: %d -> %s, %s", mysql_insert_id(), desc, address_ip);
291}
292
293/**
294 * @name: PlayerExistsInDatabase
295 * @desc: Funkcja sprawdza, czy w bazie danych konto o podanym nicku istnieje
296 * @params: playerid=id gracza
297 * @returns: false=jezeli konto nie istnieje, true=jezeli konto istnieje
298 **/
299stock PlayerExistsInDatabase(playerid) {
300 new
301 s_buf[100];
302
303 format(s_buf, sizeof(s_buf), "SELECT 1 FROM "PREFIX_DB"players WHERE nickname='%s' LIMIT 1;", PlayerData[playerid][nickname]);
304 mysql_query(s_buf);
305 mysql_store_result();
306 new exid=mysql_num_rows() && mysql_fetch_int()>=1;
307 mysql_free_result();
308
309 return exid;
310}
311
312/**
313 * @name: SavePlayerData
314 * @desc: Funkcja zapisuje podstawowe dane gracza
315 * @params: playerid=id gracza, leaving=czy juz wychodzi (potrzebne do zmiany is_online)
316 * @returns: brak
317 **/
318stock SavePlayerData(playerid, leaving=0) {
319 new
320 s_buf[128];
321
322 format(s_buf, sizeof(s_buf), "UPDATE "PREFIX_DB"players SET time_online=%d, score=%d, money=%d, deaths=%d, suicides=%d, kills=%d, is_online=%d WHERE nickname='%s';",
323 gettime()-PlayerData[playerid][timeOnline],
324 PlayerData[playerid][score],
325 PlayerData[playerid][money],
326 PlayerData[playerid][deaths],
327 PlayerData[playerid][suicides],
328 PlayerData[playerid][kills],
329 (leaving==0)?1:0,
330 PlayerData[playerid][nickname]
331 );
332 mysql_query(s_buf);
333}
334
335/**
336 * @name: FetchPlayerData
337 * @desc: Funkcja pobiera wszystkie potrzebne dane, do wyswietlenia
338 * @params: playerid=id gracza
339 * @returns: brak
340 **/
341stock FetchPlayerData(playerid) {
342 new
343 s_buf[127],
344 tmpTime;
345
346 format(s_buf, 127, "SELECT money, score, kills, time_online, deaths, suicides FROM "PREFIX_DB"players WHERE nickname='%s' LIMIT 1;", PlayerData[playerid][nickname]);
347 mysql_query(s_buf);
348 mysql_store_result();
349 if(mysql_fetch_row(s_buf, "|")) {
350 sscanf(s_buf, "p<|>dddddd",
351 PlayerData[playerid][money],
352 PlayerData[playerid][score],
353 PlayerData[playerid][kills],
354 tmpTime,
355 PlayerData[playerid][deaths],
356 PlayerData[playerid][suicides]
357 );
358 }
359 mysql_free_result();
360
361 PlayerData[playerid][timeOnline]+=tmpTime;
362 SetPlayerScore(playerid, PlayerData[playerid][score]);
363 GivePlayerMoney(playerid, PlayerData[playerid][money]);
364}
365
366
367/**
368 * @name: GetPlayerIntegerData
369 * @desc: Funkcja pobiera dane w postaci liczbowej z podanej kolumny (arg. column)
370 * @params: playerid=id gracza, column=kolumna ISTNIEJACA w tabeli players
371 * @returns: zwroci otrzymana wartosc
372 **/
373stock GetPlayerIntegerData(playerid, const column[]) {
374 new
375 s_buf[127];
376
377 format(s_buf, 127, "SELECT %s FROM "PREFIX_DB"players WHERE nickname='%s' LIMIT 1;", column, PlayerData[playerid][nickname]);
378 mysql_query(s_buf);
379 mysql_store_result();
380 mysql_fetch_row(s_buf);
381 mysql_free_result();
382 return strval(s_buf);
383}
384
385
386/**
387 * @name: CreateDatabase
388 * @desc: Funkcja, ma za zadanie stworzyc tabele
389 * @params: option=opcja tworzenia (0=tworzy, jezeli ich nie ma, 1=usuwa tabele)
390 * @returns: brak
391 **/
392stock CreateDatabase(option=0) {
393 if(option==1) {
394 mysql_query("DROP TABLE "PREFIX_DB"players, "PREFIX_DB"logins_logs;");
395 }
396
397 new
398 s_buf[1024] = "CREATE TABLE IF NOT EXISTS "PREFIX_DB"players(";
399
400 strcat(s_buf, "id INT NOT NULL AUTO_INCREMENT,");
401 strcat(s_buf, "nickname VARCHAR(24) NOT NULL,");
402 strcat(s_buf, "password VARCHAR(48) NOT NULL,");
403 strcat(s_buf, "salt VARCHAR(5) NOT NULL,");
404 strcat(s_buf, "address_ip VARCHAR(16) NOT NULL DEFAULT '0.0.0.0',");
405 strcat(s_buf, "ts_register TIMESTAMP NOT NULL,");
406 strcat(s_buf, "ts_activelast TIMESTAMP NOT NULL,");
407 strcat(s_buf, "logons SMALLINT NOT NULL DEFAULT '1',");
408 strcat(s_buf, "time_online INT NOT NULL DEFAULT '0',");
409 strcat(s_buf, "score INT NOT NULL DEFAULT '1',");
410 strcat(s_buf, "money INT NOT NULL DEFAULT '1',");
411 strcat(s_buf, "deaths INT NOT NULL DEFAULT '0',");
412 strcat(s_buf, "suicides INT NOT NULL DEFAULT '0',");
413 strcat(s_buf, "kills INT NOT NULL DEFAULT '0',");
414 strcat(s_buf, "is_online TINYINT(1) NOT NULL DEFAULT '0',");
415 strcat(s_buf, "PRIMARY KEY (`id`), UNIQUE KEY `nickname` (`nickname`), KEY `score` (`score`));");
416 mysql_query(s_buf);
417
418 s_buf="CREATE TABLE IF NOT EXISTS "PREFIX_DB"logins_logs(";
419 strcat(s_buf, "id INT NOT NULL AUTO_INCREMENT,");
420 strcat(s_buf, "ts TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,");
421 strcat(s_buf, "address_ip VARCHAR(16) NOT NULL DEFAULT '0.0.0.0',");
422 strcat(s_buf, "opis VARCHAR(100) NOT NULL DEFAULT 'brak zarejestrowanego opisu',");
423 strcat(s_buf, "PRIMARY KEY (`id`));");
424 mysql_query(s_buf);
425}
426
427// Implementacja kickfix
428stock kickPlayer(playerid) {
429 SetTimerEx("func_kickPlayer", 100, 0, "i", playerid);
430}
431
432forward func_kickPlayer(playerid);
433public func_kickPlayer(playerid) {
434 if(IsPlayerConnected(playerid)) {
435 Kick(playerid);
436 return 1;
437 }
438 return 0;
439}