Autor Wątek: SDL_net + tcp w statkach - problem  (Przeczytany 4410 razy)

Offline Freez

  • Użytkownik

# Styczeń 26, 2014, 19:16:31
przez kilka ruchów wygląda to tak:
gracz nr1 klika na pole, informacja jest przesyłana, drugi odbiera, aktualizuje plansze i może się ruszać.
w trakcie odbierania program czeka, i nawet kliknięcie gdziekolwiek nic nie robi, poza zwiechą typu "brak odpowiedzi"... ALE, po jakichś 2 "turach" gracze mogą klikać na planszę kilka razy i przesyłane są informacje pokolei, w stylu: kliknąłem 2 razy, w jednej turze wysłany jest jeden ruch i aktualizowanie planszy ale po chwili dochodzi ten drugi ruch....

Offline Mr. Spam

  • Miłośnik przetworów mięsnych

Offline Cerberus

  • Użytkownik
    • Moja strona

# Styczeń 26, 2014, 19:29:43
w trakcie odbierania program czeka, i nawet kliknięcie gdziekolwiek nic nie robi, poza zwiechą typu "brak odpowiedzi"...

No to klops, to niestety nie może tak działać ;) Nawet kiedy czekamy na ruch przeciwnika powinniśmy móc sobie np. wejść do opcji i wyłączyć muzykę, czyli pętla główna programu powinna ciągle działać, nie może zatrzymywać się na długi czas na jednej instrukcji. Stosujesz funkcje blokujące, które czekają tak długo, aż zostanie odebrany pakiet albo przekroczony timeout. Powinieneś stosować ich odpowiedniki nieblokujące, które sprawdzają, czy jest jakiś nowy pakiet oczekujący na przetworzenie i albo go zwracają, albo nie zwracają nic i kończą swoje działanie.

Generalnie w architekturze klient - serwer sytuacja, kiedy serwer jest zarówno klientem nie jest niczym wyjątkowym i nie powinna w niczym przeszkadzać. Teoretycznie powinno to działać tak, że aktualny i poprawny stan gry jest trzymany na serwerze, a klient symuluje ten stan. Czyli jeśli klient wykonuje ruch, to wysyła pakiet z danymi na serwer, serwer sprawdza czy ruch jest poprawny, jeśli jest poprawny to aktualizuje stan gry i wysyła pakiet o zmianie stanu gry do klienta. Żeby jednak klient nie miał lagów, to można zaraz po wysłaniu pakietu o swoim ruchu zaktualizować lokalny stan gry, a kiedy przyjdzie pakiet od serwera tylko zweryfikować, czy wszystko się zgadza.

Offline Freez

  • Użytkownik

# Styczeń 26, 2014, 19:38:19
Stosujesz funkcje blokujące, które czekają tak długo, aż zostanie odebrany pakiet albo przekroczony timeout. Powinieneś stosować ich odpowiedniki nieblokujące, które sprawdzają, czy jest jakiś nowy pakiet oczekujący na przetworzenie i albo go zwracają, albo nie zwracają nic i kończą swoje działanie.

Hmm, ale z tego co wiem SDLNet_TCP_Recv jest funkcją blokującą... Nie mam pojęcia jak to rozwiązać szczerze mówiąc :) Czy tworzenie serwera-klienta może być w jednej funkcji? A drugi klient w oddzielnej?

Offline Cerberus

  • Użytkownik
    • Moja strona

# Styczeń 26, 2014, 19:48:38
http://sdl.beuc.net/sdl.wiki/SDLNet_TCP_Recv

Cytuj
A non-blocking way of using this function is to check the socket with SDLNet_CheckSockets and SDLNet_SocketReady and call SDLNet_TCP_Recv only if the socket is active.

:)

Offline Freez

  • Użytkownik

# Styczeń 26, 2014, 19:52:16
Ok, a jak ma wyglądać to rozdzielenie serwer/klient? Czy może być tak jak napisałem że przy wybraniu w grze "jestem hostem" uruchamia się funkcja która najpierw oczekuje na podłączenie klienta a potem pętla gry dla serwer-gracza? Czy gracz, który jest serwerem musi jakoś wysyłać pakiety w inny sposób do serwera?

Offline Cerberus

  • Użytkownik
    • Moja strona

  • +1
# Styczeń 26, 2014, 20:24:11
Pseudokod, jak ja bym to ugryzł:

while (!bQuit)
{
if (bNewInputToProceed)
{
(...)

if (ClickedTile != None)
{
if (NetRole == SERVER)
{
if (VerifyMovement(ClickedTile))
{
UpdateGameLogic(ClickedTile);
TellClients(ClickedTile);
}
}
else if (NetRole == CLIENT)
{
TellServer(ClickedTile)
}
}
}

if (bNewTCPDataReceived)
{
if (NetRole == SERVER)
{
ClientClickedTile = GetTCPReceivedData();

if (ClientClickedTile != None && VerifyMovement(ClientClickedTile))
{
UpdateGameLogic(ClientClickedTile);
TellClients(ClientClickedTile);
}
}
else if (NetRole == CLIENT)
{
NewClickedTile = GetTCPReceivedData();

if (NewClickedTile != None)
{
UpdateGameLogic(NewClickedTile);
}
}
}

RenderScene();
}

Pętla główna (wspólna dla serwera i klienta, bo jednak większość rzeczy dla klienta i serwera jest taka sama) obsługująca same ruchy.
Kiedy serwer robi ruch, to weryfikuje jego poprawność, uaktualnia stan gry i informuje klienta. Klient odbierając pakiet o nowym ruchu serwera aktualizuje stan gry.
Kiedy klient robi ruch, to informuje o nim serwer. Serwer odbierając pakiet o nowym ruchu klienta weryfikuje jego poprawność, aktualizuje stan gry i informuje o tym klienta. Klient otrzymując pakiet o nowym ruchu (czy tam poprawności swojego ruchu) aktualizuje stan gry.

Trzeba by sobie porobić jakieś etykiety do przesyłanych pakietów, żeby było wiadomo, co do nas przychodzi, np. SERVER_MOVEMENT (przychodzi do klienta), CLIENT_MOVEMENT (przychodzi na serwer), CLIENT_VERIFIED_MOVEMENT (przychodzi do klienta).

No i to jest wersja kiedy po ruchu klienta musi odbyć się komunikacja klient -> serwer -> klient potwierdzająca poprawność ruchu, zanim zostanie zaktualizowany stan gry klienta, czyli z lagiem. Chcąc tego uniknąć to sprawdzenie poprawności ruchu powinno odbyć się na kliencie, stan gry zaktualizowany w momencie wysłania danych na serwer, a komunikat zwrotny z serwera tylko sprawdzeniem, czy ten ruch aby na pewno był poprawny.

Offline Freez

  • Użytkownik

# Styczeń 26, 2014, 21:09:52
Hmm, dobra dzięki wielkie. Powalczę jeszcze, może coś mi się uda z tym zrobić, w najgorszym wypadku ocena niżej. Dzięki :)