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.

411 lines
14KB

  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 CreateNewWire(EGID startBlock, byte startPort, EGID endBlock, byte endPort)
  33. {
  34. EGID wireEGID = new EGID(WiresExclusiveGroups.NewWireEntityId, NamedExclusiveGroup<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. ID = wireEGID
  43. });
  44. return wireInitializer.Get<WireEntityStruct>();
  45. }
  46. public ref WireEntityStruct GetWire(EGID wire)
  47. {
  48. if (!entitiesDB.Exists<WireEntityStruct>(wire))
  49. {
  50. throw new WiringException($"Wire {wire} does not exist");
  51. }
  52. return ref entitiesDB.QueryEntity<WireEntityStruct>(wire);
  53. }
  54. public ref PortEntityStruct GetPort(EGID port)
  55. {
  56. if (!entitiesDB.Exists<PortEntityStruct>(port))
  57. {
  58. throw new WiringException($"Port {port} does not exist (yet?)");
  59. }
  60. return ref entitiesDB.QueryEntity<PortEntityStruct>(port);
  61. }
  62. public ref PortEntityStruct GetPortByOffset(BlockPortsStruct bps, byte portNumber, bool input)
  63. {
  64. ExclusiveGroup group = input
  65. ? NamedExclusiveGroup<InputPortsGroup>.Group
  66. : NamedExclusiveGroup<OutputPortsGroup>.Group;
  67. uint id = (input ? bps.firstInputID : bps.firstOutputID) + portNumber;
  68. EGID egid = new EGID(id, group);
  69. if (!entitiesDB.Exists<PortEntityStruct>(egid))
  70. {
  71. throw new WiringException("Port does not exist");
  72. }
  73. return ref entitiesDB.QueryEntity<PortEntityStruct>(egid);
  74. }
  75. public ref PortEntityStruct GetPortByOffset(Block block, byte portNumber, bool input)
  76. {
  77. var bps = entitiesDB.QueryEntityOptional<BlockPortsStruct>(block);
  78. if (!bps)
  79. {
  80. throw new BlockException("Block does not exist");
  81. }
  82. return ref GetPortByOffset(bps, portNumber, input);
  83. }
  84. public ref T GetComponent<T>(EGID egid) where T : unmanaged, IEntityComponent
  85. {
  86. return ref entitiesDB.QueryEntity<T>(egid);
  87. }
  88. public bool Exists<T>(EGID egid) where T : struct, IEntityComponent
  89. {
  90. return entitiesDB.Exists<T>(egid);
  91. }
  92. public bool SetSignal(EGID blockID, float signal, out uint signalID, bool input = true)
  93. {
  94. signalID = GetSignalIDs(blockID, input)[0];
  95. return SetSignal(signalID, signal);
  96. }
  97. public bool SetSignal(uint signalID, float signal, bool input = true)
  98. {
  99. var array = GetSignalStruct(signalID, out uint index, input);
  100. var arrayB = array.ToBuffer();
  101. if (array.count > 0) arrayB.buffer[index].valueAsFloat = signal;
  102. return false;
  103. }
  104. public float AddSignal(EGID blockID, float signal, out uint signalID, bool clamp = true, bool input = true)
  105. {
  106. signalID = GetSignalIDs(blockID, input)[0];
  107. return AddSignal(signalID, signal, clamp, input);
  108. }
  109. public float AddSignal(uint signalID, float signal, bool clamp = true, bool input = true)
  110. {
  111. var array = GetSignalStruct(signalID, out uint index, input);
  112. var arrayB = array.ToBuffer();
  113. if (array.count > 0)
  114. {
  115. ref var channelData = ref arrayB.buffer[index];
  116. channelData.valueAsFloat += signal;
  117. if (clamp)
  118. {
  119. if (channelData.valueAsFloat > POSITIVE_HIGH)
  120. {
  121. channelData.valueAsFloat = POSITIVE_HIGH;
  122. }
  123. else if (channelData.valueAsFloat < NEGATIVE_HIGH)
  124. {
  125. channelData.valueAsFloat = NEGATIVE_HIGH;
  126. }
  127. return channelData.valueAsFloat;
  128. }
  129. }
  130. return signal;
  131. }
  132. public float GetSignal(EGID blockID, out uint signalID, bool input = true)
  133. {
  134. signalID = GetSignalIDs(blockID, input)[0];
  135. return GetSignal(signalID, input);
  136. }
  137. public float GetSignal(uint signalID, bool input = true)
  138. {
  139. var array = GetSignalStruct(signalID, out uint index, input);
  140. var arrayB = array.ToBuffer();
  141. return array.count > 0 ? arrayB.buffer[index].valueAsFloat : 0f;
  142. }
  143. public uint[] GetSignalIDs(EGID blockID, bool input = true)
  144. {
  145. ref BlockPortsStruct bps = ref entitiesDB.QueryEntity<BlockPortsStruct>(blockID);
  146. uint[] signals;
  147. if (input) {
  148. signals = new uint[bps.inputCount];
  149. for (uint i = 0u; i < bps.inputCount; i++)
  150. {
  151. signals[i] = bps.firstInputID + i;
  152. }
  153. } else {
  154. signals = new uint[bps.outputCount];
  155. for (uint i = 0u; i < bps.outputCount; i++)
  156. {
  157. signals[i] = bps.firstOutputID + i;
  158. }
  159. }
  160. return signals;
  161. }
  162. public EGID[] GetSignalInputs(EGID blockID)
  163. {
  164. BlockPortsStruct ports = entitiesDB.QueryEntity<BlockPortsStruct>(blockID);
  165. EGID[] inputs = new EGID[ports.inputCount];
  166. for (uint i = 0; i < ports.inputCount; i++)
  167. {
  168. inputs[i] = new EGID(i + ports.firstInputID, NamedExclusiveGroup<InputPortsGroup>.Group);
  169. }
  170. return inputs;
  171. }
  172. public EGID[] GetSignalOutputs(EGID blockID)
  173. {
  174. BlockPortsStruct ports = entitiesDB.QueryEntity<BlockPortsStruct>(blockID);
  175. EGID[] outputs = new EGID[ports.outputCount];
  176. for (uint i = 0; i < ports.outputCount; i++)
  177. {
  178. outputs[i] = new EGID(i + ports.firstOutputID, NamedExclusiveGroup<OutputPortsGroup>.Group);
  179. }
  180. return outputs;
  181. }
  182. public EGID MatchBlockInputToPort(Block block, byte portUsage, out bool exists)
  183. {
  184. var ports = entitiesDB.QueryEntityOptional<BlockPortsStruct>(block);
  185. exists = ports;
  186. return new EGID(ports.Get().firstInputID + portUsage, NamedExclusiveGroup<InputPortsGroup>.Group);
  187. }
  188. public EGID MatchBlockInputToPort(EGID block, byte portUsage, out bool exists)
  189. {
  190. if (!entitiesDB.Exists<BlockPortsStruct>(block))
  191. {
  192. exists = false;
  193. return default;
  194. }
  195. exists = true;
  196. BlockPortsStruct ports = entitiesDB.QueryEntity<BlockPortsStruct>(block);
  197. return new EGID(ports.firstInputID + portUsage, NamedExclusiveGroup<InputPortsGroup>.Group);
  198. }
  199. public EGID MatchBlockOutputToPort(Block block, byte portUsage, out bool exists)
  200. {
  201. var ports = entitiesDB.QueryEntityOptional<BlockPortsStruct>(block);
  202. exists = ports;
  203. return new EGID(ports.Get().firstOutputID + portUsage, NamedExclusiveGroup<OutputPortsGroup>.Group);
  204. }
  205. public EGID MatchBlockOutputToPort(EGID block, byte portUsage, out bool exists)
  206. {
  207. if (!entitiesDB.Exists<BlockPortsStruct>(block))
  208. {
  209. exists = false;
  210. return default;
  211. }
  212. exists = true;
  213. BlockPortsStruct ports = entitiesDB.QueryEntity<BlockPortsStruct>(block);
  214. return new EGID(ports.firstOutputID + portUsage, NamedExclusiveGroup<OutputPortsGroup>.Group);
  215. }
  216. public ref WireEntityStruct MatchPortToWire(EGID portID, EGID blockID, out bool exists)
  217. {
  218. ref PortEntityStruct port = ref entitiesDB.QueryEntity<PortEntityStruct>(portID);
  219. var wires = entitiesDB.QueryEntities<WireEntityStruct>(NamedExclusiveGroup<WiresGroup>.Group);
  220. var wiresB = wires.ToBuffer().buffer;
  221. for (uint i = 0; i < wires.count; i++)
  222. {
  223. if ((wiresB[i].destinationPortUsage == port.usage && wiresB[i].destinationBlockEGID == blockID)
  224. || (wiresB[i].sourcePortUsage == port.usage && wiresB[i].sourceBlockEGID == blockID))
  225. {
  226. exists = true;
  227. return ref wiresB[i];
  228. }
  229. }
  230. exists = false;
  231. WireEntityStruct[] defRef = new WireEntityStruct[1];
  232. return ref defRef[0];
  233. }
  234. public ref WireEntityStruct MatchBlocksToWire(EGID startBlock, EGID endBlock, out bool exists, byte startPort = byte.MaxValue,
  235. byte endPort = byte.MaxValue)
  236. {
  237. EGID[] startPorts;
  238. if (startPort == byte.MaxValue)
  239. {
  240. // search all output ports on source block
  241. startPorts = GetSignalOutputs(startBlock);
  242. }
  243. else
  244. {
  245. BlockPortsStruct ports = entitiesDB.QueryEntity<BlockPortsStruct>(startBlock);
  246. startPorts = new EGID[] {new EGID(ports.firstOutputID + startPort, NamedExclusiveGroup<OutputPortsGroup>.Group) };
  247. }
  248. EGID[] endPorts;
  249. if (startPort == byte.MaxValue)
  250. {
  251. // search all input ports on destination block
  252. endPorts = GetSignalInputs(endBlock);
  253. }
  254. else
  255. {
  256. BlockPortsStruct ports = entitiesDB.QueryEntity<BlockPortsStruct>(endBlock);
  257. endPorts = new EGID[] {new EGID(ports.firstInputID + endPort, NamedExclusiveGroup<InputPortsGroup>.Group) };
  258. }
  259. EntityCollection<WireEntityStruct> wires = entitiesDB.QueryEntities<WireEntityStruct>(NamedExclusiveGroup<WiresGroup>.Group);
  260. var wiresB = wires.ToBuffer().buffer;
  261. for (int endIndex = 0; endIndex < endPorts.Length; endIndex++)
  262. {
  263. PortEntityStruct endPES = entitiesDB.QueryEntity<PortEntityStruct>(endPorts[endIndex]);
  264. for (int startIndex = 0; startIndex < startPorts.Length; startIndex++)
  265. {
  266. PortEntityStruct startPES = entitiesDB.QueryEntity<PortEntityStruct>(startPorts[startIndex]);
  267. for (int w = 0; w < wires.count; w++)
  268. {
  269. if ((wiresB[w].destinationPortUsage == endPES.usage && wiresB[w].destinationBlockEGID == endBlock)
  270. && (wiresB[w].sourcePortUsage == startPES.usage && wiresB[w].sourceBlockEGID == startBlock))
  271. {
  272. exists = true;
  273. return ref wiresB[w];
  274. }
  275. }
  276. }
  277. }
  278. exists = false;
  279. WireEntityStruct[] defRef = new WireEntityStruct[1];
  280. return ref defRef[0];
  281. }
  282. public ref ChannelDataStruct GetChannelDataStruct(EGID portID, out bool exists)
  283. {
  284. ref PortEntityStruct port = ref entitiesDB.QueryEntity<PortEntityStruct>(portID);
  285. var channels = entitiesDB.QueryEntities<ChannelDataStruct>(NamedExclusiveGroup<ChannelDataGroup>.Group);
  286. var channelsB = channels.ToBuffer();
  287. if (port.firstChannelIndexCachedInSim < channels.count)
  288. {
  289. exists = true;
  290. return ref channelsB.buffer[port.firstChannelIndexCachedInSim];
  291. }
  292. exists = false;
  293. ChannelDataStruct[] defRef = new ChannelDataStruct[1];
  294. return ref defRef[0];
  295. }
  296. public EGID[] GetElectricBlocks()
  297. {
  298. var res = new FasterList<EGID>();
  299. foreach (var (coll, _) in entitiesDB.QueryEntities<BlockPortsStruct>())
  300. {
  301. var collB = coll.ToBuffer();
  302. for (int i = 0; i < coll.count; i++)
  303. {
  304. ref BlockPortsStruct s = ref collB.buffer[i];
  305. res.Add(s.ID);
  306. }
  307. }
  308. return res.ToArray();
  309. }
  310. public EGID[] WiredToInput(EGID block, byte port)
  311. {
  312. WireEntityStruct[] wireEntityStructs = Search(NamedExclusiveGroup<WiresGroup>.Group,
  313. (WireEntityStruct wes) => wes.destinationPortUsage == port && wes.destinationBlockEGID == block);
  314. EGID[] result = new EGID[wireEntityStructs.Length];
  315. for (uint i = 0; i < wireEntityStructs.Length; i++)
  316. {
  317. result[i] = wireEntityStructs[i].ID;
  318. }
  319. return result;
  320. }
  321. public EGID[] WiredToOutput(EGID block, byte port)
  322. {
  323. WireEntityStruct[] wireEntityStructs = Search(NamedExclusiveGroup<WiresGroup>.Group,
  324. (WireEntityStruct wes) => wes.sourcePortUsage == port && wes.sourceBlockEGID == block);
  325. EGID[] result = new EGID[wireEntityStructs.Length];
  326. for (uint i = 0; i < wireEntityStructs.Length; i++)
  327. {
  328. result[i] = wireEntityStructs[i].ID;
  329. }
  330. return result;
  331. }
  332. private T[] Search<T>(ExclusiveGroup group, Func<T, bool> isMatch) where T : unmanaged, IEntityComponent
  333. {
  334. FasterList<T> results = new FasterList<T>();
  335. EntityCollection<T> components = entitiesDB.QueryEntities<T>(group);
  336. var componentsB = components.ToBuffer();
  337. for (uint i = 0; i < components.count; i++)
  338. {
  339. if (isMatch(componentsB.buffer[i]))
  340. {
  341. results.Add(componentsB.buffer[i]);
  342. }
  343. }
  344. return results.ToArray();
  345. }
  346. private EntityCollection<ChannelDataStruct> GetSignalStruct(uint signalID, out uint index, bool input = true)
  347. {
  348. ExclusiveGroup group = input
  349. ? NamedExclusiveGroup<InputPortsGroup>.Group
  350. : NamedExclusiveGroup<OutputPortsGroup>.Group;
  351. if (entitiesDB.Exists<PortEntityStruct>(signalID, group))
  352. {
  353. index = entitiesDB.QueryEntity<PortEntityStruct>(signalID, group).anyChannelIndex;
  354. var channelData =
  355. entitiesDB.QueryEntities<ChannelDataStruct>(NamedExclusiveGroup<ChannelDataGroup>.Group);
  356. return channelData;
  357. }
  358. index = 0;
  359. return default; //count: 0
  360. }
  361. }
  362. }