Split GameServer into Core (engine) + Content (scripts), static reference #17

Open
opened 2026-07-17 08:12:54 +02:00 by marco · 1 comment
Owner

Split IsoMmo.GameServer into an engine core and a content/scripts project, mirroring ModernUO's Server/UOContent separation organizationally (not its runtime script compiler).

Decision (agreed 2026-07-17)

  • Static separation: Content is a normal .NET project that references the Core and registers content at compile time. Reloading content = restart the server. No ModernUO-style runtime Roslyn recompilation / hot-reload (a heavy subsystem, overkill for a small friends server).
  • Timing: do it as a dedicated refactor when mobiles/spawners land (M7 / #9), when there's enough content to design the extension seams against real shapes — not prematurely mid-M5. (Update 2026-07-22: #9 mobiles + #10 combat + #64 GM-placed creatures have all landed — the trigger condition has passed, so this is now actionable. Best sequenced alongside the #65 spawner work, which introduces the first real content types to design the extension seam against.)
  • Dependency is one-way: Core must not reference Content; Core exposes abstractions + a registration API that Content plugs into (DI at startup).

Scope

  • IsoMmo.GameServer keeps: networking, World/tick, AoI, persistence, protocol handling, and the base types/hooks content extends.
  • New IsoMmo.GameServer.Content holds: item kinds/definitions (today's ItemCatalog), mobile types, spawners, gameplay rules, GM commands.
  • A registration seam (e.g. an IContentModule/DI registration) so content registers itself without the core knowing concrete types.
  • ModernUO study captured in the issue to inform the seam design (Item/Mobile/Layer model, what to borrow, what to skip).

Definition of Done

Base DoD applies on top.

  • The solution builds with Core not referencing Content (verified by the project reference graph); removing Content still leaves a buildable (if content-less) Core.
  • Existing gameplay (items, and by then mobiles) works with definitions living in Content.
  • A new content type (e.g. a new item kind or mobile) can be added by touching only the Content project.
Split `IsoMmo.GameServer` into an **engine core** and a **content/scripts** project, mirroring ModernUO's Server/UOContent separation organizationally (not its runtime script compiler). ## Decision (agreed 2026-07-17) - **Static** separation: `Content` is a normal .NET project that references the Core and registers content at compile time. Reloading content = restart the server. **No** ModernUO-style runtime Roslyn recompilation / hot-reload (a heavy subsystem, overkill for a small friends server). - **Timing**: do it as a dedicated refactor **when mobiles/spawners land (M7 / #9)**, when there's enough content to design the extension seams against real shapes — not prematurely mid-M5. **(Update 2026-07-22: #9 mobiles + #10 combat + #64 GM-placed creatures have all landed — the trigger condition has passed, so this is now actionable. Best sequenced alongside the #65 spawner work, which introduces the first real content types to design the extension seam against.)** - Dependency is one-way: Core must not reference Content; Core exposes abstractions + a registration API that Content plugs into (DI at startup). ## Scope - [ ] `IsoMmo.GameServer` keeps: networking, `World`/tick, AoI, persistence, protocol handling, and the base types/hooks content extends. - [ ] New `IsoMmo.GameServer.Content` holds: item kinds/definitions (today's `ItemCatalog`), mobile types, spawners, gameplay rules, GM commands. - [ ] A registration seam (e.g. an `IContentModule`/DI registration) so content registers itself without the core knowing concrete types. - [ ] ModernUO study captured in the issue to inform the seam design (Item/Mobile/Layer model, what to borrow, what to skip). ## Definition of Done _Base DoD applies on top._ - [ ] The solution builds with Core not referencing Content (verified by the project reference graph); removing Content still leaves a buildable (if content-less) Core. - [ ] Existing gameplay (items, and by then mobiles) works with definitions living in Content. - [ ] A new content type (e.g. a new item kind or mobile) can be added by touching only the Content project.
Author
Owner

ModernUO study (informs the seam design)

Projects/ splits into Server (engine: Item, Mobile, Serial, World, networking, serialization infra) and UOContent (all gameplay content: BaseWeapon, BaseCreature, spells). UOContent depends on Server; the engine knows no concrete content types.

Content loading — key finding: NO runtime script compilation. RunUO compiled a Scripts/ folder at startup; ModernUO abandoned that. Content is plain C# AOT-compiled into UOContent.dll; at runtime the server uses reflection + attributes ([Constructible], [SerializationGenerator]) to discover types. So the engine/content boundary is a compile-time assembly boundary — exactly the static split we chose. Good confirmation.

Data model worth borrowing:

  • Serial — a strongly-typed unique id per networked entity.
  • Item with a polymorphic Parent (IEntity) — one reference models "in world / in a container / equipped" (Parent = Mobile, Item, or null). Their single best idea. Our current M5 model uses an explicit ItemPlace enum (Ground/Backpack/Equipped) — fine for now; we can evolve toward Parent when containers land.
  • Layer enum + FindItemOnLayer — equipment stored as a flat List<Item> filtered by a Layer field, rather than a fixed slot struct. Simpler; scales fine.
  • Mobile: raw vs effective stats (RawStr/Str…), derived pools (Hits/Stam/Mana), Skills. Good shape for M6/M8.

Skip as overkill: runtime script compilation (they dropped it); their source-generated custom serialization + parallel/incremental saves + JSON migration snapshots (engineering for hundreds of players/years) — we already use EF Core + SQLite/Postgres, keep that.

Takeaway for #17 (M7 refactor): copy the engine/content assembly boundary and the data-model shapes (Serial / Item+Parent / Mobile / Layer); keep our EF-based persistence; register content via a simple DI/reflection seam, no runtime compiler.

## ModernUO study (informs the seam design) `Projects/` splits into **`Server`** (engine: `Item`, `Mobile`, `Serial`, `World`, networking, serialization infra) and **`UOContent`** (all gameplay content: `BaseWeapon`, `BaseCreature`, spells). `UOContent` depends on `Server`; the engine knows no concrete content types. **Content loading — key finding: NO runtime script compilation.** RunUO compiled a `Scripts/` folder at startup; ModernUO abandoned that. Content is plain C# AOT-compiled into `UOContent.dll`; at runtime the server uses **reflection + attributes** (`[Constructible]`, `[SerializationGenerator]`) to discover types. So the engine/content boundary is a compile-time assembly boundary — exactly the **static** split we chose. Good confirmation. **Data model worth borrowing:** - **`Serial`** — a strongly-typed unique id per networked entity. - **`Item` with a polymorphic `Parent` (IEntity)** — one reference models "in world / in a container / equipped" (Parent = Mobile, Item, or null). Their single best idea. Our current M5 model uses an explicit `ItemPlace` enum (Ground/Backpack/Equipped) — fine for now; we can evolve toward `Parent` when containers land. - **`Layer` enum + `FindItemOnLayer`** — equipment stored as a flat `List<Item>` filtered by a `Layer` field, rather than a fixed slot struct. Simpler; scales fine. - **`Mobile`**: raw vs effective stats (RawStr/Str…), derived pools (Hits/Stam/Mana), `Skills`. Good shape for M6/M8. **Skip as overkill:** runtime script compilation (they dropped it); their source-generated custom serialization + parallel/incremental saves + JSON migration snapshots (engineering for hundreds of players/years) — we already use EF Core + SQLite/Postgres, keep that. **Takeaway for #17 (M7 refactor):** copy the *engine/content assembly boundary* and the *data-model shapes* (Serial / Item+Parent / Mobile / Layer); keep our EF-based persistence; register content via a simple DI/reflection seam, no runtime compiler.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
marco/IsoMmo#17
No description provided.