From 944f5958415a7f14218e423a7467dc264d3b31a2 Mon Sep 17 00:00:00 2001 From: ErrorNoInternet Date: Thu, 6 Mar 2025 21:48:22 -0500 Subject: [PATCH] feat(lib): update examples --- errornowatcher.lua | 4 ++ lib/automation.lua | 132 ++++++++++++++++++++++++++++++++++ lib/enum.lua | 11 +++ lib/events.lua | 108 +++++++++++++++++++++------- lib/inventory.lua | 55 +++++++++++++- lib/lib.lua | 72 +++++++++++++++++++ lib/movement.lua | 11 +-- lib/utils.lua | 174 +++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 531 insertions(+), 36 deletions(-) create mode 100644 lib/automation.lua create mode 100644 lib/lib.lua diff --git a/errornowatcher.lua b/errornowatcher.lua index 2b78133..0a8de8a 100644 --- a/errornowatcher.lua +++ b/errornowatcher.lua @@ -3,9 +3,11 @@ Username = "ErrorNoWatcher" Owners = { "ErrorNoInternet" } for _, module in ipairs({ + "automation", "enum", "events", "inventory", + "lib", "movement", "utils", }) do @@ -13,3 +15,5 @@ for _, module in ipairs({ package.loaded[module] = nil require(module) end + +update_listeners() diff --git a/lib/automation.lua b/lib/automation.lua new file mode 100644 index 0000000..b473eae --- /dev/null +++ b/lib/automation.lua @@ -0,0 +1,132 @@ +FishingBobber = nil +FishingTicks = 0 +FishLastCaught = 0 +LastEaten = 0 + +function auto_fish() + stop_auto_fish() + FishingTicks = 0 + + function hold_fishing_rod() + if client.held_item.kind == "minecraft:fishing_rod" or hold_items({ "minecraft:fishing_rod" }) then + return true + end + warn("no fishing rod found!") + end + + if not hold_fishing_rod() then + return + end + + add_listener("add_entity", function(entity) + if entity.kind == "minecraft:fishing_bobber" and entity.data == client.id then + FishingBobber = entity + end + end, "auto-fish_watch-bobber") + + add_listener("remove_entities", function(entity_ids) + if table.contains(entity_ids, FishingBobber.id) then + if os.time() - LastEaten < 3 then + sleep(3000) + end + hold_fishing_rod() + client:use_item() + end + end, "auto-fish_watch-bobber") + + add_listener("level_particles", function(particle) + if particle.kind == 30 and particle.count == 6 then + local current_bobber = client:find_entities(function(e) + return e.id == FishingBobber.id + end)[1] + if distance(current_bobber.position, particle.position) <= 0.75 then + FishLastCaught = os.time() + client:use_item() + end + end + end, "auto-fish") + + add_listener("tick", function() + FishingTicks = FishingTicks + 1 + if FishingTicks % 600 ~= 0 then + return + end + + if os.time() - FishLastCaught >= 60 then + hold_fishing_rod() + client:use_item() + end + end, "auto-fish_watchdog") + + client:use_item() +end + +function stop_auto_fish() + remove_listeners("add_entity", "auto-fish_watch-bobber") + remove_listeners("remove_entities", "auto-fish_watch-bobber") + remove_listeners("level_particles", "auto-fish") + remove_listeners("tick", "auto-fish_watchdog") + + if FishingBobber and client:find_entities(function(e) + return e.id == FishingBobber.id + end)[1] then + FishingBobber = nil + client:use_item() + end +end + +function attack_entities(target_kind, minimum) + if not minimum then + minimum = 0 + end + + function hold_sword() + if client.held_item.kind == "minecraft:iron_sword" or hold_items({ "minecraft:iron_sword" }) then + return true + end + warn("no sword found!") + end + + while true do + local self_pos = client.position + local entities = client:find_entities(function(e) + return e.kind == target_kind and distance(e.position, self_pos) < 5 + end) + + if #entities > minimum then + local e = entities[1] + local pos = e.position + pos.y = pos.y + 1.5 + + hold_sword() + client:look_at(pos) + client:attack(e.id) + while client.has_attack_cooldown do + sleep(100) + end + else + sleep(1000) + end + end +end + +function check_food(hunger) + if hunger.food >= 20 then + return + end + + local current_time = os.time() + if current_time - LastEaten >= 3 then + LastEaten = current_time + + while not hold_items({ + "minecraft:golden_carrot", + "minecraft:cooked_beef", + "minecraft:bread", + }) do + sleep(1000) + LastEaten = current_time + end + client:use_item() + end +end diff --git a/lib/enum.lua b/lib/enum.lua index e83500e..05b2ec9 100644 --- a/lib/enum.lua +++ b/lib/enum.lua @@ -32,3 +32,14 @@ QUICK_CRAFT_START = 0 QUICK_CRAFT_ADD = 1 QUICK_CRAFT_END = 2 PICKUP_ALL = 11 + +POSE_NAMES = { + "standing", + "flying", + "sleeping", + "swimming", + "attacking", + "sneaking", + "jumping", + "dying", +} diff --git a/lib/events.lua b/lib/events.lua index 0bd27da..fa544ad 100644 --- a/lib/events.lua +++ b/lib/events.lua @@ -1,30 +1,90 @@ -local center = { x = 0, z = 0 } -local radius = 100 +Center = { x = 0, y = 64, z = 0 } +Radius = 100 +Whitelist = Owners +Ticks = -1 -function log_player_positions() - local entities = client:find_entities(function(e) - return e.kind == "minecraft:player" - and e.position.x > center.x - radius + 1 - and e.position.x < center.x + radius - and e.position.z > center.z - radius - and e.position.z < center.z + radius +function check_radius() + Ticks = Ticks + 1 + if Ticks % 20 ~= 0 then + return + end + + local self_id = client.id + local players = client:find_players(function(p) + return self_id ~= p.id + and p.position.x > Center.x - Radius + 1 + and p.position.x < Center.x + Radius + and p.position.z > Center.z - Radius + and p.position.z < Center.z + Radius end) - for _, e in ipairs(entities) do - client:chat(string.format("%s (%s) at %.1f %.1f %.1f", e.kind, e.id, e.position.x, e.position.y, e.position.z)) + + local tab_list = client.tab_list + for _, player in ipairs(players) do + local target + for _, tab_player in ipairs(tab_list) do + if tab_player.uuid == player.uuid and not table.contains(Whitelist, tab_player.name) then + target = tab_player + break + end + end + if not target then + goto continue + end + + client:chat( + string.format( + "%s is %s %d blocks away at %.2f %.2f %.2f facing %.2f %.2f", + target.name, + POSE_NAMES[player.pose + 1], + distance(Center, player.position), + player.position.x, + player.position.y, + player.position.z, + player.direction.x, + player.direction.y + ) + ) + + ::continue:: end end -add_listener("init", function() - info("client initialized, setting information...") - client:set_client_information({ view_distance = 16 }) -end) +function update_listeners() + for type, listeners in pairs(get_listeners()) do + for id, _ in pairs(listeners) do + remove_listeners(type, id) + end + end -add_listener("login", function() - info("player successfully logged in!") -end) - -add_listener("death", function() - warn(string.format("player died at %.1f %.1f %.1f!", client.position.x, client.position.y, client.position.z)) -end, "warn_player_died") - -add_listener("tick", log_player_positions) + for type, listeners in pairs({ + login = { + message = function() + info("bot successfully logged in!") + end, + eat = function() + sleep(5000) + check_food(client.hunger) + end, + }, + death = { + warn_bot_died = function() + warn( + string.format( + "bot died at %.2f %.2f %.2f facing %.2f %.2f!", + client.position.x, + client.position.y, + client.position.z, + client.direction.x, + client.direction.y + ) + ) + end, + }, + set_health = { auto_eat = check_food }, + tick = { log_player_positions = check_radius }, + }) do + for id, callback in pairs(listeners) do + add_listener(type, callback, id) + end + end +end diff --git a/lib/inventory.lua b/lib/inventory.lua index 98f5785..bfa6ff8 100644 --- a/lib/inventory.lua +++ b/lib/inventory.lua @@ -1,10 +1,42 @@ +function hold_items_in_hotbar(target_kinds, inventory) + if not inventory then + inventory = client:open_inventory() + end + for index, item in ipairs(inventory.contents) do + if index >= 37 and index <= 45 and table.contains(target_kinds, item.kind) then + inventory = nil + sleep(500) + client:set_held_slot(index - 37) + return true + end + end + return false +end + +function hold_items(target_kinds) + local inventory = client:open_inventory() + if hold_items_in_hotbar(target_kinds, inventory) then + return true + end + for index, item in ipairs(inventory.contents) do + if table.contains(target_kinds, item.kind) then + inventory:click({ source_slot = index - 1, target_slot = client.held_slot }, SWAP) + sleep(100) + inventory = nil + sleep(500) + return true + end + end + inventory = nil + sleep(500) + return false +end + function steal(item_name) for _, chest_pos in ipairs(client:find_blocks(client.position, get_block_states({ "chest" }))) do - client:chat(dump(chest_pos)) - client:go_to({ position = chest_pos, radius = 3 }, { type = RADIUS_GOAL }) while client.pathfinder.is_calculating or client.pathfinder.is_executing do - sleep(50) + sleep(500) end client:look_at(chest_pos) @@ -23,6 +55,23 @@ function steal(item_name) end end +function dump_inventory(hide_empty) + local inventory = client:open_inventory() + for index, item in ipairs(inventory.contents) do + if hide_empty and item.count == 0 then + goto continue + end + + local item_damage = "" + if item.damage then + item_damage = item.damage + end + info(string.format("%-2d = %2dx %-32s %s", index - 1, item.count, item.kind, item_damage)) + + ::continue:: + end +end + function drop_all_hotbar() local inventory = client:open_inventory() for i = 0, 9 do diff --git a/lib/lib.lua b/lib/lib.lua new file mode 100644 index 0000000..c99bc55 --- /dev/null +++ b/lib/lib.lua @@ -0,0 +1,72 @@ +function clock_gettime(clock) + local status, module = pcall(require, "posix") + posix = status and module or nil + + if posix then + local s, ns = posix.clock_gettime(clock) + return s + ns / (10 ^ (math.floor(math.log10(ns)) + 1)) + else + warn("failed to load posix module! falling back to os.time()") + return os.time() + end +end + +function distance(p1, p2) + return math.sqrt((p2.x - p1.x) ^ 2 + (p2.y - p1.y) ^ 2 + (p2.z - p1.z) ^ 2) +end + +function table.shallow_copy(t) + local t2 = {} + for k, v in pairs(t) do + t2[k] = v + end + return t2 +end + +function table.map(t, f) + local t2 = {} + for k, v in pairs(t) do + t2[k] = f(v) + end + return t2 +end + +function table.contains(t, target) + for _, v in pairs(t) do + if v == target then + return true + end + end + return false +end + +function dump(object) + if type(object) == "table" then + local string = "{ " + local parts = {} + for key, value in pairs(object) do + table.insert(parts, key .. " = " .. dump(value)) + end + string = string .. table.concat(parts, ", ") + return string .. " " .. "}" + else + return tostring(object) + end +end + +function dumpp(object, level) + if not level then + level = 0 + end + if type(object) == "table" then + local string = "{\n" .. string.rep(" ", level + 1) + local parts = {} + for key, value in pairs(object) do + table.insert(parts, key .. " = " .. dumpp(value, level + 1)) + end + string = string .. table.concat(parts, ",\n" .. string.rep(" ", level + 1)) + return string .. "\n" .. string.rep(" ", level) .. "}" + else + return tostring(object) + end +end diff --git a/lib/movement.lua b/lib/movement.lua index f04436c..1538d3d 100644 --- a/lib/movement.lua +++ b/lib/movement.lua @@ -3,16 +3,9 @@ function look_at_player(name) if player then player.position.y = player.position.y + 1 client:look_at(player.position) - else - client:chat(string.format("/w %s player not found!", sender)) end end -function go_to_player(name, opts) - local player = get_player(name) - if player then - client:go_to(player.position, opts) - else - client:chat(string.format("/w %s player not found!", sender)) - end +function go_to_player(name, go_to_opts) + client:go_to(get_player(name).position, go_to_opts) end diff --git a/lib/utils.lua b/lib/utils.lua index 26758d3..8be29df 100644 --- a/lib/utils.lua +++ b/lib/utils.lua @@ -1,3 +1,177 @@ +SpeedTracking = {} +TpsTracking = {} + +function entity_speed(uuid, seconds) + if not seconds then + seconds = 1 + end + + local callback = function() + local old_entity = SpeedTracking[uuid] + local new_entity = client:find_entities(function(e) + return e.uuid == uuid + end)[1] + + if not new_entity then + remove_listeners("tick", "speed-tracking_" .. uuid) + SpeedTracking[uuid] = -1 + return + end + + if old_entity then + old_entity._distance = old_entity._distance + distance(old_entity.position, new_entity.position) + old_entity.position = new_entity.position + + if old_entity._ticks < seconds * 20 then + old_entity._ticks = old_entity._ticks + 1 + else + remove_listeners("tick", "speed-tracking_" .. uuid) + SpeedTracking[uuid] = old_entity._distance / seconds + end + else + new_entity._ticks = 1 + new_entity._distance = 0 + SpeedTracking[uuid] = new_entity + end + end + add_listener("tick", callback, "speed-tracking_" .. uuid) + + repeat + sleep(seconds * 1000 / 10) + until type(SpeedTracking[uuid]) == "number" + + local speed = SpeedTracking[uuid] + SpeedTracking[uuid] = nil + return speed +end + +function tps(seconds) + if not seconds then + seconds = 1 + end + + add_listener("tick", function() + if not TpsTracking.ticks then + TpsTracking.ticks = 0 + TpsTracking.start = clock_gettime(0) + else + TpsTracking.ticks = TpsTracking.ticks + 1 + if TpsTracking.ticks >= seconds * 20 then + TpsTracking.stop = clock_gettime(0) + remove_listeners("tick", "tps_tracking") + end + end + end, "tps_tracking") + + sleep(seconds * 1000) + repeat + sleep(20) + until TpsTracking.stop + + local tps = seconds * 20 / (TpsTracking.stop - TpsTracking.start) + TpsTracking = {} + return tps +end + +function nether_travel(pos, go_to_opts) + info(string.format("going to %.2f %.2f %.2f through nether", pos.x, pos.y, pos.z)) + + local portal_block_states = get_block_states({ "nether_portal" }) + local nether_pos = table.shallow_copy(pos) + nether_pos.x = nether_pos.x / 8 + nether_pos.z = nether_pos.z / 8 + + if client.dimension == "minecraft:overworld" then + info("currently in overworld, finding nearest portal") + local portals = client:find_blocks(client.position, portal_block_states) + + info(string.format("going to %.2f %.2f %.2f through nether", portals[1].x, portals[1].y, portals[1].z)) + client:go_to(portals[1], go_to_opts) + while client.dimension ~= "minecraft:the_nether" do + sleep(1000) + end + sleep(3000) + end + + info(string.format("currently in nether, going to %.2f %.2f", nether_pos.x, nether_pos.z)) + client:go_to(nether_pos, { type = XZ_GOAL }) + while client.pathfinder.is_calculating or client.pathfinder.is_executing do + sleep(1000) + end + + info("arrived, looking for nearest portal") + local portals_nether = client:find_blocks(client.position, portal_block_states) + if not next(portals_nether) then + warn("failed to find portals in the nether") + return + end + + local found_portal = false + for _, portal in ipairs(portals_nether) do + if (client.position.y > 127) == (portal.y > 127) then + found_portal = true + + info(string.format("found valid portal, going to %.2f %.2f %.2f", portal.x, portal.y, portal.z)) + client:go_to(portal) + while client.dimension ~= "minecraft:overworld" do + sleep(1000) + end + sleep(3000) + end + + if found_portal then + break + end + end + if not found_portal then + warn("failed to find valid portals in the nether") + return + end + + info(string.format("back in overworld, going to %.2f %.2f %.2f", pos.x, pos.y, pos.z)) + client:go_to(pos, go_to_opts) +end + +function interact_bed() + local bed = client:find_blocks( + client.position, + get_block_states({ + "brown_bed", + "white_bed", + "yellow_bed", + }) + )[1] + if not bed then + return + end + + client:go_to(bed, { type = REACH_BLOCK_POS_GOAL, options = { without_mining = true } }) + while client.pathfinder.is_calculating or client.pathfinder.is_executing do + sleep(500) + end + + client:look_at(bed) + client:block_interact(bed) +end + +function closest_entity(target_kind) + local self_pos = client.position + local entities = client:find_entities(function(e) + return e.kind == target_kind + end) + + local closest_entity = entities[1] + local closest_distance = distance(closest_entity.position, self_pos) + for _, entity in ipairs(entities) do + local dist = distance(entity.position, self_pos) + if dist <= closest_distance then + closest_entity = entity + closest_distance = dist + end + end + return closest_entity +end + function get_player(name) local target_uuid = nil for _, player in ipairs(client.tab_list) do