refactor: rename core-public -> engine-public; fold helpers in; drop shared

Tighten the package model around the determinism invariant: everything the
engine imports must itself be deterministic.

- Rename core-public -> engine-public (package, dir, `engine-public/*` alias,
  all 118 import sites). It is the deterministic public surface the engine
  depends on and that client/server also use.
- Move the pure formatting helpers (renderNumber/renderTroops) from shared
  into engine-public/format — the engine uses them and they're deterministic.
- Drop the now-empty shared package (and its alias). `shared` was intended for
  client/server-shared, engine-forbidden code; there is none yet, so it is
  removed and can be re-added when such code appears.

Final graph: engine-public (leaf) <- engine <- {client, server};
client/server also -> engine-public.

Verified: tsc clean; 1364 + 65 tests pass; prod build succeeds; engine-public
is a leaf; engine has no client/server imports. Lockfile regenerated.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Evan Pelle
2026-06-10 20:32:32 +00:00
parent a7f992e9b0
commit 43d07ca85f
145 changed files with 193 additions and 201 deletions
+1 -1
View File
@@ -6,6 +6,6 @@
"main": "src/index.ts",
"types": "src/index.ts",
"dependencies": {
"@openfront/core-public": "*"
"@openfront/engine-public": "*"
}
}
+2 -2
View File
@@ -1,7 +1,7 @@
// Type contracts live in core-public so the public schema layer can reference
// Type contracts live in engine-public so the public schema layer can reference
// GameEvent without depending on the engine. Re-exported here for existing
// `engine/EventBus` consumers.
import type { EventConstructor, GameEvent } from "core-public/EventBus";
import type { EventConstructor, GameEvent } from "engine-public/EventBus";
export type { EventConstructor, GameEvent };
export class EventBus {
+1 -1
View File
@@ -28,7 +28,7 @@ import { ErrorUpdate, GameUpdateViewData } from "./game/GameUpdates";
import { createNationsForGame } from "./game/NationCreation";
import { loadTerrainMap as loadGameMap } from "./game/TerrainMapLoader";
import { PseudoRandom } from "./PseudoRandom";
import { ClientID, GameStartInfo, Turn } from "core-public/Schemas";
import { ClientID, GameStartInfo, Turn } from "engine-public/Schemas";
import { simpleHash } from "./Util";
export async function createGameRunner(
+3 -3
View File
@@ -1,12 +1,12 @@
import DOMPurify from "dompurify";
import { customAlphabet } from "nanoid";
// The emoji table moved to core-public; re-exported so existing `engine/Util`
// The emoji table moved to engine-public; re-exported so existing `engine/Util`
// imports of these symbols keep working.
export {
emojiTable,
flattenedEmojiTable,
type Emoji,
} from "core-public/Emojis";
} from "engine-public/Emojis";
import { Cell, PlayerType, Unit } from "./game/Game";
import { GameMap, TileRef } from "./game/GameMap";
import {
@@ -17,7 +17,7 @@ import {
PlayerRecord,
Turn,
Winner,
} from "core-public/Schemas";
} from "engine-public/Schemas";
import {
TRIBE_NAME_PREFIXES,
+2 -2
View File
@@ -18,8 +18,8 @@ import {
import { TileRef } from "../game/GameMap";
import { PlayerLike } from "../game/ReadViews";
import { UserSettings } from "../game/UserSettings";
import { GameConfig, TeamCountConfig } from "core-public/Schemas";
import { NukeType } from "core-public/StatsSchemas";
import { GameConfig, TeamCountConfig } from "engine-public/Schemas";
import { NukeType } from "engine-public/StatsSchemas";
import { assertNever, sigmoid, toInt, within } from "../Util";
declare global {
@@ -1,4 +1,4 @@
import { renderTroops } from "shared/format";
import { renderTroops } from "engine-public/format";
import {
Attack,
Difficulty,
@@ -1,6 +1,6 @@
import { Execution, Game } from "../game/Game";
import { PseudoRandom } from "../PseudoRandom";
import { ClientID, GameID, StampedIntent, Turn } from "core-public/Schemas";
import { ClientID, GameID, StampedIntent, Turn } from "engine-public/Schemas";
import { simpleHash } from "../Util";
import { AllianceExtensionExecution } from "./alliance/AllianceExtensionExecution";
import { AllianceRejectExecution } from "./alliance/AllianceRejectExecution";
@@ -13,7 +13,7 @@ import {
} from "../game/Game";
import { TileRef } from "../game/GameMap";
import { PseudoRandom } from "../PseudoRandom";
import { GameID } from "core-public/Schemas";
import { GameID } from "engine-public/Schemas";
import { assertNever, simpleHash } from "../Util";
import { NationAllianceBehavior } from "./nation/NationAllianceBehavior";
import { NationEmojiBehavior } from "./nation/NationEmojiBehavior";
@@ -14,7 +14,7 @@ import { UniversalPathFinding } from "../pathfinding/PathFinder";
import { ParabolaUniversalPathFinder } from "../pathfinding/PathFinder.Parabola";
import { PathStatus } from "../pathfinding/types";
import { PseudoRandom } from "../PseudoRandom";
import { NukeType } from "core-public/StatsSchemas";
import { NukeType } from "engine-public/StatsSchemas";
import { listNukeBreakAlliance } from "./Util";
const SPRITE_RADIUS = 16;
@@ -9,7 +9,7 @@ import {
import { TileRef } from "../game/GameMap";
import { PathFinding } from "../pathfinding/PathFinder";
import { PathStatus, SteppingPathFinder } from "../pathfinding/types";
import { NukeType } from "core-public/StatsSchemas";
import { NukeType } from "engine-public/StatsSchemas";
export class SAMMissileExecution implements Execution {
private active = true;
@@ -9,7 +9,7 @@ import {
} from "../game/Game";
import { TileRef } from "../game/GameMap";
import { PseudoRandom } from "../PseudoRandom";
import { GameID } from "core-public/Schemas";
import { GameID } from "engine-public/Schemas";
import { simpleHash } from "../Util";
import { PlayerExecution } from "./PlayerExecution";
import { TribeExecution } from "./TribeExecution";
@@ -1,4 +1,4 @@
import { renderNumber } from "shared/format";
import { renderNumber } from "engine-public/format";
import {
Execution,
Game,
@@ -1,4 +1,4 @@
import { renderTroops } from "shared/format";
import { renderTroops } from "engine-public/format";
import {
Execution,
Game,
@@ -1,6 +1,6 @@
import { Game, PlayerInfo, PlayerType } from "../game/Game";
import { PseudoRandom } from "../PseudoRandom";
import { GameID } from "core-public/Schemas";
import { GameID } from "engine-public/Schemas";
import { simpleHash } from "../Util";
import { SpawnExecution } from "./SpawnExecution";
import { TRIBE_NAME_PREFIXES, TRIBE_NAME_SUFFIXES } from "./utils/TribeNames";
@@ -1,5 +1,5 @@
import { Game, PlayerType } from "../../game/Game";
import { GameID } from "core-public/Schemas";
import { GameID } from "engine-public/Schemas";
import { SpawnExecution } from "../SpawnExecution";
export class PlayerSpawner {
+3 -3
View File
@@ -1,7 +1,7 @@
import { Config } from "../configuration/Config";
import { AbstractGraph } from "../pathfinding/algorithms/AbstractGraph";
import { PathFinder } from "../pathfinding/types";
import { AllPlayersStats, ClientID } from "core-public/Schemas";
import { AllPlayersStats, ClientID } from "engine-public/Schemas";
import { formatPlayerDisplayName } from "../Util";
import { GameMap, TileRef } from "./GameMap";
import {
@@ -14,7 +14,7 @@ import { MotionPlanRecord } from "./MotionPlans";
import { RailNetwork } from "./RailNetwork";
import { Stats } from "./Stats";
import { UnitPredicate } from "./UnitGrid";
// Enums/consts now live in core-public so the public schema layer can use them
// Enums/consts now live in engine-public so the public schema layer can use them
// without depending on the engine. Re-exported here so existing
// `engine/game/Game` imports keep working.
import {
@@ -30,7 +30,7 @@ import {
RankedType,
Trios,
UnitType,
} from "core-public/game/GameTypes";
} from "engine-public/game/GameTypes";
export {
AllPlayers,
Difficulty,
+3 -3
View File
@@ -1,10 +1,10 @@
import { renderNumber } from "shared/format";
import { renderNumber } from "engine-public/format";
import { Config } from "../configuration/Config";
import { SharedWaterCache } from "../execution/nation/SharedWaterCache";
import { AbstractGraph } from "../pathfinding/algorithms/AbstractGraph";
import { PathFinder } from "../pathfinding/types";
import { AllPlayersStats, ClientID, Winner } from "core-public/Schemas";
import { ATTACK_INDEX_SENT } from "core-public/StatsSchemas";
import { AllPlayersStats, ClientID, Winner } from "engine-public/Schemas";
import { ATTACK_INDEX_SENT } from "engine-public/StatsSchemas";
import { simpleHash } from "../Util";
import { AllianceImpl } from "./AllianceImpl";
import { AllianceRequestImpl } from "./AllianceRequestImpl";
+1 -1
View File
@@ -1,4 +1,4 @@
import { AllPlayersStats, ClientID, Winner } from "core-public/Schemas";
import { AllPlayersStats, ClientID, Winner } from "engine-public/Schemas";
import {
EmojiMessage,
GameUpdates,
+1 -1
View File
@@ -1,5 +1,5 @@
import { PseudoRandom } from "../PseudoRandom";
import { GameStartInfo } from "core-public/Schemas";
import { GameStartInfo } from "engine-public/Schemas";
import {
Cell,
GameMapSize,
+1 -1
View File
@@ -1,5 +1,5 @@
import { PseudoRandom } from "../PseudoRandom";
import { ClientID } from "core-public/Schemas";
import { ClientID } from "engine-public/Schemas";
import {
assertNever,
findClosestBy,
+1 -1
View File
@@ -10,7 +10,7 @@
// Player-vs-PlayerView / Unit-vs-UnitView distinction.
import { Player, type PlayerID, Unit } from "./Game";
import { GameMap, type TileRef } from "./GameMap";
import { UnitType } from "core-public/game/GameTypes";
import { UnitType } from "engine-public/game/GameTypes";
/** Player members read by Config gold/troop calculations. */
export type PlayerLike = Pick<
+2 -2
View File
@@ -1,5 +1,5 @@
import { AllPlayersStats } from "core-public/Schemas";
import { NukeType, OtherUnitType, PlayerStats } from "core-public/StatsSchemas";
import { AllPlayersStats } from "engine-public/Schemas";
import { NukeType, OtherUnitType, PlayerStats } from "engine-public/StatsSchemas";
import { Player, TerraNullius } from "./Game";
export interface Stats {
+2 -2
View File
@@ -1,4 +1,4 @@
import { AllPlayersStats } from "core-public/Schemas";
import { AllPlayersStats } from "engine-public/Schemas";
import {
ATTACK_INDEX_CANCEL,
ATTACK_INDEX_RECV,
@@ -30,7 +30,7 @@ import {
PlayerStats,
unitTypeToBombUnit,
unitTypeToOtherUnit,
} from "core-public/StatsSchemas";
} from "engine-public/StatsSchemas";
import { Player, PlayerType, TerraNullius, UnitType } from "./Game";
import { Stats } from "./Stats";
+1 -1
View File
@@ -1,5 +1,5 @@
import { PseudoRandom } from "../PseudoRandom";
import { ClientID } from "core-public/Schemas";
import { ClientID } from "engine-public/Schemas";
import { simpleHash } from "../Util";
import { PlayerInfo, PlayerType, Team } from "./Game";
+1 -1
View File
@@ -1,4 +1,4 @@
import { ClientID } from "core-public/Schemas";
import { ClientID } from "engine-public/Schemas";
import { TerraNullius } from "./Game";
export class TerraNulliusImpl implements TerraNullius {
+3 -3
View File
@@ -1,9 +1,9 @@
import {
GraphicsOverrides,
GraphicsOverridesSchema,
} from "core-public/GraphicsOverrides";
import { Cosmetics } from "core-public/CosmeticSchemas";
import { PlayerPattern } from "core-public/Schemas";
} from "engine-public/GraphicsOverrides";
import { Cosmetics } from "engine-public/CosmeticSchemas";
import { PlayerPattern } from "engine-public/Schemas";
export function getDefaultKeybinds(isMac: boolean): Record<string, string> {
return {
+1 -1
View File
@@ -1,4 +1,4 @@
import { ClanTagSchema, UsernameSchema } from "core-public/Schemas";
import { ClanTagSchema, UsernameSchema } from "engine-public/Schemas";
export const MIN_USERNAME_LENGTH = 3;
export const MAX_USERNAME_LENGTH = 27;
+1 -1
View File
@@ -10,7 +10,7 @@ import {
} from "../game/Game";
import { TileRef } from "../game/GameMap";
import { ErrorUpdate, GameUpdateViewData } from "../game/GameUpdates";
import { ClientID, GameStartInfo, Turn } from "core-public/Schemas";
import { ClientID, GameStartInfo, Turn } from "engine-public/Schemas";
import { generateID } from "../Util";
import { WorkerMessage } from "./WorkerMessages";
// Inlined into the main bundle as a same-origin Blob, sidestepping the
+1 -1
View File
@@ -8,7 +8,7 @@ import {
} from "../game/Game";
import { TileRef } from "../game/GameMap";
import { ErrorUpdate, GameUpdateViewData } from "../game/GameUpdates";
import { ClientID, GameStartInfo, Turn } from "core-public/Schemas";
import { ClientID, GameStartInfo, Turn } from "engine-public/Schemas";
export type WorkerMessageType =
| "init"