run-dmcp 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -0
- package/README.md +79 -0
- package/dist/__tests__/engineVocabulary.test.d.ts +1 -0
- package/dist/__tests__/engineVocabulary.test.js +147 -0
- package/dist/db/__tests__/connection.test.d.ts +1 -0
- package/dist/db/__tests__/connection.test.js +72 -0
- package/dist/db/__tests__/testDb.d.ts +33 -0
- package/dist/db/__tests__/testDb.js +41 -0
- package/dist/db/connection.d.ts +22 -0
- package/dist/db/connection.js +107 -0
- package/dist/db/schema.d.ts +1 -0
- package/dist/db/schema.js +725 -0
- package/dist/events/emitter.d.ts +22 -0
- package/dist/events/emitter.js +71 -0
- package/dist/http/server.d.ts +3 -0
- package/dist/http/server.js +649 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +92 -0
- package/dist/register/abilities.d.ts +2 -0
- package/dist/register/abilities.js +165 -0
- package/dist/register/audio.d.ts +2 -0
- package/dist/register/audio.js +326 -0
- package/dist/register/batch.d.ts +2 -0
- package/dist/register/batch.js +343 -0
- package/dist/register/character.d.ts +2 -0
- package/dist/register/character.js +324 -0
- package/dist/register/combat.d.ts +2 -0
- package/dist/register/combat.js +207 -0
- package/dist/register/core.d.ts +2 -0
- package/dist/register/core.js +1040 -0
- package/dist/register/display.d.ts +2 -0
- package/dist/register/display.js +263 -0
- package/dist/register/factions.d.ts +2 -0
- package/dist/register/factions.js +186 -0
- package/dist/register/images.d.ts +2 -0
- package/dist/register/images.js +400 -0
- package/dist/register/inventory.d.ts +2 -0
- package/dist/register/inventory.js +115 -0
- package/dist/register/mcp-prompts.d.ts +2 -0
- package/dist/register/mcp-prompts.js +684 -0
- package/dist/register/mcp-resources.d.ts +2 -0
- package/dist/register/mcp-resources.js +335 -0
- package/dist/register/narrative.d.ts +2 -0
- package/dist/register/narrative.js +242 -0
- package/dist/register/notes.d.ts +2 -0
- package/dist/register/notes.js +170 -0
- package/dist/register/pause.d.ts +2 -0
- package/dist/register/pause.js +580 -0
- package/dist/register/quests.d.ts +2 -0
- package/dist/register/quests.js +118 -0
- package/dist/register/relationships.d.ts +2 -0
- package/dist/register/relationships.js +147 -0
- package/dist/register/resources.d.ts +2 -0
- package/dist/register/resources.js +277 -0
- package/dist/register/secrets.d.ts +2 -0
- package/dist/register/secrets.js +192 -0
- package/dist/register/status.d.ts +2 -0
- package/dist/register/status.js +130 -0
- package/dist/register/tables.d.ts +2 -0
- package/dist/register/tables.js +146 -0
- package/dist/register/tags.d.ts +2 -0
- package/dist/register/tags.js +114 -0
- package/dist/register/time.d.ts +2 -0
- package/dist/register/time.js +281 -0
- package/dist/register/world.d.ts +2 -0
- package/dist/register/world.js +127 -0
- package/dist/schemas/index.d.ts +921 -0
- package/dist/schemas/index.js +121 -0
- package/dist/test-setup.d.ts +1 -0
- package/dist/test-setup.js +13 -0
- package/dist/tools/__tests__/audio.test.d.ts +1 -0
- package/dist/tools/__tests__/audio.test.js +59 -0
- package/dist/tools/__tests__/conserved.test.d.ts +1 -0
- package/dist/tools/__tests__/conserved.test.js +488 -0
- package/dist/tools/__tests__/constraint.test.d.ts +1 -0
- package/dist/tools/__tests__/constraint.test.js +212 -0
- package/dist/tools/__tests__/expiry-consequences.test.d.ts +1 -0
- package/dist/tools/__tests__/expiry-consequences.test.js +110 -0
- package/dist/tools/__tests__/images.test.d.ts +1 -0
- package/dist/tools/__tests__/images.test.js +59 -0
- package/dist/tools/__tests__/relationship.test.d.ts +1 -0
- package/dist/tools/__tests__/relationship.test.js +132 -0
- package/dist/tools/__tests__/resource-constraints.test.d.ts +1 -0
- package/dist/tools/__tests__/resource-constraints.test.js +131 -0
- package/dist/tools/__tests__/resource.test.d.ts +1 -0
- package/dist/tools/__tests__/resource.test.js +190 -0
- package/dist/tools/__tests__/time.test.d.ts +1 -0
- package/dist/tools/__tests__/time.test.js +404 -0
- package/dist/tools/__tests__/timers.test.d.ts +1 -0
- package/dist/tools/__tests__/timers.test.js +426 -0
- package/dist/tools/__tests__/world.test.d.ts +1 -0
- package/dist/tools/__tests__/world.test.js +70 -0
- package/dist/tools/ability.d.ts +48 -0
- package/dist/tools/ability.js +238 -0
- package/dist/tools/audio.d.ts +24 -0
- package/dist/tools/audio.js +365 -0
- package/dist/tools/character.d.ts +70 -0
- package/dist/tools/character.js +309 -0
- package/dist/tools/combat.d.ts +13 -0
- package/dist/tools/combat.js +195 -0
- package/dist/tools/constraint.d.ts +132 -0
- package/dist/tools/constraint.js +269 -0
- package/dist/tools/dice.d.ts +23 -0
- package/dist/tools/dice.js +111 -0
- package/dist/tools/display.d.ts +120 -0
- package/dist/tools/display.js +528 -0
- package/dist/tools/faction.d.ts +61 -0
- package/dist/tools/faction.js +269 -0
- package/dist/tools/game.d.ts +96 -0
- package/dist/tools/game.js +526 -0
- package/dist/tools/image-prompt.d.ts +49 -0
- package/dist/tools/image-prompt.js +479 -0
- package/dist/tools/images.d.ts +47 -0
- package/dist/tools/images.js +449 -0
- package/dist/tools/inventory.d.ts +20 -0
- package/dist/tools/inventory.js +145 -0
- package/dist/tools/narrative.d.ts +58 -0
- package/dist/tools/narrative.js +237 -0
- package/dist/tools/notes.d.ts +41 -0
- package/dist/tools/notes.js +220 -0
- package/dist/tools/pause.d.ts +110 -0
- package/dist/tools/pause.js +1254 -0
- package/dist/tools/quest.d.ts +34 -0
- package/dist/tools/quest.js +164 -0
- package/dist/tools/relationship.d.ts +74 -0
- package/dist/tools/relationship.js +324 -0
- package/dist/tools/resource.d.ts +93 -0
- package/dist/tools/resource.js +374 -0
- package/dist/tools/rules.d.ts +4 -0
- package/dist/tools/rules.js +30 -0
- package/dist/tools/secrets.d.ts +49 -0
- package/dist/tools/secrets.js +195 -0
- package/dist/tools/status.d.ts +36 -0
- package/dist/tools/status.js +218 -0
- package/dist/tools/tables.d.ts +33 -0
- package/dist/tools/tables.js +209 -0
- package/dist/tools/tags.d.ts +52 -0
- package/dist/tools/tags.js +176 -0
- package/dist/tools/time.d.ts +33 -0
- package/dist/tools/time.js +276 -0
- package/dist/tools/timers.d.ts +41 -0
- package/dist/tools/timers.js +215 -0
- package/dist/tools/world.d.ts +78 -0
- package/dist/tools/world.js +331 -0
- package/dist/types/index.d.ts +969 -0
- package/dist/types/index.js +1 -0
- package/dist/utils/__tests__/json.test.d.ts +1 -0
- package/dist/utils/__tests__/json.test.js +55 -0
- package/dist/utils/__tests__/validation.test.d.ts +1 -0
- package/dist/utils/__tests__/validation.test.js +90 -0
- package/dist/utils/errors.d.ts +44 -0
- package/dist/utils/errors.js +121 -0
- package/dist/utils/json.d.ts +9 -0
- package/dist/utils/json.js +23 -0
- package/dist/utils/logger.d.ts +7 -0
- package/dist/utils/logger.js +50 -0
- package/dist/utils/output-schemas.d.ts +594 -0
- package/dist/utils/output-schemas.js +331 -0
- package/dist/utils/tool-annotations.d.ts +147 -0
- package/dist/utils/tool-annotations.js +98 -0
- package/dist/utils/validation.d.ts +34 -0
- package/dist/utils/validation.js +52 -0
- package/dist/utils/verbosity.d.ts +57 -0
- package/dist/utils/verbosity.js +67 -0
- package/dist/utils/webui.d.ts +20 -0
- package/dist/utils/webui.js +35 -0
- package/package.json +75 -0
|
@@ -0,0 +1,725 @@
|
|
|
1
|
+
import { getDatabase } from "./connection.js";
|
|
2
|
+
export function initializeSchema() {
|
|
3
|
+
const db = getDatabase();
|
|
4
|
+
// ============================================================================
|
|
5
|
+
// MIGRATION: Rename 'sessions' to 'games' and 'session_id' to 'game_id'
|
|
6
|
+
// This handles existing databases that use the old 'session' terminology
|
|
7
|
+
// ============================================================================
|
|
8
|
+
// Check if the old 'sessions' table exists and migrate to 'games'
|
|
9
|
+
const sessionsTableExists = db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='sessions'").get();
|
|
10
|
+
if (sessionsTableExists) {
|
|
11
|
+
// Rename the main sessions table to games
|
|
12
|
+
db.exec(`ALTER TABLE sessions RENAME TO games`);
|
|
13
|
+
// Rename session_id columns to game_id in all tables that have them
|
|
14
|
+
const tablesWithSessionId = [
|
|
15
|
+
'characters', 'locations', 'items', 'quests', 'narrative_events',
|
|
16
|
+
'combats', 'resources', 'game_time', 'scheduled_events', 'timers',
|
|
17
|
+
'random_tables', 'secrets', 'relationships', 'factions', 'abilities',
|
|
18
|
+
'status_effects', 'tags', 'notes', 'external_updates', 'pause_states',
|
|
19
|
+
'stored_images', 'stored_audio'
|
|
20
|
+
];
|
|
21
|
+
for (const table of tablesWithSessionId) {
|
|
22
|
+
try {
|
|
23
|
+
db.exec(`ALTER TABLE ${table} RENAME COLUMN session_id TO game_id`);
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
// Column doesn't exist or already renamed
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
// Rename session_themes table to game_themes if it exists
|
|
30
|
+
try {
|
|
31
|
+
// First rename the column, then rename the table
|
|
32
|
+
db.exec(`ALTER TABLE session_themes RENAME COLUMN session_id TO game_id`);
|
|
33
|
+
db.exec(`ALTER TABLE session_themes RENAME TO game_themes`);
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
// Table doesn't exist or already renamed
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
// Fix for edge case where game_themes table exists but has session_id column
|
|
40
|
+
// This can happen if the above RENAME COLUMN failed but RENAME TO succeeded
|
|
41
|
+
const gameThemesHasSessionId = db.prepare("SELECT name FROM pragma_table_info('game_themes') WHERE name = 'session_id'").get();
|
|
42
|
+
if (gameThemesHasSessionId) {
|
|
43
|
+
// Recreate the table with correct column name
|
|
44
|
+
db.exec(`
|
|
45
|
+
CREATE TABLE game_themes_fixed (
|
|
46
|
+
game_id TEXT PRIMARY KEY,
|
|
47
|
+
config TEXT NOT NULL DEFAULT '{}',
|
|
48
|
+
updated_at TEXT NOT NULL,
|
|
49
|
+
FOREIGN KEY (game_id) REFERENCES games(id) ON DELETE CASCADE
|
|
50
|
+
);
|
|
51
|
+
INSERT INTO game_themes_fixed (game_id, config, updated_at)
|
|
52
|
+
SELECT session_id, config, updated_at FROM game_themes;
|
|
53
|
+
DROP TABLE game_themes;
|
|
54
|
+
ALTER TABLE game_themes_fixed RENAME TO game_themes;
|
|
55
|
+
`);
|
|
56
|
+
}
|
|
57
|
+
// Games table (formerly sessions)
|
|
58
|
+
db.exec(`
|
|
59
|
+
CREATE TABLE IF NOT EXISTS games (
|
|
60
|
+
id TEXT PRIMARY KEY,
|
|
61
|
+
name TEXT NOT NULL,
|
|
62
|
+
setting TEXT NOT NULL,
|
|
63
|
+
style TEXT NOT NULL,
|
|
64
|
+
rules TEXT,
|
|
65
|
+
preferences TEXT,
|
|
66
|
+
current_location_id TEXT,
|
|
67
|
+
created_at TEXT NOT NULL,
|
|
68
|
+
updated_at TEXT NOT NULL
|
|
69
|
+
)
|
|
70
|
+
`);
|
|
71
|
+
// Add preferences column if it doesn't exist (migration)
|
|
72
|
+
try {
|
|
73
|
+
db.exec(`ALTER TABLE games ADD COLUMN preferences TEXT`);
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
// Column already exists
|
|
77
|
+
}
|
|
78
|
+
// Add title_image_id column if it doesn't exist (migration)
|
|
79
|
+
try {
|
|
80
|
+
db.exec(`ALTER TABLE games ADD COLUMN title_image_id TEXT`);
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
// Column already exists
|
|
84
|
+
}
|
|
85
|
+
// Add favicon_image_id column if it doesn't exist (migration)
|
|
86
|
+
try {
|
|
87
|
+
db.exec(`ALTER TABLE games ADD COLUMN favicon_image_id TEXT`);
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
// Column already exists
|
|
91
|
+
}
|
|
92
|
+
// Characters table
|
|
93
|
+
db.exec(`
|
|
94
|
+
CREATE TABLE IF NOT EXISTS characters (
|
|
95
|
+
id TEXT PRIMARY KEY,
|
|
96
|
+
game_id TEXT NOT NULL,
|
|
97
|
+
name TEXT NOT NULL,
|
|
98
|
+
is_player INTEGER NOT NULL DEFAULT 0,
|
|
99
|
+
attributes TEXT NOT NULL DEFAULT '{}',
|
|
100
|
+
skills TEXT NOT NULL DEFAULT '{}',
|
|
101
|
+
status TEXT NOT NULL DEFAULT '{}',
|
|
102
|
+
location_id TEXT,
|
|
103
|
+
notes TEXT DEFAULT '',
|
|
104
|
+
voice TEXT,
|
|
105
|
+
created_at TEXT NOT NULL,
|
|
106
|
+
FOREIGN KEY (game_id) REFERENCES games(id) ON DELETE CASCADE
|
|
107
|
+
)
|
|
108
|
+
`);
|
|
109
|
+
// Add voice column if it doesn't exist (migration for existing databases)
|
|
110
|
+
try {
|
|
111
|
+
db.exec(`ALTER TABLE characters ADD COLUMN voice TEXT`);
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
// Column already exists
|
|
115
|
+
}
|
|
116
|
+
// Add image_gen column to characters
|
|
117
|
+
try {
|
|
118
|
+
db.exec(`ALTER TABLE characters ADD COLUMN image_gen TEXT`);
|
|
119
|
+
}
|
|
120
|
+
catch {
|
|
121
|
+
// Column already exists
|
|
122
|
+
}
|
|
123
|
+
// Locations table
|
|
124
|
+
db.exec(`
|
|
125
|
+
CREATE TABLE IF NOT EXISTS locations (
|
|
126
|
+
id TEXT PRIMARY KEY,
|
|
127
|
+
game_id TEXT NOT NULL,
|
|
128
|
+
name TEXT NOT NULL,
|
|
129
|
+
description TEXT NOT NULL,
|
|
130
|
+
properties TEXT NOT NULL DEFAULT '{}',
|
|
131
|
+
image_gen TEXT,
|
|
132
|
+
FOREIGN KEY (game_id) REFERENCES games(id) ON DELETE CASCADE
|
|
133
|
+
)
|
|
134
|
+
`);
|
|
135
|
+
// Add image_gen column to locations
|
|
136
|
+
try {
|
|
137
|
+
db.exec(`ALTER TABLE locations ADD COLUMN image_gen TEXT`);
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
// Column already exists
|
|
141
|
+
}
|
|
142
|
+
// Items table
|
|
143
|
+
db.exec(`
|
|
144
|
+
CREATE TABLE IF NOT EXISTS items (
|
|
145
|
+
id TEXT PRIMARY KEY,
|
|
146
|
+
game_id TEXT NOT NULL,
|
|
147
|
+
owner_id TEXT NOT NULL,
|
|
148
|
+
owner_type TEXT NOT NULL CHECK (owner_type IN ('character', 'location')),
|
|
149
|
+
name TEXT NOT NULL,
|
|
150
|
+
properties TEXT NOT NULL DEFAULT '{}',
|
|
151
|
+
image_gen TEXT,
|
|
152
|
+
FOREIGN KEY (game_id) REFERENCES games(id) ON DELETE CASCADE
|
|
153
|
+
)
|
|
154
|
+
`);
|
|
155
|
+
// Add image_gen column to items
|
|
156
|
+
try {
|
|
157
|
+
db.exec(`ALTER TABLE items ADD COLUMN image_gen TEXT`);
|
|
158
|
+
}
|
|
159
|
+
catch {
|
|
160
|
+
// Column already exists
|
|
161
|
+
}
|
|
162
|
+
// Quests table
|
|
163
|
+
db.exec(`
|
|
164
|
+
CREATE TABLE IF NOT EXISTS quests (
|
|
165
|
+
id TEXT PRIMARY KEY,
|
|
166
|
+
game_id TEXT NOT NULL,
|
|
167
|
+
name TEXT NOT NULL,
|
|
168
|
+
description TEXT NOT NULL,
|
|
169
|
+
objectives TEXT NOT NULL DEFAULT '[]',
|
|
170
|
+
status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'completed', 'failed', 'abandoned')),
|
|
171
|
+
rewards TEXT,
|
|
172
|
+
FOREIGN KEY (game_id) REFERENCES games(id) ON DELETE CASCADE
|
|
173
|
+
)
|
|
174
|
+
`);
|
|
175
|
+
// Narrative events table
|
|
176
|
+
db.exec(`
|
|
177
|
+
CREATE TABLE IF NOT EXISTS narrative_events (
|
|
178
|
+
id TEXT PRIMARY KEY,
|
|
179
|
+
game_id TEXT NOT NULL,
|
|
180
|
+
event_type TEXT NOT NULL,
|
|
181
|
+
content TEXT NOT NULL,
|
|
182
|
+
metadata TEXT NOT NULL DEFAULT '{}',
|
|
183
|
+
timestamp TEXT NOT NULL,
|
|
184
|
+
FOREIGN KEY (game_id) REFERENCES games(id) ON DELETE CASCADE
|
|
185
|
+
)
|
|
186
|
+
`);
|
|
187
|
+
// Combat table
|
|
188
|
+
db.exec(`
|
|
189
|
+
CREATE TABLE IF NOT EXISTS combats (
|
|
190
|
+
id TEXT PRIMARY KEY,
|
|
191
|
+
game_id TEXT NOT NULL,
|
|
192
|
+
location_id TEXT NOT NULL,
|
|
193
|
+
participants TEXT NOT NULL DEFAULT '[]',
|
|
194
|
+
current_turn INTEGER NOT NULL DEFAULT 0,
|
|
195
|
+
round INTEGER NOT NULL DEFAULT 1,
|
|
196
|
+
status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'resolved')),
|
|
197
|
+
log TEXT NOT NULL DEFAULT '[]',
|
|
198
|
+
FOREIGN KEY (game_id) REFERENCES games(id) ON DELETE CASCADE
|
|
199
|
+
)
|
|
200
|
+
`);
|
|
201
|
+
// Resources table (for tracking currency, reputation, counters, etc.)
|
|
202
|
+
db.exec(`
|
|
203
|
+
CREATE TABLE IF NOT EXISTS resources (
|
|
204
|
+
id TEXT PRIMARY KEY,
|
|
205
|
+
game_id TEXT NOT NULL,
|
|
206
|
+
owner_id TEXT,
|
|
207
|
+
owner_type TEXT NOT NULL CHECK (owner_type IN ('game', 'character')),
|
|
208
|
+
name TEXT NOT NULL,
|
|
209
|
+
description TEXT,
|
|
210
|
+
category TEXT,
|
|
211
|
+
value REAL NOT NULL DEFAULT 0,
|
|
212
|
+
min_value REAL,
|
|
213
|
+
max_value REAL,
|
|
214
|
+
created_at TEXT NOT NULL,
|
|
215
|
+
FOREIGN KEY (game_id) REFERENCES games(id) ON DELETE CASCADE
|
|
216
|
+
)
|
|
217
|
+
`);
|
|
218
|
+
// Resource history table (tracks all changes)
|
|
219
|
+
db.exec(`
|
|
220
|
+
CREATE TABLE IF NOT EXISTS resource_history (
|
|
221
|
+
id TEXT PRIMARY KEY,
|
|
222
|
+
resource_id TEXT NOT NULL,
|
|
223
|
+
previous_value REAL NOT NULL,
|
|
224
|
+
new_value REAL NOT NULL,
|
|
225
|
+
delta REAL NOT NULL,
|
|
226
|
+
reason TEXT,
|
|
227
|
+
timestamp TEXT NOT NULL,
|
|
228
|
+
FOREIGN KEY (resource_id) REFERENCES resources(id) ON DELETE CASCADE
|
|
229
|
+
)
|
|
230
|
+
`);
|
|
231
|
+
// Resource constraints table -- optional, server-enforced invariants on
|
|
232
|
+
// resource values (see src/tools/constraint.ts). Opt-in: a resource with
|
|
233
|
+
// no row here (directly or via resource_constraint_members) behaves
|
|
234
|
+
// exactly as before this feature existed.
|
|
235
|
+
db.exec(`
|
|
236
|
+
CREATE TABLE IF NOT EXISTS resource_constraints (
|
|
237
|
+
id TEXT PRIMARY KEY,
|
|
238
|
+
game_id TEXT NOT NULL,
|
|
239
|
+
kind TEXT NOT NULL CHECK (kind IN ('bounded', 'monotonic', 'conserved')),
|
|
240
|
+
direction TEXT CHECK (direction IN ('increasing', 'decreasing')),
|
|
241
|
+
total REAL,
|
|
242
|
+
created_at TEXT NOT NULL,
|
|
243
|
+
FOREIGN KEY (game_id) REFERENCES games(id) ON DELETE CASCADE
|
|
244
|
+
)
|
|
245
|
+
`);
|
|
246
|
+
// Members of a resource constraint. 'bounded' and 'monotonic' constraints
|
|
247
|
+
// have exactly one member (the resource they govern); 'conserved'
|
|
248
|
+
// constraints have two or more (the set that must sum to a fixed total).
|
|
249
|
+
db.exec(`
|
|
250
|
+
CREATE TABLE IF NOT EXISTS resource_constraint_members (
|
|
251
|
+
constraint_id TEXT NOT NULL,
|
|
252
|
+
resource_id TEXT NOT NULL,
|
|
253
|
+
PRIMARY KEY (constraint_id, resource_id),
|
|
254
|
+
FOREIGN KEY (constraint_id) REFERENCES resource_constraints(id) ON DELETE CASCADE,
|
|
255
|
+
FOREIGN KEY (resource_id) REFERENCES resources(id) ON DELETE CASCADE
|
|
256
|
+
)
|
|
257
|
+
`);
|
|
258
|
+
// Game time table (one per game)
|
|
259
|
+
db.exec(`
|
|
260
|
+
CREATE TABLE IF NOT EXISTS game_time (
|
|
261
|
+
game_id TEXT PRIMARY KEY,
|
|
262
|
+
current_time TEXT NOT NULL,
|
|
263
|
+
calendar_config TEXT NOT NULL,
|
|
264
|
+
FOREIGN KEY (game_id) REFERENCES games(id) ON DELETE CASCADE
|
|
265
|
+
)
|
|
266
|
+
`);
|
|
267
|
+
// Scheduled events table
|
|
268
|
+
db.exec(`
|
|
269
|
+
CREATE TABLE IF NOT EXISTS scheduled_events (
|
|
270
|
+
id TEXT PRIMARY KEY,
|
|
271
|
+
game_id TEXT NOT NULL,
|
|
272
|
+
name TEXT NOT NULL,
|
|
273
|
+
description TEXT,
|
|
274
|
+
trigger_time TEXT NOT NULL,
|
|
275
|
+
recurring TEXT,
|
|
276
|
+
triggered INTEGER DEFAULT 0,
|
|
277
|
+
metadata TEXT DEFAULT '{}',
|
|
278
|
+
FOREIGN KEY (game_id) REFERENCES games(id) ON DELETE CASCADE
|
|
279
|
+
)
|
|
280
|
+
`);
|
|
281
|
+
// Add consequence column to scheduled_events (migration). Holds an
|
|
282
|
+
// optional JSON-encoded ExpiryConsequence ({resourceId, delta}) that
|
|
283
|
+
// advanceTime() applies itself, atomically with marking the event
|
|
284
|
+
// triggered, when its trigger time is crossed. Null/absent means "no
|
|
285
|
+
// consequence" -- behavior is unchanged from before this feature existed.
|
|
286
|
+
try {
|
|
287
|
+
db.exec(`ALTER TABLE scheduled_events ADD COLUMN consequence TEXT`);
|
|
288
|
+
}
|
|
289
|
+
catch {
|
|
290
|
+
// Column already exists
|
|
291
|
+
}
|
|
292
|
+
// Timers table (countdowns, stopwatches, clocks)
|
|
293
|
+
db.exec(`
|
|
294
|
+
CREATE TABLE IF NOT EXISTS timers (
|
|
295
|
+
id TEXT PRIMARY KEY,
|
|
296
|
+
game_id TEXT NOT NULL,
|
|
297
|
+
name TEXT NOT NULL,
|
|
298
|
+
description TEXT,
|
|
299
|
+
timer_type TEXT NOT NULL CHECK (timer_type IN ('countdown', 'stopwatch', 'clock')),
|
|
300
|
+
current_value INTEGER NOT NULL,
|
|
301
|
+
max_value INTEGER,
|
|
302
|
+
direction TEXT DEFAULT 'down' CHECK (direction IN ('up', 'down')),
|
|
303
|
+
trigger_at INTEGER,
|
|
304
|
+
triggered INTEGER DEFAULT 0,
|
|
305
|
+
unit TEXT DEFAULT 'tick',
|
|
306
|
+
visible_to_players INTEGER DEFAULT 1,
|
|
307
|
+
created_at TEXT NOT NULL,
|
|
308
|
+
FOREIGN KEY (game_id) REFERENCES games(id) ON DELETE CASCADE
|
|
309
|
+
)
|
|
310
|
+
`);
|
|
311
|
+
// Add consequence column to timers (migration). Same shape and semantics
|
|
312
|
+
// as scheduled_events.consequence, applied by tickTimer() the moment the
|
|
313
|
+
// timer crosses trigger_at.
|
|
314
|
+
try {
|
|
315
|
+
db.exec(`ALTER TABLE timers ADD COLUMN consequence TEXT`);
|
|
316
|
+
}
|
|
317
|
+
catch {
|
|
318
|
+
// Column already exists
|
|
319
|
+
}
|
|
320
|
+
// Random tables
|
|
321
|
+
db.exec(`
|
|
322
|
+
CREATE TABLE IF NOT EXISTS random_tables (
|
|
323
|
+
id TEXT PRIMARY KEY,
|
|
324
|
+
game_id TEXT NOT NULL,
|
|
325
|
+
name TEXT NOT NULL,
|
|
326
|
+
description TEXT,
|
|
327
|
+
category TEXT,
|
|
328
|
+
entries TEXT NOT NULL DEFAULT '[]',
|
|
329
|
+
roll_expression TEXT DEFAULT '1d100',
|
|
330
|
+
created_at TEXT NOT NULL,
|
|
331
|
+
FOREIGN KEY (game_id) REFERENCES games(id) ON DELETE CASCADE
|
|
332
|
+
)
|
|
333
|
+
`);
|
|
334
|
+
// Secrets table
|
|
335
|
+
db.exec(`
|
|
336
|
+
CREATE TABLE IF NOT EXISTS secrets (
|
|
337
|
+
id TEXT PRIMARY KEY,
|
|
338
|
+
game_id TEXT NOT NULL,
|
|
339
|
+
name TEXT NOT NULL,
|
|
340
|
+
description TEXT NOT NULL,
|
|
341
|
+
category TEXT,
|
|
342
|
+
related_entity_id TEXT,
|
|
343
|
+
related_entity_type TEXT,
|
|
344
|
+
revealed_to TEXT DEFAULT '[]',
|
|
345
|
+
is_public INTEGER DEFAULT 0,
|
|
346
|
+
clues TEXT DEFAULT '[]',
|
|
347
|
+
created_at TEXT NOT NULL,
|
|
348
|
+
FOREIGN KEY (game_id) REFERENCES games(id) ON DELETE CASCADE
|
|
349
|
+
)
|
|
350
|
+
`);
|
|
351
|
+
// Relationships table
|
|
352
|
+
db.exec(`
|
|
353
|
+
CREATE TABLE IF NOT EXISTS relationships (
|
|
354
|
+
id TEXT PRIMARY KEY,
|
|
355
|
+
game_id TEXT NOT NULL,
|
|
356
|
+
source_id TEXT NOT NULL,
|
|
357
|
+
source_type TEXT NOT NULL,
|
|
358
|
+
target_id TEXT NOT NULL,
|
|
359
|
+
target_type TEXT NOT NULL,
|
|
360
|
+
relationship_type TEXT NOT NULL,
|
|
361
|
+
value INTEGER DEFAULT 0,
|
|
362
|
+
label TEXT,
|
|
363
|
+
notes TEXT,
|
|
364
|
+
created_at TEXT NOT NULL,
|
|
365
|
+
updated_at TEXT NOT NULL,
|
|
366
|
+
FOREIGN KEY (game_id) REFERENCES games(id) ON DELETE CASCADE
|
|
367
|
+
)
|
|
368
|
+
`);
|
|
369
|
+
// Relationship history table
|
|
370
|
+
db.exec(`
|
|
371
|
+
CREATE TABLE IF NOT EXISTS relationship_history (
|
|
372
|
+
id TEXT PRIMARY KEY,
|
|
373
|
+
relationship_id TEXT NOT NULL,
|
|
374
|
+
previous_value INTEGER,
|
|
375
|
+
new_value INTEGER,
|
|
376
|
+
reason TEXT,
|
|
377
|
+
timestamp TEXT NOT NULL,
|
|
378
|
+
FOREIGN KEY (relationship_id) REFERENCES relationships(id) ON DELETE CASCADE
|
|
379
|
+
)
|
|
380
|
+
`);
|
|
381
|
+
// Factions table
|
|
382
|
+
db.exec(`
|
|
383
|
+
CREATE TABLE IF NOT EXISTS factions (
|
|
384
|
+
id TEXT PRIMARY KEY,
|
|
385
|
+
game_id TEXT NOT NULL,
|
|
386
|
+
name TEXT NOT NULL,
|
|
387
|
+
description TEXT,
|
|
388
|
+
leader_id TEXT,
|
|
389
|
+
headquarters_id TEXT,
|
|
390
|
+
resources TEXT DEFAULT '{}',
|
|
391
|
+
goals TEXT DEFAULT '[]',
|
|
392
|
+
traits TEXT DEFAULT '[]',
|
|
393
|
+
status TEXT DEFAULT 'active' CHECK (status IN ('active', 'disbanded', 'hidden')),
|
|
394
|
+
created_at TEXT NOT NULL,
|
|
395
|
+
FOREIGN KEY (game_id) REFERENCES games(id) ON DELETE CASCADE
|
|
396
|
+
)
|
|
397
|
+
`);
|
|
398
|
+
// Abilities table
|
|
399
|
+
db.exec(`
|
|
400
|
+
CREATE TABLE IF NOT EXISTS abilities (
|
|
401
|
+
id TEXT PRIMARY KEY,
|
|
402
|
+
game_id TEXT NOT NULL,
|
|
403
|
+
owner_id TEXT,
|
|
404
|
+
owner_type TEXT CHECK (owner_type IN ('template', 'character')),
|
|
405
|
+
name TEXT NOT NULL,
|
|
406
|
+
description TEXT,
|
|
407
|
+
category TEXT,
|
|
408
|
+
cost TEXT DEFAULT '{}',
|
|
409
|
+
cooldown INTEGER,
|
|
410
|
+
current_cooldown INTEGER DEFAULT 0,
|
|
411
|
+
effects TEXT DEFAULT '[]',
|
|
412
|
+
requirements TEXT DEFAULT '{}',
|
|
413
|
+
tags TEXT DEFAULT '[]',
|
|
414
|
+
created_at TEXT NOT NULL,
|
|
415
|
+
FOREIGN KEY (game_id) REFERENCES games(id) ON DELETE CASCADE
|
|
416
|
+
)
|
|
417
|
+
`);
|
|
418
|
+
// Status effects table
|
|
419
|
+
db.exec(`
|
|
420
|
+
CREATE TABLE IF NOT EXISTS status_effects (
|
|
421
|
+
id TEXT PRIMARY KEY,
|
|
422
|
+
game_id TEXT NOT NULL,
|
|
423
|
+
target_id TEXT NOT NULL,
|
|
424
|
+
name TEXT NOT NULL,
|
|
425
|
+
description TEXT,
|
|
426
|
+
effect_type TEXT CHECK (effect_type IN ('buff', 'debuff', 'neutral')),
|
|
427
|
+
duration INTEGER,
|
|
428
|
+
stacks INTEGER DEFAULT 1,
|
|
429
|
+
max_stacks INTEGER,
|
|
430
|
+
effects TEXT DEFAULT '{}',
|
|
431
|
+
source_id TEXT,
|
|
432
|
+
source_type TEXT,
|
|
433
|
+
expires_at TEXT,
|
|
434
|
+
created_at TEXT NOT NULL,
|
|
435
|
+
FOREIGN KEY (game_id) REFERENCES games(id) ON DELETE CASCADE
|
|
436
|
+
)
|
|
437
|
+
`);
|
|
438
|
+
// Tags table
|
|
439
|
+
db.exec(`
|
|
440
|
+
CREATE TABLE IF NOT EXISTS tags (
|
|
441
|
+
id TEXT PRIMARY KEY,
|
|
442
|
+
game_id TEXT NOT NULL,
|
|
443
|
+
entity_id TEXT NOT NULL,
|
|
444
|
+
entity_type TEXT NOT NULL,
|
|
445
|
+
tag TEXT NOT NULL,
|
|
446
|
+
color TEXT,
|
|
447
|
+
notes TEXT,
|
|
448
|
+
created_at TEXT NOT NULL,
|
|
449
|
+
FOREIGN KEY (game_id) REFERENCES games(id) ON DELETE CASCADE,
|
|
450
|
+
UNIQUE (game_id, entity_id, entity_type, tag)
|
|
451
|
+
)
|
|
452
|
+
`);
|
|
453
|
+
// Notes table
|
|
454
|
+
db.exec(`
|
|
455
|
+
CREATE TABLE IF NOT EXISTS notes (
|
|
456
|
+
id TEXT PRIMARY KEY,
|
|
457
|
+
game_id TEXT NOT NULL,
|
|
458
|
+
title TEXT NOT NULL,
|
|
459
|
+
content TEXT NOT NULL,
|
|
460
|
+
category TEXT,
|
|
461
|
+
pinned INTEGER DEFAULT 0,
|
|
462
|
+
related_entity_id TEXT,
|
|
463
|
+
related_entity_type TEXT,
|
|
464
|
+
tags TEXT DEFAULT '[]',
|
|
465
|
+
created_at TEXT NOT NULL,
|
|
466
|
+
updated_at TEXT NOT NULL,
|
|
467
|
+
FOREIGN KEY (game_id) REFERENCES games(id) ON DELETE CASCADE
|
|
468
|
+
)
|
|
469
|
+
`);
|
|
470
|
+
// External updates table - enables multi-agent collaboration
|
|
471
|
+
// External agents can push updates that the primary DM agent receives
|
|
472
|
+
db.exec(`
|
|
473
|
+
CREATE TABLE IF NOT EXISTS external_updates (
|
|
474
|
+
id TEXT PRIMARY KEY,
|
|
475
|
+
game_id TEXT NOT NULL,
|
|
476
|
+
|
|
477
|
+
-- Source identification
|
|
478
|
+
source_agent TEXT NOT NULL,
|
|
479
|
+
source_description TEXT,
|
|
480
|
+
|
|
481
|
+
-- Update content
|
|
482
|
+
update_type TEXT NOT NULL,
|
|
483
|
+
category TEXT,
|
|
484
|
+
title TEXT NOT NULL,
|
|
485
|
+
content TEXT NOT NULL,
|
|
486
|
+
structured_data TEXT,
|
|
487
|
+
|
|
488
|
+
-- Targeting (what entities this relates to)
|
|
489
|
+
target_entity_id TEXT,
|
|
490
|
+
target_entity_type TEXT,
|
|
491
|
+
|
|
492
|
+
-- Priority and status
|
|
493
|
+
priority TEXT DEFAULT 'normal' CHECK (priority IN ('low', 'normal', 'high', 'urgent')),
|
|
494
|
+
status TEXT DEFAULT 'pending' CHECK (status IN ('pending', 'acknowledged', 'applied', 'rejected')),
|
|
495
|
+
|
|
496
|
+
-- Timestamps
|
|
497
|
+
created_at TEXT NOT NULL,
|
|
498
|
+
acknowledged_at TEXT,
|
|
499
|
+
applied_at TEXT,
|
|
500
|
+
|
|
501
|
+
-- DM notes on how update was used
|
|
502
|
+
dm_notes TEXT,
|
|
503
|
+
|
|
504
|
+
FOREIGN KEY (game_id) REFERENCES games(id) ON DELETE CASCADE
|
|
505
|
+
)
|
|
506
|
+
`);
|
|
507
|
+
// Pause states table - captures agent context for seamless resume
|
|
508
|
+
db.exec(`
|
|
509
|
+
CREATE TABLE IF NOT EXISTS pause_states (
|
|
510
|
+
id TEXT PRIMARY KEY,
|
|
511
|
+
game_id TEXT NOT NULL UNIQUE,
|
|
512
|
+
|
|
513
|
+
-- Current scene/moment context
|
|
514
|
+
current_scene TEXT NOT NULL,
|
|
515
|
+
scene_atmosphere TEXT,
|
|
516
|
+
immediate_situation TEXT NOT NULL,
|
|
517
|
+
|
|
518
|
+
-- Pending player interaction
|
|
519
|
+
pending_player_action TEXT,
|
|
520
|
+
awaiting_response_to TEXT,
|
|
521
|
+
presented_choices TEXT,
|
|
522
|
+
|
|
523
|
+
-- Active narrative threads
|
|
524
|
+
active_threads TEXT DEFAULT '[]',
|
|
525
|
+
|
|
526
|
+
-- DM's plans and notes
|
|
527
|
+
dm_short_term_plans TEXT,
|
|
528
|
+
dm_long_term_plans TEXT,
|
|
529
|
+
upcoming_reveals TEXT DEFAULT '[]',
|
|
530
|
+
|
|
531
|
+
-- NPC states (not persisted elsewhere)
|
|
532
|
+
npc_attitudes TEXT DEFAULT '{}',
|
|
533
|
+
active_conversations TEXT DEFAULT '[]',
|
|
534
|
+
|
|
535
|
+
-- Important context that might be lost
|
|
536
|
+
recent_tone TEXT,
|
|
537
|
+
player_apparent_goals TEXT,
|
|
538
|
+
unresolved_hooks TEXT DEFAULT '[]',
|
|
539
|
+
|
|
540
|
+
-- Metadata
|
|
541
|
+
pause_reason TEXT,
|
|
542
|
+
created_at TEXT NOT NULL,
|
|
543
|
+
model_used TEXT,
|
|
544
|
+
|
|
545
|
+
FOREIGN KEY (game_id) REFERENCES games(id) ON DELETE CASCADE
|
|
546
|
+
)
|
|
547
|
+
`);
|
|
548
|
+
// Stored images table - file-based image storage
|
|
549
|
+
// entity_type is flexible to support any entity (character, location, item, scene, faction, quest, ability, etc.)
|
|
550
|
+
db.exec(`
|
|
551
|
+
CREATE TABLE IF NOT EXISTS stored_images (
|
|
552
|
+
id TEXT PRIMARY KEY,
|
|
553
|
+
game_id TEXT NOT NULL,
|
|
554
|
+
entity_id TEXT NOT NULL,
|
|
555
|
+
entity_type TEXT NOT NULL,
|
|
556
|
+
|
|
557
|
+
-- File information
|
|
558
|
+
file_path TEXT NOT NULL,
|
|
559
|
+
file_size INTEGER NOT NULL,
|
|
560
|
+
mime_type TEXT NOT NULL,
|
|
561
|
+
width INTEGER,
|
|
562
|
+
height INTEGER,
|
|
563
|
+
|
|
564
|
+
-- Metadata
|
|
565
|
+
label TEXT,
|
|
566
|
+
description TEXT,
|
|
567
|
+
source TEXT NOT NULL CHECK (source IN ('generated', 'uploaded', 'url')),
|
|
568
|
+
source_url TEXT,
|
|
569
|
+
generation_tool TEXT,
|
|
570
|
+
generation_prompt TEXT,
|
|
571
|
+
|
|
572
|
+
-- Flags
|
|
573
|
+
is_primary INTEGER DEFAULT 0,
|
|
574
|
+
|
|
575
|
+
-- Timestamps
|
|
576
|
+
created_at TEXT NOT NULL,
|
|
577
|
+
|
|
578
|
+
FOREIGN KEY (game_id) REFERENCES games(id) ON DELETE CASCADE
|
|
579
|
+
)
|
|
580
|
+
`);
|
|
581
|
+
// Migration: Remove entity_type CHECK constraint to allow any entity type
|
|
582
|
+
// SQLite doesn't support ALTER CHECK, so we need to recreate the table
|
|
583
|
+
const storedImagesInfo = db.prepare("SELECT sql FROM sqlite_master WHERE type='table' AND name='stored_images'").get();
|
|
584
|
+
if (storedImagesInfo && storedImagesInfo.sql.includes("entity_type IN")) {
|
|
585
|
+
db.exec(`
|
|
586
|
+
CREATE TABLE stored_images_new (
|
|
587
|
+
id TEXT PRIMARY KEY,
|
|
588
|
+
game_id TEXT NOT NULL,
|
|
589
|
+
entity_id TEXT NOT NULL,
|
|
590
|
+
entity_type TEXT NOT NULL,
|
|
591
|
+
file_path TEXT NOT NULL,
|
|
592
|
+
file_size INTEGER NOT NULL,
|
|
593
|
+
mime_type TEXT NOT NULL,
|
|
594
|
+
width INTEGER,
|
|
595
|
+
height INTEGER,
|
|
596
|
+
label TEXT,
|
|
597
|
+
description TEXT,
|
|
598
|
+
source TEXT NOT NULL CHECK (source IN ('generated', 'uploaded', 'url')),
|
|
599
|
+
source_url TEXT,
|
|
600
|
+
generation_tool TEXT,
|
|
601
|
+
generation_prompt TEXT,
|
|
602
|
+
is_primary INTEGER DEFAULT 0,
|
|
603
|
+
created_at TEXT NOT NULL,
|
|
604
|
+
FOREIGN KEY (game_id) REFERENCES games(id) ON DELETE CASCADE
|
|
605
|
+
);
|
|
606
|
+
INSERT INTO stored_images_new SELECT * FROM stored_images;
|
|
607
|
+
DROP TABLE stored_images;
|
|
608
|
+
ALTER TABLE stored_images_new RENAME TO stored_images;
|
|
609
|
+
`);
|
|
610
|
+
}
|
|
611
|
+
// Display configuration table (global, one row)
|
|
612
|
+
db.exec(`
|
|
613
|
+
CREATE TABLE IF NOT EXISTS display_config (
|
|
614
|
+
id INTEGER PRIMARY KEY CHECK (id = 1),
|
|
615
|
+
config TEXT NOT NULL DEFAULT '{}',
|
|
616
|
+
updated_at TEXT NOT NULL
|
|
617
|
+
)
|
|
618
|
+
`);
|
|
619
|
+
// Per-game theme configuration
|
|
620
|
+
db.exec(`
|
|
621
|
+
CREATE TABLE IF NOT EXISTS game_themes (
|
|
622
|
+
game_id TEXT PRIMARY KEY,
|
|
623
|
+
config TEXT NOT NULL DEFAULT '{}',
|
|
624
|
+
updated_at TEXT NOT NULL,
|
|
625
|
+
FOREIGN KEY (game_id) REFERENCES games(id) ON DELETE CASCADE
|
|
626
|
+
)
|
|
627
|
+
`);
|
|
628
|
+
// Stored audio table - file-based audio storage for TTS/voice clips
|
|
629
|
+
// entity_type is flexible to support any entity (character, location, game, scene, etc.)
|
|
630
|
+
db.exec(`
|
|
631
|
+
CREATE TABLE IF NOT EXISTS stored_audio (
|
|
632
|
+
id TEXT PRIMARY KEY,
|
|
633
|
+
game_id TEXT NOT NULL,
|
|
634
|
+
entity_id TEXT NOT NULL,
|
|
635
|
+
entity_type TEXT NOT NULL,
|
|
636
|
+
|
|
637
|
+
-- File information
|
|
638
|
+
file_path TEXT NOT NULL,
|
|
639
|
+
file_size INTEGER NOT NULL,
|
|
640
|
+
mime_type TEXT NOT NULL,
|
|
641
|
+
duration_ms INTEGER,
|
|
642
|
+
sample_rate INTEGER,
|
|
643
|
+
|
|
644
|
+
-- Metadata
|
|
645
|
+
label TEXT,
|
|
646
|
+
description TEXT,
|
|
647
|
+
source TEXT NOT NULL CHECK (source IN ('generated', 'uploaded', 'url')),
|
|
648
|
+
source_url TEXT,
|
|
649
|
+
|
|
650
|
+
-- TTS-specific metadata
|
|
651
|
+
tts_engine TEXT,
|
|
652
|
+
tts_voice TEXT,
|
|
653
|
+
tts_text TEXT,
|
|
654
|
+
tts_settings TEXT,
|
|
655
|
+
|
|
656
|
+
-- Voice reference metadata (for voice cloning)
|
|
657
|
+
is_voice_reference INTEGER DEFAULT 0,
|
|
658
|
+
voice_name TEXT,
|
|
659
|
+
voice_description TEXT,
|
|
660
|
+
|
|
661
|
+
-- Flags
|
|
662
|
+
is_primary INTEGER DEFAULT 0,
|
|
663
|
+
|
|
664
|
+
-- Timestamps
|
|
665
|
+
created_at TEXT NOT NULL,
|
|
666
|
+
|
|
667
|
+
FOREIGN KEY (game_id) REFERENCES games(id) ON DELETE CASCADE
|
|
668
|
+
)
|
|
669
|
+
`);
|
|
670
|
+
// Create indexes for common queries
|
|
671
|
+
db.exec(`
|
|
672
|
+
CREATE INDEX IF NOT EXISTS idx_characters_game ON characters(game_id);
|
|
673
|
+
CREATE INDEX IF NOT EXISTS idx_characters_location ON characters(location_id);
|
|
674
|
+
CREATE INDEX IF NOT EXISTS idx_locations_game ON locations(game_id);
|
|
675
|
+
CREATE INDEX IF NOT EXISTS idx_items_game ON items(game_id);
|
|
676
|
+
CREATE INDEX IF NOT EXISTS idx_items_owner ON items(owner_id, owner_type);
|
|
677
|
+
CREATE INDEX IF NOT EXISTS idx_quests_game ON quests(game_id);
|
|
678
|
+
CREATE INDEX IF NOT EXISTS idx_quests_status ON quests(status);
|
|
679
|
+
CREATE INDEX IF NOT EXISTS idx_narrative_game ON narrative_events(game_id);
|
|
680
|
+
CREATE INDEX IF NOT EXISTS idx_narrative_timestamp ON narrative_events(timestamp);
|
|
681
|
+
CREATE INDEX IF NOT EXISTS idx_combats_game ON combats(game_id);
|
|
682
|
+
CREATE INDEX IF NOT EXISTS idx_combats_status ON combats(status);
|
|
683
|
+
CREATE INDEX IF NOT EXISTS idx_resources_game ON resources(game_id);
|
|
684
|
+
CREATE INDEX IF NOT EXISTS idx_resources_owner ON resources(owner_id, owner_type);
|
|
685
|
+
CREATE INDEX IF NOT EXISTS idx_resources_category ON resources(category);
|
|
686
|
+
CREATE INDEX IF NOT EXISTS idx_resource_history_resource ON resource_history(resource_id);
|
|
687
|
+
CREATE INDEX IF NOT EXISTS idx_resource_constraints_game ON resource_constraints(game_id);
|
|
688
|
+
CREATE INDEX IF NOT EXISTS idx_resource_constraint_members_resource ON resource_constraint_members(resource_id);
|
|
689
|
+
CREATE INDEX IF NOT EXISTS idx_scheduled_events_game ON scheduled_events(game_id);
|
|
690
|
+
CREATE INDEX IF NOT EXISTS idx_scheduled_events_trigger ON scheduled_events(trigger_time);
|
|
691
|
+
CREATE INDEX IF NOT EXISTS idx_timers_game ON timers(game_id);
|
|
692
|
+
CREATE INDEX IF NOT EXISTS idx_random_tables_game ON random_tables(game_id);
|
|
693
|
+
CREATE INDEX IF NOT EXISTS idx_random_tables_category ON random_tables(category);
|
|
694
|
+
CREATE INDEX IF NOT EXISTS idx_secrets_game ON secrets(game_id);
|
|
695
|
+
CREATE INDEX IF NOT EXISTS idx_secrets_category ON secrets(category);
|
|
696
|
+
CREATE INDEX IF NOT EXISTS idx_relationships_game ON relationships(game_id);
|
|
697
|
+
CREATE INDEX IF NOT EXISTS idx_relationships_source ON relationships(source_id, source_type);
|
|
698
|
+
CREATE INDEX IF NOT EXISTS idx_relationships_target ON relationships(target_id, target_type);
|
|
699
|
+
CREATE INDEX IF NOT EXISTS idx_relationship_history_rel ON relationship_history(relationship_id);
|
|
700
|
+
CREATE INDEX IF NOT EXISTS idx_factions_game ON factions(game_id);
|
|
701
|
+
CREATE INDEX IF NOT EXISTS idx_factions_status ON factions(status);
|
|
702
|
+
CREATE INDEX IF NOT EXISTS idx_abilities_game ON abilities(game_id);
|
|
703
|
+
CREATE INDEX IF NOT EXISTS idx_abilities_owner ON abilities(owner_id, owner_type);
|
|
704
|
+
CREATE INDEX IF NOT EXISTS idx_abilities_category ON abilities(category);
|
|
705
|
+
CREATE INDEX IF NOT EXISTS idx_status_effects_game ON status_effects(game_id);
|
|
706
|
+
CREATE INDEX IF NOT EXISTS idx_status_effects_target ON status_effects(target_id);
|
|
707
|
+
CREATE INDEX IF NOT EXISTS idx_tags_game ON tags(game_id);
|
|
708
|
+
CREATE INDEX IF NOT EXISTS idx_tags_entity ON tags(entity_id, entity_type);
|
|
709
|
+
CREATE INDEX IF NOT EXISTS idx_tags_tag ON tags(tag);
|
|
710
|
+
CREATE INDEX IF NOT EXISTS idx_notes_game ON notes(game_id);
|
|
711
|
+
CREATE INDEX IF NOT EXISTS idx_notes_category ON notes(category);
|
|
712
|
+
CREATE INDEX IF NOT EXISTS idx_notes_pinned ON notes(pinned);
|
|
713
|
+
CREATE INDEX IF NOT EXISTS idx_pause_states_game ON pause_states(game_id);
|
|
714
|
+
CREATE INDEX IF NOT EXISTS idx_external_updates_game ON external_updates(game_id);
|
|
715
|
+
CREATE INDEX IF NOT EXISTS idx_external_updates_status ON external_updates(status);
|
|
716
|
+
CREATE INDEX IF NOT EXISTS idx_external_updates_priority ON external_updates(priority);
|
|
717
|
+
CREATE INDEX IF NOT EXISTS idx_stored_images_game ON stored_images(game_id);
|
|
718
|
+
CREATE INDEX IF NOT EXISTS idx_stored_images_entity ON stored_images(entity_id, entity_type);
|
|
719
|
+
CREATE INDEX IF NOT EXISTS idx_stored_images_primary ON stored_images(entity_id, entity_type, is_primary);
|
|
720
|
+
CREATE INDEX IF NOT EXISTS idx_stored_audio_game ON stored_audio(game_id);
|
|
721
|
+
CREATE INDEX IF NOT EXISTS idx_stored_audio_entity ON stored_audio(entity_id, entity_type);
|
|
722
|
+
CREATE INDEX IF NOT EXISTS idx_stored_audio_primary ON stored_audio(entity_id, entity_type, is_primary);
|
|
723
|
+
CREATE INDEX IF NOT EXISTS idx_stored_audio_voice_ref ON stored_audio(game_id, is_voice_reference);
|
|
724
|
+
`);
|
|
725
|
+
}
|