Adds ScopedOwner class, that disposes owned values when garbage collected.
This commit is contained in:
@@ -236,7 +236,10 @@ namespace PkmnLibSharp.Battling
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
Creaturelib.Generated.Battle.ClearBattle(Ptr);
|
||||
if (!IsDeleted)
|
||||
{
|
||||
Creaturelib.Generated.Battle.ClearBattle(Ptr);
|
||||
}
|
||||
base.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using PkmnLibSharp.Utilities;
|
||||
|
||||
namespace PkmnLibSharp.Battling
|
||||
{
|
||||
@@ -34,11 +35,12 @@ namespace PkmnLibSharp.Battling
|
||||
return this;
|
||||
}
|
||||
|
||||
public virtual Battle Build()
|
||||
public virtual ScopedOwner<Battle> Build()
|
||||
{
|
||||
// Use the milliseconds since epoch time as random seed if none is specified.
|
||||
_seed ??= (ulong) (DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalMilliseconds;
|
||||
return new Battle(_library, _parties.ToArray(), _canFlee, _numberOfSides, _pokemonPerSide, _seed.Value);
|
||||
return new ScopedOwner<Battle>(new Battle(_library, _parties.ToArray(), _canFlee, _numberOfSides,
|
||||
_pokemonPerSide, _seed.Value));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -67,6 +67,12 @@ namespace PkmnLibSharp.Battling
|
||||
private LearnedMove? _move;
|
||||
private Pokemon? _user;
|
||||
private ReadOnlyNativePtrArray<Pokemon>? _targets;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return base.ToString() + $": {Move.Move.Name}";
|
||||
}
|
||||
|
||||
protected override void DeletePtr()
|
||||
{
|
||||
}
|
||||
|
||||
@@ -41,7 +41,12 @@ namespace PkmnLibSharp.Battling
|
||||
(MoveLearnMethod) Creaturelib.Generated.LearnedAttack.GetLearnMethod(Ptr);
|
||||
|
||||
private MoveData? _move;
|
||||
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return base.ToString() + $": {Move.Name} PP: {RemainingUses}/{MaxUses}";
|
||||
}
|
||||
|
||||
protected override void DeletePtr()
|
||||
{
|
||||
LearnedAttack.Destruct(Ptr);
|
||||
|
||||
@@ -487,7 +487,12 @@ namespace PkmnLibSharp.Battling
|
||||
private Nature? _nature;
|
||||
private Battle? _battle;
|
||||
private BattleSide? _battleSide;
|
||||
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return base.ToString() + $": {Species.Name}-{Forme.Name} lv. {Level} HP: {CurrentHealth}/{MaxHealth}";
|
||||
}
|
||||
|
||||
protected override void DeletePtr()
|
||||
{
|
||||
Pkmnlib.Generated.Pokemon.Destruct(Ptr);
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using PkmnLibSharp.Library;
|
||||
using PkmnLibSharp.Library.Items;
|
||||
using PkmnLibSharp.Library.Moves;
|
||||
using PkmnLibSharp.Utilities;
|
||||
using Random = PkmnLibSharp.Utilities.Random;
|
||||
|
||||
namespace PkmnLibSharp.Battling
|
||||
@@ -150,7 +151,7 @@ namespace PkmnLibSharp.Battling
|
||||
|
||||
protected abstract TLearnedMove CreateLearnedMove(MoveData move, byte maxUses, MoveLearnMethod learnMethod);
|
||||
|
||||
public TPokemon Build(Random? random = null)
|
||||
public ScopedOwner<TPokemon> Build(Random? random = null)
|
||||
{
|
||||
if (!Library.StaticLibrary.SpeciesLibrary.TryGet(Species, out var species))
|
||||
{
|
||||
@@ -200,7 +201,7 @@ namespace PkmnLibSharp.Battling
|
||||
Gender = species.GetRandomGender(random);
|
||||
}
|
||||
|
||||
return Finalize(species, forme!, heldItem, moves, nature);
|
||||
return new ScopedOwner<TPokemon>(Finalize(species, forme!, heldItem, moves, nature));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -106,10 +106,12 @@ namespace PkmnLibSharp.Battling
|
||||
protected internal override void MarkAsDeleted()
|
||||
{
|
||||
base.MarkAsDeleted();
|
||||
foreach (var pokemon in Party)
|
||||
for (var index = 0; index < Party.Count; index++)
|
||||
{
|
||||
var pokemon = Party.GetDontInitialise(index);
|
||||
pokemon?.MarkAsDeleted();
|
||||
}
|
||||
|
||||
_cache = null;
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,11 @@ namespace PkmnLibSharp.Library.Items
|
||||
Initialize(p);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return base.ToString() + $": {Name}";
|
||||
}
|
||||
|
||||
protected override void DeletePtr()
|
||||
{
|
||||
Pkmnlib.Generated.Item.Destruct(Ptr);
|
||||
|
||||
@@ -44,6 +44,11 @@ namespace PkmnLibSharp.Library.Moves
|
||||
Initialize(ptr);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return base.ToString() + $": {Name}";
|
||||
}
|
||||
|
||||
protected override void DeletePtr()
|
||||
{
|
||||
AttackData.Destruct(Ptr);
|
||||
|
||||
@@ -143,6 +143,11 @@ namespace PkmnLibSharp.Library
|
||||
return SpeciesVariant.HasFlag(Ptr, flag.ToPtr()) == 1;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return base.ToString() + $": {Name}";
|
||||
}
|
||||
|
||||
protected internal override void MarkAsDeleted()
|
||||
{
|
||||
base.MarkAsDeleted();
|
||||
|
||||
@@ -146,6 +146,11 @@ namespace PkmnLibSharp.Library
|
||||
private ReadOnlyNativePtrArray<Forme>? _formes;
|
||||
private string[]? _eggGroups;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"(#{Ptr}) -> Species: {Name}";
|
||||
}
|
||||
|
||||
protected internal override void MarkAsDeleted()
|
||||
{
|
||||
base.MarkAsDeleted();
|
||||
|
||||
BIN
PkmnLibSharp/Native/Linux/libCreatureLib.so
(Stored with Git LFS)
BIN
PkmnLibSharp/Native/Linux/libCreatureLib.so
(Stored with Git LFS)
Binary file not shown.
BIN
PkmnLibSharp/Native/Linux/libpkmnLib.so
(Stored with Git LFS)
BIN
PkmnLibSharp/Native/Linux/libpkmnLib.so
(Stored with Git LFS)
Binary file not shown.
@@ -11,7 +11,7 @@ namespace PkmnLibSharp.Utilities
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_isDeleted)
|
||||
if (IsDeleted)
|
||||
{
|
||||
throw new Exception(
|
||||
"Pointer access after dispose detected. This is not legal, and will cause native exceptions.");
|
||||
@@ -21,7 +21,7 @@ namespace PkmnLibSharp.Utilities
|
||||
}
|
||||
}
|
||||
|
||||
private bool _isDeleted = false;
|
||||
protected bool IsDeleted { get; private set; } = false;
|
||||
|
||||
private static readonly ConcurrentDictionary<IntPtr, WeakReference<PointerWrapper>> Cached =
|
||||
new ConcurrentDictionary<IntPtr, WeakReference<PointerWrapper>>();
|
||||
@@ -48,7 +48,7 @@ namespace PkmnLibSharp.Utilities
|
||||
|
||||
~PointerWrapper()
|
||||
{
|
||||
if (!_isDeleted)
|
||||
if (!IsDeleted)
|
||||
Cached.TryRemove(Ptr, out _);
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ namespace PkmnLibSharp.Utilities
|
||||
|
||||
public virtual void Dispose()
|
||||
{
|
||||
if (_isDeleted)
|
||||
if (IsDeleted)
|
||||
return;
|
||||
DeletePtr();
|
||||
MarkAsDeleted();
|
||||
@@ -88,7 +88,7 @@ namespace PkmnLibSharp.Utilities
|
||||
|
||||
protected internal virtual void MarkAsDeleted()
|
||||
{
|
||||
_isDeleted = true;
|
||||
IsDeleted = true;
|
||||
Cached.TryRemove(_ptr, out _);
|
||||
}
|
||||
|
||||
|
||||
@@ -70,6 +70,25 @@ namespace PkmnLibSharp.Utilities
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
internal T? GetDontInitialise(int index)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
if (index >= Count)
|
||||
throw new IndexOutOfRangeException();
|
||||
// Where's your god now?
|
||||
// (We add the offset of the index to the pointer, then dereference the pointer pointer to get the actual pointer to the object we want.)
|
||||
var p = new IntPtr(*(void**)IntPtr.Add(_ptr, index * IntPtr.Size).ToPointer());
|
||||
if (p == IntPtr.Zero)
|
||||
return null;
|
||||
if (_cache[index]?.Ptr == p)
|
||||
return _cache[index]!;
|
||||
if (PointerWrapper.TryResolvePointer(p, out T? t))
|
||||
return t!;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public T? this[int index]
|
||||
{
|
||||
|
||||
75
PkmnLibSharp/Utilities/ScopedOwner.cs
Normal file
75
PkmnLibSharp/Utilities/ScopedOwner.cs
Normal file
@@ -0,0 +1,75 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace PkmnLibSharp.Utilities
|
||||
{
|
||||
/// <summary>
|
||||
/// Helper class to ensure an object is disposed when it goes out of scope.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public class ScopedOwner<T> : IDisposable where T : PointerWrapper
|
||||
{
|
||||
public T? Value { get; private set; }
|
||||
|
||||
public ScopedOwner(T value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public T? TakeOwnership()
|
||||
{
|
||||
var val = Value;
|
||||
Value = null;
|
||||
return val;
|
||||
}
|
||||
|
||||
~ScopedOwner()
|
||||
{
|
||||
if (Value != null)
|
||||
Value.Dispose();
|
||||
}
|
||||
|
||||
public static implicit operator T?(ScopedOwner<T> val) => val.Value;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
if (Value == null) return "null";
|
||||
return Value.ToString();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (Value != null)
|
||||
Value.Dispose();
|
||||
Value = null;
|
||||
}
|
||||
|
||||
protected bool Equals(ScopedOwner<T> other)
|
||||
{
|
||||
return EqualityComparer<T?>.Default.Equals(Value, other.Value);
|
||||
}
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
if (ReferenceEquals(null, obj)) return false;
|
||||
if (ReferenceEquals(this, obj)) return true;
|
||||
if (obj.GetType() != this.GetType()) return false;
|
||||
return Equals((ScopedOwner<T>) obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return EqualityComparer<T?>.Default.GetHashCode(Value);
|
||||
}
|
||||
|
||||
public static bool operator ==(ScopedOwner<T>? left, ScopedOwner<T>? right)
|
||||
{
|
||||
return Equals(left, right);
|
||||
}
|
||||
|
||||
public static bool operator !=(ScopedOwner<T>? left, ScopedOwner<T>? right)
|
||||
{
|
||||
return !Equals(left, right);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user