robot256 / SpaceTrainNetwork

Factorio mod in Lua to dispatch trains
1 stars 0 forks source link

Feature design discussion #2

Open robot256 opened 2 years ago

robot256 commented 2 years ago

Use this thread to provide comments and suggestions for the initial development.

robot256 commented 2 years ago

First question for feedback:

Which routing scheme would be best?

  1. Vanilla train limits: Each train has a fixed schedule. STN makes each train wait at each stop until the next destination is accessible and can be reserved in STN's table. The vanilla train limit parameter could be set by the user and read by STN, for almost transparent operation, besides having to modify each schedule to force the train to wait and to transit surfaces.
  2. Logistic Train Network-style dispatching. Trains wait in common depots until an item request can be paired with an item provider.
  3. Train Supply Manager-style dispatching. Trains wait at "supplier" stops until a "requester" stop with the same network ID summons them. The contents of the train doesn't matter, each resource gets a different network. The difference from vanilla limits is that empty trains can be sent to an "empty train provider" on its own network and requested by any loading stop.
robot256 commented 2 years ago

Second question: How much user input should be required to configure the inter-surface links?

  1. Entirely manual. Use a mod GUI to set up which surface links exist, the schedule sequence needed, the path penalty, and (potentially) which "networks" are allowed to use each link.
  2. Entirely automatic. Use rail network traversal and bespoke compatibility code with each mod to detect which links exist, if they are operational, and which train stops are connected to each side.
  3. Naive. Check for potential links between surfaces, and assume they are accessible from any train the user schedules for stops on both surfaces. This would require unique names between linked surfaces if the link is not accessible or not intended to be used. Also might have to assume the user sets particular names for the transit-entering train stops.
suisseWalter commented 2 years ago

for question number one :

  1. would be the most minimal implementation, and it wouldn't solve many of the problems you have listed in the readme. you would still need a large amount of trains,(at least one train per ressource) as STN would only allow the automated usage of elevators.
  2. I think this would be the most powerfull implementation, as it allows for the biggest freedom for the user. as one can assign one train to more then one job(sequentially). it also allows for trains carrying more then one ressource. but it would also frequently lead to underfull trains. which loads the tracks a lot more, and also causes higher elevator costs. This could be avoided with better train dispatch and requests. it should be possible to only request trains if a full train can be delievered.
  3. this allows for more flexibility then vanilla but I feel like it still limits the flexibility compared to a ltn type implementation. as there has to be at least one train per ressource always in the waiting. it also makes it hard to do providers with more then one ressource. which, especially given SE, is quite limiting, it's nice to be able to do mixed provider and requesters. but with such a implementation we can't have that.

Looking at these options I personally would go for option 2 as it gives the most flexibility especially in something like SE, where there are many different ressources and some of them are frequently needed in combination.

for question number two:

  1. I think a manual approach would be completely feasible, especially in the beginning. And I personally would prefer to be able to override any automatic system anyway. as I might not want some trains/networks to cross of not absolute needed. as elevators can be expensive. but that might not be the case for other inter-surface links.
  2. I think having a entirely automatic way would be quite limiting, if we can't override it manually(see above). but it would be nice to have automatically configured setups when two surfaces are connected, but still being able to override them.
  3. I don't think that would be worth it, or maybe to generate default configs for the manual approach. I feel like as soon as people have to rename stations to particular names it gets quite hard.

I think I would prefer a manual approach for this question, as it shouldn't be to hard to generate the gui and I think having a override for the automatic one would be really practical too. so I think nothing would be lost by going for this one first. (also it means less performance overhead as we don't need to check for new connections between surfaces)

robot256 commented 2 years ago

Thanks for the feedback! I am very glad to have someone critique my ideas.

I added another concern to the readme, to make the learning curve manageable for players who haven't used complicated train routing before. I don't want to replicate 100% of the features of LTN. Reducing the total number of trains is not the goal, only reducing the number elevator transits.


For dispatching method: Option 2 is the most complicated to implement, and learn to use. I'm a little hesitant to sign up for this amount of code and maintenance unless it's absolutely necessary. I also don't like the number of empty trips it causes in general. There are some tricks we could do to limit the number of empty trains making transits, like giving priority to storing empties in a depot on the current surface unless the other surface has not enough empties. But that ends up making it even more complicated than LTN.

For multiple-resource providers, option 1 would work with the method of filtering wagon slots to carry multiple items. With the train dedicated to that set of resources, the schedule, and potentially circuits can be set to travel whenever any one of the resources is needed. It's cumbersome but doable. But would also result in extra transits if only half the train's contents was actually requested, and the train would always have to return.

Option 3 can be done with multi-resource providers either with filtered wagons, or if each resource has a separate waiting area station they load up, and circuits to tell which resource should be loaded. Though I might be recalling how I combined the Train Supply Manager, Dispatcher, and Automatic Coupling System mods into a frankenstein's monster of a network.

With option 1, there are also vanilla ways to manually pair up-going resources with down-going resources, so that return trips would not be completely empty. That comes down to whether we want the user to make a complicated vanilla-like setup, or learn how to use the LTN system and thereafter have a potentially simpler system.

In my opinion, the strength of option 1 is that it allows all the vanilla techniques to be reused, and also several other train mods like Dispatcher and Automatic Coupling System. Even if we implement option 2 eventually, I am leaning toward implementing option 1 first, as a separate mod, for that purpose.


For network discovery: Agreed that manual configuration is the most transparent and least performance cost. I do worry it would confuse people if they forget to add a route or it gets misconfigured for some reason. And it rules out the possibility of automatically routing through Train Tunnels, or using two space elevators to cross a planet's ocean, for example. If you have to manually specify which elevator stops to use for each station pair, then it's no different from scheduling the elevator transits manually.

I guess I don't see how automatic route discovery would be a problem gameplay wise. We already have to segregate trains that are on the same surface, by using different stop names or different LTN network IDs. With a sufficient path cost, there is no risk that a train would path up-down-up-down unless something was really wrong with the rest of your network.

The hybrid link system is what I tried on my personal LTN hacking session, before I knew you could rename elevators at all, and it was very annoying even with every elevator as the default name.

suisseWalter commented 2 years ago

about dispatch method:

I do agree that 2 is the most code intensive one and therefore the hardest. I'm just not sure if 1 would really be a good first step, or if it would more be a side-step. because I see a big difference in how it operates, from a mod level. as it basically needs to modify existing schedules to create the train limits. while both 2 and 3 will create schedules from only the internal data. of course this could also be avoided by doing 1 in a different way, namely a read schedule -> store schedule internally -> delete schedule -> modify internal schedule -> write new schedule**. this way a lot of code could be transferred into the other mod. I also see the appeal of a type 1 mod for people going more vanilla. and still wanting inter surface train limits.

**I thought of the following other two options to do this but there are probably even better ones: i) turn train to manual if it shouldn't drive. (can cause a lot of confusion.) ii) insert fake station that either has a exceeded limit or is impossible to reach.


for tunnels a automatic configuration would be nice. connecting stuff automatically through elevators I see no problem with. The part where I would see manual configuration to be nice is at the transfer penalty side, how much should the mod avoid to move items through the elevator. but the connection between the surfaces could be done automatic, even we would give the options to configure that manually.

robot256 commented 2 years ago

I looked at the Factorio API again, and I am not as excited about automatic connection finding now. There are only two ways to find what stops can be pathed to from where: Create a train and tell it to find a path, or duplicate the entire Factorio rail network connection and pathing logic in the mod. I suppose creating an invisible dummy train and asking it to path to/from the surface entrance/exit could work to determine which ones are linked, but that is still a lot of work and has to be redone every time the rail network changes.

I don't think the above process will ever make sense to replace a valid vanilla train path with a multi-surface path, so the only trains that should be modified are ones that cannot already get to a station on their own surface. That means the schedule would only be modified if the train is already stuck in the state of "destination full" or "no path" and an open station exists on a linked surface. I don't see the need to make manual scheduling of elevator stops unnecessary in every single instance. Unusual ones like bridging oceans do not need to be handled.

I think for vanilla-like scheduling, this mod's behavior does not have to be completely invisible to the user. As long as the mod behavior can be triggered with a specific, relatively simple network design, that will be good enough.

The Train Tunnels mod is still very buggy and I think I will not try to support it right now. I think it has been sufficiently demonstrated that without Factorio support for interdimensional train portals, mod-teleporting trains is very slow and inefficient.

Dispatcher saves the schedule and replaces it with a blank one, and this has caused me a lot of headaches when the saved schedule is not properly restored. Limiting schedule adjustment to when the train is already stuck should solve this.
Concurrent Train Restriction does exactly (ii), creating a fake station and assigning it to trains that are "paused".

robot256 commented 2 years ago

I did another test of the train-stuck behavior. A train will pause its schedule if all stops of the target name on that surface are closed by the train limit, OR if all stops of the target name on that surface are inaccessible. If there are no stops of the target name on the surface (i.e. it only exists on the linked surface) then the train will skip that stop. There is no event when the nonexistent schedule entry is skipped.

That means for my simple method to work, you need dummy stops on the origin surface in order to trigger mod routing to the other surface. This could be handled by invisible stops created by a mod GUI, or a requirement of the user to place dummy stops manually in order to specify which stops are accessible from the other surface.

If a stop on the same surface is both inaccessible and full (or limit=0), the train pathing to it will say "destination full" rather than "no path".

If there are two stops on the same surface, one accessible but full and the other one available but inaccessible, the train will say "no path" as it is trying to get to the inaccessible one. If the accessible one becomes available, the train will not repath to it.

If there are two available stops on the same surface, one accessible and the other inaccessible, the train will reserve the accessible one even if it is farther away. This is good, because it means the relative location of the inaccessible dummy stop is not important. Real stops on the same surface will always take priority over selecting a dummy stop that requests a transit to another surface.

More notes: