Merge branch 'main' into feature/general-icon-names

# Conflicts:
#	app/game-frontend/src/views/CurrentCityView.vue
This commit is contained in:
2026-07-26 06:01:04 +00:00
111 changed files with 14430 additions and 2960 deletions
+2
View File
@@ -817,6 +817,8 @@ export const adminRouter = router({
profileName: profile.profileName,
apiRunning: false,
daemonRunning: false,
auctionRunning: false,
battleSimRunning: false,
tournamentRunning: false,
},
}));
@@ -26,6 +26,8 @@ export type LobbyProfileStatus = {
runtime: {
apiRunning: boolean;
daemonRunning: boolean;
auctionRunning: boolean;
battleSimRunning: boolean;
tournamentRunning: boolean;
};
korName: string;
@@ -69,7 +71,16 @@ export class RepositoryProfileStatusService implements GatewayProfileStatusServi
private mapProfile(
row: GatewayProfileRecord,
runtimeMap: Map<string, { apiRunning: boolean; daemonRunning: boolean; tournamentRunning: boolean }>
runtimeMap: Map<
string,
{
apiRunning: boolean;
daemonRunning: boolean;
auctionRunning: boolean;
battleSimRunning: boolean;
tournamentRunning: boolean;
}
>
): LobbyProfileStatus {
const meta = row.meta;
return {
@@ -81,6 +92,8 @@ export class RepositoryProfileStatusService implements GatewayProfileStatusServi
runtime: runtimeMap.get(row.profileName) ?? {
apiRunning: false,
daemonRunning: false,
auctionRunning: false,
battleSimRunning: false,
tournamentRunning: false,
},
korName: (meta.korName as string | undefined) ?? row.profile,
@@ -39,6 +39,8 @@ export interface GatewayOrchestratorOptions {
export interface ProfileRuntimeState {
apiRunning: boolean;
daemonRunning: boolean;
auctionRunning: boolean;
battleSimRunning: boolean;
tournamentRunning: boolean;
}
@@ -66,13 +68,24 @@ export const planProfileReconcile = (
): { shouldStart: boolean; shouldStop: boolean } => {
if (status === 'RUNNING' || status === 'PREOPEN' || status === 'PAUSED' || status === 'COMPLETED') {
return {
shouldStart: !(runtime.apiRunning && runtime.daemonRunning && runtime.tournamentRunning),
shouldStart: !(
runtime.apiRunning &&
runtime.daemonRunning &&
runtime.auctionRunning &&
runtime.battleSimRunning &&
runtime.tournamentRunning
),
shouldStop: false,
};
}
return {
shouldStart: false,
shouldStop: runtime.apiRunning || runtime.daemonRunning || runtime.tournamentRunning,
shouldStop:
runtime.apiRunning ||
runtime.daemonRunning ||
runtime.auctionRunning ||
runtime.battleSimRunning ||
runtime.tournamentRunning,
};
};
@@ -273,8 +286,21 @@ const parseInstallOptions = (
};
};
const buildProcessName = (profileName: string, role: 'api' | 'daemon' | 'tournament'): string =>
`sammo:${profileName}:${role === 'api' ? 'game-api' : role === 'daemon' ? 'turn-daemon' : 'tournament-worker'}`;
const buildProcessName = (
profileName: string,
role: 'api' | 'daemon' | 'auction' | 'battle-sim' | 'tournament'
): string =>
`sammo:${profileName}:${
role === 'api'
? 'game-api'
: role === 'daemon'
? 'turn-daemon'
: role === 'auction'
? 'auction-worker'
: role === 'battle-sim'
? 'battle-sim-worker'
: 'tournament-worker'
}`;
const isMissingProcessError = (error: unknown): boolean =>
error instanceof Error && /process or namespace not found/i.test(error.message);
@@ -285,11 +311,15 @@ export const buildProcessDefinitions = (
): {
api: { name: string; script: string; cwd: string; env: Record<string, string> };
daemon: { name: string; script: string; cwd: string; env: Record<string, string> };
auction: { name: string; script: string; cwd: string; env: Record<string, string> };
battleSim: { name: string; script: string; cwd: string; env: Record<string, string> };
tournament: { name: string; script: string; cwd: string; env: Record<string, string> };
} => {
const baseEnv = { ...(config.baseEnv ?? {}) };
const apiName = buildProcessName(profile.profileName, 'api');
const daemonName = buildProcessName(profile.profileName, 'daemon');
const auctionName = buildProcessName(profile.profileName, 'auction');
const battleSimName = buildProcessName(profile.profileName, 'battle-sim');
const tournamentName = buildProcessName(profile.profileName, 'tournament');
const runtimeWorkspace = profile.buildWorkspace ?? config.workspaceRoot;
const apiCwd = path.join(runtimeWorkspace, 'app', 'game-api');
@@ -328,6 +358,24 @@ export const buildProcessDefinitions = (
cwd: daemonCwd,
env: daemonEnv,
},
auction: {
name: auctionName,
script: apiScript,
cwd: apiCwd,
env: {
...apiEnv,
GAME_API_ROLE: 'auction-worker',
},
},
battleSim: {
name: battleSimName,
script: apiScript,
cwd: apiCwd,
env: {
...apiEnv,
GAME_API_ROLE: 'battle-sim-worker',
},
},
tournament: {
name: tournamentName,
script: apiScript,
@@ -377,11 +425,15 @@ const mapRuntimeStates = (profileNames: string[], processNames: Map<string, bool
profileNames.map((profileName) => {
const apiName = buildProcessName(profileName, 'api');
const daemonName = buildProcessName(profileName, 'daemon');
const auctionName = buildProcessName(profileName, 'auction');
const battleSimName = buildProcessName(profileName, 'battle-sim');
const tournamentName = buildProcessName(profileName, 'tournament');
return {
profileName,
apiRunning: processNames.get(apiName) ?? false,
daemonRunning: processNames.get(daemonName) ?? false,
auctionRunning: processNames.get(auctionName) ?? false,
battleSimRunning: processNames.get(battleSimName) ?? false,
tournamentRunning: processNames.get(tournamentName) ?? false,
};
});
@@ -982,6 +1034,8 @@ export class GatewayOrchestrator implements GatewayOrchestratorHandle {
try {
await this.processManager.start(definitions.api);
await this.processManager.start(definitions.daemon);
await this.processManager.start(definitions.auction);
await this.processManager.start(definitions.battleSim);
await this.processManager.start(definitions.tournament);
await this.repository.updateLastError(profile.profileName, null);
return true;
@@ -997,10 +1051,12 @@ export class GatewayOrchestrator implements GatewayOrchestratorHandle {
private async stopProfile(profile: GatewayProfileRecord): Promise<void> {
const apiName = buildProcessName(profile.profileName, 'api');
const daemonName = buildProcessName(profile.profileName, 'daemon');
const auctionName = buildProcessName(profile.profileName, 'auction');
const battleSimName = buildProcessName(profile.profileName, 'battle-sim');
const tournamentName = buildProcessName(profile.profileName, 'tournament');
const existingNames = new Set((await this.processManager.list()).map((process) => process.name));
const failures: string[] = [];
for (const name of [apiName, daemonName, tournamentName]) {
for (const name of [apiName, daemonName, auctionName, battleSimName, tournamentName]) {
if (!existingNames.has(name)) {
continue;
}