diff --git a/TechbloxModdingAPI/Block.cs b/TechbloxModdingAPI/Block.cs
index e64fab7..44bbc92 100644
--- a/TechbloxModdingAPI/Block.cs
+++ b/TechbloxModdingAPI/Block.cs
@@ -45,10 +45,12 @@ namespace TechbloxModdingAPI
/// The block's position - default block size is 0.2
/// Whether the block should be auto-wired (if functional)
/// The player who placed the block
+ ///
/// The placed block or null if failed
- 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
/// The block's position (a block is 0.2 wide in terms of position)
/// Whether the block should be auto-wired (if functional)
/// The player who placed the block
- public Block(BlockIDs type, float3 position, bool autoWire = false, Player player = null)
+ /// Place even if not in build mode
+ 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;
diff --git a/TechbloxModdingAPI/EcsObjectBase.cs b/TechbloxModdingAPI/EcsObjectBase.cs
index c96cf25..6a4db65 100644
--- a/TechbloxModdingAPI/EcsObjectBase.cs
+++ b/TechbloxModdingAPI/EcsObjectBase.cs
@@ -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> _instances =
+ new Dictionary>();
+
+ private static readonly WeakDictionary _noInstance =
+ new WeakDictionary();
+
+ internal static WeakDictionary GetInstances(Type type)
+ {
+ return _instances.TryGetValue(type, out var dict) ? dict : null;
+ }
+
+ protected EcsObjectBase()
+ {
+ if (!_instances.TryGetValue(GetType(), out var dict))
+ {
+ dict = new WeakDictionary();
+ _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;
-
+
///
/// Holds information needed to construct a component initializer
///
@@ -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(returnExpr, $"Access{paramType.Name}_{memberName}", new[] {objParam});
+ Expression.Lambda(returnExpr, $"Access{paramType.Name}_{memberName}", new[] { objParam });
return lambda.Compile();
}
+
+ #endregion
}
}
\ No newline at end of file
diff --git a/TechbloxModdingAPI/Tests/TechbloxModdingAPIPluginTest.cs b/TechbloxModdingAPI/Tests/TechbloxModdingAPIPluginTest.cs
index 32ef656..dd1d2b3 100644
--- a/TechbloxModdingAPI/Tests/TechbloxModdingAPIPluginTest.cs
+++ b/TechbloxModdingAPI/Tests/TechbloxModdingAPIPluginTest.cs
@@ -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();
diff --git a/TechbloxModdingAPI/Utility/WeakDictionary.cs b/TechbloxModdingAPI/Utility/WeakDictionary.cs
new file mode 100644
index 0000000..287b044
--- /dev/null
+++ b/TechbloxModdingAPI/Utility/WeakDictionary.cs
@@ -0,0 +1,138 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace TechbloxModdingAPI.Utility
+{
+ public class WeakDictionary : IDictionary where TValue : class
+ {
+ private Dictionary> _dictionary = new Dictionary>();
+
+ public IEnumerator> GetEnumerator()
+ {
+ using var enumerator = _dictionary.GetEnumerator();
+ while (enumerator.MoveNext())
+ {
+ if (enumerator.Current.Value.TryGetTarget(out var value))
+ yield return new KeyValuePair(enumerator.Current.Key, value);
+ }
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+
+ public void Add(KeyValuePair item)
+ {
+ Add(item.Key, item.Value);
+ }
+
+ public void Clear()
+ {
+ _dictionary.Clear();
+ }
+
+ public bool Contains(KeyValuePair item)
+ {
+ return TryGetValue(item.Key, out var value) && item.Value == value;
+ }
+
+ public void CopyTo(KeyValuePair[] array, int arrayIndex)
+ {
+ throw new System.NotImplementedException();
+ }
+
+ public bool Remove(KeyValuePair 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(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(value);
+ }
+
+ public ICollection Keys => _dictionary.Keys;
+ public ICollection Values => new ValueCollection(this);
+
+ public class ValueCollection : ICollection, IReadOnlyCollection
+ {
+ private WeakDictionary _dictionary;
+ internal ValueCollection(WeakDictionary dictionary)
+ {
+ _dictionary = dictionary;
+ }
+
+ public IEnumerator 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;
+ }
+ }
+}
\ No newline at end of file