ECS objects are stored in a newly created weak dictionary so that events can be called on them and possibly other things can be done with themtags/v2.1.0
@@ -45,10 +45,12 @@ namespace TechbloxModdingAPI | |||
/// <param name="position">The block's position - default block size is 0.2</param> | |||
/// <param name="autoWire">Whether the block should be auto-wired (if functional)</param> | |||
/// <param name="player">The player who placed the block</param> | |||
/// <param name="force"></param> | |||
/// <returns>The placed block or null if failed</returns> | |||
public static Block PlaceNew(BlockIDs block, float3 position, bool autoWire = false, Player player = null) | |||
public static Block PlaceNew(BlockIDs block, float3 position, bool autoWire = false, Player player = null, | |||
bool force = false) | |||
{ | |||
if (PlacementEngine.IsInGame && GameState.IsBuildMode()) | |||
if (PlacementEngine.IsInGame && (GameState.IsBuildMode() || force)) | |||
{ | |||
var initializer = PlacementEngine.PlaceBlock(block, position, player, autoWire); | |||
var egid = initializer.EGID; | |||
@@ -140,9 +142,10 @@ namespace TechbloxModdingAPI | |||
/// <param name="position">The block's position (a block is 0.2 wide in terms of position)</param> | |||
/// <param name="autoWire">Whether the block should be auto-wired (if functional)</param> | |||
/// <param name="player">The player who placed the block</param> | |||
public Block(BlockIDs type, float3 position, bool autoWire = false, Player player = null) | |||
/// <param name="force">Place even if not in build mode</param> | |||
public Block(BlockIDs type, float3 position, bool autoWire = false, Player player = null, bool force = false) | |||
{ | |||
if (!PlacementEngine.IsInGame || !GameState.IsBuildMode()) | |||
if (!PlacementEngine.IsInGame || !GameState.IsBuildMode() && !force) | |||
throw new BlockException("Blocks can only be placed in build mode."); | |||
var initializer = PlacementEngine.PlaceBlock(type, position, player, autoWire); | |||
Id = initializer.EGID; | |||
@@ -1,18 +1,45 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq.Expressions; | |||
using Svelto.DataStructures; | |||
using Svelto.ECS; | |||
using Svelto.ECS.Internal; | |||
using TechbloxModdingAPI.Blocks; | |||
using TechbloxModdingAPI.Utility; | |||
namespace TechbloxModdingAPI | |||
{ | |||
public abstract class EcsObjectBase | |||
{ | |||
public abstract EGID Id { get; } //Abstract to support the 'place' Block constructor | |||
private static readonly Dictionary<Type, WeakDictionary<EGID, EcsObjectBase>> _instances = | |||
new Dictionary<Type, WeakDictionary<EGID, EcsObjectBase>>(); | |||
private static readonly WeakDictionary<EGID, EcsObjectBase> _noInstance = | |||
new WeakDictionary<EGID, EcsObjectBase>(); | |||
internal static WeakDictionary<EGID, EcsObjectBase> GetInstances(Type type) | |||
{ | |||
return _instances.TryGetValue(type, out var dict) ? dict : null; | |||
} | |||
protected EcsObjectBase() | |||
{ | |||
if (!_instances.TryGetValue(GetType(), out var dict)) | |||
{ | |||
dict = new WeakDictionary<EGID, EcsObjectBase>(); | |||
_instances.Add(GetType(), dict); | |||
} | |||
// ReSharper disable once VirtualMemberCallInConstructor | |||
// The ID should not depend on the constructor | |||
dict.Add(Id, this); | |||
} | |||
#region ECS initializer stuff | |||
protected internal EcsInitData InitData; | |||
/// <summary> | |||
/// Holds information needed to construct a component initializer | |||
/// </summary> | |||
@@ -22,7 +49,7 @@ namespace TechbloxModdingAPI | |||
private EntityReference reference; | |||
public static implicit operator EcsInitData(EntityInitializer initializer) => new EcsInitData | |||
{group = GetInitGroup(initializer), reference = initializer.reference}; | |||
{ group = GetInitGroup(initializer), reference = initializer.reference }; | |||
public EntityInitializer Initializer(EGID id) => new EntityInitializer(id, group, reference); | |||
public bool Valid => group != null; | |||
@@ -56,8 +83,10 @@ namespace TechbloxModdingAPI | |||
returnExpr = Expression.ConvertChecked(memberExpr, invokeMethod.ReturnType); | |||
var lambda = | |||
Expression.Lambda<TDelegate>(returnExpr, $"Access{paramType.Name}_{memberName}", new[] {objParam}); | |||
Expression.Lambda<TDelegate>(returnExpr, $"Access{paramType.Name}_{memberName}", new[] { objParam }); | |||
return lambda.Compile(); | |||
} | |||
#endregion | |||
} | |||
} |
@@ -91,7 +91,7 @@ namespace TechbloxModdingAPI.Tests | |||
.Description("Place a block of aluminium at the given coordinates") | |||
.Action((float x, float y, float z) => | |||
{ | |||
var block = Block.PlaceNew(BlockIDs.Cube, new float3(x, y, z)); | |||
var block = Block.PlaceNew(BlockIDs.Cube, new float3(x, y, z), force: true); | |||
Logging.CommandLog("Block placed with type: " + block.Type); | |||
}) | |||
.Build(); | |||
@@ -0,0 +1,138 @@ | |||
using System; | |||
using System.Collections; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
namespace TechbloxModdingAPI.Utility | |||
{ | |||
public class WeakDictionary<TKey, TValue> : IDictionary<TKey, TValue> where TValue : class | |||
{ | |||
private Dictionary<TKey, WeakReference<TValue>> _dictionary = new Dictionary<TKey, WeakReference<TValue>>(); | |||
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() | |||
{ | |||
using var enumerator = _dictionary.GetEnumerator(); | |||
while (enumerator.MoveNext()) | |||
{ | |||
if (enumerator.Current.Value.TryGetTarget(out var value)) | |||
yield return new KeyValuePair<TKey, TValue>(enumerator.Current.Key, value); | |||
} | |||
} | |||
IEnumerator IEnumerable.GetEnumerator() | |||
{ | |||
return GetEnumerator(); | |||
} | |||
public void Add(KeyValuePair<TKey, TValue> item) | |||
{ | |||
Add(item.Key, item.Value); | |||
} | |||
public void Clear() | |||
{ | |||
_dictionary.Clear(); | |||
} | |||
public bool Contains(KeyValuePair<TKey, TValue> item) | |||
{ | |||
return TryGetValue(item.Key, out var value) && item.Value == value; | |||
} | |||
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) | |||
{ | |||
throw new System.NotImplementedException(); | |||
} | |||
public bool Remove(KeyValuePair<TKey, TValue> item) | |||
{ | |||
return Contains(item) && Remove(item.Key); | |||
} | |||
public int Count => _dictionary.Count; | |||
public bool IsReadOnly => false; | |||
public bool ContainsKey(TKey key) | |||
{ | |||
return _dictionary.ContainsKey(key); | |||
} | |||
public void Add(TKey key, TValue value) | |||
{ | |||
_dictionary.Add(key, new WeakReference<TValue>(value)); | |||
} | |||
public bool Remove(TKey key) | |||
{ | |||
return _dictionary.Remove(key); | |||
} | |||
public bool TryGetValue(TKey key, out TValue value) | |||
{ | |||
value = null; | |||
return _dictionary.TryGetValue(key, out var reference) && reference.TryGetTarget(out value); | |||
} | |||
public TValue this[TKey key] | |||
{ | |||
get => TryGetValue(key, out var value) | |||
? value | |||
: throw new KeyNotFoundException($"Key {key} not found in WeakDictionary."); | |||
set => _dictionary[key] = new WeakReference<TValue>(value); | |||
} | |||
public ICollection<TKey> Keys => _dictionary.Keys; | |||
public ICollection<TValue> Values => new ValueCollection(this); | |||
public class ValueCollection : ICollection<TValue>, IReadOnlyCollection<TValue> | |||
{ | |||
private WeakDictionary<TKey, TValue> _dictionary; | |||
internal ValueCollection(WeakDictionary<TKey, TValue> dictionary) | |||
{ | |||
_dictionary = dictionary; | |||
} | |||
public IEnumerator<TValue> GetEnumerator() | |||
{ | |||
using var enumerator = _dictionary.GetEnumerator(); | |||
while (enumerator.MoveNext()) | |||
{ | |||
yield return enumerator.Current.Value; | |||
} | |||
} | |||
IEnumerator IEnumerable.GetEnumerator() | |||
{ | |||
return GetEnumerator(); | |||
} | |||
public void Add(TValue item) | |||
{ | |||
throw new NotSupportedException("The value collection is read only."); | |||
} | |||
public void Clear() | |||
{ | |||
throw new NotSupportedException("The value collection is read only."); | |||
} | |||
public bool Contains(TValue item) | |||
{ | |||
return _dictionary.Any(kv => kv.Value == item); | |||
} | |||
public void CopyTo(TValue[] array, int arrayIndex) | |||
{ | |||
throw new NotImplementedException(); | |||
} | |||
public bool Remove(TValue item) | |||
{ | |||
throw new NotSupportedException("The value collection is read only."); | |||
} | |||
public int Count => _dictionary.Count; | |||
public bool IsReadOnly => true; | |||
} | |||
} | |||
} |