diff --git a/FightSystem_Core/src/de/steamwar/fightsystem/FightSystem.java b/FightSystem_Core/src/de/steamwar/fightsystem/FightSystem.java index 5de02c4..ff3f99f 100644 --- a/FightSystem_Core/src/de/steamwar/fightsystem/FightSystem.java +++ b/FightSystem_Core/src/de/steamwar/fightsystem/FightSystem.java @@ -22,6 +22,7 @@ package de.steamwar.fightsystem; import com.comphenix.tinyprotocol.TinyProtocol; import de.steamwar.core.Core; import de.steamwar.fightsystem.ai.LixfelAI; +import de.steamwar.fightsystem.ai.chaos.ChaosAI; import de.steamwar.fightsystem.commands.*; import de.steamwar.fightsystem.countdown.*; import de.steamwar.fightsystem.event.HellsBells; @@ -42,6 +43,7 @@ import de.steamwar.fightsystem.utils.*; import de.steamwar.fightsystem.winconditions.*; import de.steamwar.message.Message; import de.steamwar.sql.SchematicNode; +import de.steamwar.sql.SteamwarUser; import org.bukkit.Bukkit; import org.bukkit.plugin.java.JavaPlugin; @@ -167,6 +169,13 @@ public class FightSystem extends JavaPlugin { }else if(Config.mode == ArenaMode.PREPARE) { Fight.getUnrotated().setSchem(SchematicNode.getSchematicNode(Config.PrepareSchemID)); } + + FightStatistics.unrank(); + + Bukkit.getScheduler().runTask(getPlugin(), () -> { + new ChaosAI(Fight.getBlueTeam()); + new LixfelAI(Fight.getRedTeam(), "Lixfel.AI"); + }); } @Override diff --git a/FightSystem_Core/src/de/steamwar/fightsystem/ai/AI.java b/FightSystem_Core/src/de/steamwar/fightsystem/ai/AI.java index 17bed39..fc76c0e 100644 --- a/FightSystem_Core/src/de/steamwar/fightsystem/ai/AI.java +++ b/FightSystem_Core/src/de/steamwar/fightsystem/ai/AI.java @@ -155,8 +155,10 @@ public abstract class AI { return; Location location = translate(pos, true); - if(interactionDistanceViolation(location)) + if(interactionDistanceViolation(location)) { + chat("Zu kleine Arme für TNT!"); return; + } Block block = location.getBlock(); if(block.getType() == Material.AIR) @@ -170,8 +172,10 @@ public abstract class AI { @Override public void run() { Location location = translate(pos, true); - if(interactionDistanceViolation(location)) + if(interactionDistanceViolation(location)) { + chat("Ich komme da nicht dran!"); return; + } interact(location.getBlock()); } @@ -205,7 +209,7 @@ public abstract class AI { public void run() { Location location = entity.getLocation(); Location target = translate(pos, false); - if(Math.abs(location.getX() - target.getX()) > 1 || Math.abs(location.getY() - target.getY()) > 1.2 || Math.abs(location.getZ() - target.getZ()) > 1) { + if(Math.abs(location.getX() - target.getX()) > 1.9 || Math.abs(location.getY() - target.getY()) > 1.9 || Math.abs(location.getZ() - target.getZ()) > 1.9) { FightSystem.getPlugin().getLogger().log(Level.INFO, () -> entity.getName() + ": Overdistance movement " + location.toVector() + " " + target.toVector()); return; } @@ -287,7 +291,7 @@ public abstract class AI { queue.poll().run(); } - private Location translate(Vector pos, boolean blockPos) { + public Location translate(Vector pos, boolean blockPos) { Region extend = team.getExtendRegion(); if(Fight.getUnrotated() == team) return new Location( diff --git a/FightSystem_Core/src/de/steamwar/fightsystem/ai/chaos/Bridge.java b/FightSystem_Core/src/de/steamwar/fightsystem/ai/chaos/Bridge.java new file mode 100644 index 0000000..076b93f --- /dev/null +++ b/FightSystem_Core/src/de/steamwar/fightsystem/ai/chaos/Bridge.java @@ -0,0 +1,31 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2023 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.fightsystem.ai.chaos; + +import org.bukkit.util.Vector; + +public class Bridge { + private Bridge() { + } + + public static final Vector BRIDGE_POS = new Vector(25, 14, 24); + public static final Vector BRIDGE_SHIELDS = new Vector(25, 15, 25); + public static final Vector BRIDGE_MG = new Vector(21, 15, 24); +} diff --git a/FightSystem_Core/src/de/steamwar/fightsystem/ai/chaos/Cannon.java b/FightSystem_Core/src/de/steamwar/fightsystem/ai/chaos/Cannon.java new file mode 100644 index 0000000..f1abd8a --- /dev/null +++ b/FightSystem_Core/src/de/steamwar/fightsystem/ai/chaos/Cannon.java @@ -0,0 +1,172 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2023 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.fightsystem.ai.chaos; + +import org.bukkit.util.Vector; + +public class Cannon { + public final Vector[] tnt; + public final Vector button; + public final Vector load; + public final Vector escape; + public final String name; + + private Cannon(String name, Vector[] tnt, Vector button, Vector load, Vector escape) { + this.tnt = tnt; + this.button = button; + this.load = load; + this.escape = escape; + this.name = name; + } + + public static final Vector[] DS_LEFT_TNT = new Vector[] { + new Vector(40, 24, 12), + new Vector(39, 24, 12), + new Vector(38, 23, 12), + new Vector(38, 24, 12), + new Vector(40, 23, 15), + new Vector(40, 23, 14), + new Vector(40, 24, 15), + new Vector(40, 24, 14), + new Vector(38, 23, 15), + new Vector(38, 23, 14), + new Vector(38, 24, 15), + new Vector(38, 24, 14), + new Vector(40, 23, 12), + }; + public static final Vector DS_LEFT_BUTTON = new Vector(39, 25, 11); + public static final Vector DS_LEFT_LOAD = new Vector(39, 25, 13); + public static final Vector DS_LEFT_ESCAPE = new Vector(19, 26, 13); + public static final Cannon DS_LEFT = new Cannon("Downstäb Links", DS_LEFT_TNT, DS_LEFT_BUTTON, DS_LEFT_LOAD, DS_LEFT_ESCAPE); + + public static final Vector[] DS_RIGHT_TNT = new Vector[] { + new Vector(12, 24, 12), + new Vector(11, 24, 12), + new Vector(10, 23, 12), + new Vector(10, 24, 12), + new Vector(10, 23, 15), + new Vector(10, 23, 14), + new Vector(10, 24, 15), + new Vector(10, 24, 14), + new Vector(12, 23, 14), + new Vector(12, 24, 14), + new Vector(12, 23, 15), + new Vector(12, 24, 15), + new Vector(12, 23, 12), + }; + public static final Vector DS_RIGHT_BUTTON = new Vector(11, 25, 11); + public static final Vector DS_RIGHT_LOAD = new Vector(11, 25, 13); + public static final Vector DS_RIGHT_ESCAPE = new Vector(31, 26, 13); + public static final Cannon DS_RIGHT = new Cannon("Downstäb Rechts", DS_RIGHT_TNT, DS_RIGHT_BUTTON, DS_RIGHT_LOAD, DS_RIGHT_ESCAPE); + + public static final Vector[] STATIC_LEFT_TNT = new Vector[] { + new Vector(37, 17, 15), + new Vector(37, 17, 14), + new Vector(37, 18, 16), + new Vector(37, 18, 15), + new Vector(37, 18, 14), + new Vector(36, 17, 16), + new Vector(36, 17, 15), + new Vector(36, 17, 14), + new Vector(36, 18, 16), + new Vector(36, 18, 15), + new Vector(36, 18, 14), + new Vector(37, 17, 16), + }; + public static final Vector STATIC_LEFT_BUTTON = new Vector(38, 18, 11); + public static final Vector STATIC_LEFT_LOAD = new Vector(37, 16, 12); + public static final Vector STATIC_LEFT_ESCAPE = new Vector(30, 13, 9); + public static final Cannon STATIC_LEFT = new Cannon("Lupf Links", STATIC_LEFT_TNT, STATIC_LEFT_BUTTON, STATIC_LEFT_LOAD, STATIC_LEFT_ESCAPE); + + public static final Vector[] STATIC_RIGHT_TNT = new Vector[] { + new Vector(14, 17, 15), + new Vector(14, 18, 16), + new Vector(14, 18, 15), + new Vector(14, 17, 14), + new Vector(14, 18, 14), + new Vector(13, 17, 16), + new Vector(13, 17, 15), + new Vector(13, 17, 14), + new Vector(13, 18, 16), + new Vector(13, 18, 15), + new Vector(13, 18, 14), + new Vector(14, 17, 16), + }; + public static final Vector STATIC_RIGHT_BUTTON = new Vector(12, 18, 11); + public static final Vector STATIC_RIGHT_LOAD = new Vector(13, 17, 12); + public static final Vector STATIC_RIGHT_ESCAPE = new Vector(20, 13, 9); + public static final Cannon STATIC_RIGHT = new Cannon("Lupf Rechts", STATIC_RIGHT_TNT, STATIC_RIGHT_BUTTON, STATIC_RIGHT_LOAD, STATIC_RIGHT_ESCAPE); + + public static final Vector[] AK_TNT = new Vector[] { + new Vector(10, 7, 17), + new Vector(10, 11, 17), + new Vector(10, 10, 17), + new Vector(10, 8, 17), + new Vector(10, 9, 17), + new Vector(10, 7, 15), + new Vector(11, 7, 15), + new Vector(12, 7, 15), + new Vector(10, 10, 15), + new Vector(11, 10, 15), + new Vector(12, 10, 15), + new Vector(12, 8, 15), + new Vector(12, 9, 15), + new Vector(11, 8, 15), + new Vector(11, 9, 15), + new Vector(10, 8, 15), + new Vector(10, 9, 15), + new Vector(10, 7, 19), + new Vector(11, 7, 19), + new Vector(12, 7, 19), + new Vector(12, 8, 19), + new Vector(11, 8, 19), + new Vector(10, 8, 19), + new Vector(10, 10, 19), + new Vector(11, 10, 19), + new Vector(12, 10, 19), + new Vector(12, 9, 19), + new Vector(11, 9, 19), + new Vector(10, 9, 19), + new Vector(10, 6, 17), + }; + public static final Vector AK_BUTTON = new Vector(9, 9, 16); + public static final Vector AK_LOAD = new Vector(11, 8, 17); + public static final Cannon AK = new Cannon("Arschkratzer", AK_TNT, AK_BUTTON, AK_LOAD, null); + + public static final Vector[] HA_RIGHT_TNT = new Vector[] { + new Vector(23, 6, 9), + new Vector(23, 7, 9), + new Vector(23, 8, 9), + new Vector(23, 5, 9), + }; + + public static final Vector[] HA_LEFT_TNT = new Vector[] { + new Vector(27, 6, 9), + new Vector(27, 7, 9), + new Vector(27, 8, 9), + new Vector(27, 5, 9), + }; + + public static final Vector HA_RIGHT_LOAD = new Vector(23, 8, 8); + public static final Vector HA_LEFT_LOAD = new Vector(27, 8, 8); + + public static final Cannon HA_LEFT = new Cannon("Halbautomatik Links", HA_LEFT_TNT, null, HA_LEFT_LOAD, null); + public static final Cannon HA_RIGHT = new Cannon("Halbautomatik Rechts", HA_RIGHT_TNT, null, HA_RIGHT_LOAD, null); +} diff --git a/FightSystem_Core/src/de/steamwar/fightsystem/ai/chaos/ChaosAI.java b/FightSystem_Core/src/de/steamwar/fightsystem/ai/chaos/ChaosAI.java new file mode 100644 index 0000000..cf251b6 --- /dev/null +++ b/FightSystem_Core/src/de/steamwar/fightsystem/ai/chaos/ChaosAI.java @@ -0,0 +1,168 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2023 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.fightsystem.ai.chaos; + +import de.steamwar.fightsystem.FightSystem; +import de.steamwar.fightsystem.ai.AI; +import de.steamwar.fightsystem.ai.LixfelPathplanner; +import de.steamwar.fightsystem.fight.FightTeam; +import de.steamwar.fightsystem.states.FightState; +import de.steamwar.sql.SchematicNode; +import de.steamwar.sql.SteamwarUser; +import org.bukkit.Bukkit; +import org.bukkit.util.Vector; + +import java.util.Random; + +public class ChaosAI extends AI { + private final LixfelPathplanner pathplanner = new LixfelPathplanner(chooseSchematic()); + private State state = State.PRE_PREPARE; + private static final Cannon[] cannons = new Cannon[] { + Cannon.DS_LEFT, + Cannon.DS_RIGHT, + Cannon.STATIC_LEFT, + Cannon.STATIC_RIGHT, + Cannon.HA_LEFT, + Cannon.HA_RIGHT, + }; + private Cannon currentCannon = randomCannon(); + private boolean igniteMg = false; + private static final Random random = new Random(); + + + public ChaosAI(FightTeam team) { + this(team, SteamwarUser.get(14533)); + } + + + public ChaosAI(FightTeam team, SteamwarUser user) { + super(team, user); + } + + @Override + public SchematicNode chooseSchematic() { + return SchematicNode.getSchematicNode(111476); + } + + @Override + protected void plan() { + switch (state) { + case PRE_PREPARE: + Bukkit.getScheduler().runTaskLater(FightSystem.getPlugin(), () -> { + state = State.PREPARE; + }, 20 * 15); + state = State.PREPARING_PREPARE; + break; + case PREPARING_PREPARE: + break; + case PREPARE: + prepare(); + state = State.PREPARE_READY; + break; + case PREPARE_READY: + setReady(); + state = State.WAIT_TILL_START; + break; + case WAIT_TILL_START: + if (FightState.ingame()) { + state = State.IGNITE_MG; + startFight(); + } + break; + case IGNITE_MG: + if(igniteMg) { + interact(Bridge.BRIDGE_MG); + chat("Die MG ist angezündet!"); + state = State.FIGHT; + currentCannon = randomCannon(); + } + break; + case FIGHT: + fireCannon(currentCannon); + break; + case ESCAPE: + if (currentCannon.escape != null) { + pathplanner.planToAnywhere(getPosition(), currentCannon.escape).forEach(this::move); + } + + Cannon nextCannon = randomCannon(); + while (nextCannon == currentCannon) { + nextCannon = randomCannon(); + } + + currentCannon = nextCannon; + + state = State.FIGHT; + break; + } + } + + public void fireCannon(Cannon cannon) { + chat("Ich feuere " + cannon.name + " an!"); + pathplanner.planToAnywhere(getPosition(), cannon.load).forEach(this::move); + if (cannon.button == null) { + for (int i = 0; i < 20; i++) { + for (Vector vector : cannon.tnt) { + setTNT(vector); + } + for (int j = 0; j < 30; j++) { + getBlock(Bridge.BRIDGE_MG); + } + } + } else { + for (Vector vector : cannon.tnt) { + setTNT(vector); + } + interact(cannon.button); + } + + state = State.ESCAPE; + } + + private Cannon randomCannon() { + return cannons[random.nextInt(cannons.length)]; + } + + public void startFight() { + chat("gl&hf"); + chat("Ich zünde die MG an!"); + Bukkit.getScheduler().runTaskLater(FightSystem.getPlugin(), () -> { + igniteMg = true; + }, 20 * 21); + } + + public void prepare() { + chat("Prepare your fkin 4nus"); + pathplanner.planToAnywhere(getPosition(), Bridge.BRIDGE_POS).forEach(this::move); + interact(Bridge.BRIDGE_SHIELDS); + } + + private enum State { + PRE_PREPARE, + PREPARING_PREPARE, + PREPARE, + PREPARE_READY, + WAIT_TILL_START, + IGNITE_MG, + FIGHT, + ESCAPE, + + } +} diff --git a/FightSystem_Core/src/de/steamwar/fightsystem/ai/chaos/recorder.lua b/FightSystem_Core/src/de/steamwar/fightsystem/ai/chaos/recorder.lua new file mode 100644 index 0000000..e821361 --- /dev/null +++ b/FightSystem_Core/src/de/steamwar/fightsystem/ai/chaos/recorder.lua @@ -0,0 +1,25 @@ +event(events.PlaceBlock, function(e) + if storage.player.get("recording") then + pos = {math.floor(e.x), math.floor(e.y) - 100, math.floor(e.z)} + storage.player.get("recordingPos")[storage.player.get("recordingCount")] = pos + storage.player.set("recordingCount", storage.player.get("recordingCount") + 1) + end +end) + +command("recorder", function(args) + if storage.player.get("recording") then + storage.player.set("recording", false) + storage.player.set("recordingCount", 0) + print("private static final Vector[] positions = new Vector[] {") + for k, v in pairs(storage.player.get("recordingPos")) do + print("new Vector(" .. v[1] .. ", " .. v[2] .. ", " .. v[3] .. "),") + end + print("};") + print("Stopped recording") + else + storage.player.set("recording", true) + storage.player.set("recordingCount", 0) + storage.player.set("recordingPos", {}) + print("Started recording") + end +end) \ No newline at end of file diff --git a/FightSystem_Core/src/de/steamwar/fightsystem/ai/chaos/sw.def.lua b/FightSystem_Core/src/de/steamwar/fightsystem/ai/chaos/sw.def.lua new file mode 100644 index 0000000..d1570fd --- /dev/null +++ b/FightSystem_Core/src/de/steamwar/fightsystem/ai/chaos/sw.def.lua @@ -0,0 +1,343 @@ +-- This file is a part of the SteamWar software. +-- +-- Copyright (C) 2021 SteamWar.de-Serverteam +-- +-- This program is free software: you can redistribute it and/or modify +-- it under the terms of the GNU Affero General Public License as published by +-- the Free Software Foundation, either version 3 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU Affero General Public License for more details. +-- +-- You should have received a copy of the GNU Affero General Public License +-- along with this program. If not, see . + +--- +--- This file contains the definitions for the SteamWar.de script API. +--- It is used by the IDE to provide code completion and type checking. +--- Created by Chaoscaot +--- + +inventory = {} + +---@param title string +---@param size number +---@return Inventory +function inventory.create(title, size) return nil end + +---@alias InventoryClick 'LEFT' | 'SHIFT_LEFT' | 'RIGHT' | 'SHIFT_RIGHT' | 'MIDDLE' | 'NUMBER_KEY' + +---@class Inventory +local Inventory = {} + +---@overload fun(slot: number, material: string, name: string, handler: fun(click: InventoryClick)): void +---@overload fun(slot: number, material: string, name: string, handler: fun(click: InventoryClick), lore: string[]): void +---@overload fun(slot: number, material: string, name: string, handler: fun(click: InventoryClick), lore: string[], enchanted: boolean): void +---@param slot number +---@param material string +---@param name string +---@param handler fun(click: InventoryClick): void +---@param lore string[] +---@param enchanted boolean +---@param amount number +---@return void +function Inventory.item(slot, material, name, handler, lore, enchanted, amount) end + +---@param handler fun(): void +---@return void +function Inventory.setCloseHandler(handler) end + +---@return void +function Inventory.open() end + +player = {} + +---@return string +---Get the name of the player. +function player.name() return "" end + +---@return void +function player.chat(...) end + +---@return void +---Send a message to the actionbar of the player. +function player.actionbar(...) end + +---@overload fun(): number +---@param newX number +function player.x(newX) end + +---@overload fun(): number +---@param newY number +function player.y(newY) end + +---@overload fun(): number +---@param newZ number +function player.z(newZ) end + +---@overload fun(): number +---@param newYaw number +function player.yaw(newYaw) end + +---@overload fun(): number +---@param newPitch number +function player.pitch(newPitch) end + +---@return boolean +function player.sneaking() return nil end + +---@return boolean +function player.sprinting() return nil end + +---@overload fun(): number +---@param newSlot number +function player.slot(newSlot) end + +---@return string +function player.item() return nil end + +---@return string +function player.offHandItem() return nil end + +---@return void +function player.closeInventory() end + +---@field nextBool fun(): boolean +random = {} + +---@overload fun(): number +---@overload fun(bound: number): number +---@param origin number +---@param bound number +---@return number +function random.nextInt(origin, bound) return nil end + +---@overload fun(): number +---@overload fun(bound: number): number +---@param origin number +---@param bound number +---@return number +function random.nextDouble(origin, bound) return nil end + +---@return boolean +function random.nextBool() return nil end + +---@alias RegionType 'wg' | 'mwg' | 'as' | 'ws' | 'ws_inner' | 'ws_rumpf' | 'ws_rahmen' | 'spawn' + +---@class iregion +---@field tnt tnt +---@field trace trace +local iregion = {} + +---@class region: iregion +region = {} + +---@return string +function iregion.name() return nil end + +---@return RegionType +function iregion.type() return nil end + +---@return boolean +function iregion.fire() return nil end + +---@return boolean +function iregion.freeze() return nil end + +---@return boolean +function iregion.protect() return nil end + +---@return string +function iregion.loader() return nil end + +---@alias TNTMode 'ALLOW' | 'DENY' | 'ONLY_TB' + +---@class tnt +local tnt = {} + +---@return TNTMode +function tnt.mode() return nil end + +---@return boolean +function tnt.enabled() return nil end + +---@return boolean +function tnt.onlyTb() return nil end + +---@class trace +local trace = {} + +---@return boolean +function trace.active() return nil end + +---@return boolean +function trace.auto() return nil end + +---@return string +function trace.status() return nil end + +---@return number +function trace.time() return nil end + +---@param name string +---@return iregion +function region.get(name) return nil end + +---@return iregion[] +function region.list() return nil end + +---@class Position +---@field x number +---@field y number +---@field z number + +---@class server +---@field tps tps +server = {} + +---@return string +function server.time() return nil end + +---@return number +function server.ticks() return nil end + +---@param position Position +---@return string +function getBlockAt(position) return nil end + +---@param position Position +---@param material string +---@return void +function setBlockAt(position, material) return nil end + +---@class tps +local tps = {} + +---@return number +function tps.oneSecond() return nil end + +---@return number +function tps.tenSecond() return nil end + +---@return number +function tps.oneMinute() return nil end + +---@return number +function tps.fiveMinute() return nil end + +---@return number +function tps.tenMinute() return nil end + +---@return number +function tps.current() return nil end + +---@return number +function tps.limit() return nil end + +---@class storage +---@field global storageLib +---@field player storageLib +---@field region storageLib +storage = {} + +---@class storageLib +local storageLib = {} + +---@param key string +---@return any +function storageLib.get(key) return nil end + +---@param key string +---@param value any +---@return void +function storageLib.set(key, value) end + +---@param key string +---@return boolean +function storageLib.has(key) return nil end + +---@param key string +---@return void +function storageLib.remove(key) end + +---@param key string +---@return Accessor +function storageLib.accessor(key) return nil end + +---@class Accessor +---@overload fun(): any +---@overload fun(value: any) + +---@class Selection +---@field max Position +---@field min Position + +---@class _worldedit +_worldedit = {} + +---@overload fun(pos: Position[]): void +---@return Selection +function _worldedit.selection() return nil end + +---@param msg string +---@param callback fun(value: string): void +---@return void +function input(msg, callback) end + +---@param ticks number +---@param callback fun(): void +---@return void +function delayed(ticks, callback) end + +---@param x number +---@param y number +---@param z number +---@return Position +function pos(x, y, z) return nil end + +---@return void +function exec(...) end + +---@param obj any +---@return number +function length(obj) return 0 end + +---@param separator string +---@param table any[] +---@return string +function join(separator, table) return "" end + +---@class EventType +---@class events +---@field DoubleSwap EventType +---@field PlaceBlock EventType +---@field BreakBlock EventType +---@field RightClick EventType +---@field LeftClick EventType +---@field TNTSpawn EventType +---@field TNTExplode EventType +---@field TNTExplodeInBuild EventType +---@field SelfJoin EventType +---@field SelfLeave EventType +---@field DropItem EventType +---@field EntityDeath EventType +events = {} + + +---@param id EventType +---@param handler fun(params: any): void +---@return void +function event(id, handler) end + +---@param command string +---@param handler fun(params: string[]): void +---@return void +function command(command, handler) end + +---@param trigger string +---@param handler fun(pressed: boolean): void +---@return void +function hotkey(trigger, handler) end