diff --git a/mods/ITEMS/mcl_lightning_rods/api.lua b/mods/ITEMS/mcl_lightning_rods/api.lua new file mode 100644 index 000000000..5b79f12f7 --- /dev/null +++ b/mods/ITEMS/mcl_lightning_rods/api.lua @@ -0,0 +1,194 @@ +local storage = minetest.get_mod_storage() +local mod = mcl_lightning_rods +local BLOCK_SIZE = 64 + +-- Helper functions +function vector_floor(v) + return vector.new( math.floor(v.x), math.floor(v.y), math.floor(v.z) ) +end +function vector_min(a,b) + return vector.new( math.min(a.x,b.x), math.min(a.y,b.y), math.min(a.z,b.z) ) +end +function vector_max(a,b) + return vector.new( math.max(a.x,b.x), math.max(a.y,b.y), math.max(a.z,b.z) ) +end + +local function read_voxel_area(pos1, pos2) + local vm = minetest.get_voxel_manip() + local minp, maxp = vm:read_from_map(pos1, pos2) + local data = vm:get_data() + local area = VoxelArea:new({MinEdge = minp, MaxEdge = maxp}) + + return vm,data,area +end + +local function load_index(x,y,z) + local idx_key = string.format("%d-%d,%d,%d",BLOCK_SIZE,x,y,z) + local idx_str = storage:get_string(idx_key) + if idx_str and idx_str ~= "" then + local idx = minetest.deserialize(idx_str) + return idx + end + + return {} +end +local function load_index_vector(pos) + return load_index(pos.x,pos.y,pos.z) +end + +local function save_index(x,y,z, idx) + local idx_str = minetest.serialize(idx) + local idx_key = string.format("%d-%d,%d,%d",BLOCK_SIZE,x,y,z) + storage:set_string(idx_key, idx_str) +end +local function save_index_vector(pos, idx) + return save_index(pos.x,pos.y,pos.z, idx) +end + +-- Remove duplicates and verify all locations have a lightning attractor present +local function clean_index(idx, drop) + local new_idx = {} + local exists = {} + for _,p in ipairs(idx) do + local key = string.format("%d,%d,%d",p.x,p.y,p.z) + if not exists[key] then + exists[key] = true + + local node = minetest.get_node(p) + if minetest.get_item_group(node.name, "attracts_lightning") ~= 0 or (drop and vector.distance(p,drop) < 0.1 ) then + new_idx[#new_idx + 1] = p + end + end + end + return new_idx +end + +function mod.find_attractors_in_area(pos1, pos2) + -- Normalize the search area into large, regular blocks + local pos1_r = vector_floor(pos1 / BLOCK_SIZE) + local pos2_r = vector_floor(pos2 / BLOCK_SIZE) + local min = vector_min(pos1_r, pos2_r) + local max = vector_max(pos1_r, pos2_r) + + local results = {} + for z = min.z,max.z do + for y = min.y,max.y do + for x = min.x,max.x do + local idx = load_index(x,y,z) + + -- Make sure every indexed position actually has a lightning attractor present + for _,pos in ipairs(idx) do + local node = minetest.get_node(pos) + if minetest.get_item_group(node.name, "attracts_lightning") ~= 0 then + results[#results + 1] = pos + end + end + end + end + end + return results +end +local function find_closest_position_in_list(pos, list) + local dist = nil + local best = nil + for _,p in ipairs(list) do + local p_dist = vector.distance(p,pos) + if not dist or p_dist < dist then + dist = p_dist + best = p + end + end + return best +end + +function mod.find_closest_attractor(pos, search_size) + local attractor_positions = mod.find_attractors_in_area( + vector.offset(pos, -search_size, -search_size, -search_size), + vector.offset(pos, search_size, search_size, search_size) + ) + return find_closest_position_in_list(pos, attractor_positions) +end + +function mod.unregister_lightning_attractor(pos) + -- Verify the node no longer attracts lightning + local node = minetest.get_node(pos) + if minetest.get_item_group(node.name, "attracts_lightning") ~= 0 then return end + + -- Get the existing index data (if any) + local pos_r = vector_floor(pos / BLOCK_SIZE) + local idx = load_index_vector(pos_r) + + -- Clean the index and drop this node + idx = clean_index(idx, pos) + save_index_vector(pos_r, idx) +end +function mod.register_lightning_attractor(pos) + -- Verify the node attracts lightning + local node = minetest.get_node(pos) + if minetest.get_item_group(node.name, "attracts_lightning") == 0 then return end + + -- Get the existing index data (if any) + local pos_r = vector_floor(pos / BLOCK_SIZE) + local idx = load_index_vector(pos_r) + for _,p in ipairs(idx) do + -- Don't need to change anything if the rod is already registered + if vector.distance(p,pos) < 0.1 then return end + end + + -- Add and save the rod position + idx[#idx + 1] = pos + + -- Clean and save the index data + clean_index(idx) + save_index_vector(pos_r, idx) +end + +-- Constants used for content id index +local IS_ATTRACTOR = {} +local IS_NOT_ATTRACTOR = {} +function mod.index_block(pos) + local pos_r = vector_floor(pos1 / BLOCK_SIZE) + local pos1 = vector.multiply(pos_r,BLOCK_SIZE) + local pos2 = vector.offset(pos,BLOCK_SIZE - 1,BLOCK_SIZE - 1,BLOCK_SIZE - 1) + + -- We are completely rebuilding the index data so there is no nead to load + -- the existing data + local idx = {} + + -- Setup voxel manipulator + local vm,data,area = read_voxel_area() + + -- Indexes to speed things up + local cid_attractors = {} + + -- Iterate over the area and look for lightning attractors + local minx = pos1.x + local maxx = pos1.x + for z = pos1.z,pos2.z do + for y = pos1.y,pos2.y do + for x = minx,maxx do + local vi = area:index(x,y,z) + local cid = data[vi] + local attr = cid_attractors[cid] + if attr then + if attr == IS_ATTRACTOR then + idx[#idx + 1] = vector.new(x,y,z) + end + else + -- Lookup data and cache for later + local name = minetest.get_name_from_content_id(cid) + if minetest.get_item_group(name, "attracts_lightning") then + cid_attractors = IS_ATTRACTOR + idx[#idx + 1] = vector.new(x,y,z) + else + cid_attractors = IS_NOT_ATTRACTOR + end + end + end + end + end + + -- Save for later use + save_index_vector(pos_r, idx) +end + diff --git a/mods/ITEMS/mcl_lightning_rods/init.lua b/mods/ITEMS/mcl_lightning_rods/init.lua index b7d10e090..97f0ae90f 100644 --- a/mods/ITEMS/mcl_lightning_rods/init.lua +++ b/mods/ITEMS/mcl_lightning_rods/init.lua @@ -1,5 +1,12 @@ +local modname = minetest.get_current_modname() +local modpath = minetest.get_modpath(modname) local S = minetest.get_translator("mcl_lightning_rods") +mcl_lightning_rods = {} +local mod = mcl_lightning_rods + +dofile(modpath.."/api.lua") + ---@type nodebox local cbox = { type = "fixed", @@ -59,6 +66,17 @@ local rod_def = { return minetest.item_place(itemstack, placer, pointed_thing, param2) end, + after_place_node = function(pos, placer, itemstack, pointed_thing) + mod.register_lightning_attractor(pos) + end, + after_destruct = function(pos, oldnode) + mod.unregister_lightning_attractor(pos) + end, + _on_lightning_strike = function(pos, node) + minetest.set_node(pos, { name = "mcl_lightning_rods:rod_powered", param2 = node.param2 }) + mesecon.receptor_on(pos, mesecon.rules.alldirs) + minetest.get_node_timer(pos):start(0.4) + end, _mcl_blast_resistance = 6, _mcl_hardness = 3, @@ -92,21 +110,21 @@ end minetest.register_node("mcl_lightning_rods:rod_powered", rod_def_a) - lightning.register_on_strike(function(pos, pos2, objects) - local lr = minetest.find_node_near(pos, 128, { "group:attracts_lightning" }, true) + local lr = mod.find_closest_attractor(pos, 64) + if not lr then return end - if lr then - local node = minetest.get_node(lr) + -- Make sure this possition attracts lightning + local node = minetest.get_node(lr) + if minetest.get_item_group(node.name, "attracts_lightning") == 0 then return end - if node.name == "mcl_lightning_rods:rod" then - minetest.set_node(lr, { name = "mcl_lightning_rods:rod_powered", param2 = node.param2 }) - mesecon.receptor_on(lr, mesecon.rules.alldirs) - minetest.get_node_timer(lr):start(0.4) - end + -- Allow the node to process a lightning strike + local nodedef = minetest.registered_nodes[node.name] + if nodedef and nodedef._on_lightning_strike then + nodedef._on_lightning_strike(lr, node) end - return lr, nil + return lr end) minetest.register_craft({ @@ -117,3 +135,10 @@ minetest.register_craft({ { "", "mcl_copper:copper_ingot", "" }, }, }) +minetest.register_lbm({ + name = "mcl_lightning_rods:index_rods", + nodenames = {"mcl_lightning_rods:rod","mcl_lightning_rods:rod_powered"}, + action = function(pos, node) + mod.register_lightning_attractor(pos) + end +}) diff --git a/mods/PLAYER/mcl_player/init.lua b/mods/PLAYER/mcl_player/init.lua index 164e0083b..e59222f78 100644 --- a/mods/PLAYER/mcl_player/init.lua +++ b/mods/PLAYER/mcl_player/init.lua @@ -213,6 +213,11 @@ local player_attached = mcl_player.player_attached -- Check each player and apply animations minetest.register_globalstep(function(dtime) + -- Track server lag greater than 110ms + if dtime > 0.11 then + minetest.log("warning", "Timestep greater than 110ms("..tostring(math.floor(dtime*1000)).." ms), server lag detected") + end + for _, player in pairs(minetest.get_connected_players()) do local name = player:get_player_name() local model_name = player_model[name]