• Domingo 17 de Noviembre de 2024, 17:37

Autor Tema:  Problema Raro Con Un Juego Online (winsock/c++)  (Leído 1998 veces)

plaf

  • Miembro activo
  • **
  • Mensajes: 57
    • Ver Perfil
Problema Raro Con Un Juego Online (winsock/c++)
« en: Jueves 1 de Enero de 2004, 21:08 »
0
holas, q emocion, primer tema del año :)

bueno pues les cuento q he estado haciendo un juego en c++ y ocupo la libreria winsock2 para hacerlo online. el juego es por turnos asi q no necesito hacer cosas raras para la transferencia de datos, solo mando/recibo
el problema es q por alguna razon cuando hosteo un juego yo veo todo perfectamente, pero parece q el/los otro(s) jugador(es) por algun motivo no recibe(n) perfectamente los datos y ve como si jugada por medio yo hiciera la misma movida q el. bueno la cosa es medio rara... pero aca pongo una idea del ciclo q hace el juego a ver si alguien ve algun desperfecto:

Código: Text
  1.  
  2. void jugador::mover()
  3. {
  4. int Listo=0;
  5. char txt[16], aux[16];
  6. x=xa+dx; y=ya+dy; // asigno las posiciones nuevas
  7. xx=-1; yy=-1; // esto es como la "posicion anterior" para redibujar solo si cambia
  8. while(!Listo)
  9. {
  10. if(x!=xx || y!=yy){ dibujo de nuevo }
  11. xx=x; yy=y;
  12. // ahora lo q importa
  13. if(soy_yo) // si es mi turno mando mis movimientos
  14. {
  15. leo el teclado y recalculo x e y o hago Listo=1; si apretaron enter
  16. sprintf(txt,"%ld",10000*x+y); // mando la posicion nueva como xxxxyyyy
  17. if(hosteo)
  18. {
  19. if(Listo) mandar_todos("l"); // mandar_todos() hace send() a todos los jugadores
  20. else mandar_todos(txt); // si aprete enter mando una l, si no, la nueva posicion
  21. }
  22. else // si no soy el host
  23. {
  24. if(Listo) mandar("l"); // mandar() le manda al host
  25. else mandar(txt);
  26.  
  27. recibir(aux); // cuando el host recibe algo se lo manda a todos, incluyendome a mi
  28. }
  29. }
  30. else // si no es mi turno solo recibo
  31. {
  32.  
  33. strcpy(texto,""); // x si acaso
  34.  
  35. if(hosteo)
  36. {
  37. recibir(turno,txt); // recibo lo q hizo el jugador de turno
  38. mandar_todos(txt); // y se lo mando a todos
  39. }
  40. else{ recibir(txt); } // si no soy el host recibo lo q el me mando (q es lo q le mando el jugador)
  41.  
  42. // aca manejo lo q me llego...
  43.  
  44. }
  45.  
  46. } // fin del while
  47.  
  48. // aca manejo los resultados finales del movimiento...
  49.  
  50. }
  51.  
  52.  

bueno la idea es esa, si no se cacha bien mas rato pongo el codigo entero por si a alguien le interesa

Amilius

  • Miembro HIPER activo
  • ****
  • Mensajes: 665
    • Ver Perfil
Re: Problema Raro Con Un Juego Online (winsock/c++)
« Respuesta #1 en: Viernes 2 de Enero de 2004, 02:19 »
0
Te digo algo: Si necesitas hacer un par de cosas "raras" para que tu juego funcione bien.

1.- Cuando envias información, la información no siempre llega "igual" como la mandaste. Ej:

Mandas "Hola", luego mandas "Mundo":

Puede que reciban: "Hola" y luego "Mundo", también: "HolaMundo" todo unido, :(
esto complica la recepción de datos y puede que necesites reconsiderar tu "protocolo" de envio de mensajes que creaste para tu juego. Necesitas diferenciar el "Hola" del "Mundo". Puedes usar un caracter especial que es lo más fácil, o también indicar la longitud de cada fragmento de información que envies:

Así si a tu método de recepción de mensajes llega:

t4Holat5Mundo

Tu método podría reconocer la "t" como inicio de fragmento de texto y el 4 como la longitud de ese texo. Obviamente mejor si envías datos binarios y en lugar de enviar un "4" en texto envías un 4 binario (un byte) para poder enviar más información sin desperdiciar ancho de banda:

Ej, Mandar 200 en texto es menos eficiente y más complicado que enviar un 200 en binario que ocupa un solo byte. Es más fácil hacer un programa que luego de leer la "t" que indica texto lea el siguiente byte y determine la longitud del texto que sigue que tener que distinguir si 200 se refiere a que la longitud del texto es 200 caracteres o es de 2 caracteres y los caracteres son "00".


2.- Si el paquete es demasiado grande, se fragmenta. Es bueno que evites despachar paquetes grandes (más de 1Kb de información), especialmente si quieres hacer un juego en "Tiempo real" ya que esperar por el resto de información faltante destruye el ritmo del juego y en los juegos en "tiempo real" el ritmo de juego es vital para crear la ilusión de continuidad.

Como tu juego es por turnos, puedes agregar a tu método de recepción un sistema que detecte que falta información adicional y espere a que llegue otro paquete con la información faltante en cierto tiempo, evitando que el juego quede detenido mucho tiempo.

Ej. Te llega "t4ho", como esperas 4 caracteres y sólo te llegaron dos esperas hasta que en un máximo de digamos 4 segundos llegue otro paquete con el "la"

¿Pero no se supone que esas cosas las hace el winsock?

Nadie dijo que los "paquetes" no vayan a llegar juntos o primero llegue una parte y luego el resto. Lo que asegura el "soket" es que TODA la información llegue a destino... eventualmente (si no ocurren problemas mayores) y llegue en orden. Tu tienes que saber distinquir tus fragmentos de datos que envias y saber si ya te llego todo tu fragmento.

P.D.

Si no me crees haz que tu programa te muestre en pantalla todo lo que recibe el socket y haz la prueba enviando cantidades muy chicas de información en varias llamadas a "send" una detrás de otra  y también enviando un buen paquete de información como de 64Kb y verás los resultados. Prueba sólo en tu máquina, en red a otra máquina y si es posible por internet usando una conexión "dial up". Verás las sorpresas que te esperan.

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

  La diferencia de los juegos "con turnos" de los juegos en "tiempo real" es que en los juegos en tiempo real puedes o no usar tu turno, los turnos apenas duran una fracción de segundo o en el caso de juegos de estrategia tu puedes mandar la cantidad de órdenes que desees pero tus "bichos" se mueven y atacan en sus respectivos turnos. Obviamente no se espera a que el jugador realize su "movida" para que comience el siguiente turno y pocas veces los turnos están sincronizados.

 Turnos no sincronizados: por bicho y acción existe un tiempo de espera hasta realizar la siguiente acción, esto lo sincronizan muy bien con la animación de los "bichos" para desvanecer cualquier impresión que todo el juego va por turnos y dar la sensación de continuidad.

plaf

  • Miembro activo
  • **
  • Mensajes: 57
    • Ver Perfil
Re: Problema Raro Con Un Juego Online (winsock/c++)
« Respuesta #2 en: Miércoles 7 de Enero de 2004, 07:17 »
0
wow, gracias por la respuesta, a parte de util tb muy ilustrante :)
x lo menos el problema de mandar paquetes muy grandes no lo voy a tener, al menos por ahora, xq lo unico q hago es mandar cosas de maximo unos 12 caracteres, o en casos muy extremos hasta 32 bytes... pero eso es una cosa impresionantemente gigante...

bueno segun lo q dices no se ve dificil solucionar lo de los datos q llegan a pedazos, con un while hasta q no llegue el ultimo byte basta, pero lo q no veo bien es como separar lo q me llega pegado sin perder lo del final sin hacer cambios muy radicales...

por ejemplo podria dejar algo asi y no cambiar nada en el juego:

Código: Text
  1.  
  2. void wsock::mandar(int Num, char *Datos)
  3. {
  4.     if(sock[Num-1]!=INVALID_SOCKET)
  5.     {
  6.         char aux[16];
  7.         sprintf(aux, "%c%s", (char)(strlen(Datos)), Datos); // mando como #texto
  8.  
  9.         send(sock[Num-1], aux, 16, 0);
  10.     }
  11. }
  12.  
  13. void wsock::mandar(char *Datos)
  14. {
  15.     mandar(1,Datos);
  16. }
  17.  
  18. void wsock::mandar_todos(char *Datos)
  19. {
  20.     int i;
  21.     for(i=1;i<=jugadores;i++) mandar(i,Datos);
  22. }
  23.  
  24. void wsock::recibir(int Num, char *Datos)
  25. {
  26.     if(sock[Num-1]!=INVALID_SOCKET)
  27.     {
  28.         char aux[16];
  29.         strcpy(Datos, "");
  30.         do
  31.         {
  32.             recv(sock[Num-1], aux, 16, 0);
  33.             strcat(Datos, aux);
  34.         }while(strlen(Datos) <= (int)(Datos[0])); // sale cuando recibio #+1 caracteres (el '#' es el +1)
  35.  
  36.         int i;
  37.  
  38.         if(strlen(Datos) > (int)(Datos[0])+1) // si me llego mas de lo q esperaba
  39.         {
  40.             for(i=0; i < (int)(Datos[0])+1; i++) // copio en aux lo q esperaba
  41.                 aux[i] = Datos[i];
  42.             aux[i] = 0;
  43.  
  44.             strcpy(Datos, aux);
  45.         }
  46.  
  47.         for(i=0; i < strlen(Datos); i++) // y al final le quito el #
  48.             Datos[i] = Datos[i+1];
  49.     }
  50. }
  51.  
  52. void wsock::recibir(char *Datos)
  53. {
  54.     recibir(1,Datos);
  55. }
  56.  
  57.  

el problema es q si quisiera prevenir lo de los textos pegados tendria q hacer algo como pasar de parametro al recibir() un arreglo de strings para dejar una especie de cola de datos en espera, y despues al codigo del juego decirle q si no hay nada en la cola q reciba, y si hay algo q lo use y lo saque de la cola. eso deberia funcionar, o no? lo malo es q me da lata hacerlo ahora, pero si es necesario lo podria hacer otro dia :P