Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Junction restrictions hook #1579

Open
wants to merge 24 commits into
base: master
Choose a base branch
from

Conversation

Elesbaan70
Copy link
Contributor

@Elesbaan70 Elesbaan70 commented May 22, 2022

Resolves #902

An IJunctionRestrictionsHook API implementing the idea discussed in #1557.

I had originally planned make this change before beginning the optimization/reorg of JunctionRestrictionsManager, but it turned out that I had to do most of that work in conjunction with this. But all the redundancies can't finally be eliminated until the patches are gone from the stable builds of NC2 and NCR.

Relevant PRs for the Node Controller projects:
MacSergey/NodeController30#55
kianzarrin/NodeController#33

@@ -574,5 +576,17 @@ public interface IJunctionRestrictionsManager {
/// Updates the default values for all junction restrictions and segments.
/// </summary>
void UpdateAllDefaults();

event Action<FlagsChangedEventArgs> FlagsChanged;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's incomplete. It's an event that will be invoked when junction restrictions flags change on a segment end, but FlagsChangedEventArgs is missing some properties right now.

Great example of this being a work in progress. I've just created the draft pull request so there's visibility on what I'm doing. 🙂

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To further elaborate, it's intended to be a clear and supported way for mods like AN to know when junction restrictions change. That way you don't have to detect changes based on assumptions about the implementation. Instead, you have a direct promise that this will always work.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mods like AN to know when junction restrictions change.

isn't that what notifier does?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mods like AN to know when junction restrictions change.

isn't that what notifier does?

Yes, but INotifier is limited in the amount of specific information it can share about the change. By adding specific events at the manager level, you can provide more useful information. And if implemented correctly, when no one is using them all they cost is a check for null.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I notifier does tell which manager caused the update. and it has space for manager specific even args. although the event args is just an object and do not have hard-coded type safety.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

although the event args is just an object and do not have hard-coded type safety.

And that's the key difference. Manager-owned events put less responsibility on the consumer. Fewer opportunities for error.

AllowForwardLaneChange = 1 << 3,
AllowEnterWhenBlocked = 1 << 4,
AllowPedestrianCrossing = 1 << 5,
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I need to control TrafficLights too. not sure if it would fit here. segmnetId would be irrelevant.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe this could be a rule override hook?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe this could be a rule override hook?

I anticipate that in general, hooks would correspond to managers. There might be exceptions, but since that's the existing organization of the API, it's the pattern that makes sense.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am patching Traffic lights too. and could be causing problems because my patch will break if we overload those methods. I can easily fix my patch but I don't know about NCR.

Traffic light manager does not seem to manage default value for traffic lights though. so that is a difference. I am only patching if traffic lights are configurable.

@Elesbaan70 Elesbaan70 mentioned this pull request May 24, 2022
@Elesbaan70
Copy link
Contributor Author

This is basically done now, but I want to get NCR working before taking it out of draft status.

@Elesbaan70
Copy link
Contributor Author

I had originally planned make this change before beginning the optimization/reorg of JunctionRestrictionsManager, but it turned out that I had to do most of that work in conjunction with this. But all the redundancies can't finally be eliminated until the patches are gone from the stable builds of NC2 and NCR.

@Elesbaan70 Elesbaan70 marked this pull request as ready for review May 29, 2022 18:37
@krzychu124
Copy link
Member

Solve conflicts and sync with master? 😅

@Elesbaan70 Elesbaan70 added the DO NOT MERGE YET Don't merge this PR, even if approved, until further notice label Jun 15, 2022
@Elesbaan70
Copy link
Contributor Author

Elesbaan70 commented Jun 15, 2022

We need to determine the correct resolution to the duplication of @kianzarrin's JunctionRestrictionFlags and my JunctionRestrictionsFlags before this can be merged.

@Elesbaan70 Elesbaan70 added code cleanup Refactor code, remove old code, improve maintainability API API for external mods JUNCTION RESTRICTIONS Feature: Junction restrictions and removed DO NOT MERGE YET Don't merge this PR, even if approved, until further notice labels Jun 18, 2022
@Elesbaan70
Copy link
Contributor Author

The number of external mods patching junction restrictions is increasing. This really needs to get merged.

@Elesbaan70 Elesbaan70 requested review from kianzarrin, kvakvs and krzychu124 and removed request for kvakvs, krzychu124 and kianzarrin June 18, 2022 15:17
@Elesbaan70
Copy link
Contributor Author

Elesbaan70 commented Jun 19, 2022

The end objectives here were:

  1. Simplify the Junction Restrictions API
  2. Cache Junction Restrictions calculations to improve the efficiency of pathfinding

The ordinary way to do this would be to make a new cached API, and have the existing methods call the new ones. Unfortunately, this could not be done straightforwardly, because the API methods are being patched by Node Controller, and now also by Adaptive Networks. The conventional approach would leave the new API methods unaffected by the patches, breaking those mods.

Therefore, the following strategy is being followed:

  1. Phase 1: Caching and Hooks API
    1. Re-implement the patched API methods as privately implemented interface methods so that the patch is no longer altering the API entry points. These still call the existing patched methods so that the patches continue to work.
    2. Create a caching layer between the new API and the patched methods.
    3. Introduce a "hooks" API that will allow external mods to alter Junction Restrictions behavior without patching.
    4. Release to stable.
  2. Phase 2: External mods change over to the hooks API and release to stable.
  3. Phase 3: We remove the extra patched method layer, simplifying and cleaning up the final implementation.

This PR represents Phase 1.

@Elesbaan70
Copy link
Contributor Author

Since most of the TMPE implementation was calling the API methods directly via JunctionRestrictionsManager instead of through its interface, they all had to be updated when the deprecated API was changed to privately implemented interface methods. This was necessary because the methods they were calling are now an uncached implementation detail, and are not guaranteed to work correctly if called from outside.

This means that a lot of files were changed. Following is a list of the files with functional changes. All other changes are refactoring and eliminating deprecated API calls.

API Declaration and Instantiation

TLM/TLM/Constants.cs
TLM/TLM/Hook/Impl/HookFactory.cs
TLM/TMPE.API/Hook/IHookFactory.cs
TLM/TMPE.API/Hook/IJunctionRestrictionsHook.cs
TLM/TMPE.API/Implementations.cs
TLM/TMPE.API/Manager/IJunctionRestrictionsManager.cs

JunctionRestrictionsManager changes

TLM/TLM/Manager/Impl/JunctionRestrictionsManager.cs

Rename a deprecated class to avoid confusion.

TLM/TLM/Geometry/Impl/SegmentEndIdApi.cs
TLM/TLM/Network/Data/SegmentEndId.cs
TLM/TLM/Util/Extensions/SegmentEndIdExtensions.cs

Copy link
Collaborator

@kvakvs kvakvs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm OK with this

StartNode = startNode;
}

public static bool operator ==(SegmentEndId x, SegmentEndId y) => x.Equals(y);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, no override Equals and GetHashCode?
It will generate garbage, not to mention it will be quite slow compared to version with the Equals

=> segmentRestrictions[segmentId].GetValue(segmentId.AtNode(startNode), flags);

public bool GetValueOrDefault(ushort segmentId, bool startNode, JunctionRestrictionFlags flags)
=> segmentRestrictions[segmentId].GetValueOrDefault(segmentId.AtNode(startNode), flags);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if it's a good idea to do lazy recalculation, especially on "get" while that method might be called by multiple threads at the same time (main, simulation, or any of pathfinding threads (1+)).

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I double that.

}

public bool IsDefault() {
private bool Set(SegmentEndId segmentEndId, JunctionRestrictionFlags flags, JunctionRestrictionFlags newValues, JunctionRestrictionFlags newMask) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe rename flags to values?

@@ -1368,70 +973,219 @@ private struct JunctionRestrictions {

private JunctionRestrictionFlags defaults;

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is the difference between mask and valid? maybe a bit of doc?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
API API for external mods code cleanup Refactor code, remove old code, improve maintainability JUNCTION RESTRICTIONS Feature: Junction restrictions
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Junction restriction manager is not using the cache it has
4 participants