Post your code!

Jaime Alterado
Member
Posts: 13
Joined: Sat Jun 04, 2022 20:24

Re: Post your code!

by Jaime Alterado » Post

El comando «deleteblocks» pero con una pequeña modificación:
Ahora admite el formato «from <pos> [<radius>]». Supongo que algo parecido ya existe, pero como no pude encontrarlo hice mi propia versión*.

Google Translate:
The "deleteblocks" command but with a little modification:
Now supports “from <pos> [<radius>]” format. I guess something similar already exists, but since I couldn't find it I made my own version*.

Code: Select all

local S = minetest.get_translator("")

local function get_player_pos(player_name)
    local player = minetest.get_player_by_name(player_name)
    
    if player == nil then
        return nil
    end

    return player:get_pos()
end

-- Returns two position vectors representing a box of `radius` in each
-- direction centered around of `position`
local function get_position_radius_area(position, radius)
    local p1 = position
    local p2 = p1

    if radius then
        p1 = vector.subtract(p1, radius)
        p2 = vector.add(p2, radius)
    end

    return p1, p2
end

-- Parses a "range" string in the format of "here (number)" or
-- "from (x,y,z) (number)" or "(x1,y1,z1) (x2,y2,z2)",
-- returning two position vectors
local function parse_range_str(player_name, str)
    local p1, p2, radius
    local args = str:split(" ")

    if args[1] == "here" then
        p1 = get_player_pos(player_name)
        
        if p1 == nil then
            return false, S("Unable to get position of player @1.", player_name)
        end
        
        radius = tonumber(args[2])
        p1, p2 = get_position_radius_area(p1, radius)
    elseif args[1] == "from" then
        p1 = minetest.string_to_pos(args[2])
        
        if p1 == nil then
            return false, S("Incorrect format. "
                .. "Expected: from (x,y,z) [radius]")
        end
        
        radius = tonumber(args[3])
        p1, p2 = get_position_radius_area(p1, radius)
    else
        p1, p2 = minetest.string_to_area(str)
        
        if p1 == nil then
            return false, S("Incorrect area format. "
                .. "Expected: (x1,y1,z1) (x2,y2,z2)")
        end
    end

    return p1, p2
end

minetest.override_chatcommand("deleteblocks", {
    params = S("(here [<radius>]) | (from <pos> [<radius>]) | (<pos1> <pos2>)"),
    description = S("Delete map blocks contained in area pos1 to pos2 "
        .. "(<pos1> and <pos2> must be in parentheses)"),
    privs = {server=true},
    func = function(name, param)
        local p1, p2 = parse_range_str(name, param)
        
        if p1 == false then
            return false, p2
        end

        if minetest.delete_area(p1, p2) then
            return true, S("Successfully cleared area "
                .. "ranging from @1 to @2.",
                minetest.pos_to_string(p1, 1),
                minetest.pos_to_string(p2, 1))
        else
            return false, S("Failed to clear one or more "
                .. "blocks in area.")
        end
    end,
})
El inglés no es mi lengua natal, por lo que si hay errores de gramática pueden corregirme ^_^.
*Basado en el código de la versión 5.5.0 del motor.

Google Translate:
English is not my native language, so if there are any grammar mistakes you can correct me ^_^.
*Based on code from version 5.5.0 of the engine.

JohnnyBravo1212
New member
Posts: 1
Joined: Fri Aug 19, 2022 11:09
In-game: Billyelei
Contact:

Re: post your code!

by JohnnyBravo1212 » Post

Stix wrote:
Thu Sep 14, 2017 17:38
heres my code, its not much as i was going through rubenwardy's modding book and just changed some examples, but here you go!

Code: Select all

minetest.register_craftitem("better_bread:doublebaked_bread", {
descrition = "doublebaked_bread",
inventory_image = "better_bread_doublebaked_bread.png",
on_use = minetest.item_eat(10)
})
minetest.register_craft({
type = "cooking",
output = "better_bread:doublebaked_bread"
recipie = "farming:bread",
})
Wow! Thanks for the code.

User avatar
debiankaios
Member
Posts: 910
Joined: Thu Dec 03, 2020 12:48
IRC: debiankaios
In-game: debiankaios Nowe
Location: germany
Contact:

Re: Post your code!

by debiankaios » Post

Create a recipe for a machine enter it in i3 and use the recipes:

Code: Select all

i3.register_craft_type("deoxidize", {
	description = "Deoxidize",
	icon = "pxs_default_deoxidizer_1.png",
})

function deoxidizer.register_recipe(input, output_iron, output_trash)
	table.insert(deoxidizer.recipes, {input = input, oi = output_iron, ot = output_trash})
	i3.register_craft {
		type   = "deoxidize",
		result = output_iron,
		items  = {input},
	}
end

local function deoxidizer_do(pos)
	local inv = minetest.get_meta(pos):get_inventory()
	local input = inv:get_stack("input", 1)
	local output_iron = inv:get_stack("output_iron", 1)
	local output_trash = inv:get_stack("output_trash", 1)
	for i, v in pairs(deoxidizer.recipes) do
		if inv:contains_item("input", v.input) then
			inv:add_item("output_iron", v.oi)
			inv:remove_item("input", v.input)
		end
	end
end

User avatar
Skamiz Kazzarch
Member
Posts: 613
Joined: Fri Mar 09, 2018 20:34
GitHub: Skamiz
In-game: Skamiz
Location: la lojbaugag.

Re: Post your code! - How I changed sleeping times.

by Skamiz Kazzarch » Post

Whenever I play minetest in single player I frequently end up writing small QoL mod.
Today I wanted to make it possible to skip the night as soon as it starts getting dark, instead of having to wait another minute.

At first I though it would be easy. Just override the function which determines night time in the beds mod.
Except it turns out that the time values are hardcoded in the on_rightclick function with several other things. But OK, I will just copy over the whole function, change the time values and add a disclaimer as to where to find the original code and license.

Turns out, that doesn't work, because the on_rightclick function relies on several other functions local to it's original file. I would end up copying half the beds mod before I managed to change that single number I cared about. That was way too heavy-handed for me.

For a while I gave up. Even considered opening a github issue, because it should not be that hard to modify the sleeping times.
But then I remembered that Lua is AWESOME!

Here is my final solution, which doesn't even require me to copy over code from the original mod.
Spoiler

Code: Select all

local morning = 0.2
local evening = 0.75       -- original is 0.805

local old_on_rightclick = beds.on_rightclick
local orig_get_timeofday = minetest.get_timeofday
local function custom_get_timeofday()
	local tod = orig_get_timeofday()
	if tod > morning and tod < evening then
		return 0.5
	else
		return 0
	end
end

beds.on_rightclick = function(pos, player)
	minetest.get_timeofday = custom_get_timeofday
	old_on_rightclick(pos, player)
	minetest.get_timeofday = orig_get_timeofday
end
I absolutely love that this kind of thing is possible in Lua.

Edit: updated version based on Blockheads suggestions:

Code: Select all

local morning = 0.2
local evening = 0.75 -- original is 0.805

local old_on_rightclick = beds.on_rightclick
local function custom_get_timeofday()
	local tod = minetest.get_timeofday()
	assert(tod, "No time of day.")
	if tod > morning and tod < evening then
		return 0.5
	else
		return 0
	end
end

local mints = {get_timeofday = custom_get_timeofday}
setmetatable(mints, {__index = minetest})
local new_env = {minetest = mints}
setmetatable(new_env, {__index = _G})
setfenv(old_on_rightclick, new_env)

beds.on_rightclick = function(pos, player)
	old_on_rightclick(pos, player)
end
Last edited by Skamiz Kazzarch on Sat Sep 17, 2022 11:25, edited 1 time in total.

User avatar
Blockhead
Member
Posts: 1622
Joined: Wed Jul 17, 2019 10:14
GitHub: Montandalar
IRC: Blockhead256
In-game: Blockhead Blockhead256
Location: Land Down Under
Contact:

Re: Post your code! - How I changed sleeping times.

by Blockhead » Post

Skamiz Kazzarch wrote:
Fri Sep 16, 2022 13:41

Code: Select all

beds.on_rightclick = function(pos, player)
	minetest.get_timeofday = custom_get_timeofday
	old_on_rightclick(pos, player)
	minetest.get_timeofday = orig_get_timeofday
end
I absolutely love that this kind of thing is possible in Lua.
Well uh I mean it works which is great, but it's not thread-safe. Minetest is of course mostly single-threaded (for now), but it's a bit sketchy. It would say to get my tick of approval it should use a custom environment to run the old_on_rightclick function which has a custom minetest table. You know, like LuaControllers or advtrains LuaATC do.
/˳˳_˳˳]_[˳˳_˳˳]_[˳˳_˳˳\ Advtrains enthusiast | My map: Noah's Railyard | My Content on ContentDB ✝️♂

User avatar
v-rob
Developer
Posts: 970
Joined: Thu Mar 24, 2016 03:19
GitHub: v-rob
IRC: v-rob
Location: Right behind you.

Re: Post your code!

by v-rob » Post

I personally like that solution; it's a good way to work around inherent limitations. I'm almost certain that Lua itself isn't thread safe, so two threads can't use the same state, meaning that this code can't break. Even the new async API uses its own state and doesn't interfere with normal Lua operations.

Environments might be better, but they're complicated, and this code seems to work well to me.
Core Developer | My Best Mods: Bridger - Slats - Stained Glass

User avatar
Skamiz Kazzarch
Member
Posts: 613
Joined: Fri Mar 09, 2018 20:34
GitHub: Skamiz
In-game: Skamiz
Location: la lojbaugag.

Re: Post your code!

by Skamiz Kazzarch » Post

Something something original licenses apply.
This is mostly a straight copy from "builtin/game/item.lua".

Anyway. It's just a small override you can use to prevent 'buildable_to' nodes from replacing themselves. That is, it prevents the player from placing jungle grass into jungle grass, snow into snow, etc... .

Code: Select all

local function user_name(user)
	return user and user:get_player_name() or ""
end

local function make_log(name)
	return name ~= "" and minetest.log or function() end
end

local function copy_pointed_thing(pointed_thing)
	return {
		type  = pointed_thing.type,
		above = pointed_thing.above and vector.copy(pointed_thing.above),
		under = pointed_thing.under and vector.copy(pointed_thing.under),
		ref   = pointed_thing.ref,
	}
end

function check_attached_node(p, n)
	local def = minetest.registered_nodes[n.name]
	local d = vector.zero()
	if def.paramtype2 == "wallmounted" or
			def.paramtype2 == "colorwallmounted" then
		-- The fallback vector here is in case 'wallmounted to dir' is nil due
		-- to voxelmanip placing a wallmounted node without resetting a
		-- pre-existing param2 value that is out-of-range for wallmounted.
		-- The fallback vector corresponds to param2 = 0.
		d = minetest.wallmounted_to_dir(n.param2) or vector.new(0, 1, 0)
	else
		d.y = -1
	end
	local p2 = vector.add(p, d)
	local nn = minetest.get_node(p2).name
	local def2 = minetest.registered_nodes[nn]
	if def2 and not def2.walkable then
		return false
	end
	return true
end

minetest.item_place_node = function(itemstack, placer, pointed_thing, param2, prevent_after_place)
	local def = itemstack:get_definition()
	if def.type ~= "node" or pointed_thing.type ~= "node" then
		return itemstack, nil
	end

	local under = pointed_thing.under
	local oldnode_under = minetest.get_node_or_nil(under)
	local above = pointed_thing.above
	local oldnode_above = minetest.get_node_or_nil(above)
	local playername = user_name(placer)
	local log = make_log(playername)

	if not oldnode_under or not oldnode_above then
		log("info", playername .. " tried to place"
			.. " node in unloaded position " .. minetest.pos_to_string(above))
		return itemstack, nil
	end

	local olddef_under = minetest.registered_nodes[oldnode_under.name]
	olddef_under = olddef_under or minetest.nodedef_default
	local olddef_above = minetest.registered_nodes[oldnode_above.name]
	olddef_above = olddef_above or minetest.nodedef_default
	local old_under_bildable_to = olddef_under.buildable_to and oldnode_under.name ~= def.name
	local old_above_bildable_to = olddef_above.buildable_to and oldnode_above.name ~= def.name

	if not old_above_bildable_to and not old_under_bildable_to then
		log("info", playername .. " tried to place"
			.. " node in invalid position " .. minetest.pos_to_string(above)
			.. ", replacing " .. oldnode_above.name)
		return itemstack, nil
	end

	-- Place above pointed node
	local place_to = vector.copy(above)

	-- If node under is buildable_to, place into it instead (eg. snow)
	if old_under_bildable_to then
		log("info", "node under is buildable to")
		place_to = vector.copy(under)
	end

	if minetest.is_protected(place_to, playername) then
		log("action", playername
				.. " tried to place " .. def.name
				.. " at protected position "
				.. minetest.pos_to_string(place_to))
		minetest.record_protection_violation(place_to, playername)
		return itemstack, nil
	end

	local oldnode = minetest.get_node(place_to)
	local newnode = {name = def.name, param1 = 0, param2 = param2 or 0}

	-- Calculate direction for wall mounted stuff like torches and signs
	if def.place_param2 ~= nil then
		newnode.param2 = def.place_param2
	elseif (def.paramtype2 == "wallmounted" or
			def.paramtype2 == "colorwallmounted") and not param2 then
		local dir = vector.subtract(under, above)
		newnode.param2 = minetest.dir_to_wallmounted(dir)
	-- Calculate the direction for furnaces and chests and stuff
	elseif (def.paramtype2 == "facedir" or
			def.paramtype2 == "colorfacedir") and not param2 then
		local placer_pos = placer and placer:get_pos()
		if placer_pos then
			local dir = vector.subtract(above, placer_pos)
			newnode.param2 = minetest.dir_to_facedir(dir)
			log("info", "facedir: " .. newnode.param2)
		end
	end

	local metatable = itemstack:get_meta():to_table().fields

	-- Transfer color information
	if metatable.palette_index and not def.place_param2 then
		local color_divisor = nil
		if def.paramtype2 == "color" then
			color_divisor = 1
		elseif def.paramtype2 == "colorwallmounted" then
			color_divisor = 8
		elseif def.paramtype2 == "colorfacedir" then
			color_divisor = 32
		elseif def.paramtype2 == "colordegrotate" then
			color_divisor = 32
		end
		if color_divisor then
			local color = math.floor(metatable.palette_index / color_divisor)
			local other = newnode.param2 % color_divisor
			newnode.param2 = color * color_divisor + other
		end
	end

	-- Check if the node is attached and if it can be placed there
	if minetest.get_item_group(def.name, "attached_node") ~= 0 and
		not check_attached_node(place_to, newnode) then
		log("action", "attached node " .. def.name ..
			" can not be placed at " .. minetest.pos_to_string(place_to))
		return itemstack, nil
	end

	log("action", playername .. " places node "
		.. def.name .. " at " .. minetest.pos_to_string(place_to))

	-- Add node and update
	minetest.add_node(place_to, newnode)

	-- Play sound if it was done by a player
	if playername ~= "" and def.sounds and def.sounds.place then
		minetest.sound_play(def.sounds.place, {
			pos = place_to,
			exclude_player = playername,
		}, true)
	end

	local take_item = true

	-- Run callback
	if def.after_place_node and not prevent_after_place then
		-- Deepcopy place_to and pointed_thing because callback can modify it
		local place_to_copy = vector.copy(place_to)
		local pointed_thing_copy = copy_pointed_thing(pointed_thing)
		if def.after_place_node(place_to_copy, placer, itemstack,
				pointed_thing_copy) then
			take_item = false
		end
	end

	-- Run script hook
	for _, callback in ipairs(minetest.registered_on_placenodes) do
		-- Deepcopy pos, node and pointed_thing because callback can modify them
		local place_to_copy = vector.copy(place_to)
		local newnode_copy = {name=newnode.name, param1=newnode.param1, param2=newnode.param2}
		local oldnode_copy = {name=oldnode.name, param1=oldnode.param1, param2=oldnode.param2}
		local pointed_thing_copy = copy_pointed_thing(pointed_thing)
		if callback(place_to_copy, newnode_copy, placer, oldnode_copy, itemstack, pointed_thing_copy) then
			take_item = false
		end
	end

	if take_item then
		itemstack:take_item()
	end
	return itemstack, place_to
end
My changes happen between the lines 60 and 80 of the posted snippet.

User avatar
Skamiz Kazzarch
Member
Posts: 613
Joined: Fri Mar 09, 2018 20:34
GitHub: Skamiz
In-game: Skamiz
Location: la lojbaugag.

Re: Post your code!

by Skamiz Kazzarch » Post

Have this:

Code: Select all

local r = math.pi/2
local d = math.pi
rotations.facedir = {
	[0] = vector.new(0, 0, 0),
	vector.new( 0, -r,  0),
	vector.new( 0,  d,  0),
	vector.new( 0,  r,  0),

	vector.new(-r,  0,  0),
	vector.new( 0, -r, -r),
	vector.new( r,  r,  r),
	vector.new( d, -r, -r),

	vector.new( r,  0,  0),
	vector.new( 0, -r,  r),
	vector.new(-r, -r,  r),
	vector.new( d, -r,  r),

	vector.new( 0,  0,  r),
	vector.new(-r,  0,  r),
	vector.new( d,  0,  r),
	vector.new( r,  0,  r),

	vector.new( 0,  0, -r),
	vector.new( r,  0, -r),
	vector.new( d,  0, -r),
	vector.new(-r,  0, -r),

	vector.new( 0,  0,  d),
	vector.new( 0,  r,  d),
	vector.new( 0,  d,  d),
	vector.new( 0, -r,  d),
}
What is it? It's a table mapping param2 values of 'facedir' nodes to the appropriate rotation vectors for objects.
If you have a stair node and an object using visual = "item" to look like the stair node, you can use this table to easily give the object the same rotation as the node.

User avatar
Skamiz Kazzarch
Member
Posts: 613
Joined: Fri Mar 09, 2018 20:34
GitHub: Skamiz
In-game: Skamiz
Location: la lojbaugag.

Re: Post your code!

by Skamiz Kazzarch » Post

Here's another table with the exactly same purpose as the one in my previous post except for the teeny tiny difference of this one being for the rotation of attached objects, instead of just objects by themselves.

Because why have both working the same way when you can:
1) Use degrees instead of radians.
2) Make the axis ordering Z>Y>X instead of Y>X>Z, (look up 'gimbal lock' if you don't know why this maters).
3) Have the rotation go in the opposite direction.

And I can safely expect this disparity to never change because merging it into a single rotation system would break backwards compatibility. Horay! \o/

Code: Select all

local d = 180
local r = d / 2
rotations.facedir = {
	[0] = vector.new(0, 0, 0),
	vector.new( 0,  r,  0),
	vector.new( 0,  d,  0),
	vector.new( 0, -r,  0),

	vector.new( r,  0,  0),
	vector.new( r,  0,  r),
	vector.new( r,  0,  d),
	vector.new( r,  0, -r),

	vector.new(-r,  0,  0),
	vector.new(-r,  0, -r),
	vector.new(-r,  0,  d),
	vector.new(-r,  0,  r),

	vector.new( 0,  0, -r),
	vector.new( 0,  r, -r),
	vector.new( 0,  d, -r),
	vector.new( 0, -r, -r),

	vector.new( 0,  0,  r),
	vector.new( 0, -r,  r),
	vector.new( 0,  d,  r),
	vector.new( 0,  r,  r),

	vector.new( 0,  0,  d),
	vector.new( 0,  r,  d),
	vector.new( 0,  d,  d),
	vector.new( 0, -r,  d),
}

User avatar
Desour
Member
Posts: 1469
Joined: Thu Jun 19, 2014 19:49
GitHub: Desour
IRC: Desour
In-game: DS
Location: I'm scared that if this is too exact, I will be unable to use my keyboard.

Re: Post your code!

by Desour » Post

Oh god. I never used set_attach rotations. I guess the minetest API never will never disappoint in showing how retarded it can be.

Btw., for your first table, you could also use my rotnums: rotnum.to_euler(rotnum.from_facedir(facedir))
They can also do the opposite ((with rounding) rotnum.to_facedir(rotnum.from_euler(euler_vec))), and much more (e.g. inverse and combine (aka rotate) rotations, and rotate vectors), in case someone needs that.
he/him; Codeberg; GitHub; ContentDB; public personal TODO list; "DS" is preferred (but often too short)

User avatar
apercy
Member
Posts: 640
Joined: Wed Mar 25, 2020 16:31
GitHub: APercy
In-game: APercy
Location: Pinheiral - RJ - Brazil

Re: Post your code!

by apercy » Post

Ah! Finally I found the right place to post this!
Well, I was playing recently with one Hybrid Dog experiment in shaders and I made one for water. So, try putting this file named as "opengl_fragment.glsl" to the path "minetest/client/shaders/nodes_shader/".
Remember to backup the original file before. And uses bloom activated. Works better on 5.7 version.

Code: Select all

uniform sampler2D baseTexture;

uniform vec3 dayLight;
uniform vec4 skyBgColor;
uniform float fogDistance;
uniform vec3 eyePosition;

// The cameraOffset is the current center of the visible world.
uniform vec3 cameraOffset;
uniform float animationTimer;
#ifdef ENABLE_DYNAMIC_SHADOWS
	// shadow texture
	uniform sampler2D ShadowMapSampler;
	// shadow uniforms
	uniform vec3 v_LightDirection;
	uniform float f_textureresolution;
	uniform mat4 m_ShadowViewProj;
	uniform float f_shadowfar;
	uniform float f_shadow_strength;
	uniform vec4 CameraPos;
	uniform float xyPerspectiveBias0;
	uniform float xyPerspectiveBias1;
	
	varying float adj_shadow_strength;
	varying float cosLight;
	varying float f_normal_length;
	varying vec3 shadow_position;
	varying float perspective_factor;
#endif


varying vec3 vNormal;
varying vec3 vPosition;
// World position in the visible world (i.e. relative to the cameraOffset.)
// This can be used for many shader effects without loss of precision.
// If the absolute position is required it can be calculated with
// cameraOffset + worldPosition (for large coordinates the limits of float
// precision must be considered).
varying vec3 worldPosition;
varying lowp vec4 varColor;
#ifdef GL_ES
varying mediump vec2 varTexCoord;
#else
centroid varying vec2 varTexCoord;
#endif
varying vec3 eyeVec;
varying float nightRatio;
varying vec3 tsEyeVec;
varying vec3 lightVec;
varying vec3 tsLightVec;

const float fogStart = FOG_START;
const float fogShadingParameter = 1.0 / ( 1.0 - fogStart);

#ifdef ENABLE_DYNAMIC_SHADOWS

// assuming near is always 1.0
float getLinearDepth()
{
	return 2.0 * f_shadowfar / (f_shadowfar + 1.0 - (2.0 * gl_FragCoord.z - 1.0) * (f_shadowfar - 1.0));
}

vec3 getLightSpacePosition()
{
	return shadow_position * 0.5 + 0.5;
}
// custom smoothstep implementation because it's not defined in glsl1.2
// https://docs.gl/sl4/smoothstep
float mtsmoothstep(in float edge0, in float edge1, in float x)
{
	float t = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);
	return t * t * (3.0 - 2.0 * t);
}

#ifdef COLORED_SHADOWS

// c_precision of 128 fits within 7 base-10 digits
const float c_precision = 128.0;
const float c_precisionp1 = c_precision + 1.0;

float packColor(vec3 color)
{
	return floor(color.b * c_precision + 0.5)
		+ floor(color.g * c_precision + 0.5) * c_precisionp1
		+ floor(color.r * c_precision + 0.5) * c_precisionp1 * c_precisionp1;
}

vec3 unpackColor(float value)
{
	vec3 color;
	color.b = mod(value, c_precisionp1) / c_precision;
	color.g = mod(floor(value / c_precisionp1), c_precisionp1) / c_precision;
	color.r = floor(value / (c_precisionp1 * c_precisionp1)) / c_precision;
	return color;
}

vec4 getHardShadowColor(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
{
	vec4 texDepth = texture2D(shadowsampler, smTexCoord.xy).rgba;

	float visibility = step(0.0, realDistance - texDepth.r);
	vec4 result = vec4(visibility, vec3(0.0,0.0,0.0));//unpackColor(texDepth.g));
	if (visibility < 0.1) {
		visibility = step(0.0, realDistance - texDepth.b);
		result = vec4(visibility, unpackColor(texDepth.a));
	}
	return result;
}

#else

float getHardShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
{
	float texDepth = texture2D(shadowsampler, smTexCoord.xy).r;
	float visibility = step(0.0, realDistance - texDepth);
	return visibility;
}

#endif


#if SHADOW_FILTER == 2
	#define PCFBOUND 2.0 // 5x5
	#define PCFSAMPLES 25
#elif SHADOW_FILTER == 1
	#define PCFBOUND 1.0 // 3x3
	#define PCFSAMPLES 9
#else
	#define PCFBOUND 0.0
	#define PCFSAMPLES 1
#endif

#ifdef COLORED_SHADOWS
float getHardShadowDepth(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
{
	vec4 texDepth = texture2D(shadowsampler, smTexCoord.xy);
	float depth = max(realDistance - texDepth.r, realDistance - texDepth.b);
	return depth;
}
#else
float getHardShadowDepth(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
{
	float texDepth = texture2D(shadowsampler, smTexCoord.xy).r;
	float depth = realDistance - texDepth;
	return depth;
}
#endif

#define BASEFILTERRADIUS 1.0

float getPenumbraRadius(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
{
	// Return fast if sharp shadows are requested
	if (PCFBOUND == 0.0 || SOFTSHADOWRADIUS <= 0.0)
		return 0.0;

	vec2 clampedpos;
	float y, x;
	float depth = getHardShadowDepth(shadowsampler, smTexCoord.xy, realDistance);
	// A factor from 0 to 1 to reduce blurring of short shadows
	float sharpness_factor = 1.0;
	// conversion factor from shadow depth to blur radius
	float depth_to_blur = f_shadowfar / SOFTSHADOWRADIUS / xyPerspectiveBias0;
	if (depth > 0.0 && f_normal_length > 0.0)
		// 5 is empirical factor that controls how fast shadow loses sharpness
		sharpness_factor = clamp(5 * depth * depth_to_blur, 0.0, 1.0);
	depth = 0.0;

	float world_to_texture = xyPerspectiveBias1 / perspective_factor / perspective_factor
			* f_textureresolution / 2.0 / f_shadowfar;
	float world_radius = 0.2; // shadow blur radius in world float coordinates, e.g. 0.2 = 0.02 of one node

	return max(BASEFILTERRADIUS * f_textureresolution / 4096.0,  sharpness_factor * world_radius * world_to_texture * SOFTSHADOWRADIUS);
}

#ifdef POISSON_FILTER
const vec2[64] poissonDisk = vec2[64](
	vec2(0.170019, -0.040254),
	vec2(-0.299417, 0.791925),
	vec2(0.645680, 0.493210),
	vec2(-0.651784, 0.717887),
	vec2(0.421003, 0.027070),
	vec2(-0.817194, -0.271096),
	vec2(-0.705374, -0.668203),
	vec2(0.977050, -0.108615),
	vec2(0.063326, 0.142369),
	vec2(0.203528, 0.214331),
	vec2(-0.667531, 0.326090),
	vec2(-0.098422, -0.295755),
	vec2(-0.885922, 0.215369),
	vec2(0.566637, 0.605213),
	vec2(0.039766, -0.396100),
	vec2(0.751946, 0.453352),
	vec2(0.078707, -0.715323),
	vec2(-0.075838, -0.529344),
	vec2(0.724479, -0.580798),
	vec2(0.222999, -0.215125),
	vec2(-0.467574, -0.405438),
	vec2(-0.248268, -0.814753),
	vec2(0.354411, -0.887570),
	vec2(0.175817, 0.382366),
	vec2(0.487472, -0.063082),
	vec2(0.355476, 0.025357),
	vec2(-0.084078, 0.898312),
	vec2(0.488876, -0.783441),
	vec2(0.470016, 0.217933),
	vec2(-0.696890, -0.549791),
	vec2(-0.149693, 0.605762),
	vec2(0.034211, 0.979980),
	vec2(0.503098, -0.308878),
	vec2(-0.016205, -0.872921),
	vec2(0.385784, -0.393902),
	vec2(-0.146886, -0.859249),
	vec2(0.643361, 0.164098),
	vec2(0.634388, -0.049471),
	vec2(-0.688894, 0.007843),
	vec2(0.464034, -0.188818),
	vec2(-0.440840, 0.137486),
	vec2(0.364483, 0.511704),
	vec2(0.034028, 0.325968),
	vec2(0.099094, -0.308023),
	vec2(0.693960, -0.366253),
	vec2(0.678884, -0.204688),
	vec2(0.001801, 0.780328),
	vec2(0.145177, -0.898984),
	vec2(0.062655, -0.611866),
	vec2(0.315226, -0.604297),
	vec2(-0.780145, 0.486251),
	vec2(-0.371868, 0.882138),
	vec2(0.200476, 0.494430),
	vec2(-0.494552, -0.711051),
	vec2(0.612476, 0.705252),
	vec2(-0.578845, -0.768792),
	vec2(-0.772454, -0.090976),
	vec2(0.504440, 0.372295),
	vec2(0.155736, 0.065157),
	vec2(0.391522, 0.849605),
	vec2(-0.620106, -0.328104),
	vec2(0.789239, -0.419965),
	vec2(-0.545396, 0.538133),
	vec2(-0.178564, -0.596057)
);

#ifdef COLORED_SHADOWS

vec4 getShadowColor(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
{
	float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance);
	if (radius < 0.1) {
		// we are in the middle of even brightness, no need for filtering
		return getHardShadowColor(shadowsampler, smTexCoord.xy, realDistance);
	}

	vec2 clampedpos;
	vec4 visibility = vec4(0.0);
	float scale_factor = radius / f_textureresolution;

	int samples = (1 + 1 * int(SOFTSHADOWRADIUS > 1.0)) * PCFSAMPLES; // scale max samples for the soft shadows
	samples = int(clamp(pow(4.0 * radius + 1.0, 2.0), 1.0, float(samples)));
	int init_offset = int(floor(mod(((smTexCoord.x * 34.0) + 1.0) * smTexCoord.y, 64.0-samples)));
	int end_offset = int(samples) + init_offset;

	for (int x = init_offset; x < end_offset; x++) {
		clampedpos = poissonDisk[x] * scale_factor + smTexCoord.xy;
		visibility += getHardShadowColor(shadowsampler, clampedpos.xy, realDistance);
	}

	return visibility / samples;
}

#else

float getShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
{
	float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance);
	if (radius < 0.1) {
		// we are in the middle of even brightness, no need for filtering
		return getHardShadow(shadowsampler, smTexCoord.xy, realDistance);
	}

	vec2 clampedpos;
	float visibility = 0.0;
	float scale_factor = radius / f_textureresolution;

	int samples = (1 + 1 * int(SOFTSHADOWRADIUS > 1.0)) * PCFSAMPLES; // scale max samples for the soft shadows
	samples = int(clamp(pow(4.0 * radius + 1.0, 2.0), 1.0, float(samples)));
	int init_offset = int(floor(mod(((smTexCoord.x * 34.0) + 1.0) * smTexCoord.y, 64.0-samples)));
	int end_offset = int(samples) + init_offset;

	for (int x = init_offset; x < end_offset; x++) {
		clampedpos = poissonDisk[x] * scale_factor + smTexCoord.xy;
		visibility += getHardShadow(shadowsampler, clampedpos.xy, realDistance);
	}

	return visibility / samples;
}

#endif

#else
/* poisson filter disabled */

#ifdef COLORED_SHADOWS

vec4 getShadowColor(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
{
	float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance);
	if (radius < 0.1) {
		// we are in the middle of even brightness, no need for filtering
		return getHardShadowColor(shadowsampler, smTexCoord.xy, realDistance);
	}

	vec2 clampedpos;
	vec4 visibility = vec4(0.0);
	float x, y;
	float bound = (1 + 0.5 * int(SOFTSHADOWRADIUS > 1.0)) * PCFBOUND; // scale max bound for soft shadows
	bound = clamp(0.5 * (4.0 * radius - 1.0), 0.5, bound);
	float scale_factor = radius / bound / f_textureresolution;
	float n = 0.0;

	// basic PCF filter
	for (y = -bound; y <= bound; y += 1.0)
	for (x = -bound; x <= bound; x += 1.0) {
		clampedpos = vec2(x,y) * scale_factor + smTexCoord.xy;
		visibility += getHardShadowColor(shadowsampler, clampedpos.xy, realDistance);
		n += 1.0;
	}

	return visibility / max(n, 1.0);
}

#else
float getShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
{
	float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance);
	if (radius < 0.1) {
		// we are in the middle of even brightness, no need for filtering
		return getHardShadow(shadowsampler, smTexCoord.xy, realDistance);
	}

	vec2 clampedpos;
	float visibility = 0.0;
	float x, y;
	float bound = (1 + 0.5 * int(SOFTSHADOWRADIUS > 1.0)) * PCFBOUND; // scale max bound for soft shadows
	bound = clamp(0.5 * (4.0 * radius - 1.0), 0.5, bound);
	float scale_factor = radius / bound / f_textureresolution;
	float n = 0.0;

	// basic PCF filter
	for (y = -bound; y <= bound; y += 1.0)
	for (x = -bound; x <= bound; x += 1.0) {
		clampedpos = vec2(x,y) * scale_factor + smTexCoord.xy;
		visibility += getHardShadow(shadowsampler, clampedpos.xy, realDistance);
		n += 1.0;
	}

	return visibility / max(n, 1.0);
}

#endif

#endif
#endif

vec3 get_water_reflections(vec4 base, vec3 eyePosition, vec3 cameraOffset, vec3 worldPosition,
                        vec3 v_LightDirection, vec3 dayLight, vec4 col, float f_adj_shadow_strength,
                        vec3 vNormal, float shadow_int)
{
    #if (MATERIAL_TYPE == TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT || MATERIAL_TYPE == TILE_MATERIAL_WAVING_LIQUID_OPAQUE || MATERIAL_TYPE == TILE_MATERIAL_WAVING_LIQUID_BASIC) || (MATERIAL_TYPE == TILE_MATERIAL_LIQUID_TRANSPARENT || MATERIAL_TYPE == TILE_MATERIAL_LIQUID_OPAQUE || MATERIAL_TYPE == TILE_MATERIAL_LIQUID_BASIC)
    shadow_int *= f_adj_shadow_strength; //adjust by strength

    // Get the Y component of base.rgb converted to YCbCr as an approximate
    // brightness of the albedo
    float albedo_y = dot(base.rgb, vec3(0.299, 0.587, 0.114));
    albedo_y = albedo_y - min(shadow_int,albedo_y); //remove from the shadows

    // There are no specular map textures, so approximate the shininess
    // and specular reflections strength with albedo_y
    float shininess = 6.0 + (16.0 * (1.0 - albedo_y));
    float specular_visibility = albedo_y; //pow(albedo_y, 3.0);

    // view_dir: Vector from the fragment to the eye in world space.
    // (eyeVec is view_dir in view space, but v_LightDirection is in
    // world space)
    vec3 view_dir = normalize((eyePosition - cameraOffset) - worldPosition);
    vec3 halfway = normalize(normalize(view_dir) + normalize(-v_LightDirection));

    // Colour (and intensity) of the sun/moon light
    vec3 light_colour = mix(dayLight.rgb/4, col.rgb, 0.5);

    float multiplier = 7.0;
    specular_visibility *= multiplier - (shadow_int*multiplier); //set shadow correctly

    // Calculate specular reflections with the Blinn-Phong model
    float spec = specular_visibility * pow(max(dot(vNormal, halfway), 0.0), shininess);
    
    vec3 retVal = light_colour * spec;
    return retVal;
    #else
    return vec3(0.0);
    #endif
}

void main(void)
{
	vec3 color;
	vec2 uv = varTexCoord.st;

	vec4 base = texture2D(baseTexture, uv).rgba;
	// If alpha is zero, we can just discard the pixel. This fixes transparency
	// on GPUs like GC7000L, where GL_ALPHA_TEST is not implemented in mesa,
	// and also on GLES 2, where GL_ALPHA_TEST is missing entirely.
#ifdef USE_DISCARD
	if (base.a == 0.0)
		discard;
#endif
#ifdef USE_DISCARD_REF
	if (base.a < 0.5)
		discard;
#endif

	color = base.rgb;
	vec4 col = vec4(color.rgb * varColor.rgb, 1.0);

#ifdef ENABLE_DYNAMIC_SHADOWS
	if (f_shadow_strength > 0.0) {
		float shadow_int = 0.0;
		vec3 shadow_color = vec3(0.0, 0.0, 0.0);
		vec3 posLightSpace = getLightSpacePosition();

		float distance_rate = (1.0 - pow(clamp(2.0 * length(posLightSpace.xy - 0.5),0.0,1.0), 10.0));
		if (max(abs(posLightSpace.x - 0.5), abs(posLightSpace.y - 0.5)) > 0.5)
			distance_rate = 0.0;
		float f_adj_shadow_strength = max(adj_shadow_strength-mtsmoothstep(0.9,1.1,  posLightSpace.z),0.0);

		if (distance_rate > 1e-7) {

#ifdef COLORED_SHADOWS
			vec4 visibility;
			if (cosLight > 0.0 || f_normal_length < 1e-3)
				visibility = getShadowColor(ShadowMapSampler, posLightSpace.xy, posLightSpace.z);
			else
				visibility = vec4(1.0, 0.0, 0.0, 0.0);
			shadow_int = visibility.r;
			shadow_color = visibility.gba;
#else
			if (cosLight > 0.0 || f_normal_length < 1e-3)
				shadow_int = getShadow(ShadowMapSampler, posLightSpace.xy, posLightSpace.z);
			else
				shadow_int = 1.0;
#endif
			shadow_int *= distance_rate;
			shadow_int = clamp(shadow_int, 0.0, 1.0);

		}

		// turns out that nightRatio falls off much faster than
		// actual brightness of artificial light in relation to natual light.
		// Power ratio was measured on torches in MTG (brightness = 14).
		float adjusted_night_ratio = pow(max(0.0, nightRatio), 0.6);

		// Apply self-shadowing when light falls at a narrow angle to the surface
		// Cosine of the cut-off angle.
		const float self_shadow_cutoff_cosine = 0.035;
		if (f_normal_length != 0 && cosLight < self_shadow_cutoff_cosine) {
			shadow_int = max(shadow_int, 1 - clamp(cosLight, 0.0, self_shadow_cutoff_cosine)/self_shadow_cutoff_cosine);
			shadow_color = mix(vec3(0.0), shadow_color, min(cosLight, self_shadow_cutoff_cosine)/self_shadow_cutoff_cosine);
		}

		shadow_int *= f_adj_shadow_strength;

        vec3 water_reflections = get_water_reflections(base, eyePosition, cameraOffset, worldPosition,
                        v_LightDirection, dayLight, col, f_adj_shadow_strength,
                        vNormal, shadow_int);

		// calculate fragment color from components:
		col.rgb =
				adjusted_night_ratio * col.rgb + // artificial light
				(1.0 - adjusted_night_ratio) * ( // natural light
						col.rgb * (1.0 - shadow_int * (1.0 - shadow_color)) +  // filtered texture color
                        water_reflections +
						dayLight * shadow_color * shadow_int);                 // reflected filtered sunlight/moonlight
	}
#endif

	// Due to a bug in some (older ?) graphics stacks (possibly in the glsl compiler ?),
	// the fog will only be rendered correctly if the last operation before the
	// clamp() is an addition. Else, the clamp() seems to be ignored.
	// E.g. the following won't work:
	//      float clarity = clamp(fogShadingParameter
	//		* (fogDistance - length(eyeVec)) / fogDistance), 0.0, 1.0);
	// As additions usually come for free following a multiplication, the new formula
	// should be more efficient as well.
	// Note: clarity = (1 - fogginess)
	float clarity = clamp(fogShadingParameter
		- fogShadingParameter * length(eyeVec) / fogDistance, 0.0, 1.0);
	col = mix(skyBgColor, col, clarity);
	col = vec4(col.rgb, base.a);

	gl_FragData[0] = col;
}

User avatar
RobotZombieWizGirl
Member
Posts: 14
Joined: Wed Jan 18, 2023 22:42
GitHub: RobotZombieWizardGirl
In-game: RobotZombieWizardGirl

Re: Post your code!

by RobotZombieWizGirl » Post

This snippet roughly finds all the "stone" nodes in any game to determine where to place ores and what textures to use for ore nodes. This is one of the strategies I'm using to develop game-agnostic mods. I have a similar approach for finding surface nodes and a different method for figuring out which metals are available in a game.

Code: Select all

local stone_nodes = {minetest.registered_aliases['mapgen_stone']}
local processed = {}
for _, v in pairs(minetest.registered_biomes) do
	if v.node_stone and not (v.node_stone: find 'sand' or v.node_stone: find 'ice') and (not processed[v.node_stone]) then
		table.insert(stone_nodes, v.node_stone)
		processed[v.node_stone] = true
	end
end

User avatar
sorcerykid
Member
Posts: 1841
Joined: Fri Aug 26, 2016 15:36
GitHub: sorcerykid
In-game: Nemo
Location: Illinois, USA

Re: Post your code!

by sorcerykid » Post

Here's' a Lua helper function that returns ordinal numbers as a string with the correct suffix, such as "10th", "2nd", etc.

get_ordinal( num )
Converts integer num to a string with the correct ordinal suffix.

Code: Select all

local function get_ordinal( num )
        if ( num - 1 ) % 10 == 0 and num ~= 11 then
                return num .. "st"
        elseif ( num - 2 ) % 10 == 0 and num ~= 12 then
                return num .. "nd"
        elseif ( num - 3 ) % 10 == 0 and num ~= 13 then
                return num .. "rd"
        else
                return num .. "th"
        end
end

Tim790
Member
Posts: 124
Joined: Mon May 31, 2021 23:32
GitHub: Tim79000
In-game: Tim7
Location: Land of the Midnight Sun

Re: Post your code!

by Tim790 » Post

minetest.register_node("terraific:amaranth_tree", {
description = ("Amaranth Tree"),
tiles = {"terraific_amaranth_tree.png", "terraific_amaranth_tree.png", "terraific_amaranth_tree_side.png"},
paramtype2 = "facedir",
is_ground_content = false,
groups = {tree = 1, choppy = 2, oddly_breakable_by_hand = 1, flammable = 2},
sounds = default.node_sound_wood_defaults(),

on_place = minetest.rotate_node
})
Yeet.

Tim790
Member
Posts: 124
Joined: Mon May 31, 2021 23:32
GitHub: Tim79000
In-game: Tim7
Location: Land of the Midnight Sun

Re: Post your code!

by Tim790 » Post

Awesome, I know.
Yeet.

User avatar
iisu
Member
Posts: 220
Joined: Tue Mar 28, 2017 20:13
GitHub: iisu
IRC: iisu
In-game: iisu
Location: Internet

Re: Post your code!

by iisu » Post

sorcerykid wrote:
Tue Apr 18, 2023 16:55
Here's' a Lua helper function that returns ordinal numbers as a string with the correct suffix, such as "10th", "2nd", etc.

get_ordinal( num )
Converts integer num to a string with the correct ordinal suffix.

Code: Select all

local function get_ordinal( num )
        if ( num - 1 ) % 10 == 0 and num ~= 11 then
                return num .. "st"
        elseif ( num - 2 ) % 10 == 0 and num ~= 12 then
                return num .. "nd"
        elseif ( num - 3 ) % 10 == 0 and num ~= 13 then
                return num .. "rd"
        else
                return num .. "th"
        end
end
I remade it for Polish just for fun:

Code: Select all

local function get_ordinal(num)
        if num == 0 then        
                return num .. "-wy"
        elseif (num % 100) >= 10 and (num % 100) < 20 then
                return num .. "-ty"
        else
            return num .. (({ "-szy", "-gi", "-ci", [7] = "-my", [8] = "-my" })[num % 10] or "-ty")
        end
end
Now, this is an incorrect way of writing the ordinals, but still kind of popular. The correct way would be just:

Code: Select all

local function get_ordinal(num)
        return num .. "."
end
PS: your code won't work correctly for num > 100.
Roses are red, violets are blue. Omae wa mou shindeiru.

User avatar
Hybrid Dog
Member
Posts: 2828
Joined: Thu Nov 01, 2012 12:46
GitHub: HybridDog

Re: Post your code!

by Hybrid Dog » Post

I phound this code and highly doubt that it works.

Code: Select all

local my_shader_material = minetest.create_shader_material({
  vertex_shader = my_vertex_shader,
  fragment_shader = my_fragment_shader,
  uniforms = {
    u_color = { 1.0, 0.0, 0.0 },
    u_time = 0.0,
  },
})
local my_mesh = minetest.create_mesh({
  geometry = my_geometry,
  material = my_shader_material,
})
local function on_material_change(new_material)
  my_shader_material:set_uniform("u_color", new_material.color)
  my_shader_material:set_uniform("u_time", new_material.time)
end

‮‪‮
‮‪‮
‮‪‮
‮‪‮
‮‪‮
‮‪‮
‮‪‮
‮‪‮
‮‪

User avatar
Hybrid Dog
Member
Posts: 2828
Joined: Thu Nov 01, 2012 12:46
GitHub: HybridDog

Re: Post your code!

by Hybrid Dog » Post

A very simple class to measure time differences:

Code: Select all

local Stopwatch = {}
setmetatable(Stopwatch, {__call = function()
	local obj = {
		time = minetest.get_us_time()
	}
	setmetatable(obj, Stopwatch)
	return obj
end})
Stopwatch.__index = {
	click = function(self, msg_prefix)
		local t = minetest.get_us_time()
		local msg = ("%s after %.5g s"):format(msg_prefix,
			(t - self.time) / 1000000)
		self.time = t
		return msg
	end
}
Usage:

Code: Select all

local stwatch = Stopwatch()
-- do something
minetest.chat_send_all(stwatch:click("Doing something finished"))
-- do something else
minetest.chat_send_all(stwatch:click("Doing something else finished"))
I'm not sure if I've shown this code already or not.
A function to set a big number of nodes at once:

Code: Select all

-- A simple function to set nodes with vmanip instead of set_node
-- Nodes is a list, for example {{{x=0, y=1, z=2}, "default:cobble"}}
local function simple_vmanip(nodes)
	local num_nodes = #nodes
	if num_nodes == 0 then
		return
	end
	local minp = vector.new(nodes[1][1])
	local maxp = vector.new(minp)
	for i = 1, num_nodes do
		local pos = nodes[i][1]
		local coords = {"x", "y", "z"}
		for k = 1, 3 do
			local c = coords[k]
			if pos[c] < minp[c] then
				minp[c] = pos[c]
			elseif pos[c] > maxp[c] then
				maxp[c] = pos[c]
			end
		end
	end

	local manip = minetest.get_voxel_manip()
	local e1, e2 = manip:read_from_map(minp, maxp)
	local area = VoxelArea:new{MinEdge=e1, MaxEdge=e2}
	local data = manip:get_data()

	local ids = {}
	for i = 1, num_nodes do
		local vi = area:indexp(nodes[i][1])
		local nodename = nodes[i][2]
		ids[nodename] = ids[nodename] or minetest.get_content_id(nodename)
		data[vi] = ids[nodename]
	end

	manip:set_data(data)
	manip:write_to_map()
end

‮‪‮
‮‪‮
‮‪‮
‮‪‮
‮‪‮
‮‪‮
‮‪‮
‮‪‮
‮‪

User avatar
sorcerykid
Member
Posts: 1841
Joined: Fri Aug 26, 2016 15:36
GitHub: sorcerykid
In-game: Nemo
Location: Illinois, USA

Re: Post your code!

by sorcerykid » Post

I already created a Stopwatch mod for easily benchmarking sections of code:

https://github.com/sorcerykid/stopwatch/blob/master/example.lua

Running the example above with multiple iterations produces the following output:

Image

What's particularly nice is how it shows a summary of all the test series at shutdown (including average, median, standard deviation, minimum, and maximum). So for server operators, it's an essential tool for daily reporting of server performance.

User avatar
Hybrid Dog
Member
Posts: 2828
Joined: Thu Nov 01, 2012 12:46
GitHub: HybridDog

Re: Post your code!

by Hybrid Dog » Post

sorcerykid wrote:
Tue Apr 25, 2023 21:43
I already created a Stopwatch mod for easily benchmarking sections of code:

https://github.com/sorcerykid/stopwatch/blob/master/example.lua

Running the example above with multiple iterations produces the following output:

Image

What's particularly nice is how it shows a summary of all the test series at shutdown (including average, median, standard deviation, minimum, and maximum). So for server operators, it's an essential tool for daily reporting of server performance.
I think I've found two mistakes in your stopwatch code:

The calculation of the median is wrong. The median is the 50% percentile. To calculate it, you can sort trials and take the element in the center (e.g. trials[#trials / 2] if the sorted array has an odd number of elements).
Your code calculates the Midrange and not the Median; see https://en.wikipedia.org/wiki/Median.

The standard deviation estimation is biased. While for the calculation of the variance, Bessel's correction can be used, for the standard deviation it is more complicated. I think it is reasonable to assume that times are normally distributed, so the rule of thumb could be used. In your code this means replacing

Code: Select all

v_dev = math.sqrt( v_var / #trials )
by

Code: Select all

v_dev = math.sqrt( v_var / (#trials - 1.5) )
.



Nonetheless, I like that it supports colourised output.

‮‪‮
‮‪‮
‮‪‮
‮‪‮
‮‪‮
‮‪‮
‮‪‮
‮‪‮
‮‪

User avatar
sorcerykid
Member
Posts: 1841
Joined: Fri Aug 26, 2016 15:36
GitHub: sorcerykid
In-game: Nemo
Location: Illinois, USA

Re: Post your code!

by sorcerykid » Post

Here's a very simple script for RocketLib Toolkit that calculates the peak elevation on the map. So if you've ever wondered where the heighest generated mountain is, then this will give you the exact coordinates.

You can specify the set of search_nodes to account for non-standard terrain (defaults to stone and desert stone). The minimum peak elevation can also be specified by peak_pos.y.

Code: Select all

package.path = "/home/minetest/maplib/?.lua;" .. package.path
local maplib = require "maplib"

local source_path = "/pub/worlds/test/map.sqlite"
local search_nodes = {
	["default:stone"] = true,
	["default:desert_stone"] = true,
}

local peak_pos = { y = 75 }

---------------------------------------

local decode_pos = maplib.decode_pos
local decode_node_pos = maplib.decode_node_pos

local function analyze_block( block, index, node_list, nodename_map )
	local is_found = false

	for node_idx, node in ipairs( node_list ) do
		local node_name = nodename_map[ node.id ]
		local pos = decode_node_pos( node_idx, index )

		if search_nodes[ node_name ] and pos.y > peak_pos.y then
			peak_pos = pos
			print( "Next elevation: " .. peak_pos.y .. "m in mapblock " .. index )
		end
	end
end

-----------------------------------------

local map_db = MapDatabase( source_path, false )

for index, block in map_db.iterate( ) do
	local block_pos = decode_pos( index )
	if block_pos.y >= math.floor( peak_pos.y / 16 ) then
		local node_list = block.get_node_list( )
		local nodename_map = block.nodename_map

		analyze_block( block, index, node_list, nodename_map )
	end
end

if peak_pos.x then
	print( "Peak elevation at (" .. peak_pos.x .. "," .. peak_pos.y .. "," .. peak_pos.z .. ")" )
end
NB: With some simple tweaking this script could account for peak elevations at multiple coordinates.

Pro Superstar
Member
Posts: 10
Joined: Sat Aug 19, 2023 19:56

Re: Post your code!

by Pro Superstar » Post

This is my first creation in Minetest engine! I wanted to create a recipe for the enderpearl mod, so I had the idea to use MTG's dye mod as a sort of quick herbalism concept.

Code: Select all

minetest.register_craft({
 type = "shapeless",
 output = "enderpearl:ender_pearl",
 recipe = {
 "dye:red", "dye:orange", "dye:yellow",
 "dye:magenta", "default:snow", "dye:green",
 "dye:violet", "dye:blue", "dye:cyan",
 }
})
Flowers could be ground into an essence that can be used in alchemy, but I haven't put much thought beyond this one recipe. As a side effect, coal and blueberries are used similarly, but I'm not that concerned about the overlap. Others have been more ambitious as to create their own custom ingredients that can be generated, but I saw the potential already there, plus not using the flora directly provides some interaction in the processing.

User avatar
TenPlus1
Member
Posts: 3715
Joined: Mon Jul 29, 2013 13:38
In-game: TenPlus1
Contact:

Double Jump Code

by TenPlus1 » Post

The simplest way to implement double-jump in Minetest:

Code: Select all

local jumping = {}
local holding = {}

minetest.register_privilege("extra_jump", {
	description = "Give players an extra jump",
	give_to_singleplayer = false, give_to_admin = false
})

minetest.register_globalstep(function(dtime)

	local controls, privs, flying, name, vel

	for _, player in ipairs(minetest.get_connected_players()) do

		privs = minetest.get_player_privs(player:get_player_name())
		controls = player:get_player_control()
		name = player:get_player_name()
		vel = player:get_velocity()
		flying = (privs.fly and minetest.settings:get_bool("free_move")) or not privs.extra_jump

		if vel.y > 0 and controls.jump and not flying and jumping[name] == nil then

			jumping[name] = true
			holding[name] = true

			minetest.sound_play("default_place_node",
						{to_player = name, gain = 0.7, pitch = 1.4}, true)

		elseif controls.jump and not holding[name] and not flying and jumping[name] == true then

			local physics = player:get_physics_override()
			local jump_speed = physics.jump * 6.5
			local up_vel = vel.y < 0 and -vel.y + jump_speed or jump_speed

			player:add_velocity({x = vel.x, y = up_vel, z = vel.z})

			jumping[name] = false

			minetest.sound_play("default_place_node_hard",
						{to_player = name, gain = 0.7, pitch = 1.4}, true)

		elseif vel.y == 0 then jumping[name] = nil
		elseif not controls.jump then holding[name] = nil
		end
	end
end)

Post Reply

Who is online

Users browsing this forum: No registered users and 14 guests