Multijugador LAN - vykymoon/rpg-character-creator GitHub Wiki

El modo LAN permite que varios jugadores, cada uno con un personaje creado en el wizard, compartan un mapa y se vean moverse en tiempo real dentro de una red local.

  • Commit de implementación: 827ab59 — feat: implementar multijugador LAN con servidor autoritativo (ADR-001)
  • Mejoras posteriores: 05081c5 (imagen de fondo del mapa y botones en la galería)

Decisión de arquitectura (ADR-001): servidor autoritativo

El servidor es la única fuente de verdad del estado del juego (posiciones, mapa, jugadores conectados). Los clientes:

  1. Envían intenciones ("quiero moverme a x,y").
  2. Dibujan el estado que el servidor les devuelve.

Un cliente nunca modifica su propia posición: solo cambia cuando el servidor confirma.

Aspecto Valor
Transporte WebSocket (org.java-websocket:Java-WebSocket:1.5.6)
Formato JSON (Gson 2.11.0)
Puerto por defecto 8887
Mapa 50×50 celdas, generado de forma determinista en el servidor
Punto de aparición Celda (2, 2)

Arquitectura por capas

flowchart LR
    subgraph Cliente
        GA[ui/GameApp<br/>JavaFX] --> GC[adapters/client/GameClient]
        CL[ui/CharacterLoader] --> GA
    end
    subgraph Servidor
        GS[adapters/server/GameServer] --> SVC[application/GameSessionService]
        SVC --> MR[domain/MovementRules]
        MR --> MAP[domain/GameMap]
        SVC --> PS[domain/PlayerState]
    end
    GC <-->|WebSocket + JSON| GS
    JSON[(characters.json)] --> CL
Loading

Flujo de dependencias: adapters → application → domain. El dominio no conoce WebSocket ni JavaFX.

Archivos

com.proyecto.rpg
├── domain/
│   ├── GameMap.java            Mapa 50x50 con terreno
│   ├── MovementRules.java      Valida movimientos
│   ├── PlayerState.java        Estado de un jugador (se serializa)
│   └── Position.java           (sin uso actual)
├── application/
│   └── GameSessionService.java Fuente de verdad + serialización JSON
├── adapters/server/
│   ├── GameServer.java         WebSocketServer
│   ├── ServerMain.java         main() del servidor
│   └── client/
│       ├── GameClient.java     WebSocketClient con callback
│       └── ClientMain.java     Cliente de consola
└── ui/
    ├── GameApp.java            Cliente gráfico JavaFX
    ├── CharacterLoader.java    Lee characters.json tolerantemente
    └── GalleryController.java  Botones "Iniciar Servidor" / "Multijugador LAN"

Dominio

GameMap

Cuadrícula char[][] de width × height. La generación usa new Random(42), así que siempre produce el mismo mapa.

Carácter Terreno ¿Caminable?
. Pasto Sí
= Camino Sí
# Piedra (borde del mapa) No
T Árbol (8 % de las celdas interiores) No
~ Agua No

Elementos generados: borde de piedra, árboles aleatorios, dos lagos (en (10,8) de 8×6 y en (32,30) de 10×8), caminos en cruz por el centro y una zona de aparición despejada (celdas 1 a 5 en x e y).

MovementRules

isValidMove(x, y) delega en map.isWalkable(x, y). Hoy solo valida el terreno de destino.

PlayerState

Campos públicos: playerId, x, y, name, race, characterClass, items (lista de strings).

Aplicación: GameSessionService

Guarda un ConcurrentHashMap<String, PlayerState> y el GameMap. Los métodos son synchronized.

Método Comportamiento
join(id, name, race, class, items) Crea al jugador en (2,2) si no existe y actualiza sus datos de personaje
move(id, x, y) Valida con MovementRules; si es válido actualiza y devuelve true, si no ignora y devuelve false
remove(id) Elimina al jugador
stateAsJson() {"type":"state","players":{…}}
mapAsJson() {"type":"map","width":50,"height":50,"rows":[…]}

Integración con el menú

  • Iniciar Servidor (onStartServer): ejecuta ServerMain.main(new String[]{}) en un hilo nuevo para no congelar la UI → puerto 8887.
  • Multijugador LAN (onPlayMultiplayer): cierra la ventana del menú y ejecuta new GameApp().start(new Stage()).

Siguiente lectura

⚠️ **GitHub.com Fallback** ⚠️