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

Offline Freez

  • Użytkownik

# Styczeń 26, 2014, 12:21:38
Witam, mam problem z moim projektem. Piszę na zaliczenie Statki z wykorzystaniem bibiloteki SDL 1.2 w C++. Muszę dodać multiplayer. Zdecydowałem się na SDL_net i TCP. Jeden z klientów ma być "serwerem", drugi się do niego łączy. I wszystko spoko, tylko że mam problem z ruchami, a dokładniej w momencie jak jeden z graczy wykona ruch, to drugi następnie może wykonać dwa ruchy, choć nie powinien...

pętla działa na zasadzie:
Jeśli spełniony jest warunek, że gracz się jeszcze nie ruszył, pętla while(SDL_PollEvent(&event)) itd. umożliwia mu wybrać pole. Po wybraniu pola wysyłany jest pakiet jakie pole gracz wybrał, warunek czy_ruszył zmienia się na true i w tym momencie wychodzi z pętli PolllEvent i przeładowuje plansze a następnie wchodzi do funkcji gdzie oczekuje na pakiet. Powinna to być prosta turowa gra, a jednak coś mam z tym problemy. Ktoś mógłby pomóc ?

Offline Mr. Spam

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

Offline Freez

  • Użytkownik

# Styczeń 26, 2014, 13:55:01
Wydaje mi się, że problem jakoś leży w tym że leci nadmiar pakietów... Nie mam pojęcia jak zablokować żeby gracz w momencie jak odbierze pakiet, mógł się poruszyć tylko

Offline rem

  • Użytkownik

# Styczeń 26, 2014, 14:09:15
Jeśli jest to aplikacja klient-serwer, to każdy pakiet od klienta powinien być weryfikowany przez serwer zanim faktycznie wpłynie na stan gry. Ja widzę to tak, że nawet jeśli z jakiegoś powodu gracz wyśle dwa razy informacje o wybraniu pola, to serwer automatycznie odrzuci drugi pakiet.

Czyli: klient wysyła informacje o wybranym polu do serwera, ten odbiera pakiet i sprawdza czy pochodzi on od osoby która akutalnie może wykonać ruch. Jeśli ten warunek jest spełniony, serwer zaznacza to pole na mapie i przesyła uaktualnienie do wszystkich klientow, jeśli nie, ignoruje pakiet i czeka na ten właściwy. W ten sposób nie ma możliwości, że któryś z graczy wykona więcej niż jeden ruch.

Offline Freez

  • Użytkownik

# Styczeń 26, 2014, 14:11:59
tylko problem jest w tym, że jeden z klientów jest jako serwer... każdemu z klientów wyświetlają się dwie plansze, jedna to plansza wroga, jedna własna. Klikając na pole planszy wroga, wysyłany jest pakiet z informacją w które pole kliknęlliśmy, drugi klient odbiera tę informację i "aktualizuje" pole na swojej mapie i tak dalej...

Offline Rocik

  • Użytkownik

# Styczeń 26, 2014, 14:37:17
Trochę miesza to, że jeden z clientów jest serverem. Lepiej zrobić normalny system server - clients, lub jeżeli nie chcesz dodatkowego programu w tle, p2p.
Nie zdziwiłbym się jakby przypadkiem to właśnie był ten problem. Ten z clientow jako server, nie jest czasem i clientem i serverem na raz?

Offline Freez

  • Użytkownik

# Styczeń 26, 2014, 14:40:44
No tak, właśnie o to chodzi że jest klientem i serwerem jednocześnie. Jest jakiś w miarę mało skomplikowany sposób żeby zrobić serwer do którego się łączy wielu klientów? Coś czytałem, że do tego potrzebne wątki itp. Nie mam zbytnio czasu na robienie czegoś skomplikowanego, poza tym nie chcę żeby sama sieć przerastała projekt, bo to tylko zwykłe turowe statki :D

Offline Rocik

  • Użytkownik

# Styczeń 26, 2014, 15:15:52
Zrób tak, żeby był osobny program, do którego łącza się klienci. Tam wysyłane są wszystkie pakiety z informacjami. Wtedy serwer sprawdza kto wysłał i przesyła do drugiego, w tej formie to by działało nawet z 3+ klientami, ale to statki wiec więcej niż 2 nie potrzeba.
I polecam też przejść na SDL2, chwila roboty, a korzyści duże.

W kodzie źródłowym SDL2_net masz ładny przykład chatu. (chat.cpp -client oraz chatd.c - server)
« Ostatnia zmiana: Styczeń 26, 2014, 15:17:27 wysłana przez Rocik »

Offline Freez

  • Użytkownik

# Styczeń 26, 2014, 15:25:51
Hmm, ale to ma się opierać jakoś na socketach? Bo połączenie klient serwer było dość łatwe, TCPsocket client, server i nawiązanie pomiędzy tym połączenia... A jeśli zrobię na obsługę kilku klientów to muszę czegoś dodatkowo używać?

Offline Krzysiek K.

  • Redaktor
    • DevKK.net

  • +2
# Styczeń 26, 2014, 15:51:35
Cytuj
Trochę miesza to, że jeden z clientów jest serverem. Lepiej zrobić normalny system server - clients, lub jeżeli nie chcesz dodatkowego programu w tle, p2p.
Ale po co komu dodatkowy program w tle? Można to bez żadnych problemów zrobić na jednym procesie.

Offline Freez

  • Użytkownik

# Styczeń 26, 2014, 16:01:11
No tak niby zrobiłem, tylko jest problem, że przez kilka ruchów jest ok, każdy ma po jednym a nagle się to zmienia i po kilka ruchów ma gracz. Tylko że ten ruch jest interpretowany tak jakby "na zaś" dopiero jak drugi gracz się ruszy to mu ten pakiet przychodzi. Czyli ewidentnie jest jakaś nadwyżka pakietów, ale nie potrafię tego zablokować. Teoretycznie powinno być ok, bo funkcja nie powinna dopuszczać do możliwości kliknięcia na pole... Niestety teoretycznie...

Dodam, że jest to mój pierwszy raz kiedy się bawię w obsługę sieci, dostałem takie wytyczne do projektu szkolnego trochę za późno, i nie mam czasu żeby dogłębnie się zapoznawać z tworzeniem multiplayera, chociaż bardzo bym chciał. Więc zależy mi żeby zrobić to jak najprościej i najefektywniej.
« Ostatnia zmiana: Styczeń 26, 2014, 16:03:17 wysłana przez Freez »

Offline Xion

  • Redaktor
    • xion.log

# Styczeń 26, 2014, 16:21:14
Cytuj
A jeśli zrobię na obsługę kilku klientów to muszę czegoś dodatkowo używać?
Cytuj
Tylko że ten ruch jest interpretowany tak jakby "na zaś" dopiero jak drugi gracz się ruszy to mu ten pakiet przychodzi. Czyli ewidentnie jest jakaś nadwyżka pakietów, ale nie potrafię tego zablokować.
Wprawdzie z moją staropolszczyzną kiepsko i nie wiem co dokładnie znaczy "na zaś", ale mam przeczucie, że próbujesz po prostu na zmianę (1) czytać z socketa zdalnego klienta (2) wykonywać ruch lokalnego. Przez to wprowadzasz dziwne zależności do swojej (niejawnej) maszyny stanów gry.

Offline Freez

  • Użytkownik

# Styczeń 26, 2014, 17:38:33
Chodzi o to, że jak już dojdzie do tego mojego błędu z dwoma ruchami, gracz 1 wybiera pierwszy raz pole, to pole jest przesyłane do gracza nr2, on to odbiera, aktualizuje mu plansze ALE:
- gracz numer 1 może sobie wybrać jeszcze raz pole
- gracz numer 2 może w tym czasie też wybrać pole

jeśli gracz numer 1 wybierze pole, to ono nie zostaje przesłane tak jakby w tej samej turze, tylko dopiero po ruchu gracza 2...


pętla wygląda w taki sposób, że: jeśli gracz się nie poruszył -> włącza się możliwość kliknięcia w pole -> klikamy w pole -> przesyłamy pakiet oraz ustawiamy że gracz się poruszył -> pętla przechodzi do momentu gdzie sprawdzamy czy gracz się poruszył, jeśli tak to czekamy aż odbierze pakiet -> jeśli odebrał to ustawiamy że się nie poruszył i zaczynamy od nowa... przez chwilę to działa, ale po kilku ruchach niestety są problemy....
« Ostatnia zmiana: Styczeń 26, 2014, 17:41:31 wysłana przez Freez »

Offline Xion

  • Redaktor
    • xion.log

# Styczeń 26, 2014, 18:54:38
Są problemy, bo nigdzie nie przechowujesz stanu, w którym znajduje się gra, tylko zakładasz, że zdarzenia będą zawsze następowały po sobie w ściśle określonej kolejności. W programie, który jest interaktywny oraz sieciowy, to zupełnie niepoważne.

Cytuj
jeśli gracz się nie poruszył -> włącza się możliwość kliknięcia w pole -> klikamy w pole -> przesyłamy pakiet oraz ustawiamy że gracz się poruszył -> pętla przechodzi do momentu gdzie sprawdzamy czy gracz się poruszył, jeśli tak to czekamy aż odbierze pakiet -> jeśli odebrał to ustawiamy że się nie poruszył i zaczynamy od nowa
Zupełnie tego nie widzę. W jaki sposób "klikamy w pole" jest częścią głównej pętli? Użytkownik może klikać w dowolnym momencie. Podobnie "pakiet" może być odebrany w dowolnym momencie, więc "czekamy aż odbierze" też jest dla mnie kompletnie niezrozumiałe...

Może rzuć w końcu jakiś kawałek kodu bo jak na razie to bardziej wróżenie z fusów i próba zgadnięcia, jak bardzo jesteś w błędzie.

Offline Freez

  • Użytkownik

# Styczeń 26, 2014, 19:01:33
while(quit == false) {
if(if_moved == false) {
while(SDL_PollEvent(&event)) {
for(i=0;i<10;i++) {
for(j=0;j<10;j++) {
if(client_human.enemy_map->field[i][j].field_event()) {
if(client_human.enemy_map->field[i][j].status == empty){
buff_send[0] = i;
buff_send[1] = j;
buff_send[2] = client_human.enemy_map->field[i][j].status;
if_moved=true;
klient.send_packet(buff_send);
}
}
}
}
if(event.type == SDL_QUIT) {
quit = true;
}
}
}

client_human.enemy_map->draw_map(600,100,0);
SDL_Flip(screen);

if(if_moved) {
klient.receive_packet(buff_receive);
client_human.your_map.field[buff_receive[0]][buff_receive[1]].status = buff_receive[2];
if_moved=false;
}

client_human.your_map.draw_map(100,100,0);
SDL_Flip(screen);
SDL_Delay(200);
}


Offline Xion

  • Redaktor
    • xion.log

  • +1
# Styczeń 26, 2014, 19:09:41
Zagadka: co się stanie gdy program wisi na którejś z tych dwóch linijek:
klient.send_packet(buff_send);
klient.receive_packet(buff_receive);
a użytkownik w tym czasie kliknie w planszę? Czy jego kliknięcie zostanie zarejestrowane czy też nie? :)