Browse Source

Fix looking at wires, reduce Wire code

Also added two port name properties directly to the wires
Also added support for converting OptionalRefs to Nullables
tags/v2.1.0
NorbiPeti 2 years ago
parent
commit
033ebdb86d
6 changed files with 117 additions and 176 deletions
  1. +4
    -2
      TechbloxModdingAPI/Block.cs
  2. +27
    -49
      TechbloxModdingAPI/Blocks/Engines/SignalEngine.cs
  3. +4
    -5
      TechbloxModdingAPI/Blocks/SignalingBlock.cs
  4. +79
    -119
      TechbloxModdingAPI/Blocks/Wire.cs
  5. +2
    -1
      TechbloxModdingAPI/Player.cs
  6. +1
    -0
      TechbloxModdingAPI/Utility/OptionalRef.cs

+ 4
- 2
TechbloxModdingAPI/Block.cs View File

@@ -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)


+ 27
- 49
TechbloxModdingAPI/Blocks/Engines/SignalEngine.cs View File

@@ -208,48 +208,35 @@ namespace TechbloxModdingAPI.Blocks.Engines
}
return outputs;
}
public EGID MatchBlockInputToPort(Block block, byte portUsage, out bool exists)
public OptionalRef<PortEntityStruct> MatchBlockIOToPort(Block block, byte portUsage, bool output)
{
var ports = entitiesDB.QueryEntityOptional<BlockPortsStruct>(block);
exists = ports;
return new EGID(ports.Get().firstInputID + portUsage, NamedExclusiveGroup<InputPortsGroup>.Group);
return MatchBlockIOToPort(block.Id, portUsage, output);
}
public EGID MatchBlockInputToPort(EGID block, byte portUsage, out bool exists)
public OptionalRef<PortEntityStruct> MatchBlockIOToPort(EGID block, byte portUsage, bool output)
{
if (!entitiesDB.Exists<BlockPortsStruct>(block))
{
exists = false;
return default;
}
exists = true;
var group = output
? NamedExclusiveGroup<OutputPortsGroup>.Group
: NamedExclusiveGroup<InputPortsGroup>.Group;
BlockPortsStruct ports = entitiesDB.QueryEntity<BlockPortsStruct>(block);
return new EGID(ports.firstInputID + portUsage, NamedExclusiveGroup<InputPortsGroup>.Group);
}
public EGID MatchBlockOutputToPort(Block block, byte portUsage, out bool exists)
{
var ports = entitiesDB.QueryEntityOptional<BlockPortsStruct>(block);
exists = ports;
return new EGID(ports.Get().firstOutputID + portUsage, NamedExclusiveGroup<OutputPortsGroup>.Group);
}
public EGID MatchBlockOutputToPort(EGID block, byte portUsage, out bool exists)
{
if (!entitiesDB.Exists<BlockPortsStruct>(block))
{
exists = false;
if (!entitiesDB.TryQueryMappedEntities<PortEntityStruct>(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<PortEntityStruct>(array, index);
}
exists = true;
BlockPortsStruct ports = entitiesDB.QueryEntity<BlockPortsStruct>(block);
return new EGID(ports.firstOutputID + portUsage, NamedExclusiveGroup<OutputPortsGroup>.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<PortEntityStruct>(portID);
var wires = entitiesDB.QueryEntities<WireEntityStruct>(NamedExclusiveGroup<WiresGroup>.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<PortEntityStruct>(portID);
public OptionalRef<ChannelDataStruct> GetChannelDataStruct(EGID portID)
{
var port = GetPort(portID);
var channels = entitiesDB.QueryEntities<ChannelDataStruct>(NamedExclusiveGroup<ChannelDataGroup>.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<ChannelDataStruct>(channelsB.buffer, port.firstChannelIndexCachedInSim)
: default;
}

public EGID[] GetElectricBlocks()


+ 4
- 5
TechbloxModdingAPI/Blocks/SignalingBlock.cs View File

@@ -46,9 +46,9 @@ namespace TechbloxModdingAPI.Blocks
/// <returns>The connected wire.</returns>
/// <param name="portId">Port identifier.</param>
/// <param name="connected">Whether the port has a wire connected to it.</param>
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);
}

/// <summary>
@@ -56,10 +56,9 @@ namespace TechbloxModdingAPI.Blocks
/// </summary>
/// <returns>The channel data.</returns>
/// <param name="portId">Port identifier.</param>
/// <param name="exists">Whether the channel actually exists.</param>
protected ref ChannelDataStruct GetChannelData(EGID portId, out bool exists)
protected OptionalRef<ChannelDataStruct> GetChannelData(EGID portId)
{
return ref SignalEngine.GetChannelDataStruct(portId, out exists);
return SignalEngine.GetChannelDataStruct(portId);
}

/// <summary>


+ 79
- 119
TechbloxModdingAPI/Blocks/Wire.cs View File

@@ -43,14 +43,12 @@ namespace TechbloxModdingAPI.Blocks
/// <returns>The wire, where the end of the wire is the block port specified, or null if does not exist.</returns>
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;
}

/// <summary>
@@ -62,18 +60,16 @@ namespace TechbloxModdingAPI.Blocks
/// <returns>The wire, where the start of the wire is the block port specified, or null if does not exist.</returns>
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;
}

/// <summary>
/// Construct a wire object from an existing connection.
/// Construct a wire object froam n existing connection.
/// </summary>
/// <param name="start">Starting block ID.</param>
/// <param name="end">Ending block ID.</param>
@@ -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
/// <param name="wire">The wire ID.</param>
/// <param name="inputToOutput">Whether the wire direction goes input -> output (true) or output -> input (false, preferred).</param>
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
/// <param name="wireEgid">The wire ID.</param>
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;
}

/// <summary>
@@ -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
/// </summary>
public SignalingBlock Start
{
get => new SignalingBlock(startBlockEGID);
get => (SignalingBlock)Block.New(startBlockEGID);
}

/// <summary>
@@ -291,13 +239,21 @@ namespace TechbloxModdingAPI.Blocks
{
get => startPort;
}

/// <summary>
/// The display name of the start port.
/// </summary>
public string StartPortName
{
get => signalEngine.GetPort(startPortEGID).portNameLocalised;
}
/// <summary>
/// The block at the end of the wire.
/// </summary>
public SignalingBlock End
{
get => new SignalingBlock(endBlockEGID);
get => (SignalingBlock)Block.New(endBlockEGID);
}
/// <summary>
@@ -308,6 +264,14 @@ namespace TechbloxModdingAPI.Blocks
get => endPort;
}

/// <summary>
/// The display name of the end port.
/// </summary>
public string EndPortName
{
get => signalEngine.GetPort(endPortEGID).portNameLocalised;
}

/// <summary>
/// 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<WireEntityStruct>(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})";
}


+ 2
- 1
TechbloxModdingAPI/Player.cs View File

@@ -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<WiresGroup>.Group))
: null;
}



+ 1
- 0
TechbloxModdingAPI/Utility/OptionalRef.cs View File

@@ -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<T> opt) => opt.Get();



Loading…
Cancel
Save