feat: implement database-backed game settings with a new schema, service, and migration script.
Some checks failed
Deploy to Production / test (push) Failing after 26s
Some checks failed
Deploy to Production / test (push) Failing after 26s
This commit is contained in:
192
shared/modules/game-settings/game-settings.service.ts
Normal file
192
shared/modules/game-settings/game-settings.service.ts
Normal file
@@ -0,0 +1,192 @@
|
||||
import { eq } from "drizzle-orm";
|
||||
import { gameSettings } from "@db/schema";
|
||||
import { DrizzleClient } from "@shared/db/DrizzleClient";
|
||||
import type {
|
||||
LevelingConfig,
|
||||
EconomyConfig,
|
||||
InventoryConfig,
|
||||
LootdropConfig,
|
||||
TriviaConfig,
|
||||
ModerationConfig,
|
||||
} from "@db/schema/game-settings";
|
||||
|
||||
export type GameSettingsData = {
|
||||
leveling: LevelingConfig;
|
||||
economy: EconomyConfig;
|
||||
inventory: InventoryConfig;
|
||||
lootdrop: LootdropConfig;
|
||||
trivia: TriviaConfig;
|
||||
moderation: ModerationConfig;
|
||||
commands: Record<string, boolean>;
|
||||
system: Record<string, unknown>;
|
||||
};
|
||||
|
||||
let cachedSettings: GameSettingsData | null = null;
|
||||
let cacheTimestamp = 0;
|
||||
const CACHE_TTL_MS = 30000;
|
||||
|
||||
export const gameSettingsService = {
|
||||
getSettings: async (useCache = true): Promise<GameSettingsData | null> => {
|
||||
if (useCache && cachedSettings && Date.now() - cacheTimestamp < CACHE_TTL_MS) {
|
||||
return cachedSettings;
|
||||
}
|
||||
|
||||
const settings = await DrizzleClient.query.gameSettings.findFirst({
|
||||
where: eq(gameSettings.id, "default"),
|
||||
});
|
||||
|
||||
if (!settings) return null;
|
||||
|
||||
cachedSettings = {
|
||||
leveling: settings.leveling,
|
||||
economy: settings.economy,
|
||||
inventory: settings.inventory,
|
||||
lootdrop: settings.lootdrop,
|
||||
trivia: settings.trivia,
|
||||
moderation: settings.moderation,
|
||||
commands: settings.commands ?? {},
|
||||
system: settings.system ?? {},
|
||||
};
|
||||
cacheTimestamp = Date.now();
|
||||
|
||||
return cachedSettings;
|
||||
},
|
||||
|
||||
upsertSettings: async (data: Partial<GameSettingsData>) => {
|
||||
const existing = await gameSettingsService.getSettings(false);
|
||||
|
||||
const values: typeof gameSettings.$inferInsert = {
|
||||
id: "default",
|
||||
leveling: data.leveling ?? existing?.leveling ?? gameSettingsService.getDefaultLeveling(),
|
||||
economy: data.economy ?? existing?.economy ?? gameSettingsService.getDefaultEconomy(),
|
||||
inventory: data.inventory ?? existing?.inventory ?? gameSettingsService.getDefaultInventory(),
|
||||
lootdrop: data.lootdrop ?? existing?.lootdrop ?? gameSettingsService.getDefaultLootdrop(),
|
||||
trivia: data.trivia ?? existing?.trivia ?? gameSettingsService.getDefaultTrivia(),
|
||||
moderation: data.moderation ?? existing?.moderation ?? gameSettingsService.getDefaultModeration(),
|
||||
commands: data.commands ?? existing?.commands ?? {},
|
||||
system: data.system ?? existing?.system ?? {},
|
||||
updatedAt: new Date(),
|
||||
};
|
||||
|
||||
const [result] = await DrizzleClient.insert(gameSettings)
|
||||
.values(values)
|
||||
.onConflictDoUpdate({
|
||||
target: gameSettings.id,
|
||||
set: values,
|
||||
})
|
||||
.returning();
|
||||
|
||||
gameSettingsService.invalidateCache();
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
updateSection: async <K extends keyof GameSettingsData>(
|
||||
section: K,
|
||||
value: GameSettingsData[K]
|
||||
) => {
|
||||
const existing = await gameSettingsService.getSettings(false);
|
||||
|
||||
if (!existing) {
|
||||
throw new Error("Game settings not found. Initialize settings first.");
|
||||
}
|
||||
|
||||
const updates: Partial<GameSettingsData> = { [section]: value };
|
||||
await gameSettingsService.upsertSettings(updates);
|
||||
|
||||
gameSettingsService.invalidateCache();
|
||||
},
|
||||
|
||||
toggleCommand: async (commandName: string, enabled: boolean) => {
|
||||
const settings = await gameSettingsService.getSettings(false);
|
||||
|
||||
if (!settings) {
|
||||
throw new Error("Game settings not found. Initialize settings first.");
|
||||
}
|
||||
|
||||
const commands = {
|
||||
...settings.commands,
|
||||
[commandName]: enabled,
|
||||
};
|
||||
|
||||
await gameSettingsService.updateSection("commands", commands);
|
||||
},
|
||||
|
||||
invalidateCache: () => {
|
||||
cachedSettings = null;
|
||||
cacheTimestamp = 0;
|
||||
},
|
||||
|
||||
getDefaultLeveling: (): LevelingConfig => ({
|
||||
base: 100,
|
||||
exponent: 1.5,
|
||||
chat: {
|
||||
cooldownMs: 60000,
|
||||
minXp: 5,
|
||||
maxXp: 15,
|
||||
},
|
||||
}),
|
||||
|
||||
getDefaultEconomy: (): EconomyConfig => ({
|
||||
daily: {
|
||||
amount: "100",
|
||||
streakBonus: "10",
|
||||
weeklyBonus: "50",
|
||||
cooldownMs: 86400000,
|
||||
},
|
||||
transfers: {
|
||||
allowSelfTransfer: false,
|
||||
minAmount: "1",
|
||||
},
|
||||
exam: {
|
||||
multMin: 1.0,
|
||||
multMax: 2.0,
|
||||
},
|
||||
}),
|
||||
|
||||
getDefaultInventory: (): InventoryConfig => ({
|
||||
maxStackSize: "99",
|
||||
maxSlots: 20,
|
||||
}),
|
||||
|
||||
getDefaultLootdrop: (): LootdropConfig => ({
|
||||
activityWindowMs: 300000,
|
||||
minMessages: 5,
|
||||
spawnChance: 0.1,
|
||||
cooldownMs: 60000,
|
||||
reward: {
|
||||
min: 10,
|
||||
max: 50,
|
||||
currency: "AU",
|
||||
},
|
||||
}),
|
||||
|
||||
getDefaultTrivia: (): TriviaConfig => ({
|
||||
entryFee: "50",
|
||||
rewardMultiplier: 1.8,
|
||||
timeoutSeconds: 30,
|
||||
cooldownMs: 60000,
|
||||
categories: [],
|
||||
difficulty: "random",
|
||||
}),
|
||||
|
||||
getDefaultModeration: (): ModerationConfig => ({
|
||||
prune: {
|
||||
maxAmount: 100,
|
||||
confirmThreshold: 50,
|
||||
batchSize: 100,
|
||||
batchDelayMs: 1000,
|
||||
},
|
||||
}),
|
||||
|
||||
getDefaults: (): GameSettingsData => ({
|
||||
leveling: gameSettingsService.getDefaultLeveling(),
|
||||
economy: gameSettingsService.getDefaultEconomy(),
|
||||
inventory: gameSettingsService.getDefaultInventory(),
|
||||
lootdrop: gameSettingsService.getDefaultLootdrop(),
|
||||
trivia: gameSettingsService.getDefaultTrivia(),
|
||||
moderation: gameSettingsService.getDefaultModeration(),
|
||||
commands: {},
|
||||
system: {},
|
||||
}),
|
||||
};
|
||||
Reference in New Issue
Block a user