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: ….