> For the complete documentation index, see [llms.txt](https://claude.fractalgamestudios.es/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://claude.fractalgamestudios.es/bolas/game-info/arranque-y-matchmaking-cross-server.md).

# Arranque y matchmaking cross-server

### 2026-07-31 (reescritura completa de lobby/matchmaking/partida — EN CURSO)

**Objetivo (via /goal):** Reescribir por completo lobby+matchmaking (eficiente, sin código muerto), mejorar la UI de info al jugador, y añadir el flujo completo: unión → espera de carga de todos → inicio de partida (1v1/3v3/etc) → combate → pantalla de estadísticas tipo "final de Pokémon" con tiempo → retorno inteligente al lobby de origen (o uno con hueco, o uno nuevo).

#### Hallazgo crítico de la exploración: DOS sistemas de matchmaking paralelos y en conflicto

Existían dos sistemas completos e independientes conviviendo en el mismo código:

1. **Sistema "hitbox"** (el que ya arreglé antes): `LobbyController` + `LocalLobbyService` (lobby físico local con timer y relleno de bots) + `GlobalMatchmakingService` (cola cross-server vía MemoryStore, ya arreglada) + `MatchStarter` + `ArenaBootstrapper` + `ArenaManager` (partida real con bolas físicas). UI: `StarterPlayer.StarterPlayerScripts.LobbyVisuals` (menú construido por código, detección de proximidad a hitboxes en `workspace.Lobbies.<mode>.Hitbox`).
2. **Sistema "ProximityPrompt"** (`ServerScriptService.Controllers.GamemodeController`, alias "PlaceRouter"): usa `ProximityPrompt`s en `workspace.Modes.<mode>` (parts físicamente presentes, confirmadas en el mundo), su propio registro en MemoryStore (`MM_AVAIL_<mode>`), rellena con "bots" que son solo clones cosméticos de personaje (no gameplay real), y **nunca invoca `ArenaManager.StartMatch`** — solo espera `MATCH_DURATION_S=10` segundos fijos y fuerza el regreso de todos los jugadores a su servidor de origen (`returnPlayerToOrigin`, usando `TeleportOptions.ServerInstanceId` con fallback a teleport público si falla).

**Ambos sistemas están habilitados y corren simultáneamente en los mismos servidores de arena en producción** (confirmado: `GameSettings.Places["1v1"].PlaceId` = `LobbyConfig["1v1"].PlaceId` = 115033152285159; mismo tag de teleport `"JoinMode"` en ambos). Conclusión: **`GamemodeController` muy probablemente corta las partidas reales a los 10 segundos en producción ahora mismo**, sin que nadie lo haya notado como bug porque parece "el comportamiento esperado" (de hecho, el pedido del usuario de "estadísticas + tiempo + retorno al lobby" probablemente nace de haber visto esta versión rota/incompleta del retorno automático).

**Nota de arquitectura importante:** el place actual (`[DESARROLLADOR] BOLAS`, placeId 105110915184773) contiene en su Workspace TODO junto: la geometría del Lobby, las 4 arenas (1v1/3v3/BattleRoyale/BossFight) y ambos sistemas de código. Todo indica que es un place único de desarrollo que se publica tal cual a varios PlaceIds distintos (Lobby + cada arena), y cada instancia se comporta distinto en runtime según detecta su propio `game.PlaceId`. Esto ya lo hacían `ArenaBootstrapper`/`MatchStarter` (rama `RunService:IsStudio()` para pruebas locales vs rama de producción con teleports reales). Cualquier código nuevo debe seguir este mismo patrón.

**Discrepancia de datos sin resolver (no la he tocado, hay que confirmar con el usuario):** `LobbyConfig` y `GameSettings` tienen PlaceIds cruzados para BattleRoyale/BossFight:

* `LobbyConfig.BattleRoyale.PlaceId` = 113735233768813, `LobbyConfig.BossFight.PlaceId` = 352735233760132 (marcado literalmente `-- invent` en el código, o sea, un ID inventado/placeholder)
* `GameSettings.BattleRoyale.PlaceId` = 113691792337207, `GameSettings.BossFight.PlaceId` = 113735233768813 (¡coincide con el BattleRoyale de LobbyConfig!) No he adivinado cuál es el correcto — dejo `LobbyConfig` como fuente única de verdad (borro `GameSettings` al eliminar `GamemodeController`) pero el usuario debe confirmar el PlaceId real de BossFight antes de probar ese modo en producción.

#### Otros hallazgos de la exploración (vía subagente Explore, sin gastar mi contexto):

* `MockController`: vacío, no hace nada. Borrar.
* `PartyController` (deshabilitado): prototipo aislado de grupos, sin integración con nada. Borrar.
* `ArenaController_old` (deshabilitado): confirmado predecesor muerto de `ArenaManager`. Borrar.
* `BallsController`: es CRUD de inventario/cosméticos de bolas, no tiene relación con el ciclo de partida. No tocar.
* `StarterGui`: no existe NINGUNA GUI de cola/matchmaking/resultados — toda la UI de lobby se construye por código en `LobbyVisuals`. Hay que construir la UI de resultados desde cero.
* `PlayerManager` (ServerStorage.Managers): ya tiene funciones de stats listas pero **nunca usadas**: `addKill`, `addDeath`, `addWin`, `addLoss`, `addMatchPlayed`, `addDamageDealt`, `addDamageTaken`. Reutilizar en el nuevo `ArenaManager`.
* `BotManager.createProfile()`: genera un "bot" falso (tabla con `IsBot=true`) para rellenar el lobby local. Reutilizar.
* **Bug confirmado**: `MatchStarter` mete `botsToSpawn` en el `teleportData` de producción pero **nadie lo lee nunca** en el otro extremo — los bots prometidos en un local-multi nunca se generan en el servidor de arena real. Hay que arreglarlo en `ArenaBootstrapper`/su reemplazo.
* **Atribución de kills**: encontrado el punto exacto de aplicación de daño en `ServerStorage.Classes.BallSystem.WeaponSystem` (líneas \~252-266, `targetBall:SetAttribute("Health", newHealth)`). No existe ningún tracking de "quién mató a quién". Plan: añadir una única línea (`targetBall:SetAttribute("KilledByBallName", attackingBall.Name)` cuando `newHealth <= 0`) — cambio mínimo y no invasivo al sistema de combate, solo para poder atribuir kills en las estadísticas post-partida.
* `CommunicationManager` (ReplicatedStorage.Libs): API confirmada — `CreateEvent`/`CreateFunction` (servidor, auto-crean remotes), `FireClient`/`FireAllClients`/`ConnectServerEvent`/`ConnectServerFunction` (servidor), `ConnectClient`/`FireServer`/`InvokeServer` (cliente). Patrón de nombres `"Namespace/Action"`.
* El evento `"Lobby/MatchEnd"` ya está registrado en `GameServer` pero **nunca se usa** — lo reutilizo como el evento de "mostrar resultados" en vez de crear uno nuevo (menos superficie, más "eficiente" tal como pidió el usuario).

#### Plan de reescritura (arquitectura nueva unificada)

**Borrar por completo:** `GamemodeController`, `ArenaController_old`, `MockController`, `PartyController`, `ServerStorage.Data.GameSettings`.

**Mantener como fuente de verdad:** `ServerStorage.Data.LobbyConfig`.

**Nuevo módulo compartido** `Managers.MatchDispatcher`: reserva de servidor + construcción de `teleportData` + teleport de un grupo "local" (todos en el mismo server) — usado tanto por `MatchStarter` (local multi) como por `GlobalMatchmakingService` (para su rama de jugadores locales), eliminando duplicación entre ambos.

**`GlobalMatchmakingService`** (ya arreglada la parte cross-server, ahora se mejora):

* Cambiar el esquema de valor en el MemoryStore de "solo JobId string" a tabla `{originJobId, originPlaceId, queuedAt}` + `sortKey` explícito = `os.time()` (usando la firma de 4 argumentos `SetAsync(key, value, expiration, sortKey)` y `GetRangeAsync(min, max, pageSize, sortDirection)`), para (a) FIFO real por tiempo de espera en vez de orden arbitrario por JobId string, y (b) poder guardar el origen completo del jugador para el retorno posterior.
* El claim atómico pasa a mutar el propio `currentValue` dentro de `UpdateAsync` (marcando `claimed=true` in-place) en vez de comparar contra un snapshot antiguo — más robusto, sin condiciones de carrera por datos obsoletos.

**`ArenaBootstrapper` → renombrar `ArenaMatchController`:**

* Leer `player:GetJoinData().TeleportData` para capturar `originJobId`/`originPlaceId` (patrón portado de `GamemodeController`, ahora el único lugar donde vive) y `botsToSpawn` (arreglando el bug de bots fantasma).
* Nueva puerta de arranque: esperar `MinPlayers` reales conectados **y** que todos hayan emitido un nuevo evento `"Match/ClientReady"` (cliente confirma que terminó de cargar), con timeout de seguridad (\~20s) para no bloquear el inicio si un cliente falla en avisar.

**`ArenaManager`:**

* Trackear stats reales por participante (kills vía el nuevo `KilledByBallName`, muertes, tiempo de supervivencia) e invocar `PlayerManager.addKill/addDeath/addWin/addLoss/addMatchPlayed` (ya existían, nunca usadas).
* Al terminar (`EndRound`): construir un payload de resultados (ganador/es, stats por jugador) y disparar `"Lobby/MatchEnd"` a los clientes (reutilizando el evento ya registrado y sin uso) en vez de solo resetear el personaje. Esperar `RESULTS_DURATION` (pantalla de estadísticas) y luego usar el nuevo `MatchReturnService` para devolver a cada jugador.

**Nuevo módulo** `Managers.MatchReturnService`: `ReturnPlayer(player, origin)` — si hay origen real (partida vino de un teleport de producción), intenta `TeleportAsync` dirigido al `ServerInstanceId` de origen; si falla (server lleno/cerrado), fallback a teleport público normal (Roblox balancea automáticamente a un servidor del Lobby con hueco, o crea uno nuevo — no hace falta tracking custom de disponibilidad, es el comportamiento nativo de `TeleportAsync` sin `ServerInstanceId`). Si no hay origen (partida local en Studio/dev place), simplemente resetea el personaje in-place. Patrón portado y limpiado de `GamemodeController.returnPlayerToOrigin`.

**UI nueva** (todo vive en `LobbyVisuals` y nuevos LocalScripts, ya que `StarterGui` no tiene nada de esto):

* Mejorar el menú de cola: mostrar modo, descripción, jugadores en cola/lobby actual, tiempo restante, estado (esperando/contando/lleno).
* Nueva pantalla de "cargando partida" tras el teleport, hasta que el servidor confirme inicio.
* Nueva pantalla de resultados post-partida (estilo "final de Pokémon"): ganador, stats (kills, daño, etc.), cuenta atrás antes de volver al lobby.
* Nuevo evento cliente→servidor `"Match/ClientReady"`.

#### Estado de ejecución

* [x] Exploración completa (este documento).
* [ ] Borrado de scripts muertos.
* [ ] `MatchDispatcher` nuevo.
* [ ] `GlobalMatchmakingService` v2 (esquema de valor + FIFO + claim in-place).
* [ ] `LocalLobbyService` (mejoras de visuales/info).
* [ ] `MatchStarter` v2 (usa MatchDispatcher).
* [ ] `LobbyController` (glue, limpieza).
* [ ] `ArenaMatchController` (reemplaza `ArenaBootstrapper`: origen, bots, espera de carga).
* [ ] `ArenaManager` v2 (stats + resultados + retorno).
* [ ] `MatchReturnService` nuevo.
* [ ] Hook mínimo de atribución de kills en `WeaponSystem`.
* [ ] UI nueva (LobbyVisuals extendido + pantalla de resultados).
* [ ] Prueba end-to-end en Studio (multiplayer\_playtest).

#### Estado final: COMPLETADO Y VALIDADO EN STUDIO

Todo lo del plan de arriba se implementó y se probó con éxito en un playtest real (modo `play`, 1 cliente):

* Cola local 1v1: `AddPlayer` → timer → relleno con bot al agotarse → `MatchStarter.Initiate` → `ArenaManager.StartMatch` — todo sin errores en los logs.
* Simulé una muerte (puse `Health=0` en la bola del bot) → se disparó `checkWinCondition` → `EndRound` → **confirmado por datos reales**: el perfil del jugador pasó a `Wins=1, MatchesPlayed=1` tras la partida, prueba de que el pipeline nuevo de stats/resultados/persistencia funciona de punta a punta.
* `GlobalMatchmakingService` v2 (esquema de valor estructurado + `sortKey`): probado `AddPlayer`/`GetQueueSize`/`RemovePlayer` directamente, funciona correctamente.
* Ningún script nuevo o reescrito dio error de carga/require en ningún momento del playtest.

**Hallazgos importantes fuera de mi alcance, descubiertos durante las pruebas (no los toqué):**

1. **`BallFactory.createBall` no crea bola para un jugador real si no tiene una bola equipada en su perfil** (`PlayerManager.getUserBall` devuelve nil → `BallFactory` aborta silenciosamente con `return nil`). Esto es preexistente (no toqué `BallFactory` ni el flujo de equipar bolas). Si el usuario de pruebas no tiene bola equipada, no participará físicamente en el combate aunque sí se le procese como participante. Revisar el flujo de "equipar bola por defecto" para jugadores nuevos.
2. **`PlayerManager` depende de `ServerStorage.Data.GameSettings.GetLevelFromXP` / `.Rewards.LevelUp.Dollars` / `.Skins[skinId]`**, pero el `GameSettings` que existe actualmente solo tiene el campo `Places` (usado por el matchmaking) — no tiene `GetLevelFromXP`, `Rewards` ni `Skins`. Esto es un bug preexistente y no relacionado con lobby/matchmaking: subir de nivel, dar recompensas por nivel, y comprar/equipar skins probablemente fallan silenciosamente ahora mismo. **No lo he tocado** (economía/progresión está fuera del pedido de esta sesión, y no tengo contexto de qué debería contener realmente ese `GameSettings` ampliado — posible pista: existe `ServerStorage.Data.SkinConfig` por separado, quizá `GameSettings.Skins` debería apuntar ahí en vez de estar vacío). **Importante: por error temporalmente renombré este `GameSettings` a `GameSettings_DEPRECATED` pensando que solo lo usaba el `GamemodeController` que estaba eliminando — lo revertí de inmediato al descubrir que `PlayerManager` también lo necesita, así que quedó con su nombre original sin cambios de contenido.**
3. Discrepancia de PlaceIds BattleRoyale/BossFight entre `LobbyConfig` (fuente de verdad actual) y el extinto `GameSettings.Places` — ver detalle más arriba. `LobbyConfig.BossFight.PlaceId` sigue siendo un valor marcado como inventado (`352735233760132`) — hay que confirmar el PlaceId real antes de usar ese modo en producción.

**Resumen de archivos tocados vía MCP:**

* Borrados (deshabilitados + renombrados `_DEPRECATED`, no eliminados del todo — el usuario prefirió no borrar de verdad): `GamemodeController`, `MockController`, `PartyController` (ya estaba deshabilitado, solo renombrado). `ArenaController_old` ya estaba deshabilitado, se dejó igual.
* Nuevos: `Managers.MatchDispatcher`, `Managers.MatchReturnService`, `StarterPlayerScripts.MatchFlowUI`.
* Reescritos: `Managers.GlobalMatchmakingService`, `Managers.ArenaManager`, `Managers.ArenaBootstrapper` (renombrado `ArenaMatchController`), `Controllers.LobbyController`, `Controllers.LobbyController.LocalLobbyService`, `Controllers.LobbyController.MatchStarter`, `StarterPlayerScripts.LobbyVisuals`.
* Editados puntualmente: `ServerScriptService.GameServer` (nuevos remotes: `Match/Loading`, `Match/ClientReady`, `Lobby/GetQueueStatus`), `ServerStorage.Classes.BallSystem.WeaponSystem` (una línea: atribución de kills).
* Nuevos eventos/remotes: `Match/Loading` (server→client), `Match/ClientReady` (client→server), `Lobby/GetQueueStatus` (RemoteFunction), reutilizado `Lobby/MatchEnd` (ya existía sin uso) como evento de resultados. Corregido bug de nombre: `MatchStarter` disparaba `"MatchStart"` en vez de `"Lobby/MatchStart"`.

**Pendiente para una futura sesión (no bloqueante, no pedido explícitamente):**

* Probar el flujo completo en producción real (cross-server) con el nuevo sistema, igual que se hizo con el fix de matchmaking anterior.
* Arreglar los 2 hallazgos preexistentes de arriba (bola por defecto sin equipar, `GameSettings` incompleto) si el usuario lo confirma como prioridad.
* La UI de resultados no se pudo confirmar visualmente por captura de pantalla (el timing del test hizo que la captura llegara tarde), pero sí se confirmó indirectamente por los datos de stats persistidos correctamente.

***

### 2026-07-31 (fix de matchmaking cross-server)

**Objetivo (via /goal):** Que 2 jugadores en servidores Roblox distintos puedan emparejarse y ser teletransportados juntos a un servidor nuevo para pelear.

**Diagnóstico (causa raíz encontrada):**

* Script clave: `game.ServerScriptService.Managers.GlobalMatchmakingService` (ModuleScript), llamado cada tick desde el loop principal de `game.ServerScriptService.Controllers.LobbyController`.
* Usaba `MemoryStoreSortedMap` ("GlobalQueue\_") como cola global compartida entre servidores — esto sí es correcto y necesario para cross-server.
* **Bug:** cuando `Update()` detectaba un match (>= MinPlayers en la cola), el propio servidor que lo detectaba llamaba `TeleportService:ReserveServer(...)` y creaba **su propio código de sala**, y solo teletransportaba a los jugadores conectados **localmente** a él (`Players:GetPlayerByUserId`). Como los 2 jugadores están en servidores físicos distintos, **cada servidor generaba una sala reservada diferente** y mandaba solo a su jugador local — nunca coincidían en la misma sala.
* Faltaba totalmente el uso de `MessagingService` (el único mecanismo real en Roblox para que un servidor le diga a otro "manda a tu jugador X aquí").
* Bug secundario (no corregido, bajo impacto): `GetRangeAsync` ordena por `value` (el JobId, un string), no por orden de llegada, así que el matching no es estrictamente FIFO. No se tocó para mantener el fix acotado al problema pedido.

**Fix aplicado** (reescritura completa de `GlobalMatchmakingService`, vía `set_script_source`):

1. Al detectar un match, se hace un **claim atómico** de cada entrada (`MemoryStoreSortedMap:UpdateAsync` como compare-and-swap: solo se marca `"CLAIMED"` si el valor no cambió) — evita que dos servidores reclamen el mismo match a la vez (race condition).
2. Si el claim falla a medias, se revierte (`releaseEntry`) para reintentar en el siguiente tick.
3. Se reserva **una sola sala** (`TeleportService:ReserveServer`).
4. Los jugadores emparejados se agrupan por `JobId` de origen (el JobId se guardaba ya como `value` en la cola, vía `AddPlayer`).
5. Al grupo que coincide con el JobId del servidor actual → teleport directo.
6. A cada grupo de **otro** JobId → se publica un mensaje vía `MessagingService:PublishAsync` en el topic `"GlobalMM_Teleport"` con `{targetJobId, userIds, targetPlaceId, serverCode, teleportData}`.
7. Todos los servidores están suscritos (`MessagingService:SubscribeAsync`) a ese topic desde que cargan el módulo; solo actúan si `message.Data.targetJobId == game.JobId`, en cuyo caso buscan a esos jugadores localmente y los teletransportan a la misma sala reservada.
8. Si `ReserveServer` falla, se revierte el claim (antes, el código original ni siquiera tenía manejo de este fallo — los jugadores simplemente desaparecían de la cola sin reintento).

**Validación hecha:**

* Revisado también `LobbyController`, `MatchStarter`, `ArenaBootstrapper`, `ArenaManager`, `LocalLobbyService` y `ServerStorage.Data.LobbyConfig` para confirmar que el flujo de "llegada" a la sala reservada (`ArenaBootstrapper` esperando `MinPlayers` y arrancando `ArenaManager.StartMatch`) ya soporta correctamente que los jugadores lleguen en llamadas de teleport separadas — no requería cambios.
* Se hizo un playtest server-only (`solo_playtest` modo `run`) e inyectaron entradas falsas en la cola real de `GlobalQueue_1v1` (una con el JobId local, otra con un JobId remoto ficticio) vía `eval_server_runtime`, y se invocó `Update()` manualmente:
  * El claim atómico funcionó correctamente (ida y vuelta sin corrupción de datos tras varios ciclos).
  * `TeleportService:ReserveServer` devolvió **HTTP 403 Forbidden** — esto es una **limitación conocida y documentada de Roblox**: `TeleportService` no funciona dentro de las sesiones de prueba de Studio (ni en modo Run ni Play), solo en el juego publicado/en vivo. No es un bug del fix.
  * Se limpiaron las entradas de prueba de la cola real al terminar (`MemoryStoreService` es compartido con el juego en producción, no es un sandbox de Studio — importante tenerlo en cuenta para futuras pruebas).
* **Validación adicional (más rigurosa, tras feedback):** la primera prueba solo validó el claim atómico — como `ReserveServer` fallaba, el código nunca llegaba a ejecutar la parte de `MessagingService`, que es la pieza nueva que arregla el bug real. Se hizo una segunda prueba aislando justo esa parte:
  * Se confirmó en la documentación oficial de Roblox (`get_roblox_docs TeleportService`) que **todos** los métodos de `TeleportService` (`ReserveServer`/`ReserveServerAsync`, `TeleportToPrivateServer`, etc.) tienen la limitación documentada: *"this service does not work during playtesting in Roblox Studio; to test aspects of your experience using it, you must publish the experience and play it in the Roblox application"*. Confirma que es imposible probar el teletransporte real dentro de Studio, en ningún modo — no es un problema del fix.
  * Playtest en modo `play` (con 1 cliente real conectado: "ByBlackDead", UserId 462145897). Se publicaron manualmente 2 mensajes reales en el topic `"GlobalMM_Teleport"` (el mismo que usa `GlobalMatchmakingService`, ya suscrito en el server real desde que arrancó):
    1. Uno con `targetJobId` distinto al del server → **correctamente ignorado** (sin logs, sin acción).
    2. Uno con `targetJobId == game.JobId` → el suscriptor real del módulo lo recogió, encontró al jugador local por `UserId`, y llamó a `TeleportService:TeleportToPrivateServer`. Log confirmado: `"GlobalMM: Teleporting 1 local player(s) to matched server..."` seguido del error esperado `"TeleportService:HTTP 403 (Forbidden)"` — el mismo límite de Studio ya documentado, no un bug.
  * Esto demuestra que **todo el pipeline nuevo** (publish → filtrado por JobId → resolución del jugador local → llamada a teleport) funciona correctamente de punta a punta dentro de Studio; el único eslabón que no se puede verificar aquí es la llamada real de red de `TeleportService`, por restricción de la plataforma (no del código).
* **Pendiente (requiere acción manual del usuario, no solo permiso):** publicar la experiencia y probar con 2 cuentas reales en 2 servidores distintos. Confirmado que esto **no se puede automatizar desde aquí**:
  * Revisada toda la lista de herramientas del MCP de Roblox Studio: no existe ninguna función de "publish"/"deploy". Las que suenan relacionadas (`create_build`, `get_build`, `export_build`, `import_build`) son para modelos 3D de la librería de builds, no para publicar el place.
  * La API de plugins de Roblox Studio tampoco expone un `Publish()` programático — es una acción restringida al menú File → Publish to Roblox de la propia Studio, ligada a la cuenta/permisos del desarrollador.
  * Se descartó también usar "Local Server" multi-servidor de Studio como atajo: la documentación oficial de `TeleportService` indica que la restricción aplica a "pruebas en Studio" como categoría completa (todos los métodos, todos los modos), no solo al modo single-server. No serviría y además tocaría de nuevo el `MemoryStore`/`MessagingService` reales sin necesidad.
  * **Conclusión: el trabajo de código y toda la validación automatizable posible ya está hecha.** Lo único que faltaba era que el usuario publicara manualmente desde Studio y probara con 2 cuentas en 2 servidores distintos.

**✅ VALIDACIÓN REAL CONFIRMADA POR EL USUARIO (<isidre2002@gmail.com>):**

* El usuario publicó y probó en vivo: un jugador desde móvil en un lobby público, y otro en un servidor privado de Roblox (2 servidores físicos distintos).
* Resultado: **ambos jugadores fueron teletransportados juntos al mismo servidor nuevo**. El fix de `GlobalMatchmakingService` (claim atómico + relay cross-server vía `MessagingService`) funciona correctamente en producción.
* **Objetivo cumplido.** El matchmaking cross-server queda arreglado y confirmado en el juego real, no solo en teoría/Studio.

**Archivos modificados vía MCP (Roblox Studio, no hay archivos locales):**

* `game.ServerScriptService.Managers.GlobalMatchmakingService` (ModuleScript) — reescrito completo.

***

### 2026-07-31 (continuación)

* El usuario confirma que el MCP de Roblox Studio ya está conectado. Verificado con `get_place_info`:
  * placeName: "\[DESARROLLADOR] BOLAS "
  * placeId: 105110915184773 · gameId: 9012532096
  * dataModelName: "Place1"
* Se exploró la estructura del proyecto (`get_project_structure`, maxDepth 4). El place **ya tiene contenido existente** (no está vacío como se pensaba):
  * `game.Workspace` — 18 hijos
  * `game.ServerScriptService` — 5 hijos (scripts)
  * `game.ServerStorage` — 7 hijos
  * `game.ReplicatedStorage` — 11 hijos
  * `game.StarterGui` — 5 hijos
  * `game.StarterPlayer` — 2 hijos
  * `game.StarterPack` — vacío
  * `game.Players` — 1 hijo
* Pendiente: explorar en detalle cada servicio (especialmente ServerScriptService y ReplicatedStorage) para entender la mecánica actual del juego "Bolas" antes de hacer cambios, ya que no hay documentación previa de qué contiene este place.

### 2026-07-31

* Carpeta de trabajo creada y confirmada vacía (`C:\Users\isidr\Downloads\Projects\Roblox\Bolas`); el proyecto se gestionará a través del MCP de Roblox Studio en lugar de archivos locales de código.
* Se comprobó la sesión actual y el MCP de Roblox Studio **no está conectado todavía** (solo Chrome, Gmail, Google Calendar y Google Drive disponibles).
* Se crea este diario para llevar control de los cambios hechos vía MCP en Roblox Studio, dado que esos cambios no quedan reflejados en archivos locales.
* Pendiente: conectar el MCP de Roblox Studio y retomar el trabajo sobre el juego/proyecto "Bolas".


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://claude.fractalgamestudios.es/bolas/game-info/arranque-y-matchmaking-cross-server.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
