A stable modding interface between Techblox and mods https://mod.exmods.org/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

357 lines
12KB

  1. using System;
  2. using Gamecraft.Wires;
  3. using Svelto.DataStructures;
  4. using Svelto.ECS;
  5. using TechbloxModdingAPI.Engines;
  6. using TechbloxModdingAPI.Utility;
  7. namespace TechbloxModdingAPI.Blocks.Engines
  8. {
  9. /// <summary>
  10. /// Engine which executes signal actions
  11. /// </summary>
  12. public class SignalEngine : IApiEngine, IFactoryEngine
  13. {
  14. public const float POSITIVE_HIGH = 1.0f;
  15. public const float NEGATIVE_HIGH = -1.0f;
  16. public const float HIGH = 1.0f;
  17. public const float ZERO = 0.0f;
  18. public string Name { get; } = "TechbloxModdingAPISignalGameEngine";
  19. public EntitiesDB entitiesDB { set; private get; }
  20. public IEntityFactory Factory { get; set; }
  21. public bool isRemovable => false;
  22. public bool IsInGame = false;
  23. public void Dispose()
  24. {
  25. IsInGame = false;
  26. }
  27. public void Ready()
  28. {
  29. IsInGame = true;
  30. }
  31. // implementations for block wiring
  32. public (WireEntityStruct Wire, EGID ID) CreateNewWire(EGID startBlock, byte startPort, EGID endBlock, byte endPort)
  33. {
  34. EGID wireEGID = new EGID(BuildModeWiresGroups.NewWireEntityId, BuildModeWiresGroups.WiresGroup.Group);
  35. EntityInitializer wireInitializer = Factory.BuildEntity<WireEntityDescriptor>(wireEGID);
  36. wireInitializer.Init(new WireEntityStruct
  37. {
  38. sourceBlockEGID = startBlock,
  39. sourcePortUsage = startPort,
  40. destinationBlockEGID = endBlock,
  41. destinationPortUsage = endPort
  42. });
  43. return (wireInitializer.Get<WireEntityStruct>(), wireEGID);
  44. }
  45. public ref WireEntityStruct GetWire(EGID wire)
  46. {
  47. if (!entitiesDB.Exists<WireEntityStruct>(wire))
  48. {
  49. throw new WiringException($"Wire {wire} does not exist");
  50. }
  51. return ref entitiesDB.QueryEntity<WireEntityStruct>(wire);
  52. }
  53. public ref PortEntityStruct GetPort(EGID port)
  54. {
  55. if (!entitiesDB.Exists<PortEntityStruct>(port))
  56. {
  57. throw new WiringException($"Port {port} does not exist (yet?)");
  58. }
  59. return ref entitiesDB.QueryEntity<PortEntityStruct>(port);
  60. }
  61. public ref PortEntityStruct GetPortByOffset(BlockPortsStruct bps, byte portNumber, bool input)
  62. {
  63. ExclusiveGroup group = input
  64. ? NamedExclusiveGroup<BuildModeWiresGroups.InputPortsGroup>.Group
  65. : NamedExclusiveGroup<BuildModeWiresGroups.OutputPortsGroup>.Group;
  66. uint id = (input ? bps.firstInputID : bps.firstOutputID) + portNumber;
  67. EGID egid = new EGID(id, group);
  68. if (!entitiesDB.Exists<PortEntityStruct>(egid))
  69. {
  70. throw new WiringException("Port does not exist");
  71. }
  72. return ref entitiesDB.QueryEntity<PortEntityStruct>(egid);
  73. }
  74. public ref PortEntityStruct GetPortByOffset(Block block, byte portNumber, bool input)
  75. {
  76. var bps = entitiesDB.QueryEntityOptional<BlockPortsStruct>(block);
  77. if (!bps)
  78. {
  79. throw new BlockException("Block does not exist");
  80. }
  81. return ref GetPortByOffset(bps, portNumber, input);
  82. }
  83. public ref T GetComponent<T>(EGID egid) where T : unmanaged, IEntityComponent
  84. {
  85. return ref entitiesDB.QueryEntity<T>(egid);
  86. }
  87. public bool Exists<T>(EGID egid) where T : struct, IEntityComponent
  88. {
  89. return entitiesDB.Exists<T>(egid);
  90. }
  91. public bool SetSignal(EGID blockID, float signal, out uint signalID, bool input = true)
  92. {
  93. signalID = GetSignalIDs(blockID, input)[0];
  94. return SetSignal(signalID, signal);
  95. }
  96. public bool SetSignal(uint signalID, float signal, bool input = true)
  97. {
  98. var (array, count) = GetSignalStruct(signalID, out uint index, input);
  99. if (count > 0) array[index].valueAsFloat = signal;
  100. return false;
  101. }
  102. public float AddSignal(EGID blockID, float signal, out uint signalID, bool clamp = true, bool input = true)
  103. {
  104. signalID = GetSignalIDs(blockID, input)[0];
  105. return AddSignal(signalID, signal, clamp, input);
  106. }
  107. public float AddSignal(uint signalID, float signal, bool clamp = true, bool input = true)
  108. {
  109. var (array, count) = GetSignalStruct(signalID, out uint index, input);
  110. if (count > 0)
  111. {
  112. ref var channelData = ref array[index];
  113. channelData.valueAsFloat += signal;
  114. if (clamp)
  115. {
  116. if (channelData.valueAsFloat > POSITIVE_HIGH)
  117. {
  118. channelData.valueAsFloat = POSITIVE_HIGH;
  119. }
  120. else if (channelData.valueAsFloat < NEGATIVE_HIGH)
  121. {
  122. channelData.valueAsFloat = NEGATIVE_HIGH;
  123. }
  124. return channelData.valueAsFloat;
  125. }
  126. }
  127. return signal;
  128. }
  129. public float GetSignal(EGID blockID, out uint signalID, bool input = true)
  130. {
  131. signalID = GetSignalIDs(blockID, input)[0];
  132. return GetSignal(signalID, input);
  133. }
  134. public float GetSignal(uint signalID, bool input = true)
  135. {
  136. var (array, count) = GetSignalStruct(signalID, out uint index, input);
  137. return count > 0 ? array[index].valueAsFloat : 0f;
  138. }
  139. public uint[] GetSignalIDs(EGID blockID, bool input = true)
  140. {
  141. ref BlockPortsStruct bps = ref entitiesDB.QueryEntity<BlockPortsStruct>(blockID);
  142. uint[] signals;
  143. if (input) {
  144. signals = new uint[bps.inputCount];
  145. for (uint i = 0u; i < bps.inputCount; i++)
  146. {
  147. signals[i] = bps.firstInputID + i;
  148. }
  149. } else {
  150. signals = new uint[bps.outputCount];
  151. for (uint i = 0u; i < bps.outputCount; i++)
  152. {
  153. signals[i] = bps.firstOutputID + i;
  154. }
  155. }
  156. return signals;
  157. }
  158. public EGID[] GetSignalInputs(EGID blockID)
  159. {
  160. BlockPortsStruct ports = entitiesDB.QueryEntity<BlockPortsStruct>(blockID);
  161. EGID[] inputs = new EGID[ports.inputCount];
  162. for (uint i = 0; i < ports.inputCount; i++)
  163. {
  164. inputs[i] = new EGID(i + ports.firstInputID, NamedExclusiveGroup<BuildModeWiresGroups.InputPortsGroup>.Group);
  165. }
  166. return inputs;
  167. }
  168. public EGID[] GetSignalOutputs(EGID blockID)
  169. {
  170. BlockPortsStruct ports = entitiesDB.QueryEntity<BlockPortsStruct>(blockID);
  171. EGID[] outputs = new EGID[ports.outputCount];
  172. for (uint i = 0; i < ports.outputCount; i++)
  173. {
  174. outputs[i] = new EGID(i + ports.firstOutputID, NamedExclusiveGroup<BuildModeWiresGroups.OutputPortsGroup>.Group);
  175. }
  176. return outputs;
  177. }
  178. public OptionalRef<PortEntityStruct> MatchBlockIOToPort(Block block, byte portUsage, bool output)
  179. {
  180. return MatchBlockIOToPort(block.Id, portUsage, output);
  181. }
  182. public OptionalRef<PortEntityStruct> MatchBlockIOToPort(EGID block, byte portUsage, bool output)
  183. {
  184. if (!entitiesDB.Exists<BlockPortsStruct>(block))
  185. return default;
  186. var group = output
  187. ? NamedExclusiveGroup<BuildModeWiresGroups.OutputPortsGroup>.Group
  188. : NamedExclusiveGroup<BuildModeWiresGroups.InputPortsGroup>.Group;
  189. BlockPortsStruct ports = entitiesDB.QueryEntity<BlockPortsStruct>(block);
  190. if (!entitiesDB.TryQueryMappedEntities<PortEntityStruct>(group, out var mapper))
  191. return default;
  192. for (uint i = 0; i < (output ? ports.outputCount : ports.inputCount); ++i)
  193. {
  194. uint entityID = (output ? ports.firstOutputID : ports.firstInputID) + i;
  195. if (!mapper.TryGetArrayAndEntityIndex(entityID, out var index, out var array) ||
  196. array[index].usage != portUsage) continue;
  197. return new OptionalRef<PortEntityStruct>(array, index, new EGID(entityID, group));
  198. }
  199. return default;
  200. }
  201. public OptionalRef<WireEntityStruct> MatchPortToWire(PortEntityStruct port, EGID blockID, out EGID wireID)
  202. {
  203. var (wires, ids, count) = entitiesDB.QueryEntities<WireEntityStruct>(NamedExclusiveGroup<BuildModeWiresGroups.WiresGroup>.Group);
  204. for (uint i = 0; i < count; i++)
  205. {
  206. if ((wires[i].destinationPortUsage == port.usage && wires[i].destinationBlockEGID == blockID)
  207. || (wires[i].sourcePortUsage == port.usage && wires[i].sourceBlockEGID == blockID))
  208. {
  209. wireID = new EGID(ids[i], BuildModeWiresGroups.WiresGroup.Group);
  210. return new OptionalRef<WireEntityStruct>(wires, i);
  211. }
  212. }
  213. wireID = default;
  214. return default;
  215. }
  216. public EGID MatchBlocksToWire(EGID startBlock, EGID endBlock, byte startPort = byte.MaxValue, byte endPort = byte.MaxValue)
  217. {
  218. EGID[] startPorts;
  219. if (startPort == byte.MaxValue)
  220. {
  221. // search all output ports on source block
  222. startPorts = GetSignalOutputs(startBlock);
  223. }
  224. else
  225. {
  226. BlockPortsStruct ports = entitiesDB.QueryEntity<BlockPortsStruct>(startBlock);
  227. startPorts = new EGID[] {new EGID(ports.firstOutputID + startPort, NamedExclusiveGroup<BuildModeWiresGroups.OutputPortsGroup>.Group) };
  228. }
  229. EGID[] endPorts;
  230. if (startPort == byte.MaxValue)
  231. {
  232. // search all input ports on destination block
  233. endPorts = GetSignalInputs(endBlock);
  234. }
  235. else
  236. {
  237. BlockPortsStruct ports = entitiesDB.QueryEntity<BlockPortsStruct>(endBlock);
  238. endPorts = new EGID[] {new EGID(ports.firstInputID + endPort, NamedExclusiveGroup<BuildModeWiresGroups.InputPortsGroup>.Group) };
  239. }
  240. for (int endIndex = 0; endIndex < endPorts.Length; endIndex++)
  241. {
  242. PortEntityStruct endPES = entitiesDB.QueryEntity<PortEntityStruct>(endPorts[endIndex]);
  243. for (int startIndex = 0; startIndex < startPorts.Length; startIndex++)
  244. {
  245. PortEntityStruct startPES = entitiesDB.QueryEntity<PortEntityStruct>(startPorts[startIndex]);
  246. foreach (var wireOpt in entitiesDB.QueryEntitiesOptional<WireEntityStruct>(
  247. NamedExclusiveGroup<BuildModeWiresGroups.WiresGroup>.Group))
  248. {
  249. var wire = wireOpt.Get();
  250. if ((wire.destinationPortUsage == endPES.usage && wire.destinationBlockEGID == endBlock)
  251. && (wire.sourcePortUsage == startPES.usage && wire.sourceBlockEGID == startBlock))
  252. {
  253. return wireOpt.EGID;
  254. }
  255. }
  256. }
  257. }
  258. return default;
  259. }
  260. public OptionalRef<ChannelDataStruct> GetChannelDataStruct(EGID portID)
  261. {
  262. var port = GetPort(portID);
  263. var (channels, count) = entitiesDB.QueryEntities<ChannelDataStruct>(NamedExclusiveGroup<BuildModeWiresGroups.ChannelDataGroup>.Group);
  264. return port.firstChannelIndexCachedInSim < count
  265. ? new OptionalRef<ChannelDataStruct>(channels, port.firstChannelIndexCachedInSim)
  266. : default;
  267. }
  268. public EGID[] GetElectricBlocks()
  269. {
  270. var res = new FasterList<EGID>();
  271. foreach (var ((coll, ids, count), _) in entitiesDB.QueryEntities<BlockPortsStruct>())
  272. {
  273. for (int i = 0; i < count; i++)
  274. {
  275. ref BlockPortsStruct s = ref coll[i];
  276. //res.Add(s.ID); - TODO: Would need to search for the groups for each block
  277. }
  278. }
  279. return res.ToArray();
  280. }
  281. public EGID[] WiredToInput(EGID block, byte port)
  282. {
  283. return entitiesDB
  284. .QueryEntitiesOptional<WireEntityStruct>(NamedExclusiveGroup<BuildModeWiresGroups.WiresGroup>.Group)
  285. .ToArray(wire => wire.ID,
  286. wire => wire.Component.destinationPortUsage == port && wire.Component.destinationBlockEGID == block);
  287. }
  288. public EGID[] WiredToOutput(EGID block, byte port)
  289. {
  290. return entitiesDB
  291. .QueryEntitiesOptional<WireEntityStruct>(NamedExclusiveGroup<BuildModeWiresGroups.WiresGroup>.Group)
  292. .ToArray(wire => wire.ID,
  293. wire => wire.Component.sourcePortUsage == port && wire.Component.sourceBlockEGID == block);
  294. }
  295. private EntityCollection<ChannelDataStruct> GetSignalStruct(uint signalID, out uint index, bool input = true)
  296. {
  297. ExclusiveGroup group = input
  298. ? NamedExclusiveGroup<BuildModeWiresGroups.InputPortsGroup>.Group
  299. : NamedExclusiveGroup<BuildModeWiresGroups.OutputPortsGroup>.Group;
  300. if (entitiesDB.Exists<PortEntityStruct>(signalID, group))
  301. {
  302. index = entitiesDB.QueryEntity<PortEntityStruct>(signalID, group).firstChannelIndexCachedInSim;
  303. var channelData =
  304. entitiesDB.QueryEntities<ChannelDataStruct>(NamedExclusiveGroup<BuildModeWiresGroups.ChannelDataGroup>.Group);
  305. return channelData;
  306. }
  307. index = 0;
  308. return default; //count: 0
  309. }
  310. }
  311. }