diff --git a/TechbloxModdingAPI/Block.cs b/TechbloxModdingAPI/Block.cs index d4dead6..3ace802 100644 --- a/TechbloxModdingAPI/Block.cs +++ b/TechbloxModdingAPI/Block.cs @@ -103,11 +103,13 @@ namespace TechbloxModdingAPI {CommonExclusiveGroups.WHEELRIG_BLOCK_BUILD_GROUP, (id => new WheelRig(id), typeof(WheelRig))} }; - internal static Block New(EGID egid) + internal static Block New(EGID egid, bool signaling = false) { return GroupToConstructor.ContainsKey(egid.groupID) ? GroupToConstructor[egid.groupID].Constructor(egid) - : new Block(egid); + : signaling + ? new SignalingBlock(egid) + : new Block(egid); } public Block(EGID id) diff --git a/TechbloxModdingAPI/Blocks/Engines/SignalEngine.cs b/TechbloxModdingAPI/Blocks/Engines/SignalEngine.cs index b5d2083..ee2e42b 100644 --- a/TechbloxModdingAPI/Blocks/Engines/SignalEngine.cs +++ b/TechbloxModdingAPI/Blocks/Engines/SignalEngine.cs @@ -208,48 +208,35 @@ namespace TechbloxModdingAPI.Blocks.Engines } return outputs; } - - public EGID MatchBlockInputToPort(Block block, byte portUsage, out bool exists) + + public OptionalRef MatchBlockIOToPort(Block block, byte portUsage, bool output) { - var ports = entitiesDB.QueryEntityOptional(block); - exists = ports; - return new EGID(ports.Get().firstInputID + portUsage, NamedExclusiveGroup.Group); + return MatchBlockIOToPort(block.Id, portUsage, output); } - public EGID MatchBlockInputToPort(EGID block, byte portUsage, out bool exists) + public OptionalRef MatchBlockIOToPort(EGID block, byte portUsage, bool output) { if (!entitiesDB.Exists(block)) - { - exists = false; return default; - } - exists = true; + var group = output + ? NamedExclusiveGroup.Group + : NamedExclusiveGroup.Group; BlockPortsStruct ports = entitiesDB.QueryEntity(block); - return new EGID(ports.firstInputID + portUsage, NamedExclusiveGroup.Group); - } - - public EGID MatchBlockOutputToPort(Block block, byte portUsage, out bool exists) - { - var ports = entitiesDB.QueryEntityOptional(block); - exists = ports; - return new EGID(ports.Get().firstOutputID + portUsage, NamedExclusiveGroup.Group); - } - - public EGID MatchBlockOutputToPort(EGID block, byte portUsage, out bool exists) - { - if (!entitiesDB.Exists(block)) - { - exists = false; + if (!entitiesDB.TryQueryMappedEntities(group, out var mapper)) return default; + for (uint i = 0; i < (output ? ports.outputCount : ports.inputCount); ++i) + { + uint entityID = (output ? ports.firstOutputID : ports.firstInputID) + i; + if (!mapper.TryGetArrayAndEntityIndex(entityID, out var index, out var array) || + array[index].usage != portUsage) continue; + return new OptionalRef(array, index); } - exists = true; - BlockPortsStruct ports = entitiesDB.QueryEntity(block); - return new EGID(ports.firstOutputID + portUsage, NamedExclusiveGroup.Group); + + return default; } - public ref WireEntityStruct MatchPortToWire(EGID portID, EGID blockID, out bool exists) + public ref WireEntityStruct MatchPortToWire(PortEntityStruct port, EGID blockID, out bool exists) { - ref PortEntityStruct port = ref entitiesDB.QueryEntity(portID); var wires = entitiesDB.QueryEntities(NamedExclusiveGroup.Group); var wiresB = wires.ToBuffer().buffer; for (uint i = 0; i < wires.count; i++) @@ -266,8 +253,7 @@ namespace TechbloxModdingAPI.Blocks.Engines return ref defRef[0]; } - public ref WireEntityStruct MatchBlocksToWire(EGID startBlock, EGID endBlock, out bool exists, byte startPort = byte.MaxValue, - byte endPort = byte.MaxValue) + public EGID MatchBlocksToWire(EGID startBlock, EGID endBlock, byte startPort = byte.MaxValue, byte endPort = byte.MaxValue) { EGID[] startPorts; if (startPort == byte.MaxValue) @@ -306,31 +292,23 @@ namespace TechbloxModdingAPI.Blocks.Engines if ((wiresB[w].destinationPortUsage == endPES.usage && wiresB[w].destinationBlockEGID == endBlock) && (wiresB[w].sourcePortUsage == startPES.usage && wiresB[w].sourceBlockEGID == startBlock)) { - exists = true; - return ref wiresB[w]; + return wiresB[w].ID; } } } } - - exists = false; - WireEntityStruct[] defRef = new WireEntityStruct[1]; - return ref defRef[0]; + + return EGID.Empty; } - public ref ChannelDataStruct GetChannelDataStruct(EGID portID, out bool exists) - { - ref PortEntityStruct port = ref entitiesDB.QueryEntity(portID); + public OptionalRef GetChannelDataStruct(EGID portID) + { + var port = GetPort(portID); var channels = entitiesDB.QueryEntities(NamedExclusiveGroup.Group); var channelsB = channels.ToBuffer(); - if (port.firstChannelIndexCachedInSim < channels.count) - { - exists = true; - return ref channelsB.buffer[port.firstChannelIndexCachedInSim]; - } - exists = false; - ChannelDataStruct[] defRef = new ChannelDataStruct[1]; - return ref defRef[0]; + return port.firstChannelIndexCachedInSim < channels.count + ? new OptionalRef(channelsB.buffer, port.firstChannelIndexCachedInSim) + : default; } public EGID[] GetElectricBlocks() diff --git a/TechbloxModdingAPI/Blocks/SignalingBlock.cs b/TechbloxModdingAPI/Blocks/SignalingBlock.cs index 45b5aa6..7ae6e6a 100644 --- a/TechbloxModdingAPI/Blocks/SignalingBlock.cs +++ b/TechbloxModdingAPI/Blocks/SignalingBlock.cs @@ -46,9 +46,9 @@ namespace TechbloxModdingAPI.Blocks /// The connected wire. /// Port identifier. /// Whether the port has a wire connected to it. - protected ref WireEntityStruct GetConnectedWire(EGID portId, out bool connected) + protected ref WireEntityStruct GetConnectedWire(PortEntityStruct port, out bool connected) { - return ref SignalEngine.MatchPortToWire(portId, Id, out connected); + return ref SignalEngine.MatchPortToWire(port, Id, out connected); } /// @@ -56,10 +56,9 @@ namespace TechbloxModdingAPI.Blocks /// /// The channel data. /// Port identifier. - /// Whether the channel actually exists. - protected ref ChannelDataStruct GetChannelData(EGID portId, out bool exists) + protected OptionalRef GetChannelData(EGID portId) { - return ref SignalEngine.GetChannelDataStruct(portId, out exists); + return SignalEngine.GetChannelDataStruct(portId); } /// diff --git a/TechbloxModdingAPI/Blocks/Wire.cs b/TechbloxModdingAPI/Blocks/Wire.cs index 10b880d..f46ca48 100644 --- a/TechbloxModdingAPI/Blocks/Wire.cs +++ b/TechbloxModdingAPI/Blocks/Wire.cs @@ -43,14 +43,12 @@ namespace TechbloxModdingAPI.Blocks /// The wire, where the end of the wire is the block port specified, or null if does not exist. public static Wire ConnectedToInputPort(SignalingBlock end, byte endPort) { - EGID port = signalEngine.MatchBlockInputToPort(end, endPort, out bool exists); - if (!exists) return null; - WireEntityStruct wire = signalEngine.MatchPortToWire(port, end.Id, out exists); - if (exists) - { - return new Wire(Block.New(wire.sourceBlockEGID), end, wire.sourcePortUsage, endPort); - } - return null; + var port = signalEngine.MatchBlockIOToPort(end, endPort, false); + if (!port) return null; + WireEntityStruct wire = signalEngine.MatchPortToWire(port, end.Id, out var exists); + return exists + ? new Wire(wire.sourceBlockEGID, end.Id, wire.sourcePortUsage, endPort, wire.ID, false) + : null; } /// @@ -62,18 +60,16 @@ namespace TechbloxModdingAPI.Blocks /// The wire, where the start of the wire is the block port specified, or null if does not exist. public static Wire ConnectedToOutputPort(SignalingBlock start, byte startPort) { - EGID port = signalEngine.MatchBlockOutputToPort(start, startPort, out bool exists); - if (!exists) return null; - WireEntityStruct wire = signalEngine.MatchPortToWire(port, start.Id, out exists); - if (exists) - { - return new Wire(start, Block.New(wire.destinationBlockEGID), startPort, wire.destinationPortUsage); - } - return null; + var port = signalEngine.MatchBlockIOToPort(start, startPort, true); + if (!port) return null; + WireEntityStruct wire = signalEngine.MatchPortToWire(port, start.Id, out var exists); + return exists + ? new Wire(start.Id, wire.destinationBlockEGID, startPort, wire.destinationPortUsage, wire.ID, false) + : null; } /// - /// Construct a wire object from an existing connection. + /// Construct a wire object froam n existing connection. /// /// Starting block ID. /// Ending block ID. @@ -84,40 +80,25 @@ namespace TechbloxModdingAPI.Blocks { startBlockEGID = start.Id; endBlockEGID = end.Id; + bool flipped = false; // find block ports - WireEntityStruct wire = signalEngine.MatchBlocksToWire(start.Id, end.Id, out bool exists, startPort, endPort); - if (exists) + EGID wire = signalEngine.MatchBlocksToWire(start.Id, end.Id, startPort, endPort); + if (wire == EGID.Empty) { - wireEGID = wire.ID; - endPortEGID = signalEngine.MatchBlockInputToPort(end, wire.destinationPortUsage, out exists); - if (!exists) throw new WireInvalidException("Wire end port not found"); - startPortEGID = signalEngine.MatchBlockOutputToPort(start, wire.sourcePortUsage, out exists); - if (!exists) throw new WireInvalidException("Wire start port not found"); - inputToOutput = false; - endPort = wire.destinationPortUsage; - startPort = wire.sourcePortUsage; + // flip I/O around and try again + wire = signalEngine.MatchBlocksToWire(end.Id, start.Id, endPort, startPort); + flipped = true; + // NB: start and end are handled exactly as they're received as params. + // This makes wire traversal easier, but makes logic in this class a bit more complex + } + + if (wire != EGID.Empty) + { + Construct(start.Id, end.Id, startPort, endPort, wire, flipped); } else { - // flip I/O around and try again - wire = signalEngine.MatchBlocksToWire(end.Id, start.Id, out exists, endPort, startPort); - if (exists) - { - wireEGID = wire.ID; - endPortEGID = signalEngine.MatchBlockOutputToPort(end, wire.sourcePortUsage, out exists); - if (!exists) throw new WireInvalidException("Wire end port not found"); - startPortEGID = signalEngine.MatchBlockInputToPort(start, wire.destinationPortUsage, out exists); - if (!exists) throw new WireInvalidException("Wire start port not found"); - inputToOutput = true; // end is actually the source - // NB: start and end are handled exactly as they're received as params. - // This makes wire traversal easier, but makes logic in this class a bit more complex - endPort = wire.sourcePortUsage; - startPort = wire.destinationPortUsage; - } - else - { - throw new WireInvalidException("Wire not found"); - } + throw new WireInvalidException("Wire not found"); } } @@ -131,25 +112,25 @@ namespace TechbloxModdingAPI.Blocks /// The wire ID. /// Whether the wire direction goes input -> output (true) or output -> input (false, preferred). public Wire(Block start, Block end, byte startPort, byte endPort, EGID wire, bool inputToOutput) + : this(start.Id, end.Id, startPort, endPort, wire, inputToOutput) + { + } + + private Wire(EGID startBlock, EGID endBlock, byte startPort, byte endPort, EGID wire, bool inputToOutput) { - this.startBlockEGID = start.Id; - this.endBlockEGID = end.Id; + Construct(startBlock, endBlock, startPort, endPort, wire, inputToOutput); + } + + private void Construct(EGID startBlock, EGID endBlock, byte startPort, byte endPort, EGID wire, bool inputToOutput) + { + this.startBlockEGID = startBlock; + this.endBlockEGID = endBlock; this.inputToOutput = inputToOutput; this.wireEGID = wire; - if (inputToOutput) - { - endPortEGID = signalEngine.MatchBlockOutputToPort(start, startPort, out bool exists); - if (!exists) throw new WireInvalidException("Wire end port not found"); - startPortEGID = signalEngine.MatchBlockInputToPort(end, endPort, out exists); - if (!exists) throw new WireInvalidException("Wire start port not found"); - } - else - { - endPortEGID = signalEngine.MatchBlockInputToPort(end, endPort, out bool exists); - if (!exists) throw new WireInvalidException("Wire end port not found"); - startPortEGID = signalEngine.MatchBlockOutputToPort(start, startPort, out exists); - if (!exists) throw new WireInvalidException("Wire start port not found"); - } + endPortEGID = signalEngine.MatchBlockIOToPort(startBlock, startPort, inputToOutput).Nullable()?.ID ?? EGID.Empty; + if (endPortEGID == EGID.Empty) throw new WireInvalidException("Wire end port not found"); + startPortEGID = signalEngine.MatchBlockIOToPort(endBlock, endPort, !inputToOutput).Nullable()?.ID ?? EGID.Empty; + if (startPortEGID == EGID.Empty) throw new WireInvalidException("Wire start port not found"); this.startPort = startPort; this.endPort = endPort; } @@ -160,31 +141,14 @@ namespace TechbloxModdingAPI.Blocks /// The wire ID. public Wire(EGID wireEgid) { - this.wireEGID = wireEgid; WireEntityStruct wire = signalEngine.GetWire(wireEGID); - this.startBlockEGID = wire.sourceBlockEGID; - this.endBlockEGID = wire.destinationBlockEGID; - this.inputToOutput = false; - endPortEGID = signalEngine.MatchBlockInputToPort(wire.destinationBlockEGID, wire.destinationPortUsage, out bool exists); - if (!exists) throw new WireInvalidException("Wire end port not found"); - startPortEGID = signalEngine.MatchBlockOutputToPort(wire.sourceBlockEGID, wire.sourcePortUsage, out exists); - if (!exists) throw new WireInvalidException("Wire start port not found"); - this.endPort = wire.destinationPortUsage; - this.startPort = wire.sourcePortUsage; + Construct(wire.sourceBlockEGID, wire.destinationBlockEGID, wire.sourcePortUsage, wire.destinationPortUsage, + wireEgid, false); } - internal Wire(WireEntityStruct wire, SignalingBlock src, SignalingBlock dest) + private Wire(WireEntityStruct wire, SignalingBlock src, SignalingBlock dest) + : this(src, dest, wire.sourcePortUsage, wire.destinationPortUsage, wire.ID, false) { - this.wireEGID = wire.ID; - this.startBlockEGID = wire.sourceBlockEGID; - this.endBlockEGID = wire.destinationBlockEGID; - inputToOutput = false; - endPortEGID = signalEngine.MatchBlockInputToPort(dest, wire.destinationPortUsage, out bool exists); - if (!exists) throw new WireInvalidException("Wire end port not found"); - startPortEGID = signalEngine.MatchBlockOutputToPort(src, wire.sourcePortUsage, out exists); - if (!exists) throw new WireInvalidException("Wire start port not found"); - this.endPort = wire.destinationPortUsage; - this.startPort = wire.sourcePortUsage; } /// @@ -202,16 +166,12 @@ namespace TechbloxModdingAPI.Blocks { get { - ref ChannelDataStruct cds = ref signalEngine.GetChannelDataStruct(startPortEGID, out bool exists); - if (!exists) return 0f; - return cds.valueAsFloat; + return signalEngine.GetChannelDataStruct(startPortEGID).Get().valueAsFloat; } set { - ref ChannelDataStruct cds = ref signalEngine.GetChannelDataStruct(startPortEGID, out bool exists); - if (!exists) return; - cds.valueAsFloat = value; + signalEngine.GetChannelDataStruct(startPortEGID).Get().valueAsFloat = value; } } @@ -222,16 +182,12 @@ namespace TechbloxModdingAPI.Blocks { get { - ref ChannelDataStruct cds = ref signalEngine.GetChannelDataStruct(startPortEGID, out bool exists); - if (!exists) return ""; - return cds.valueAsEcsString; + return signalEngine.GetChannelDataStruct(startPortEGID).Get().valueAsEcsString; } set { - ref ChannelDataStruct cds = ref signalEngine.GetChannelDataStruct(startPortEGID, out bool exists); - if (!exists) return; - cds.valueAsEcsString.Set(value); + signalEngine.GetChannelDataStruct(startPortEGID).Get().valueAsEcsString.Set(value); } } @@ -242,16 +198,12 @@ namespace TechbloxModdingAPI.Blocks { get { - ref ChannelDataStruct cds = ref signalEngine.GetChannelDataStruct(startPortEGID, out bool exists); - if (!exists) return default; - return cds.valueAsEcsString; + return signalEngine.GetChannelDataStruct(startPortEGID).Get().valueAsEcsString; } set { - ref ChannelDataStruct cds = ref signalEngine.GetChannelDataStruct(startPortEGID, out bool exists); - if (!exists) return; - cds.valueAsEcsString = value; + signalEngine.GetChannelDataStruct(startPortEGID).Get().valueAsEcsString = value; } } @@ -263,16 +215,12 @@ namespace TechbloxModdingAPI.Blocks { get { - ref ChannelDataStruct cds = ref signalEngine.GetChannelDataStruct(startPortEGID, out bool exists); - if (!exists) return uint.MaxValue; - return cds.valueAsID; + return signalEngine.GetChannelDataStruct(startPortEGID).Get().valueAsID; } - + set { - ref ChannelDataStruct cds = ref signalEngine.GetChannelDataStruct(startPortEGID, out bool exists); - if (!exists) return; - cds.valueAsID = value; + signalEngine.GetChannelDataStruct(startPortEGID).Get().valueAsID = value; } } @@ -281,7 +229,7 @@ namespace TechbloxModdingAPI.Blocks /// public SignalingBlock Start { - get => new SignalingBlock(startBlockEGID); + get => (SignalingBlock)Block.New(startBlockEGID); } /// @@ -291,13 +239,21 @@ namespace TechbloxModdingAPI.Blocks { get => startPort; } + + /// + /// The display name of the start port. + /// + public string StartPortName + { + get => signalEngine.GetPort(startPortEGID).portNameLocalised; + } /// /// The block at the end of the wire. /// public SignalingBlock End { - get => new SignalingBlock(endBlockEGID); + get => (SignalingBlock)Block.New(endBlockEGID); } /// @@ -308,6 +264,14 @@ namespace TechbloxModdingAPI.Blocks get => endPort; } + /// + /// The display name of the end port. + /// + public string EndPortName + { + get => signalEngine.GetPort(endPortEGID).portNameLocalised; + } + /// /// 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. /// This is simply a different memory configuration and does not affect the in-game wire (which is always output -> input). @@ -329,15 +293,11 @@ namespace TechbloxModdingAPI.Blocks { inputToOutput = false; // swap inputs and outputs - EGID temp = endBlockEGID; - endBlockEGID = startBlockEGID; - startBlockEGID = temp; - temp = endPortEGID; + (endBlockEGID, startBlockEGID) = (startBlockEGID, endBlockEGID); + var tempPort = endPortEGID; endPortEGID = startPortEGID; - startPortEGID = temp; - byte tempPortNumber = endPort; - endPort = startPort; - startPort = tempPortNumber; + startPortEGID = tempPort; + (endPort, startPort) = (startPort, endPort); } } @@ -345,7 +305,7 @@ namespace TechbloxModdingAPI.Blocks { if (signalEngine.Exists(wireEGID)) { - 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)})"; + return $"{nameof(Id)}: {Id}, Start{nameof(Start.Id)}: {Start.Id}, End{nameof(End.Id)}: {End.Id}, ({Start.Type}::{StartPort} aka {(StartPort != byte.MaxValue ? Start.PortName(StartPort, inputToOutput) : "")}) -> ({End.Type}::{EndPort} aka {(EndPort != byte.MaxValue ? End.PortName(EndPort, !inputToOutput) : "")})"; } return $"{nameof(Id)}: {Id}, Start{nameof(Start.Id)}: {Start.Id}, End{nameof(End.Id)}: {End.Id}, ({Start.Type}::{StartPort} -> {End.Type}::{EndPort})"; } diff --git a/TechbloxModdingAPI/Player.cs b/TechbloxModdingAPI/Player.cs index 811056e..a98f67f 100644 --- a/TechbloxModdingAPI/Player.cs +++ b/TechbloxModdingAPI/Player.cs @@ -1,4 +1,5 @@ using System; +using Gamecraft.Wires; using RobocraftX.Character; using RobocraftX.Character.Movement; using Unity.Mathematics; @@ -460,7 +461,7 @@ namespace TechbloxModdingAPI { var egid = playerEngine.GetThingLookedAt(Id, maxDistance); return egid != EGID.Empty && egid.groupID == WiresGUIExclusiveGroups.WireGroup - ? new Wire(egid) + ? new Wire(new EGID(egid.entityID, NamedExclusiveGroup.Group)) : null; } diff --git a/TechbloxModdingAPI/Utility/OptionalRef.cs b/TechbloxModdingAPI/Utility/OptionalRef.cs index 79f7175..c83e0da 100644 --- a/TechbloxModdingAPI/Utility/OptionalRef.cs +++ b/TechbloxModdingAPI/Utility/OptionalRef.cs @@ -65,6 +65,7 @@ namespace TechbloxModdingAPI.Utility } public bool Exists => state != State.Empty; + public T? Nullable() => this ? Get() : default; public static implicit operator T(OptionalRef opt) => opt.Get();