Skocz do zawartości

Zablokowane Rozumienie kodu..


Paqoo

Rekomendowane odpowiedzi

Mam coś takiego: <_<

clearbuffer();
writebyte(2);
writebyte(global.myid);
writeshort(x);
writeshort(y);
writeshort(sprite_index);
writeshort(image_speed);
writeshort(image_index);
writeshort(direction);
writeshort(health);
sendmessage(global.clienttcp);

 

Proszę niech ktoś mi wyjaśni co robi każda linijka. :blink:

Ja raczej wiem co oznacza, ale nie potrafię tego odczytać. Może coś źle rozumiem.

Jak to zrobić? Gdy chcę aby inni gracze widzieli, np. direction gracza z tym kodem? :sp_ike:

 

Napiszcie to kodem, a nie w stylu "odbierasz to, dajesz tamto" :thumbsup:

Odnośnik do komentarza
Udostępnij na innych stronach

clearbuffer(); // Czyścimy buffera
writebyte(2); // Zapisujemy dane pod liczbą 2
writebyte(global.myid); // Zapisujemy dane pod ID gracza
writeshort(x); // Zapisujemy pozycje X
writeshort(y); // Zapisujemy pozycje Y
writeshort(sprite_index); // Zapisujemy aktualny "sprite_index"
writeshort(image_speed); // Zapisujemy "image_speed"
writeshort(image_index); // Zapisujemy "image_index"
writeshort(direction); // Zapisujemy "direction"
writeshort(health); // Zapisujemy zmienną "health"
sendmessage(global.clienttcp); // Wysyłamy dane do serwera

Jeśli się pomyliłem to niech ktoś poprawi ;p

Odnośnik do komentarza
Udostępnij na innych stronach

Tak wogóle jest sens zapisywać dwa razy dane? Pod ID gracza i liczbą?
Tak. Ponieważ otrzymuje się coś takiego
|Wyczyść buffera |WriteByte(2)-->WriteByte(ID)|<-- Zapisz dane |> Wyślij|>

Czyli jak zauważysz zapisuje pod liczbą 2 jeszcze ID gracza, będzie łatwiej to odebrać przy switch'owaniu ;p

Odnośnik do komentarza
Udostępnij na innych stronach

Nie znałem tego przykładu wcześniej. Jechałem na Racer online, ale tam mało co jest.. Właściwie nie widze odbierania nigdzie.. Może to w skryptach gdzieś w SCRIPTS.. Nie wiem :) Przejrze ten co mi podałes link. THX

 

Dzięki Kirby Online coś już rozumiem. obj_client ma w step całość switchowania. Jeśli switch zwraca wartość bajta np. 2 to wykonywana jest akcja. Wydaje się teraz zrozumiałe. Nie wiem co tam się działo w Racer Engine, ale tam to jakoś inaczej wygląda.. Tutaj widać, że tworzy się zmienną na ID itp. i przypisuje się receivemessage(global.clienttcp).. :P

 

Teraz będzie mi łatwiej i wreszcie jakiś porządek jest.. W miare osiągnięty...

 

Edit X(5?): Jednak po sprawdzeniu RAcer Engine działa podobnie.. Po prostu nie rozumiałem tego wcześniej i ominąłem tę część kodu i się dziwiłem czemu drugi gracz działa, a nie ma kodu do niego ^.^. Jest fajnie! Thx

Odnośnik do komentarza
Udostępnij na innych stronach

  • Filar Społeczności

Oj Konrad, jemu to za dużo nie wyjaśni. Trzeba wszystko wytłumaczyć od początku...

 

W grach sieciowych jest ten kłopot, że jeżeli na komputerze lokalnym stworzysz jakiś obiekt, to tenże obiekt nie pojawi się automatycznie na innych komputerach podłączonych do twojego. Weźmy jako przykład wystrzelony pocisk na komputerze A - co zrobić by pocisk pojawił się również na komputerze B. Otóż musisz wysłać stosowane dane do drugiego komputera, na podstawie których to zostanie utworzony odpowiedni pocisk, na określonej pozycji, w określonym kierunku i szybkości.

 

Wróćmy teraz do transferu danych pomiędzy komputerami. Jak wysłać odpowiednie zmienne? Otóż dane wysyłane są pakietami czyli w jednym pakiecie mieści się tyle i tyle danych. W pakietach grupowane są zmienne powiązane ze sobą w jakiś sposób i żadne inne! Pakiety nie mogą być zbyt krótkie (w przypadku TCP) lub też zbyt długie. Zbyt krótkie dlatego, że w protokole TCP prócz informacji właściwych, których wysyłasz, narzucane są jeszcze inne dane do synchronizacji itd. czyli im więcej osobnych pakietów, tym więcej danych pobocznych - większe obciążenie łącza - potrzebna jest większa przepustowość. Pakiety danych nie mogą być też zbyt długie, ponieważ jeden pakiet jest odczytywany w całości, więc jeśli w danym pakiecie jest za dużo informacji, to jego odczyt wydłuży się zbytnio w czasie...

 

Jak wysyłać dane? 39dll oferuje wysyłanie tekstu oraz liczb. Oczywście mógłbyś komponować odpowiedni tekst, a potem dekodować na komputerze klienckim, np.:

 

"pocisk|x:10|y:100|dir:340|speed:50".

 

Następnie byś odczytywał kolejne literki itd. uzyskując odpowiednie informacje. Jednak jest to bezsensowne rozwiązanie, gdyż wysyłasz dużo zbędnych danych. Lepiej zapisać tak:

 

"12|10|100|340|50"

 

Od razu lepiej, prawda? A ile znaków zaoszczędziłeś! Pierwsza liczba (12) to będzie zmienna, która symbolizuje co chcesz przekazać drugiemu komputerowi, jaką akcję. W tym celu musisz sam sobie ustalić własną strukturę protokołu, którą następnie będziesz odczytywał. Załóżmy, że pierwsza liczba w pakiecie będzie nas informowała co to za akcja i jakie dane są wysyłane. Załóżmy, że kody będą wyglądać tak:

 

1 - poruszanie się obiektu. Będą potrzebne zmienne id obiektu, x oraz y. Pakiet będzie wyglądał przykładowo tak: "1|1312|100|100".

2 - tworzenie obiektu. Potrzebne będą dane typ obiektu (jego indeks w GM), x i y. Pakiet będzie wyglądał tak "2|12|100|100". (tworzymy obiekt typu ... o indeksie 12 w pozycji 100,100.

3 - niszczenie obiektu. Potrzebne będą dane id obiektu. "3|13124"

 

Widzisz, musisz stworzyć całą taką strukturę protokołu, gdzie pierwsza liczba to typ komendy, a kolejne to dane dodatkowe, które będziesz zawsze wiedział w jakiej są kolejności, więc nie będziesz miał kłopotu z ich odczytem.

 

 

Teraz wrócmy do twojego przykładowego skryptu z początku tematu. To powinno Ci nieco rozjaśnić sytuację. Funkcja clearbuffer służy do czyszczenia bufora (przestrzeni) pakietu danych. Przed wysłaniem pakietu zawsze wywołuj tą komendę. Kolejna funkcja, writebyte zapisuję liczbę 2. Oho! To musi być właśnie jakiś identyfikator komendy, która jest wysyłana. Zobaczmy co jeszcze jest zapisywane. ID obiektu, x, y, indeks spritu itd. Czyli 2 symbolizuje w tym przypadku ogólny stan obiektu - uaktualnienie jego danych na komputerze klienckim. Po prostu teraz musisz odczytać te zmienne i jesteś w domu. Tylko jak?

 

Wrócmy do naszego sposobu zapisu:

 

"12|10|100|340|50"

 

Tak naprawdę jest to niewydajny sposób, gdyż musisz parsować ten tekst (zabijesz gmla) a w dodatku wciąż za dużo zajmuje. Pamiętaj, że pakietów wysyłanych w trakcie gry, w czasie jednej sekundy, jest od groma, do tego dodaj wielu graczy i wyjdzie na to, że twoja gra będzie wymagać łącza 5Mbit. :)

 

Jak temu zaradzić? Prosta sprawa, wysyłać dane w formie liczb i to nie dziesiętnych a heksadecymalnych (czyli w systemie szesnastkowym). A czemu tak? Ponieważ komputer operuje właśnie na liczbach w formie szesnastkowej czyli cyfry są takie: 0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f. Przykładowe liczby w formie dziesiętnej i szesnastkowej:

7 -> 7

9 - 9

10 - a

12 - c

15 - f

16 - 10

17 - 11

18 - 12

29 - 1d

255 - ff

 

Ty akurat nie musisz znać przelicznika. Musisz tylko wiedzieć, że jeden bajt to liczby od 0 do 255 (0-ff w trybie hex). Teraz jak sobie spojrzysz w oferty usługodawców w internetu, to oni podają szybkość w bitach, podziel tą szybkość na 8 i masz szybkość właśnie w bajtach czyli ile łącze może wysłać bajtów - liczb od 0 do 255. 1Mbit/s to 128Kbit/s czyli 128000 liczb typu bajt (0-255). Okej, możesz tyle danych wysłać, ale co z liczbami większymi. Jak masz np. planszę 4000x4000 i chcesz wysłać x i y, to bajt do określenia liczby nie wystarczy. :( Dlatego też w informatyce ludzie poszli po rozum do głowy i wymyślili parę dodatkowych typów zmiennych. Kliknij w poniższy link:

 

http://www.zive.cz/Files/Obrazky/2004/12/vbnet1902.gif

 

Zwróć uwagę na kolumnę numer 3 (byte, short, int) - prawda że znajome nazwy! Dokładnie takie nazewnictwo masz w funkcjach przez siebie używanych! Zwróć teraz uwagę na kolumnę numer 6 - tam masz napisane ile dany typ zajmuje bajtów, a w kolumnie 7 masz natomiast podany zakres liczby w formie dziesiętnej jaką dany typ może przechowywać.

 

Z powyższej tabelki dowiadujemy się, że bajt to liczba 0-255 zajmująca jednego bajta. Short to liczba 0 - 65535 zajmująca już 2 bajty! Int zajmuje 4 bajty, a double aż 8! Czemu to jest ważne? Ano dlatego, że im więcej bajtów tym.... tak, większe obciążenie łącza. Dlatego Ty sam musisz ustalić do jakiego typu danych użyjesz jakiego formatu, czy byte czy short czy co tam jeszcze. Przykładowo pierwsza liczba w pakiecie, która będzie odzwierciedlała rodzaj pakietu czyli jakie dane wysyłasz / jaka komenda, to może być byte czyli liczba od 0 do 255, bo chyba nie wymyślisz więcej niż 255 rodzajów zdarzeń, prawda? I tyle starczy. Lećmy dalej, x i y? Jeżeli plansza będzie o wielkości maksimum 4000x4000, to już bajt nie starszy, ale nie musisz zaraz stosować int, wystarczy short, ponieważ 4000 mieści się w zakresie 0-65535. Jeżeli zastosowałbyś int, to byłbyś 2 bajty w plecy, a tak to oszczędzisz trochę łącza. Widzisz? Dla każdego rodzaju liczby musisz ustalić jak je wyślesz! Uważaj tylko na liczby ujemne, jeżeli chcesz je stosować, to w tym celu masz w 39dll specjalne funkcje. Np. zamiast writeshort( liczba od 0-65535 ), masz writeushort ( liczba od -32767 do 32767). typy z przedrostkiem "u" to po prostu liczby przesunięte o połowę "w tył", czyli byte: 0-255, a ubyte: -128-127.

 

Wróćmy teraz do twojego skryptu. Jak się przypatrzysz na nazwy funkcji, to już wiesz jak są dane wysyłane, w jakim formacie określone liczby. Tylko nasuwa się pytanie czemu jednak nie w formacie dziesiętnym, lub jakoś bardziej luźnie. Ano z prostej przyczyny, ponieważ między liczbą 2 a 21 jest zasadnicza różnica - długości jednego znaku, dlatego musiałbyś jakoś te liczby rozdzielać i wróciłbyś do punktu wyjscia. Stosując natomiast liczby w formie byte, short itd., zawsze wiesz jaką długość mają te liczby, np. int to liczby od 0000 do ffff - widzisz, zawsze są cztery znaki. Dla komputera jest to łatwiejsze do odczytania a przy okazji jaka oszczędność, bo int w trybi dziesiętnym to parę milionów czyli 2 razy więcej znaków.

 

Wrócmy teraz do naszego zapisu sprzed paru akapitów:

 

"1|1312|100|100"

 

Taki pakiet danych oznacza:

przesunięcie obiektu (1), id obiektu (1312), x (100), y (100).

 

Zamiast parsować ten tekst ustalmy inny format danych, za pomocą liczb typu byte, short, short, short. Byte to będzie komenda na wstępie, bo więcej takowych komend niż 255 nie wymyślimy, rpg nie robisz. :) Short dla id obiektu starczy (0-65535), tylko pamiętaj, że to nie to samo co ID obiektu w gm! x i y też będą shortami, bo zakładamy, że te zmienne nie będą ujemne, oraz zawsze mieszczą się w przedziale 0-65535 (plansza jest 4000x4000).

 

Jak będzie wyglądać przykładowy powyższy pakiet danych? Ano tak:

 

01052000640064

 

Rozłóżmy to na czynniki pierwsze:

 

byte: 01 (nasza komenda), short: 0520 (id obiektu), short: 0064 (x), short 0064 (y)

 

Przykładowo 1312, to w zapisie hex 520, ale wiemy, że short to 4 znaki, więc jeżeli liczba jest zbyt krótka (o jeden znak), to komputer dodaje 0 od lewej strony. Aby konwertować z dec na hex i hed na dec, możesz użyć windowsowego kalkulatora, widok w trybie zaawansowanym i tam masz przełączniki.

 

W powyższym przypadku oba formaty danych zajmują 4 znaków. Nic nie zaoszczędziliśmy? Pozornie. Weźmy maksymalne liczby (sytuacja hipotetyczna):

 

dec jako tekst z rozdzielnikami: "255|65535|65535|65535" - 21 znaków

 

hex: ffffffffffffff - 14 znaków

 

Widzisz? w drugim przypadku już zaoszczędziliśmy 7 znaków.

 

Podsumowując:

 

1) W grach sieciowych wysyłasz pakiety danych o ZNANEJ TOBIE STRUKTURZE, którą sam wymyślasz.

2) Do odpowiednich zmiennych dobierasz odpowiednie formy liczb: byte, ubyte, short itd. tak by jak najmniej obciążyć łącze.

3) Pierwsza liczba w każdym pakiecie odpowiadać będzie za rozpoznanie pakietu i z góry ustalasz jaki to ma być typ liczby. Najlepiej by każdy pakiet zaczynał się liczbą typu byte.

4) Odczytywanie pakietu rozpoczynasz od pierwszej liczby (w formie byte), a następnie przepuszczasz przez instrukcję warunkową switch i w zależności od zmiennej, odczytujesz resztę liczby według ZNANEJ CI KOLEJNOŚCI, bo sam zaprojektowałeś dany pakiet.

 

Ostateczny przykład wysyłu np. komendy przesunięcia obiektu na nowe x i y:

 

Komputer A (wysyła):

clearbuffer(); // Czyścimy bufor pakietu danych

writebyte(1); // Jest to nasz identyfikator pakietu danych

writebyte(global.myid); // Zapisujemy id gracza jako liczba byte

writeshort(x); // Zapisujemy pozycje X

writeshort(y); // Zapisujemy pozycje Y

sendmessage(global.clienttcp); // Wysyłamy dane do innego komputera

 

Komputer B (odbiera):

pakiet = receivemessage( global.innykomp ); //Pobieramy dane z odpowiedniego połączenia

 

if ( pakiet < 0 ) break; // Jeżeli pakietu nie ma, to przerywamy skrypt

 

akcja = readbyte(); // odczytujemy pierwszą liczbę w formie byte (zapisaną poprzez writebyte)

 

switch( akcja)

{

case 1: // Oho! Akcja o numerze 1 czyli przesuw obiektu!

obiekt = readbyte(); // odczytujemy 2 liczbe

xx = readshort(); // odycztujemy kolejne liczby zapisane jako writeshort, więc stosujemy readshort

yy = readshort();

break;

}

 

Tym sposobem odczytałeś rodzaj pakietu (ustaliliśmy, że 1 to pakiet odpowiadający za przesuw obiektu, który posiada zmienne w odpowiedniej kolejności: byte, short, short) oraz dane i zapisałeś je do zmiennych obiekt, xx i yy.

 

------------------------------------------

 

W temacie pytałeś się o direction gracza. Nic prostszego, po prostu przed funkcją sendmessage dopisz funkcję writeshort! Tak, short to 0-65535 a direction w GM może przyjmować ułamki, bo w GM każda zmienna to zmienna typu double. Jednak jeżeli będziesz wysyłał wszystko jako double, to łącze obciążone będzie strasznie, więc polecam byś zaokrąglał direction, wtedy wyjdzie Ci liczb od 0 do 360. W byte się nie zmieści, ale w short już tak.

 

Po prostu dopisz:

writeshort( floor( direction ) );

 

A w odczycie na samym końcu:

direction = readshort();

 

Pamiętaj, że kolejność odczytu danych jak i ich rodzaj są bardzo ważne, bo gdy coś źle zapiszesz, to po prostu inni gracze będą otrzymywali złe dane.

Odnośnik do komentarza
Udostępnij na innych stronach

@Gnysek: Tabelką mogę spróbować się zająć. To będzie art "iAk zorobici grem wstyló tibji". To nie miało być śmieszne, ale tak to wyjdzie. Miliony n00bów zaśsie 39dll potem ten milion n00bów przeczyta ten art i mamy milion klonów Tibii! Szkoda, że nie ma jakiejś blokady...

Odnośnik do komentarza
Udostępnij na innych stronach

  • Administratorzy
Bajera :thumbsup:

Teraz Ranmus sprawił, że każdy zrobi własną Tibię :P Może Borek coś podchwyci do swojej Almory i nie będzie tylu lagów :lol2:

 

Ta jasne, Borek pierwszy raz na oczy takie coś widzi... ba, on nawet catch the clown nie umie zrobić !

Myślisz, że Borek nie optymalizował swojej gry ? Poza tym jak ktoś robi taki projekt to raczej ma jakieś pojęcie...

Lagi wynikaly raczej z tego, że mapa jest duża, ze serwer jest w gm i nie wyrabiał system tego, niż z ilości wysyłanych pakietów.

Odnośnik do komentarza
Udostępnij na innych stronach

  • Filar Społeczności

Yyyyy eee noooo... Akurat to są podstawy działania 39dll, które Borek doskonale rozumie. Musisz jeszcze się nauczyć jak kodować i dekodować pakiety, jak zrobić efektywne przemieszczanie obiektów (dead reckoning itp.), wyliczyć ile pakietów i w jakich odstępach czasu wysyłać, które częściej, które mniej, jak ustalać limity transferu na klienta itd.

 

Niektórzy to nawet prace inżynierskie piszą na ten temat:

https://dip.felk.cvut.cz/browse/pdfcache/ha...m1_2006bach.pdf

Odnośnik do komentarza
Udostępnij na innych stronach

Tak se to czytam, i znalazlem blad!

zamiast writeshort( liczba od 0-65535 ), masz writeushort ( liczba od -32767 do 32767). typy z przedrostkiem "u" to po prostu liczby przesunięte o połowę "w tył", czyli byte: 0-255, a ubyte: -128-127.
maly blad, bo jest odwrotnie

 

Cytat ze skryptu writeuint() z 39dll

Writes a 4 byte UNSIGNED integer to the internal buffer. The value can be between

0 and +4294967296

Odnośnik do komentarza
Udostępnij na innych stronach

Gość
Ten temat został zamknięty. Brak możliwości dodania odpowiedzi.
  • Ostatnio przeglądający   0 użytkowników

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