|
- using System;
- using Svelto.ECS;
- using Svelto.DataStructures;
- using Gamecraft.Wires;
-
- using GamecraftModdingAPI.Engines;
-
- namespace GamecraftModdingAPI.Blocks
- {
- /// <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; } = "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<WiresGroup>.Group);
- EntityComponentInitializer wireInitializer = Factory.BuildEntity<WireEntityDescriptor>(wireEGID);
- wireInitializer.Init(new WireEntityStruct
- {
- sourceBlockEGID = startBlock,
- sourcePortUsage = startPort,
- destinationBlockEGID = endBlock,
- destinationPortUsage = endPort,
- ID = wireEGID
- });
- return wireInitializer.Get<WireEntityStruct>();
- }
-
- 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<InputPortsGroup>.Group
- : NamedExclusiveGroup<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)
- {
- BlockPortsStruct bps = GetFromDbOrInitData<BlockPortsStruct>(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<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 = 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<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<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<OutputPortsGroup>.Group);
- }
- return outputs;
- }
-
- public EGID MatchBlockInputToPort(Block block, byte portUsage, out bool exists)
- {
- BlockPortsStruct ports = GetFromDbOrInitData<BlockPortsStruct>(block, block.Id, out exists);
- return new EGID(ports.firstInputID + portUsage, NamedExclusiveGroup<InputPortsGroup>.Group);
- }
-
- public EGID MatchBlockInputToPort(EGID block, byte portUsage, out bool exists)
- {
- if (!entitiesDB.Exists<BlockPortsStruct>(block))
- {
- exists = false;
- return default;
- }
- exists = true;
- BlockPortsStruct ports = entitiesDB.QueryEntity<BlockPortsStruct>(block);
- return new EGID(ports.firstInputID + portUsage, NamedExclusiveGroup<InputPortsGroup>.Group);
- }
-
- public EGID MatchBlockOutputToPort(Block block, byte portUsage, out bool exists)
- {
- BlockPortsStruct ports = GetFromDbOrInitData<BlockPortsStruct>(block, block.Id, out exists);
- return new EGID(ports.firstOutputID + portUsage, NamedExclusiveGroup<OutputPortsGroup>.Group);
- }
-
- public EGID MatchBlockOutputToPort(EGID block, byte portUsage, out bool exists)
- {
- if (!entitiesDB.Exists<BlockPortsStruct>(block))
- {
- exists = false;
- return default;
- }
- exists = true;
- BlockPortsStruct ports = entitiesDB.QueryEntity<BlockPortsStruct>(block);
- return new EGID(ports.firstOutputID + portUsage, NamedExclusiveGroup<OutputPortsGroup>.Group);
- }
-
- public ref WireEntityStruct MatchPortToWire(EGID portID, EGID blockID, out bool exists)
- {
- ref PortEntityStruct port = ref entitiesDB.QueryEntity<PortEntityStruct>(portID);
- var wires = entitiesDB.QueryEntities<WireEntityStruct>(NamedExclusiveGroup<WiresGroup>.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<BlockPortsStruct>(startBlock);
- startPorts = new EGID[] {new EGID(ports.firstOutputID + startPort, NamedExclusiveGroup<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 EGID(ports.firstInputID + endPort, NamedExclusiveGroup<InputPortsGroup>.Group) };
- }
-
- EntityCollection<WireEntityStruct> wires = entitiesDB.QueryEntities<WireEntityStruct>(NamedExclusiveGroup<WiresGroup>.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]);
- 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<PortEntityStruct>(portID);
- var channels = entitiesDB.QueryEntities<ChannelDataStruct>(NamedExclusiveGroup<ChannelDataGroup>.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<EGID>();
- foreach (var (coll, _) in entitiesDB.QueryEntities<BlockPortsStruct>())
- foreach (ref BlockPortsStruct s in coll)
- res.Add(s.ID);
- return res.ToArray();
- }
-
- public EGID[] WiredToInput(EGID block, byte port)
- {
- WireEntityStruct[] wireEntityStructs = Search(NamedExclusiveGroup<WiresGroup>.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<WiresGroup>.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<T>(ExclusiveGroup group, Func<T, bool> isMatch) where T : struct, IEntityComponent
- {
- FasterList<T> results = new FasterList<T>();
- EntityCollection<T> components = entitiesDB.QueryEntities<T>(group);
- for (uint i = 0; i < components.count; i++)
- {
- if (isMatch(components[i]))
- {
- results.Add(components[i]);
- }
- }
- return results.ToArray();
- }
-
- private ref T GetFromDbOrInitData<T>(Block block, EGID id, out bool exists) where T : unmanaged, IEntityComponent
- {
- T[] defRef = new T[1];
- if (entitiesDB.Exists<T>(id))
- {
- exists = true;
- return ref entitiesDB.QueryEntity<T>(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<T>())
- {
- exists = true;
- return ref initializer.Get<T>();
- }
- exists = false;
- return ref defRef[0];
- }
-
- private EntityCollection<ChannelDataStruct> GetSignalStruct(uint signalID, out uint index, bool input = true)
- {
- ExclusiveGroup group = input
- ? NamedExclusiveGroup<InputPortsGroup>.Group
- : NamedExclusiveGroup<OutputPortsGroup>.Group;
- if (entitiesDB.Exists<PortEntityStruct>(signalID, group))
- {
- index = entitiesDB.QueryEntity<PortEntityStruct>(signalID, group).anyChannelIndex;
- var channelData =
- entitiesDB.QueryEntities<ChannelDataStruct>(NamedExclusiveGroup<ChannelDataGroup>.Group);
- return channelData;
- }
-
- index = 0;
- return default; //count: 0
- }
- }
- }
|