Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Execution leveling #228

Merged
merged 3 commits into from
Aug 28, 2023
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -92,13 +92,13 @@ private void PatchMethod(MethodDef method, MethodDef condition, CustomAttribute
instructions.Add(OpCodes.Call.ToInstruction(condition).WithSequencePoint(beginning));
instructions.Add(OpCodes.Ldc_I4_0.ToInstruction().WithSequencePoint(beginning));
instructions.Add(OpCodes.Ceq.ToInstruction().WithSequencePoint(beginning));
instructions.Add(OpCodes.Brfalse_S.ToInstruction(source.First()).WithSequencePoint(beginning));
instructions.Add(OpCodes.Brfalse.ToInstruction(source.First()).WithSequencePoint(beginning));

Instruction? retInstruction = null;
if (source.Last().OpCode.Code != Code.Ret)
retInstruction = OpCodes.Ret.ToInstruction().WithSequencePoint(beginning);

instructions.Add(OpCodes.Br_S.ToInstruction(retInstruction ?? source.Last()).WithSequencePoint(beginning));
instructions.Add(OpCodes.Br.ToInstruction(retInstruction ?? source.Last()).WithSequencePoint(beginning));
instructions.AddRange(source);
if (retInstruction != null)
instructions.Add(retInstruction);
Expand All @@ -109,6 +109,8 @@ private void PatchMethod(MethodDef method, MethodDef condition, CustomAttribute
method.Body.ExceptionHandlers,
method.Body.Variables
);

method.Body.OptimizeBranches();
}

private void AssertTargetMethodContract(MethodDef method) {
Expand Down
30 changes: 16 additions & 14 deletions MultiplayerMod.Test/ModRuntime/ExecutionContextManagerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,58 +15,60 @@ public class ExecutionContextManagerTests {
public static void TearDown() => UnityTestRuntime.Uninstall();

[Test]
public void MainContextChange() {
public void RootContextChange() {
var container = CreateContainer();
var manager = container.Get<ExecutionContextManager>();

Assert.AreEqual(expected: ExecutionLevel.System, actual: manager.EffectiveContext.Level);
manager.MainContext = new ExecutionContext(ExecutionLevel.Runtime);
Assert.AreEqual(expected: ExecutionLevel.Runtime, actual: manager.EffectiveContext.Level);
Assert.AreEqual(expected: ExecutionLevel.System, actual: manager.Context.Level);
manager.Replace(new ExecutionContext(ExecutionLevel.Gameplay));
Assert.AreEqual(expected: ExecutionLevel.Gameplay, actual: manager.Context.Level);
}

[Test]
public void BasicOverride() {
var container = CreateContainer();
var manager = container.Get<ExecutionContextManager>();
manager.UsingLevel(
var levelManager = container.Get<ExecutionLevelManager>();
levelManager.RunUsingLevel(
ExecutionLevel.Command,
() => { Assert.AreEqual(expected: ExecutionLevel.Command, actual: manager.EffectiveContext.Level); }
() => { Assert.AreEqual(expected: ExecutionLevel.Command, actual: manager.Context.Level); }
);
Assert.AreEqual(expected: ExecutionLevel.System, actual: manager.EffectiveContext.Level);
Assert.AreEqual(expected: ExecutionLevel.System, actual: manager.Context.Level);
}

[Test]
public void RestoreEmptyContextException() {
var container = CreateContainer();
var manager = container.Get<ExecutionContextManager>();
Assert.Throws<ExecutionContextIntegrityFailureException>(() => manager.RestoreContext());
Assert.Throws<ExecutionContextIntegrityFailureException>(() => manager.Pop());
}

[Test]
public void ManualGuardedOverrideStaleContextException() {
var container = CreateContainer();
var manager = container.Get<ExecutionContextManager>();
manager.OverrideContext(new ExecutionContext(ExecutionLevel.Runtime));
manager.Push(new ExecutionContext(ExecutionLevel.Gameplay));
UnityTestRuntime.NextFrame();
Assert.Throws<ExecutionContextIntegrityFailureException>(
() => manager.OverrideContext(new ExecutionContext(ExecutionLevel.Runtime))
() => manager.Push(new ExecutionContext(ExecutionLevel.Gameplay))
);
}

[Test]
public void ManualGuardedOverride() {
var container = CreateContainer();
var manager = container.Get<ExecutionContextManager>();
manager.OverrideContext(new ExecutionContext(ExecutionLevel.Runtime));
Assert.AreEqual(expected: ExecutionLevel.Runtime, actual: manager.EffectiveContext.Level);
manager.RestoreContext();
manager.Push(new ExecutionContext(ExecutionLevel.Gameplay));
Assert.AreEqual(expected: ExecutionLevel.Gameplay, actual: manager.Context.Level);
manager.Pop();
UnityTestRuntime.NextFrame();
Assert.AreEqual(expected: ExecutionLevel.System, actual: manager.EffectiveContext.Level);
Assert.AreEqual(expected: ExecutionLevel.System, actual: manager.Context.Level);
}

private static DependencyContainer CreateContainer() {
var container = new DependencyContainer();
container.Register<ExecutionContextManager>();
container.Register<ExecutionLevelManager>();
return container;
}

Expand Down
54 changes: 54 additions & 0 deletions MultiplayerMod.Test/ModRuntime/ExecutionLevelTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
using MultiplayerMod.ModRuntime;
using MultiplayerMod.ModRuntime.Context;
using MultiplayerMod.ModRuntime.StaticCompatibility;
using MultiplayerMod.Test.Environment.Unity;
using NUnit.Framework;

namespace MultiplayerMod.Test.ModRuntime;

[TestFixture]
public class ExecutionLevelTests {

[OneTimeSetUp]
public static void SetUp() => UnityTestRuntime.Install();

[OneTimeTearDown]
public static void TearDown() => UnityTestRuntime.Uninstall();

[Test]
public void RequiredLevelHigherThanCurrent() {
var runtime = new Runtime();
var manager = runtime.Dependencies.Get<ExecutionContextManager>();
manager.Replace(new ExecutionContext(ExecutionLevel.Multiplayer));

var executed = false;
Execution.RunIfPossible(ExecutionLevel.Gameplay, () => executed = true);

Assert.IsFalse(executed);
}

[Test]
public void RequiredLevelEqualsToCurrent() {
var runtime = new Runtime();
var manager = runtime.Dependencies.Get<ExecutionContextManager>();
manager.Replace(new ExecutionContext(ExecutionLevel.Gameplay));

var executed = false;
Execution.RunIfPossible(ExecutionLevel.Gameplay, () => executed = true);

Assert.IsTrue(executed);
}

[Test]
public void RequiredLevelLowerThanCurrent() {
var runtime = new Runtime();
var manager = runtime.Dependencies.Get<ExecutionContextManager>();
manager.Replace(new ExecutionContext(ExecutionLevel.Gameplay));

var executed = false;
Execution.RunIfPossible(ExecutionLevel.Multiplayer, () => executed = true);

Assert.IsTrue(executed);
}

}
36 changes: 0 additions & 36 deletions MultiplayerMod/Core/Patch/Context/PatchContext.cs

This file was deleted.

This file was deleted.

50 changes: 0 additions & 50 deletions MultiplayerMod/Core/Patch/Context/PatchContextGuard.cs

This file was deleted.

17 changes: 0 additions & 17 deletions MultiplayerMod/Core/Patch/PatchControl.cs

This file was deleted.

8 changes: 7 additions & 1 deletion MultiplayerMod/Directory.Build.targets
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project>

<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.targets', '$(MSBuildThisFileDirectory)../'))" />

<Target Name="SetVersionAttributes" BeforeTargets="InitializeSourceControlInformation">
Expand Down Expand Up @@ -67,7 +68,7 @@
</Target>

<Target Name="InstallMod"
AfterTargets="Build"
AfterTargets="AfterBuild"
Condition="'$(Configuration)' == 'Debug'"
DependsOnTargets="CopyModDescription; GenerateModInfo"
>
Expand Down Expand Up @@ -139,4 +140,9 @@
</ItemGroup>
<ExposeAssembly SourceAssemblies="@(HarmonyAssembly)" Rules="@(HarmonyExposureRules)"/>
</Target>

<Target Name="ProcessAttributes" DependsOnTargets="BuildAttributeProcessor" AfterTargets="PostBuildEvent">
<ProcessAttributes AssemblyPath="$(TargetPath)"/>
</Target>

</Project>
9 changes: 6 additions & 3 deletions MultiplayerMod/Game/Configuration/MethodPatchesDisabler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
using System.Reflection;
using HarmonyLib;
using MultiplayerMod.Core.Patch;
using MultiplayerMod.Core.Patch.Context;
using MultiplayerMod.ModRuntime.Context;
using MultiplayerMod.ModRuntime.StaticCompatibility;

namespace MultiplayerMod.Game.Configuration;

Expand All @@ -22,10 +23,12 @@ public static class MethodPatchesDisabler {

// ReSharper disable once UnusedMember.Local
[HarmonyPrefix]
private static void BeforeMethod() => PatchContext.Enter(PatchControl.DisablePatches);
[RequireExecutionLevel(ExecutionLevel.Multiplayer)]
private static void BeforeMethod() => Execution.EnterLevelSection(ExecutionLevel.Component);

// ReSharper disable once UnusedMember.Local
[HarmonyPostfix]
private static void AfterMethod() => PatchContext.Leave();
[RequireExecutionLevel(ExecutionLevel.Component)]
private static void AfterMethod() => Execution.LeaveLevelSection();

}
Loading