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

Paquete com.proyecto.rpg.adapters.server.

ServerMain

int port = args.length > 0 ? Integer.parseInt(args[0]) : 8887;
GameServer server = new GameServer(port);
server.start();          // no bloqueante

Imprime Servidor host levantado en ws://localhost:<puerto> y deja el proceso vivo hasta Ctrl+C.

Formas de arrancarlo

Forma Comando / acción
Desde la aplicación Botón Iniciar Servidor en la galería (puerto 8887)
Sin interfaz mvn compile exec:java@server
Otro puerto mvn compile exec:java@server -Dexec.args="9000"

GameServer

Extiende org.java_websocket.server.WebSocketServer.

super(new InetSocketAddress(port));   // todas las interfaces de red

Escucha en todas las interfaces (no solo localhost), lo que permite que otras máquinas de la LAN se conecten.

Estado interno

Campo Uso
GameSessionService sessionService Fuente de verdad del juego
Map<WebSocket, String> playerIdByConnection Asocia cada conexión con su jugador (ConcurrentHashMap)
Gson gson Parseo de los mensajes entrantes

Callbacks

Callback Comportamiento
onOpen Solo registra cliente conectado: <dirección>
onMessage Parsea el JSON y despacha por type (ver abajo)
onClose Quita la conexión del mapa, llama a sessionService.remove(playerId), hace broadcast y registra jugador desconectado
onError Imprime el error en consola
onStart Imprime GameServer autoritativo escuchando en puerto <n>

Despacho de mensajes (onMessage)

JsonObject json = gson.fromJson(message, JsonObject.class);
String type = json.get("type").getAsString();
String playerId = json.get("playerId").getAsString();

switch (type) {
    case "join" -> { … }
    case "move" -> { … }
    default     -> System.err.println("tipo de mensaje desconocido: " + type);
}

Todo el bloque está dentro de un try/catch (Exception): un mensaje inválido se registra y no tumba el servidor.

join

  1. playerIdByConnection.put(conn, playerId).
  2. Lee name, race, characterClass (con el helper str, que tolera campos ausentes o null) e items.
  3. sessionService.join(...).
  4. conn.send(sessionService.mapAsJson()) — el mapa va solo a este cliente.
  5. broadcastState().

move

  1. Lee x e y como enteros.
  2. sessionService.move(playerId, x, y).
  3. broadcastState().

broadcastState()

String json = sessionService.stateAsJson();
for (WebSocket conn : getConnections()) conn.send(json);

Envía la foto completa del estado a todas las conexiones abiertas.

Concurrencia

  • GameSessionService usa ConcurrentHashMap y métodos synchronized.
  • Java-WebSocket atiende las conexiones en hilos propios, por eso el acceso al estado está protegido.
  • mapAsJson() no está sincronizado: el mapa es de solo lectura tras la construcción.

Ciclo de vida de un jugador

stateDiagram-v2
    [*] --> Conectado: onOpen
    Conectado --> EnJuego: join (spawn en 2,2)
    EnJuego --> EnJuego: move válido
    EnJuego --> [*]: onClose (se elimina y se notifica)
    Conectado --> [*]: onClose sin join
Loading

Limitaciones del servidor

Ver Limitaciones y Mejoras. Las más relevantes: no valida que el movimiento sea de una sola casilla, confía en el playerId y los datos de personaje que envía el cliente, y no hay forma de detenerlo desde la interfaz.

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