Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion CentrED/Application.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ public class Application

public static CentrEDGame CEDGame { get; private set; } = null!;
public static CEDServer? CEDServer;
public static readonly CentrEDClient CEDClient = new();
private static readonly CentrEDClient _bootstrapClient = new();
public static CentrEDClient BootstrapClient => _bootstrapClient;
public static CentrEDClient CEDClient => CEDGame?.Worlds?.ActiveClient ?? _bootstrapClient;
public static readonly Metrics Metrics = new();

[STAThread]
Expand Down
44 changes: 36 additions & 8 deletions CentrED/CentrEDGame.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@ public class CentrEDGame : Game
public readonly GraphicsDeviceManager _gdm;

private Keymap _keymap;
public MapManager MapManager;
public WorldManager Worlds = new();
public MapManager MapManager => Worlds.Active!.Map;
public UIManager UIManager;
public FacetController FacetController;
public bool Closing { get; set; }

public CentrEDGame()
Expand Down Expand Up @@ -54,8 +56,11 @@ protected override void Initialize()
//UIManager have to exist before MapManager, since Tools can be dependent on Windows
_keymap = new Keymap();
UIManager = new UIManager(_gdm.GraphicsDevice, Window, _keymap);
MapManager = new MapManager(_gdm.GraphicsDevice, Window, _keymap);
RadarMap.Initialize(_gdm.GraphicsDevice);
Worlds.Initialize(_gdm.GraphicsDevice, Window, _keymap);
Worlds.CreateWorld(Application.BootstrapClient);
FacetController = new FacetController();
FacetController.Refresh();
FacetController.SyncWorlds();

base.Initialize();
}
Expand All @@ -69,18 +74,33 @@ protected override void BeginRun()
protected override void UnloadContent()
{
CEDClient.Disconnect();
FacetController?.Shutdown();
}

protected override void Update(GameTime gameTime)
{
try
{
_keymap.Update(Keyboard.GetState());
FacetController.ProcessSync();
foreach (var world in Worlds.Worlds)
{
if (world.Map.WindowVisible)
FacetController.EnsureLoaded(world);
}
Metrics.Start("UpdateClient");
if(CEDClient.Running)
CEDClient.Update();
foreach (var world in Worlds.Worlds)
{
if (world.Client.Running && !FacetController.IsBusy(world))
world.Client.Update();
}
Metrics.Stop("UpdateClient");
MapManager.Update(gameTime, IsActive, !UIManager.CapturingMouse, !UIManager.CapturingKeyboard);
foreach (var world in Worlds.Worlds)
{
if (FacetController.IsBusy(world) || !world.Map.WindowVisible)
continue;
world.Map.Update(gameTime, IsActive, world.Map.ViewHovered, world.Map.ViewFocused);
}
Config.AutoSave();
}
catch(Exception e)
Expand Down Expand Up @@ -119,11 +139,19 @@ protected override void Draw(GameTime gameTime)
try
{
Metrics.Start("Draw");
MapManager.Draw();
foreach (var world in Worlds.Worlds)
{
if (FacetController.IsBusy(world) || !world.Map.WindowVisible)
continue;
world.Map.Draw();
}
GraphicsDevice.SetRenderTarget(null);
GraphicsDevice.Clear(Color.Black);
UIManager.Draw();
Present();
UIManager.DrawExtraWindows();
MapManager.AfterDraw();
foreach (var world in Worlds.Worlds)
world.Map.AfterDraw();
Metrics.Stop("Draw");
}
catch (Exception e)
Expand Down
30 changes: 29 additions & 1 deletion CentrED/Config.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using System.Text.Json;
using System.Security.Cryptography;
using System.Text.Json;
using System.Text.Json.Serialization;
using CentrED.IO.Models;
using Microsoft.Xna.Framework.Input;

Expand All @@ -16,10 +18,36 @@ public class ImageOverlaySettings
public float Screen = 0.0f;
}

public class FacetOverride
{
public int Index;
public string Name = "";
public int Width;
public int Height;
}

public class FacetSettings
{
public string MapsFolder = "";
public int BasePort = 2598;
public List<FacetOverride> Overrides = new();

[JsonIgnore] public string AdminUsername => "admin";
[JsonIgnore] public string AdminPassword { get; } = GenerateSecret();

private static string GenerateSecret()
{
Span<byte> bytes = stackalloc byte[24];
RandomNumberGenerator.Fill(bytes);
return Convert.ToHexString(bytes);
}
}

public class ConfigRoot
{
public string ActiveProfile = "";
public string ServerConfigPath = "cedserver.xml";
public FacetSettings Facets = new();
public bool PreferTexMaps;
public bool ObjectBrightHighlight;
public bool LegacyMouseScroll;
Expand Down
138 changes: 138 additions & 0 deletions CentrED/Map/Facet.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
using System.Text.RegularExpressions;

namespace CentrED.Map;

public class Facet
{
public int Index;
public string Name = "";
public string MapPath = "";
public string StaIdxPath = "";
public string StaticsPath = "";
public ushort Width;
public ushort Height;
public int Port;
public bool IsUop;
public bool DimensionsKnown;

public bool HasStatics => StaIdxPath.Length > 0 && StaticsPath.Length > 0;
public override string ToString() => $"{Name} ({Width}x{Height} @ :{Port})";
}

public partial class FacetManager
{
public List<Facet> Facets { get; } = new();
public string ScannedFolder { get; private set; } = "";

private const int BytesPerLandBlock = 196;

private static readonly string[] FacetNames =
{ "Felucca", "Trammel", "Ilshenar", "Malas", "Tokuno", "TerMur" };

private static readonly (ushort W, ushort H)[] DimensionCandidates =
{
(768, 512), (896, 512), (288, 200), (320, 256), (160, 512), (320, 512), (181, 181), (128, 128)
};

private static readonly (ushort W, ushort H)[] ConventionalDimensions =
{
(896, 512), (896, 512), (288, 200), (320, 256), (181, 181), (160, 512)
};

[GeneratedRegex(@"^map(\d+)(LegacyMUL)?\.(mul|uop)$", RegexOptions.IgnoreCase)]
private static partial Regex MapFileRegex();

public void Scan(string folder, FacetSettings settings)
{
Facets.Clear();
ScannedFolder = folder;
if (string.IsNullOrWhiteSpace(folder) || !Directory.Exists(folder))
return;

foreach (var path in Directory.EnumerateFiles(folder))
{
var match = MapFileRegex().Match(Path.GetFileName(path));
if (!match.Success)
continue;
if (!int.TryParse(match.Groups[1].Value, out var index))
continue;

var isUop = match.Groups[3].Value.Equals("uop", StringComparison.OrdinalIgnoreCase);
var facet = new Facet
{
Index = index,
Name = index < FacetNames.Length ? FacetNames[index] : $"Map {index}",
MapPath = path,
IsUop = isUop,
Port = settings.BasePort,
};
FindStatics(folder, index, facet);
ResolveDimensions(facet, settings);
Facets.Add(facet);
}
Facets.Sort((a, b) => a.Index.CompareTo(b.Index));
}

private static void FindStatics(string folder, int index, Facet facet)
{
foreach (var candidate in new[] { $"staidx{index}.mul", $"staidx{index}LegacyMUL.uop" })
{
var p = Path.Combine(folder, candidate);
if (File.Exists(p)) { facet.StaIdxPath = p; break; }
}
foreach (var candidate in new[] { $"statics{index}.mul", $"statics{index}LegacyMUL.uop" })
{
var p = Path.Combine(folder, candidate);
if (File.Exists(p)) { facet.StaticsPath = p; break; }
}
}

private static void ResolveDimensions(Facet facet, FacetSettings settings)
{
var ovr = settings.Overrides.Find(o => o.Index == facet.Index);
if (ovr != null && !string.IsNullOrWhiteSpace(ovr.Name))
facet.Name = ovr.Name;
if (ovr != null && ovr.Width > 0 && ovr.Height > 0)
{
facet.Width = (ushort)ovr.Width;
facet.Height = (ushort)ovr.Height;
facet.DimensionsKnown = true;
return;
}

var (w, h, known) = GuessDimensions(facet);
facet.Width = w;
facet.Height = h;
facet.DimensionsKnown = known;
}

private static (ushort W, ushort H, bool Known) GuessDimensions(Facet facet)
{
var conventional = facet.Index < ConventionalDimensions.Length
? ConventionalDimensions[facet.Index]
: ((ushort)0, (ushort)0);

if (facet.IsUop)
return (conventional.Item1, conventional.Item2, conventional.Item1 > 0);

long size;
try { size = new FileInfo(facet.MapPath).Length; }
catch { return (conventional.Item1, conventional.Item2, false); }

if (size <= 0 || size % BytesPerLandBlock != 0)
return (conventional.Item1, conventional.Item2, false);

var totalBlocks = size / BytesPerLandBlock;
var matches = DimensionCandidates.Where(c => (long)c.W * c.H == totalBlocks).ToList();
if (matches.Count == 1)
return (matches[0].W, matches[0].H, true);
if (matches.Count > 1)
{
var pick = matches.FirstOrDefault(m => m.W == conventional.Item1 && m.H == conventional.Item2);
if (pick.W > 0)
return (pick.W, pick.H, true);
return (matches[0].W, matches[0].H, false);
}
return (conventional.Item1, conventional.Item2, false);
}
}
Loading
Loading