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

El cliente tiene tres piezas: el cliente de red (GameClient), el cliente gráfico (GameApp) y el lector de personajes (CharacterLoader).

GameClient

Extiende WebSocketClient. Recibe un Consumer<String> que se invoca con cada mensaje del servidor:

client.setOnState(this::handleMessage);

Por defecto imprime en consola ([client] estado recibido: …), de modo que el mismo cliente sirve para la consola (ClientMain) y para la UI (GameApp). También registra en consola la conexión, el cierre y los errores.

ClientMain (cliente de consola)

Herramienta de pruebas sin interfaz.

mvn compile exec:java@client -Dexec.args="ws://localhost:8887 p1"
  • Argumentos: [url] [playerId] (por defecto ws://localhost:8887 y player-<ms%1000>).
  • Envía join con solo playerId.
  • Luego lee líneas: x y envía un move; salir cierra.

CharacterLoader

Lee characters.json sin depender de las clases del modelo, de forma tolerante, y devuelve objetos Info (name, race, characterClass, items, skills, stats).

Dato Claves aceptadas (en orden)
Nombre name, nombre
Raza race, raza
Clase characterClass, class, clazz, clase
Ítems outfit, outfits, items, inventory, equipment (se acumulan)
Habilidades skills
Stats stats, finalStats, baseStats (objeto)
  • El archivo puede ser un arreglo o un objeto que contenga un arreglo.
  • Si un valor es un objeto, usa su name, nombre o title como etiqueta; si no, el JSON tal cual.
  • Búsqueda del archivo: src/main/resources/data/characters.json, data/characters.json, characters.json. Si no aparece, GameApp abre un FileChooser.

Importante: un personaje guardado por el wizard tiene los stats como campos planos (strength, dexterity, …), no dentro de un objeto stats. Por eso la sección Stats de la barra lateral de GameApp queda vacía. Ver Limitaciones y Mejoras.

GameApp (cliente gráfico JavaFX)

Clase principal alternativa: mvn compile javafx:run@game, o desde la galería con Multijugador LAN.

Secuencia de arranque (start)

  1. pickCharacter() — carga los personajes y muestra un ChoiceDialog. Si no hay ninguno: "No hay personajes guardados. Crea uno en el wizard primero." y sale.
  2. Pide la URL del servidor (por defecto ws://localhost:8887).
  3. Genera playerId = nombre + "#" + (currentTimeMillis % 1000).
  4. Conecta con connectBlocking(3, SECONDS). Si falla: "No se pudo conectar a " y sale.
  5. Envía join con nombre, raza, clase e ítems.
  6. Crea el lienzo (50×50 celdas de 14 px = 700×700 px) y la barra lateral (220 px).
  7. Registra el manejo de teclado y muestra la ventana "RPG LAN - <nombre>" (no redimensionable).
  8. Carga la imagen de fondo y dibuja.

Al cerrar la ventana: client.close() + Platform.exit().

Controles

Tecla Acción
↑ / W Arriba
↓ / S Abajo
← / A Izquierda
→ / D Derecha

Se usa un addEventFilter (no un handler) y se hace consume() para que las flechas no muevan la selección de las listas de la barra lateral.

Recepción de mensajes (handleMessage)

Se ejecuta en el hilo de red y delega la UI con Platform.runLater.

type Acción
map Guarda mapRows (usado para la validación local del movimiento) y redibuja
state Reconstruye el mapa de jugadores (PV: x, y, nombre, clase) y redibuja

Movimiento (move(dx, dy))

  1. Toma la posición propia del último state recibido (si aún no existe, no hace nada).
  2. Calcula la celda destino.
  3. Si según mapRows es caminable (. o =), envía move.
  4. No actualiza la posición localmente: espera el state del servidor (modelo autoritativo). El chequeo local solo evita mandar mensajes inútiles.

Dibujo (draw)

  • Fondo: background_multiplayer.jpg estirada al lienzo; si no carga, color sólido #1e2327.
  • Cada jugador: círculo de 12 px con color derivado del hash de su clase (Color.hsb(hash % 360, 0.6, 0.95)), de modo que misma clase = mismo color.
  • El jugador propio lleva un anillo dorado.
  • El nombre se dibuja encima del círculo.

Barra lateral

Nombre (negrita), "Raza / Clase", y secciones Stats, Items y Habilidades (listas de 120 px, no enfocables). Al pie: "Mover: flechas / WASD".

Imagen del mapa

Intenta, en orden: /map/background_multiplayer.jpg, /battle/map/background_multiplayer.jpg, /background_multiplayer.jpg, y como último recurso una ruta absoluta de Windows en Descargas. Los archivos reales están en src/main/resources/sprites/map/ (.jpg y .png).

Las tres rutas del classpath no coinciden con la ubicación real (/sprites/map/…). Ver Limitaciones y Mejoras.

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