-
Notifications
You must be signed in to change notification settings - Fork 38
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
Implement batched messages for Digiline Chests #87
base: master
Are you sure you want to change the base?
Implement batched messages for Digiline Chests #87
Conversation
I didn't edit README.inventory since it's already outdated |
|
I described how to reproduce the issue in comment. But here is also a video with demonstration of the bug: https://youtu.be/lDgaIYBr0ZA
This patch adds a new digiline signal for the chest in the format |
I think I also need to restrict the maximum size of a batch to avoid its growth up to infinity under particular conditions. UPD. Done |
To do: handle the situation of multiple channels |
inventory.lua
Outdated
@@ -2,6 +2,43 @@ local S = digilines.S | |||
|
|||
local pipeworks_enabled = minetest.get_modpath("pipeworks") ~= nil | |||
|
|||
-- Messages which will be sent in a single batch | |||
local batched_messages = {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This and last_message_time_for_chest
needs periodic cleanups (e.g. every 30 seconds) in cases where mapblocks are unloaded from memory without sending the message(s).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry for late response. I'm not sure that it's the best solution because this means that the message will be lost that might be crucial in some cases and disrupt the logic of the factory. Also, I don't know how likely the situation is when the map region with a digiline chest is unloaded within 0.1 seconds after placing something to there in a batch (it might happen in case of delivering items to there by tubes in large amounts though). But even when it happens, I'm not sure how crucial is that we'll have some extra records in the structure for a while (maybe even until the server is shutting down). Another option would be to create such a timer, and if it detects such a situation, load the area and flush the signal. But in this case, as far as I understand, it'll activate the factory again that might send more items to the chest and trigger another batched message, and we're stuck in a vicious circle. Some players could theoretically even use it to keep their factories running when offline. I currently haven't come up with a solution of this dilemma.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mapblocks can be unloaded from one server step to the next. This case won't occur often, but it can accumulate over time (unless the same mapblocks are loaded again).
Option 1: Clearing old entries periodically
-- TODO: change to 120 seconds if confirmed working
local cleanup_interval = 30
local function cleanup_batches_from_unloaded_blocks()
local threshold_time = minetest.get_us_time() - 1E6 * (cleanup_interval + interval_to_batch)
local pos_to_remove = {}
for pos_hash, prev_time in pairs(last_message_time_for_chest) do
if prev_time < threshold_time then
-- Expired
table.insert(pos_to_remove, pos_hash)
end
end
-- Remove all data assigned to this position
for _, pos_hash in ipairs(pos_to_remove) do
batched_messages[pos_hash] = nil
last_message_time_for_chest[pos_hash] = nil
end
minetest.after(cleanup_interval, cleanup_batches_from_unloaded_blocks)
end
-- Short time to discover code issues early
minetest.after(5, cleanup_batches_from_unloaded_blocks)
Option 2: node name validity check
- get rid of node timers
- iterate through
batched_messages
periodically (minetest.register_globalstep
) - process the entry or remove it, depending on whether
minetest.get_node(pos)
returns the digiline node name.
Also: might there be nodes that switch between two states (e.g. active/inactive) ? AFAIK when using Judging from the C++ code, node timers appear to be untouched by swap node.minetest.swap_node
or equivalent, the node timer is removed but no on _destruct
called.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems I've come up with the perfect solution: use batched messages only for inventory actions triggered by a user. In this case it will be very unlikely that the chest is unloaded after 0.1 seconds after interaction with a user. But even if it happens, it shouldn't be a problem to load the map region again, and flush the batched messages. Regarding actions triggered by tubes, it will be the chest's owner's responsibility to ensure that the incoming (or outcoming) items won't burn the controller.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And yes, use minetest.after()
instead of node timers to trigger it, so that it'll be triggered even if the node is unloaded
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
We use |
I see. You'd generally want to send single messages immediately and batch afterwards to avoid delays. I currently do not know of any solution that wouldn't involve a periodic "garbage collector" by iterating. You could add a |
return | ||
end | ||
|
||
batches[pos_hash].timer:cancel() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
job:cancel()
was added in 5.4.0. Increase the required version in README.md accordingly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, got it. However I'm not sure what's the best: increase the supported version or make a workaround and not use job:cancel()
Co-authored-by: SmallJoker <[email protected]>
I think I'll try to add a timer which will perform garbage collection in some periods of time such as every 1 minute or 1 hour, and it will start on insertion if there are no active timers, or restart if the table is not empty at the moment of expiration. |
By the way, I call |
This patch implements wrapping signals, which were send in a very short interval (<= 0.1 seconds), into batches. This is needed to prevent Lua Controller from burning when using batch move (performed by selecting an item, and clicking on an item of the same type with Shift pressed). This causes the chest to send Digiline signals for each added stack within milliseconds that will cause the connected Lua controller to burn, and since Digiline chests can be accessed by any player, this gives any player possibility to burn any Lua controller connected to a Digiline chest.
This patch implements sending several Digiline chest events in a single Digiline signal if the previous Digiline signal was sent or included into the pending batch less than 0.1 seconds ago. Thus, in case of using batch move, the first chest event is sent as a separate Digiline signal (since we cannot know in advance about the upcoming events), and the next events are included into a batch.