diff --git a/PkmnLibSharp/Battling/Battle/Battle.cs b/PkmnLibSharp/Battling/Battle/Battle.cs index 96ccffa..34cec23 100644 --- a/PkmnLibSharp/Battling/Battle/Battle.cs +++ b/PkmnLibSharp/Battling/Battle/Battle.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Linq; using PkmnLibSharp.Battling.ChoiceTurn; using PkmnLibSharp.Battling.Events; @@ -173,8 +174,10 @@ namespace PkmnLibSharp.Battling return Creaturelib.Generated.Battle.HasVolatileScript(Ptr, key.ToPtr()) == 1; } + private List _listeners = new List(); public void RegisterEventListener(BattleEventListener listener) { + _listeners.Add(listener); Creaturelib.Generated.Battle.RegisterEventListener(Ptr, listener.FunctionPtr).Assert(); } diff --git a/PkmnLibSharp/Battling/Events/BattleEventListener.cs b/PkmnLibSharp/Battling/Events/BattleEventListener.cs index 5f74233..2ef9286 100644 --- a/PkmnLibSharp/Battling/Events/BattleEventListener.cs +++ b/PkmnLibSharp/Battling/Events/BattleEventListener.cs @@ -1,29 +1,42 @@ using System; using System.Runtime.InteropServices; +using System.Threading.Tasks; namespace PkmnLibSharp.Battling.Events { public class BattleEventListener { - public delegate void BattleEventDelegate(EventData evt); + public delegate Task BattleEventDelegate(EventData evt); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate void BattleEventPtrDelegate(IntPtr ptr); private readonly BattleEventDelegate _del; // ReSharper disable once PrivateFieldCanBeConvertedToLocalVariable private readonly BattleEventPtrDelegate _innerDel; internal readonly IntPtr FunctionPtr; + private Task _currentTask; public BattleEventListener(BattleEventDelegate del) { _del = del; _innerDel = OnEventPtr; - FunctionPtr = Marshal.GetFunctionPointerForDelegate(_innerDel); + FunctionPtr = Marshal.GetFunctionPointerForDelegate(_innerDel); } private void OnEventPtr(IntPtr ptr) + { + // If we're still registering another event, wait until that's done. This ensures events always arrive in + // the correct order. + _currentTask?.Wait(); + // We do however want to run the event handler async, so that the battle code can keep running up till its + // next event. + _currentTask = Task.Run(() => InternalRunner(ptr)); + } + + private async Task InternalRunner(IntPtr ptr) { var wrapped = WrapEventPtr(ptr); - _del.Invoke(wrapped); + await _del.Invoke(wrapped); } private static EventData WrapEventPtr(IntPtr ptr) diff --git a/PkmnLibSharp/Native/libCreatureLib.so b/PkmnLibSharp/Native/libCreatureLib.so index 328c1c2..ed781a2 100755 --- a/PkmnLibSharp/Native/libCreatureLib.so +++ b/PkmnLibSharp/Native/libCreatureLib.so @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ee35262a37b06529c8b23f0401418e72ca2bfce0d98f1b0e7e760ccaf6e02a15 -size 672528 +oid sha256:597b08c350461f91dab9746349463663ae86487bf6f85a058883f67067f739e0 +size 672552 diff --git a/PkmnLibSharpTests/Battling/BattleTests/BasicBattleTests.cs b/PkmnLibSharpTests/Battling/BattleTests/BasicBattleTests.cs index 8ba2bf4..cd273ed 100644 --- a/PkmnLibSharpTests/Battling/BattleTests/BasicBattleTests.cs +++ b/PkmnLibSharpTests/Battling/BattleTests/BasicBattleTests.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using NUnit.Framework; using PkmnLibSharp.Battling; using PkmnLibSharp.Battling.ChoiceTurn; @@ -94,6 +95,7 @@ namespace PkmnLibSharpTests.Battling.BattleTests battle.RegisterEventListener(new BattleEventListener(evt => { evts.Add(evt); + return Task.CompletedTask; })); battle.SwitchPokemon(0, 0, p1.GetAtIndex(0));