LAN Protocolo de Mensajes - vykymoon/rpg-character-creator GitHub Wiki

Toda la comunicación es texto JSON sobre WebSocket. Cada mensaje tiene un campo type.

Resumen

Dirección type Cuándo
Cliente → Servidor join Al conectarse
Cliente → Servidor move Al pulsar una tecla de movimiento
Servidor → Cliente map Solo al jugador que hizo join
Servidor → Clientes state A todos, tras cada join, move válido o desconexión

Cliente → Servidor

Todos los mensajes del cliente deben incluir playerId: el servidor lo lee siempre, y si falta, descarta el mensaje como inválido.

join

{
  "type": "join",
  "playerId": "Aria#482",
  "name": "Aria",
  "race": "Elfo",
  "characterClass": "Mago",
  "items": ["Túnica de Mago", "Capa Élfica"]
}
Campo Tipo Notas
playerId string Identificador único de la sesión. GameApp lo genera como nombre#(milisegundos % 1000)
name, race, characterClass string Opcionales; si faltan o son null se guardan como null
items array de string Opcional; se ignoran los elementos que no sean texto

move

{ "type": "move", "playerId": "Aria#482", "x": 3, "y": 2 }

x e y son coordenadas absolutas de celda destino (no un desplazamiento).

Servidor → Cliente

map

{
  "type": "map",
  "width": 50,
  "height": 50,
  "rows": ["##################################################", "#....T...."]
}

rows tiene height cadenas de width caracteres (ver tabla de terreno en Visión General).

state

{
  "type": "state",
  "players": {
    "Aria#482": {
      "playerId": "Aria#482", "x": 3, "y": 2,
      "name": "Aria", "race": "Elfo", "characterClass": "Mago",
      "items": ["Túnica de Mago"]
    },
    "Thor#117": { "playerId": "Thor#117", "x": 10, "y": 7, "name": "Thor", "race": "Orco", "characterClass": "Guerrero", "items": [] }
  }
}

Es la foto completa de todos los jugadores (no un diff). El cliente reemplaza todo lo que tenía.

Secuencia de una sesión

sequenceDiagram
    participant C as GameApp (cliente)
    participant S as GameServer
    participant G as GameSessionService
    C->>S: (handshake WebSocket)
    C->>S: join {playerId, name, race, class, items}
    S->>G: join(...)
    S-->>C: map {rows}
    S-->>C: state {players}   (a todos)
    loop cada tecla de movimiento
        C->>C: ¿celda caminable según mapRows?
        C->>S: move {x, y}
        S->>G: move(id, x, y) → MovementRules
        S-->>C: state {players}   (a todos)
    end
    C->>S: (cierra la conexión)
    S->>G: remove(id)
    S-->>C: state {players}   (a los que quedan)

Comportamiento ante errores

Situación Resultado
JSON mal formado, falta type o playerId El servidor lo registra en consola (mensaje invalido) y lo ignora; no se cae
type desconocido tipo de mensaje desconocido en consola
move a una celda no caminable o fuera del mapa Se ignora; el servidor igualmente reenvía el state (sin cambios)
move de un playerId que no hizo join Se crea el jugador en esa posición (sin datos de personaje)

Probar el protocolo a mano

Con el cliente de consola:

mvn compile exec:java@client -Dexec.args="ws://localhost:8887 p1"

Al conectarse envía un join solo con playerId. Luego se escribe x y (por ejemplo 3 4) para enviar un move. Cada mensaje recibido se imprime como [client] estado recibido: ….