Rework codegen and vl_block_update, add lookaside cache for core.get_node(), make observers get alternating on/off pulses when starting up a world

This commit is contained in:
teknomunk
2024-11-28 17:27:36 -06:00
parent a8ffecf944
commit ea576cdaa0
5 changed files with 155 additions and 152 deletions
mods
CORE
mcl_util
vl_block_update
ITEMS/REDSTONE/mcl_observers

@ -787,3 +787,11 @@ function mcl_util.caller_from_traceback(traceback)
return "mods"..DIR_DELIM..parts[1]..":"..parts[2]
end
function dofile_codegen(path, debug)
local contents = io.open(path):read("a*")
contents = contents:gsub("<<<","]]"):gsub(">>>","code=code..[[")
contents = "local code = \"\"\ncode = code .. [[\n"..contents.."]]\nreturn code"
local code = loadstring(contents)()
if debug then print(code) end
loadstring(code)()
end

@ -0,0 +1,132 @@
local mod = {
updated = {},
}
vl_block_update = mod
local PROFILE = false
local lookaside_cache = {}
local pending_block_updates = {}
-- Block updates are processed on the next timestep
-- This is written to consolidate multiple updates to the same position
local function queue_block_updates(pos)
pos = vector.round(pos)
local pos_hash = core.hash_node_position(pos)
vl_block_update.updated[pos_hash] = true
lookaside_cache[pos_hash] = nil
<<<
local block_update_pattern = {
vector.new( 1, 0, 0), vector.new(-1, 0, 0), vector.new( 0, 1, 0),
vector.new( 0,-1, 0), vector.new( 0, 0, 1), vector.new( 0, 0,-1),
vector.new( 2, 0, 0), vector.new(-2, 0, 0), vector.new( 0, 2, 0),
vector.new( 0,-2, 0), vector.new( 0, 0, 2), vector.new( 0, 0,-2),
vector.new( 1, 1, 0), vector.new( 1,-1, 0), vector.new(-1, 1, 0),
vector.new(-1,-1, 0), vector.new( 1, 0, 1), vector.new(-1, 0, 1),
vector.new( 1, 0,-1), vector.new(-1, 0,-1), vector.new( 0, 1, 1),
vector.new( 0,-1, 1), vector.new( 0, 1,-1), vector.new( 0,-1,-1),
}
if type(core.hash_node_position(vector.zero())) == "number" then
local offset = core.hash_node_position(vector.zero())
for i = 1,#block_update_pattern do
local hash_diff = core.hash_node_position(block_update_pattern[i]) - core.hash_node_position(vector.zero())
if hash_diff < 0 then
code = code.. "\tpending_block_updates[pos_hash-"..tostring(-hash_diff).."]=true\n"
else
code = code.. "\tpending_block_updates[pos_hash+"..tostring(hash_diff).."]=true\n"
end
end
else
local p = block_update_pattern[1]
code = code .. "\tlocal np = vector.offset(pos,"..p.x..","..p.y..","..p.z..")\n"
code = code .. "\tpending_block_updates[core.hash_node_position(np)]=true\n"
for i = 2,#block_update_pattern do
local p = block_update_pattern[i]
code = code .. "\tnp.x = pos.x+("..p.x..")\n"
code = code .. "\tnp.y = pos.y+("..p.x..")\n"
code = code .. "\tnp.z = pos.z+("..p.x..")\n"
code = code .. "\tpending_block_updates[core.hash_node_position(np)]=true\n"
end
end
>>>
end
core.register_globalstep(function(dtime)
local start = core.get_us_time()
local updates = pending_block_updates
pending_block_updates = {}
for hash,_ in pairs(updates) do
local lookaside = lookaside_cache[hash]
if false ~= lookaside then
local pos = lookaside and lookaside[1] or core.get_position_from_hash(hash)
local node = lookaside and lookaside[2] or core.get_node(pos)
local def = lookaside and lookaside[3] or core.registered_nodes[node.name]
if def and def.vl_block_update then
def.vl_block_update(pos, node, def)
lookaside_cache[hash] = lookaside or {pos, node, def}
else
lookaside_cache[hash] = false
end
end
end
vl_block_update.updated = {}
if PROFILE then core.log("[vl_block_update] took "..(core.get_us_time() - start).." us") end
end)
local node_change_callbacks = {queue_block_updates}
function mod.register_node_change(callback)
node_change_callbacks[#node_change_callbacks + 1] = callback
end
local function node_changed(pos)
for i = 1,#node_change_callbacks do
node_change_callbacks[i](pos)
end
end
local old_add_node = core.add_node
function core.add_node(pos, node)
old_add_node(pos, node)
node_changed(pos)
end
local old_set_node = core.set_node
function core.set_node(pos, node)
old_set_node(pos, node)
node_changed(pos)
end
local old_remove_node = core.remove_node
function core.remove_node(pos)
old_remove_node(pos)
node_changed(pos)
end
local old_bulk_set_node = core.bulk_set_node
function core.bulk_set_node(lst, node)
old_bulk_set_node(lst, node)
for i=1,#lst do
node_changed(lst[i])
end
end
core.register_lbm({
label = "Call _onload() when blocks load",
name = "vl_block_update:handle_onload",
nodenames = {"group:has_onload"},
run_at_every_load = true,
action = function(pos, node)
core.registered_nodes[node.name]._onload(pos)
end
})
core.register_on_mods_loaded(function()
for name,def in pairs(core.registered_nodes) do
if def._onload then
local new_groups = table.copy(def.groups)
new_groups.has_onload = 1
core.override_item(name, {groups = new_groups})
end
end
end)

@ -1,143 +1,4 @@
local mod = {
updated = {},
}
vl_block_update = mod
local modpath = core.get_modpath("vl_block_update")
local block_update_pattern = {
vector.new( 1, 0, 0), vector.new(-1, 0, 0), vector.new( 0, 1, 0),
vector.new( 0,-1, 0), vector.new( 0, 0, 1), vector.new( 0, 0,-1),
vector.new( 2, 0, 0), vector.new(-2, 0, 0), vector.new( 0, 2, 0),
vector.new( 0,-2, 0), vector.new( 0, 0, 2), vector.new( 0, 0,-2),
vector.new( 1, 1, 0), vector.new( 1,-1, 0), vector.new(-1, 1, 0),
vector.new(-1,-1, 0), vector.new( 1, 0, 1), vector.new(-1, 0, 1),
vector.new( 1, 0,-1), vector.new(-1, 0,-1), vector.new( 0, 1, 1),
vector.new( 0,-1, 1), vector.new( 0, 1,-1), vector.new( 0,-1,-1),
}
dofile_codegen(modpath.."/core.lua")
-- Block updates are processed on the next timestep
-- This is written to consolidate multiple updates to the same position
local queue_block_updates
if type(core.hash_node_position(vector.zero())) == "number" then
-- core.hash_node_position() returns a number, we can perform some optimizations based on this that will make the
-- JIT compiler generate better code
local function codegen_queue_block_updates()
local code = [[
local pending_block_updates = {}
local function queue_block_updates(pos)
pos = vector.round(pos)
local pos_hash = core.hash_node_position(pos)
vl_block_update.updated[pos_hash] = true
]]
for i = 1,#block_update_pattern do
local hash_diff = core.hash_node_position(block_update_pattern[i]) - core.hash_node_position(vector.zero())
if hash_diff < 0 then
code = code.. "\tpending_block_updates[pos_hash-"..tostring(-hash_diff).."]=true\n"
else
code = code.. "\tpending_block_updates[pos_hash+"..tostring(hash_diff).."]=true\n"
end
end
code = code..[[
end
core.register_globalstep(function(dtime)
local start = core.get_us_time()
local updates = pending_block_updates
pending_block_updates = {}
for hash,_ in pairs(updates) do
local pos = core.get_position_from_hash(hash)
local node = core.get_node(pos)
local def = core.registered_nodes[node.name]
if def and def.vl_block_update then
def.vl_block_update(pos, node, def)
end
end
vl_block_update.updated = {}
end)
return queue_block_updates
]]
return loadstring(code)()
end
queue_block_updates = codegen_queue_block_updates()
else
-- Use fallback that makes no assumptions about the result from core.hash_node_position()
local pending_block_updates = {}
queue_block_updates = function(pos)
pos = vector.round(pos)
vl_block_update.updated[core.hash_node_position(pos)] = true
for i = 1,#block_update_pattern do
local offset = block_update_pattern[i]
pending_block_updates[core.hash_node_position(pos + offset)] = true
end
end
core.register_globalstep(function(dtime)
local start = core.get_us_time()
local updates = pending_block_updates
pending_block_updates = {}
for hash,_ in pairs(updates) do
local pos = core.get_position_from_hash(hash)
local node = core.get_node(pos)
local def = core.registered_nodes[node.name]
if def and def.vl_block_update then
def.vl_block_update(pos, node, def)
end
end
mod.updated = {}
end)
end
local node_change_callbacks = {queue_block_updates}
function mod.register_node_change(callback)
node_change_callbacks[#node_change_callbacks + 1] = callback
end
local function node_changed(pos)
for i = 1,#node_change_callbacks do
node_change_callbacks[i](pos)
end
end
local old_add_node = core.add_node
function core.add_node(pos, node)
old_add_node(pos, node)
node_changed(pos)
end
local old_set_node = core.set_node
function core.set_node(pos, node)
old_set_node(pos, node)
node_changed(pos)
end
local old_remove_node = core.remove_node
function core.remove_node(pos)
old_remove_node(pos)
node_changed(pos)
end
local old_bulk_set_node = core.bulk_set_node
function core.bulk_set_node(lst, node)
old_bulk_set_node(lst, node)
for i=1,#lst do
node_changed(lst[i])
end
end
core.register_lbm({
label = "Call _onload() when blocks load",
name = "vl_block_update:handle_onload",
nodenames = {"group:has_onload"},
run_at_every_load = true,
action = function(pos, node)
core.registered_nodes[node.name]._onload(pos)
end
})
core.register_on_mods_loaded(function()
for name,def in pairs(core.registered_nodes) do
if def._onload then
local new_groups = table.copy(def.groups)
new_groups.has_onload = 1
core.override_item(name, {groups = new_groups})
end
end
end)

@ -1,4 +1,4 @@
name = vl_block_update
author = teknomunk
description = Block updater
depends = vl_scheduler
depends = vl_scheduler, mcl_util

@ -46,16 +46,18 @@ local function update_observer(pos, node, def, force_activate)
end
-- Node state changed! Activate observer
if node.name == "mcl_observers:observer_off" then
core.set_node(pos, {name = "mcl_observers:observer_on", param2 = node.param2})
mesecon.receptor_on(pos, get_rules_flat(node))
elseif node.name == "mcl_observers:observer_down_off" then
core.set_node(pos, {name = "mcl_observers:observer_down_on"})
mesecon.receptor_on(pos, rules_down)
elseif node.name == "mcl_observers:observer_up_off" then
core.set_node(pos, {name = "mcl_observers:observer_up_on"})
mesecon.receptor_on(pos, rules_up)
end
core.after(0,function()
if node.name == "mcl_observers:observer_off" then
core.set_node(pos, {name = "mcl_observers:observer_on", param2 = node.param2})
mesecon.receptor_on(pos, get_rules_flat(node))
elseif node.name == "mcl_observers:observer_down_off" then
core.set_node(pos, {name = "mcl_observers:observer_down_on"})
mesecon.receptor_on(pos, rules_down)
elseif node.name == "mcl_observers:observer_up_off" then
core.set_node(pos, {name = "mcl_observers:observer_up_on"})
mesecon.receptor_on(pos, rules_up)
end
end)
end
local function activate_observer(pos, node, def)
update_observer(pos, node, def, true)