Skip to content

Commit 3b167d1

Browse files
committed
[3D] Added lazy MPQ loading and an error messagebox when mpq files are corrupted
1 parent 55d5dcd commit 3b167d1

File tree

14 files changed

+124
-36
lines changed

14 files changed

+124
-36
lines changed

Rendering/TheEngine/IGame.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ namespace TheEngine
22
{
33
public interface IGame
44
{
5-
public void Initialize(Engine engine);
5+
public bool Initialize(Engine engine);
66
public void Update(float diff);
77
public void Render(float delta);
88
public event Action RequestDispose;

Rendering/TheEngine/Managers/MeshManager.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ public IMesh CreateManagedOnlyMesh(Vector3[] vertices, int[] indices)
9191
var mesh = new Mesh(engine, handle, true);
9292
mesh.SetVertices(vertices);
9393
mesh.SetIndices(indices, 0);
94+
mesh.BuildBoundingBox();
9495

9596
meshes.Add(mesh);
9697
#if TRACK_ALLOCATIONS

Rendering/TheEngine/TheEnginePanel.cs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,9 @@ protected override void OnOpenGlDeinit(GlInterface gl, int fb)
140140

141141
protected override void OnOpenGlRender(GlInterface gl, int fb)
142142
{
143+
if (engine == null || delayedDispose)
144+
return;
145+
143146
engine.statsManager.PixelSize = new Vector2(PixelSize.Item1, PixelSize.Item2);
144147
engine.statsManager.Counters.PresentTime.Add(PresentTime);
145148
renderStopwatch.Restart();
@@ -194,12 +197,14 @@ protected override void OnOpenGlRender(GlInterface gl, int fb)
194197
private bool gameInitialized;
195198
public static readonly DirectProperty<TheEnginePanel, IGame?> GameProperty = AvaloniaProperty.RegisterDirect<TheEnginePanel, IGame?>(nameof(Game), o => o.Game, (o, v) => o.Game = v);
196199

200+
private System.IDisposable? globalKeyDownDisposable;
201+
private System.IDisposable? globalKeyUpDisposable;
197202
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
198203
{
199204
base.OnAttachedToVisualTree(e);
200205
sw.Restart();
201-
((IControl)e.Root).AddDisposableHandler(KeyDownEvent, GlobalKeyDown, RoutingStrategies.Tunnel);
202-
((IControl)e.Root).AddDisposableHandler(KeyUpEvent, GlobalKeyUp, RoutingStrategies.Tunnel);
206+
globalKeyDownDisposable = ((IControl)e.Root).AddDisposableHandler(KeyDownEvent, GlobalKeyDown, RoutingStrategies.Tunnel);
207+
globalKeyUpDisposable = ((IControl)e.Root).AddDisposableHandler(KeyUpEvent, GlobalKeyUp, RoutingStrategies.Tunnel);
203208
}
204209

205210
private bool IsModifierKey(Key key) => key is Key.LeftShift or Key.LeftCtrl or Key.LeftAlt or Key.LWin;
@@ -221,6 +226,10 @@ protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e
221226
//if (delayedDispose)
222227
// base.OnDetachedFromVisualTree(e);
223228
//delayedDispose = false;
229+
globalKeyUpDisposable?.Dispose();
230+
globalKeyDownDisposable?.Dispose();
231+
globalKeyUpDisposable = null;
232+
globalKeyDownDisposable = null;
224233
sw.Stop();
225234
}
226235

@@ -230,7 +239,11 @@ protected virtual void Update(float delta)
230239
{
231240
gameInitialized = true;
232241
game.RequestDispose += GameOnRequestDispose;
233-
game.Initialize(engine!);
242+
if (!game.Initialize(engine!))
243+
{
244+
GameOnRequestDispose();
245+
game = null;
246+
}
234247
}
235248
game?.Update(delta);
236249
}

Rendering/TheMaths/Vector3.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1922,5 +1922,9 @@ public override bool Equals(object value)
19221922
// {
19231923
// return *(Vector3*)&value;
19241924
// }
1925+
public Vector3 WithY(float y)
1926+
{
1927+
return new Vector3(X, y, Z);
1928+
}
19251929
}
19261930
}

WDE.MPQ/Services/MpqService.cs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,20 @@ public IMpqArchive Open()
2828
if (settings.Path == null)
2929
return new EmptyMpqArchive();
3030
var files = Directory.EnumerateFiles(Path.Join(settings.Path, "Data"), "*.mpq", SearchOption.AllDirectories).ToList();
31-
return new MpqArchiveSet(files.Select(MpqArchive.Open).ToArray());
31+
List<IMpqArchive> archives = new();
32+
foreach (var file in files)
33+
{
34+
try
35+
{
36+
var archive = MpqArchive.Open(file);
37+
archives.Add(archive);
38+
}
39+
catch (Exception e)
40+
{
41+
throw new Exception("Couldn't open MPQ file " + Path.GetFileName(file) + ": " + e.Message, e);
42+
}
43+
}
44+
return new MpqArchiveSet(archives.ToArray());
3245
}
3346

3447
private class EmptyMpqArchive : IMpqArchive

WDE.MapRenderer/GameManager.cs

Lines changed: 49 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,32 +8,68 @@
88
using TheEngine.PhysicsSystem;
99
using WDE.Common.DBC;
1010
using WDE.Common.MPQ;
11+
using WDE.Common.Services.MessageBox;
1112
using WDE.MapRenderer.Managers;
13+
using WDE.Module.Attributes;
1214
using WDE.MpqReader;
1315
using WDE.MpqReader.Structures;
1416

1517
namespace WDE.MapRenderer
1618
{
19+
[AutoRegister]
1720
public class GameManager : IGame, IGameContext
1821
{
19-
private IMpqArchive mpq;
22+
private readonly IMpqService mpqService;
2023
private readonly IGameView gameView;
24+
private readonly IMessageBoxService messageBoxService;
2125
private readonly IDatabaseClientFileOpener databaseClientFileOpener;
2226
private AsyncMonitor monitor = new AsyncMonitor();
2327
private Engine engine;
2428
public event Action? OnInitialized;
29+
public event Action? OnFailedInitialize;
2530

26-
public GameManager(IMpqArchive mpq, IGameView gameView, IDatabaseClientFileOpener databaseClientFileOpener)
31+
public GameManager(IMpqService mpqService,
32+
IGameView gameView,
33+
IMessageBoxService messageBoxService,
34+
IDatabaseClientFileOpener databaseClientFileOpener)
2735
{
28-
this.mpq = mpq;
36+
this.mpqService = mpqService;
2937
this.gameView = gameView;
38+
this.messageBoxService = messageBoxService;
3039
this.databaseClientFileOpener = databaseClientFileOpener;
3140
UpdateLoop = new UpdateManager(this);
3241
}
3342

34-
public void Initialize(Engine engine)
43+
private bool TryOpenMpq(out IMpqArchive m)
44+
{
45+
try
46+
{
47+
m = mpqService.Open();
48+
return true;
49+
}
50+
catch (Exception e)
51+
{
52+
messageBoxService.ShowDialog(new MessageBoxFactory<bool>()
53+
.SetTitle("Invalid MPQ")
54+
.SetMainInstruction("Couldn't parse game MPQ.")
55+
.SetContent(e.Message + "\n\nAre you using modified game files?")
56+
.WithButton("Ok", false)
57+
.Build());
58+
m = null;
59+
return false;
60+
}
61+
}
62+
63+
public bool Initialize(Engine engine)
3564
{
3665
this.engine = engine;
66+
if (!TryOpenMpq(out mpq))
67+
{
68+
OnFailedInitialize?.Invoke();
69+
waitForInitialized.SetResult(false);
70+
waitForInitialized = new();
71+
return false;
72+
}
3773
coroutineManager = new();
3874
TimeManager = new TimeManager(this);
3975
ScreenSpaceSelector = new ScreenSpaceSelector(this);
@@ -51,16 +87,17 @@ public void Initialize(Engine engine)
5187

5288
OnInitialized?.Invoke();
5389
IsInitialized = true;
54-
waitForInitialized.SetResult();
90+
waitForInitialized.SetResult(true);
91+
return true;
5592
}
5693

5794
public void StartCoroutine(IEnumerator coroutine)
5895
{
5996
coroutineManager.Start(coroutine);
6097
}
6198

62-
private TaskCompletionSource waitForInitialized = new();
63-
public Task WaitForInitialized => waitForInitialized.Task;
99+
private TaskCompletionSource<bool> waitForInitialized = new();
100+
public Task<bool> WaitForInitialized => waitForInitialized.Task;
64101

65102
private Material? prevMaterial;
66103
public void Update(float delta)
@@ -108,7 +145,8 @@ public void SetMap(int mapId)
108145

109146
public void DoDispose()
110147
{
111-
RequestDispose?.Invoke();
148+
if (IsInitialized)
149+
RequestDispose?.Invoke();
112150
Debug.Assert(!IsInitialized);
113151
}
114152

@@ -125,6 +163,8 @@ public void DisposeGame()
125163
MdxManager.Dispose();
126164
TextureManager.Dispose();
127165
MeshManager.Dispose();
166+
mpq.Dispose();
167+
mpq = null!;
128168
coroutineManager = null!;
129169
TimeManager = null!;
130170
ScreenSpaceSelector = null!;
@@ -143,6 +183,7 @@ public void DisposeGame()
143183

144184
public Engine Engine => engine;
145185

186+
private IMpqArchive mpq;
146187
private CoroutineManager coroutineManager;
147188
public TimeManager TimeManager { get; private set; }
148189
public ScreenSpaceSelector ScreenSpaceSelector { get; private set; }

WDE.MapRenderer/GameView.cs

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,4 @@ public async Task<IGameContext> Open()
4545
return tool.Game;
4646
}
4747
}
48-
49-
public class GemView : OpenGlControlBase
50-
{
51-
protected override void OnOpenGlRender(GlInterface gl, int fb)
52-
{
53-
54-
}
55-
}
5648
}

WDE.MapRenderer/GameViewModel.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,13 +90,18 @@ public GameViewModel(IMpqService mpqService,
9090
IMessageBoxService messageBoxService,
9191
IDatabaseClientFileOpener databaseClientFileOpener,
9292
IGameView gameView,
93+
GameManager gameManager,
9394
GameViewSettings settings)
9495
{
9596
this.mpqService = mpqService;
9697
this.messageBoxService = messageBoxService;
9798
this.settings = settings;
9899
MapData = mapData;
99-
Game = new GameManager(mpqService.Open(), gameView, databaseClientFileOpener);
100+
Game = gameManager;
101+
Game.OnFailedInitialize += () =>
102+
{
103+
Dispatcher.UIThread.Post(() => Visibility = false, DispatcherPriority.Background);
104+
};
100105
Game.OnInitialized += () =>
101106
{
102107
Game.LightingManager.OverrideLighting = settings.OverrideLighting;

WDE.MapRenderer/Managers/IGameContext.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,6 @@ public interface IGameContext
2828
Map CurrentMap { get; }
2929
void SetMap(int id);
3030
void StartCoroutine(IEnumerator coroutine);
31-
Task WaitForInitialized { get; }
31+
Task<bool> WaitForInitialized { get; }
3232
}
3333
}

WDE.MapRenderer/Managers/MdxManager.cs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -204,14 +204,18 @@ public IEnumerator LoadM2Mesh(string path, TaskCompletionSource<MdxInstance?> re
204204
material.SetTexture("texture2", th2 ?? gameContext.TextureManager.EmptyTexture);
205205

206206
var trans = 1.0f;
207-
if (batch.colorIndex != -1)
207+
if (batch.colorIndex != -1 && m2.colors.Length < batch.colorIndex)
208208
{
209-
trans = m2.colors[batch.colorIndex].alpha.values[0][0].Value;
209+
if (m2.colors[batch.colorIndex].alpha.values.Length == 0 || m2.colors[batch.colorIndex].alpha.values[0].Length == 0)
210+
trans = 1;
211+
else
212+
trans = m2.colors[batch.colorIndex].alpha.values[0][0].Value;
210213
}
211214

212-
if (batch.transparencyIndex != -1)
215+
if (batch.transparencyIndex != -1 && m2.texture_weights.Length < batch.transparencyIndex)
213216
{
214-
trans *= m2.texture_weights[batch.transparencyIndex].weight.values[0][0].Value;
217+
if (m2.texture_weights[batch.transparencyIndex].weight.values.Length > 0 && m2.texture_weights[batch.transparencyIndex].weight.values[0].Length > 0)
218+
trans *= m2.texture_weights[batch.transparencyIndex].weight.values[0][0].Value;
215219
}
216220

217221
Vector4 mesh_color = new Vector4(1.0f, 1.0f, 1.0f, trans);

0 commit comments

Comments
 (0)