Added block placement in sim and ECS object tracking
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 them
This commit is contained in:
parent
aa947eaba1
commit
8a03277d84
4 changed files with 180 additions and 10 deletions
|
@ -45,10 +45,12 @@ namespace TechbloxModdingAPI
|
||||||
/// <param name="position">The block's position - default block size is 0.2</param>
|
/// <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="autoWire">Whether the block should be auto-wired (if functional)</param>
|
||||||
/// <param name="player">The player who placed the block</param>
|
/// <param name="player">The player who placed the block</param>
|
||||||
|
/// <param name="force"></param>
|
||||||
/// <returns>The placed block or null if failed</returns>
|
/// <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 initializer = PlacementEngine.PlaceBlock(block, position, player, autoWire);
|
||||||
var egid = initializer.EGID;
|
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="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="autoWire">Whether the block should be auto-wired (if functional)</param>
|
||||||
/// <param name="player">The player who placed the block</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.");
|
throw new BlockException("Blocks can only be placed in build mode.");
|
||||||
var initializer = PlacementEngine.PlaceBlock(type, position, player, autoWire);
|
var initializer = PlacementEngine.PlaceBlock(type, position, player, autoWire);
|
||||||
Id = initializer.EGID;
|
Id = initializer.EGID;
|
||||||
|
|
|
@ -1,18 +1,45 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq.Expressions;
|
using System.Linq.Expressions;
|
||||||
using Svelto.DataStructures;
|
using Svelto.DataStructures;
|
||||||
using Svelto.ECS;
|
using Svelto.ECS;
|
||||||
using Svelto.ECS.Internal;
|
using Svelto.ECS.Internal;
|
||||||
using TechbloxModdingAPI.Blocks;
|
using TechbloxModdingAPI.Utility;
|
||||||
|
|
||||||
namespace TechbloxModdingAPI
|
namespace TechbloxModdingAPI
|
||||||
{
|
{
|
||||||
public abstract class EcsObjectBase
|
public abstract class EcsObjectBase
|
||||||
{
|
{
|
||||||
public abstract EGID Id { get; } //Abstract to support the 'place' Block constructor
|
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;
|
protected internal EcsInitData InitData;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Holds information needed to construct a component initializer
|
/// Holds information needed to construct a component initializer
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -22,7 +49,7 @@ namespace TechbloxModdingAPI
|
||||||
private EntityReference reference;
|
private EntityReference reference;
|
||||||
|
|
||||||
public static implicit operator EcsInitData(EntityInitializer initializer) => new EcsInitData
|
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 EntityInitializer Initializer(EGID id) => new EntityInitializer(id, group, reference);
|
||||||
public bool Valid => group != null;
|
public bool Valid => group != null;
|
||||||
|
@ -56,8 +83,10 @@ namespace TechbloxModdingAPI
|
||||||
returnExpr = Expression.ConvertChecked(memberExpr, invokeMethod.ReturnType);
|
returnExpr = Expression.ConvertChecked(memberExpr, invokeMethod.ReturnType);
|
||||||
|
|
||||||
var lambda =
|
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();
|
return lambda.Compile();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -91,7 +91,7 @@ namespace TechbloxModdingAPI.Tests
|
||||||
.Description("Place a block of aluminium at the given coordinates")
|
.Description("Place a block of aluminium at the given coordinates")
|
||||||
.Action((float x, float y, float z) =>
|
.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);
|
Logging.CommandLog("Block placed with type: " + block.Type);
|
||||||
})
|
})
|
||||||
.Build();
|
.Build();
|
||||||
|
|
138
TechbloxModdingAPI/Utility/WeakDictionary.cs
Normal file
138
TechbloxModdingAPI/Utility/WeakDictionary.cs
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue