Bulk Teleporters

by dorfl

Use spare gigajoules to teleport shipments of items.

Content
3 years ago
0.17 - 1.1
1.99K
Logistics

i Power settings?

4 years ago

With their current power draw, these things are better than trains in every single way. Hilariously so. In my own game I have modded them to draw ~500MW each (instead of 20) because that way there is a meaningful impact to my power grid (~90GW) when they spin up. Rather than have you change the default value for the mod, would you instead consider adding a mod setting to adjust the base mod power? That way I don't have to keep manually merging my fork with your root mod.

4 years ago

Yeah, I've wondered how to make these scale better with power grid size. Unfortunately since recipe energy usage is static, a setting might be the only option.

4 years ago
(updated 4 years ago)

Hey man. So I've realized this mod is the source of stuttering in my game (basically every few seconds the game will briefly freeze; shows up in F5 view as a 300ms tick update). The reason why has to do with how you're going through teleporters. After spending a few hours thinking of ways to fix it, I had a fairly decent idea that...actually changes quite a lot about how the mod would work. But tell me what you think regardless.

Instead of having the teleporters have a variable charge time based on distance, have them use an energy buffer. You can then set the size of the buffer and how fast energy is allowed in to control the charge time; this removes the need for modules on the teleporters, and for all the code around 'is_crafting()'. Once the energy buffer is full the teleporter can instantly move items from source to dest. Why would you want to do this? Currently you need to iterate through both source and destination during ontick() calls, this would allow you to update both source and dest at the same time in the same ontick() call, and you'd only ever need to scan through senders. The final piece of this upgrade would be changing find_target to not worry about who is emptiest of them all. Rather, just leave the list static and always send to the first available target. This saves some checking time, but also makes it super clear that more senders are needed on the network; if something in particular is starved and shuts down, it's obvious there is a problem. If is sometimes deemed 'emptiest' as currently happens, it is much harder to figure out you need more production...and checking for emptiest costs more time in the ontick() call which is super sensitive to delays already. :(

"But what if the player wants to make it send faster?" Look into how this mod: https://mods.factorio.com/mod/MFDefenses made upgradable walls and defenses. Basically, you define (in a loop) a bunch of higher tier versions of the sender/receiver (all the various upgraded names would have the same in-game name in the locale file though). Each version has a higher charge rate than the previous one.

So say that you decide you want a teleport to cost 3GJ. Well, if the player has a really tiny base and only 3GW of total power output, and 500MW to spare, then they could charge, at most, at 500MJ/s. Now, maybe the first version of your teleporter only allows that model to charge at 250MJ/s. That's fine, but you'd have research options to increase the charge rate. But that would be a two-edged sword. If the player actually upgraded to the point that they were able to charge at 650MJ/s then they'd actually have massive power problems in their base, so they'd have to be smart about upgrading.

Similarly, as bases get bigger, and accumulator stacks are able to handle massive power spikes better, a large base might be able to let a charge rate of 30GJ/s slide, meaning that each teleporter could send items every 100ms (this would also require the ontick() code to be faster :p, but whatever).

edit: There are three ways to tackle range: 1) Decide you don't want to allow faster charging, ever, and instead make buffer capacity the thing you scale on. Larger buffer = takes longer to charge! 2) Define a large buffer (say, enough for up to 5KM) that doesn't change, but then determine the energy cost to teleport and only subtract that amount from the buffer, rather than emptying it. 3) Have both buffer size and charge rate be upgradable. If someone wants to send really far, they have to put a lot of research in to upgrades needed to get there. Would require a LOT (hundreds? Thousands?) of possible entity prototype definitions. For loops are great, but still...

What are your thoughts?

4 years ago

How many teleporters were in your network when you saw the 300ms stall? I've only tested with a couple hundred.

Do you think it's find_target that's actually the main culprit? OnNthTick is intended to check a single teleporter from the queue each time, but as you noted find_target may scan many receivers when a shipment starts. I wonder if a short term fix might be to keep the current architecture and just make find_target dumber (perhaps optionally).

I wonder if keeping receivers in list sorted on free space would help. Continue to check one entity per tick round-robin style, bubbling receivers up the list if necessary. Then when sending make find_target iterate down the list without needing emptiest().

The idea of a charge buffer is interesting. I tried something like it early on but as you say the number of entities and recipes spikes a lot. Wonder if a charge-only accumulator is possible.

4 years ago
(updated 4 years ago)

EDIT: I re-tested and I was talking out of my ass below. i do still get the spikes. It apparently isn't your mod. Weird. Let me look into this more. Sorry to both you.

I can send you my save game (though it's got a bunch of other mods you'd need to download). I'm not sure where/how though. The save file is 70MB. I'm about 99% sure it's this mod causing the spikes though because for the 2 or so minutes my facotires continue to run after the teleporters are despawned I get no spikes. As for how many, the largest networks are the copper/iron plate networks. There are maybe 10 senders and 20-30 receivers on each network (for copper and iron respectively). Essentially I designed a giant 'pool' where anyone who needed iron could get it.

My current leading theory as to why this happens has to do with strings. Lua garbage collection pauses the game world (IE: everything, the game, etc) in order to do garbage collection, and the way strings work in LUA they cannot me modified. Every time you concatentate a string, you're creating a new string (or several new strings) rather than re-using an old one. I do notice (in the debug view of factorio) 10+ms garbage collect pauses at the same time as the 300ms spikes. I also see that your mod is making a new recipe for the senders/recievers every time they activate. It looks like the network names are strings, as well as a lot of the recipe data, and that these strings get regenerated every time the teleporter fires. That may actually be the source of the lag. If my theory is right, swapping the network IDs over to being numbers instead of strings, and pre-allocating the strings for the most common distances (1-10km?), then selecting the recipe that is the next-highest distance interval might help with the garbage creation rate. IE: sending 1.4km = select 2 km recipe.

4 years ago
(updated 4 years ago)

I ended up doing some work on this mod and, for now, it sits as something I only plan to use personally. Mostly because I'm getting 1-3 tickets a day about the Turret Shields mod as people find a hundred ways to make it crash / the scripting not work. Bleh, scripting.

Still, I put a few changes into your mod in my branch, and thought you might want to see what I did and decide if any of it is of use to you: https://github.com/Kingdud/Factorio-Mods/tree/bulkteleport/Kingsbulkteleport

  • Rewrote teleporter search algorithm: All teleporters are now checked at least once a second, or every X seconds according to mod setting.
  • Rewrote teleporter search algorithm: Priority receivers are not allowed. Only senders. Configuring a priority receiver (giving a receiver a green signal) will have no additional effect.
  • Rewrote teleporter search algorithm: Senders will remain full and Receiver teleporters will be empty for the duration of their cycles. Any items inserted into a receiver during a cycle will be overwritten and lost. Any items inserted into a sender...won't actually do anything until the next cycle. If you need constant uptime/throughput, service the entity with two teleporters!
  • Greatly increased power draw from teleporting. From 30MW to 1GW for the small, and from 45MW to 4GW for the large.
  • Teleporting cycles now are 4 seconds regardless of distance. (total energy from 300MJ to 4GJ for small, and 450MJ to 16GJ for large).
  • Replaced Teleporter sounds.
  • Changed Fluid Teleporter to use the base_level property. DeMaterializers now want to empty, and Materializers want to fill.
  • Teleporters no longer benefit from modules or beacons.

It still has some bugs. The main one being that if I have a priority sender it will sometimes pick a nearly empty sender instead of a full one. That's another reason I don't plan to release it wider. I can live with the quirks, I don't want to deal with other people complaining about them. :p

As for performance, I have around 150 materializers/dematerializers on around 15 subnets. I have another 30 fluid materializers/dematerializers on 3 subnets. Your code had an update time of ~.1-.13ms per tick and 1ms for...whatever the 3rd column is (99th percentile?). My code fluctuates a bit, but is in the .03-0.8 range, probably averaging closer to .04. The 99th percentile time is around 2.5ms. I don't see any stuttering.

New response