diff options
Diffstat (limited to 'forceload.lua')
-rw-r--r-- | forceload.lua | 355 |
1 files changed, 355 insertions, 0 deletions
diff --git a/forceload.lua b/forceload.lua new file mode 100644 index 0000000..dd32045 --- /dev/null +++ b/forceload.lua @@ -0,0 +1,355 @@ +--[[ + + Based primarily on code from: + TechAge + ======= + + Copyright (C) 2019-2020 Joachim Stolberg + + AGPL v3 + See LICENSE.txt for more information + + Forceload block + +]]-- + +-- for lazy programmers +local M = minetest.get_meta +local P2S = function(pos) if pos then return minetest.pos_to_string(pos) end end +local S = function(...) return ... end +local maxnum = tonumber(core.settings:get("techage_forceload_max") or "") or 16 + +local function calc_area(pos) + local xpos = (math.floor(pos.x / 16) * 16) + local ypos = (math.floor(pos.y / 16) * 16) + local zpos = (math.floor(pos.z / 16) * 16) + local pos1 = {x=xpos, y=ypos, z=zpos} + local pos2 = {x=xpos+15, y=ypos+15, z=zpos+15} + return pos1, pos2 +end + +local function in_list(list, x) + local pos1 = calc_area(x) + for _,v in ipairs(list) do + local pos2 = calc_area(v) + if vector.equals(pos1, pos2) then return true end + end + return false +end + +local function remove_list_elem(list, x) + local n = nil + for idx, v in ipairs(list) do + if vector.equals(v, x) then + n = idx + break + end + end + if n then + table.remove(list, n) + end + return list +end + +local function chat(player, text) + minetest.chat_send_player(player:get_player_name(), "[Forceload] "..text) +end + +local function postload_area(pos) + minetest.log("warning", "[FLB] area "..P2S(pos).." not loaded!") + if not minetest.forceload_block(pos, true) then + minetest.after(60, postload_area, pos) + end +end + +local function add_pos(pos, player) + local meta = player:get_meta() + local lPos = minetest.deserialize(meta:get_string("techage_forceload_blocks")) or {} + if not in_list(lPos, pos) and #lPos < maxnum then + lPos[#lPos+1] = pos + meta:set_string("techage_forceload_blocks", minetest.serialize(lPos)) + return true + end + return false +end + +local function del_pos(pos, player) + local meta = player:get_meta() + local lPos = minetest.deserialize(meta:get_string("techage_forceload_blocks")) or {} + lPos = remove_list_elem(lPos, pos) + if next(lPos) then + meta:set_string("techage_forceload_blocks", minetest.serialize(lPos)) + else + meta:set_string("techage_forceload_blocks", "") + end +end + +local function get_pos_list(player) + local meta = player:get_meta() + return minetest.deserialize(meta:get_string("techage_forceload_blocks")) or {} +end + +local function set_pos_list(player, lPos) + local meta = player:get_meta() + if next(lPos) then + meta:set_string("techage_forceload_blocks", minetest.serialize(lPos)) + else + meta:set_string("techage_forceload_blocks", "") + end +end + +local function show_flbs(pos, name, range) + local pos1 = {x=pos.x-range, y=pos.y-range, z=pos.z-range} + local pos2 = {x=pos.x+range, y=pos.y+range, z=pos.z+range} + local nodenames = {"techage_forceload:forceload", "techage_forceload:forceloadtile"} + for _,npos in ipairs(minetest.find_nodes_in_area(pos1, pos2, nodenames)) do + local _pos1, _pos2 = calc_area(npos) + local owner = M(npos):get_string("owner") + techage_forceload.mark_region(name, _pos1, _pos2, owner .. " " .. P2S(npos)) + end +end + +local function get_data(pos, player) + local pos1, pos2 = calc_area(pos) + local meta = player:get_meta() + local num = #minetest.deserialize(meta:get_string("techage_forceload_blocks")) or 0 + local max = maxnum + return pos1, pos2, num, max +end + +local function formspec(name) + local player = minetest.get_player_by_name(name) + if player then + local lPos = get_pos_list(player) + local tRes = {} + tRes[#tRes+1] = "#" + tRes[#tRes+1] = S("Block at pos") + tRes[#tRes+1] = S("Area from") + tRes[#tRes+1] = S("Area to") + tRes[#tRes+1] = S("Status") + for idx,pos in ipairs(lPos) do + local pos1, pos2 = calc_area(pos) + tRes[#tRes+1] = idx + tRes[#tRes+1] = minetest.formspec_escape(P2S(pos)) + tRes[#tRes+1] = minetest.formspec_escape(P2S(pos1)) + tRes[#tRes+1] = minetest.formspec_escape(P2S(pos2)) + tRes[#tRes+1] = minetest.forceload_block(pos, true) and 'Loaded' or 'Unloaded' + end + return "size[9,9]".. + default.gui_bg.. + default.gui_bg_img.. + default.gui_slots.. + "label[0,0;"..S("List of your Forceload Blocks:").."]".. + "tablecolumns[text,width=1.8;text,width=12;text,width=12;text,width=12;text,width=12]".. + "table[0,0.6;8.8,8.4;output;"..table.concat(tRes, ",")..";1]" + end +end + +local function on_place(itemstack, placer, pointed_thing) + if pointed_thing.type ~= "node" then + return itemstack + end + return minetest.rotate_and_place(itemstack, placer, pointed_thing) +end + +local function after_place_node(pos, placer, itemstack) + if add_pos(pos, placer) then + minetest.forceload_block(pos, true) + local pos1, pos2, num, max = get_data(pos, placer) + M(pos):set_string("infotext", "Area "..P2S(pos1).." to "..P2S(pos2).." "..S("loaded").."!\n".. + S("Punch the block to make the area visible.")) + chat(placer, "Area ("..num.."/"..max..") "..P2S(pos1).." to "..P2S(pos2).." "..S("loaded").."!") + techage_forceload.mark_region(placer:get_player_name(), pos1, pos2) + M(pos):set_string("owner", placer:get_player_name()) + else + chat(placer, S("Area already loaded or max. number of Forceload Blocks reached!")) + minetest.remove_node(pos) + return itemstack + end +end + +local function after_dig_node(pos, _, oldmetadata) + local player = minetest.get_player_by_name(oldmetadata.fields.owner) + if player then + del_pos(pos, player) + end + minetest.forceload_free_block(pos, true) + techage_forceload.unmark_region(oldmetadata.fields.owner) +end + +local function on_rightclick(pos, _, clicker) + local owner = M(pos):get_string("owner") + local name = clicker:get_player_name() + if name == owner or minetest.check_player_privs(name, "server") then + local s = formspec(owner) + if s then + minetest.show_formspec(name, "techage_forceload:forceload", s) + end + end +end + +local function on_punch(pos, _, puncher) + local pos1, pos2 = calc_area(pos) + techage_forceload.switch_region(puncher:get_player_name(), pos1, pos2) +end + +minetest.register_node("techage_forceload:forceload", { + description = S("Forceload Block"), + tiles = { + -- up, down, right, left, back, front + 'techage_filling_ta2.png^techage_frame_ta2_top.png', + 'techage_filling_ta2.png^techage_frame_ta2_top.png', + { + name = "techage_filling_ta2.png^techage_frame_ta2_top.png^techage_appl_forceload.png", + backface_culling = false, + animation = { + type = "vertical_frames", + aspect_w = 32, + aspect_h = 32, + length = 0.5, + }, + }, + }, + + after_place_node = after_place_node, + after_dig_node = after_dig_node, + on_rightclick = on_rightclick, + on_punch = on_punch, + + paramtype = "light", + sunlight_propagates = true, + groups = {choppy=2, cracky=2, crumbly=2, + digtron_protected = 1,}, + is_ground_content = false, + sounds = default.node_sound_wood_defaults(), +}) + +minetest.register_node("techage_forceload:forceloadtile", { + description = S("Forceload Tile"), + tiles = { + -- up, down, right, left, back, front + { + name = "techage_filling_ta2.png^techage_frame_ta2_top.png^techage_appl_forceload.png", + backface_culling = false, + animation = { + type = "vertical_frames", + aspect_w = 32, + aspect_h = 32, + length = 0.5, + }, + }, + }, + + drawtype = "nodebox", + node_box = { + type = "fixed", + fixed = { + --{-5/16, -7/16, -5/16, 5/16, -5/16, 5/16}, + {-4/16, -8/16, -4/16, 4/16, -15/32, 4/16}, + }, + }, + + on_place = on_place, + after_place_node = after_place_node, + after_dig_node = after_dig_node, + on_rightclick = on_rightclick, + on_punch = on_punch, + + paramtype = "light", + paramtype2 = "facedir", + sunlight_propagates = true, + groups = {choppy=2, cracky=2, crumbly=2,}, + is_ground_content = false, + sounds = default.node_sound_wood_defaults(), +}) + +minetest.register_craft({ + output = "techage_forceload:forceload", + recipe = { + {"group:wood", "", "group:wood"}, + {"default:mese_crystal_fragment", "moreores:mithril_ingot", "default:mese_crystal_fragment"}, + {"group:wood", "default:steel_ingot", "group:wood"}, + }, +}) +minetest.register_craft({ + type = "shapeless", + output = "techage_forceload:forceloadtile", + recipe = {"techage_forceload:forceload"}, +}) +minetest.register_craft({ + type = "shapeless", + output = "techage_forceload:forceload", + recipe = {"techage_forceload:forceloadtile"}, +}) + +function techage_forceload.get_node_lvm(pos) + local node = minetest.get_node_or_nil(pos) + if node then + return node + end + local vm = minetest.get_voxel_manip() + local MinEdge, MaxEdge = vm:read_from_map(pos, pos) + local data = vm:get_data() + local param2_data = vm:get_param2_data() + local area = VoxelArea:new({MinEdge = MinEdge, MaxEdge = MaxEdge}) + local idx = area:indexp(pos) + if data[idx] and param2_data[idx] then + return { + name = minetest.get_name_from_content_id(data[idx]), + param2 = param2_data[idx] + } + end + return {name="ignore", param2=0} +end + +minetest.register_on_joinplayer(function(player) + local lPos = {} + for _,pos in ipairs(get_pos_list(player)) do + local node = techage_forceload.get_node_lvm(pos) + if node.name == "techage_forceload:forceload" or node.name == "techage_forceload:forceloadtile" then + if not minetest.forceload_block(pos, true) then + minetest.after(60, postload_area, pos) + end + lPos[#lPos+1] = pos + end + end + set_pos_list(player, lPos) +end) + +minetest.register_on_leaveplayer(function(player) + for _,pos in ipairs(get_pos_list(player)) do + minetest.forceload_free_block(pos, true) + end +end) + + +minetest.register_chatcommand("forceload", { + params = "", + description = S("Show all forceload blocks in a 64x64x64 range"), + func = function(name) + local player = minetest.get_player_by_name(name) + if player then + local pos = player:get_pos() + pos = vector.round(pos) + show_flbs(pos, name, 64) + end + end, +}) + +minetest.register_chatcommand("forceload_verify", { + params = "", + description = "Checks each forceload block and returns a count of active/placed blocks", + func = function(name) + local player = minetest.get_player_by_name(name) + if player then + local loaded = {} + local wanted = get_pos_list(player) + for _,pos in ipairs(wanted) do + if minetest.forceload_block(pos, true) then + loaded[#loaded+1] = pos + end + end + minetest.chat_send_player(name, "Found "..#loaded.." out of ".. #wanted .. " force loads") + end + end, +}) |