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.

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