Add RepairImportedMapWarpsSeason6 update (warp indexes 83-87)
Databases whose imported season 6 maps (Arkania 82, Acheron 83, Debenter 84, Uruk 85, Ferea 86) were created by a fresh initialization end up with the map rows and exit gates but without the /move WarpInfo entries, so /move answers "Unknown warp index" for those maps. AddArkaniaMapUpdateSeason6 and AddImportedMapsUpdateSeason6 return early when the map already exists, which also skips the spawn gate, the WarpInfo and the game-server assignment that follow. Update 104 repairs each of those pieces independently and is safe to run repeatedly. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,177 @@
|
||||
// <copyright file="RepairImportedMapWarpsSeason6.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>
|
||||
/// Repairs the five imported season 6 maps (Arkania 82, Acheron 83, Debenter 84,
|
||||
/// Uruk 85, Ferea 86) on databases where the map rows exist but their spawn gate,
|
||||
/// /move warp entry or game-server assignment do not.
|
||||
///
|
||||
/// <para>
|
||||
/// Why this is needed: <see cref="AddArkaniaMapUpdateSeason6"/> and
|
||||
/// <see cref="AddImportedMapsUpdateSeason6"/> guard on the map already existing and
|
||||
/// <c>return</c> immediately when it does. That guard also skips the spawn gate, the
|
||||
/// <see cref="WarpInfo"/> and the <see cref="GameServerConfiguration"/> assignment
|
||||
/// that follow it. A database whose maps were created any other way therefore ends up
|
||||
/// with maps but no warps, and <c>/move</c> answers "Unknown warp index".
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// Every step below is guarded on its own, so this update repairs whatever is missing
|
||||
/// and leaves whatever is already correct untouched. It is safe to run repeatedly.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("6E4A57C3-1D82-4B90-A5F7-3C08E1D6B742")]
|
||||
public class RepairImportedMapWarpsSeason6 : UpdatePlugInBase
|
||||
{
|
||||
internal const string PlugInName = "Repair imported map warps (Arkania, Acheron, Debenter, Uruk, Ferea)";
|
||||
internal const string PlugInDescription = "Adds the missing spawn gates, /move warp entries (indexes 83-87) and game-server assignments for maps 82-86 on databases where the maps exist but the warps do not.";
|
||||
|
||||
/// <summary>
|
||||
/// Spawn point per map, taken from a working database rather than from the
|
||||
/// original add-updates: those hardcoded map centres, which were later moved to
|
||||
/// walkable tiles. Order: map number, map name, warp index, spawn X, spawn Y.
|
||||
/// </summary>
|
||||
private static readonly (byte MapNumber, string MapName, short WarpIndex, byte X, byte Y)[] Repairs =
|
||||
{
|
||||
(Arkania.Number, Arkania.Name, 83, 213, 44),
|
||||
(Acheron.Number, Acheron.Name, 84, 59, 202),
|
||||
(Debenter.Number, Debenter.Name, 85, 18, 100),
|
||||
(Uruk.Number, Uruk.Name, 86, 115, 103),
|
||||
(Ferea.Number, Ferea.Name, 87, 237, 146),
|
||||
};
|
||||
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.RepairImportedMapWarpsSeason6;
|
||||
|
||||
/// <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, 31, 12, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
var serverConfigs = (await context.GetAsync<GameServerConfiguration>().ConfigureAwait(false)).ToList();
|
||||
|
||||
foreach (var (mapNumber, mapName, warpIndex, x, y) in Repairs)
|
||||
{
|
||||
var map = gameConfiguration.Maps.FirstOrDefault(m => m.Number == mapNumber);
|
||||
if (map is null)
|
||||
{
|
||||
// The map itself is missing, so the original add-update never ran here.
|
||||
// Create it (definition + terrain) before repairing the rest.
|
||||
CreateMap(context, gameConfiguration, mapNumber);
|
||||
map = gameConfiguration.Maps.FirstOrDefault(m => m.Number == mapNumber);
|
||||
if (map is null)
|
||||
{
|
||||
continue; // unknown map number — nothing sensible to do
|
||||
}
|
||||
}
|
||||
|
||||
var spawnGate = EnsureSpawnGate(context, map, x, y);
|
||||
EnsureWarp(context, gameConfiguration, warpIndex, mapName, spawnGate);
|
||||
EnsureHostedByServers(serverConfigs, map, mapNumber);
|
||||
}
|
||||
}
|
||||
|
||||
private static void CreateMap(IContext context, GameConfiguration gameConfiguration, byte mapNumber)
|
||||
{
|
||||
IMapInitializer? initializer = mapNumber switch
|
||||
{
|
||||
Arkania.Number => new Arkania(context, gameConfiguration),
|
||||
Acheron.Number => new Acheron(context, gameConfiguration),
|
||||
Debenter.Number => new Debenter(context, gameConfiguration),
|
||||
Uruk.Number => new Uruk(context, gameConfiguration),
|
||||
Ferea.Number => new Ferea(context, gameConfiguration),
|
||||
_ => null,
|
||||
};
|
||||
|
||||
if (initializer is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
initializer.Initialize();
|
||||
initializer.SetSafezoneMap();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the map's spawn gate, creating it at the given tile when absent.
|
||||
/// A warp whose <see cref="WarpInfo.Gate"/> is null still fails at /move time,
|
||||
/// so the gate has to exist before the warp is wired to it.
|
||||
/// </summary>
|
||||
private static ExitGate EnsureSpawnGate(IContext context, GameMapDefinition map, byte x, byte y)
|
||||
{
|
||||
var existing = map.ExitGates.FirstOrDefault(g => g.IsSpawnGate);
|
||||
if (existing is not null)
|
||||
{
|
||||
return existing;
|
||||
}
|
||||
|
||||
var spawnGate = context.CreateNew<ExitGate>();
|
||||
map.ExitGates.Add(spawnGate);
|
||||
spawnGate.Map = map;
|
||||
spawnGate.X1 = x;
|
||||
spawnGate.Y1 = y;
|
||||
spawnGate.X2 = x;
|
||||
spawnGate.Y2 = y;
|
||||
spawnGate.IsSpawnGate = true;
|
||||
return spawnGate;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures a <see cref="WarpInfo"/> exists for the index and points at the gate.
|
||||
/// Matches on index OR name so a half-created entry is repaired instead of
|
||||
/// duplicated — WarpHandlerPlugIn looks the entry up by index alone.
|
||||
/// </summary>
|
||||
private static void EnsureWarp(IContext context, GameConfiguration gameConfiguration, short warpIndex, string mapName, ExitGate spawnGate)
|
||||
{
|
||||
var warp = gameConfiguration.WarpList.FirstOrDefault(w => w.Index == warpIndex || w.Name == mapName);
|
||||
if (warp is null)
|
||||
{
|
||||
warp = context.CreateNew<WarpInfo>();
|
||||
gameConfiguration.WarpList.Add(warp);
|
||||
warp.Costs = 5000;
|
||||
warp.LevelRequirement = 10;
|
||||
}
|
||||
|
||||
warp.Index = warpIndex;
|
||||
warp.Name = mapName;
|
||||
warp.Gate ??= spawnGate;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the map to every game server configuration that does not host it yet.
|
||||
/// Without this the warp resolves but the target map is not running.
|
||||
/// </summary>
|
||||
private static void EnsureHostedByServers(IList<GameServerConfiguration> serverConfigs, GameMapDefinition map, byte mapNumber)
|
||||
{
|
||||
foreach (var serverConfig in serverConfigs)
|
||||
{
|
||||
if (serverConfig.Maps.All(m => m.Number != mapNumber))
|
||||
{
|
||||
serverConfig.Maps.Add(map);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -524,4 +524,9 @@ public enum UpdateVersion
|
||||
/// The version of the <see cref="FinishElfMasterTreePlugIn"/>.
|
||||
/// </summary>
|
||||
FinishElfMasterTree = 103,
|
||||
|
||||
/// <summary>
|
||||
/// The version of the <see cref="RepairImportedMapWarpsSeason6"/>.
|
||||
/// </summary>
|
||||
RepairImportedMapWarpsSeason6 = 104,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user