diff --git a/mods/MISC/mcl_anti_dimension_hopping/init.lua b/mods/MISC/mcl_anti_dimension_hopping/init.lua new file mode 100644 index 00000000..222a002e --- /dev/null +++ b/mods/MISC/mcl_anti_dimension_hopping/init.lua @@ -0,0 +1,196 @@ +local S = minetest.get_translator("mcl_anti_dimension_hopping") + +local storage = minetest.get_mod_storage() + +local MAX_DISTANCE = 40 + +local dimension_change_is_legal = function( + player_name, -- string + dimension, -- "end"|"nether"|"overworld"|"void" + last_dimension, -- "end"|"nether"|"overworld"|"void" + last_last_dimension -- "end"|"nether"|"overworld"|"void"|nil +) + local player = minetest.get_player_by_name(player_name) + if nil == player then + return false + end + local player_position = player:get_pos() + if "end" == dimension then + assert( player_position.y > mcl_vars.mg_end_min ) + assert( player_position.y < mcl_vars.mg_end_max ) + -- Going to the End through a portal is allowed, but a + -- player must appear at End spawn for it to be legal. + -- The Nether as the last dimension is included here + -- because an End portal in the nether sends players + -- to End spawn, but cheaters can not teleport down + -- from the Nether, as the Nether is below the End. + if ( + "overworld" == last_dimension or + "nether" == last_dimension + ) then + if vector.distance( + player_position, + mcl_vars.mg_end_platform_pos + ) < MAX_DISTANCE then + return true + end + end + -- Going to the End via the Void is allowed only if + -- the player entered the Void from the End … nil has + -- to be handled here for players who were in the Void + -- when this mod was activated. + if "void" == last_dimension then + if ( + "end" == last_last_dimension or + nil == last_last_dimension + ) then + return true + end + end + end + if "nether" == dimension then + assert( player_position.y > mcl_vars.mg_nether_min ) + assert( player_position.y < mcl_vars.mg_nether_max ) + -- Going to the Nether through a portal is allowed. + if "overworld" == last_dimension then + if nil ~= minetest.find_node_near( + player_position, + MAX_DISTANCE, + "mcl_portals:portal" + ) then + -- TODO: check for portal in Overworld + return true + end + end + -- Going to the Nether via the Void is allowed only if + -- the player entered the Void from the Nether … nil + -- has to be handled here for players who were in the + -- Void when this mod was activated. + if "void" == last_dimension then + if ( + "nether" == last_last_dimension or + nil == last_last_dimension + ) then + return true + end + end + end + if "overworld" == dimension then + assert( player_position.y > mcl_vars.mg_overworld_min ) + assert( player_position.y < mcl_vars.mg_overworld_max ) + -- Going to the Overworld from the Nether through a + -- portal is allowed. + if "nether" == last_dimension then + if nil ~= minetest.find_node_near( + player_position, + MAX_DISTANCE, + "mcl_portals:portal" + ) then + -- TODO: check for portal in Nether + return true + end + return true + end + -- Going to the Overworld via the Void is allowed only + -- if the player entered the Void from the Overworld … + -- nil has to be handled here for players who were in + -- the Void when this mod was activated. + if "void" == last_dimension then + if ( + "overworld" == last_last_dimension or + nil == last_last_dimension + ) then + return true + end + end + -- Going to the Overworld via the End (or any other + -- way) is allowed only if the player appears at their + -- respawn location, either because they used a portal + -- in the End or because they died. + local player_spawn_position = ( + mcl_spawn.get_player_spawn_pos(player) or + mcl_spawn.get_world_spawn_pos(player) + ) + if vector.distance( + player_position, + player_spawn_position + ) < MAX_DISTANCE then + return true + end + end + if "void" == dimension then + -- Going to the Void is always legal – a player who + -- enters the Void and then goes anywhere else than + -- where they came from is already handled above. + return true + end + return false +end + +local kill_player_for_dimensional_trespassing = function( + player_name, + dimension, + last_dimension, + last_last_dimension +) + local player = minetest.get_player_by_name(player_name) + if nil == player then + return + end + -- If the player holds a totem of undying, destroy it before + -- killing, so it doesn't rescue the player. + local wield = player:get_wielded_item() + if wield:get_name() == "mobs_mc:totem" then + player:set_wielded_item("") + end + local death_message = S( + "@1 was executed for dimensional trespassing.", + player_name + ) + mcl_death_messages.player_damage( + player, + death_message + ) + player:set_hp(0) + local log_message = string.format( + "%s was executed for dimensional trespassing: %s → %s → %s", + tostring(player_name), + tostring(last_last_dimension), + tostring(last_dimension), + tostring(dimension) + ) + minetest.log( + "action", + log_message + ) +end + +mcl_worlds.register_on_dimension_change( + function(player, dimension, last_dimension) + local player_name = player:get_player_name() + local last_last_dimension = storage:get_string( + player_name .. ":dimension_previous_previous" + ) + if not dimension_change_is_legal( + player_name, + dimension, + last_dimension, + last_last_dimension + ) then + kill_player_for_dimensional_trespassing( + player_name, + dimension, + last_dimension, + last_last_dimension + ) + end + storage:set_string( + player_name .. ":dimension_previous_previous", + last_dimension + ) + storage:set_string( + player_name .. ":dimension_previous", + dimension + ) + end +) diff --git a/mods/MISC/mcl_anti_dimension_hopping/mod.conf b/mods/MISC/mcl_anti_dimension_hopping/mod.conf new file mode 100644 index 00000000..f0822e10 --- /dev/null +++ b/mods/MISC/mcl_anti_dimension_hopping/mod.conf @@ -0,0 +1,2 @@ +name = mcl_anti_dimension_hopping +depends = mcl_init, mcl_death_messages, mcl_portals, mcl_spawn, mcl_worlds