Skocz do zawartości
KoPcIu

GameMode oparty o MySQL - Część 2

Rekomendowane odpowiedzi

GameMode oparty o MySQL - Część 2

 

Wstęp

W tej wersji, połączymy się z bazą danych jak i pierwszy raz wejdziemy na swój serwer.

 

Nowa zmienna w config.xml

W poprzedniej wersji poradnika, w pliku konfiguracyjnym zabrakło dodatkowego argumentu jakim jest wersja developerska, pozwoli ona na wyświetlanie większej ilości wiadomości w konsoli, w celu lepszej identyfikacji błędu.

 

Plik config.xml:

W grupie GM dodamy jako ostatnią zmienną linijkę:

		<dev>true</dev>					<!-- Wersja developerska -->

Dodatkowo w pliku settings.inc po załadowaniu wersji mapy dodamy:

		Settings[gmDev] = xml_get_bool(xml_file, "settings/gm/dev");

Ostatni plik to core.inc w enum'ie po zmiennej "gmVersion" dodamy linijkę:

	bool:gmDev,		// Wersja developerska

Możliwość wejścia na serwer

Pierwszy etap jaki powinniśmy wykonać to dodanie listy skinów, spośród, której gracz wybierze swój skin.

 

W OnGameModeInit na końcu public'a umieścimy skiny, w moim wypadku będzie to lista dwóch skinów.

	AddPlayerClass(59, 1242.1100, -2036.9662, 60.0575, 275.4927, 0, 0, 0, 0, 0, 0);
	AddPlayerClass(30, 1242.1100, -2036.9662, 60.0575, 275.4927, 0, 0, 0, 0, 0, 0);

Oczywiście, gracz będzie mógł wybrać skin, jednak najpierw trzeba by mu dać możliwość sprawdzić jak wygląda skin, w tym celu stwórzmy proste wybranie skinu. Dodamy na dole Naszej mapy public gdy gracz znajdzie się właśnie w wyborze skina:

public OnPlayerRequestClass(playerid, classid) {
	SetPlayerPos(playerid, 1422.5410, -2253.7104, 13.5469);
	SetPlayerFacingAngle(playerid, 53.9220);
	SetPlayerCameraPos(playerid, 1414.9441, -2247.8792, 13.5469);
	SetPlayerCameraLookAt(playerid, 1422.5410, -2253.7104, 13.5469);
	return true;
}

Po wykonaniu powyższych operacji, po raz pierwszy możemy dołączyć do swojego serwera.

2.png

 

Funkcja Core_Start

Po wczytaniu konfiguracji wykorzystajmy dane pobrane z pliku konfiguracyjnego. Pierwszym etapem będzie utworzenie funkcji Core_Start , jej kod wygląda następująco, a wrzucimy go do pliku core.inc :

stock Core_Start() {
	new String[128];
	format(String, sizeof(String), "hostname %s", Settings[servName]);
	SendRconCommand(String);
	SetGameModeText(Settings[servMode]);
	format(String, sizeof(String), "mapname %s", Settings[servMap]);
	SendRconCommand(String);
	
	if(MAX_PLAYERS != GetMaxPlayers())
		return Core_Stop(REASON_MAXPLAYERS);
	
	printf("[LOAD] Gamemode %s w wersji %s by %s zostal zaladowany!", Settings[gmName], Settings[gmVersion], Settings[gmAuthor]);
	return true;
}

Kod funkcji wygląda następująco. Pierwszym etapem jest ustalenie nazwy serwera, nazwy moda jak i nazwy mapy. Wszystkie te dane są wyświetlane w oknie programu SA-Mp. Następnym etapem jest sprawdzenie liczby slotów. Wiele osób zapyta o co tutaj chodzi? Jak wiadomo standardowo liczba MAX_PLAYERS posiada 500 slotów, jednak nie każdy serwer posiada taką ilość slotów, dlatego sami zdefiniujemy tą liczbę, aby nie marnować pamięci. Sloty sprawdzamy porównując właśnie zmienną MAX_PLAYERS z funkcją GetMaxPlayers, która pobiera liczbę slotów z pliku konfiguracyjnego. Gdy liczby te nie będą równe wykonujemy funkcję Core_Stop z parametrem REASON_MAXPLAYERS. Dodatkowo wyświetlamy informacje ze Nasz GameMode został poprawnie wczytany.

 

Nie zapominając o MAX_PLAYERS. W pliku core.inc tuż pod definicjami Naszych błędów przy wyłączaniu serwera umieśćmy kod:

#undef MAX_PLAYERS
#define MAX_PLAYERS 			10

Najpierw, "usuwamy" definicje MAX_PLAYERS, a następnie tworzymy ją znów, jednak tym razem ma ona wartość 10.

 

Dodatkowo w tym samym pliku musimy dodać definicję REASON_MAXPLAYERS, będzie to błąd o numerze 2. Kod ten umieszczamy za naszym błędem o numerze 1:

#define REASON_MAXPLAYERS			2

Kolejnym etapem jest dodanie do funkcji Core_Stop informacji jeśli wykona się błąd numer 2. Plik core.inc , treść następnego case wygląda następująco:

		case REASON_MAXPLAYERS: {
			print("[ERROR] Liczba slotow serwera nie zgadza sie z ta zapisana w core.inc .");
			printf("[ERROR] Core.inc > %d, Server.cfg > %d", MAX_PLAYERS, GetMaxPlayers());
		}

Ostatnim etapem będzie wykonanie funkcji Core_Start przy starcie serwera. W pliku tutorial.pwn w public'u OnGameModeInit po załadowaniu konfiguracji umieśćmy kod odwołujący się do funkcji Core_Start .

	Core_Start();

Tabela MySQL

Wcześniejsza treść poradnika znacznie odbiegała od treści całego poradnika, jednak teraz zaczynamy prawdziwą zabawę dotyczącą operacji na bazie danych.

 

Pierwszą tabelą jaką utworzymy będzie, tabela zawierająca dane gracza. Zobaczmy jak będzie wyglądało zapytanie.

CREATE TABLE IF NOT EXISTS `Tut_Players` (
  `pid` int(5) NOT NULL AUTO_INCREMENT,
  `name` varchar(24) NOT NULL,
  `password` varchar(32) NOT NULL,
  `salt` int(6) NOT NULL,
  `register_address` varchar(16) NOT NULL,
  `lastlogin_address` varchar(16) NOT NULL,
  `register_time` int(11) NOT NULL,
  `lastlogin_time` int(11) NOT NULL,
  `level` int(3) NOT NULL DEFAULT '0',
  `money` int(11) NOT NULL DEFAULT '5000',
  `score` int(11) NOT NULL DEFAULT '0',
  `visits` int(5) NOT NULL DEFAULT '1',
  `autologin` tinyint(1) NOT NULL DEFAULT '0',
  PRIMARY KEY (`pid`)
);

Okej, na początku zapytania SQL, przekazujemy informacje że chcemy utworzyć tabelę "Tut_Players", jednak tylko wtedy gdy ona nie istnieje. Kolejne linijki oznaczają tutaj konkretne pola w jakich będziemy trzymać dane swoich graczy.

  • pid - Unikalne ID gracza, pole to INT(wartość liczbowa), o długości maksymalnie 5 znaków,
  • name - Nazwa gracza, pole to VARCHAR(pole tekstowe), o długości maksymalnie 24 znaków,
  • password - Hasło gracza zakodowane w MD5, pole to VARCHAR(pole tekstowe), o długości maksymalnie 32 znaków,
  • salt - Dodatkowe zabezpieczenie do hasła, pole to INT(wartość liczbowa), o długości maksymalnie 6 znaków,
  • register_address - Adres IP z rejestracji, pole to VARCHAR(pole tekstowe), o długości maksymalnie 16 znaków,
  • lastlogin_address - Adres IP ostatniego logowania, pole to VARCHAR(pole tekstowe), o długości maksymalnie 16 znaków,
  • register_time - Data rejestracji(UNIX), pole to INT(wartość liczbowa), o długości maksymalnie 11 znaków,
  • lastlogin_time - Data ostatniego logowania(UNIX), pole to INT(wartość liczbowa), o długości maksymalnie 11 znaków,
  • level - Level gracza(Admin, Mod, VIP), pole to INT(wartość liczbowa), o długości maksymalnie 3 znaków,
  • money - Pieniądze gracza, pole to INT(wartość liczbowa), o długości maksymalnie 11 znaków,
  • score - Punkty gracza, pole to INT(wartość liczbowa), o długości maksymalnie 11 znaków,
  • visits - Liczba wizyt gracza(wartość liczbowa), o długości maksymalnie 5 znaków,
  • autologin - Auto logowanie na podstawie adresu IP, pole to tinyint(wartość liczbowa), o długości maksymalnie 1 znaku, inaczej bool.

Dodatkowo NOT NULL oznacza, że żadne pole nie może zostać puste, a DEFAULT oznacza domyślną wartość pola, jeśli nie zostanie uzupełnione.

 

Tworzenie tabeli

Zapytanie podane wyżej możemy wykonać dzięki PHPMyAdmin.

Więcej informacji na ten temat już niebawem!

 

Połączenie MySQL

W pliku mysql.inc utworzymy funkcję do połączenia z naszą bazą danych. Dodatkowo w niej sprawdzimy czy wersja developerska jest włączona, jeśli tak, logujemy wszystkie zapytania, w przeciwieństwie tylko błędy. Funkcja mysql_ping sprawdzamy czy nasze połączenie przebiegło pomyślnie, jeśli nie zawracamy FALSE.

stock Mysql_Connect(host[], user[], pass[], name[]) {
	if(Settings[gmDev])
		print("[MySQL] Proba polaczenia z baza danych.");

	new MySQL:Handle = mysql_init((Settings[gmDev]) ? LOG_ALL : LOG_ONLY_ERRORS, true);
	mysql_connect(host, user, pass, name, Handle, true);
	if(mysql_ping())
		return false;
	
	if(Settings[gmDev])
		print("[MySQL] Pomyslnie polaczono z baza danych!");
	return true;
}

Następnym etapem będzie wykonanie tej funkcji. Wiadomo połączenie startujemy w pliku tutorial.pwn w public'u OnGameModeInit, tuż po załadowaniu danych z pliku konfiguracyjnego. Dodatkowo jak poprzednio jeśli połączenie się nie uda wykonujemy funkcję Core_Stop jednak tym razem z parametrem REASON_MYSQLCONNECTION, w celu wyłączenia serwera. Jako parametry podajemy Nasze dane bazy danych.

	check = Mysql_Connect(Settings[dbHost], Settings[dbUser], Settings[dbPass], Settings[dbName]);
	if(!check)
		return Core_Stop(REASON_MYSQLCONNECTION);

Zdefiniujmy REASON_MYSQLCONNECTION w pliku core.inc w tej części dodawaliśmy już tam inną zmienną identyfikacyjną, z numerem ID 2. Kod jaki dodamy:

#define REASON_MYSQLCONNECTION		3

A w funkcji Core_Stop jako kolejny case umieśćmy kod:

		case REASON_MYSQLCONNECTION: {
			print("[ERROR] Problemy przy polaczeniu z baza danych.");
			printf("[ERROR] Dane: %s %s %s %s", Settings[dbHost], Settings[dbUser], Settings[dbPass], Settings[dbName]);
		}

Sprawdźmy jak wygląda Nasz GameMode, odpalmy go. U mnie konsola wygląda następująco:

 

3.png

 

Przygotowanie danych gracza

Przyszedł czas na rozpoczęcie systemu rejestracji i logowania. Proces ten nie będzie należał do najprostszych, ponieważ wiele plików będziemy edytować, aby wykonać proste operacje. Cały system będziemy starać się pisać tak, aby późniejsza modyfikacja nie sprawiała nam wiele problemów.

 

Tablica danych gracza

Dane graczy przetrzymywać będziemy w Naszej bazie danych, jednak nie będziemy pobierać tych danych za każdym razem gdy będziemy potrzebować informacji o graczu, dlatego przy wejściu gracza na serwer pobierzemy je, a następnie przy wyjściu gracza, prześlemy je do bazy.

 

Tablica z danymi gracza będzie znajdować się w pliku core.inc, tuż pod tablicą z ustawieniami(Settings). Wygląda ona następująco:

enum e_Player {
	pID,            				// Unikalne ID gracza
	pName[MAX_PLAYER_NAME],				// Nazwa gracza
	pPassword[33],					// Zakodowane haslo md5, system md5(SALT.PASSWORD)
	pSalt,						// Szescio cyfrowe zabezpieczenie do hasla
	pRegisterAddress[MAX_PLAYER_IP],	        // Adres IP z rejestracji
	pLastAddress[MAX_PLAYER_IP],		        // Adres IP ostatniego logowania
	pRegisterTime,					// Data rejestracji, w formacie UNIX. Liczba sekund od  00:00:00, 1 stycznia 1970 (UTC)
	pLastTime,					// Data ostatniego logowania, w formacie UNIX
	pLevel,						// Admin, Mod, VIP? Wiecej w innej czesci
	pMoney,						// Ilosc gotowki
	pScore,						// Ilosc punktow score
	pVisits,					// Licza wizyt
	bool:pAutoLogin,				// Auto logowanie na podstawie adresu IP. 1 - ON, 0 - OFF 
	
	bool:pLogged,					// Gracz zalogowany. 1 - TAK, 0 - NIE
	pAutoKick,					// Zmienna liczaca sekundy, przy braku zalogowania
	pAttemptLogin,					// Zmienna liczaca, bledne proby logowania
}
new Player[MAX_PLAYERS][e_Player];

Mamy tutaj podstawowe dane graczy, niektóre pobierzemy z bazy danych, a niektóre zostały umieszczone tam przyszłościowo, do systemu logowania/rejestracji. Dodatkowo używamy tutaj definicji MAX_PLAYERS jak dobrze pamiętamy ma ona teraz wartość 10.

 

W zmiennej zawierającej informacje na temat adresu IP użyliśmy definicji MAX_PLAYER_IP dodajmy ją na górę pliku powiedzmy pod MAX_PLAYERS, aby określić długość adresu IP gracza:

#define MAX_PLAYER_IP 			16

PlayerName(playerid)

Wiele GameMode'ów posiada właśnie tą funkcję, dzięki której pobieramy nazwę Naszego gracza. Funkcja ta zazwyczaj zawiera tablice o wielkości 24 znaków i funkcję GetPlayerName, wykonującą się za każdym razem gdy pobieramy te dane. Rozwiązanie te jest całkowicie bez sensu, ponieważ podczas godzinnej pracy serwera wykonamy tą funkcję kilkaset razy...

 

W Naszej tablicy znajduje się zmienna Player[playerid][pName] i to właśnie ona będzie zawierać Naszą nazwę użytkownika. Po podłączeniu gracza do serwera pobierzemy nick gracza i zapiszemy go do tej zmiennej. Plik tutorial.pwn dodajmy tam public:

public OnPlayerConnect(playerid) {
	GetPlayerName(playerid, Player[playerid][pName], MAX_PLAYER_NAME);
	return true;
}

Wiele osób przyzwyczajonych jest właśnie do używania PlayerName(playerid) dlatego nie odbierajmy sobie tej przyjemności i stwórzmy prostą definicję, w pliku core.inc, dodajmy ją powiedzmy pod MAX_PLAYER_IP :

#define PlayerName(%0) \
Player[%0][pName]

#define PlayerPID(%0) \
Player[%0][pID]

Dodatkowo w ten sam sposób będziemy mogli korzystać z PlayerPID pobierając unikalne ID gracza.

 

Pobranie danych przy połączeniu

Ostatnim etapem w tej części będzie próba pobrania danych jeśli konto będzie istniało.

 

Utwórzmy najpierw nowy moduł o nazwie player.inc , jak pamiętamy w katalogu source . Nie zapominajmy o załadowaniu tego pliku do naszego GameMode, w tym celu udajmy się do pliku header.inc i na końcu pliku umieśćmy:

#include "source/player.inc"

W pliku player.inc utwórzmy funkcję pobierająca dane gracza, a jeśli nie odnajdziemy gracza zwracamy INVALID_PLAYER_ID .

stock Player_GetData(playerid) {
	if(Settings[gmDev])
		printf("[PLAYER] Zaczynam wczytywac dane gracza %s[%d].", PlayerName(playerid), playerid);

	new Query[256];
	format(Query, sizeof(Query), "SELECT `pid`,`password`,`salt`,`register_address`,`lastlogin_address`,`register_time`,`lastlogin_time`,`level`,`money`,`score`,`visits`,`autologin` FROM `%sPlayers` WHERE `name`='%s';", Settings[dbPrefix], PlayerName(playerid));
	mysql_query(Query);
	mysql_store_result();
	if(mysql_num_rows()) {
		mysql_fetch_row(Query);
		sscanf(Query,"p<|>ds[33]ds[16]s[16]ddddddl",
			Player[playerid][pID],
			Player[playerid][pPassword],
			Player[playerid][pSalt],
			Player[playerid][pRegisterAddress],
			Player[playerid][pLastAddress],
			Player[playerid][pRegisterTime],
			Player[playerid][pLastTime],
			Player[playerid][pLevel],
			Player[playerid][pMoney],
			Player[playerid][pScore],
			Player[playerid][pVisits],
			Player[playerid][pAutoLogin]
		);
		mysql_free_result();
	} else {
		mysql_free_result();
		if(Settings[gmDev])
			printf("[PLAYER] Nie odnalazlem gracza %s[%d].", PlayerName(playerid), playerid);
		return INVALID_PLAYER_ID;
	}
	
	if(Settings[gmDev])
		printf("[PLAYER] Zakonczylem wczytywac dane gracza %s[%d, UID: %d].", PlayerName(playerid), playerid, PlayerPID(playerid));
	return Player[playerid][pID];
}

Na początku tworzymy zapytanie. Pobierz, następnie podajemy dane jakie chcemy pobrać, z jakiej tabeli, gdzie nazwa gracza to...
Kolejnym etapem jest wykonanie zapytania, później wykonujemy funkcje mysql_store_result, dzięki której otrzymamy możliwość sprawdzenia liczby rekordów, dzięki mysql_num_rows . Kolejnym etapem jest fetch'owanie danych do formy POLE1|POLE2|POLE3... ostatnim krokiem jest rozdzielenie danych, użyjemy do tego sscanf'a. Ostatnim krokiem jest uwolnienie danych, czyli mysql_free_result. Gdy odnajdziemy użytkownika zwracamy jego numer identyfikacyjny, jeśli jednak nie zwracamy INVALID_PLAYER_ID 
 
Nie martw się jeśli niewiele zrozumiałeś, niebawem postaram się opisać w osobnym poradniku kilka prostych zapytań, jak i opisać wszystkie funkcje pluginu MySQL.
 
Ostatnim etapem w tym poradniku będzie wykonanie funkcji Player_GetData po podłączeniu gracza do serwera. Przejdźmy do pliku tutorial.pwn i umieśćmy w OnPlayerConnect ten oto kod:
	Player_GetData(playerid);
Zakończenie
Na tym etapie rozpoczęliśmy już pierwsze kroki w stronę rejestracji jak i logowania. Po raz pierwszy dostaliśmy się na Nasz serwer jak i połączyliśmy się z bazą danych.
 
W kolejnej części zajmiemy się rejestracją, logowaniem, jednak głównie będziemy operować na dialogach.
 

W załączniku znajduje się projekt, który do tej pory utworzyliśmy   :)

 

Pozdrawiam i czekam na opinie!

Tutorial_#2.zip

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

Nie wiem, czy to literówka, czy też nie. Ale warto to przeanalizować. Zapewne podczas tworzenia tego poradnika, miałeś myśl, aby to unikalne ID było ciągiem znaków, przynajmniej mi się tak zdaje. Potem, zmieniłeś zdanie i zastąpiłeś to pid, czyli id, które się zwiększa, co stworzony rekord.

 `pid` int(5) NOT NULL AUTO_INCREMENT,
pID[64],

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

Zupełnie nowe spojrzenie na programowanie gamemode do SA-MPa, bardzo dobrze wyjaśnione, podoba mi się :)

Miejmy nadzieję że dzięki temu będą powstawały lepsze gamemode niż te które aktualnie mamy dostępne w sieci.

Pozdrawiam

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

Bardzo dobry poradnik, miałem zmieniać dini na mysql lecz nie wiedziałem jak to zrobić, dlatego "odstawiałem" to w czasie, Ty to doskonale wytłumaczyłeś. Dziękuję. ;)

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

Nie wiem, czy to literówka, czy też nie. Ale warto to przeanalizować. Zapewne podczas tworzenia tego poradnika, miałeś myśl, aby to unikalne ID było ciągiem znaków, przynajmniej mi się tak zdaje. Potem, zmieniłeś zdanie i zastąpiłeś to pid, czyli id, które się zwiększa, co stworzony rekord.

 `pid` int(5) NOT NULL AUTO_INCREMENT,
pID[64],

 

Enum Player to edycja Setting, jak kopiowałem po prostu zmieniłem nazwę, ale zapomniałem poprawić. Dziękuje za zwrócenie uwagi.

 

Zupełnie nowe spojrzenie na programowanie gamemode do SA-MPa, bardzo dobrze wyjaśnione, podoba mi się :)

Miejmy nadzieję że dzięki temu będą powstawały lepsze gamemode niż te które aktualnie mamy dostępne w sieci.

Pozdrawiam

 

Mam nadzieje, że kiedyś usłyszę, "napisałem ciekawy gamemode, na podstawie Twojego poradnika"... Kto wie?

 

@Quis Gdy ktoś zrobi takiego GMa i tak go nie wyda za darmo, bez jaj, przecież to duużo pracy

@Topic Fajny poradnik, tak z ciekawości, planujesz pisać coś takiego o SQLite?

 

Aktualnie czeka mnie wiele pracy przy tej części, dlatego nie mam aż tak odległych planów.

 

Pozdrawiam!

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

Piękny poradnik, gratuluję KoPcIu! Na pewno wielu użytkowników skorzysta z twojego poradnika, czekam na trzecią część , mam nadzieję że opiszesz wszystkie funkcje MySQL ponieważ też chciałbym się nauczyć coś więcej od Ciebie. 

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

Piękny poradnik, gratuluję KoPcIu! Na pewno wielu użytkowników skorzysta z twojego poradnika, czekam na trzecią część , mam nadzieję że opiszesz wszystkie funkcje MySQL ponieważ też chciałbym się nauczyć coś więcej od Ciebie. 

http://wiki.sa-mp.com/wiki/MySQL/R33

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

 

Dokładniej: http://wiki.sa-mp.com/wiki/MySQL_Plugin

 

Aktualnie w ukrytym dziale mam przetłumaczone wszystkie funkcje. Wiele z nich musiałem napisać po swojemu, a dodatkowo muszę przygotować przykłady. Niebawem cały "poradnik" powinien znaleźć się na forum.

 

Pozdrawiam!

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

Nie napisze "napisałem gamemode na podstawie Twojego poradnika" ale za to: Dziękuję za objaśnienie podstaw MySQL, dzięki czemu w 1 dzień nauczyłem się wszystkich możliwości sam, lecz dzięki Twoim podstawom też, bez nich nic bym nie osiągnął :D

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

Mam pytanie, czemu mi wywala błąd związany z linią, w której jest " ); ", gdy dodam kilka kolejnych zmiennych, typu team, ranga, i usunę visity, dodam dobrzer d, l itp. ?

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

Mam pytanie, czemu mi wywala błąd związany z linią, w której jest " ); ", gdy dodam kilka kolejnych zmiennych, typu team, ranga, i usunę visity, dodam dobrzer d, l itp. ?

 

"dodam dobrze" - zakładasz, że robisz to dobrze, ale jednak robisz to źle, bo wywala Ci błąd. Pokaż kod. 

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach
stock Player_GetData(playerid) {
    if(Settings[gmDev])
        printf("[PLAYER] Zaczynam wczytywac dane gracza %s[%d].", PlayerName(playerid), playerid);

    new Query[2048];
    format(Query, sizeof(Query), "SELECT `pid`,`password`,`salt`,`register_address`,`lastlogin_address`,`register_time`,`lastlogin_time`,`money`,`score`,`autologin`,`admin`,`vip`,`team`,`rank`,`money`,`bank`,`jail`,`mute`,`dom`,`pcar`,`dowod`,`prawko`,`amfa`,`crack`,`hera`,`mari` FROM `%sPlayers` WHERE `name`='%s';", Settings[dbPrefix], PlayerName(playerid));
    mysql_query(Query);
    mysql_store_result();
    if(mysql_num_rows()) {
        mysql_fetch_row(Query);
        sscanf(Query,"p<|>ds[33]ds[16]s[16]ddddllddddddddddddddd",
            Player[playerid][pID],
            Player[playerid][pPassword],
            Player[playerid][pSalt],
            Player[playerid][pRegisterAddress],
            Player[playerid][pLastAddress],
            Player[playerid][pRegisterTime],
            Player[playerid][pLastTime],
            Player[playerid][pMoney],
            Player[playerid][pScore],
            Player[playerid][pAutoLogin],
            Player[playerid][pAdmin],
            Player[playerid][pVip],
            Player[playerid][pTeam],
            Player[playerid][pRank],
            Player[playerid][pMoney],
            Player[playerid][pBank],
            Player[playerid][pJail],
            Player[playerid][pMute],
            Player[playerid][pDom],
            Player[playerid][pPcar],
            Player[playerid][pDowod]
            Player[playerid][pPrawko],
            Player[playerid][pAmfa],
            Player[playerid][pCrack],
            Player[playerid][pHera],
            Player[playerid][pMari]
        );
        mysql_free_result();
    } else {
        mysql_free_result();
        if(Settings[gmDev])
            printf("[PLAYER] Nie odnalazlem gracza %s[%d].", PlayerName(playerid), playerid);
        return INVALID_PLAYER_ID;
    }
    
    if(Settings[gmDev])
        printf("[PLAYER] Zakonczylem wczytywac dane gracza %s[%d, UID: %d].", PlayerName(playerid), playerid, PlayerPID(playerid));
    return Player[playerid][pID];
}
Edytowane przez kornel8697

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

Dam ci wszystkie błędy

source/player.inc(11 -- 33) : error 001: expected token: ",", but found "-identifier-"
source/player.inc(33) : warning 217: loose indentation
source/player.inc(33) : warning 215: expression has no effect
source/player.inc(33 -- 34) : warning 215: expression has no effect
source/player.inc(33 -- 35) : warning 215: expression has no effect
source/player.inc(33 -- 36) : warning 215: expression has no effect
source/player.inc(33 -- 38) : warning 215: expression has no effect
source/player.inc(38) : error 001: expected token: ";", but found ")"
source/player.inc(38) : warning 217: loose indentation
source/player.inc(38) : error 029: invalid expression, assumed zero
source/player.inc(38) : fatal error 107: too many error messages on one line

Dodam, że jak użyje tego pierwotnego kodu Kopcia to brak błędów. Te błędy są związane tylko z tym kodem co ci dałem.

Edytowane przez kornel8697

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

która linia?

 

Dam ci wszystkie błędy

source/player.inc(11 -- 33) : error 001: expected token: ",", but found "-identifier-"
source/player.inc(33) : warning 217: loose indentation
source/player.inc(33) : warning 215: expression has no effect
source/player.inc(33 -- 34) : warning 215: expression has no effect
source/player.inc(33 -- 35) : warning 215: expression has no effect
source/player.inc(33 -- 36) : warning 215: expression has no effect
source/player.inc(33 -- 38) : warning 215: expression has no effect
source/player.inc(38) : error 001: expected token: ";", but found ")"
source/player.inc(38) : warning 217: loose indentation
source/player.inc(38) : error 029: invalid expression, assumed zero
source/player.inc(38) : fatal error 107: too many error messages on one line

Dodam, że jak użyje tego pierwotnego kodu Kopcia to brak błędów. Te błędy są związane tylko z tym kodem co ci dałem.

 

Jednak nie potrafisz czytać ze zrozumieniem...nie będę się domyślał, które linie zawierają błędy. Trudno, szkoda czasu. 

@Down: Masz możliwość numerowania linii, mogłeś skorzystać. A błąd pojawia się na 99% z powodu takiego, że nie masz zdefiniowanych pRank i tak dalej w enum. 

Edytowane przez KaLu

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

Tak ciężko przeczytać co ja napisałem w poprzednim poście ? Napisałem że linia, w której jest " ); " i jest to linia 38. Głównie w tej wywala, ale pewnie coś innego zbugowało, albo, no nie wiem.

Edytowane przez kornel8697

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

  • Przeglądający   0 użytkowników

    Brak zarejestrowanych użytkowników przeglądających tę stronę.

×