From a3fb5c6164d5123eddb7522608c19f412c907b69 Mon Sep 17 00:00:00 2001 From: kay27 Date: Thu, 16 Dec 2021 05:52:50 +0400 Subject: [PATCH 1/5] Try to disable flights in Survival Mode (Hi Mr_Anderson) --- mods/PLAYER/mcl_anticheat/init.lua | 128 +++++++++++++++++++++++++++++ mods/PLAYER/mcl_anticheat/mod.conf | 3 + 2 files changed, 131 insertions(+) create mode 100644 mods/PLAYER/mcl_anticheat/init.lua create mode 100644 mods/PLAYER/mcl_anticheat/mod.conf diff --git a/mods/PLAYER/mcl_anticheat/init.lua b/mods/PLAYER/mcl_anticheat/init.lua new file mode 100644 index 000000000..ec135f3bc --- /dev/null +++ b/mods/PLAYER/mcl_anticheat/init.lua @@ -0,0 +1,128 @@ +local flights_kick_threshold = 10 + +local after = minetest.after +local get_connected_players = minetest.get_connected_players +local get_node = minetest.get_node +local get_player_by_name = minetest.get_player_by_name +local is_creative_enabled = minetest.is_creative_enabled +local kick_player = minetest.kick_player +local pos_to_string = minetest.pos_to_string + +local ceil = math.ceil +local floor = math.floor + +local window_size = 10 +local detection_interval = 1.7 +local step_seconds = detection_interval / window_size +local joined_players = {} + +local function update_player(player_object) + if not player_object then return end + local name = player_object:get_player_name() + if not name then return end + + local pos = player_object:get_pos() + local x, y, z = floor(pos.x), floor(pos.y-0.1), floor(pos.z) + + if mcl_playerplus.elytra then + local elytra = mcl_playerplus.elytra[player_object] + if elytra and elytra.active then + return + end + end + + local air = get_node({x = x , y = y , z = z }).name == "air" + and get_node({x = x , y = y , z = z + 1}).name == "air" + and get_node({x = x , y = y + 1, z = z }).name == "air" + and get_node({x = x , y = y + 1, z = z + 1}).name == "air" + and get_node({x = x + 1, y = y , z = z }).name == "air" + and get_node({x = x + 1, y = y , z = z + 1}).name == "air" + and get_node({x = x + 1, y = y + 1, z = z }).name == "air" + and get_node({x = x + 1, y = y + 1, z = z + 1}).name == "air" + + local player_data = { + pos = pos, + velocity = player_object:get_velocity(), + air = air + } + + if joined_players[name] then + local window_offset = (joined_players[name].window_offset + 1) % window_size + joined_players[name].window_offset = window_offset + joined_players[name][window_offset] = player_data + else + joined_players[name] = { + window_offset = 0, + [0] = player_data, + } + end +end + +local function check_player(name) + if is_creative_enabled(name) then return end + local data = joined_players[name] + if not data then return end + if not data[0] then return end + + local always_air = true + local falling = data[0].velocity.y < 0 + for i = 0, window_size - 1 do + local derivative = data[i] + local not_enough_data = not derivative + if not_enough_data then + return + end + always_air = always_air and derivative.air + falling = falling or derivative.velocity.y < 0 + end + if always_air and not falling then + -- fly detected + if not data.flights then + data.flights = 1 + else + data.flights = data.flights + 1 + if data.flights >= flights_kick_threshold then + kick_player(name, "flights") + end + end + local obj_player = minetest.get_player_by_name(name) + if not obj_player then + kick_player(name, "flights") + end + local velocity = obj_player:get_velocity() + local pos = obj_player:get_pos() + local x, y, z = floor(pos.x), floor(pos.y), floor(pos.z) + while ( get_node({x = x , y = y, z = z }).name == "air" + and get_node({x = x , y = y, z = z + 1}).name == "air" + and get_node({x = x + 1, y = y, z = z }).name == "air" + and get_node({x = x + 1, y = y, z = z + 1}).name == "air" + ) do + y = y - 1 + end + obj_player:set_velocity({x = velocity.x, y = -10, z = velocity.z}) + obj_player:set_pos({x = x, y = y + 0.5, z = z}) + end +end + +local function remove_player(player_object) + if not player_object then return end + local name = player_object:get_player_name() + if not name then return end + minetest.after(step_seconds, function() + joined_players[name] = nil + end) +end + +local function step() + for _, player in pairs(get_connected_players()) do + update_player(player) + check_player(player:get_player_name()) + end + after(step_seconds, step) +end + +minetest.register_on_joinplayer(update_player) + +minetest.register_on_leaveplayer(remove_player) + +after(step_seconds, step) diff --git a/mods/PLAYER/mcl_anticheat/mod.conf b/mods/PLAYER/mcl_anticheat/mod.conf new file mode 100644 index 000000000..612b2f505 --- /dev/null +++ b/mods/PLAYER/mcl_anticheat/mod.conf @@ -0,0 +1,3 @@ +name = mcl_anticheat +author = kay27 +depends = mcl_playerplus From d547e435bb7306bcc651cae35d978196e177cc9b Mon Sep 17 00:00:00 2001 From: kay27 Date: Thu, 16 Dec 2021 07:09:56 +0400 Subject: [PATCH 2/5] Try to disable suffocating bombs (Hi again Mr_Anderson) --- mods/PLAYER/mcl_anticheat/init.lua | 59 ++++++++++++++++++++++++++---- 1 file changed, 52 insertions(+), 7 deletions(-) diff --git a/mods/PLAYER/mcl_anticheat/init.lua b/mods/PLAYER/mcl_anticheat/init.lua index ec135f3bc..8c5189023 100644 --- a/mods/PLAYER/mcl_anticheat/init.lua +++ b/mods/PLAYER/mcl_anticheat/init.lua @@ -1,16 +1,20 @@ local flights_kick_threshold = 10 +local suffocations_threshold = 1 -local after = minetest.after -local get_connected_players = minetest.get_connected_players -local get_node = minetest.get_node -local get_player_by_name = minetest.get_player_by_name -local is_creative_enabled = minetest.is_creative_enabled -local kick_player = minetest.kick_player -local pos_to_string = minetest.pos_to_string +local after = minetest.after +local get_connected_players = minetest.get_connected_players +local get_node = minetest.get_node +local get_objects_inside_radius = minetest.get_objects_inside_radius +local get_player_by_name = minetest.get_player_by_name +local is_creative_enabled = minetest.is_creative_enabled +local kick_player = minetest.kick_player +local set_node = minetest.set_node local ceil = math.ceil local floor = math.floor +local distance = vector.distance + local window_size = 10 local detection_interval = 1.7 local step_seconds = detection_interval / window_size @@ -121,6 +125,47 @@ local function step() after(step_seconds, step) end +minetest.register_on_placenode(function(pos, newnode, placer, oldnode, itemstack, pointed_thing) + if not oldnode then return end + if not placer then return end + if oldnode.name ~= "air" then return end + if not placer:is_player() then return end + local placer_pos = placer:get_pos() + local placer_distance = distance(pos, placer_pos) + if placer_distance < 13 then return end + local is_choker = false + for _, object in pairs(get_objects_inside_radius(pos, 2)) do + if object and object:is_player() then + local player_head_pos = object:get_pos() + player_head_pos.y = player_head_pos.y + 1.5 + local player_head_distance = distance(pos, player_head_pos) + if player_head_distance < 0.7 then + after(0.05, function() + set_node(pos, oldnode) + end) + is_choker = true + break + end + end + end + if not is_choker then return end + -- cheater choked the player from distance greater than 12: + local name = placer:get_player_name() + local data = joined_players[name] + if not data then + joined_players[name].suffocations = 1 + else + if not data.suffocations then + data.suffocations = 1 + else + data.suffocations = data.suffocations + 1 + if data.suffocations >= suffocations_threshold then + kick_player(name, "choker") + end + end + end +end) + minetest.register_on_joinplayer(update_player) minetest.register_on_leaveplayer(remove_player) From 9d77c22c4f9cc8c5ab23b71e9d6971d8768ebd69 Mon Sep 17 00:00:00 2001 From: kay27 Date: Fri, 17 Dec 2021 03:04:36 +0400 Subject: [PATCH 3/5] Fix light --- mods/ENVIRONMENT/mcl_ambient_light/init.lua | 14 ++++++++------ mods/ENVIRONMENT/mcl_ambient_light/mod.conf | 2 +- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/mods/ENVIRONMENT/mcl_ambient_light/init.lua b/mods/ENVIRONMENT/mcl_ambient_light/init.lua index 11a11d4bc..1b030d1b9 100644 --- a/mods/ENVIRONMENT/mcl_ambient_light/init.lua +++ b/mods/ENVIRONMENT/mcl_ambient_light/init.lua @@ -1,11 +1,13 @@ minetest.register_on_mods_loaded(function () local light_min = 1 - for i, def in pairs(minetest.registered_nodes) do - local light_source = def.light_source - if light_source == nil or light_source < light_min then - minetest.override_item(i, { light_source = light_min }) - elseif light_source == light_min then - minetest.override_item(i, { light_source = light_min + 1 }) + for name, def in pairs(minetest.registered_nodes) do + if name ~= "air" then + local light_source = def.light_source + if light_source == nil or light_source < light_min then + minetest.override_item(name, { light_source = light_min }) + elseif light_source == light_min then + minetest.override_item(name, { light_source = light_min + 1 }) + end end end end) diff --git a/mods/ENVIRONMENT/mcl_ambient_light/mod.conf b/mods/ENVIRONMENT/mcl_ambient_light/mod.conf index f5e77b563..bccea6a89 100644 --- a/mods/ENVIRONMENT/mcl_ambient_light/mod.conf +++ b/mods/ENVIRONMENT/mcl_ambient_light/mod.conf @@ -1,3 +1,3 @@ name = ambient_light -author = MikeRedwood +author = MikeRedwood, kay27 description = Makes all nodes lit to a small degree! From 21f305e33ab8eb71646e0b07954cec305a9b7807 Mon Sep 17 00:00:00 2001 From: kay27 Date: Fri, 17 Dec 2021 03:19:24 +0400 Subject: [PATCH 4/5] Add minor anticheat fixes --- mods/PLAYER/mcl_anticheat/init.lua | 347 +++++++++++++++-------------- 1 file changed, 174 insertions(+), 173 deletions(-) diff --git a/mods/PLAYER/mcl_anticheat/init.lua b/mods/PLAYER/mcl_anticheat/init.lua index 8c5189023..a37c9b5b8 100644 --- a/mods/PLAYER/mcl_anticheat/init.lua +++ b/mods/PLAYER/mcl_anticheat/init.lua @@ -1,173 +1,174 @@ -local flights_kick_threshold = 10 -local suffocations_threshold = 1 - -local after = minetest.after -local get_connected_players = minetest.get_connected_players -local get_node = minetest.get_node -local get_objects_inside_radius = minetest.get_objects_inside_radius -local get_player_by_name = minetest.get_player_by_name -local is_creative_enabled = minetest.is_creative_enabled -local kick_player = minetest.kick_player -local set_node = minetest.set_node - -local ceil = math.ceil -local floor = math.floor - -local distance = vector.distance - -local window_size = 10 -local detection_interval = 1.7 -local step_seconds = detection_interval / window_size -local joined_players = {} - -local function update_player(player_object) - if not player_object then return end - local name = player_object:get_player_name() - if not name then return end - - local pos = player_object:get_pos() - local x, y, z = floor(pos.x), floor(pos.y-0.1), floor(pos.z) - - if mcl_playerplus.elytra then - local elytra = mcl_playerplus.elytra[player_object] - if elytra and elytra.active then - return - end - end - - local air = get_node({x = x , y = y , z = z }).name == "air" - and get_node({x = x , y = y , z = z + 1}).name == "air" - and get_node({x = x , y = y + 1, z = z }).name == "air" - and get_node({x = x , y = y + 1, z = z + 1}).name == "air" - and get_node({x = x + 1, y = y , z = z }).name == "air" - and get_node({x = x + 1, y = y , z = z + 1}).name == "air" - and get_node({x = x + 1, y = y + 1, z = z }).name == "air" - and get_node({x = x + 1, y = y + 1, z = z + 1}).name == "air" - - local player_data = { - pos = pos, - velocity = player_object:get_velocity(), - air = air - } - - if joined_players[name] then - local window_offset = (joined_players[name].window_offset + 1) % window_size - joined_players[name].window_offset = window_offset - joined_players[name][window_offset] = player_data - else - joined_players[name] = { - window_offset = 0, - [0] = player_data, - } - end -end - -local function check_player(name) - if is_creative_enabled(name) then return end - local data = joined_players[name] - if not data then return end - if not data[0] then return end - - local always_air = true - local falling = data[0].velocity.y < 0 - for i = 0, window_size - 1 do - local derivative = data[i] - local not_enough_data = not derivative - if not_enough_data then - return - end - always_air = always_air and derivative.air - falling = falling or derivative.velocity.y < 0 - end - if always_air and not falling then - -- fly detected - if not data.flights then - data.flights = 1 - else - data.flights = data.flights + 1 - if data.flights >= flights_kick_threshold then - kick_player(name, "flights") - end - end - local obj_player = minetest.get_player_by_name(name) - if not obj_player then - kick_player(name, "flights") - end - local velocity = obj_player:get_velocity() - local pos = obj_player:get_pos() - local x, y, z = floor(pos.x), floor(pos.y), floor(pos.z) - while ( get_node({x = x , y = y, z = z }).name == "air" - and get_node({x = x , y = y, z = z + 1}).name == "air" - and get_node({x = x + 1, y = y, z = z }).name == "air" - and get_node({x = x + 1, y = y, z = z + 1}).name == "air" - ) do - y = y - 1 - end - obj_player:set_velocity({x = velocity.x, y = -10, z = velocity.z}) - obj_player:set_pos({x = x, y = y + 0.5, z = z}) - end -end - -local function remove_player(player_object) - if not player_object then return end - local name = player_object:get_player_name() - if not name then return end - minetest.after(step_seconds, function() - joined_players[name] = nil - end) -end - -local function step() - for _, player in pairs(get_connected_players()) do - update_player(player) - check_player(player:get_player_name()) - end - after(step_seconds, step) -end - -minetest.register_on_placenode(function(pos, newnode, placer, oldnode, itemstack, pointed_thing) - if not oldnode then return end - if not placer then return end - if oldnode.name ~= "air" then return end - if not placer:is_player() then return end - local placer_pos = placer:get_pos() - local placer_distance = distance(pos, placer_pos) - if placer_distance < 13 then return end - local is_choker = false - for _, object in pairs(get_objects_inside_radius(pos, 2)) do - if object and object:is_player() then - local player_head_pos = object:get_pos() - player_head_pos.y = player_head_pos.y + 1.5 - local player_head_distance = distance(pos, player_head_pos) - if player_head_distance < 0.7 then - after(0.05, function() - set_node(pos, oldnode) - end) - is_choker = true - break - end - end - end - if not is_choker then return end - -- cheater choked the player from distance greater than 12: - local name = placer:get_player_name() - local data = joined_players[name] - if not data then - joined_players[name].suffocations = 1 - else - if not data.suffocations then - data.suffocations = 1 - else - data.suffocations = data.suffocations + 1 - if data.suffocations >= suffocations_threshold then - kick_player(name, "choker") - end - end - end -end) - -minetest.register_on_joinplayer(update_player) - -minetest.register_on_leaveplayer(remove_player) - -after(step_seconds, step) +local flights_kick_threshold = 10 +local suffocations_kick_threshold = 1 + +local after = minetest.after +local get_connected_players = minetest.get_connected_players +local get_node = minetest.get_node +local get_objects_inside_radius = minetest.get_objects_inside_radius +local get_player_by_name = minetest.get_player_by_name +local is_creative_enabled = minetest.is_creative_enabled +local kick_player = minetest.kick_player +local set_node = minetest.set_node + +local ceil = math.ceil +local floor = math.floor + +local distance = vector.distance + +local window_size = 10 +local detection_interval = 1.7 +local step_seconds = detection_interval / window_size +local joined_players = {} + +local function update_player(player_object) + if not player_object then return end + local name = player_object:get_player_name() + if not name then return end + + local pos = player_object:get_pos() + local x, y, z = floor(pos.x), floor(pos.y-0.1), floor(pos.z) + + if mcl_playerplus.elytra then + local elytra = mcl_playerplus.elytra[player_object] + if elytra and elytra.active then + return + end + end + + local air = get_node({x = x , y = y , z = z }).name == "air" + and get_node({x = x , y = y , z = z + 1}).name == "air" + and get_node({x = x , y = y + 1, z = z }).name == "air" + and get_node({x = x , y = y + 1, z = z + 1}).name == "air" + and get_node({x = x + 1, y = y , z = z }).name == "air" + and get_node({x = x + 1, y = y , z = z + 1}).name == "air" + and get_node({x = x + 1, y = y + 1, z = z }).name == "air" + and get_node({x = x + 1, y = y + 1, z = z + 1}).name == "air" + + local player_data = { + pos = pos, + velocity = player_object:get_velocity(), + air = air + } + + if joined_players[name] then + local window_offset = (joined_players[name].window_offset + 1) % window_size + joined_players[name].window_offset = window_offset + joined_players[name][window_offset] = player_data + else + joined_players[name] = { + window_offset = 0, + [0] = player_data, + } + end +end + +local function check_player(name) + if is_creative_enabled(name) then return end + local data = joined_players[name] + if not data then return end + if not data[0] then return end + + local always_air = true + local falling = data[0].velocity.y < 0 + for i = 0, window_size - 1 do + local derivative = data[i] + local not_enough_data = not derivative + if not_enough_data then + return + end + always_air = always_air and derivative.air + falling = falling or derivative.velocity.y < 0 + end + if always_air and not falling then + -- fly detected + if not data.flights then + data.flights = 1 + else + data.flights = data.flights + 1 + if data.flights >= flights_kick_threshold then + kick_player(name, "flights") + end + end + local obj_player = minetest.get_player_by_name(name) + if not obj_player then + kick_player(name, "flights") + end + local velocity = obj_player:get_velocity() + local pos = obj_player:get_pos() + local x, y, z = floor(pos.x), floor(pos.y), floor(pos.z) + while ( get_node({x = x , y = y, z = z }).name == "air" + and get_node({x = x , y = y, z = z + 1}).name == "air" + and get_node({x = x + 1, y = y, z = z }).name == "air" + and get_node({x = x + 1, y = y, z = z + 1}).name == "air" + ) do + y = y - 1 + end + obj_player:set_velocity({x = velocity.x, y = -10, z = velocity.z}) + obj_player:set_pos({x = x, y = y + 0.5, z = z}) + end +end + +local function remove_player(player_object) + if not player_object then return end + local name = player_object:get_player_name() + if not name then return end + minetest.after(step_seconds, function() + joined_players[name] = nil + end) +end + +local function step() + for _, player in pairs(get_connected_players()) do + update_player(player) + check_player(player:get_player_name()) + end + after(step_seconds, step) +end + +minetest.register_on_placenode(function(pos, newnode, placer, oldnode, itemstack, pointed_thing) + if not oldnode then return end + if not placer then return end + if oldnode.name ~= "air" then return end + if not placer:is_player() then return end + local placer_pos = placer:get_pos() + local placer_distance = distance(pos, placer_pos) + if placer_distance < 13 then return end + local is_choker = false + for _, object in pairs(get_objects_inside_radius(pos, 2)) do + if object and object:is_player() then + local player_head_pos = object:get_pos() + player_head_pos.y = player_head_pos.y + 1.5 + local player_head_distance = distance(pos, player_head_pos) + if player_head_distance < 0.7 then + after(0.05, function(node) + set_node(pos, node) + end, oldnode) + is_choker = true + break + end + end + end + if not is_choker then return end + -- cheater choked the player from distance greater than 12: + local name = placer:get_player_name() + local data = joined_players[name] + if not data then + joined_players[name].suffocations = 1 + data = joined_players[name] + else + if not data.suffocations then + data.suffocations = 1 + else + data.suffocations = data.suffocations + 1 + end + end + if data.suffocations >= suffocations_kick_threshold then + kick_player(name, "choker") + end +end) + +minetest.register_on_joinplayer(update_player) + +minetest.register_on_leaveplayer(remove_player) + +after(step_seconds, step) From 315ed0e72cf6cfa9d1ba0d4ae1af36e6a792c748 Mon Sep 17 00:00:00 2001 From: kay27 Date: Fri, 17 Dec 2021 03:33:36 +0400 Subject: [PATCH 5/5] Add dummy trident texture --- .../mcl_tridents/textures/mcl_trident.png | Bin 0 -> 249 bytes .../mcl_tridents/textures/mcl_trident_inv.png | Bin 299 -> 5945 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 mods/ITEMS/mcl_tridents/textures/mcl_trident.png diff --git a/mods/ITEMS/mcl_tridents/textures/mcl_trident.png b/mods/ITEMS/mcl_tridents/textures/mcl_trident.png new file mode 100644 index 0000000000000000000000000000000000000000..1cc256d003767698b14bababe6cfd1075d51112b GIT binary patch literal 249 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)3iZ{~7kS-Y(n(6yYrJ zh%9Dc&{GCs#)_r(WkA81o-U3d7N?UF4lvKNtNif)chOogXTxM}BfdHC=B| o*S8lmY3OWpGT_-`z;lR!;jopTwNkaW0??5Rp00i_>zopr04XC^YybcN literal 0 HcmV?d00001 diff --git a/mods/ITEMS/mcl_tridents/textures/mcl_trident_inv.png b/mods/ITEMS/mcl_tridents/textures/mcl_trident_inv.png index 5b4d4290ac0b1f5b09d2b828ddecbadeeebad46c..6b8c85db232c45f09ffe0e1e655acce6410f07b2 100644 GIT binary patch literal 5945 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 delta 273 zcmV+s0q*{}E~^5NBYyz$Nkl=`4f^ggIxg9{QAphhV$po5ezVF8IO@6#1ouc357PUTnhk} X){YUuva25e015yANkvXXu0mjfyJ&p4