Chat API — Ruleta

Backend PHP + MySQL para el sistema de chat del juego de ruleta (Godot 4). Simula tiempo real mediante long-polling, sin WebSockets, compatible con hosting compartido HostGator.

Esta guía cubre la instalación completa en HostGator, la referencia de la API, la integración con Godot 4 y el uso del panel de administración web.

Componentes del sistema

ComponenteTecnologíaDescripción
Backend APIPHP 7.4+Endpoints REST para enviar, recibir y moderar mensajes
Base de datosMySQL 5.7+Almacena mensajes y lista de jugadores baneados
Chat webJS VanillaInterfaz de chat pública para cualquier usuario
Panel adminJS VanillaLogs en tiempo real, moderación y mensajes del sistema
Cliente GodotGDScript 4Integración nativa con el juego de ruleta

Estructura del proyecto

chat-api/ ├── config.php # Credenciales de BD y API keys ├── schema.sql # Script SQL para crear las tablas ├── .htaccess # Protege config.php y desactiva directorios ├── guia-integracion.html # Este archivo │ ├── includes/ │ ├── db.php # Conexión PDO singleton │ ├── response.php # Helpers: json_response, CORS, método HTTP │ ├── auth.php # Validación de API keys y baneos │ └── GameChatMessage.php # Modelo de mensaje (espejo de Godot) │ ├── api/ │ ├── get_messages.php # Long-polling: entrega mensajes nuevos │ ├── send_message.php # Jugador envía mensaje de chat │ ├── system_message.php # Servidor publica apuestas, premios… │ ├── clear_chat.php # Vacía el historial completo │ ├── delete_message.php # Elimina un mensaje por ID │ ├── ban_player.php # Silencia a un jugador │ └── unban_player.php # Quita el silencio │ └── web/ ├── index.html # Chat público para jugadores ├── admin.html # Panel de administración ├── proxy.php # Proxy seguro (sesión PHP para admin) ├── css/style.css # Tema oscuro casino └── js/ ├── chat.js # Lógica del chat público └── admin.js # Lógica del panel admin

Requisitos

ComponenteVersión mínimaNotas
PHP7.4Se usa fn() arrow functions y str_contains()
MySQL / MariaDB5.7 / 10.2Se requiere soporte de tipo JSON
Extensión PDOCualquieraCon driver pdo_mysql habilitado
mod_rewritePara las reglas del .htaccess
HostGator compartido cumple todos estos requisitos. PHP suele estar en 7.4 o 8.x por defecto.

Pasos de instalación

  1. Crear base de datos en cPanel
    Ve a cPanel → MySQL® Databases. Crea una base de datos, un usuario y asígnalo con todos los permisos. Anota el nombre de la BD, usuario y contraseña.
  2. Importar el schema
    En phpMyAdmin, selecciona tu base de datos, ve a la pestaña Importar y sube schema.sql. Esto crea las tablas chat_messages y banned_players.
  3. Editar config.php
    Llena las credenciales de BD y genera las API keys con OpenSSL (ver sección Configuración).
  4. Subir archivos
    Sube la carpeta chat-api/ completa a public_html/ via FTP o el Administrador de archivos de cPanel.
    public_html/
    └── chat-api/   ← aquí
  5. Verificar permisos
    Asegúrate de que config.php tenga permisos 644 y que .htaccess esté presente en la raíz de chat-api/.
  6. Probar el endpoint
    Abre en el navegador: https://tudominio.com/chat-api/api/get_messages.php?wait=0
    Respuesta esperada: {"ok":true,"since_id":0,"messages":[]}

Configuración — config.php

// Base de datos
define('DB_HOST', 'localhost');
define('DB_NAME', 'cpanelusr_chat');
define('DB_USER', 'cpanelusr_chatuser');
define('DB_PASS', 'tu_password');

// Genera con: openssl rand -hex 32
define('SYSTEM_API_KEY', 'clave_servidor_juego');  // ← servidor Godot
define('ADMIN_API_KEY',  'clave_moderacion');     // ← endpoints REST de moderación

// Contraseña del panel web admin
define('ADMIN_WEB_PASSWORD', 'contrasena_panel_web');

// Long-polling
define('LONG_POLL_TIMEOUT',  25);    // segundos máximo de espera
define('LONG_POLL_INTERVAL', 0.75); // segundos entre consultas a BD

Generar API keys seguras

# En terminal (Linux/Mac/WSL):
openssl rand -hex 32

# Salida de ejemplo:
a3f7c2e891b04d56f2a8e3c7b1d94f20e5a6b8c3d7e2f1a4b9c6d8e3f2a1b5c4
Nunca incluyas SYSTEM_API_KEY ni ADMIN_API_KEY en el cliente exportado de Godot. El cliente Godot solo usa los endpoints públicos. Tampoco subas config.php a repositorios públicos (GitHub, GitLab, etc.).

Esquema SQL

Importar schema.sql en phpMyAdmin. Crea dos tablas:

CREATE TABLE chat_messages (
    id         INT UNSIGNED    NOT NULL AUTO_INCREMENT,
    type       VARCHAR(20)     NOT NULL,
    sender     VARCHAR(100)    NOT NULL,
    text       TEXT            NOT NULL,
    data       JSON                NULL,
    created_at DOUBLE          NOT NULL,  -- microtime(true)
    PRIMARY KEY (id)
);

CREATE TABLE banned_players (
    id           INT UNSIGNED  NOT NULL AUTO_INCREMENT,
    player_id    VARCHAR(100)  NOT NULL,
    reason       VARCHAR(255)      NULL,
    banned_at    DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP,
    banned_until DATETIME          NULL,  -- NULL = permanente
    PRIMARY KEY (id),
    UNIQUE KEY (player_id)
);

Tablas

chat_messages

CampoTipoDescripción
idINT UNSIGNED PKAutoincremental
typeVARCHAR(20)CHAT, SYSTEM, BET, PRIZE_TOTAL, PRIZE_TOP, TIP, ERROR
senderVARCHAR(100)ID o nombre del remitente
textTEXTContenido visible del mensaje
dataJSON NULLDatos estructurados (monto, selección, etc.)
created_atDOUBLETimestamp Unix con microsegundos (microtime(true))

banned_players

CampoTipoDescripción
idINT UNSIGNED PKAutoincremental
player_idVARCHAR(100) UNIQUEID del jugador silenciado
reasonVARCHAR(255) NULLMotivo (opcional)
banned_atDATETIMEFecha del silenciamiento
banned_untilDATETIME NULLFin del baneo. NULL = permanente

Cómo funciona el long-polling

HostGator no soporta WebSockets en hosting compartido. Esta API los simula manteniendo la conexión HTTP abierta hasta 25 segundos:

Cliente Godot / Browser Servidor PHP │ │ │─── GET /get_messages?since_id=0 ──►│ │ │ espera hasta 25s │ │ revisando BD cada 0.75s │ │ ← llega mensaje nuevo │◄──────── { messages: [...] } ──────│ │ │ │─── GET /get_messages?since_id=5 ──►│ reconectar inmediatamente │ │ ...

El cliente debe reconectarse inmediatamente al recibir cada respuesta, usando el id más alto recibido como nuevo since_id.

Short-polling (alternativa ligera)

Para reducir carga del servidor con muchos jugadores, usa ?wait=0 y llama cada 2-3 segundos:

GET /api/get_messages.php?since_id=42&wait=0
Cada conexión de long-polling ocupa un proceso PHP durante hasta 25s. Con +50 jugadores simultáneos, baja LONG_POLL_TIMEOUT a 10 en config.php o migra a short-polling.

Endpoints públicos Sin auth

GET api/get_messages.php

Long-polling. Devuelve mensajes con id > since_id. Espera hasta timeout segundos si no hay mensajes nuevos.

ParámetroTipoDefaultDescripción
since_idint0ID del último mensaje recibido
wait0 / 110 = responder inmediatamente (short-polling)
timeoutint25Segundos máximo de espera (máx. 25)
// Respuesta
{
  "ok": true,
  "since_id": 0,
  "messages": [
    {
      "id": 1,
      "type": "CHAT",
      "sender": "Juan",
      "text": "¡Buena suerte!",
      "data": {},
      "created_at": 1718200000.123
    }
  ]
}
POST api/send_message.php

Publica un mensaje de chat del jugador. Verifica si el jugador está baneado antes de insertar.

// Body JSON
{ "player_id": "maria_92", "text": "¡Hola a todos!" }
CampoRequeridoDescripción
player_idRequeridoID o nickname del jugador
textRequeridoTexto del mensaje (máx. 300 caracteres)

Endpoints del sistema SYSTEM_API_KEY

Todos usan POST a api/system_message.php con el campo action. Requieren el header X-API-Key: SYSTEM_API_KEY.

actionCampos adicionalesEquivalente Godot
systemtextadd_system_message()
betplayer_id, selection, cashadd_bet_message()
tipplayer_nickname, tipadd_tip_message()
prize_totaltotal_prizesadd_prize_total()
prize_topplayer_name, prizeadd_top_prize()
errortextadd_error_message()

Ejemplos

// Apuesta
{ "action": "bet", "player_id": "maria_92", "selection": "Rojo", "cash": "100.00" }

// Premio destacado
{ "action": "prize_top", "player_name": "Carlos", "prize": "500.00" }

// Total de premios de la ronda
{ "action": "prize_total", "total_prizes": "1234.50" }

// Propina
{ "action": "tip", "player_nickname": "Pedro", "tip": "25.00" }

Endpoints de moderación ADMIN_API_KEY

MétodoEndpointBodyDescripción
POSTapi/clear_chat.php{}Vacía todo el historial (TRUNCATE)
POSTapi/delete_message.php{"id": 42}Elimina un mensaje por ID
POSTapi/ban_player.php{"player_id":"...","reason":"...","minutes":60}Silencia un jugador (omite minutes para permanente)
POSTapi/unban_player.php{"player_id":"..."}Quita el silencio

Autenticación

La API key puede enviarse por cualquiera de estas tres vías (en orden de prioridad):

MétodoEjemploRecomendación
Header HTTPX-API-Key: tu_claveRecomendado
Query string?api_key=tu_claveQueda en logs del servidor
Campo JSON"api_key": "tu_clave"Aceptable para POST
Usar la API key en query string (?api_key=) hace que aparezca en los logs de acceso del servidor web. Usa el header X-API-Key siempre que sea posible.

Formato de respuestas

Todas las respuestas son JSON con la forma {"ok": true, ...} o {"ok": false, "error": "..."}.

// Éxito
{ "ok": true, "message": { ... } }

// Error
{ "ok": false, "error": "Descripción del error." }
HTTP StatusSignificado
200OK — operación exitosa
400Bad Request — parámetros inválidos o faltantes
401Unauthorized — API key incorrecta o ausente
403Forbidden — jugador baneado
404Not Found — mensaje o jugador no encontrado
405Method Not Allowed — método HTTP incorrecto

Chat público web

Acceso: https://tudominio.com/chat-api/web/

Para simular varios usuarios en local: abre web/index.html en varias pestañas del navegador con diferentes nicknames. Cada uno se conecta de forma independiente.

Panel de administración web

Acceso: https://tudominio.com/chat-api/web/admin.html

Contraseña: ADMIN_WEB_PASSWORD definida en config.php

PestañaFuncionalidades
Live Feed Mensajes en tiempo real con colores por tipo · Botón eliminar en cada mensaje · Formulario para enviar mensajes del sistema (apuesta, premio, propina, error…) · Formulario para banear jugadores desde el sidebar
Historial Tabla paginada de todos los mensajes · Filtro por tipo y búsqueda de texto · Eliminar mensajes individuales · Contador total
Baneados Lista de jugadores baneados con motivo y duración · Desbanear con un click · Formulario para banear nuevos jugadores
El panel admin usa web/proxy.php como intermediario: las API keys nunca llegan al navegador. El navegador solo recibe/envía la sesión PHP.

Integración con Godot 4

El cliente está en godot_client_example.gd. Añade dos nodos HTTPRequest como hijos: HttpPoll y HttpSend.

# Configuración mínima
const API_BASE  := "https://tudominio.com/chat-api/api"
const PLAYER_ID := "jugador_prueba"  # reemplazar con el nickname real

# 1. Iniciar el long-polling
func _ready() -> void:
    _http_poll.request_completed.connect(_on_poll_completed)
    _http_send.request_completed.connect(_on_send_completed)
    _start_polling()

# 2. Loop de long-polling
func _poll_once() -> void:
    var url := "%s/get_messages.php?since_id=%d&wait=1" % [API_BASE, _last_id]
    _http_poll.request(url)

func _on_poll_completed(..., body):
    var json := JSON.parse_string(body.get_string_from_utf8())
    for msg in json.get("messages", []):
        if msg["id"] > _last_id: _last_id = int(msg["id"])
        _apply_message(msg)
    _poll_once()  # reconectar inmediatamente

# 3. Enviar mensaje del jugador
func send_chat_message(text: String) -> void:
    var body    := JSON.stringify({ "player_id": PLAYER_ID, "text": text })
    var headers := PackedStringArray(["Content-Type: application/json"])
    _http_send.request(API_BASE + "/send_message.php", headers,
                       HTTPClient.METHOD_POST, body)

Publicar eventos del juego (SYSTEM_API_KEY)

# Apuesta registrada
var body := JSON.stringify({
    "action": "bet", "player_id": player_id,
    "selection": "Rojo", "cash": "100.00"
})
var headers := PackedStringArray([
    "Content-Type: application/json",
    "X-API-Key: " + SYSTEM_API_KEY
])
http.request(API_BASE + "/system_message.php", headers,
             HTTPClient.METHOD_POST, body)

Tipos de mensaje y colores

TipoColorRemitente por defecto
CHAT #FFFFFF player_id
SYSTEM #FFD966 Sistema
BET #D7FF77 player_id
PRIZE_TOTAL #FFED00 Premios
PRIZE_TOP #FFED00 Premios
TIP #00FF7F player_nickname
ERROR #EF4F85 Error

Pendientes de integración

Estos puntos deben completarse antes de pasar a producción con usuarios reales.
  1. require_player_token() en includes/auth.php
    Actualmente, send_message.php es público y acepta cualquier player_id sin verificación de identidad. Conectar con el sistema real de tokens de RouletteApi (JWT, sesiones PHP, etc.) para validar que el jugador es quien dice ser.
  2. GameChatMessage::money_format() en includes/GameChatMessage.php
    Ajustar el símbolo de moneda y el formato numérico para que coincida exactamente con CurrencyManager.money_format() del proyecto Godot.
  3. Remitente "Tú" en el cliente Godot
    El backend almacena el player_id real. El cliente debe mostrar "Tú" cuando msg.sender == PLAYER_ID (ya implementado en godot_client_example.gd).
  4. life_time correcto al recargar historial
    Al cargar mensajes previos via since_id=0, calcular el tiempo de vida restante como:
    life_time = max(0.0, 6.0 - (Time.get_unix_time_from_system() - msg.created_at))
    en lugar de reiniciar siempre a 6s (ya implementado en el cliente web y en el ejemplo GDScript).

Seguridad

MedidaImplementación
Timing attackshash_equals() para comparar API keys (previene ataques de temporización)
SQL InjectionPDO con sentencias preparadas en todas las consultas
Acceso a configuración.htaccess bloquea acceso directo a config.php e includes/
Directory listingOptions -Indexes en .htaccess
BBCode injectionEscape de [ y ] en el cliente Godot y en el chat web
XSS (chat web)escHtml() en todo el texto dinámico del chat y del admin
Panel adminSesión PHP con hash_equals(); las API keys nunca salen del servidor
Cookies de sesiónhttponly: true, samesite: Strict en proxy.php

Recomendaciones adicionales para producción