Skocz do zawartości

Wymagana pomoc z Networking'iem


TheVVaS

Rekomendowane odpowiedzi

Cześć!

Ostatnio przy robieniu gry napotykam sporo problemów, które w wersji 8.0 są trudne albo niemożliwe do naprawienia.

Z kolei patrząc na dokumentację do GMS'a widzę sporo fajnych rzeczy(np. support pada :D).

I teraz bardzo chciałbym przerzucić się na GMS'a, lecz tu rodzi się problem - NETWORKING :(

Sporo szukałem, lecz skończyło się na tym, że znajdowałem tylko BARDZO skomplikowane wzory albo takie, które nie działają :(

I teraz moje pytanko po małym rozpisaniu :D

Czy ktoś z was miszcze GM'a mógłby:

1. wytłumaczyć zielonemu jak działa networking w GMS'ie?

2. Zrobić/wskazać prosty wzór podstawowego networkingu client-server?

(Wybierz jedno, ewentualnie dwa :D)

Wcześniej pracowałem na 39dll.dll, lecz ten coś nie chce działać na GMSie :(

Na razie to chyba będzie wszystko.

Odnośnik do komentarza
Udostępnij na innych stronach

Trochę poszukałem i popytałem o networkingu i jedyne co udało mi się znaleźć to napisany na szybko wzór client-server przez kumpla.

Wszystko byłoby fajnie, ale ten wzór coś nie działa :(

A kumpel magiczne się rozpłynął i coś nie wraca.

Mógłby ktoś zerknąć na ten wzór i powiedzieć Co jest nie tak?

Przydałyby się także wytłumaczenia co za co odpowiada.

Dał mi gdzieś jeszcze link do jakiegoś mocno rozbudowanego wzory, niestety gdzieś go zapodziałem, postaram się go znaleźć.

 

Wzór networkingu?!

Odnośnik do komentarza
Udostępnij na innych stronach

Zacznę od podstaw:

Jak korzystać z networking:

Aby korzystać z networking musisz stworzyć w jakimś obiekcie event Asynchronous>Networking, gdyż poprzez ten event możesz jedynie obsługiwać pakiety.

 

Aby wysyłać dane musisz posiadać bufor. Aby stworzyć bufor użyj funkcji buffer_create, na przykład w następujący sposób:

GML
DataBuffer=buffer_create(1024,buffer_grow,1);

 

Taki bufor jest potrzebny zarówno dla klienta jak i serwera, jeśli oba masz w jednej aplikacji, pojedyńczy bufor starczy dla obu stron, jeśli masz oddzielnie program klienta i serwera, to w obu potrzebny jest jakiś bufor.

 

Jak połączyć się...

 

Serwer:

 

Zakładasz serwer za pomocą funkcji network_create_server(type,port,maxclients)

w argumencie typ podajesz jedną z trzech zmiennych w zależności od typu połączenia

GML
network_socket_tcp

network_socket_udp

network_socket_bluetooth

 

GML
network_create_server(network_socket_udp,30666,32);

(przykładowe ustawienie)

Jeśli funkcja zwróci ujemną wartość to serwera nie udało się założyć.

 

Klient:

Wpierw musisz przygotować socket dla klienta, korzystając z funkcji network_create_socket(type)

W argumencie podajesz typ połączenia jaki będzie obsługiwany, np. network_socket_udp dla UDP - musi to być ten sam typ na który nasłuchuje serwer

GML
GameSocket = network_create_socket(network_socket_udp);

Połączenie nawiązuje się korzystając z funkcji network_connect(socket,ip,port);

przykładowo...

GML
network_connect(ChatSocket,"127.0.0.1",30666);

 

Jeśli funkcja zwróci liczbę ujemną, nie udało się połączyć z serwerem.

 

Socket którego użyłeś do połączenia z serwerem będziesz wykorzystywać do wysyłania danych do niego

 

KOMUNIKACJA:

 

Teraz najważniejsza część, czyli jak obsługiwać odbieranie i wysyłanie danych.

 

Wysyłanie

 

Do wysyłania danych potrzebujesz bufor, ale takowy już przygotowaliśmy wcześniej.

Na początek musisz go wyzerować

GML
buffer_seek(DataBuffer,buffer_seek_start,0);

Dane, które chcesz wysłać, musisz załadować do buforu korzystając z funkcji buffer_write(buffer, type, value)

W argumencie "buffer" podajesz nazwę swojego buforu, w typie podajesz typ danych(poszukaj pełenej listy w dokumentacji),

w argumencie "value" podajesz dane które mają zostać wysłane.

 

przykład:

GML
buffer_write(DataBuffer,buffer_string,"Siemano ludziska!");

 

Kiedy wrzucimy do buforu wszystko co chcemy wysłać korzystamy z funkcji network_send_packet(socket, buffer, size)

W argumencie "socket" podajemy socket serwera, jak mówiłem wcześniej, po to jest nam potrzebny.

W argumencie "buffer" podajemy bufor z danymi które chcemy wysłać.

W argumencie "size" podaje się wielkość buforu, aby to zrobić użyj funkcji buffer_tell(buffer)

 

Jeżeli masz nazwy zmiennych takie jak ja podałem to poniższa linia zadziała w każdym przypadku gdy chcesz wysłać pakiet od klienta do serwera:

GML
network_send_packet(GameSocket,DataBuffer,buffer_tell(DataBuffer));

 

Odbieranie:

 

Odbieranie danych dzieje się w evencie Asynchronous>Networking

 

Za każdym razem gdy dostaniesz jakieś dane od serwera/klienta ten event zostanie wywołany.

W momencie wywołania, przysłane dane zostaną automatycznie zapisane do ds_map'y pod nazwa "async_load".

Aby móc z pełnym zrozumieniem korzystać z odbierania danych, musisz wiedzieć jak działa ds_map.

Możesz to sprawdzić w dokumentacji.

 

 

Odebrane dane są jednego z trzech rodzajów:

network_type_data - Kiedy odebrane są jakieś dane

network_type_connect - Kiedy połączysz się z serwerem/klientem

network_type_disconnect - Kiedy rozłączy się klient/serwer

 

Typ przysłanych danych zapisany jest w kluczu "type", a więc odczytanie go wygląda następująco:

GML
ds_map_find_value(async_load,"type")

 

Zwróconą wartością będzie jedna z 3 powyższych.

 

Odczytywanie z odebranego buforu

Dane przysłane, np. od klienta do serwera, pozostają przysłane w formacie buforu, więc aby je odczytać musisz skorzystać z funkcji buffer_read(buffer, type)

W tym przypadku 'buffer' to bufor zapisany w ds_map pod kluczem "buffer".

'type' to typ danych jaki został przysłany. Jeżeli klient przysłał bufor w którym jest string, to jako string musi zostać odczytany, inaczej będą błędy.

 

przykład:

GML
buffer_read(ds_map_find_value(async_load,"buffer"),buffer_string);

(powyższe wczyta przysłane dane jako string)

 

 

Skąd wiedzieć jaki typ danych został przesłany?

To bardzo ważne pytanie. Jeśli klient prześle swoje X i Y a nie np. nazwę gracza, a serwer spróbuje odczytać odebrane dane jako string, to cały serwer się wysypie. Musi więc istnieć sposób na rozpoznanie jakie dane wysyłamy.

 

Jak to bylo w średniowieczu? Nie było łatwego i szybkiego kontaktu a wszyscy nosili puszki i miecze, dzidy. Po czym poznawano przynależność armii? Po banerze.

0045_zps1f1acecf.jpg

 

 

Zanim przejdziemy do wysłania faktycznej wiadomości dodajmy na samym początku małą informację co tak naprawdę znajduje się w środku. Taki banner mówiący kim jesteśmy.

Ta informacja zawsze będzie danego typu (np. u8) więc nigdy nic się nei wysypie, dopóki podamy odpowiednią informację.

 

Założmy, że chcemy wysyłać 2 rodzaje informacji:

 

1:

X,Y,image_angle

 

2:

HP,nick_gracza

 

Pierwszy typ informacji więc nazwijmy "grupą 1", zaś drugi "grupą 2".

 

Przykład dla wysłania grupy 1:

 

GML
buffer_seek(DataBuffer,buffer_seek_start,0); // Czyszczenie buforu

buffer_write(DataBuffer,buffer_u8,1); // W pierwszej wyslanej informacji mowimy ze w buforze jest grupa 1!

buffer_write(DataBuffer,buffer_u32,x);

buffer_write(DataBuffer,buffer_u32,y);

buffer_write(DataBuffer,buffer_u32,image_angle);

network_send_packet(GameSocket,DataBuffer,buffer_tell(DataBuffer));

 

Jak widzisz, na sam początek wrzuciliśmy liczbę 1.

Kiedy serwer dostanie pakiet, sprawdzi jaka liczba jest na początku, będzie mógł sprawdzić czy będzie to 1 czy 2 i dzięki temu będzie wiedział czy powinien odczytać x,y,image_angle czy hp i nick

Odnośnik do komentarza
Udostępnij na innych stronach

Dzięki za wytłumaczenie co jest za co odpowiedzialne, wcześniej udawało mi się jedynie znaleźć szczątkowe informacje.

Natrafiałem także na wzory, lecz było tam w nich stanowczo za dużo i po prostu w nich gubiłem.

Byłbym wdzięczny gdybyś stworzył projekt z podstawami podstaw networkingu, wtedy miałbym lepszy zarys jak to ma wyglądać.

 

Ps. Jak ten wbudowany system networkingu przypisuje ID poszczególnym graczom? (Oczywiście chodzi mi o client-server)

Oraz jak znaleźć id danego gracza?

 

EDIT1: Dzięki tobie zaczyna mi już świtać coś na temat wysyłania/odbierania w tym wbudowanym networkingu.

Po dłuższym wglądzie jest trochę podobny do 39dll.

Odnośnik do komentarza
Udostępnij na innych stronach

(rozszerzyłem to trochę gdy pisałeś, zajrzyj do góry)

 

Jeśli przysłana informacja jest typu "network_type_connect" to w ds_map znajduje się ID gracza pod kluczem "socket", więc:

GML
IDGracza = ds_map_find_value(async_load,"socket")
Odnośnik do komentarza
Udostępnij na innych stronach

Czyli gdybym chciał poinformować gracza, który właśnie dołączył do serwera to musiałbym zrobić to mniej więcej tak:?

GML
IDGracza = ds_map_find_value(async_load,"socket");

buffer_seek(DataBuffer,buffer_seek_start,0); //Czyszczę bufor?

buffer_write(DataBuffer,buffer_u8,1); //Jako pierwsze wysyłam 1 - jest to informacja o "Panie drogi client zaraz dostaniesz swoje ID"

buffer_write(DataBuffer,buffer_u8,IDGracza);

network_send_packet(GameSocket,DataBuffer,buffer_get_size(DataBuffer));

 

Póki co ten async_load mnie zastanawia, bo nigdzie nie mogę znaleźć o nim informacji.

Szukałem w dokumentacji - nic nie znalazłem, pewnie źle szukałem :/

 

PS. ds_map_find_value(async_load,"socket");

Rozumiem, że "socket" pobrany z async_load podaje ID drugiej strony?

Odnośnik do komentarza
Udostępnij na innych stronach

CO do przykładu tak, w ten sposób byś poinformował klienta jakie ma ID.

Naturalnie przyda mu się aby Cię informować kim jest.

I tak, "socket" z async_load podaje ID adresata(wydaje mi sie ze tylko przy connect i disconnect, niech ktos mnie poprawi jesli sie myle)

 

Edit:

Co do async_load samego w sobie, to jest to jedynie zmienna wykorzystywana przez GM do tymczasowego przetrzymywania ds_map'y gdy przyjdą dane, mapa jest usuwana gdy event się skończy.

Odnośnik do komentarza
Udostępnij na innych stronach

1. Skoro nie znalazłeś to wątpię, ale tam nawet nie byłoby nic do czytania, to zwykła zmienna w której jest ID mapy.

2. Wysyłanie w dowolnym miejscu o dowolnym czasie, odbieranie ZAWSZE w Asynchronous>Networking

3. Może później

Odnośnik do komentarza
Udostępnij na innych stronach

1. Jest gdzieś informacja o async_load?

2. Jeśli wysyłam albo odbieram bufory to ZAWSZE w Asynchronous>Networking?

3. Mógłbyś skombinować podstawowy wzór networkingu? :D

 

1. W helpie/dokumentacji GMa...

2. Odbieranie Tak. Wysyłanie skąd chcesz.

3. Jest do sciagniecia wsrod przykladow GMa.

Odnośnik do komentarza
Udostępnij na innych stronach

Bardzo przejrzyście opisane :) , ale wkradł się mały błąd:

 

ServerSocket = network_create_socket(network_socket_udp);

 

network_create_server(ServerSocket,30666,32);

zgodnie z dokumentacją pierwszym argumentem network_create_server jest "The type of server to create", a nie nr socketu. Czyli w przypadku hosta network_craeate_socket jest niepotrzebny, wystarczy:

 

GML
ServerSocket = network_create_server(network_socket_udp, 30666, 32);

 

Druga uwaga:

network_send_packet(GameSocket,DataBuffer,buffer_get_size(DataBuffer));

 

powyższy kod wysyła cały bufor (1024 bytes), lepiej wysyłać tylko dane, bez śmieci - do tego służy buffer_tell:

 

GML
network_send_packet(GameSocket, DataBuffer, buffer_tell(DataBuffer));
Odnośnik do komentarza
Udostępnij na innych stronach

Co do pierwszego - rzeczywiście, pisałem z pogmatwanego, wielomiesięcznego kodu, do którego nie zaglądałem od dawna i musiałem coś pomylić, nie wiem jak to się tam znalazło. Zaraz to poprawię.

Co do drugiego - interesujące, nie wiedziałem o tym. Z pewnością się przyda w optymalizacji!

 

Dzięki :thumbsup:

 

Edit: Oba poprawione...chyba

Odnośnik do komentarza
Udostępnij na innych stronach

Akurat ten kod podałem jako przykład wysyłania od klienta do serwera.

 

 

Jeśli przysłana informacja jest typu "network_type_connect" to w ds_map znajduje się ID gracza pod kluczem "socket", więc:

GML
IDGracza = ds_map_find_value(async_load,"socket")

 

Innymi słowy masz tablicę lub ds_listę lub cokolwiek w której zapisujesz sobie sockety do klientów i potem wysyłasz oddzielnie do każdego.

Tylko taki szybki tip, jak do każdego wysyłasz tę samą informację nie musisz za każdym razem resetować i przepisywać bufera, po prostu robisz jakąś pętlę i wysyłasz do każdego po kolei.

 

GML (kawalekkodu)
// (...) <jakis switch czy cos>

case network_type_connect:

ds_list_add(PlayerList,ds_map_find_value(async_load,"socket")); // Zapisanie do ds_list socketa podlaczonego gracza

break;

// (...)</span></span>

 

GML (wysylaniemasowe)
// < wrzucanie informacji do bufera >

for(var i=0; i<ds_list_size(PlayerList); i++ )

{

network_send_packet(ds_list_find_value(PlayerList,i),DataBuffer,buffer_tell(DataBuffer));

}

 

Edit:

Naprawcie te tagi GML bo 5 lat poprawiania "</span>" i "quotnazwaeventaquot" robi sie nudne.. i jak zwykle pisze z glowy wiec mądrzejszych ode mnie proszę o bezczelne wytykanie błędów

Odnośnik do komentarza
Udostępnij na innych stronach

Jeśli chcesz dodać odpowiedź, zaloguj się lub zarejestruj nowe konto

Jedynie zarejestrowani użytkownicy mogą komentować zawartość tej strony.

Zarejestruj nowe konto

Załóż nowe konto. To bardzo proste!

Zarejestruj się

Zaloguj się

Posiadasz już konto? Zaloguj się poniżej.

Zaloguj się
  • Ostatnio przeglądający   0 użytkowników

    • Brak zarejestrowanych użytkowników przeglądających tę stronę.
×
×
  • Dodaj nową pozycję...