Make event handler async, while preserving the order of incoming events.

This commit is contained in:
Deukhoofd 2020-08-07 20:28:38 +02:00
parent 90d5c2a16c
commit 1acd32b986
Signed by: Deukhoofd
GPG Key ID: F63E044490819F6F
4 changed files with 23 additions and 5 deletions

View File

@ -1,4 +1,5 @@
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using PkmnLibSharp.Battling.ChoiceTurn; using PkmnLibSharp.Battling.ChoiceTurn;
using PkmnLibSharp.Battling.Events; using PkmnLibSharp.Battling.Events;
@ -173,8 +174,10 @@ namespace PkmnLibSharp.Battling
return Creaturelib.Generated.Battle.HasVolatileScript(Ptr, key.ToPtr()) == 1; return Creaturelib.Generated.Battle.HasVolatileScript(Ptr, key.ToPtr()) == 1;
} }
private List<BattleEventListener> _listeners = new List<BattleEventListener>();
public void RegisterEventListener(BattleEventListener listener) public void RegisterEventListener(BattleEventListener listener)
{ {
_listeners.Add(listener);
Creaturelib.Generated.Battle.RegisterEventListener(Ptr, listener.FunctionPtr).Assert(); Creaturelib.Generated.Battle.RegisterEventListener(Ptr, listener.FunctionPtr).Assert();
} }

View File

@ -1,29 +1,42 @@
using System; using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading.Tasks;
namespace PkmnLibSharp.Battling.Events namespace PkmnLibSharp.Battling.Events
{ {
public class BattleEventListener 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 delegate void BattleEventPtrDelegate(IntPtr ptr);
private readonly BattleEventDelegate _del; private readonly BattleEventDelegate _del;
// ReSharper disable once PrivateFieldCanBeConvertedToLocalVariable // ReSharper disable once PrivateFieldCanBeConvertedToLocalVariable
private readonly BattleEventPtrDelegate _innerDel; private readonly BattleEventPtrDelegate _innerDel;
internal readonly IntPtr FunctionPtr; internal readonly IntPtr FunctionPtr;
private Task _currentTask;
public BattleEventListener(BattleEventDelegate del) public BattleEventListener(BattleEventDelegate del)
{ {
_del = del; _del = del;
_innerDel = OnEventPtr; _innerDel = OnEventPtr;
FunctionPtr = Marshal.GetFunctionPointerForDelegate(_innerDel); FunctionPtr = Marshal.GetFunctionPointerForDelegate<BattleEventPtrDelegate>(_innerDel);
} }
private void OnEventPtr(IntPtr ptr) 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); var wrapped = WrapEventPtr(ptr);
_del.Invoke(wrapped); await _del.Invoke(wrapped);
} }
private static EventData WrapEventPtr(IntPtr ptr) private static EventData WrapEventPtr(IntPtr ptr)

BIN
PkmnLibSharp/Native/libCreatureLib.so (Stored with Git LFS)

Binary file not shown.

View File

@ -1,5 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
using NUnit.Framework; using NUnit.Framework;
using PkmnLibSharp.Battling; using PkmnLibSharp.Battling;
using PkmnLibSharp.Battling.ChoiceTurn; using PkmnLibSharp.Battling.ChoiceTurn;
@ -94,6 +95,7 @@ namespace PkmnLibSharpTests.Battling.BattleTests
battle.RegisterEventListener(new BattleEventListener(evt => battle.RegisterEventListener(new BattleEventListener(evt =>
{ {
evts.Add(evt); evts.Add(evt);
return Task.CompletedTask;
})); }));
battle.SwitchPokemon(0, 0, p1.GetAtIndex(0)); battle.SwitchPokemon(0, 0, p1.GetAtIndex(0));