197 lines
5.3 KiB
Lua
197 lines
5.3 KiB
Lua
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
|
||
)
|