feat: agregar propuesta de migraciones futuras para el sistema RPG; incluir tablas para logging, efectos de estado avanzados y optimización de economía
This commit is contained in:
227
README/PROPUESTA_MIGRACIONES_RPG.md
Normal file
227
README/PROPUESTA_MIGRACIONES_RPG.md
Normal file
@@ -0,0 +1,227 @@
|
|||||||
|
# Propuesta de Migraciones Futuras del Sistema RPG
|
||||||
|
|
||||||
|
Estado base: el esquema actual cubre economía, minijuegos, efectos de estado, rachas, logros, quests y death log. Para la siguiente fase (robustecer balance, trazabilidad y escalabilidad en tiempo real) se plantean las siguientes migraciones y extensiones.
|
||||||
|
|
||||||
|
## 1. Logging y Telemetría de Equipo / Herramientas
|
||||||
|
|
||||||
|
### 1.1 Tabla ToolBreakLog
|
||||||
|
|
||||||
|
Motivación: hoy el log de ruptura de instancias de herramientas está sólo en memoria.
|
||||||
|
|
||||||
|
```prisma
|
||||||
|
model ToolBreakLog {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
userId String
|
||||||
|
guildId String
|
||||||
|
itemKey String // referencia lógica; no necesitamos FK dura a EconomyItem para evitar cascadas.
|
||||||
|
instance Int? // índice o identificador lógico de la instancia rota (si aplica)
|
||||||
|
maxDurability Int?
|
||||||
|
remainingDurability Int? // antes de romper (o 0)
|
||||||
|
totalInstancesRemaining Int?
|
||||||
|
reason String? // "durability_zero" | "manual_discard" | etc
|
||||||
|
metadata Json?
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
|
||||||
|
// Índices para consultas analíticas
|
||||||
|
@@index([userId, guildId])
|
||||||
|
@@index([itemKey])
|
||||||
|
@@index([createdAt])
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1.2 Tabla CombatEncounter (Normalizada)
|
||||||
|
|
||||||
|
Motivación: `MinigameRun.result` mezcla recompensas y combate en JSON opaco. Extraer una capa estructurada permite queries analíticas (DPS promedio, distribución de rondas, etc.).
|
||||||
|
|
||||||
|
```prisma
|
||||||
|
model CombatEncounter {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
runId String @unique // FK 1-1 con MinigameRun
|
||||||
|
userId String
|
||||||
|
guildId String
|
||||||
|
areaId String?
|
||||||
|
level Int?
|
||||||
|
victory Boolean
|
||||||
|
mobsDefeated Int @default(0)
|
||||||
|
totalDamageDealt Int @default(0)
|
||||||
|
totalDamageTaken Int @default(0)
|
||||||
|
autoDefeatNoWeapon Boolean @default(false)
|
||||||
|
deathGoldLost Int @default(0)
|
||||||
|
deathPercentApplied Float @default(0)
|
||||||
|
fatigueMagnitude Float? // replicado para analíticas rápidas
|
||||||
|
durationMs Int? // futuro: tiempo total (si medimos timestamps por ronda)
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
|
||||||
|
// Índices clave
|
||||||
|
@@index([userId, guildId])
|
||||||
|
@@index([areaId])
|
||||||
|
@@index([createdAt])
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1.3 Tabla CombatRound / CombatMobLog (detallado opcional)
|
||||||
|
|
||||||
|
Sólo si se necesita analítica profunda.
|
||||||
|
|
||||||
|
```prisma
|
||||||
|
model CombatMobLog {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
encounterId String
|
||||||
|
mobKey String
|
||||||
|
maxHp Int
|
||||||
|
defeated Boolean
|
||||||
|
totalDamageDealt Int @default(0)
|
||||||
|
totalDamageTakenFromMob Int @default(0)
|
||||||
|
rounds Json // [{ round, playerDamageDealt, playerDamageTaken, mobRemainingHp }]
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
|
||||||
|
@@index([encounterId])
|
||||||
|
@@index([mobKey])
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
(Alternativa: normalizar rounds en tabla `CombatRound` con FK a `CombatMobLog` si se requiere agregación muy granular.)
|
||||||
|
|
||||||
|
## 2. Efectos de Estado Avanzados
|
||||||
|
|
||||||
|
### 2.1 Cambios en PlayerStatusEffect
|
||||||
|
|
||||||
|
- Eliminar `@@unique([userId, guildId, type])` para permitir stacking OR introducir campo `stackGroup`.
|
||||||
|
- Añadir campos:
|
||||||
|
|
||||||
|
```prisma
|
||||||
|
source String? // itemKey, abilityId, areaEffect
|
||||||
|
stackingRule String? // "STACK", "REFRESH_DURATION", "IGNORE" (evaluado en servicio)
|
||||||
|
maxStacks Int? // para limitar acumulación
|
||||||
|
currentStack Int? // si se maneja colapsado
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.2 Tabla EffectApplicationLog (opcional)
|
||||||
|
|
||||||
|
Auditar quién/qué aplicó o purgó efectos.
|
||||||
|
|
||||||
|
```prisma
|
||||||
|
model EffectApplicationLog {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
userId String
|
||||||
|
guildId String
|
||||||
|
type String
|
||||||
|
action String // apply|refresh|expire|purge
|
||||||
|
source String? // itemKey|system|deathPenalty
|
||||||
|
magnitude Float?
|
||||||
|
durationMinutes Int?
|
||||||
|
metadata Json?
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
|
||||||
|
@@index([userId, guildId])
|
||||||
|
@@index([type])
|
||||||
|
@@index([createdAt])
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3. Economía y Balance
|
||||||
|
|
||||||
|
### 3.1 Soft References para Drops/Mobs
|
||||||
|
|
||||||
|
Actualmente `Mob.drops` es JSON. Futuro: tabla `MobDrop` con pesos para consultar/ajustar sin actualizar JSON masivo.
|
||||||
|
|
||||||
|
```prisma
|
||||||
|
model MobDrop {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
mobId String
|
||||||
|
itemKey String
|
||||||
|
weight Int @default(1)
|
||||||
|
minQty Int @default(1)
|
||||||
|
maxQty Int @default(1)
|
||||||
|
metadata Json?
|
||||||
|
|
||||||
|
mob Mob @relation(fields: [mobId], references: [id])
|
||||||
|
@@index([mobId])
|
||||||
|
@@index([itemKey])
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.2 Tabla AreaMob (override de tabla de mobs a nivel área+nivel)
|
||||||
|
|
||||||
|
Si se requiere override dinámico sin editar JSON en `GameAreaLevel.mobs`.
|
||||||
|
|
||||||
|
```prisma
|
||||||
|
model AreaMob {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
areaId String
|
||||||
|
level Int
|
||||||
|
mobKey String
|
||||||
|
weight Int @default(1)
|
||||||
|
metadata Json?
|
||||||
|
|
||||||
|
area GameArea @relation(fields: [areaId], references: [id])
|
||||||
|
@@index([areaId, level])
|
||||||
|
@@index([mobKey])
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 4. Escalado y Eventos en Tiempo Real (Appwrite / Realtime)
|
||||||
|
|
||||||
|
### 4.1 Propuesta Appwrite Functions
|
||||||
|
|
||||||
|
- Scheduler para procesar `ScheduledMobAttack` (trigger cada minuto). Migrar lógica actual Node a función aislada.
|
||||||
|
- Function para purgar efectos expirados (scan `PlayerStatusEffect.expiresAt < now()`), evitando hacerlo on-demand.
|
||||||
|
|
||||||
|
### 4.2 Realtime Streams
|
||||||
|
|
||||||
|
- Canal: `deathlog.{guildId}` -> push entradas nuevas de `DeathLog`.
|
||||||
|
- Canal: `effects.{userId}.{guildId}` -> cambios en efectos (aplicación / expiración / purga).
|
||||||
|
Requiere wrapper que escuche events (webhook o poll) y publique a Appwrite Realtime.
|
||||||
|
|
||||||
|
## 5. Índices de Performance Recomendados
|
||||||
|
|
||||||
|
| Área | Tabla | Índice sugerido | Motivo |
|
||||||
|
| ---- | ----- | --------------- | ------ |
|
||||||
|
| Combate | DeathLog | (guildId, createdAt DESC) | Listados recientes por servidor |
|
||||||
|
| Economía | InventoryEntry | (guildId, userId) ya existe, añadir (itemId) | Búsquedas de stock masivas |
|
||||||
|
| Efectos | PlayerStatusEffect | (expiresAt) ya existe, añadir (guildId, expiresAt) | Sweep eficiente por servidor |
|
||||||
|
| Runs | MinigameRun | (guildId, areaId, startedAt DESC) | Historial filtrado por área |
|
||||||
|
|
||||||
|
## 6. Estrategia Evolutiva de Migraciones
|
||||||
|
|
||||||
|
Orden sugerido (minimiza locking y pasos de refactor):
|
||||||
|
|
||||||
|
1. ToolBreakLog (aditivo, sin tocar lógica actual) => empezar a registrar.
|
||||||
|
2. CombatEncounter + (opcional) CombatMobLog: poblar en paralelo mientras se mantiene JSON original (doble escritura). Periodo sombra 1-2 semanas.
|
||||||
|
3. PlayerStatusEffect flexibilizar stacking (quitar unique) + campos extra. Hacer migración en ventana corta; script que colapsa duplicados previos.
|
||||||
|
4. MobDrop / AreaMob (sólo si se requiere UI de balance). Inicialmente poblar desde JSON existente via script.
|
||||||
|
5. EffectApplicationLog (si se necesita auditoría de buffs/debuffs).
|
||||||
|
6. Limpieza: remover campos redundantes del JSON `MinigameRun.result.combat` cuando dashboards confirmen que CombatEncounter cubre todas las consultas.
|
||||||
|
|
||||||
|
## 7. Retro-llenado (Backfill) y Scripts
|
||||||
|
|
||||||
|
- Backfill CombatEncounter: recorrer últimas N (ej. 30k) filas de `MinigameRun` y extraer métricas simples.
|
||||||
|
- Backfill ToolBreakLog: NO retroactivo (aceptable) — iniciar desde migración.
|
||||||
|
- Backfill MobDrop / AreaMob: script que lee `GameAreaLevel.mobs` y crea registros.
|
||||||
|
|
||||||
|
## 8. Riesgos y Mitigaciones
|
||||||
|
|
||||||
|
| Riesgo | Impacto | Mitigación |
|
||||||
|
| ------ | ------- | ---------- |
|
||||||
|
| Doble escritura inconsistente (MinigameRun vs CombatEncounter) | Datos divergentes | Wrap en transacción; test de integridad periódico |
|
||||||
|
| Explosión filas CombatMobLog | Coste almacenamiento | Gate: habilitar sólo en áreas high-value; TTL archivado |
|
||||||
|
| Contención en barridos de expiración efectos | Latencia | Índice compuesto (guildId, expiresAt) y paginación |
|
||||||
|
| Cambios stacking efectos rompen lógica actual | Buffs perdidos o duplicados | Feature flag: mantener unique hasta terminar servicio stacking |
|
||||||
|
|
||||||
|
## 9. Campos / Cambios Pequeños Inmediatos (Fast Wins)
|
||||||
|
|
||||||
|
- Añadir `PlayerStatusEffect.source` para trazabilidad rápida (sin romper unique aún).
|
||||||
|
- Añadir índice `DeathLog (guildId, createdAt)` compuesto (sólo `createdAt` existe). Actualmente ya hay `@@index([createdAt])` y `@@index([userId, guildId])`; considerar `@@index([guildId, createdAt])` para consultas por servidor.
|
||||||
|
|
||||||
|
## 10. Roadmap Resumido
|
||||||
|
|
||||||
|
T0 (ahora): Aprobar diseño.
|
||||||
|
T1 (24-48h): ToolBreakLog + índice DeathLog compuesto.
|
||||||
|
T2 (Semana 1): CombatEncounter sombra + script backfill parcial.
|
||||||
|
T3 (Semana 2): Stacking efectos (remover unique + nuevos campos) bajo flag.
|
||||||
|
T4 (Semana 3): MobDrop / AreaMob si se confirma necesidad de balance fino.
|
||||||
|
T5 (Semana 4): Limpieza JSON y dashboards.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Cualquier punto se puede profundizar; este documento sirve como guía de implementación incremental evitando big-bang.
|
||||||
Reference in New Issue
Block a user