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.

Wire.cs 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  1. using System;
  2. using Gamecraft.Wires;
  3. using Svelto.ECS;
  4. using Svelto.ECS.Experimental;
  5. using GamecraftModdingAPI.Utility;
  6. namespace GamecraftModdingAPI.Blocks
  7. {
  8. public class Wire
  9. {
  10. internal static SignalEngine signalEngine;
  11. protected EGID startPortEGID;
  12. protected EGID endPortEGID;
  13. protected EGID startBlockEGID;
  14. protected EGID endBlockEGID;
  15. protected EGID wireEGID;
  16. protected bool inputToOutput;
  17. public static Wire Connect(SignalingBlock start, byte startPort, SignalingBlock end, byte endPort)
  18. {
  19. WireEntityStruct wire = signalEngine.CreateNewWire(start.Id, startPort, end.Id, endPort);
  20. return new Wire(wire);
  21. }
  22. /// <summary>
  23. /// An existing wire connection ending at the specified input.
  24. /// If multiple exist, this will return the first one found.
  25. /// </summary>
  26. /// <param name="end">Destination block.</param>
  27. /// <param name="endPort">Port number.</param>
  28. /// <returns>The wire, where the end of the wire is the block port specified, or null if does not exist.</returns>
  29. public static Wire ConnectedToInputPort(SignalingBlock end, byte endPort)
  30. {
  31. EGID port = signalEngine.MatchBlockInputToPort(end, endPort, out bool exists);
  32. if (!exists) return null;
  33. WireEntityStruct wire = signalEngine.MatchPortToWire(port, end.Id, out exists);
  34. if (exists)
  35. {
  36. return new Wire(new Block(wire.sourceBlockEGID), end, wire.sourcePortUsage, endPort);
  37. }
  38. return null;
  39. }
  40. /// <summary>
  41. /// An existing wire connection starting at the specified output.
  42. /// If multiple exist, this will return the first one found.
  43. /// </summary>
  44. /// <param name="start">Source block entity ID.</param>
  45. /// <param name="startPort">Port number.</param>
  46. /// <returns>The wire, where the start of the wire is the block port specified, or null if does not exist.</returns>
  47. public static Wire ConnectedToOutputPort(SignalingBlock start, byte startPort)
  48. {
  49. EGID port = signalEngine.MatchBlockOutputToPort(start, startPort, out bool exists);
  50. if (!exists) return null;
  51. WireEntityStruct wire = signalEngine.MatchPortToWire(port, start.Id, out exists);
  52. if (exists)
  53. {
  54. return new Wire(start, new Block(wire.destinationBlockEGID), startPort, wire.destinationPortUsage);
  55. }
  56. return null;
  57. }
  58. /// <summary>
  59. /// Construct a wire object from an existing connection.
  60. /// </summary>
  61. /// <param name="start">Starting block ID.</param>
  62. /// <param name="end">Ending block ID.</param>
  63. /// <param name="startPort">Starting port number, or guess if omitted.</param>
  64. /// <param name="endPort">Ending port number, or guess if omitted.</param>
  65. /// <exception cref="WireInvalidException">Guessing failed or wire does not exist.</exception>
  66. public Wire(Block start, Block end, byte startPort = Byte.MaxValue, byte endPort = Byte.MaxValue)
  67. {
  68. startBlockEGID = start.Id;
  69. endBlockEGID = end.Id;
  70. // find block ports
  71. WireEntityStruct wire = signalEngine.MatchBlocksToWire(start.Id, end.Id, out bool exists, startPort, endPort);
  72. if (exists)
  73. {
  74. wireEGID = wire.ID;
  75. endPortEGID = signalEngine.MatchBlockInputToPort(end, wire.destinationPortUsage, out exists);
  76. if (!exists) throw new WireInvalidException("Wire end port not found");
  77. startPortEGID = signalEngine.MatchBlockOutputToPort(start, wire.sourcePortUsage, out exists);
  78. if (!exists) throw new WireInvalidException("Wire start port not found");
  79. inputToOutput = false;
  80. }
  81. else
  82. {
  83. // flip I/O around and try again
  84. wire = signalEngine.MatchBlocksToWire(end.Id, start.Id, out exists, endPort, startPort);
  85. if (exists)
  86. {
  87. wireEGID = wire.ID;
  88. endPortEGID = signalEngine.MatchBlockOutputToPort(end, wire.sourcePortUsage, out exists);
  89. if (!exists) throw new WireInvalidException("Wire end port not found");
  90. startPortEGID = signalEngine.MatchBlockInputToPort(start, wire.destinationPortUsage, out exists);
  91. if (!exists) throw new WireInvalidException("Wire start port not found");
  92. inputToOutput = true; // end is actually the source
  93. // NB: start and end are handled exactly as they're received as params.
  94. // This makes wire traversal easier, but makes logic in this class a bit more complex
  95. }
  96. else
  97. {
  98. throw new WireInvalidException("Wire not found");
  99. }
  100. }
  101. }
  102. /// <summary>
  103. /// Construct a wire object from an existing wire connection.
  104. /// </summary>
  105. /// <param name="start">Starting block ID.</param>
  106. /// <param name="end">Ending block ID.</param>
  107. /// <param name="startPort">Starting port number.</param>
  108. /// <param name="endPort">Ending port number.</param>
  109. /// <param name="wire">The wire ID.</param>
  110. /// <param name="inputToOutput">Whether the wire direction goes input -> output (true) or output -> input (false, preferred).</param>
  111. public Wire(Block start, Block end, byte startPort, byte endPort, EGID wire, bool inputToOutput)
  112. {
  113. this.startBlockEGID = start.Id;
  114. this.endBlockEGID = end.Id;
  115. this.inputToOutput = inputToOutput;
  116. this.wireEGID = wire;
  117. if (inputToOutput)
  118. {
  119. endPortEGID = signalEngine.MatchBlockOutputToPort(start, startPort, out bool exists);
  120. if (!exists) throw new WireInvalidException("Wire end port not found");
  121. startPortEGID = signalEngine.MatchBlockInputToPort(end, endPort, out exists);
  122. if (!exists) throw new WireInvalidException("Wire start port not found");
  123. }
  124. else
  125. {
  126. endPortEGID = signalEngine.MatchBlockInputToPort(end, endPort, out bool exists);
  127. if (!exists) throw new WireInvalidException("Wire end port not found");
  128. startPortEGID = signalEngine.MatchBlockOutputToPort(start, startPort, out exists);
  129. if (!exists) throw new WireInvalidException("Wire start port not found");
  130. }
  131. }
  132. /// <summary>
  133. /// Construct a wire object from an existing wire connection.
  134. /// </summary>
  135. /// <param name="wireEgid">The wire ID.</param>
  136. public Wire(EGID wireEgid)
  137. {
  138. this.wireEGID = wireEgid;
  139. WireEntityStruct wire = signalEngine.GetWire(wireEGID);
  140. this.startBlockEGID = wire.sourceBlockEGID;
  141. this.endBlockEGID = wire.destinationBlockEGID;
  142. this.inputToOutput = false;
  143. endPortEGID = signalEngine.MatchBlockInputToPort(wire.destinationBlockEGID, wire.destinationPortUsage, out bool exists);
  144. if (!exists) throw new WireInvalidException("Wire end port not found");
  145. startPortEGID = signalEngine.MatchBlockOutputToPort(wire.sourceBlockEGID, wire.sourcePortUsage, out exists);
  146. if (!exists) throw new WireInvalidException("Wire start port not found");
  147. }
  148. internal Wire(WireEntityStruct wire)
  149. {
  150. this.wireEGID = wire.ID;
  151. this.startBlockEGID = wire.sourceBlockEGID;
  152. this.endBlockEGID = wire.destinationBlockEGID;
  153. inputToOutput = false;
  154. endPortEGID = signalEngine.MatchBlockInputToPort(wire.destinationBlockEGID, wire.destinationPortUsage, out bool exists);
  155. if (!exists) throw new WireInvalidException("Wire end port not found");
  156. startPortEGID = signalEngine.MatchBlockOutputToPort(wire.sourceBlockEGID, wire.sourcePortUsage, out exists);
  157. if (!exists) throw new WireInvalidException("Wire start port not found");
  158. }
  159. /// <summary>
  160. /// The wire's in-game id.
  161. /// </summary>
  162. public EGID Id
  163. {
  164. get => wireEGID;
  165. }
  166. /// <summary>
  167. /// The wire's signal value, as a float.
  168. /// </summary>
  169. public float Float
  170. {
  171. get
  172. {
  173. ref ChannelDataStruct cds = ref signalEngine.GetChannelDataStruct(startPortEGID, out bool exists);
  174. if (!exists) return 0f;
  175. return cds.valueAsFloat;
  176. }
  177. set
  178. {
  179. ref ChannelDataStruct cds = ref signalEngine.GetChannelDataStruct(startPortEGID, out bool exists);
  180. if (!exists) return;
  181. cds.valueAsFloat = value;
  182. }
  183. }
  184. /// <summary>
  185. /// The wire's string signal.
  186. /// </summary>
  187. public string String
  188. {
  189. get
  190. {
  191. ref ChannelDataStruct cds = ref signalEngine.GetChannelDataStruct(startPortEGID, out bool exists);
  192. if (!exists) return "";
  193. return cds.valueAsEcsString;
  194. }
  195. set
  196. {
  197. ref ChannelDataStruct cds = ref signalEngine.GetChannelDataStruct(startPortEGID, out bool exists);
  198. if (!exists) return;
  199. cds.valueAsEcsString.Set(value);
  200. }
  201. }
  202. /// <summary>
  203. /// The wire's raw string signal.
  204. /// </summary>
  205. public ECSString ECSString
  206. {
  207. get
  208. {
  209. ref ChannelDataStruct cds = ref signalEngine.GetChannelDataStruct(startPortEGID, out bool exists);
  210. if (!exists) return default;
  211. return cds.valueAsEcsString;
  212. }
  213. set
  214. {
  215. ref ChannelDataStruct cds = ref signalEngine.GetChannelDataStruct(startPortEGID, out bool exists);
  216. if (!exists) return;
  217. cds.valueAsEcsString = value;
  218. }
  219. }
  220. /// <summary>
  221. /// The wire's signal id.
  222. /// I'm 50% sure this is useless.
  223. /// </summary>
  224. public uint SignalId
  225. {
  226. get
  227. {
  228. ref ChannelDataStruct cds = ref signalEngine.GetChannelDataStruct(startPortEGID, out bool exists);
  229. if (!exists) return uint.MaxValue;
  230. return cds.valueAsID;
  231. }
  232. set
  233. {
  234. ref ChannelDataStruct cds = ref signalEngine.GetChannelDataStruct(startPortEGID, out bool exists);
  235. if (!exists) return;
  236. cds.valueAsID = value;
  237. }
  238. }
  239. /// <summary>
  240. /// The block at the beginning of the wire.
  241. /// </summary>
  242. public SignalingBlock Start
  243. {
  244. get => new SignalingBlock(startBlockEGID);
  245. }
  246. /// <summary>
  247. /// The port number that the beginning of the wire connects to.
  248. /// </summary>
  249. public byte StartPort
  250. {
  251. get
  252. {
  253. WireEntityStruct wire = signalEngine.GetWire(wireEGID);
  254. if (inputToOutput)
  255. {
  256. return wire.destinationPortUsage;
  257. }
  258. return wire.sourcePortUsage;
  259. }
  260. }
  261. /// <summary>
  262. /// The block at the end of the wire.
  263. /// </summary>
  264. public SignalingBlock End
  265. {
  266. get => new SignalingBlock(endBlockEGID);
  267. }
  268. /// <summary>
  269. /// The port number that the end of the wire connects to.
  270. /// </summary>
  271. public byte EndPort
  272. {
  273. get
  274. {
  275. WireEntityStruct wire = signalEngine.GetWire(wireEGID);
  276. if (inputToOutput)
  277. {
  278. return wire.sourcePortUsage;
  279. }
  280. return wire.destinationPortUsage;
  281. }
  282. }
  283. /// <summary>
  284. /// Create a copy of the wire object where the direction of the wire is guaranteed to be from a block output to a block input.
  285. /// This is simply a different memory configuration and does not affect the in-game wire (which is always output -> input).
  286. /// </summary>
  287. /// <returns>A copy of the wire object.</returns>
  288. public Wire OutputToInputCopy()
  289. {
  290. return new Wire(wireEGID);
  291. }
  292. /// <summary>
  293. /// Convert the wire object to the direction the signal flows.
  294. /// Signals on wires always flows from a block output port to a block input port.
  295. /// This is simply a different memory configuration and does not affect the in-game wire (which is always output -> input).
  296. /// </summary>
  297. public void OutputToInputInPlace()
  298. {
  299. if (inputToOutput)
  300. {
  301. inputToOutput = false;
  302. // swap inputs and outputs
  303. EGID temp = endBlockEGID;
  304. endBlockEGID = startBlockEGID;
  305. startBlockEGID = temp;
  306. temp = endPortEGID;
  307. endPortEGID = startPortEGID;
  308. startPortEGID = temp;
  309. }
  310. }
  311. public override string ToString()
  312. {
  313. if (signalEngine.Exists<WireEntityStruct>(wireEGID))
  314. {
  315. return $"{nameof(Id)}: {Id}, Start{nameof(Start.Id)}: {Start.Id}, End{nameof(End.Id)}: {End.Id}, ({Start.Type}::{StartPort} aka {Start.PortName(StartPort, inputToOutput)}) -> ({End.Type}::{EndPort} aka {End.PortName(EndPort, !inputToOutput)})";
  316. }
  317. return $"{nameof(Id)}: {Id}, Start{nameof(Start.Id)}: {Start.Id}, End{nameof(End.Id)}: {End.Id}, ({Start.Type} -> {End.Type})";
  318. }
  319. internal static void Init() { }
  320. }
  321. }