Add bulk fill and staggered retrofit at campus/cluster level
CI / build-and-push (push) Successful in 40s
CI / build-and-push (push) Successful in 40s
Campus level: "Fill All DCs" instantly fills all operational DCs with selected SKU in one click. "Retrofit Campus" queues a staggered retrofit with configurable concurrency (1/10%/25%/custom) so only a fraction of DCs go offline at a time, preserving capacity during the upgrade. Cluster level: "Fill All DCs" fills across all campuses in one action. The game engine automatically advances the retrofit queue each tick, promoting pending DCs as active ones complete. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import type {
|
||||
GameState, InfrastructureState, Cluster, Campus, DataCenter,
|
||||
DeploymentCohort, NetworkHealthState, PipelineStage,
|
||||
DeploymentCohort, NetworkHealthState, PipelineStage, RackSkuId,
|
||||
CampusRetrofitQueue,
|
||||
} from '@ai-tycoon/shared';
|
||||
import {
|
||||
LOCATION_CONFIGS,
|
||||
@@ -442,7 +443,74 @@ export function processInfrastructure(state: GameState): InfraTickResult {
|
||||
};
|
||||
});
|
||||
|
||||
return { ...campus, dataCenters };
|
||||
// Process campus retrofit queue
|
||||
let finalDCs = dataCenters;
|
||||
let updatedQueue: CampusRetrofitQueue | null = campus.retrofitQueue ?? null;
|
||||
|
||||
if (updatedQueue && updatedQueue.pendingDCIds.length + updatedQueue.activeDCIds.length > 0) {
|
||||
updatedQueue = { ...updatedQueue };
|
||||
|
||||
// Detect DCs that just completed retrofit (were active, now operational)
|
||||
const newlyCompleted = finalDCs.filter(
|
||||
dc => updatedQueue!.activeDCIds.includes(dc.id) && dc.status === 'operational',
|
||||
);
|
||||
|
||||
if (newlyCompleted.length > 0) {
|
||||
updatedQueue.activeDCIds = updatedQueue.activeDCIds.filter(
|
||||
id => !newlyCompleted.some(dc => dc.id === id),
|
||||
);
|
||||
updatedQueue.completedDCIds = [
|
||||
...updatedQueue.completedDCIds,
|
||||
...newlyCompleted.map(dc => dc.id),
|
||||
];
|
||||
}
|
||||
|
||||
// Promote DCs from pending to active
|
||||
const slotsAvailable = updatedQueue.maxConcurrent - updatedQueue.activeDCIds.length;
|
||||
if (slotsAvailable > 0 && updatedQueue.pendingDCIds.length > 0) {
|
||||
const toStart = updatedQueue.pendingDCIds.slice(0, slotsAvailable);
|
||||
updatedQueue.pendingDCIds = updatedQueue.pendingDCIds.slice(toStart.length);
|
||||
updatedQueue.activeDCIds = [...updatedQueue.activeDCIds, ...toStart];
|
||||
|
||||
finalDCs = finalDCs.map(dc => {
|
||||
if (!toStart.includes(dc.id)) return dc;
|
||||
if (dc.status !== 'operational' || !dc.rackSkuId) return dc;
|
||||
|
||||
const pipelineCount = dc.deploymentCohorts.filter(c => c.stage !== 'decommission').reduce((sum, c) => sum + c.count, 0);
|
||||
const totalRacks = dc.computeRacksOnline + pipelineCount;
|
||||
if (totalRacks <= 0) return dc;
|
||||
|
||||
const oldSku = RACK_SKU_CONFIGS[dc.rackSkuId as RackSkuId];
|
||||
const decommTicks = Math.ceil(oldSku.pipelineTimeTicks.installation * (1 + COHORT_SCALE_FACTOR * totalRacks));
|
||||
|
||||
return {
|
||||
...dc,
|
||||
status: 'retrofitting' as const,
|
||||
deploymentCohorts: [],
|
||||
retrofitState: {
|
||||
fromSkuId: dc.rackSkuId as RackSkuId,
|
||||
toSkuId: updatedQueue!.targetSkuId,
|
||||
phase: 'decommissioning' as const,
|
||||
progress: 0,
|
||||
total: decommTicks,
|
||||
racksRemaining: totalRacks,
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
// Check if queue is complete
|
||||
if (updatedQueue.pendingDCIds.length === 0 && updatedQueue.activeDCIds.length === 0) {
|
||||
notifications.push({
|
||||
title: 'Campus Retrofit Complete',
|
||||
message: `All DCs in ${campus.name} have been retrofitted to ${RACK_SKU_CONFIGS[updatedQueue.targetSkuId].name}!`,
|
||||
type: 'success',
|
||||
});
|
||||
updatedQueue = null;
|
||||
}
|
||||
}
|
||||
|
||||
return { ...campus, dataCenters: finalDCs, retrofitQueue: updatedQueue };
|
||||
});
|
||||
|
||||
return { ...cluster, campuses };
|
||||
|
||||
@@ -18,6 +18,15 @@ export interface Cluster {
|
||||
|
||||
export type CampusStatus = 'constructing' | 'operational';
|
||||
|
||||
export interface CampusRetrofitQueue {
|
||||
targetSkuId: RackSkuId;
|
||||
maxConcurrent: number;
|
||||
pendingDCIds: string[];
|
||||
activeDCIds: string[];
|
||||
completedDCIds: string[];
|
||||
skippedDCIds: string[];
|
||||
}
|
||||
|
||||
export interface Campus {
|
||||
id: string;
|
||||
name: string;
|
||||
@@ -27,6 +36,7 @@ export interface Campus {
|
||||
status: CampusStatus;
|
||||
constructionProgress: number;
|
||||
constructionTotal: number;
|
||||
retrofitQueue: CampusRetrofitQueue | null;
|
||||
}
|
||||
|
||||
// --- Data Center ---
|
||||
|
||||
Reference in New Issue
Block a user