From 94311e3a6cdd06460c9d290bad9e714134269351 Mon Sep 17 00:00:00 2001 From: whosit Date: Thu, 10 Apr 2025 20:30:31 +0300 Subject: [PATCH] from this day on, I decided to commit all my crimes --- change_item_texture.lua | 11 ++ cmd_wolfvision.lua | 34 ++++++ map_loaded_chunks.lua | 190 ++++++++++++++++++++++++++++++++++ mimic.lua | 1 + proxy_table.lua | 40 ++++++++ shield_item.lua | 16 +++ shield_skin.lua | 14 +++ show_all_quest_spawns.lua | 15 +++ snippet_canary.lua | 35 +++++++ snippet_make_botm_cert.lua | 72 +++++++++++++ whosit_instrument_mod.lua | 205 +++++++++++++++++++++++++++++++++++++ 11 files changed, 633 insertions(+) create mode 100644 change_item_texture.lua create mode 100644 cmd_wolfvision.lua create mode 100644 map_loaded_chunks.lua create mode 100644 mimic.lua create mode 100644 proxy_table.lua create mode 100644 shield_item.lua create mode 100644 shield_skin.lua create mode 100644 show_all_quest_spawns.lua create mode 100644 snippet_canary.lua create mode 100644 snippet_make_botm_cert.lua create mode 100644 whosit_instrument_mod.lua diff --git a/change_item_texture.lua b/change_item_texture.lua new file mode 100644 index 0000000..16ddb6b --- /dev/null +++ b/change_item_texture.lua @@ -0,0 +1,11 @@ +local mename = 'whostand' +local player = core.get_player_by_name(mename) +if not player then + return +end +local item = player:get_wielded_item() +local meta = item:get_meta() +local texture = "default_stick.png" +meta:set_string('inventory_image', texture) +meta:set_string('wielded_image', texture) +player:set_wielded_item(item) diff --git a/cmd_wolfvision.lua b/cmd_wolfvision.lua new file mode 100644 index 0000000..b66dbc1 --- /dev/null +++ b/cmd_wolfvision.lua @@ -0,0 +1,34 @@ +local function clamp(val, min_val, max_val) + return math.min(math.max(val, min_val), max_val) +end + +core.register_chatcommand( + "wolfvision", + { + privs = { interact = true }, + func = function(player_name, params) + local p = core.get_player_by_name(player_name) + if not p then + return + end + + local words = string.split(params, " ") + local saturation, light = 1.0, nil + if words[1] then + saturation = tonumber(words[1]) or 1.0 + saturation = clamp(saturation, -2.0, 2.0) + end + if words[2] then + light = tonumber(words[2]) + if light then + light = clamp(light, 0.0, 1.0) + end + end + + p:set_lighting({saturation = saturation}) + p:override_day_night_ratio(light) + + return true, string.format("set: saturation=%s, light=%s %s", dump(saturation), dump(light), ".") + end, + } +) diff --git a/map_loaded_chunks.lua b/map_loaded_chunks.lua new file mode 100644 index 0000000..88e1ffd --- /dev/null +++ b/map_loaded_chunks.lua @@ -0,0 +1,190 @@ +if not whosit then + print("define `whosit` global first") + --return +end + +whosit = whosit or {} -- this will become snippet's "global" if not defined as a real one + +local chunk_map = whosit.chunk_map or {} +whosit.chunk_map = chunk_map + +local jobs = chunk_map.jobs or {} +chunk_map.jobs = jobs + +local FORMNAME = "whosit:chunk_map" + +chunk_map._contexts = chunk_map._contexts or {} + +local function get_context(player_name, create_new) + local context = chunk_map._contexts[player_name] or (create_new and {}) + if context then + chunk_map._contexts[player_name] = context + context.player_name = player_name + end + return context +end + + +local function round_to_mapblock(pos) + return vector.apply(pos/16, math.floor) * 16 +end + + +local function is_pos_emerged(pos) + core.load_area(pos,pos) + local node = core.get_node(pos) + if node and node.name ~= 'ignore' then + return true + end + return false +end + +chunk_map.show_map = function(playername, pos) + if type(playername) ~= "string" or (not vector.check(pos)) then + return + end + + local context = get_context(playername, true) + context.pos = pos or context.pos + if not pos then + return + end + + + local map_display_width = 11 + local map_display_height = 10 + local map_display_cell_size = 0.5 + + local formspec = [[ + formspec_version[6] + size[16,10] + box[0.1,0.1;13.0,10.0;grey] + button[13.9,0.8;0.8,0.8;north;N] + button[14.8,1.7;0.8,0.8;east;E] + button[13.9,2.6;0.8,0.8;south;S] + button[13,1.7;0.8,0.8;west;W] + button[12.9,0.7;0.8,0.8;down;D] + button[14.9,0.7;0.8,0.8;up;U] + box[0.2,0.2;0.5,0.5;yellow] + ]] + + local cells = {} + local template = string.format("box[%%s,%%s;%s,%s;%%s]", map_display_cell_size, map_display_cell_size) + + local center = pos + local map_display_width_blocks = map_display_width/map_display_cell_size + local map_display_height_blocks = map_display_height/map_display_cell_size + local top_left_x = pos.x - (map_display_width_blocks*16/2) + local top_left_z = pos.z - (map_display_height_blocks*16/2) + local query_pos = vector.new(top_left_x, pos.y, top_left_z) + for idx_x = 0, map_display_width_blocks do + for idx_z = 0, map_display_height_blocks do + local emerged = is_pos_emerged(query_pos) + table.insert(cells, string.format(template, + idx_x * map_display_cell_size, + idx_z * map_display_cell_size, + emerged and "blue" or "white")) + query_pos.z = query_pos.z + 16 + end + query_pos.x = query_pos.x + 16 + end + + table.insert(cells, string.format("label[13.1,4;%s]", dump(pos))) + + core.show_formspec(playername, FORMNAME, formspec .. table.concat(cells,'')) +end + + +chunk_map.map_on_player_receive_fields = function(playername, formname, fields) + if formname ~= FORMNAME then + return + end + + local context = get_context(playername, false) + if not context or not context.pos then + return + end + + if fields.quit then + return + end + + local pos = context.pos + if fields.north then + pos.z = pos.z + 16 + elseif fields.south then + pos.z = pos.z - 16 + elseif fields.east then + pos.x = pos.x + 16 + elseif fields.west then + pos.x = pos.x - 16 + elseif fields.up then + -- FIXME use 16 here + pos.y = pos.y + 1 + elseif fields.down then + -- FIXME use 16 here + pos.y = pos.y - 1 + end + chunk_map.show_map(playername, pos) +end + + +chunk_map.step_jobs = function(dtime) + --print(dtime) + local jobs = chunk_map.jobs + for i=#jobs,1,-1 do + local job = jobs[i] + if job.coro and coroutine.status(job.coro) == "supended" then + coroutine.resume(job.coro) + else + print(string.format("job %s done", job)) + jobs[i] = nil + end + end + +end + + +chunk_map.cmd_show_map = function(playername, params) + local params = params:split(" ") + local x,y,z = table.unpack(params) + x,y,z = tonumber(x), tonumber(y), tonumber(z) + + local pos + if (type(x) == "number" and type(y) == "number" and type(z) == "number") then + pos = vector.new(x,y,z) + else + local context = get_context(playername) + pos = context and context.pos + end + + if not pos then + return false, "please provide x y z" + end + chunk_map.show_map(playername, pos) +end + + +if not chunk_map.callbacks_registered then + core.register_on_player_receive_fields( + chunk_map.map_on_player_receive_fields + ) + + core.register_on_leaveplayer(function(player) + local player_name = player:get_player_name() + chunk_map._contexts[player_name] = nil + end) + + -- core.register_globalstep(function(dtime) + -- chunk_map.step_jobs(dtime) + -- end) + + + core.register_chatcommand("whosit_chunk_map", + { + func = chunk_map.cmd_show_map, + } + ) + + chunk_map.callbacks_registered = true +end diff --git a/mimic.lua b/mimic.lua new file mode 100644 index 0000000..037b7e2 --- /dev/null +++ b/mimic.lua @@ -0,0 +1 @@ +/eval local props = this_obj:get_properties(); me:set_properties({mesh = props.mesh, textures = props.textures, nametag = props.nametag, nametag_color = props.nametag_color, visual_size = props.visual_size, collisionbox = props.collisionbox}) diff --git a/proxy_table.lua b/proxy_table.lua new file mode 100644 index 0000000..c90efdd --- /dev/null +++ b/proxy_table.lua @@ -0,0 +1,40 @@ +do -- Require luajit __pairs metamethod to be enabled + local enabled + local t = setmetatable({}, { __pairs = function() enabled = true; return function() end end }) + for _ in pairs(t) do + end + if not enabled then + error("This thing requires luajit with __pairs metamethod enabled") + end +end + + +local function make_proxy(actual, name) + local function fmt(...) print(string.format(...)) end + local function get_trace(label) + return debug.traceback(label,3):gsub("([^\n]+)", "| %1") + end + + local proxy = {} -- this must be empty to intercept every access + local mt = { + __index = function (_self, k) + fmt("* %s.self.%s -- %s", name, k, get_trace('read')) + return actual[k] -- access the original table + end, + + __newindex = function (_self, k, v) + fmt("* %s.self.%s = %s -- %s", name, k, v, get_trace('write')) + actual[k] = v -- update original table + end, + __pairs = function (self) + -- wrap `next` to enable proxy hits during traversal + return function (tab, key) + local index, value = next(actual, key) + return index, value ~= nil and self[index] + end, self, nil + end, + } + return setmetatable(proxy, mt) +end + +return make_proxy diff --git a/shield_item.lua b/shield_item.lua new file mode 100644 index 0000000..538a8b8 --- /dev/null +++ b/shield_item.lua @@ -0,0 +1,16 @@ +local mename = 'whostand' +local player = core.get_player_by_name(mename) +if not player then + print('player not found') + return +end +local item = player:get_wielded_item() +local meta = item:get_meta() +local def = item:get_definition() +local ii = def.inventory_image +--local texture = "shields_shield_crystal.png^farming_tomato.png" +--local texture = "farming_tomato.png^default_stick.png" +local texture = "[combine:32x32:0,0=" .. ii .. "\\^[resize\\:32x32:8,8=farming_tomato.png" +meta:set_string('inventory_image', texture) +meta:set_string('wielded_image', texture) +player:set_wielded_item(item) diff --git a/shield_skin.lua b/shield_skin.lua new file mode 100644 index 0000000..f8afec1 --- /dev/null +++ b/shield_skin.lua @@ -0,0 +1,14 @@ +local mename = 'whostand' +local player = core.get_player_by_name(mename) +if not player then + print('player not found') + return +end +--local item = player:get_wielded_item() +--local meta = item:get_meta() + +local texture = "((" .. armor.textures[mename].armor .. ")^[resize:128x64)^([combine:128x64:8,8=farming_tomato.png)" +armor.textures[mename].armor = texture +print(armor.textures[mename].armor) +local skin = skins.get_player_skin(player) +skin:apply_skin_to_player(player) diff --git a/show_all_quest_spawns.lua b/show_all_quest_spawns.lua new file mode 100644 index 0000000..d659f4e --- /dev/null +++ b/show_all_quest_spawns.lua @@ -0,0 +1,15 @@ +whosit.huds = whosit.huds or {} +local name = 'tour' +local player = core.get_player_by_name(name) +local meh = whosit.huds[name] or {} +whosit.huds[name] = meh +for k,v in pairs(yl_events.events) do + local p = v.pos + meh[p] = futil.EphemeralHud(player, { + hud_elem_type = "image_waypoint", + text = "bubble.png", + world_pos = p, + scale = { x = 1, y = 1 }, + }) +end +---yl_events.events[27].pos = vector.new(1964,10,1246) diff --git a/snippet_canary.lua b/snippet_canary.lua new file mode 100644 index 0000000..a24940f --- /dev/null +++ b/snippet_canary.lua @@ -0,0 +1,35 @@ +whosit = _G.whosit or {} +_G.whosit = whosit +local t = "farming_tomato.png" +whosit.counter = whosit.counter or 0 +core.register_entity( + ":canary", + { + initial_properties = { + visual = "cube", + textures = {t,t,t,t,t,t}, + use_texture_alpha = true, + hp_max = 1, + physical = true, + static_save = true, + }, + on_activate = function(self, s, d) + whosit.counter = (whosit.counter or 0) + 1 + self.__id = whosit.counter + core.chat_send_all(string.format("canary %s activated", self.__id)) + self.lifetime = 0 + end, + on_step = function(self, dtime, movres) + self.timer = self.timer or 0 + self.lifetime = (self.lifetime or 0) + dtime + if self.timer > 30 then + core.chat_send_all(string.format("canary %s chirp %s", self.__id, self.lifetime)) + self.timer = 0 + end + self.timer = self.timer + dtime + end, + on_deactivate = function(self, removal) + core.chat_send_all(string.format("canary %s deactivated %s", self.__id, self.lifetime)) + end + } +) diff --git a/snippet_make_botm_cert.lua b/snippet_make_botm_cert.lua new file mode 100644 index 0000000..18e9728 --- /dev/null +++ b/snippet_make_botm_cert.lua @@ -0,0 +1,72 @@ +--yl_commons.botm_certificate_issuer = "Parrish" +--yl_commons.botm_certificate_issuer = "whostand" +yl_commons.botm_certificate_issuer = "daydream" + +yl_commons.botm_create_certificate = function(place, description) + if type(description) ~= "string" then + return false, "* Description must be a string" + end + + local place = tonumber(place) + if not place or place < 1 or place > 3 then + return false, "* Place must be a number between 1 and 3" + end + + local cert_names = { + "yl_events:certificate_gold_item", + "yl_events:certificate_silver_item", + "yl_events:certificate_bronze_item", + } + + description = string.gsub(description, "\\n", "\n") + + local item = ItemStack(cert_names[place]) + local meta = item:get_meta() + meta:set_string("_infotext", description) + + return item, "ok" +end + +local function botm_cert_command(name, param) + if name ~= yl_commons.botm_certificate_issuer then + return false, "* You can't do this" + end + if type(param) ~= "string" then + return false, "* Needs a string argument" + end + + local split_idx = string.find(param, " ") + if not split_idx then + return false, "Needs at least 2 parameters: 1|2|3 description" + end + local place = param:sub(1, split_idx) + local description = param:sub(split_idx) + + local res, msg = yl_commons.botm_create_certificate(place, description) + if res then + local item = res + local player = core.get_player_by_name(name) + if not player then + return false, "* can't give items to an offline player" + end + + local inv = player:get_inventory() + if not inv:room_for_item("main", item) then + return false, "* You must have space in your inv" + end + inv:add_item("main", item) + return true, "* Certificate created" + else + return res, msg + end +end + +core.register_chatcommand( + "create_botm_cert", + { + params = "1|2|3 description", + description = "Create a BOTM certificate", + privs = { interact = true }, + func = botm_cert_command, + } +) diff --git a/whosit_instrument_mod.lua b/whosit_instrument_mod.lua new file mode 100644 index 0000000..ade54d4 --- /dev/null +++ b/whosit_instrument_mod.lua @@ -0,0 +1,205 @@ +-- luacheck: globals debuggery whosit minetest futil +-- based on debuggery by flux, but outputs result in chat and calculates max/avg +local f = string.format +local S = debuggery.S + +local get_us_time = minetest.get_us_time +local log = debuggery.log +local log_level = minetest.settings:get("debug_log_level") or "action" + +local pairs_by_key = futil.table.pairs_by_key + +local s = debuggery.settings + +whosit = _G.whosit or {} +_G.whosit = whosit + +local old_values = whosit.instrument_old_values or {} +whosit.instrument_old_values = old_values +local total_steps = whosit.instrument_total_steps or {} +whosit.instrument_total_steps = total_steps +local total_elapsed = whosit.instrument_total_elapsed or {} +whosit.instrument_total_elapsed = total_elapsed +local total_calls = whosit.instrument_total_calls or {} +whosit.instrument_total_calls = total_calls +local max_time = whosit.instrument_max_time or {} +whosit.instrument_max_time = max_time + +local num_instrumented = 0 + +local function instrument(name, value, _cache) + if type(value) == "function" then + log("action", "instrumenting %q", name) + return function(...) + if s.instrument_log_every_call then + log("action", "%s(%s)", name, dump({ ... })) + end + local begin = get_us_time() + local rvs = { value(...) } + local elapsed = get_us_time() - begin + total_elapsed[name] = (total_elapsed[name] or 0) + elapsed + total_calls[name] = (total_calls[name] or 0) + 1 + max_time[name] = math.max(elapsed, max_time[name] or 0) + -- if s.instrument_log_every_call then + -- log("action", "%s(...) -> %s", name, dump(rvs)) + -- end + return unpack(rvs) + end + elseif type(value) == "table" then + _cache = _cache or {} + local cached = _cache[value] + if cached then + return cached + end + + local t = {} + _cache[value] = t + + for k, v in pairs(value) do + if type(k) == "string" then + t[k] = instrument(f("%s.%s", name, k), v, _cache) + else + t[k] = instrument(f("%s[%s]", name, k), v, _cache) + end + end + + setmetatable(t, instrument(f("getmetatable(%s)", name), getmetatable(value), _cache)) + + return t + else + return value + end +end + +local function instrument_mod(mod) + log("action", "instrumenting %s", mod) + old_values[mod] = _G[mod] + _G[mod] = instrument(mod, _G[mod]) + num_instrumented = num_instrumented + 1 +end + +local function uninstrument_mod(mod) + log("action", "uninstrumenting %s", mod) + _G[mod] = old_values[mod] + old_values[mod] = nil + num_instrumented = num_instrumented - 1 +end + +minetest.register_chatcommand( + "whosit_instrument_mod", + { + params = S(""), + description = S("toggles recording timing data for all functions declared in a particular global"), + privs = { [s.admin_priv] = true }, + func = function(name, param) + if param == "" then + local mods = {} + for mod in pairs(old_values) do + table.insert(mods, mod) + end + if #mods == 0 then + return true, S("no mods currently instrumented") + else + return true, S("mods currently instrumented: @1", table.concat(mods, ", ")) + end + end + + if not (minetest.global_exists(param) and _G[param]) then + return false, S("unknown global @1", param) + end + + if old_values[param] then + uninstrument_mod(param) + return true, S("instrumentation disabled for @1", param) + else + instrument_mod(param) + return true, S("instrumentation enabled for @1", param) + end + end, +}) + +local function get_modname(fname) + local dot = fname:find('.',1,true) + if dot then + return fname:sub(1, dot - 1) + else + return fname + end +end + +function whosit.instrument_summary_lines(perstep) + if futil.table.is_empty(total_calls) then + return false, "nothing to report" + end + + local out = {} + for name, num_calls in pairs_by_key(total_calls, function(a,b) return total_elapsed[a] > total_elapsed[b] end) do + local steps = perstep and total_steps[get_modname(name)] or 1 + local te = math.round(total_elapsed[name]) + local avg = total_elapsed[name]/num_calls + local tm = max_time[name] + + table.insert(out, f("%15.1f | n:%11d | avg:%9.1f | max:%9.1f -- %s()", te/steps, num_calls/steps, avg, tm, name)) + end + return true, out +end + +minetest.register_chatcommand( + "whosit_instrument_summary", + { + -- params = S(""), + -- description = S("toggles recording timing data for all functions declared in a particular global"), + privs = { [s.admin_priv] = true }, + func = function(name, param) + if futil.table.is_empty(total_calls) then + return false, "nothing to report" + end + local ok, res = whosit.instrument_summary_lines() + if ok then + core.chat_send_player(name, table.concat(res, '\n')) + end + return true, "done." + end, +}) + + +minetest.register_chatcommand( + "whosit_instrument_reset", + { + -- params = S(""), + -- description = S("toggles recording timing data for all functions declared in a particular global"), + privs = { [s.admin_priv] = true }, + func = function(name, param) + for name, _ in pairs(total_calls) do + total_calls[name] = nil + total_elapsed[name] = nil + max_time[name] = nil + end + for name, _ in pairs(total_steps) do + total_steps[name] = nil + end + return true, "Stats reset." + end, +}) + + + +function whosit.instrument_summary_text(perstep) + local _,out = whosit.instrument_summary_lines(perstep) + return table.concat(out, '\n') +end + +function whosit.instrument_update_steps() + for name,_ in pairs(old_values) do + total_steps[name] = (total_steps[name] or 0) + 1 + end +end + +if not whosit.instrument_globalstep_registered then + minetest.register_globalstep( + function(_dtime) + whosit.instrument_update_steps() + end + ) + whosit.instrument_globalstep_registered = true +end