358 lines
12 KiB
C#
358 lines
12 KiB
C#
using System;
|
|
|
|
using Gamecraft.Wires;
|
|
using Svelto.DataStructures;
|
|
using Svelto.ECS;
|
|
using TechbloxModdingAPI.Common;
|
|
using TechbloxModdingAPI.Common.Engines;
|
|
using TechbloxModdingAPI.Engines;
|
|
using TechbloxModdingAPI.Utility;
|
|
using TechbloxModdingAPI.Utility.ECS;
|
|
|
|
namespace TechbloxModdingAPI.Blocks.Engines
|
|
{
|
|
/// <summary>
|
|
/// Engine which executes signal actions
|
|
/// </summary>
|
|
public class SignalEngine : IApiEngine, IFactoryEngine
|
|
{
|
|
public const float POSITIVE_HIGH = 1.0f;
|
|
public const float NEGATIVE_HIGH = -1.0f;
|
|
public const float HIGH = 1.0f;
|
|
public const float ZERO = 0.0f;
|
|
|
|
public string Name { get; } = "TechbloxModdingAPISignalGameEngine";
|
|
|
|
public EntitiesDB entitiesDB { set; private get; }
|
|
|
|
public IEntityFactory Factory { get; set; }
|
|
|
|
public bool isRemovable => false;
|
|
|
|
public bool IsInGame = false;
|
|
|
|
public void Dispose()
|
|
{
|
|
IsInGame = false;
|
|
}
|
|
|
|
public void Ready()
|
|
{
|
|
IsInGame = true;
|
|
}
|
|
|
|
// implementations for block wiring
|
|
|
|
public (WireEntityStruct Wire, EGID ID) CreateNewWire(EGID startBlock, byte startPort, EGID endBlock, byte endPort)
|
|
{
|
|
EGID wireEGID = new EGID(BuildModeWiresGroups.NewWireEntityId, BuildModeWiresGroups.WiresGroup.Group);
|
|
EntityInitializer wireInitializer = Factory.BuildEntity<WireEntityDescriptor>(wireEGID);
|
|
wireInitializer.Init(new WireEntityStruct
|
|
{
|
|
sourceBlockEGID = startBlock,
|
|
sourcePortUsage = startPort,
|
|
destinationBlockEGID = endBlock,
|
|
destinationPortUsage = endPort
|
|
});
|
|
return (wireInitializer.Get<WireEntityStruct>(), wireEGID);
|
|
}
|
|
|
|
public ref WireEntityStruct GetWire(EGID wire)
|
|
{
|
|
if (!entitiesDB.Exists<WireEntityStruct>(wire))
|
|
{
|
|
throw new WiringException($"Wire {wire} does not exist");
|
|
}
|
|
return ref entitiesDB.QueryEntity<WireEntityStruct>(wire);
|
|
}
|
|
|
|
public ref PortEntityStruct GetPort(EGID port)
|
|
{
|
|
if (!entitiesDB.Exists<PortEntityStruct>(port))
|
|
{
|
|
throw new WiringException($"Port {port} does not exist (yet?)");
|
|
}
|
|
return ref entitiesDB.QueryEntity<PortEntityStruct>(port);
|
|
}
|
|
|
|
public ref PortEntityStruct GetPortByOffset(BlockPortsStruct bps, byte portNumber, bool input)
|
|
{
|
|
ExclusiveGroup group = input
|
|
? NamedExclusiveGroup<BuildModeWiresGroups.InputPortsGroup>.Group
|
|
: NamedExclusiveGroup<BuildModeWiresGroups.OutputPortsGroup>.Group;
|
|
uint id = (input ? bps.firstInputID : bps.firstOutputID) + portNumber;
|
|
EGID egid = new EGID(id, group);
|
|
if (!entitiesDB.Exists<PortEntityStruct>(egid))
|
|
{
|
|
throw new WiringException("Port does not exist");
|
|
}
|
|
return ref entitiesDB.QueryEntity<PortEntityStruct>(egid);
|
|
}
|
|
|
|
public ref PortEntityStruct GetPortByOffset(Block block, byte portNumber, bool input)
|
|
{
|
|
var bps = entitiesDB.QueryEntityOptional<BlockPortsStruct>(block);
|
|
if (!bps)
|
|
{
|
|
throw new BlockException("Block does not exist");
|
|
}
|
|
return ref GetPortByOffset(bps, portNumber, input);
|
|
}
|
|
|
|
public ref T GetComponent<T>(EGID egid) where T : unmanaged, IEntityComponent
|
|
{
|
|
return ref entitiesDB.QueryEntity<T>(egid);
|
|
}
|
|
|
|
public bool Exists<T>(EGID egid) where T : struct, IEntityComponent
|
|
{
|
|
return entitiesDB.Exists<T>(egid);
|
|
}
|
|
|
|
public bool SetSignal(EGID blockID, float signal, out uint signalID, bool input = true)
|
|
{
|
|
signalID = GetSignalIDs(blockID, input)[0];
|
|
return SetSignal(signalID, signal);
|
|
}
|
|
|
|
public bool SetSignal(uint signalID, float signal, bool input = true)
|
|
{
|
|
var (array, count) = GetSignalStruct(signalID, out uint index, input);
|
|
if (count > 0) array[index].valueAsFloat = signal;
|
|
return false;
|
|
}
|
|
|
|
public float AddSignal(EGID blockID, float signal, out uint signalID, bool clamp = true, bool input = true)
|
|
{
|
|
signalID = GetSignalIDs(blockID, input)[0];
|
|
return AddSignal(signalID, signal, clamp, input);
|
|
}
|
|
|
|
public float AddSignal(uint signalID, float signal, bool clamp = true, bool input = true)
|
|
{
|
|
var (array, count) = GetSignalStruct(signalID, out uint index, input);
|
|
if (count > 0)
|
|
{
|
|
ref var channelData = ref array[index];
|
|
channelData.valueAsFloat += signal;
|
|
if (clamp)
|
|
{
|
|
if (channelData.valueAsFloat > POSITIVE_HIGH)
|
|
{
|
|
channelData.valueAsFloat = POSITIVE_HIGH;
|
|
}
|
|
else if (channelData.valueAsFloat < NEGATIVE_HIGH)
|
|
{
|
|
channelData.valueAsFloat = NEGATIVE_HIGH;
|
|
}
|
|
|
|
return channelData.valueAsFloat;
|
|
}
|
|
}
|
|
|
|
return signal;
|
|
}
|
|
|
|
public float GetSignal(EGID blockID, out uint signalID, bool input = true)
|
|
{
|
|
signalID = GetSignalIDs(blockID, input)[0];
|
|
return GetSignal(signalID, input);
|
|
}
|
|
|
|
public float GetSignal(uint signalID, bool input = true)
|
|
{
|
|
var (array, count) = GetSignalStruct(signalID, out uint index, input);
|
|
return count > 0 ? array[index].valueAsFloat : 0f;
|
|
}
|
|
|
|
public uint[] GetSignalIDs(EGID blockID, bool input = true)
|
|
{
|
|
ref BlockPortsStruct bps = ref entitiesDB.QueryEntity<BlockPortsStruct>(blockID);
|
|
uint[] signals;
|
|
if (input) {
|
|
signals = new uint[bps.inputCount];
|
|
for (uint i = 0u; i < bps.inputCount; i++)
|
|
{
|
|
signals[i] = bps.firstInputID + i;
|
|
}
|
|
} else {
|
|
signals = new uint[bps.outputCount];
|
|
for (uint i = 0u; i < bps.outputCount; i++)
|
|
{
|
|
signals[i] = bps.firstOutputID + i;
|
|
}
|
|
}
|
|
return signals;
|
|
}
|
|
|
|
public EGID[] GetSignalInputs(EGID blockID)
|
|
{
|
|
BlockPortsStruct ports = entitiesDB.QueryEntity<BlockPortsStruct>(blockID);
|
|
EGID[] inputs = new EGID[ports.inputCount];
|
|
for (uint i = 0; i < ports.inputCount; i++)
|
|
{
|
|
inputs[i] = new EGID(i + ports.firstInputID, NamedExclusiveGroup<BuildModeWiresGroups.InputPortsGroup>.Group);
|
|
}
|
|
return inputs;
|
|
}
|
|
|
|
public EGID[] GetSignalOutputs(EGID blockID)
|
|
{
|
|
BlockPortsStruct ports = entitiesDB.QueryEntity<BlockPortsStruct>(blockID);
|
|
EGID[] outputs = new EGID[ports.outputCount];
|
|
for (uint i = 0; i < ports.outputCount; i++)
|
|
{
|
|
outputs[i] = new EGID(i + ports.firstOutputID, NamedExclusiveGroup<BuildModeWiresGroups.OutputPortsGroup>.Group);
|
|
}
|
|
return outputs;
|
|
}
|
|
|
|
public OptionalRef<PortEntityStruct> MatchBlockIOToPort(Block block, byte portUsage, bool output)
|
|
{
|
|
return MatchBlockIOToPort(block.Id, portUsage, output);
|
|
}
|
|
|
|
public OptionalRef<PortEntityStruct> MatchBlockIOToPort(EGID block, byte portUsage, bool output)
|
|
{
|
|
if (!entitiesDB.Exists<BlockPortsStruct>(block))
|
|
return default;
|
|
var group = output
|
|
? NamedExclusiveGroup<BuildModeWiresGroups.OutputPortsGroup>.Group
|
|
: NamedExclusiveGroup<BuildModeWiresGroups.InputPortsGroup>.Group;
|
|
BlockPortsStruct ports = entitiesDB.QueryEntity<BlockPortsStruct>(block);
|
|
if (!entitiesDB.TryQueryMappedEntities<PortEntityStruct>(group, out var mapper))
|
|
return default;
|
|
for (uint i = 0; i < (output ? ports.outputCount : ports.inputCount); ++i)
|
|
{
|
|
uint entityID = (output ? ports.firstOutputID : ports.firstInputID) + i;
|
|
if (!mapper.TryGetArrayAndEntityIndex(entityID, out var index, out var array) ||
|
|
array[index].usage != portUsage) continue;
|
|
return new OptionalRef<PortEntityStruct>(array, index, new EGID(entityID, group));
|
|
}
|
|
|
|
return default;
|
|
}
|
|
|
|
public OptionalRef<WireEntityStruct> MatchPortToWire(PortEntityStruct port, EGID blockID, out EGID wireID)
|
|
{
|
|
var (wires, ids, count) = entitiesDB.QueryEntities<WireEntityStruct>(NamedExclusiveGroup<BuildModeWiresGroups.WiresGroup>.Group);
|
|
for (uint i = 0; i < count; i++)
|
|
{
|
|
if ((wires[i].destinationPortUsage == port.usage && wires[i].destinationBlockEGID == blockID)
|
|
|| (wires[i].sourcePortUsage == port.usage && wires[i].sourceBlockEGID == blockID))
|
|
{
|
|
wireID = new EGID(ids[i], BuildModeWiresGroups.WiresGroup.Group);
|
|
return new OptionalRef<WireEntityStruct>(wires, i);
|
|
}
|
|
}
|
|
|
|
wireID = default;
|
|
return default;
|
|
}
|
|
|
|
public EGID MatchBlocksToWire(EGID startBlock, EGID endBlock, byte startPort = byte.MaxValue, byte endPort = byte.MaxValue)
|
|
{
|
|
EGID[] startPorts;
|
|
if (startPort == byte.MaxValue)
|
|
{
|
|
// search all output ports on source block
|
|
startPorts = GetSignalOutputs(startBlock);
|
|
}
|
|
else
|
|
{
|
|
BlockPortsStruct ports = entitiesDB.QueryEntity<BlockPortsStruct>(startBlock);
|
|
startPorts = new EGID[] {new(ports.firstOutputID + startPort, NamedExclusiveGroup<BuildModeWiresGroups.OutputPortsGroup>.Group) };
|
|
}
|
|
|
|
EGID[] endPorts;
|
|
if (startPort == byte.MaxValue)
|
|
{
|
|
// search all input ports on destination block
|
|
endPorts = GetSignalInputs(endBlock);
|
|
}
|
|
else
|
|
{
|
|
BlockPortsStruct ports = entitiesDB.QueryEntity<BlockPortsStruct>(endBlock);
|
|
endPorts = new EGID[] {new(ports.firstInputID + endPort, NamedExclusiveGroup<BuildModeWiresGroups.InputPortsGroup>.Group) };
|
|
}
|
|
|
|
for (int endIndex = 0; endIndex < endPorts.Length; endIndex++)
|
|
{
|
|
PortEntityStruct endPES = entitiesDB.QueryEntity<PortEntityStruct>(endPorts[endIndex]);
|
|
for (int startIndex = 0; startIndex < startPorts.Length; startIndex++)
|
|
{
|
|
PortEntityStruct startPES = entitiesDB.QueryEntity<PortEntityStruct>(startPorts[startIndex]);
|
|
foreach (var wireOpt in entitiesDB.QueryEntitiesOptional<WireEntityStruct>(
|
|
NamedExclusiveGroup<BuildModeWiresGroups.WiresGroup>.Group))
|
|
{
|
|
var wire = wireOpt.Get();
|
|
if ((wire.destinationPortUsage == endPES.usage && wire.destinationBlockEGID == endBlock)
|
|
&& (wire.sourcePortUsage == startPES.usage && wire.sourceBlockEGID == startBlock))
|
|
{
|
|
return wireOpt.EGID;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return default;
|
|
}
|
|
|
|
public OptionalRef<ChannelDataStruct> GetChannelDataStruct(EGID portID)
|
|
{
|
|
var port = GetPort(portID);
|
|
var (channels, count) = entitiesDB.QueryEntities<ChannelDataStruct>(NamedExclusiveGroup<BuildModeWiresGroups.ChannelDataGroup>.Group);
|
|
return port.firstChannelIndexCachedInSim < count
|
|
? new OptionalRef<ChannelDataStruct>(channels, port.firstChannelIndexCachedInSim)
|
|
: default;
|
|
}
|
|
|
|
public EGID[] GetElectricBlocks()
|
|
{
|
|
var res = new FasterList<EGID>();
|
|
foreach (var ((coll, ids, count), _) in entitiesDB.QueryEntities<BlockPortsStruct>())
|
|
{
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
ref BlockPortsStruct s = ref coll[i];
|
|
//res.Add(s.ID); - TODO: Would need to search for the groups for each block
|
|
}
|
|
}
|
|
|
|
return res.ToArray();
|
|
}
|
|
|
|
public EGID[] WiredToInput(EGID block, byte port)
|
|
{
|
|
return entitiesDB
|
|
.QueryEntitiesOptional<WireEntityStruct>(NamedExclusiveGroup<BuildModeWiresGroups.WiresGroup>.Group)
|
|
.ToArray(wire => wire.ID,
|
|
wire => wire.Component.destinationPortUsage == port && wire.Component.destinationBlockEGID == block);
|
|
}
|
|
|
|
public EGID[] WiredToOutput(EGID block, byte port)
|
|
{
|
|
return entitiesDB
|
|
.QueryEntitiesOptional<WireEntityStruct>(NamedExclusiveGroup<BuildModeWiresGroups.WiresGroup>.Group)
|
|
.ToArray(wire => wire.ID,
|
|
wire => wire.Component.sourcePortUsage == port && wire.Component.sourceBlockEGID == block);
|
|
}
|
|
|
|
private EntityCollection<ChannelDataStruct> GetSignalStruct(uint signalID, out uint index, bool input = true)
|
|
{
|
|
ExclusiveGroup group = input
|
|
? NamedExclusiveGroup<BuildModeWiresGroups.InputPortsGroup>.Group
|
|
: NamedExclusiveGroup<BuildModeWiresGroups.OutputPortsGroup>.Group;
|
|
if (entitiesDB.Exists<PortEntityStruct>(signalID, group))
|
|
{
|
|
index = entitiesDB.QueryEntity<PortEntityStruct>(signalID, group).firstChannelIndexCachedInSim;
|
|
var channelData =
|
|
entitiesDB.QueryEntities<ChannelDataStruct>(NamedExclusiveGroup<BuildModeWiresGroups.ChannelDataGroup>.Group);
|
|
return channelData;
|
|
}
|
|
|
|
index = 0;
|
|
return default; //count: 0
|
|
}
|
|
}
|
|
}
|