Pregunta

¿Cómo modelarías un servidor de juegos por turnos como una API RESTful? Por ejemplo, un servidor de ajedrez, donde puedes jugar una partida de ajedrez contra otro cliente de la misma API. Necesitaría alguna forma de solicitar y negociar un juego con el otro cliente, y alguna forma de jugar los movimientos individuales del juego.

¿Es este un buen candidato para una API REST (RESTful)? ¿O debería ser modelado de una manera diferente?

¿Fue útil?

Solución

¿Cuáles son los recursos que intenta modelar? Parecería tener cuatro: usted, su oponente, el juego particular (sesión, instancia) y el estado del tablero de juego. Entonces comenzaría con algo como

/game
/game/gameID/gamer/gamerID
/game/gameID/board

tenemos una buena introducción / descripción general en InfoQ .

Otros consejos

Estoy pensando en algo como:

/game/<gameID>/move/<moveID>

en lo que respecta a los recursos básicos. No estoy seguro de cómo manejar el " ¿se ha movido el otro jugador todavía? & Quot; idea, sin embargo. He pensado en simplemente tener el bloque de solicitud GET hasta que se juegue el movimiento, es decir. mi cliente PONDRÍA las coordenadas de mi movimiento a

/game/13/move/1

y luego OBTENDRÍA

/game/13/move/2

El servidor no responderá de inmediato, pero mantendrá la conexión abierta hasta que el otro jugador se haya movido (es decir, PONER a esa ubicación). ¿Es esto a lo que se refiere nakajima como "cometa-esque"?

Charlie, no estoy muy seguro de lo que quisiste decir con el "token" para quién es el turno, ¿esto resuelve el mismo problema sin necesidad de sondeo o una conexión de bloqueo?

Para las ID de jugadores, ¿tiene sentido modelarlas como un recurso en parte de la URL? Estaba planeando simplemente usar la autenticación de usuario HTTP (donde el usuario / pase se envía como parte de cada solicitud). Aún podría OBTENER la mayoría de los recursos sin autenticación, pero si intentara, por ejemplo,

PUT /game/13/move/2

le daría un error de permiso denegado si no tuviera las credenciales correctas para ese juego.

Bien, la idea básica de REST es que estás transfiriendo el estado; desea tener poco o nada de "estado de sesión" en el servidor Por lo tanto, no querrá usar el estado de sesión y un keepalive, que es lo que hace Comet. Pero piense en el ejemplo de un juego de jugar por correo: ambos tienen una copia del tablero e intercambian movimientos. La oficina de correos no sabe sobre el juego.

Ahora, admito que esto está creciendo en mi mente mientras lo pienso, de hecho, puedo escribir un artículo basado en esta pregunta, pero esta es la idea, como algunas historias:

  1. Quieres jugar un juego de ajedrez en línea, así que vas a un URI conocido para conseguir uno. Vuelves una página mostrando quién, si alguien, está esperando para comenzar un juego.
  2. Escoges a una de las personas que esperan para jugar y haga clic en el apropiado enlazar. Obtienes una nueva pantalla (ajax magia aquí si quieres) con el Junta puesta. Uno de ustedes es blanco las blancas se mueven primero.
  3. Quien tiene derecho a moverse entra un movimiento y se compromete (como tomar tu mano fuera de la pieza en un juego.) El tablero se actualiza y el derecho a el movimiento va al otro jugador.

No necesita mucho en términos de estado del servidor --- aunque es posible que desee ampliar esto haciendo un seguimiento de los movimientos, etc., digamos para la clasificación, y se puede calcular la pregunta de quién tiene derecho a moverse enteramente desde la página del tablero: si tiene el derecho, tiene un formulario para ingresar un movimiento; cuando devuelve el formulario, la respuesta le devuelve una página sin espacio para ingresar un movimiento.

Por " el token " Solo quiero decir alguna representación arbitraria de ese bit de estado '' Mi movimiento '' / '' tu movimiento ''.

Parece que los recursos que necesita son

  • A " encuentra un juego " página de inicio
  • Una página de usuario si está rastreando estadísticas y tal
  • un URI único para cada juego activo.

Gracias, Charlie. Todavía no tengo claro cómo se le notifica el movimiento del oponente en su esquema. Por supuesto, la cuestión de quién tiene el derecho de moverse puede calcularse simplemente, ya sea desde el recurso del tablero o mediante el uso de un recurso separado que explícitamente indique a quién le toca moverse. Pero, ¿cómo sabe un cliente que este recurso ha cambiado? ¿Tiene que sondear continuamente, recordando el estado anterior, hasta que note que algo ha cambiado? En el modelo de la oficina de correos, la oficina de correos '' empuja '' un mensaje al cliente (su buzón), que no es posible en HTTP.

Supongo que esto es parte de una pregunta más general: si hay un recurso REST que quiero monitorear para detectar cambios o modificaciones, ¿cuál es la mejor manera de hacerlo? ¿Y hay algo que el servidor pueda hacer para facilitar esto al cliente?

Creo que realmente publicaré esto como una pregunta separada ya que creo que es interesante por sí mismo.

Editado para agregar: What ¿Es una manera RESTful de monitorear un recurso REST para cambios?

No creo que REST sea una buena opción para tal aplicación. Las transformaciones y operaciones que necesita hacer (hacer un movimiento, ver el historial de movimientos, deshacer, obtener sugerencias, notificaciones de giro) no se asignan perfectamente al concepto de recursos de REST. (Las dificultades son quizás más obvias si se considera cómo podría verse una API RESTful para juegos por turnos más complicados como Scrabble o Monopoly).

Creo que cualquier API REST sensible probablemente terminaría siendo una envoltura alrededor de algo no RESTful, como un protocolo con estado que envió notación de juego portátil de un lado a otro.

Creo que podrías modelar RESTEMENTE. Implementar será más difícil, ya que necesitarías un solución cometa ) -esque tendría que sondear el servidor en un intervalo relativamente corto a través de AJAX.

En términos de cómo expondría una interfaz RESTful, diría que necesita un tablero de juego con coordenadas, piezas que puedan ocupar esas coordenadas y acciones que alteren esas coordenadas.

Cuando un jugador hace un movimiento, se creará una nueva acción. Después de validar para asegurarse de que esté permitido, actualizaría el estado del juego y luego representaría la respuesta necesaria para actualizar la interfaz de usuario.

Entonces, básicamente, así es como lo modelaría. Sin embargo, el lado de la implementación es lo que consideraría la mayor dificultad aquí.

No creo que sea todo lo que te complace, Nakajima. Pasaría datos, digamos en JSON, para la posición del tablero, para los movimientos y con una ficha de quién tiene el próximo movimiento. Sería exactamente como jugar por correo.

Empiezas yendo al juego y buscando un compañero, así que

/game/

te da una lista de personas esperando. cuando entras, si no hay nadie esperando, obtienes una identificación del juego; de lo contrario, eliges a alguien que espera y obtienes la ID del juego que tienen.

/game/gameID

te muestra el tablero. Necesitas algo para establecer la decisión de quién juega blanco, lo dejaré como ejercicio. La operación GET te da el tablero, por lo que un POST envía un movimiento; Si no tienes el movimiento, obtienes un error. Encontrará el resultado en el próximo GET.

Demonios, en este modelo ni siquiera estoy usando la identificación de jugador, aunque podría ser bueno para que nadie pueda colarse en el juego como un kibitzer.

¿Entonces piensas que en lugar de hacer de las acciones un objeto de primera clase, cada movimiento se trataría como una actualización del juego en sí? Sin duda, es una forma diferente de hacerlo, aunque creo que preferiría dividir el objeto de acción en su propia entidad de primera clase por un par de razones. La razón más importante es que creo que es más comprobable. Si un movimiento es válido o no, podría vivir en el objeto de acción, en lugar de tener que preocuparse de que el tablero esté en un estado válido en todo momento. De acuerdo, no sé qué implicaría cualquiera de los dos enfoques, pero me parece mejor.

La otra razón, que puede refutar totalmente a través de YAGNI, es que un objeto de acción de primera clase proporcionaría un historial de movimientos. Quizás sea interesante, pero a menos que sea un requisito, es un punto discutible.

Uno de los desarrolladores en planet.jabber está involucrado en Chesspark , una comunidad de ajedrez en línea. Están utilizando Jabber / XMPP ampliamente; si no me equivoco, estas son sus publicaciones sobre el tema .

XMPP es un protocolo de mensajería instantánea, basado aproximadamente en un pequeño intercambio de mensajes XML. Hay bibliotecas para la mayoría de los idiomas, incluyendo Javascript. Sin embargo, no estoy seguro de que se adapte a su problema.

Para un juego simple como el ajedrez, en realidad solo se trata de definir el tipo de medio.

Aquí hay un ejemplo de lo que probablemente sea un tipo de medio demasiado simplificado para modelar un juego de ajedrez.

Voy a omitir la administración de varios juegos que pueden ejecutarse en el mismo servidor y simplemente modelar un juego que ya se está ejecutando.

El primer paso suele ser definir un índice para la aplicación.

index

El punto de entrada para el juego. Obtenga esto para descubrir información sobre el juego.

La carga útil podría verse más o menos así:

{
    "links": {
        "self": "http://my-chess-game.host/games/123",
        "player": "http://my-chess-game.host/players/1",
        "player": "http://my-chess-game.host/players/2",
        "me": "http://my-chess-game.host/players/1",
         ...
    }
    "board": [
        {
           "x": 0,
           "y": 1,
           "piece": null,
           "rel": "space",
           "href": "http://my-chess-game/.../boards/123/0/1/something-random-to-discourage-uri-construction"
        },
        {
           "x": 1,
           "y": 2,
           "rel": "space",
           "href": "...",
           "piece": {
               "player": "http://my-chess-game/.../players/1",
               "type": "http://my-chess-game/pieces/Bishop",
               "rel": "piece",
               "href": "http://my-chess-game/games/123/pieces/player1/Bishop/1",
               "links": [
                    { "rel": "move": "href": "http://my-chess-game/.../boards/123/..." },
                    ...
                ]
            }
        },

        ...
    ]
}

move

PUBLICAR una carga JSON en enlaces marcados con un rel de move para mover una pieza. DEBEN incluirse los siguientes campos:

  • ubicación: el URI del espacio para moverse

Las respuestas exitosas tienen un código de estado de 200 y contendrán una entidad que es igual a la carga útil index con el estado actualizado del juego.

400 si el usuario no puede mover su pieza allí, o si no es su turno.

player

OBTENER una descripción de un jugador.

Los siguientes campos DEBEN estar en la respuesta:

  • nombre de usuario: nombre de usuario del jugador
  • href: URI que identifica al jugador de este juego.

piece

Las piezas están incrustadas en la carga útil index , pero PUEDEN existir por sí mismas. Cada pieza DEBE tener los siguientes campos:

  • tipo: un URI que identifica el tipo de la pieza. P.EJ. Obispo, torre, rey. OBTENER ESTE URI PUEDE proporcionar información sobre cómo funciona esta pieza dentro del juego de ajedrez.
  • href: un URI que identifica la pieza real en este tablero. OBTENER solicitudes a este URI PUEDE proporcionar información sobre esta pieza en particular.

Cada pieza DEBE tener un enlace move .


Una decisión de diseño alternativa que podría haber tomado aquí fue proporcionar un enlace individual para cada movimiento válido. Puede haber buenas razones para hacerlo, pero quería demostrar que no es obligatorio. Probablemente hay un puñado de otros recursos que le gustaría incluir para manejar cosas como ayudar al cliente a determinar de quién es el turno y qué.

Para juegos más complicados, como Civilization, RTS, FPS o MMOG y lo que no, esto puede no ser tan práctico IMO.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top