CitiesSkylinesMods / TMPE

Cities: Skylines Traffic Manager: President Edition
https://steamcommunity.com/sharedfiles/filedetails/?id=1637663252
MIT License
577 stars 85 forks source link

Cars Pulling Over for Emergency Vehicles (Emergency Vehicle Evasion - EVE) #64

Open FireController1847 opened 5 years ago

FireController1847 commented 5 years ago

As found in many countries, vehicles must pull to the side of the road and stop if there is an emergency vehicle behind them or coming toward them. If at an intersection, all vehicles must STOP at the intersection regardless of the light color until the emergency vehicles has exited. Reckless drivers could possibly ignore this rule entirely (as seen in real life, people can refuse to pull over. My parents always honk at those guys lol).

I plan on implementing this, but I must research a lot more into how to do it. Mostly making this issue for logging sakes and so nobody steals it ;)

krzychu124 commented 5 years ago

Hahaha I'm sure nobody wants to steal this task

krzychu124 commented 5 years ago

Which milestone do you want ? 1.10.16? 😆 🤣

FireController1847 commented 5 years ago

Heh, I don't know if I'd be able to get it done in time for 1.10.16. I think we should hold off the milestone for now and maybe 1.10.17 / 1.11.0. I can confirm to you if I can get it done for 1.10.16 when I make the PR xD

krzychu124 commented 5 years ago

Just joking, great, feel free to ask for help, maybe I would find something while digging in other places 😉

pcfantasy commented 5 years ago

This is very very helpful in the game.

FireController1847 commented 5 years ago

@krzychu124 I do have a question and one thing for you to look for, if you can.

  1. What is a "Simulation Step" and how does it work?
  2. If you can find out how to get cars within a segment, that'd be awesome. I think I asked about this in Turn on Red, but didn't need it to do what was tasked. Now, to do this, I'd need to detect if there's an emergency vehicle in a nearby segment, which means I need to get the cars from within a certain segment.
pcfantasy commented 5 years ago

@FireController1847

uint num2 = vehicleData.m_path;
byte b = vehicleData.m_pathPositionIndex;
byte b2 = vehicleData.m_lastPathOffset;

PathUnit.Position position;
instance.m_pathUnits.m_buffer[(int)((UIntPtr)num2)].GetPosition(b >> 1, out position)

   You can find vehicle is in Position.m_segment.  You need a for {} to find cars within a certain segment
FireController1847 commented 5 years ago

@pcfantasy Haha, thank you so much! I was trying my best to understand this and I saw this but was completely lost by what it meant. This helped a lot.

pcfantasy commented 5 years ago

@FireController1847

VehicleAI.UpdatePathTargetPositions

In fact, those data can be read in everywhere, For example

You can do it in onBeforeSimulationFrame();

for (i = 0; i< 16384, i++) { vehicle vehicleData = Singleton.instance.m_vehicles.m_buffer[i]; var instance = Singleton.instance; uint num2 = vehicleData.m_path; byte b = vehicleData.m_pathPositionIndex; byte b2 = vehicleData.m_lastPathOffset;

PathUnit.Position position; instance.m_pathUnits.m_buffer[(int)((UIntPtr)num2)].GetPosition(b >> 1, out position) //then you can get everything use my above codes }

FireController1847 commented 5 years ago

@pcfantasy I hate to ask of this, but do you also know how to get the targetNodeId? I need to detect whether or not the vehicle's target is the intersection, or if the vehicle has left the intersection and everything can return to normal.

FireController1847 commented 5 years ago

Okay, now I need some opinions because there's a few ways I can do this. I'm currently implementing the "stop at a light no matter what if oncoming," not the pulling over aspect.

  1. The first way is that I can use the VehicleBehaviorManager to check and see if there's an emergency vehicles within the opposing segments, and if their targets are the intersection. This would not only require a loop and filtering of all vehicles to find the ones on the segments, it has to happen for every segment connected to a node, AND it has to recalculate the position of the emergency vehicle.
  2. I can add an attribute to segments with traffic lights like "HasEmergencyVehicleApproaching." Then, I can use the VehicleBehaviorManager to handle accordingly. This would require a modification of the emergency vehicle AI to where if the target node has traffic lights to update that node that it's approaching, and then when that node is no longer its target to set the boolean back to zero.

These are my two suggestions on how to do this. Do you guys have any ideas? I've been attempting the first one without realizing what a huge task it is, so I think I want to go with the second one. But if you guys can come up with better ideas, then please let me know.

Also, do any of you guys have Discord? If so, would you mind if we chatted about it? I'm FireController1847#3577

pcfantasy commented 5 years ago

@FireController1847

//you can try blow codes, I have not test this.

targetNodeId = Singleton.instance.m_segments.m_buffer[(int)this.SegmentId].m_endNode

//maybe there will be a reverse targetNodeId = Singleton.instance.m_segments.m_buffer[(int)this.SegmentId].m_startNode

//So there must be another vehicle direction to decide which is the correct targetNodeId

originalfoo commented 5 years ago

Why not add an EmergencyWeight integer to segments?

Emergency vehicles then increment the value a certain number of segments ahead (in diagram below, 4 segments, but could be any number depending on what works best) and decrements the number behind it.

      4     3     2     1
V -> ____  ____  ____  ____

So the emergency vehicle would create sort of a wave or ripple ahead of itself, warning segments that it's on the way.

If multiple emergency vehicles are heading the same way (common with fire engines) then that would create multiple overlapping waves, ensuring 'moved to the side' vehicles know to wait before continuing their journey.

Other vehicle AIs would check the EmergencyWeight for the segment they are on and take action accordingly. Likewise, traffic lights would also take account of the value. Even toll booths could prepare themselves by allowing a lane of traffic to pass without paying to ensure the emergency vehicle has clear route. Busses could avoid pulling out of bus stops... anything that's travelling on, or is part of, the road network, can adapt by looking at the EmergencyWeight value.

Note: I'm hopeless at monomorphic coding (like C#) so if what I've said above is stupid don't hesitate to ignore it (I know enough to know that I don't know enough!).

krzychu124 commented 5 years ago

Generally I like your idea 👍 , but I think it would look quite strange when vehicles were started to pulling over up to 3 junctions ahead of ambulance (imagine that ambulance has to follow this route:

                * <- target
                |
        *-------*
        |
V ------*
originalfoo commented 5 years ago

They only need to start pulling over when it the weight reaches a certain value (for example, 3 or 4).

But other things might need to adapt sooner, such as traffic lights and toll booths (they would start acting when the weight is lower, for example 1 or 2).

There are some problems with the general approach I propose, for example what if an emergency vehicle despawns for some reason (eg. user uses "Clear Traffic" tool)? So what it might need to do is at the start of each frame clear all the weight values; that way emergency vehicles don't need to worry about clearing up after themselves so much.

Furthermore, remember that once a vehicle passes, if there are others nearby on same route, you don't want "pulled over vehicles" to start driving back in to the road, they should wait for 'weight = 0' before doing that. This is particularly important for fire engines, but also important on roads near the emergency service buildings which might be spawning multiple vehicles.

Another thing: The "pulling over" should only happen where necessary, perhaps on smaller roads. On larger roads, or roads with bus lanes, it's less important as the emergency vehicle can lane swap.

Strdate commented 5 years ago

I would say that the important part of this code is to know when to trigger the vehicle pathfinding recalculation. I don't know if there is a list of vehicles on current nodes, but it should be because how would otherwise traffic lights and stuff like that work? I recommend looking at their source code. It think that for this to work well you would need to allow the vehicle to use the opposite direction lanes. Overall, this feature is one of the most difficult tasks. Then there is the limitation that vehicles cannot switch lanes anywhere else than on junctions (taking into consideration the virtual ones as well). (Or am I wrong?) So it is a question how realistic the behavior will be.

originalfoo commented 5 years ago

When vehicles park, or when things like ambulances and hearses pull on to pavement, the code that does those things would be worth inspecting.

IMO it would be worth doing some mini mods, like on that just deals with making vehicles on a segment pull over when segment is clicked, and then resume driving when segment clicked a second time. People could test that out and report back any obvious bugs or issues. I think if we try and release this feature in one big update, there will be so many bugs that we won't know where to look.

Strdate commented 5 years ago

I agree with this. I think that slowing down is ok, but anything more complex like going on the side is something which requires more than what usually happens in TMPE mod.

krzychu124 commented 5 years ago

AFAIK ambulances and hearses pull on to pavement because every building in game has set spawn point there which in this case is used as destination target

In regards to problem that vehicles cannot change lanes maybe we should create these nodes (before and after accident) if it's possible (I didn't tried yet)

originalfoo commented 5 years ago

For accident zones: When a road is demolished and new one built nearby, cars will travel from their current location to the new road (if it's near enough) - so we could simply 'block' (somehow) the affected lanes and repath nearby vehicles (they might do it automatically without us needing to repath them?) - they will swerve on to remaining paths.

FireController1847 commented 5 years ago

While yes I agree this is a very complicated feature with many ifs and buts, I don't think it's too much to add to TMPE. TMPE is supposed to give people control over their traffic, right? By allowing people to have cars pull over for emergency vehicles, it gives even more control over their traffic. I don't see this being any smaller of a feature than #46, no? Overall, though, I'll let @krzychu124 decide if I should make this a separate mod or not.

On another note, @aubergine10 I like your idea of having a "weight" for emergency vehicles. However there's one major issue: Segments are huge. If we were to do 3-4 segments down, it would be the equivalent of stopping vehicles miles away. Now I know you said they'd only stop on the highest one, and I think that's a great idea, but that provides us with a problem of needing to calculate three to four segments in advance for the emergency vehicles. I'm having mixed feelings about this but I feel this is our best option, honestly.

When it comes to traffic lights, we will need a way of detecting if there's a blocked path. When in a traffic jam, vehicles cannot pull over or cannot move. Due to this, vehicles must continue moving forward as if there wasn't an emergency vehicle there until there is a clear path to the intersection. I have a great example of a blocked road in one of my maps at home, so later today I'll be able to send an image of it.

Having vehicles pull over and stop does not seem very difficult. Would it not just be setting their trajectory like how busses pull over, like mentioned? Busses do the exact thing that cars need to do.

I agree that this is a daunting task, but I believe you guys are making it more complicated than it needs to be really. There's three major things we need to do for emergency vehicles:

Don't look at this from a general perspective (unless you're brainstorming about the entire feature, in which case feel free), but look at this from the initial three points. If we add features slowly, step by step, instead of trying to tackle it all at once, we'll actually be able to do something.

Strdate commented 5 years ago

Nobody wants to exclude this from traffic manager, but the idea is that this requires testing of so many features which should be rather done outside TMPE. This is indeed very daunting task, it is connected to half of other features we would like to implement.

originalfoo commented 5 years ago

As @Strdate says above, community testing is going too be important for this feature - it has to be tested on all sorts of roads, junctions, etc. What effect does it have on trams, bikes, busses? What about roads with multiple medians and weird lane arrangements... The end result will be in TMPE, but initial dev might be better suited to a separate mod where we can easily roll out changes to a large audience of beta testers rather than waiting for TMPE release cycle.

Anyway, for the cars pulling over, I think the best place to look might be how parking lanes work. That's almost exactly what we want (more so than bus stops), we basically make an entire lane of traffic "park". We could perhaps create fake parking lane for them to park in or something? That way we can reuse vanilla code that does the parking manoeuvres.

If we release a mod with that "pull over" feature, activated by clicking a segment to toggle between "pull over" and "go back to normal", it will allow lots of people to test it out and let us know what happens.

krzychu124 commented 5 years ago

The best would be create separate github repositories and maybe Steam workshop items to test small parts of this feature (As @aubergine10 there would be tenths of different roads types to test with).

Later we could try to merge those small parts to create something bigger and test again.

I've been thinking about splitting TE:PE to be more modular and open on extensions (plugins) like other great big Steam workshop mods. Tell me what do you think. Is it possible and worth effort?

This feature could be a sort of plugin to mod base, but first I need to make some tests to check if it is possible.

FireController1847 commented 5 years ago

@krzychu124 I don't think it's worth the effort. I know a lot of people would rather have a mod that can be installed with one click and then modified how they wish, versus having to try and find a bunch of different workshop extensions which may or may not be updated and may or may not be listed in the description which may or may not be working. When it's all in one big bundle, you can be sure it will either work or it wont -- or at least, some features will work and some wont. A great example of bundled would be the NeXT mod, as well as how TMPE is now. You can see the total downloads and are sure that it will either work or not. Now look at the mess of a mod called Roads United. There's many different versions of the base mod, some of which work and others don't, there's many extensions that sometimes work and sometimes don't, and there's often confusion between which mods are required and which mods should be enabled. I personally would much rather have a mod like TMPE is now where you can enable and disable settings as you please, all in one place.

@aubergine10 @Strdate Apologies of my misunderstanding. I think it's a good idea to make a separate workshop mod & GitHub repository to test these features, however I would have no idea where to start. How do I access TMPE methods? How do I set up Visual Studio? Do I make it a completely separate mod? Also, what if I need to use protected/private methods?

krzychu124 commented 5 years ago

With regards to setting up Visual Studio and accessing methods, just add compiled TrafficManager.dll to project references and you will have access to everything (public). Reflection should help with protected/private methods - it's just test it doesn't have to have max performance, I think.

originalfoo commented 5 years ago

@krzychu124 I agree with @FireController1847 , I think TMPE is best as single mod for now.

Even back in early T++ and TM days, back in 2015, people were asking for modularisation. But it became clear that doing so actually caused performance issues. The approach TMPE takes currently seems to work well in that it recompiles itself with just the activated features, thus completely removing unnecessary code from runtime.

Imagine if it were a bundle of separate mods, all having to interact with each other to some degree. Also, what if in a year or so, we've all gone off to do other things and someone else takes over with a new branch of the mod. I think this would be confusing for end-users to try and work out what component mods work with the main mod; like @FireController1847 says, we'd end up with a "roads (dis)united" situation.

I can obviously see the benefits of modularisation, however, such as being able to rapidly iterate individual features without needing to roll out the full mod, but in practice I think it always turns out not to be as simple as that. Another example is the various 'extensions' to "Move It" mod that were released; despite the main Move It mod being stable and not changing much any more, those extensions were short lived and in the end it was easier for quboid (sp?) to just release a new Move It with all those features bundled in to the main mod. Compared to TMPE, Move It is a simple mod, so...

krzychu124 commented 5 years ago

Thanks @FireController1847 and @aubergine10 for your opiions 👍 So now we can focus on more important things 😉

FireController1847 commented 5 years ago

I think I'm not fully understanding this "separation" thing. How do I make a mod that modifies the logic of TM;PE? Particularly, the driving logic? I would need to interrupt the normal driving logic (which, by the way, I have no idea where it is) with the parking logic. Wouldn't that require me to make a modification to the AI, which you can't do if you only extend/use functions within TMPE?

krzychu124 commented 5 years ago

In this mod we have pathfinding function which finds paths from source to destinations and vehicle behavior which is responsible for their travel along path (stop, move, wait etc.) In case we want to force vehicles to pull over I think vehicle behavior class would be better place to start

Just throwing my ideas below...

I suggest to digging through code to find what is going on when ambulances driving on emergency (Flag.Emergency2 in vanilla). Sometimes I can see that ambulances are moving in-between lanes or turning right with overtaking other vehicles at junctions. There must be a line in code where their path bend is performed.

FireController1847 commented 5 years ago

Alright. I'll look at some of that path stuff this weekend.

FireController1847 commented 5 years ago

Okay, I've began the creation of the separate mod for testing. You can find it at FireController1847/Cities-Skylines-Emergency-Vehicle-TMPE-Extension. So far I've only added the button to the vehicles, not any cool things like the beginnings to pulling over. Tomorrow I will research more into that :)

FireController1847 commented 5 years ago

So I've been experimenting (this is like my favorite phrase now I guess xD), and I've found some things. First, I found that "FindBestLane", which is part of VehicleAI, it used during the UpdatePathTargetPositions. TM:PE does not currently override this method, so I did using the mod. And it works, however I think I may be in the wrong area.

If we want to detect if there's an emergency vehicle, and have the vehicles pull over, should I be using UpdatePathTargetPositions, or should I be using something else? Because if I'm in the fundamentally wrong area, then I don't want to waste hours on something that wont work. Using ILSpy, it's clear that the FindBestLane thing relates directly to the parking of vehicles within the simulation step. Within TM:PE's custom UpdatePathTargetPositions for vehicles, there's a comment called "park vehicle." Does this have to do with anything, or does it not do what I need it to?

Edit: I can't seem to even get vehicles to stop using that method. Do any of you guys happen to know where I should be putting the code to make vehicles stop not at the end of segments, but in the middle of the road? Because if I can find that, I can find where I need to put everything else.

This is particularly odd: Whenever I try and add the "Vehicle.Flags.Stopped" flag, all the cars disappear O.O

Edit 2: So, studying the code, it appears simulations steps run like the following:

Things Worthy of Note

krzychu124 commented 5 years ago

It's fun to watch you fighting with game codebase (edit log) 😄

There is a thing which could greatly help you with investigations and testing. It's magic tab with name Call Stack if you are debugging with dnSpy 😃

In game we have a lot of threads but we can separate them on some general:

No worries I will try to help you later today.

FireController1847 commented 5 years ago

@krzychu124 Sorry, I'm not normally a C# coder (I'm more Java), so I'm still learning all of this visual studio stuff >.< And just wow, dnSpy is amazingly useful. Thanks for suggesting it to me.

I'm unsure where to find the path finding methods. Is that were we should be putting this? I'd assume so, that does make sense (if there's vehicle pathfind to tell card to pull over and step).

Sorry I wasn't able to "be helped" throughout the day, I think we may be in very different timezones (as of this post it's 3:44 PM for me), but I try my best to be as active as possible 🙂

Also, what is the different between CustomPathFind2 and CustomPathFind?

krzychu124 commented 5 years ago

No problem I am Java coder too (and Typescript/Javascript). C# was my first language that I've learnt and I still like it.

I'm unsure where to find the path finding methods. Is that were we should be putting this? I'd assume so, that does make sense (if there's vehicle pathfind to tell card to pull over and step).

Me too, and that pathfind sorcery... I'm still trying to understand what is going on there, so for now I can't help here. Give me a few days...

CustomPathFind2 is current pathfind implementation and CustomPathFind2 is older - currently not used.

Haha timezones. I see 11:56PM on my system clock.

FireController1847 commented 5 years ago

Alright, not a problem. I'll also take a look and see if I can't understand some of it too! 🙂

krzychu124 commented 5 years ago

It's hard to read if you see those preprocessor directives (for conditional compilation) everywhere...

#if CONFIGURATION_VARIABLE
#endIf
originalfoo commented 5 years ago

I'm not sure it's the pathfinder you want; we're not changing routes of vehicles, just asking them to temporarily pull over. So, IMO, it should be in the vehicle AIs - they pause from their path to let emergency vehicle get through, then return to their path. That being said, I also am still wading through code trying to work out how all this stuff works so I could be talking rubbish.

FireController1847 commented 5 years ago

@aubergine10 That's not rubbish, it appears to make a lot of sense. I've been looking more in-depth at the CarAI.SimulationStep (with 5 arguments), which appears to be the area we want. I got here by following the calls to the VehicleBehaviorManager (which stops cars for junctions). My current goal is to literally just add a button that makes cars stop in the middle of the road.

FireController1847 commented 5 years ago

Huzzah! I've reached my goal of making all the cars stop. After wondering why my override went into an infinite loop (crashing the game), I finally got it to work. This is the first big step!

To test it, build & enable my "Emergency Vehicle Extension" mod, and then enable the "buildings" overlay for TM:PE. When you enable it, all cars stop moving. When it's disabled, they resume.

originalfoo commented 5 years ago

I was looking at code and noticed that there are separate simulation steps for things like flooded (car washed away) or blown (car sucked in to tornado)... Were you planning to do something similar with the stopping for emergency vehicles? As separate simulation step could make them stop or pull over or something?

originalfoo commented 5 years ago

Link to repo: https://github.com/FireController1847/Cities-Skylines-Emergency-Vehicle-TMPE-Extension

FireController1847 commented 5 years ago

@aubergine10 That's actually not a bad idea, I'll check into that at some point this weekend or this week (probably this weekend, though). (Edited out my question, I'm dumb, I know what you're talking about now lol)

Also, @krzychu124 I have no idea how to add/remove my own VehicleFlag, which is something I'll need to be able to do this. Does TM:PE do this anywhere within the mod? Are there any workarounds that you know of?

On another note, @krzychu124 & @aubergine10, do you guys think it's a good idea to add the ability to get the cars from a lane? I think it could be useful for future issues (even though this does not pertain to this issue), especially if we need to fetch the vehicles around a vehicle. I can't think of many other use cases for that, though.

originalfoo commented 5 years ago

We'll probably need to get cars from a lane anyway... We'd have to decide a lane for the emergency vehicle, and then move cars either side (where applicable) of that lane out of the way so the emergency vehicle has a clear lane.

As for adding vehicle flags, I have no idea how to do that, however I would suggest investigating the code of the Real Time mod: https://github.com/dymanoid/RealTime and ideally chat to Dymanoid. From what I understand, he created some sort of 'extension' for citizens so he could detail their daily routine, and I imagine the same approach could be used for vehicles (and transport networks too I suppose).

Dymanoid really nailed the development of that Real Time mod - not only did he add loads of stuff in a very reliable manner, it barely affected performance of the game. I think there's likely lots of his coding techniques that would benefit TMPE.

originalfoo commented 5 years ago

saving these for prosperity

garbage_truck

giphy

originalfoo commented 5 years ago

bus car

Strdate commented 5 years ago

From the screenshots it looks soo good! Maybe garbage trucks travelling in the opposite direction is a bit of an overkill (it would be too easy for the player and it is not too realistic), but emergency vehicles can really use that! Pls update the github repo if you have time! 🥇

FireController1847 commented 5 years ago

@Strdate Those were test cases that were modified live using the debugger, there's not any actual in-game code to run this. However, I am currently implementing that, while @krzychu124 is currently looking at ways to get the vehicles to pull over! :)

originalfoo commented 5 years ago

@FireController1847 Can you upload that video of the police car going crazy to youtube or something for easier sharing; we have to save that for prosperity!