Round robin when multiple requesters request same item
johalun opened this issue ยท 8 comments
Describe the feature
If there are multiple requester frames requesting the same item, it would be nice if the logistics drone/widget could round robin between them.
Reasons why it should be considered
This is useful when you want to split delivery of items to different locations. If the two target inventories are input to some item processing, the inventories would constantly be emptied and the drone always delivery to only one of them.
Additional details
I guess this could also apply to storage frames that store the same type of item.
I think the simplest code change would be to just round robin instead of arbitrary picking a frame from the list / hash table of available frames. I might take a look at this if I get some spare time.
The channel idea sounds interesting, although it would require multiple frames on the source inventory, unless you can configure different channels simultaneously in one frame, like a tab for each channel. This would require a lot of implementation work though..
Thanks for the tip with the redstone shutoff. It would work but makes everything bulky and ugly which I rather avoid on my IE/Create factory floor. Yes, aesthetics are important :)
I handle this by having one active provider or one storage frame supplying to two requester frames, each set to only request 1 or 2 of an item. You can use that setup as-is (good for processing stuff in a furnace) or have the requester attached to a hopper attached to a chest which is redstone monitored and sends a shutoff signal when > some threshold. It works pretty well, but I've never run it at insane item transfer speeds so I'm not sure how scalable it is.
I wonder if this might come 'for free' if logistics modules also supported coloured channels like the redstone modules do, although that would probably require extending the channels to frames as well (as Drones don't have a concept of channels at all). It's still an interesting thought, because then you could split from one channel into two just by using any inventory as a buffer with two storage frames (one for each color)
I have taken a look at this, and I do have concerns about performance here; right now, the logistics manager scans through applicable frames, and picks the first one it finds which is requesting items that the drone or logistics module in question is able to provide.
To make this behaviour round-robin, all frames would need to be scanned, which could be a non-trivial server CPU hit in regions with many frames. I can do some experimentation with (and if you're interested @johalun, the code is in LogisticsManager#getTasks()
- maybe you can come up with an elegant solution ๐ )
How often does it do this scan currently? Every time it's looks for a frame, or once per invocation of the logistics widget?
Looking at the code, from what I can tell, all frames are already in the logistics list. So, if when iterating over them we can just check for some flag, or counter, before the isObstructed
check, it shouldn't have that big impact I imagine. I will tinker a bit see what I can come up with.
Drones do the scan (for tasks - frames are cached when the drone is deployed, and the cache is invalidated whenever a frame is placed or removed) each time the Logistics puzzle piece executes.
Logistic frames do the scan once every 20 ticks as long as there are tasks to do. If there are no tasks, they back off to every 100 ticks.
So finding the frames isn't the issue, it's scanning the inventories that the frames are attached to, and matching provided items vs requested items. This could add up quite significantly when there are a very large number of frames.
Drones do the scan (for tasks - frames are cached when the drone is deployed, and the cache is invalidated whenever a frame is placed or removed) each time the Logistics puzzle piece executes.
This is good. That means the logistics
ArrayList won't change unless there's a change in the frames within the area.
So, if we can change the order in which the frames are iterated for each invocation of getTasks
by using a custom "round robin" list instead of plain ArrayList for the inner list there should be no penalty. The round robin list should retain its internal ordering between invocations of getTasks
until there's an actual change in frames in the area where it would get recreated, if I understand correctly.
The round robin functionality could be as simple as doing remove(i)
+ add
on the ArrayList (given that we don't use for-each to iterate) to move the selected frame last, or something more fancy like a custom CircularList or RoundRobinList class for example.
Sounds doable to you or did I miss something?
Yeah, this could work in theory. Note the logistics
list is actually a list of lists - each element of the list is a list of frames, indexed by the frame priority, where priority is basically the frame type:
- Active & Passive providers = 0
- Default Storages = 1
- Storages = 2
- Requesters = 3
Any frame can provide to a frame of higher priority (special case: passive providers only provide to requesters). When the scan for tasks is carried out (which can be quite frequent), the list is iterated from high priority to low, and all frames at that priority are checked to see if they can request items/fluids from any frames of a lower priority. As soon as a match is found, the scan stops.
You'll notice that priority 3 scans all frames of priority 0/1/2, 2 scans frames of 0/1, and 1 scans frames of 0. So this basically O(n^2 / 2) right now, which is why care needs to be taken not to scan more frames than strictly necessary.
With your suggestion, we'd be potentially rotating the list of frames at each element after each scan, which I think could work. Since each LogisticsManager
is specific to a drone or logistics frame, this does mean each drone/frame could have their own frame-search policy, if desired (random/nearest/round-robin/etc.). Although nearest would require a distance-sort to be done whenever the drone moves...