Domanda

Come modelleresti un server di gioco a turni come API RESTful? Ad esempio, un server di scacchi, in cui è possibile giocare a scacchi contro un altro client della stessa API. Avresti bisogno di un modo di richiedere e negoziare un gioco con l'altro cliente e un modo di giocare le singole mosse del gioco.

È un buon candidato per un'API REST (RESTful)? O dovrebbe essere modellato in un modo diverso?

È stato utile?

Soluzione

Quali sono le risorse che stai cercando di modellare? Sembrerei averne quattro: tu, il tuo avversario, il gioco particolare (sessione, istanza) e lo stato del tabellone. Quindi inizierebbe con qualcosa del genere

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

abbiamo una buona introduzione / panoramica su InfoQ .

Altri suggerimenti

Sto pensando a qualcosa del tipo:

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

per quanto riguarda le risorse di base. Non sono sicuro di come gestire " l'altro giocatore si è ancora mosso? & Quot; idea, comunque. Ho pensato di avere semplicemente il blocco di richiesta GET fino a quando non viene giocata la mossa, ad es. il mio cliente Metterebbe le coordinate del mio trasferimento in

/game/13/move/1

e poi RICEVERE

/game/13/move/2

Il server non risponderà immediatamente, ma manterrà aperta la connessione fino a quando l'altro giocatore non verrà spostato (ovvero PUT in quella posizione). È questo ciò a cui nakajima si riferisce come "comet-esque"?

Charlie, non sono del tutto sicuro di cosa intendevi con il token " per chi è il turno - questo risolve lo stesso problema senza la necessità di polling o di bloccare una connessione?

Per gli ID giocatore, ha senso modellarli come risorsa in parte dell'URL? Avevo intenzione di utilizzare semplicemente l'autenticazione utente HTTP (in cui l'utente / pass viene inviato come parte di ogni richiesta). Potresti comunque ottenere la maggior parte delle risorse senza autenticazione, ma se provassi, diciamo,

PUT /game/13/move/2

ti darebbe un errore di autorizzazione negata se non avessi le credenziali corrette per quel gioco.

OK, l'idea di base di REST è che stai trasferendo lo stato; vuoi avere un "stato sessione" scarso o nullo " sul server. Quindi non vorrai usare lo stato della sessione e un keepalive, che è ciò che fa la cometa. Ma pensa all'esempio di un gioco di posta elettronica: entrambi avete una copia del tabellone e scambiate le mosse. L'ufficio postale non è a conoscenza del gioco.

Ora, ammetto che questo mi sta crescendo nella mente mentre ci penso - in effetti, potrei scrivere un articolo basato su questa domanda - ma ecco l'idea, come alcune storie:

  1. Vuoi giocare una partita a scacchi linea, quindi vai a un URI noto a prendine uno. Torna indietro di una pagina mostrando chi, se qualcuno, sta aspettando per iniziare una partita.
  2. Scegli una delle persone in attesa per giocare e fare clic sull'appropriato collegamento. Ottieni un nuovo display (ajax magia qui se vuoi) con il installazione della scheda. Uno di voi è bianco il bianco si sposta per primo.
  3. Chiunque abbia il diritto di muoversi entra una mossa e si impegna (come prendere la tua mano dal pezzo in una partita.) La scheda si aggiorna e il diritto di la mossa va all'altro giocatore.

Non hai bisogno di molto in termini di stato del server --- anche se potresti voler estenderlo tenendo traccia delle mosse, ecc., per la classifica - e la domanda su chi ha il diritto di muovere può essere calcolata interamente dalla pagina della bacheca: se hai il diritto, hai un modulo per inserire una mossa; quando rispedisci il modulo torna indietro, la risposta ti restituisce una pagina senza spazio per inserire una mossa.

Di " il token " Intendo solo una rappresentazione arbitraria di quel bit di stato "La mia mossa" / "La tua mossa".

Sembra che le risorse necessarie siano

  • A " trova un gioco " home page
  • Una pagina utente se stai monitorando le statistiche e tale
  • un URI unico per ogni gioco attivo.

Grazie, Charlie. Non sono ancora chiaro come vieni informato della mossa dell'avversario nel tuo schema. Naturalmente la questione di chi ha il diritto di muoversi può essere calcolata semplicemente - dalla risorsa del consiglio di amministrazione, o usando una risorsa separata che indica esplicitamente di chi è il turno di muoversi. Ma come fa un cliente a sapere che questa risorsa è cambiata? Deve semplicemente sondare continuamente, ricordando lo stato precedente, fino a quando non nota che qualcosa è cambiato? Nel modello di ufficio postale, l'ufficio postale "spinge" un messaggio al client (la tua casella di posta), che non è possibile in HTTP.

Suppongo che questo faccia parte di una domanda più generale: se esiste una risorsa REST che voglio monitorare per cambiamenti o modifiche, qual è il modo migliore per farlo? E c'è qualcosa che il server può fare per rendere questo più facile per il client?

Penso che pubblicherò questo post come una domanda separata poiché penso che sia interessante da solo.

Modificato per aggiungere: What è un modo RESTful di monitorare una risorsa REST per le modifiche?

Non credo che REST sia una buona scelta per una tale applicazione. Le trasformazioni e le operazioni che è necessario eseguire (effettuare lo spostamento, visualizzare la cronologia degli spostamenti, annullare, ottenere suggerimenti, attivare la notifica) non si associano perfettamente al concetto di risorse REST. (Le difficoltà sono forse più ovvie se si considera come potrebbe apparire un'API RESTful per giochi a turni più complicati come Scrabble o Monopoly.)

Penso che qualsiasi API REST ragionevole finirà probabilmente per essere un wrapper per qualcosa di non RESTful, come un protocollo stateful che ha inviato notazione di gioco portatile avanti e indietro.

Penso che potresti modellare RESTfully. L'implementazione sarà più difficile, perché avresti bisogno di un cometa ) -esque soluzione o dovresti eseguire il polling del server a un intervallo relativamente breve tramite AJAX.

In termini di come esporresti un'interfaccia RESTful, direi che hai bisogno di una tavola da gioco con coordinate, pezzi che possono occupare quelle coordinate e azioni che alterano quelle coordinate.

Quando un giocatore fa una mossa, viene creata una nuova azione. Dopo la convalida per assicurarti che sia consentito, aggiorni lo stato del gioco, quindi esegui il rendering di qualsiasi risposta necessaria per aggiornare l'interfaccia utente.

Quindi in pratica è così che lo modellerei. Il lato dell'implementazione è ciò che considererei la maggiore difficoltà qui.

Non credo sia tutto così compiacente, Nakajima. Passeresti i dati, diciamo in JSON, per la posizione della scacchiera, per le mosse e con un token per chi ha la mossa successiva. Sarebbe esattamente come giocare per posta.

Inizi andando nel gioco e cercando un partner, quindi

/game/

ti dà un elenco di persone in attesa. quando entri, se non c'è nessuno in attesa ricevi un ID di gioco; altrimenti scegli qualcuno in attesa e ottieni l'ID del gioco che hanno.

/game/gameID

ti mostra la scheda. Hai bisogno di qualcosa per decidere chi gioca al bianco, lo lascerò come esercizio. L'operazione GET ti dà la scheda, quindi un POST invia una mossa; se non hai la mossa ricevi un errore. Trovi il risultato al prossimo GET.

Inferno, in questo modello non sto nemmeno usando l'ID giocatore, anche se potrebbe essere buono, quindi nessuno può intrufolarsi nel gioco come un kibitzer.

Quindi il tuo pensiero è che invece di rendere le azioni un oggetto di prima classe, ogni mossa sarebbe trattata come un aggiornamento del gioco stesso? È certamente un modo diverso di farlo, anche se penso che preferirei dividere l'oggetto azione nella sua entità di prima classe per un paio di ragioni. Il motivo principale è che credo sia più testabile. Se una mossa è valida o meno potrebbe vivere nell'oggetto azione, invece di preoccuparsi che la tavola sia sempre in uno stato valido. Certo, non so che cosa implicherebbe nessuno dei due approcci, ma questo mi sembra migliore.

L'altro motivo, che puoi confutare totalmente tramite YAGNI, è che un oggetto azione di prima classe fornirebbe una cronologia delle mosse. Interessante forse, ma a meno che un requisito, sia un punto controverso.

Uno degli sviluppatori di planet.jabber è coinvolto in Chesspark , una comunità di scacchi online. Stanno usando ampiamente Jabber / XMPP; se non sbaglio, questi sono i suoi post sull'argomento .

XMPP è un protocollo di messaggistica istantanea, basato approssimativamente su piccoli scambi di messaggi XML. Esistono librerie per la maggior parte delle lingue, incluso Javascript. Non sono sicuro che si adatta al tuo problema, però.

Per un gioco semplice come gli scacchi, si tratta solo di definire il tipo di media.

Ecco un esempio di quello che probabilmente è un tipo di media semplificato per modellare una partita a scacchi.

Salterò la gestione di più giochi che potrebbero essere in esecuzione sullo stesso server e modellerò un gioco già in esecuzione.

Il primo passo è di solito definire un indice per l'applicazione.

index

Il punto di ingresso per il gioco. Scarica questo per scoprire informazioni sul gioco.

Il payload potrebbe assomigliare a questo:

{
    "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/..." },
                    ...
                ]
            }
        },

        ...
    ]
}

mossa

INVIA un payload JSON ai collegamenti contrassegnati con un rel di move per spostare un pezzo. I seguenti campi DEVONO essere inclusi:

  • posizione: l'URI dello spazio in cui spostarsi

Le risposte riuscite hanno un codice di stato di 200 e conterranno un'entità uguale al payload index con lo stato di gioco aggiornato.

400 se l'utente non è autorizzato a spostare lì il suo pezzo, o se non è il suo turno.

Lettore

OTTIENI una descrizione di un giocatore.

I seguenti campi DEVONO essere nella risposta:

  • nome utente: nome utente del giocatore
  • href: URI che identifica il giocatore di questo gioco.

pezzo

I pezzi sono incorporati nel payload index , ma possono esistere da soli. Ogni pezzo DEVE avere i seguenti campi:

  • type: un URI che identifica il tipo di pezzo. PER ESEMPIO. Vescovo, Torre, Re. Ottenere questo URI può fornire informazioni su come funziona questo pezzo nel gioco degli scacchi.
  • href: un URI che identifica il pezzo reale su questo tabellone. OTTIENI richieste a questo URI PUOI fornire informazioni su questo particolare pezzo.

Ogni pezzo DEVE avere un link move .


Una decisione di progettazione alternativa che avrei potuto prendere qui è stata quella di fornire un link individuale per ogni mossa valida. Potrebbero esserci buone ragioni per farlo, ma volevo dimostrare che non è necessario. Probabilmente ci sono alcune altre risorse che vorresti includere per gestire cose come aiutare il cliente a determinare di chi è il turno e cosa no.

Per giochi più complicati, come Civilization, RTS, FPS o MMOG e quant'altro, potrebbe non essere così pratico IMO.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top