Skip to content

Commit

Permalink
Merge pull request #1724 from CitiesSkylinesMods/bugfix/invalid-arrow…
Browse files Browse the repository at this point in the history
…-directions-not-allowed

Prevent the user from setting invalid Lane Arrows, update if lane connections exist
  • Loading branch information
krzychu124 committed Mar 9, 2023
2 parents 1d2abbb + 117eba0 commit 183d209
Show file tree
Hide file tree
Showing 9 changed files with 230 additions and 36 deletions.
96 changes: 94 additions & 2 deletions TLM/TLM/Manager/Impl/ExtSegmentEndManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ namespace TrafficManager.Manager.Impl {
using System;
using TrafficManager.API.Manager;
using TrafficManager.API.Traffic.Data;
using TrafficManager.API.Traffic.Enums;
using TrafficManager.Lifecycle;
using TrafficManager.State.ConfigData;
using TrafficManager.Util;
using TrafficManager.Util.Extensions;
Expand Down Expand Up @@ -71,7 +73,7 @@ public void Reset(ushort segmentId) {
Reset(ref ExtSegmentEnds[GetIndex(segmentId, false)]);
}

private void Reset(ref ExtSegmentEnd extSegmentEnd) {
private void Reset(ref ExtSegmentEnd extSegmentEnd, bool retainLaneArrows = false) {
IExtVehicleManager extVehicleMan = Constants.ManagerFactory.ExtVehicleManager;
int numIter = 0;

Expand All @@ -92,6 +94,9 @@ private void Reset(ref ExtSegmentEnd extSegmentEnd) {
extSegmentEnd.outgoing = false;
extSegmentEnd.incoming = false;
extSegmentEnd.firstVehicleId = 0;
if (!retainLaneArrows) {
extSegmentEnd.laneArrows = LaneArrows.None;
}
}

public ref ExtSegmentEnd GetEnd(ushort segmentId, bool startNode) => ref ExtSegmentEnds[GetIndex(segmentId, startNode)];
Expand Down Expand Up @@ -230,6 +235,24 @@ private static Vector3 GetSegmentDir(ref NetSegment segment, bool startNode) {
return startNode ? segment.m_startDirection : segment.m_endDirection;
}

public LaneArrows? GetOutgoingAvailableLaneArrows(uint laneId) {
NetInfo.Lane laneInfo = ExtLaneManager.Instance.GetLaneInfo(laneId);
if (laneInfo == null || !(laneInfo.m_finalDirection is NetInfo.Direction.Forward or NetInfo.Direction.Backward)) {
// bi-directional lanes are not supported anyways
return null;
}

ushort segmentId = laneId.ToLane().m_segment;
ref NetSegment segment = ref segmentId.ToSegment();
// code borrowed from LaneConnectionSubManager.IsHeadingTowardsStartNode(laneId)
bool inverted = (segment.m_flags & NetSegment.Flags.Invert) != 0;
bool isHeadingTowardsStartNode = (laneInfo.m_finalDirection == NetInfo.Direction.Forward) ^ !inverted;

// get allowed lane arrows for selected segment end
ref ExtSegmentEnd segmentEnd = ref ExtSegmentEnds[GetIndex(segmentId, isHeadingTowardsStartNode)];
return segmentEnd.laneArrows;
}

public void Recalculate(ushort segmentId) {
Recalculate(ref ExtSegmentEnds[GetIndex(segmentId, true)]);
Recalculate(ref ExtSegmentEnds[GetIndex(segmentId, false)]);
Expand All @@ -254,7 +277,8 @@ private void Recalculate(ref ExtSegmentEnd segEnd) {
}

ushort nodeIdBeforeRecalc = segEnd.nodeId;
Reset(ref segEnd);
LaneArrows laneArrowsBefore = segEnd.laneArrows;
Reset(ref segEnd, retainLaneArrows: true);

ref NetSegment netSegment = ref segmentId.ToSegment();

Expand Down Expand Up @@ -286,6 +310,58 @@ private void Recalculate(ref ExtSegmentEnd segEnd) {
}
}

internal void RecalculateAvailableLaneArrows(ushort segmentId) {
ref ExtSegmentEnd segmentEndStart = ref ExtSegmentEnds[GetIndex(segmentId, true)];
ref ExtSegmentEnd segmentEndEnd = ref ExtSegmentEnds[GetIndex(segmentId, false)];
RecalculateAvailableLaneArrows(ref segmentEndStart, LaneArrows.None, validate: false);
RecalculateAvailableLaneArrows(ref segmentEndEnd, LaneArrows.None, validate: false);
}

internal void RecalculateAvailableLaneArrows(ushort segmentId, bool startNode) {
ref ExtSegmentEnd segmentEnd = ref ExtSegmentEnds[GetIndex(segmentId, startNode)];
RecalculateAvailableLaneArrows(ref segmentEnd, segmentEnd.laneArrows);
}

/// <summary>
/// Recaclulates available lane arrows for selected segment end
/// </summary>
/// <param name="segEnd"></param>
/// <param name="laneArrowsBefore">optional previously set arrows for comparison</param>
/// <param name="validate">runs soft validation comparing previous are new lane arrows</param>
private void RecalculateAvailableLaneArrows(ref ExtSegmentEnd segEnd,
LaneArrows laneArrowsBefore = LaneArrows.None,
bool validate = true
) {
if (segEnd.incoming) {
#if DEBUG
bool logGeometry = DebugSwitch.GeometryDebug.Get();
#else
const bool logGeometry = false;
#endif
ref NetSegment netSegment = ref segEnd.segmentId.ToSegment();
ushort nodeId = segEnd.startNode ? netSegment.m_startNode : netSegment.m_endNode;
CalculateAvailableLaneArrowDirections(
ref segEnd,
ref nodeId.ToNode(),
out segEnd.laneArrows);

if (logGeometry) {
Log._Debug($"ExtSegmentEndManager.RecalculateAvailableLaneArrows({segEnd.segmentId},{segEnd.startNode}): " +
$"Calculated Lane Arrows: {segEnd.laneArrows} Before: {laneArrowsBefore}");
}

if (validate) {
if (laneArrowsBefore != segEnd.laneArrows) {
if (logGeometry) {
Log._Debug($"ExtSegmentEndManager.RecalculateAvailableLaneArrows({segEnd.segmentId},{segEnd.startNode}): " +
$"Different set of available lane arrows after recalculation! Resetting custom Lane Arrows! Before: [{laneArrowsBefore}] Now: [{segEnd.laneArrows}]. ");
}
LaneArrowManager.Instance.ResetLaneArrows(segEnd.segmentId, segEnd.startNode);
}
}
}
}

/// <summary>
/// This recalculation must requires to be called after CalcualteSegment(). therefore it is not being called together
/// with other calculations.
Expand Down Expand Up @@ -441,6 +517,22 @@ public bool CalculateOnlyHighways(ushort segmentId, bool startNode) {
return hasOtherSegments;
}

public void CalculateAvailableLaneArrowDirections(ref ExtSegmentEnd segmentEnd,
ref NetNode node,
out LaneArrows directions) {
directions = LaneArrows.None;
CalculateOutgoingLeftStraightRightSegments(ref segmentEnd, ref node, out bool hasLeft, out bool hasStraight, out bool hasRight);
if (hasLeft) {
directions |= LaneArrows.Left;
}
if (hasStraight) {
directions |= LaneArrows.Forward;
}
if (hasRight) {
directions |= LaneArrows.Right;
}
}

public void CalculateOutgoingLeftStraightRightSegments(
ref ExtSegmentEnd segEnd,
ref NetNode node,
Expand Down
40 changes: 39 additions & 1 deletion TLM/TLM/Manager/Impl/ExtSegmentManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ namespace TrafficManager.Manager.Impl {
using CSUtil.Commons;
using System;
using System.Collections.Generic;
using TrafficManager.API.Geometry;
using TrafficManager.API.Manager;
using TrafficManager.API.Traffic.Data;
using TrafficManager.Patch;
Expand All @@ -12,7 +13,7 @@ namespace TrafficManager.Manager.Impl {
using TrafficManager.Util.Iterators;

public class ExtSegmentManager
: AbstractCustomManager,
: AbstractGeometryObservingManager,
IExtSegmentManager
{
static ExtSegmentManager() {
Expand Down Expand Up @@ -146,6 +147,15 @@ private void Recalculate(ref ExtSegment extSegment) {
Constants.ManagerFactory.GeometryManager.OnUpdateSegment(ref extSegment);
}

private void RecalculateAvailableLaneArrows(ref ExtSegment extSegment) {
if (extSegment.valid) {
#if DEBUGFLAGS
Log._Debug($"Recalculating available lane arrows for segment {extSegment.segmentId} STARTED");
#endif
ExtSegmentEndManager.Instance.RecalculateAvailableLaneArrows(extSegment.segmentId);
}
}

public bool CalculateIsOneWay(ushort segmentId) {
ref NetSegment netSegment = ref segmentId.ToSegment();

Expand Down Expand Up @@ -260,6 +270,25 @@ private bool CalculateIsHighway(NetInfo segmentInfo) {
&& ((RoadBaseAI)segmentInfo.m_netAI).m_highwayRules;
}

protected override void HandleValidNode(ushort nodeId, ref NetNode node) {
for (int i = 0; i < 8; i++) {
ushort segmentId = node.GetSegment(i);
if (segmentId == 0)
continue;
ref NetSegment segment = ref segmentId.ToSegment();
if (!segment.IsValid()) {
continue;
}

ref ExtSegment extSegment = ref ExtSegments[segmentId];
if (!extSegment.valid)
continue;

bool isStartNode = segment.IsStartNode(nodeId);
ExtSegmentEndManager.Instance.RecalculateAvailableLaneArrows(segmentId, isStartNode);
}
}

[Obsolete]
public GetSegmentLaneIdsEnumerable GetSegmentLaneIdsAndLaneIndexes(ushort segmentId) =>
segmentId.ToSegment().GetSegmentLaneIdsAndLaneIndexes();
Expand Down Expand Up @@ -299,5 +328,14 @@ public override void OnBeforeLoadData() {

Log._Debug($"ExtSegmentManager.OnBeforeLoadData: Calculation finished.");
}

public override void OnAfterLoadData() {
base.OnAfterLoadData();
Log.Info($"ExtSegmentManager.OnAfterLoadData: Calculating Available lane arrows for {ExtSegments.Length} extended segments...");
for (int i = 0; i < ExtSegments.Length; i++) {
RecalculateAvailableLaneArrows(ref ExtSegments[i]);
}
Log.Info($"ExtSegmentManager.OnAfterLoadData: Calculation finished.");
}
}
}
7 changes: 7 additions & 0 deletions TLM/TLM/Manager/Impl/LaneArrowManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,12 @@ private void ApplyFlags() {
}
}

private void ValidateCustomLaneArrows() {
for (uint laneId = 0; laneId < NetManager.MAX_LANE_COUNT; ++laneId) {
Flags.ValidateLaneCustomArrows(laneId);
}
}

public override void OnLevelLoading() {
base.OnLevelLoading();
if (SavedGameOptions.Instance.DedicatedTurningLanes) {
Expand All @@ -243,6 +249,7 @@ public override void OnBeforeSaveData() {
public override void OnAfterLoadData() {
base.OnAfterLoadData();
Flags.ClearHighwayLaneArrows();
ValidateCustomLaneArrows();
ApplyFlags();
}

Expand Down
40 changes: 32 additions & 8 deletions TLM/TLM/Manager/Impl/LaneConnection/LaneConnectionSubManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -550,12 +550,31 @@ private void RecalculateLaneArrows(uint laneId, ushort nodeId, bool startNode) {
return;
}

var targetLaneIds = this.GetLaneConnections(laneId, startNode);
if (targetLaneIds.IsNullOrEmpty()) {
LaneArrows? arrows = GetArrowsForConnection(laneId, startNode);
if (!arrows.HasValue) {
LaneArrowManager.Instance.ResetLaneArrows(laneId);
return;
}

if (verbose_) {
Log._Debug($"LaneConnectionSubManager({Group}).RecalculateLaneArrows({laneId}, {nodeId}): " +
$"setting lane arrows to {arrows}");
}

LaneArrowManager.Instance.SetLaneArrows(laneId, arrows.Value, true);
}

private LaneArrows? GetArrowsForConnection(uint laneId, bool startNode) {
var targetLaneIds = this.GetLaneConnections(laneId, startNode);
if (targetLaneIds.IsNullOrEmpty()) {
return null;
}

ushort segmentId = laneId.ToLane().m_segment;
if (segmentId == 0) {
return null;
}

ref ExtSegmentEnd segEnd = ref ExtSegmentEndManager.Instance.ExtSegmentEnds[segEndMan.GetIndex(segmentId, startNode)];

var arrows = LaneArrows.None;
Expand All @@ -566,12 +585,7 @@ private void RecalculateLaneArrows(uint laneId, ushort nodeId, bool startNode) {
}
}

if (verbose_) {
Log._Debug($"LaneConnectionSubManager({Group}).RecalculateLaneArrows({laneId}, {nodeId}): " +
$"setting lane arrows to {arrows}");
}

LaneArrowManager.Instance.SetLaneArrows(laneId, arrows, true);
return arrows;

static LaneArrows ToLaneArrows(ArrowDirection dir) {
switch (dir) {
Expand All @@ -589,6 +603,16 @@ static LaneArrows ToLaneArrows(ArrowDirection dir) {
}
}

/// <summary>
/// Returns Arrows corresponding with outgoing lane connections
/// </summary>
/// <param name="laneId">source lane ID</param>
/// <param name="startNode">is start node lane side</param>
/// <returns>LaneArrows if valid and has outgoing connection(s), otherwise null</returns>
public LaneArrows? GetArrowsForOutgoingConnections(uint laneId) {
return GetArrowsForConnection(laneId, IsHeadingTowardsStartNode(laneId));
}

internal void ResetLaneConnections() {
Log.Info($"Resetting lane connections of group: {Group}");
connectionDataBase_.ResetConnectionsDatabase();
Expand Down
14 changes: 0 additions & 14 deletions TLM/TLM/Patch/_RoadBaseAI/SegmentSimulationStepPatch.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,20 +27,6 @@ public static MethodBase TargetMethod()
/// </summary>
[UsedImplicitly]
public static void Prefix(RoadBaseAI __instance, ushort segmentID, ref NetSegment data) {
// TODO check if this is required *START*
uint curLaneId = data.m_lanes;
int numLanes = data.Info.m_lanes.Length;
uint laneIndex = 0;

while (laneIndex < numLanes && curLaneId != 0u) {
Flags.ApplyLaneArrowFlags(curLaneId);

laneIndex++;
curLaneId = Singleton<NetManager>.instance.m_lanes.m_buffer[curLaneId].m_nextLane;
}

// ↑↑↑↑
// TODO check if this is required *END*
if (segmentID < lastSimulatedSegmentId) {
// segment simulation restart
++trafficMeasurementMod;
Expand Down
31 changes: 28 additions & 3 deletions TLM/TLM/State/Flags.cs
Original file line number Diff line number Diff line change
Expand Up @@ -146,12 +146,12 @@ public static bool SetNodeTrafficLight(ushort nodeId, bool flag) {
NetNode.Flags flags = node.m_flags | NetNode.Flags.CustomTrafficLights;
if (flag) {
#if DEBUGFLAGS
Log._Debug($"Adding traffic light @ node {nId}");
Log._Debug($"Adding traffic light @ node {nodeId}");
#endif
flags |= NetNode.Flags.TrafficLights;
} else {
#if DEBUGFLAGS
Log._Debug($"Removing traffic light @ node {nId}");
Log._Debug($"Removing traffic light @ node {nodeId}");
#endif
flags &= ~NetNode.Flags.TrafficLights;
}
Expand Down Expand Up @@ -316,7 +316,12 @@ public static bool ResetLaneArrowFlags(uint laneId) {
Log._Debug($"Flags.resetLaneArrowFlags: Resetting lane arrows of lane {laneId}.");
#endif
if (LaneConnectionManager.Instance.Road.HasOutgoingConnections(laneId)) {
return false;
LaneArrows? arrows = LaneConnectionManager.Instance.Road.GetArrowsForOutgoingConnections(laneId);
#if DEBUGFLAGS
Log._Debug($"Flags.resetLaneArrowFlags: Lane {laneId} has outgoing connections. Calculated Arrows: {(arrows.HasValue ? arrows.Value : "<none>")}");
#endif
laneArrowFlags[laneId] = arrows.HasValue ? arrows.Value : null;
return true;
}

laneArrowFlags[laneId] = null;
Expand Down Expand Up @@ -618,6 +623,26 @@ public static void RemoveLaneArrowFlags(uint laneId) {
}
}

public static void ValidateLaneCustomArrows(uint laneId) {
LaneArrows? laneArrowFlag = laneArrowFlags[laneId];
if (!laneArrowFlag.HasValue)
return;

LaneArrows? outgoingAvailableLaneArrows = ExtSegmentEndManager.Instance.GetOutgoingAvailableLaneArrows(laneId);
if (!outgoingAvailableLaneArrows.HasValue) {
LaneArrowManager.Instance.ResetLaneArrows(laneId);
return;
}

// check is laneArrowFlag is any different than available
if ((laneArrowFlag.Value & ~outgoingAvailableLaneArrows.Value) != LaneArrows.None) {
#if DEBUGFLAGS
Log._Debug($"Invalid custom Lane Arrows. Resetting for lane {laneId}! Available: {outgoingAvailableLaneArrows.Value} Custom: {laneArrowFlag.Value}");
#endif
LaneArrowManager.Instance.ResetLaneArrows(laneId);
}
}

internal static void RemoveHighwayLaneArrowFlagsAtSegment(ushort segmentId) {
ref NetSegment netSegment = ref segmentId.ToSegment();

Expand Down
Loading

0 comments on commit 183d209

Please sign in to comment.