using System;
using Svelto.ECS;
using Svelto.DataStructures;
using Gamecraft.Wires;
using GamecraftModdingAPI.Engines;
namespace GamecraftModdingAPI.Blocks
{
///
/// Engine which executes signal actions
///
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; } = "GamecraftModdingAPISignalGameEngine";
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 CreateNewWire(EGID startBlock, byte startPort, EGID endBlock, byte endPort)
{
EGID wireEGID = new EGID(WiresExclusiveGroups.NewWireEntityId, NamedExclusiveGroup.Group);
EntityComponentInitializer wireInitializer = Factory.BuildEntity(wireEGID);
wireInitializer.Init(new WireEntityStruct
{
sourceBlockEGID = startBlock,
sourcePortUsage = startPort,
destinationBlockEGID = endBlock,
destinationPortUsage = endPort,
ID = wireEGID
});
return wireInitializer.Get();
}
public ref WireEntityStruct GetWire(EGID wire)
{
if (!entitiesDB.Exists(wire))
{
throw new WiringException($"Wire {wire} does not exist");
}
return ref entitiesDB.QueryEntity(wire);
}
public ref PortEntityStruct GetPort(EGID port)
{
if (!entitiesDB.Exists(port))
{
throw new WiringException($"Port {port} does not exist (yet?)");
}
return ref entitiesDB.QueryEntity(port);
}
public ref PortEntityStruct GetPortByOffset(BlockPortsStruct bps, byte portNumber, bool input)
{
ExclusiveGroup group = input
? NamedExclusiveGroup.Group
: NamedExclusiveGroup.Group;
uint id = (input ? bps.firstInputID : bps.firstOutputID) + portNumber;
EGID egid = new EGID(id, group);
if (!entitiesDB.Exists(egid))
{
throw new WiringException("Port does not exist");
}
return ref entitiesDB.QueryEntity(egid);
}
public ref PortEntityStruct GetPortByOffset(Block block, byte portNumber, bool input)
{
BlockPortsStruct bps = GetFromDbOrInitData(block, block.Id, out bool exists);
if (!exists)
{
throw new BlockException("Block does not exist");
}
return ref GetPortByOffset(bps, portNumber, input);
}
public ref T GetComponent(EGID egid) where T : struct, IEntityComponent
{
return ref entitiesDB.QueryEntity(egid);
}
public bool Exists(EGID egid) where T : struct, IEntityComponent
{
return entitiesDB.Exists(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 = GetSignalStruct(signalID, out uint index, input);
if (array.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 = GetSignalStruct(signalID, out uint index, input);
if (array.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 = GetSignalStruct(signalID, out uint index, input);
return array.count > 0 ? array[index].valueAsFloat : 0f;
}
public uint[] GetSignalIDs(EGID blockID, bool input = true)
{
ref BlockPortsStruct bps = ref entitiesDB.QueryEntity(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(blockID);
EGID[] inputs = new EGID[ports.inputCount];
for (uint i = 0; i < ports.inputCount; i++)
{
inputs[i] = new EGID(i + ports.firstInputID, NamedExclusiveGroup.Group);
}
return inputs;
}
public EGID[] GetSignalOutputs(EGID blockID)
{
BlockPortsStruct ports = entitiesDB.QueryEntity(blockID);
EGID[] outputs = new EGID[ports.outputCount];
for (uint i = 0; i < ports.outputCount; i++)
{
outputs[i] = new EGID(i + ports.firstOutputID, NamedExclusiveGroup.Group);
}
return outputs;
}
public EGID MatchBlockInputToPort(Block block, byte portUsage, out bool exists)
{
BlockPortsStruct ports = GetFromDbOrInitData(block, block.Id, out exists);
return new EGID(ports.firstInputID + portUsage, NamedExclusiveGroup.Group);
}
public EGID MatchBlockInputToPort(EGID block, byte portUsage, out bool exists)
{
if (!entitiesDB.Exists(block))
{
exists = false;
return default;
}
exists = true;
BlockPortsStruct ports = entitiesDB.QueryEntity(block);
return new EGID(ports.firstInputID + portUsage, NamedExclusiveGroup.Group);
}
public EGID MatchBlockOutputToPort(Block block, byte portUsage, out bool exists)
{
BlockPortsStruct ports = GetFromDbOrInitData(block, block.Id, out exists);
return new EGID(ports.firstOutputID + portUsage, NamedExclusiveGroup.Group);
}
public EGID MatchBlockOutputToPort(EGID block, byte portUsage, out bool exists)
{
if (!entitiesDB.Exists(block))
{
exists = false;
return default;
}
exists = true;
BlockPortsStruct ports = entitiesDB.QueryEntity(block);
return new EGID(ports.firstOutputID + portUsage, NamedExclusiveGroup.Group);
}
public ref WireEntityStruct MatchPortToWire(EGID portID, EGID blockID, out bool exists)
{
ref PortEntityStruct port = ref entitiesDB.QueryEntity(portID);
var wires = entitiesDB.QueryEntities(NamedExclusiveGroup.Group);
for (uint i = 0; i < wires.count; i++)
{
if ((wires[i].destinationPortUsage == port.usage && wires[i].destinationBlockEGID == blockID)
|| (wires[i].sourcePortUsage == port.usage && wires[i].sourceBlockEGID == blockID))
{
exists = true;
return ref wires[i];
}
}
exists = false;
WireEntityStruct[] defRef = new WireEntityStruct[1];
return ref defRef[0];
}
public ref WireEntityStruct MatchBlocksToWire(EGID startBlock, EGID endBlock, out bool exists, 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(startBlock);
startPorts = new EGID[] {new EGID(ports.firstOutputID + startPort, NamedExclusiveGroup.Group) };
}
EGID[] endPorts;
if (startPort == byte.MaxValue)
{
// search all input ports on destination block
endPorts = GetSignalInputs(endBlock);
}
else
{
BlockPortsStruct ports = entitiesDB.QueryEntity(endBlock);
endPorts = new EGID[] {new EGID(ports.firstInputID + endPort, NamedExclusiveGroup.Group) };
}
EntityCollection wires = entitiesDB.QueryEntities(NamedExclusiveGroup.Group);
for (int endIndex = 0; endIndex < endPorts.Length; endIndex++)
{
PortEntityStruct endPES = entitiesDB.QueryEntity(endPorts[endIndex]);
for (int startIndex = 0; startIndex < startPorts.Length; startIndex++)
{
PortEntityStruct startPES = entitiesDB.QueryEntity(startPorts[startIndex]);
for (int w = 0; w < wires.count; w++)
{
if ((wires[w].destinationPortUsage == endPES.usage && wires[w].destinationBlockEGID == endBlock)
&& (wires[w].sourcePortUsage == startPES.usage && wires[w].sourceBlockEGID == startBlock))
{
exists = true;
return ref wires[w];
}
}
}
}
exists = false;
WireEntityStruct[] defRef = new WireEntityStruct[1];
return ref defRef[0];
}
public ref ChannelDataStruct GetChannelDataStruct(EGID portID, out bool exists)
{
ref PortEntityStruct port = ref entitiesDB.QueryEntity(portID);
var channels = entitiesDB.QueryEntities(NamedExclusiveGroup.Group);
if (port.firstChannelIndexCachedInSim < channels.count)
{
exists = true;
return ref channels[port.firstChannelIndexCachedInSim];
}
exists = false;
ChannelDataStruct[] defRef = new ChannelDataStruct[1];
return ref defRef[0];
}
public EGID[] GetElectricBlocks()
{
var res = new FasterList();
foreach (var (coll, _) in entitiesDB.QueryEntities())
foreach (ref BlockPortsStruct s in coll)
res.Add(s.ID);
return res.ToArray();
}
public EGID[] WiredToInput(EGID block, byte port)
{
WireEntityStruct[] wireEntityStructs = Search(NamedExclusiveGroup.Group,
(WireEntityStruct wes) => wes.destinationPortUsage == port && wes.destinationBlockEGID == block);
EGID[] result = new EGID[wireEntityStructs.Length];
for (uint i = 0; i < wireEntityStructs.Length; i++)
{
result[i] = wireEntityStructs[i].ID;
}
return result;
}
public EGID[] WiredToOutput(EGID block, byte port)
{
WireEntityStruct[] wireEntityStructs = Search(NamedExclusiveGroup.Group,
(WireEntityStruct wes) => wes.sourcePortUsage == port && wes.sourceBlockEGID == block);
EGID[] result = new EGID[wireEntityStructs.Length];
for (uint i = 0; i < wireEntityStructs.Length; i++)
{
result[i] = wireEntityStructs[i].ID;
}
return result;
}
private T[] Search(ExclusiveGroup group, Func isMatch) where T : struct, IEntityComponent
{
FasterList results = new FasterList();
EntityCollection components = entitiesDB.QueryEntities(group);
for (uint i = 0; i < components.count; i++)
{
if (isMatch(components[i]))
{
results.Add(components[i]);
}
}
return results.ToArray();
}
private ref T GetFromDbOrInitData(Block block, EGID id, out bool exists) where T : struct, IEntityComponent
{
T[] defRef = new T[1];
if (entitiesDB.Exists(id))
{
exists = true;
return ref entitiesDB.QueryEntity(id);
}
if (block == null || block.InitData.Group == null)
{
exists = false;
return ref defRef[0];
}
EntityComponentInitializer initializer = new EntityComponentInitializer(block.Id, block.InitData.Group);
if (initializer.Has())
{
exists = true;
return ref initializer.Get();
}
exists = false;
return ref defRef[0];
}
private EntityCollection GetSignalStruct(uint signalID, out uint index, bool input = true)
{
ExclusiveGroup group = input
? NamedExclusiveGroup.Group
: NamedExclusiveGroup.Group;
if (entitiesDB.Exists(signalID, group))
{
index = entitiesDB.QueryEntity(signalID, group).anyChannelIndex;
var channelData =
entitiesDB.QueryEntities(NamedExclusiveGroup.Group);
return channelData;
}
index = 0;
return default; //count: 0
}
}
}