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.

359 lines
13KB

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