docs: implementation plan for Arkania map pilot
7 tasks: terrain resource (Terrain84.att, off-by-one), map class + registration, update-plugin with spawn gate + /move warp, docker build/verify, deploy + panel verify, client assets, client verify. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
446
docs/superpowers/plans/2026-07-19-new-maps-arkania-pilot.md
Normal file
446
docs/superpowers/plans/2026-07-19-new-maps-arkania-pilot.md
Normal file
@@ -0,0 +1,446 @@
|
|||||||
|
# New Map Import — Arkania Pilot Implementation Plan
|
||||||
|
|
||||||
|
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||||
|
|
||||||
|
**Goal:** Import one pilot map (Arkania, map number 83) end-to-end into OpenMU server, the PC MUnique client, and make it visible/editable in the web admin panel.
|
||||||
|
|
||||||
|
**Architecture:** OpenMU-native map: a C# `BaseMapInitializer` subclass registers map number 83 into `GameConfiguration.Maps`; its terrain comes from an embedded `.att` resource; an update-plugin applies the same to an existing DB. The map is seeded EMPTY (no monsters/NPCs) plus one spawn gate and a `/move` warp for reachability. The web admin panel needs no code changes — the map appears automatically once in the DB. PC client gets the `World83`/`Object83` asset folders.
|
||||||
|
|
||||||
|
**Tech Stack:** C# (.NET), OpenMU `Persistence.Initialization`, Docker build, MUnique MuMain PC client (Data folder assets).
|
||||||
|
|
||||||
|
## Global Constraints
|
||||||
|
|
||||||
|
- Server repo: `AdamuSw` at `D:\OpenMU\MU Client_Mobile 1.04d - Season 6E3\AdamuSw`, work on branch `feature/new-maps-import` (already created).
|
||||||
|
- Pilot map: **Arkania**, **map number 83** (client `World83`). Confirm the name against the pack screenshots before finalizing; if it turns out World83 is a different map, change only the `Name`/class name.
|
||||||
|
- **Terrain resource naming is OFF-BY-ONE:** OpenMU map number `N` loads embedded resource `Resources/Terrain{N+1}.att`. For Arkania (83) that is **`Terrain84.att`**, whose CONTENT is the pack's `Terrain83.att` (world-83 attributes). Verified against Aida (33→Terrain34.att), Vulcanus (63→Terrain64.att).
|
||||||
|
- Terrain resources are NOT globbed by the csproj — each is listed explicitly (`<None Remove>` + `<EmbeddedResource Include>`). New `.att` MUST be added to both lists.
|
||||||
|
- Update-plugins are applied to existing DBs ONLY via AdminPanel → Updates (`DataUpdateService`); never call `SaveChanges` inside a plugin. Fresh/zero DB auto-seeds via the map registration.
|
||||||
|
- Server build is via Docker: `docker build -f Startup/Dockerfile -t adamu-openmu:dev .` from the `AdamuSw` root. Local `dotnet build` is expected to fail at the source-generator pre-step (known, per the Imperial-items work) — use the Docker build as the compile gate.
|
||||||
|
- Pack source: `C:\Users\efpa\Downloads\Arkania Acheron Debenter Uruk Ferea\Arkania Acheron Debenter Uruk Ferea`.
|
||||||
|
- PC client Data root: `D:\AdaMu\AdaMu\Data`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 1: Add the Arkania terrain resource
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `AdamuSw/src/Persistence/Initialization/Resources/Terrain84.att` (copied from pack `Server Side/Data/Terrain/Terrain83.att`)
|
||||||
|
- Modify: `AdamuSw/src/Persistence/Initialization/MUnique.OpenMU.Persistence.Initialization.csproj` (two lines: a `<None Remove>` and an `<EmbeddedResource Include>`)
|
||||||
|
|
||||||
|
**Interfaces:**
|
||||||
|
- Produces: embedded manifest resource `MUnique.OpenMU.Persistence.Initialization.Resources.Terrain84.att`, consumed by `TerrainUpdateHelper.UpdateTerrainFromResources` for map number 83.
|
||||||
|
|
||||||
|
- [ ] **Step 1: Copy the terrain file (pack Terrain83.att → resource Terrain84.att)**
|
||||||
|
|
||||||
|
Run (Git Bash):
|
||||||
|
```bash
|
||||||
|
SRC="/c/Users/efpa/Downloads/Arkania Acheron Debenter Uruk Ferea/Arkania Acheron Debenter Uruk Ferea/Server Side/Data/Terrain/Terrain83.att"
|
||||||
|
DST="/d/OpenMU/MU Client_Mobile 1.04d - Season 6E3/AdamuSw/src/Persistence/Initialization/Resources/Terrain84.att"
|
||||||
|
cp -f "$SRC" "$DST"
|
||||||
|
stat -c '%s %n' "$DST"
|
||||||
|
```
|
||||||
|
Expected: prints `65539 .../Terrain84.att` (matches OpenMU's other `.att` sizes).
|
||||||
|
|
||||||
|
- [ ] **Step 2: Verify the header matches OpenMU's terrain format**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
```bash
|
||||||
|
xxd "/d/OpenMU/MU Client_Mobile 1.04d - Season 6E3/AdamuSw/src/Persistence/Initialization/Resources/Terrain84.att" | head -1
|
||||||
|
xxd "/d/OpenMU/MU Client_Mobile 1.04d - Season 6E3/AdamuSw/src/Persistence/Initialization/Resources/Terrain1.att" | head -1
|
||||||
|
```
|
||||||
|
Expected: both start with `00ff ff04` (same 3-byte header family). If Terrain84.att differs structurally, STOP — the pack file may be a different format.
|
||||||
|
|
||||||
|
- [ ] **Step 3: Register the resource in the csproj**
|
||||||
|
|
||||||
|
In `MUnique.OpenMU.Persistence.Initialization.csproj`, add next to the existing `Terrain*.att` entries:
|
||||||
|
|
||||||
|
In the `<None Remove=...>` group (near `<None Remove="Resources\Terrain1.att" />`):
|
||||||
|
```xml
|
||||||
|
<None Remove="Resources\Terrain84.att" />
|
||||||
|
```
|
||||||
|
In the `<EmbeddedResource Include=...>` group (near `<EmbeddedResource Include="Resources\Terrain1.att" />`):
|
||||||
|
```xml
|
||||||
|
<EmbeddedResource Include="Resources\Terrain84.att" />
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 4: Verify both csproj lines are present**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
```bash
|
||||||
|
grep -n "Terrain84.att" "/d/OpenMU/MU Client_Mobile 1.04d - Season 6E3/AdamuSw/src/Persistence/Initialization/MUnique.OpenMU.Persistence.Initialization.csproj"
|
||||||
|
```
|
||||||
|
Expected: exactly two lines — one `<None Remove ...Terrain84.att />` and one `<EmbeddedResource Include ...Terrain84.att />`.
|
||||||
|
|
||||||
|
- [ ] **Step 5: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd "/d/OpenMU/MU Client_Mobile 1.04d - Season 6E3/AdamuSw"
|
||||||
|
git add src/Persistence/Initialization/Resources/Terrain84.att src/Persistence/Initialization/MUnique.OpenMU.Persistence.Initialization.csproj
|
||||||
|
git commit -m "feat(maps): add Arkania (map 83) terrain resource Terrain84.att"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 2: Create the Arkania map initializer and register it
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `AdamuSw/src/Persistence/Initialization/VersionSeasonSix/Maps/Arkania.cs`
|
||||||
|
- Modify: `AdamuSw/src/Persistence/Initialization/VersionSeasonSix/GameMapsInitializer.cs` (add one `yield return`)
|
||||||
|
|
||||||
|
**Interfaces:**
|
||||||
|
- Produces: `internal class Arkania : BaseMapInitializer` with `internal const byte Number = 83;` and `internal const string Name = "Arkania";`. Consumed by `GameMapsInitializer` (fresh seed) and the update-plugin (Task 3).
|
||||||
|
|
||||||
|
- [ ] **Step 1: Create the map class**
|
||||||
|
|
||||||
|
Create `Arkania.cs` (mirrors the `Aida.cs` pattern, but with no monster/NPC spawns — those are added later via the web panel):
|
||||||
|
```csharp
|
||||||
|
// <copyright file="Arkania.cs" company="MUnique">
|
||||||
|
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||||
|
// </copyright>
|
||||||
|
|
||||||
|
namespace MUnique.OpenMU.Persistence.Initialization.VersionSeasonSix.Maps;
|
||||||
|
|
||||||
|
using MUnique.OpenMU.DataModel.Configuration;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The initialization for the Arkania map (imported map, world 83).
|
||||||
|
/// Seeded empty; monster/NPC spawns and gates are added via the admin panel.
|
||||||
|
/// </summary>
|
||||||
|
internal class Arkania : BaseMapInitializer
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The Number of the Map.
|
||||||
|
/// </summary>
|
||||||
|
internal const byte Number = 83;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The Name of the Map.
|
||||||
|
/// </summary>
|
||||||
|
internal const string Name = "Arkania";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="Arkania"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context">The context.</param>
|
||||||
|
/// <param name="gameConfiguration">The game configuration.</param>
|
||||||
|
public Arkania(IContext context, GameConfiguration gameConfiguration)
|
||||||
|
: base(context, gameConfiguration)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override byte MapNumber => Number;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override string MapName => Name;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: Register the map in GameMapsInitializer**
|
||||||
|
|
||||||
|
In `VersionSeasonSix/GameMapsInitializer.cs`, in `MapInitializerTypes`, add after the last existing `yield return typeof(...);` line:
|
||||||
|
```csharp
|
||||||
|
yield return typeof(Arkania);
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 3: Verify class + registration compile-consistency (textual)**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
```bash
|
||||||
|
grep -n "class Arkania : BaseMapInitializer" "/d/OpenMU/MU Client_Mobile 1.04d - Season 6E3/AdamuSw/src/Persistence/Initialization/VersionSeasonSix/Maps/Arkania.cs"
|
||||||
|
grep -n "typeof(Arkania)" "/d/OpenMU/MU Client_Mobile 1.04d - Season 6E3/AdamuSw/src/Persistence/Initialization/VersionSeasonSix/GameMapsInitializer.cs"
|
||||||
|
```
|
||||||
|
Expected: one match each.
|
||||||
|
|
||||||
|
- [ ] **Step 4: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd "/d/OpenMU/MU Client_Mobile 1.04d - Season 6E3/AdamuSw"
|
||||||
|
git add src/Persistence/Initialization/VersionSeasonSix/Maps/Arkania.cs src/Persistence/Initialization/VersionSeasonSix/GameMapsInitializer.cs
|
||||||
|
git commit -m "feat(maps): add Arkania map initializer (number 83) and register it"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 3: Add spawn gate + `/move` warp, and the DB update-plugin
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `AdamuSw/src/Persistence/Initialization/Updates/UpdateVersion.cs` (add enum value)
|
||||||
|
- Create: `AdamuSw/src/Persistence/Initialization/Updates/AddArkaniaMapUpdateSeason6.cs`
|
||||||
|
|
||||||
|
**Interfaces:**
|
||||||
|
- Consumes: `Arkania.Number` (83) and `Arkania.Name` ("Arkania") from Task 2; the `Terrain84.att` resource from Task 1.
|
||||||
|
- Produces: update-plugin `AddArkaniaMapUpdateSeason6` with `Version => UpdateVersion.AddArkaniaMapSeason6`.
|
||||||
|
|
||||||
|
- [ ] **Step 1: Compute a walkable spawn coordinate from the terrain**
|
||||||
|
|
||||||
|
The spawn gate must sit on a walkable tile. Scan the pack `Terrain83.att` (3-byte header + 256×256 attribute bytes; a tile is walkable when its attribute byte has none of the blocking bits `0x04` NOMOVE / `0x08` NOGROUND set — i.e. value `0x00` or `0x01` safezone). Run:
|
||||||
|
```bash
|
||||||
|
python - <<'PY'
|
||||||
|
p="/c/Users/efpa/Downloads/Arkania Acheron Debenter Uruk Ferea/Arkania Acheron Debenter Uruk Ferea/Server Side/Data/Terrain/Terrain83.att"
|
||||||
|
d=open(p,'rb').read()[3:] # drop 3-byte header
|
||||||
|
# attribute[y*256 + x]; find a walkable tile nearest the center
|
||||||
|
best=None
|
||||||
|
for y in range(256):
|
||||||
|
for x in range(256):
|
||||||
|
a=d[y*256+x]
|
||||||
|
if a & 0x04 or a & 0x08: # NOMOVE or NOGROUND -> blocked
|
||||||
|
continue
|
||||||
|
dist=abs(x-128)+abs(y-128)
|
||||||
|
if best is None or dist<best[0]:
|
||||||
|
best=(dist,x,y,a)
|
||||||
|
print("spawn X=%d Y=%d (attr=0x%02x, dist-from-center=%d)"%(best[1],best[2],best[3],best[0]))
|
||||||
|
PY
|
||||||
|
```
|
||||||
|
Expected: prints a concrete `X`/`Y` (e.g. `spawn X=128 Y=130 ...`). Record these as `SPAWN_X` / `SPAWN_Y` for Step 3.
|
||||||
|
|
||||||
|
- [ ] **Step 2: Add the UpdateVersion enum value**
|
||||||
|
|
||||||
|
In `Updates/UpdateVersion.cs`, after `AddImperialWeapons050Season6 = 95,` add:
|
||||||
|
```csharp
|
||||||
|
/// <summary>
|
||||||
|
/// Adds the imported Arkania map (number 83).
|
||||||
|
/// </summary>
|
||||||
|
AddArkaniaMapSeason6 = 96,
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 3: Create the update-plugin**
|
||||||
|
|
||||||
|
Create `Updates/AddArkaniaMapUpdateSeason6.cs` (mirrors `AddImperialWeapons050UpdateSeason6.cs` for the boilerplate and `AddLorenMarketJuliaWarpPlugIn.cs` for the inline gate/warp). Replace `SPAWN_X`/`SPAWN_Y` with the values from Step 1:
|
||||||
|
```csharp
|
||||||
|
// <copyright file="AddArkaniaMapUpdateSeason6.cs" company="MUnique">
|
||||||
|
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||||
|
// </copyright>
|
||||||
|
|
||||||
|
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||||
|
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using MUnique.OpenMU.DataModel.Configuration;
|
||||||
|
using MUnique.OpenMU.Persistence.Initialization.VersionSeasonSix.Maps;
|
||||||
|
using MUnique.OpenMU.PlugIns;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds the imported Arkania map (number 83): the map definition with its terrain,
|
||||||
|
/// a spawn gate on a walkable tile, and a /move warp entry so players can reach it.
|
||||||
|
/// Monsters and NPCs are added afterwards via the admin panel.
|
||||||
|
/// </summary>
|
||||||
|
[PlugIn]
|
||||||
|
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||||
|
[Guid("2C8B0A61-6B2E-4D7A-9E3C-7F1A4B2D9E01")]
|
||||||
|
public class AddArkaniaMapUpdateSeason6 : UpdatePlugInBase
|
||||||
|
{
|
||||||
|
internal const string PlugInName = "Add Arkania map (83)";
|
||||||
|
internal const string PlugInDescription = "Adds the imported Arkania map (number 83) with terrain, a spawn gate, and a /move warp. Monsters/NPCs are added via the admin panel.";
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override UpdateVersion Version => UpdateVersion.AddArkaniaMapSeason6;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override string DataInitializationKey => VersionSeasonSix.DataInitialization.Id;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override string Name => PlugInName;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override string Description => PlugInDescription;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override bool IsMandatory => true;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override DateTime CreatedAt => new(2026, 07, 19, 12, 0, 0, DateTimeKind.Utc);
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||||
|
{
|
||||||
|
// Idempotency guard: skip if Arkania (83) already exists.
|
||||||
|
if (gameConfiguration.Maps.Any(m => m.Number == Arkania.Number))
|
||||||
|
{
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
|
||||||
|
var initializer = new Arkania(context, gameConfiguration);
|
||||||
|
initializer.Initialize(); // creates map 83 + loads terrain from Terrain84.att
|
||||||
|
initializer.SetSafezoneMap();
|
||||||
|
|
||||||
|
var arkania = gameConfiguration.Maps.First(m => m.Number == Arkania.Number);
|
||||||
|
|
||||||
|
// Spawn gate on a walkable tile (coordinates from the terrain scan).
|
||||||
|
var spawnGate = context.CreateNew<ExitGate>();
|
||||||
|
arkania.ExitGates.Add(spawnGate);
|
||||||
|
spawnGate.Map = arkania;
|
||||||
|
spawnGate.X1 = SPAWN_X;
|
||||||
|
spawnGate.Y1 = SPAWN_Y;
|
||||||
|
spawnGate.X2 = SPAWN_X;
|
||||||
|
spawnGate.Y2 = SPAWN_Y;
|
||||||
|
spawnGate.IsSpawnGate = true;
|
||||||
|
|
||||||
|
// /move warp entry.
|
||||||
|
if (gameConfiguration.WarpList.All(w => w.Name != Arkania.Name))
|
||||||
|
{
|
||||||
|
var warp = context.CreateNew<WarpInfo>();
|
||||||
|
warp.Index = 83;
|
||||||
|
warp.Name = Arkania.Name;
|
||||||
|
warp.Costs = 5000;
|
||||||
|
warp.LevelRequirement = 10;
|
||||||
|
warp.Gate = spawnGate;
|
||||||
|
}
|
||||||
|
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 4: Verify enum + plugin references are consistent (textual)**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
```bash
|
||||||
|
grep -n "AddArkaniaMapSeason6 = 96" "/d/OpenMU/MU Client_Mobile 1.04d - Season 6E3/AdamuSw/src/Persistence/Initialization/Updates/UpdateVersion.cs"
|
||||||
|
grep -n "UpdateVersion.AddArkaniaMapSeason6" "/d/OpenMU/MU Client_Mobile 1.04d - Season 6E3/AdamuSw/src/Persistence/Initialization/Updates/AddArkaniaMapUpdateSeason6.cs"
|
||||||
|
grep -cn "SPAWN_X\|SPAWN_Y" "/d/OpenMU/MU Client_Mobile 1.04d - Season 6E3/AdamuSw/src/Persistence/Initialization/Updates/AddArkaniaMapUpdateSeason6.cs"
|
||||||
|
```
|
||||||
|
Expected: first two match; the third prints `0` (all `SPAWN_X`/`SPAWN_Y` placeholders were replaced with numbers). If it prints non-zero, replace the remaining placeholders.
|
||||||
|
|
||||||
|
- [ ] **Step 5: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd "/d/OpenMU/MU Client_Mobile 1.04d - Season 6E3/AdamuSw"
|
||||||
|
git add src/Persistence/Initialization/Updates/UpdateVersion.cs src/Persistence/Initialization/Updates/AddArkaniaMapUpdateSeason6.cs
|
||||||
|
git commit -m "feat(maps): add Arkania DB update-plugin with spawn gate and /move warp"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 4: Build the server image and verify map 83 is in the configuration
|
||||||
|
|
||||||
|
**Files:** none (build + verification only).
|
||||||
|
|
||||||
|
- [ ] **Step 1: Docker build**
|
||||||
|
|
||||||
|
Run (from AdamuSw root):
|
||||||
|
```bash
|
||||||
|
cd "/d/OpenMU/MU Client_Mobile 1.04d - Season 6E3/AdamuSw"
|
||||||
|
docker build -f Startup/Dockerfile -t adamu-openmu:dev . 2>&1 | tail -20
|
||||||
|
```
|
||||||
|
Expected: `Successfully tagged adamu-openmu:dev` (or the buildkit equivalent). If compilation fails, fix the reported C# error and rebuild.
|
||||||
|
|
||||||
|
- [ ] **Step 2: Verify the map symbol is compiled into the initialization DLL**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
```bash
|
||||||
|
docker run --rm --entrypoint sh adamu-openmu:dev -c "grep -a -c Arkania /app/MUnique.OpenMU.Persistence.Initialization.dll || true"
|
||||||
|
```
|
||||||
|
Expected: a non-zero count (the `Arkania`/`AddArkaniaMap` strings are present in the built DLL). If `0`, the class was not compiled in — recheck Task 2/3.
|
||||||
|
|
||||||
|
- [ ] **Step 3: Commit (no code; checkpoint only if any fixes were made)**
|
||||||
|
|
||||||
|
Only if Steps 1-2 required code fixes:
|
||||||
|
```bash
|
||||||
|
cd "/d/OpenMU/MU Client_Mobile 1.04d - Season 6E3/AdamuSw"
|
||||||
|
git commit -am "fix(maps): resolve Arkania build errors"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 5: Deploy the server and apply the update on the existing DB
|
||||||
|
|
||||||
|
**Files:** none (deploy + admin action).
|
||||||
|
|
||||||
|
- [ ] **Step 1: Bring the server up with the new image**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
```bash
|
||||||
|
cd "/d/OpenMU/MU Client_Mobile 1.04d - Season 6E3/AdamuSw"
|
||||||
|
docker compose -f deploy-adamu/docker-compose.local.yml up -d
|
||||||
|
```
|
||||||
|
Expected: containers start (game server + admin panel). Note: if the server points at the remote `65.109.224.204` DB instead of local, deploy the image to that host per the established deploy flow instead.
|
||||||
|
|
||||||
|
- [ ] **Step 2: Apply the update via AdminPanel → Updates**
|
||||||
|
|
||||||
|
Open the AdminPanel (`http://localhost:8081` locally, or the server's admin URL), go to the **Updates** page, and apply **"Add Arkania map (83)"**. (Fresh/zero DBs already have it from the seed and will show nothing to apply.)
|
||||||
|
Expected: the update applies without error and disappears from the pending list.
|
||||||
|
|
||||||
|
- [ ] **Step 3: Verify the map exists in the panel**
|
||||||
|
|
||||||
|
In the AdminPanel, open `/edit-config-grid/GameMapDefinition` (Config → Game Maps).
|
||||||
|
Expected: **Arkania** appears in the list with number 83.
|
||||||
|
|
||||||
|
- [ ] **Step 4: Verify terrain renders in the visual editor**
|
||||||
|
|
||||||
|
Open `/map-editor`, select **Arkania**.
|
||||||
|
Expected: the terrain image renders (not blank). Blank terrain ⇒ the `Terrain84.att` resource didn't load — recheck Task 1 (csproj embedding + off-by-one name).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 6: Copy the client assets to the PC client
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Copy into `D:\AdaMu\AdaMu\Data`: `World83\`, `Object83\`, and the `Local\Eng` additions.
|
||||||
|
|
||||||
|
**Interfaces:**
|
||||||
|
- Consumes: pack `Client Side/Data/{World83,Object83,Local/Eng}`.
|
||||||
|
- Produces: renderable client-side assets for world index 83.
|
||||||
|
|
||||||
|
- [ ] **Step 1: Copy World83 and Object83**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
```bash
|
||||||
|
PACK="/c/Users/efpa/Downloads/Arkania Acheron Debenter Uruk Ferea/Arkania Acheron Debenter Uruk Ferea/Client Side/Data"
|
||||||
|
DST="/d/AdaMu/AdaMu/Data"
|
||||||
|
cp -rf "$PACK/World83" "$DST/World83"
|
||||||
|
cp -rf "$PACK/Object83" "$DST/Object83"
|
||||||
|
ls "$DST/World83/EncTerrain83.att" "$DST/Object83" >/dev/null && echo "World83+Object83 copied"
|
||||||
|
```
|
||||||
|
Expected: prints `World83+Object83 copied`.
|
||||||
|
|
||||||
|
- [ ] **Step 2: Copy the map-name image and minimap for world 83**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
```bash
|
||||||
|
PACK="/c/Users/efpa/Downloads/Arkania Acheron Debenter Uruk Ferea/Arkania Acheron Debenter Uruk Ferea/Client Side/Data/Local/Eng"
|
||||||
|
DST="/d/AdaMu/AdaMu/Data/Local/Eng"
|
||||||
|
[ -f "$PACK/ImgsMapName/Arkania.OZT" ] && cp -f "$PACK/ImgsMapName/Arkania.OZT" "$DST/ImgsMapName/" && echo "map-name image copied"
|
||||||
|
ls "$PACK/Minimap/" | grep -i "World83" | while read f; do cp -f "$PACK/Minimap/$f" "$DST/Minimap/"; echo "minimap $f copied"; done
|
||||||
|
```
|
||||||
|
Expected: the Arkania name image copies; a `Minimap_World83_eng.bmd` copies if present (absence is non-fatal — minimap only).
|
||||||
|
|
||||||
|
- [ ] **Step 3: Verify no case-mismatch on referenced object textures**
|
||||||
|
|
||||||
|
The MUnique PC client on Windows is case-insensitive, so a spot check suffices. Run:
|
||||||
|
```bash
|
||||||
|
ls "/d/AdaMu/AdaMu/Data/World83" | head; echo "---"; ls "/d/AdaMu/AdaMu/Data/Object83" | wc -l
|
||||||
|
```
|
||||||
|
Expected: World83 lists `EncTerrain83.*` + tiles; Object83 has a non-zero file count.
|
||||||
|
|
||||||
|
_(No commit — `D:\AdaMu\AdaMu` is the deployed client folder, not a git repo.)_
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 7: Verify in the PC client
|
||||||
|
|
||||||
|
**Files:** none (runtime verification). May require a `vendor/MuMain` change + rebuild if the engine caps the world index.
|
||||||
|
|
||||||
|
- [ ] **Step 1: Launch the client and reach Arkania**
|
||||||
|
|
||||||
|
Start `D:\AdaMu\AdaMu\main.exe`, log in to a character (server `65.109.224.204`), and issue `/move Arkania` in chat.
|
||||||
|
Expected: the character warps to Arkania; terrain and objects render; no `OpenTexture Failed` / crash; the character can walk.
|
||||||
|
|
||||||
|
- [ ] **Step 2: If the map does not load (world-index unsupported by the engine)**
|
||||||
|
|
||||||
|
If the client crashes or shows a blank/again-Lorencia world on entering 83, the MUnique engine may not handle world index 83. Check the terrain/world-loading path in `vendor/MuMain/src/source` for a hard world-index cap or a per-index table, add support for 83, and rebuild `Main.exe` (see the `pc-munique-client-build` memory: `vcvarsall x86` + vswhere on PATH + `cmake --build out/build/windows-x86-fresh --config Release`), then redeploy `main.exe` to `D:\AdaMu\AdaMu`. Re-run Step 1.
|
||||||
|
|
||||||
|
- [ ] **Step 3: Final confirmation**
|
||||||
|
|
||||||
|
Confirm all three surfaces:
|
||||||
|
- Server/panel: Arkania (83) listed, terrain renders in `/map-editor`.
|
||||||
|
- Client: `/move Arkania` enters a walkable, correctly-rendered map.
|
||||||
|
- Then the user adds monster/NPC spawns via the panel and confirms they appear live.
|
||||||
|
|
||||||
|
Pilot complete. Follow-up (separate plan): replicate Tasks 1-7 for maps 84-87 (Acheron/Debenter/Uruk/Ferea) — each terrain resource is `Terrain{N+1}.att` from the pack's `Terrain{N}.att` — plus mobile-client asset import.
|
||||||
|
|
||||||
|
## Self-Review notes
|
||||||
|
|
||||||
|
- **Spec coverage:** terrain resource (Task 1), map class + registration (Task 2), update-plugin + warp/gate (Task 3), build/verify (Task 4), deploy/apply/panel-verify (Task 5), client assets (Task 6), client verify (Task 7). All spec sections covered.
|
||||||
|
- **Off-by-one terrain naming** is captured as a Global Constraint and applied in Task 1 (resource `Terrain84.att` ← pack `Terrain83.att`).
|
||||||
|
- **No monster spawns in the seed** — intentional (user adds via panel), matching the spec.
|
||||||
|
- **Type consistency:** `Arkania.Number` (83) and `Arkania.Name` ("Arkania") defined in Task 2 are the exact symbols consumed in Task 3; `UpdateVersion.AddArkaniaMapSeason6` (=96) defined and consumed consistently.
|
||||||
Reference in New Issue
Block a user