mirror of
https://github.com/LeavesMC/Leaves.git
synced 2025-12-19 14:59:32 +00:00
3884 lines
153 KiB
Diff
3884 lines
153 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: violetc <58360096+s-yh-china@users.noreply.github.com>
|
|
Date: Thu, 3 Feb 2022 12:28:15 +0800
|
|
Subject: [PATCH] Fakeplayer support
|
|
|
|
|
|
diff --git a/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java b/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java
|
|
index a1c9726d25479b5326fe2fa2b0f5a98d6b2da4c5..0724bd95143cb5dc69b5f1eb2e67ecd679e09a99 100644
|
|
--- a/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java
|
|
+++ b/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java
|
|
@@ -41,6 +41,12 @@ class PaperEventManager {
|
|
throw new IllegalStateException(event.getEventName() + " may only be triggered synchronously.");
|
|
}
|
|
|
|
+ // Leaves start - skip bot
|
|
+ if (event instanceof org.bukkit.event.player.PlayerEvent playerEvent && playerEvent.getPlayer() instanceof org.leavesmc.leaves.entity.Bot) {
|
|
+ return;
|
|
+ }
|
|
+ // Leaves end - skip bot
|
|
+
|
|
HandlerList handlers = event.getHandlers();
|
|
RegisteredListener[] listeners = handlers.getRegisteredListeners();
|
|
|
|
diff --git a/src/main/java/net/minecraft/advancements/critereon/SimpleCriterionTrigger.java b/src/main/java/net/minecraft/advancements/critereon/SimpleCriterionTrigger.java
|
|
index 35772110e9318df46a2729dbc0b5879b290011b7..f26989a44cdda9baabf337d573436c6c115c9884 100644
|
|
--- a/src/main/java/net/minecraft/advancements/critereon/SimpleCriterionTrigger.java
|
|
+++ b/src/main/java/net/minecraft/advancements/critereon/SimpleCriterionTrigger.java
|
|
@@ -39,6 +39,7 @@ public abstract class SimpleCriterionTrigger<T extends SimpleCriterionTrigger.Si
|
|
}
|
|
|
|
protected void trigger(ServerPlayer player, Predicate<T> predicate) {
|
|
+ if (player instanceof org.leavesmc.leaves.bot.ServerBot) return; // Leaves - bot skip
|
|
PlayerAdvancements playerAdvancements = player.getAdvancements();
|
|
Set<CriterionTrigger.Listener<T>> set = (Set) playerAdvancements.criterionData.get(this); // Paper - fix AdvancementDataPlayer leak
|
|
if (set != null && !set.isEmpty()) {
|
|
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
|
|
index 7ce0fb36690e12f3f36c9a43e45ac71814be8e69..a625473805fcde57f3987f3d788efb36fa89073e 100644
|
|
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
|
|
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
|
|
@@ -746,6 +746,8 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
|
}
|
|
// Paper end - Configurable player collision
|
|
|
|
+ org.leavesmc.leaves.bot.ServerBot.loadAllBot(); // Leaves - load resident bot
|
|
+
|
|
this.server.enablePlugins(org.bukkit.plugin.PluginLoadOrder.POSTWORLD);
|
|
this.server.spark.registerCommandBeforePlugins(this.server); // Paper - spark
|
|
this.server.spark.enableAfterPlugins(this.server); // Paper - spark
|
|
@@ -1034,6 +1036,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
|
|
|
MinecraftServer.LOGGER.info("Stopping server");
|
|
Commands.COMMAND_SENDING_POOL.shutdownNow(); // Paper - Perf: Async command map building; Shutdown and don't bother finishing
|
|
+ org.leavesmc.leaves.bot.ServerBot.saveOrRemoveAllBot(); // Leaves - save or remove bot
|
|
this.server.spark.disable(); // Paper - spark
|
|
// CraftBukkit start
|
|
if (this.server != null) {
|
|
diff --git a/src/main/java/net/minecraft/server/PlayerAdvancements.java b/src/main/java/net/minecraft/server/PlayerAdvancements.java
|
|
index 9fabf9322acd663c4452b562494e74aa42eb19da..b5f18a0115b629930de84a9d086505adaa6087dd 100644
|
|
--- a/src/main/java/net/minecraft/server/PlayerAdvancements.java
|
|
+++ b/src/main/java/net/minecraft/server/PlayerAdvancements.java
|
|
@@ -221,6 +221,11 @@ public class PlayerAdvancements {
|
|
}
|
|
|
|
public boolean award(AdvancementHolder advancement, String criterionName) {
|
|
+ // Leaves start - bot can't get advancement
|
|
+ if (player instanceof org.leavesmc.leaves.bot.ServerBot) {
|
|
+ return false;
|
|
+ }
|
|
+ // Leaves end - bot can't get advancement
|
|
boolean flag = false;
|
|
AdvancementProgress advancementprogress = this.getOrStartProgress(advancement);
|
|
boolean flag1 = advancementprogress.isDone();
|
|
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
|
index 460e3c572884db5862a3f6101cf44d900d77e43b..14494e49cdd9d07be125823b07731d04768a64b3 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
|
@@ -1267,6 +1267,13 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
}
|
|
} else if (this.seenBy.remove(player.connection)) {
|
|
this.serverEntity.removePairing(player);
|
|
+ // Leaves start - render bot
|
|
+ if (entity instanceof org.leavesmc.leaves.bot.ServerBot bot) {
|
|
+ if (bot.alwaysSendData) {
|
|
+ bot.sendFakeData(player.connection, false);
|
|
+ }
|
|
+ }
|
|
+ // Leaves end - render bot
|
|
}
|
|
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
|
|
index b835b259d9e371ff18b1704249b290d1ecbe06e1..49e7d9bc75e029a8800f7369681e43efd14cd322 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
|
|
@@ -228,6 +228,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ca.spottedleaf.
|
|
public boolean hasPhysicsEvent = true; // Paper - BlockPhysicsEvent
|
|
public boolean hasEntityMoveEvent; // Paper - Add EntityMoveEvent
|
|
private final alternate.current.wire.WireHandler wireHandler = new alternate.current.wire.WireHandler(this); // Paper - optimize redstone (Alternate Current)
|
|
+ final List<ServerPlayer> realPlayers; // Leaves - skip
|
|
|
|
public LevelChunk getChunkIfLoaded(int x, int z) {
|
|
return this.chunkSource.getChunkAtIfLoadedImmediately(x, z); // Paper - Use getChunkIfLoadedImmediately
|
|
@@ -589,6 +590,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ca.spottedleaf.
|
|
this.chunkTaskScheduler = new ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkTaskScheduler((ServerLevel)(Object)this, ca.spottedleaf.moonrise.common.util.MoonriseCommon.WORKER_POOL);
|
|
// Paper end - rewrite chunk system
|
|
this.getCraftServer().addWorld(this.getWorld()); // CraftBukkit
|
|
+ this.realPlayers = Lists.newArrayList(); // Leaves - skip
|
|
}
|
|
|
|
// Paper start
|
|
@@ -2024,6 +2026,12 @@ public class ServerLevel extends Level implements WorldGenLevel, ca.spottedleaf.
|
|
return this.players;
|
|
}
|
|
|
|
+ // Leaves start - fakeplayer skip
|
|
+ public List<ServerPlayer> realPlayers() {
|
|
+ return this.realPlayers;
|
|
+ }
|
|
+ // Leaves end - fakeplayer skip
|
|
+
|
|
@Override
|
|
public void onBlockStateChange(BlockPos pos, BlockState oldBlock, BlockState newBlock) {
|
|
Optional<Holder<PoiType>> optional = PoiTypes.forState(oldBlock);
|
|
@@ -2495,6 +2503,11 @@ public class ServerLevel extends Level implements WorldGenLevel, ca.spottedleaf.
|
|
// ServerLevel.this.getChunkSource().addEntity(entity); // Paper - ignore and warn about illegal addEntity calls instead of crashing server; moved down below valid=true
|
|
if (entity instanceof ServerPlayer entityplayer) {
|
|
ServerLevel.this.players.add(entityplayer);
|
|
+ // Leaves start - skip
|
|
+ if (!(entityplayer instanceof org.leavesmc.leaves.bot.ServerBot)) {
|
|
+ ServerLevel.this.realPlayers.add(entityplayer);
|
|
+ }
|
|
+ // Leaves end - skip
|
|
ServerLevel.this.updateSleepingPlayerList();
|
|
}
|
|
|
|
@@ -2572,6 +2585,11 @@ public class ServerLevel extends Level implements WorldGenLevel, ca.spottedleaf.
|
|
ServerLevel.this.getChunkSource().removeEntity(entity);
|
|
if (entity instanceof ServerPlayer entityplayer) {
|
|
ServerLevel.this.players.remove(entityplayer);
|
|
+ // Leaves start - skip
|
|
+ if (!(entityplayer instanceof org.leavesmc.leaves.bot.ServerBot)) {
|
|
+ ServerLevel.this.realPlayers.remove(entityplayer);
|
|
+ }
|
|
+ // Leaves end - skip
|
|
ServerLevel.this.updateSleepingPlayerList();
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
|
|
index 763cffdc2e1e2e7cc9af88cc46bbaa240a20fd0d..647a6c9dd39e113625377273281d74ae3f902f96 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
|
|
@@ -212,7 +212,7 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player imple
|
|
private static final AttributeModifier CREATIVE_ENTITY_INTERACTION_RANGE_MODIFIER = new AttributeModifier(ResourceLocation.withDefaultNamespace("creative_mode_entity_range"), 2.0D, AttributeModifier.Operation.ADD_VALUE);
|
|
public ServerGamePacketListenerImpl connection;
|
|
public final MinecraftServer server;
|
|
- public final ServerPlayerGameMode gameMode;
|
|
+ public ServerPlayerGameMode gameMode; // Leaves - final -> null
|
|
private final PlayerAdvancements advancements;
|
|
private final ServerStatsCounter stats;
|
|
private float lastRecordedHealthAndAbsorption = Float.MIN_VALUE;
|
|
@@ -770,16 +770,20 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player imple
|
|
--this.invulnerableTime;
|
|
}
|
|
|
|
- // Paper start - Configurable container update tick rate
|
|
- if (--containerUpdateDelay <= 0) {
|
|
- this.containerMenu.broadcastChanges();
|
|
- containerUpdateDelay = this.level().paperConfig().tickRates.containerUpdate;
|
|
- }
|
|
- // Paper end - Configurable container update tick rate
|
|
- if (!this.level().isClientSide && this.containerMenu != this.inventoryMenu && (this.isImmobile() || !this.containerMenu.stillValid(this))) { // Paper - Prevent opening inventories when frozen
|
|
- this.closeContainer(org.bukkit.event.inventory.InventoryCloseEvent.Reason.CANT_USE); // Paper - Inventory close reason
|
|
- this.containerMenu = this.inventoryMenu;
|
|
+ // Leaves start - skip bot
|
|
+ if (!(this instanceof org.leavesmc.leaves.bot.ServerBot)) {
|
|
+ // Paper start - Configurable container update tick rate
|
|
+ if (--containerUpdateDelay <= 0) {
|
|
+ this.containerMenu.broadcastChanges();
|
|
+ containerUpdateDelay = this.level().paperConfig().tickRates.containerUpdate;
|
|
+ }
|
|
+ // Paper end - Configurable container update tick rate
|
|
+ if (!this.level().isClientSide && this.containerMenu != this.inventoryMenu && (this.isImmobile() || !this.containerMenu.stillValid(this))) { // Paper - Prevent opening inventories when frozen
|
|
+ this.closeContainer(org.bukkit.event.inventory.InventoryCloseEvent.Reason.CANT_USE); // Paper - Inventory close reason
|
|
+ this.containerMenu = this.inventoryMenu;
|
|
+ }
|
|
}
|
|
+ // Leaves end - skip bot
|
|
|
|
Entity entity = this.getCamera();
|
|
|
|
@@ -795,7 +799,7 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player imple
|
|
}
|
|
}
|
|
|
|
- CriteriaTriggers.TICK.trigger(this);
|
|
+ if (!(this instanceof org.leavesmc.leaves.bot.ServerBot)) CriteriaTriggers.TICK.trigger(this); // Leaves - skip bot
|
|
if (this.levitationStartPos != null) {
|
|
CriteriaTriggers.LEVITATION.trigger(this, this.levitationStartPos, this.tickCount - this.levitationStartTime);
|
|
}
|
|
@@ -1015,7 +1019,7 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player imple
|
|
List<DefaultDrop> loot = new java.util.ArrayList<>(this.getInventory().getContainerSize()); // Paper - Restore vanilla drops behavior
|
|
boolean keepInventory = this.level().getGameRules().getBoolean(GameRules.RULE_KEEPINVENTORY) || this.isSpectator();
|
|
|
|
- if (!keepInventory) {
|
|
+ if (!keepInventory || this instanceof org.leavesmc.leaves.bot.ServerBot) { // Leaves - skip bot
|
|
for (ItemStack item : this.getInventory().getContents()) {
|
|
if (!item.isEmpty() && !EnchantmentHelper.has(item, EnchantmentEffectComponents.PREVENT_EQUIPMENT_DROP)) {
|
|
loot.add(new DefaultDrop(item, stack -> this.drop(stack, true, false, false))); // Paper - Restore vanilla drops behavior; drop function taken from Inventory#dropAll (don't fire drop event)
|
|
@@ -1417,6 +1421,13 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player imple
|
|
this.lastSentHealth = -1.0F;
|
|
this.lastSentFood = -1;
|
|
|
|
+ // Leaves start - bot support
|
|
+ if (org.leavesmc.leaves.LeavesConfig.fakeplayerSupport) {
|
|
+ org.leavesmc.leaves.bot.ServerBot.getBots().forEach(bot1 ->
|
|
+ bot1.sendFakeDataIfNeed(this, true)); // Leaves - render bot
|
|
+ }
|
|
+ // Leaves end - bot support
|
|
+
|
|
// CraftBukkit start
|
|
PlayerChangedWorldEvent changeEvent = new PlayerChangedWorldEvent(this.getBukkitEntity(), worldserver1.getWorld());
|
|
this.level().getCraftServer().getPluginManager().callEvent(changeEvent);
|
|
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
|
|
index 8ccd40b562691e757c7b5efa1497d93a95040a9a..901e9ff7a89b48b7acca7e5b96f640045c1d265e 100644
|
|
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
|
|
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
|
|
@@ -123,6 +123,8 @@ import org.bukkit.event.player.PlayerRespawnEvent.RespawnReason;
|
|
import org.bukkit.event.player.PlayerSpawnChangeEvent;
|
|
// CraftBukkit end
|
|
|
|
+import org.leavesmc.leaves.bot.ServerBot;
|
|
+
|
|
public abstract class PlayerList {
|
|
|
|
public static final File USERBANLIST_FILE = new File("banned-players.json");
|
|
@@ -351,6 +353,21 @@ public abstract class PlayerList {
|
|
|
|
org.leavesmc.leaves.protocol.core.LeavesProtocolManager.handlePlayerJoin(player); // Leaves - protocol
|
|
|
|
+ // Leaves start - bot support
|
|
+ if (org.leavesmc.leaves.LeavesConfig.fakeplayerSupport) {
|
|
+ ServerBot bot = ServerBot.getBot(player.getScoreboardName().toLowerCase(java.util.Locale.ROOT));
|
|
+ if (bot != null) {
|
|
+ bot.die(bot.damageSources().fellOutOfWorld()); // Leaves - remove bot with the same name
|
|
+ this.playersByName.put(player.getScoreboardName().toLowerCase(java.util.Locale.ROOT), player);
|
|
+ this.playersByUUID.put(player.getUUID(), player);
|
|
+ }
|
|
+ ServerBot.getBots().forEach(bot1 -> {
|
|
+ bot1.sendPlayerInfo(player);
|
|
+ bot1.sendFakeDataIfNeed(player, true);
|
|
+ }); // Leaves - render bot
|
|
+ }
|
|
+ // Leaves end - bot support
|
|
+
|
|
final net.kyori.adventure.text.Component jm = playerJoinEvent.joinMessage();
|
|
|
|
if (jm != null && !jm.equals(net.kyori.adventure.text.Component.empty())) { // Paper - Adventure
|
|
@@ -937,6 +954,13 @@ public abstract class PlayerList {
|
|
}
|
|
// Paper end - Add PlayerPostRespawnEvent
|
|
|
|
+ // Leaves start - bot support
|
|
+ if (org.leavesmc.leaves.LeavesConfig.fakeplayerSupport) {
|
|
+ ServerBot.getBots().forEach(bot1 ->
|
|
+ bot1.sendFakeDataIfNeed(entityplayer1, true)); // Leaves - render bot
|
|
+ }
|
|
+ // Leaves end - bot support
|
|
+
|
|
// CraftBukkit end
|
|
|
|
return entityplayer1;
|
|
@@ -1069,11 +1093,16 @@ public abstract class PlayerList {
|
|
}
|
|
|
|
public String[] getPlayerNamesArray() {
|
|
- String[] astring = new String[this.players.size()];
|
|
+ String[] astring = new String[this.players.size() + ServerBot.getBots().size()]; // Leaves - fakeplayer support
|
|
|
|
for (int i = 0; i < this.players.size(); ++i) {
|
|
astring[i] = ((ServerPlayer) this.players.get(i)).getGameProfile().getName();
|
|
}
|
|
+ // Leaves start - fakeplayer support
|
|
+ for (int i = this.players.size(); i < astring.length; ++i) {
|
|
+ astring[i] = ((ServerPlayer) ServerBot.getBots().get(i - this.players.size())).getGameProfile().getName();
|
|
+ }
|
|
+ // Leaves end - fakeplayer support
|
|
|
|
return astring;
|
|
}
|
|
@@ -1553,4 +1582,16 @@ public abstract class PlayerList {
|
|
public boolean isAllowCommandsForAllPlayers() {
|
|
return this.allowCommandsForAllPlayers;
|
|
}
|
|
+
|
|
+ // Leaves start - fakeplayer support
|
|
+ public void addNewBot(ServerBot bot) {
|
|
+ playersByName.put(bot.getScoreboardName().toLowerCase(java.util.Locale.ROOT), bot);
|
|
+ playersByUUID.put(bot.getUUID(), bot);
|
|
+ }
|
|
+
|
|
+ public void removeBot(ServerBot bot) {
|
|
+ playersByName.remove(bot.getScoreboardName().toLowerCase(java.util.Locale.ROOT));
|
|
+ playersByUUID.remove(bot.getUUID());
|
|
+ }
|
|
+ // Leaves end - fakeplayer support
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
|
|
index 7d7258c56cfade2d82acdf83dfa20cd0416c0dab..1beefbd05c13181214e188f50e88fb67f6712a45 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/Entity.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
|
|
@@ -1455,7 +1455,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
|
return offsetFactor;
|
|
}
|
|
|
|
- private Vec3 collide(Vec3 movement) {
|
|
+ public Vec3 collide(Vec3 movement) { // Leaves - private -> public
|
|
// Paper start - optimise collisions
|
|
final boolean xZero = movement.x == 0.0;
|
|
final boolean yZero = movement.y == 0.0;
|
|
diff --git a/src/main/java/net/minecraft/world/entity/projectile/FishingHook.java b/src/main/java/net/minecraft/world/entity/projectile/FishingHook.java
|
|
index 1223c5d23d0ea6aed068bdf0f5725e2ad49fc82c..0e00f59a8962dd6356d483ef5be3209a3a410008 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/projectile/FishingHook.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/projectile/FishingHook.java
|
|
@@ -63,7 +63,7 @@ public class FishingHook extends Projectile {
|
|
public static final EntityDataAccessor<Integer> DATA_HOOKED_ENTITY = SynchedEntityData.defineId(FishingHook.class, EntityDataSerializers.INT);
|
|
private static final EntityDataAccessor<Boolean> DATA_BITING = SynchedEntityData.defineId(FishingHook.class, EntityDataSerializers.BOOLEAN);
|
|
private int life;
|
|
- private int nibble;
|
|
+ public int nibble; // Leaves - private -> public
|
|
public int timeUntilLured;
|
|
public int timeUntilHooked;
|
|
public float fishAngle;
|
|
diff --git a/src/main/java/net/minecraft/world/inventory/AbstractContainerMenu.java b/src/main/java/net/minecraft/world/inventory/AbstractContainerMenu.java
|
|
index 32910f677b0522ac8ec513fa0d00b714b52cfae4..961a7193fda00fa62acea9c39fda1c93f7fbe412 100644
|
|
--- a/src/main/java/net/minecraft/world/inventory/AbstractContainerMenu.java
|
|
+++ b/src/main/java/net/minecraft/world/inventory/AbstractContainerMenu.java
|
|
@@ -406,6 +406,8 @@ public abstract class AbstractContainerMenu {
|
|
ItemStack itemstack1;
|
|
int l;
|
|
|
|
+ if (!doClickCheck(slotIndex, button, actionType, player)) return; // Leaves - doClick check
|
|
+
|
|
if (actionType == ClickType.QUICK_CRAFT) {
|
|
int i1 = this.quickcraftStatus;
|
|
|
|
@@ -680,6 +682,22 @@ public abstract class AbstractContainerMenu {
|
|
|
|
}
|
|
|
|
+ // Leaves start - doClick check
|
|
+ private boolean doClickCheck(int slotIndex, int button, ClickType actionType, Player player) {
|
|
+ if (slotIndex < 0) {
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ Slot slot = getSlot(slotIndex);
|
|
+ ItemStack itemStack = slot.getItem();
|
|
+ net.minecraft.world.item.component.CustomData customData = itemStack.get(net.minecraft.core.component.DataComponents.CUSTOM_DATA);
|
|
+ if (customData != null && customData.contains("Leaves.Gui.Placeholder")) {
|
|
+ return !customData.copyTag().getBoolean("Leaves.Gui.Placeholder");
|
|
+ }
|
|
+ return true;
|
|
+ }
|
|
+ // Leaves end - doClick check
|
|
+
|
|
private boolean tryItemClickBehaviourOverride(Player player, ClickAction clickType, Slot slot, ItemStack stack, ItemStack cursorStack) {
|
|
FeatureFlagSet featureflagset = player.level().enabledFeatures();
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/level/levelgen/PhantomSpawner.java b/src/main/java/net/minecraft/world/level/levelgen/PhantomSpawner.java
|
|
index 1b1b475ca27e799e251d6f8a8c9fe1a4fd8bae83..aff4e1e4d2462adf00d6e984b0a0540592257056 100644
|
|
--- a/src/main/java/net/minecraft/world/level/levelgen/PhantomSpawner.java
|
|
+++ b/src/main/java/net/minecraft/world/level/levelgen/PhantomSpawner.java
|
|
@@ -67,6 +67,11 @@ public class PhantomSpawner implements CustomSpawner {
|
|
ServerStatsCounter serverstatisticmanager = entityplayer.getStats();
|
|
int j = Mth.clamp(serverstatisticmanager.getValue(Stats.CUSTOM.get(Stats.TIME_SINCE_REST)), 1, Integer.MAX_VALUE);
|
|
boolean flag2 = true;
|
|
+ // Leaves start - fakeplayer spawn
|
|
+ if (entityplayer instanceof org.leavesmc.leaves.bot.ServerBot bot && bot.spawnPhantom) {
|
|
+ j = Math.max(bot.notSleepTicks, 1);
|
|
+ }
|
|
+ // Leaves end - fakeplayer spawn
|
|
|
|
if (randomsource.nextInt(j) >= world.paperConfig().entities.behavior.playerInsomniaStartTicks) { // Paper - Ability to control player's insomnia and phantoms
|
|
BlockPos blockposition1 = blockposition.above(20 + randomsource.nextInt(15)).east(-10 + randomsource.nextInt(21)).south(-10 + randomsource.nextInt(21));
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
|
index f2a0a1f32bf456c302e5d18b91367aa0c041cc6c..59cdcf7b25c17705b613c83dea107934b683af28 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
|
@@ -310,6 +310,7 @@ public final class CraftServer implements Server {
|
|
private final io.papermc.paper.logging.SysoutCatcher sysoutCatcher = new io.papermc.paper.logging.SysoutCatcher(); // Paper
|
|
private final io.papermc.paper.potion.PaperPotionBrewer potionBrewer; // Paper - Custom Potion Mixes
|
|
public final io.papermc.paper.SparksFly spark; // Paper - spark
|
|
+ private final org.leavesmc.leaves.entity.CraftBotManager botManager = new org.leavesmc.leaves.entity.CraftBotManager(); // Leaves
|
|
|
|
// Paper start - Folia region threading API
|
|
private final io.papermc.paper.threadedregions.scheduler.FallbackRegionScheduler regionizedScheduler = new io.papermc.paper.threadedregions.scheduler.FallbackRegionScheduler();
|
|
@@ -1463,7 +1464,7 @@ public final class CraftServer implements Server {
|
|
return false;
|
|
}
|
|
|
|
- if (handle.players().size() > 0) {
|
|
+ if (handle.realPlayers().size() > 0) { // Leaves - skip
|
|
return false;
|
|
}
|
|
|
|
@@ -3229,4 +3230,11 @@ public final class CraftServer implements Server {
|
|
return this.potionBrewer;
|
|
}
|
|
// Paper end
|
|
+
|
|
+ // Leaves start - Bot API
|
|
+ @Override
|
|
+ public org.leavesmc.leaves.entity.CraftBotManager getBotManager() {
|
|
+ return botManager;
|
|
+ }
|
|
+ // Leaves end - Bot API
|
|
}
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
|
index 362ca138a5cd5ad19f1300015c2571794adc3649..7daa86bff793c040a10125e033b36fbd59a09a05 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
|
@@ -210,7 +210,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
|
|
|
|
@Override
|
|
public int getPlayerCount() {
|
|
- return world.players().size();
|
|
+ return world.realPlayers().size(); // Leaves - skip
|
|
}
|
|
|
|
@Override
|
|
@@ -1247,9 +1247,9 @@ public class CraftWorld extends CraftRegionAccessor implements World {
|
|
|
|
@Override
|
|
public List<Player> getPlayers() {
|
|
- List<Player> list = new ArrayList<Player>(this.world.players().size());
|
|
+ List<Player> list = new ArrayList<Player>(this.world.realPlayers().size()); // Leaves - skip
|
|
|
|
- for (net.minecraft.world.entity.player.Player human : this.world.players()) {
|
|
+ for (net.minecraft.world.entity.player.Player human : this.world.realPlayers()) { // Leaves - skip
|
|
HumanEntity bukkitEntity = human.getBukkitEntity();
|
|
|
|
if ((bukkitEntity != null) && (bukkitEntity instanceof Player)) {
|
|
@@ -1934,7 +1934,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
|
|
public void playSound(final net.kyori.adventure.sound.Sound sound) {
|
|
org.spigotmc.AsyncCatcher.catchOp("play sound"); // Paper
|
|
final long seed = sound.seed().orElseGet(this.world.getRandom()::nextLong);
|
|
- for (ServerPlayer player : this.getHandle().players()) {
|
|
+ for (ServerPlayer player : this.getHandle().realPlayers()) { // Leaves - skip
|
|
player.connection.send(io.papermc.paper.adventure.PaperAdventure.asSoundPacket(sound, player.getX(), player.getY(), player.getZ(), seed, null));
|
|
}
|
|
}
|
|
@@ -1950,7 +1950,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
|
|
org.spigotmc.AsyncCatcher.catchOp("play sound"); // Paper
|
|
final long seed = sound.seed().orElseGet(this.getHandle().getRandom()::nextLong);
|
|
if (emitter == net.kyori.adventure.sound.Sound.Emitter.self()) {
|
|
- for (ServerPlayer player : this.getHandle().players()) {
|
|
+ for (ServerPlayer player : this.getHandle().realPlayers()) { // Leaves - skip
|
|
player.connection.send(io.papermc.paper.adventure.PaperAdventure.asSoundPacket(sound, player, seed, null));
|
|
}
|
|
} else if (emitter instanceof CraftEntity craftEntity) {
|
|
@@ -2171,7 +2171,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
|
|
Preconditions.checkArgument(particle.getDataType().isInstance(data), "data (%s) should be %s", data.getClass(), particle.getDataType());
|
|
}
|
|
this.getHandle().sendParticles(
|
|
- receivers == null ? this.getHandle().players() : receivers.stream().map(player -> ((CraftPlayer) player).getHandle()).collect(java.util.stream.Collectors.toList()), // Paper - Particle API
|
|
+ receivers == null ? this.getHandle().realPlayers() : receivers.stream().map(player -> ((CraftPlayer) player).getHandle()).collect(java.util.stream.Collectors.toList()), // Paper - Particle API // Leaves - skip
|
|
sender != null ? ((CraftPlayer) sender).getHandle() : null, // Sender // Paper - Particle API
|
|
CraftParticle.createParticleParam(particle, data), // Particle
|
|
x, y, z, // Position
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
|
|
index cd789c235acf740ec29c30b180e7fbe1a140caa9..9c3df47ae65289c27a1d63ac06ca41c074ff374e 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
|
|
@@ -94,6 +94,8 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity {
|
|
return new CraftHumanEntity(server, (net.minecraft.world.entity.player.Player) entity);
|
|
}
|
|
|
|
+ if (entity instanceof org.leavesmc.leaves.bot.ServerBot bot) { return new org.leavesmc.leaves.entity.CraftBot(server, bot); }
|
|
+
|
|
// Special case complex part, since there is no extra entity type for them
|
|
if (entity instanceof EnderDragonPart complexPart) {
|
|
if (complexPart.parentMob instanceof EnderDragon) {
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
|
|
index 8dd85b9ca3b3e3429de4d0ec0654982589c6e93e..de9f63fb3b8dcf11a9271794850ce448b020f14f 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
|
|
@@ -1025,7 +1025,10 @@ public class CraftEventFactory {
|
|
event.setKeepInventory(keepInventory);
|
|
event.setKeepLevel(victim.keepLevel); // SPIGOT-2222: pre-set keepLevel
|
|
populateFields(victim, event); // Paper - make cancellable
|
|
- Bukkit.getServer().getPluginManager().callEvent(event);
|
|
+ // Leaves start - disable bot death event
|
|
+ if (!(victim instanceof org.leavesmc.leaves.bot.ServerBot)) {
|
|
+ Bukkit.getServer().getPluginManager().callEvent(event);
|
|
+ } // Leaves end
|
|
// Paper start - make cancellable
|
|
if (event.isCancelled()) {
|
|
return event;
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java
|
|
index 22f1ed383313829b8af4badda9ef8dc85cae8fd1..1c47e320e464af9651953ff308a2583fcb965891 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java
|
|
@@ -1,7 +1,7 @@
|
|
package org.bukkit.craftbukkit.scheduler;
|
|
|
|
import com.google.common.base.Preconditions;
|
|
-import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
|
+
|
|
import java.util.ArrayList;
|
|
import java.util.Comparator;
|
|
import java.util.Iterator;
|
|
@@ -10,7 +10,6 @@ import java.util.PriorityQueue;
|
|
import java.util.concurrent.Callable;
|
|
import java.util.concurrent.ConcurrentHashMap;
|
|
import java.util.concurrent.Executor;
|
|
-import java.util.concurrent.Executors;
|
|
import java.util.concurrent.Future;
|
|
import java.util.concurrent.atomic.AtomicInteger;
|
|
import java.util.concurrent.atomic.AtomicReference;
|
|
@@ -23,6 +22,7 @@ import org.bukkit.scheduler.BukkitRunnable;
|
|
import org.bukkit.scheduler.BukkitScheduler;
|
|
import org.bukkit.scheduler.BukkitTask;
|
|
import org.bukkit.scheduler.BukkitWorker;
|
|
+import org.leavesmc.leaves.plugin.MinecraftInternalPlugin;
|
|
|
|
/**
|
|
* The fundamental concepts for this implementation:
|
|
@@ -46,6 +46,8 @@ import org.bukkit.scheduler.BukkitWorker;
|
|
*/
|
|
public class CraftScheduler implements BukkitScheduler {
|
|
|
|
+ public static final Plugin MINECRAFT = new MinecraftInternalPlugin(); // Leaves - run async task
|
|
+
|
|
/**
|
|
* The start ID for the counter.
|
|
*/
|
|
@@ -478,10 +480,14 @@ public class CraftScheduler implements BukkitScheduler {
|
|
"Task #%s for %s generated an exception",
|
|
task.getTaskId(),
|
|
task.getOwner().getDescription().getFullName());
|
|
- task.getOwner().getLogger().log(
|
|
- Level.WARNING,
|
|
- logMessage,
|
|
- throwable);
|
|
+ if (task.getOwner() instanceof MinecraftInternalPlugin) {
|
|
+ net.minecraft.server.MinecraftServer.LOGGER.error(logMessage, throwable);
|
|
+ } else {
|
|
+ task.getOwner().getLogger().log(
|
|
+ Level.WARNING,
|
|
+ logMessage,
|
|
+ throwable);
|
|
+ }
|
|
org.bukkit.Bukkit.getServer().getPluginManager().callEvent(
|
|
new com.destroystokyo.paper.event.server.ServerExceptionEvent(new com.destroystokyo.paper.exception.ServerSchedulerException(logMessage, throwable, task)));
|
|
// Paper end
|
|
diff --git a/src/main/java/org/leavesmc/leaves/bot/BotCommand.java b/src/main/java/org/leavesmc/leaves/bot/BotCommand.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..918cdffedddba7cebb8013600bf3a2f5ce1e32c0
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/leavesmc/leaves/bot/BotCommand.java
|
|
@@ -0,0 +1,472 @@
|
|
+package org.leavesmc.leaves.bot;
|
|
+
|
|
+import org.bukkit.Bukkit;
|
|
+import org.bukkit.ChatColor;
|
|
+import org.bukkit.Location;
|
|
+import org.bukkit.World;
|
|
+import org.bukkit.command.Command;
|
|
+import org.bukkit.command.CommandSender;
|
|
+import org.bukkit.command.ConsoleCommandSender;
|
|
+import org.bukkit.craftbukkit.entity.CraftPlayer;
|
|
+import org.bukkit.craftbukkit.scheduler.CraftScheduler;
|
|
+import org.bukkit.entity.Player;
|
|
+import org.bukkit.generator.WorldInfo;
|
|
+import org.bukkit.permissions.Permission;
|
|
+import org.bukkit.permissions.PermissionDefault;
|
|
+import org.bukkit.plugin.PluginManager;
|
|
+import org.jetbrains.annotations.NotNull;
|
|
+import org.leavesmc.leaves.LeavesConfig;
|
|
+import org.leavesmc.leaves.bot.agent.Actions;
|
|
+import org.leavesmc.leaves.bot.agent.BotAction;
|
|
+import org.leavesmc.leaves.bot.agent.actions.CraftCustomBotAction;
|
|
+import org.leavesmc.leaves.entity.Bot;
|
|
+import org.leavesmc.leaves.event.bot.BotActionEvent;
|
|
+import org.leavesmc.leaves.event.bot.BotConfigModifyEvent;
|
|
+import org.leavesmc.leaves.event.bot.BotCreateEvent;
|
|
+import org.leavesmc.leaves.event.bot.BotRemoveEvent;
|
|
+
|
|
+import java.util.*;
|
|
+import java.util.stream.Stream;
|
|
+
|
|
+public class BotCommand extends Command {
|
|
+
|
|
+ public BotCommand(String name) {
|
|
+ super(name);
|
|
+ this.description = "FakePlayer Command";
|
|
+ this.usageMessage = "/bot [create | remove | action | list | config]";
|
|
+ this.setPermission("bukkit.command.bot");
|
|
+ final PluginManager pluginManager = Bukkit.getServer().getPluginManager();
|
|
+ if (pluginManager.getPermission("bukkit.command.bot") == null) {
|
|
+ pluginManager.addPermission(new Permission("bukkit.command.bot", PermissionDefault.OP));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public @NotNull List<String> tabComplete(@NotNull CommandSender sender, @NotNull String alias, String @NotNull [] args, Location location) throws IllegalArgumentException {
|
|
+ var list = new ArrayList<String>();
|
|
+
|
|
+ if (args.length <= 1) {
|
|
+ list.add("create");
|
|
+ list.add("remove");
|
|
+ if (LeavesConfig.fakeplayerUseAction) {
|
|
+ list.add("action");
|
|
+ }
|
|
+ if (LeavesConfig.fakeplayerModifyConfig) {
|
|
+ list.add("config");
|
|
+ }
|
|
+ list.add("list");
|
|
+ }
|
|
+
|
|
+ if (args.length == 2) {
|
|
+ switch (args[0]) {
|
|
+ case "create" -> list.add("<BotName>");
|
|
+ case "remove", "action", "config" -> list.addAll(ServerBot.getBots().stream().map(e -> e.getName().getString()).toList());
|
|
+ case "list" -> list.addAll(Bukkit.getWorlds().stream().map(WorldInfo::getName).toList());
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (args.length == 3) {
|
|
+ switch (args[0]) {
|
|
+ case "action" -> {
|
|
+ list.add("list");
|
|
+ list.addAll(Actions.getNames());
|
|
+ }
|
|
+ case "create" -> list.add("<BotSkinName>");
|
|
+ case "config" -> list.addAll(acceptConfig);
|
|
+ case "remove" -> list.addAll(List.of("cancel", "[hour]"));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (args.length == 4) {
|
|
+ switch (args[0]) {
|
|
+ case "config" -> {
|
|
+ if (args[2].equals(BotConfig.SIMULATION_DISTANCE.configName)) {
|
|
+ list.add("10");
|
|
+ list.add("2");
|
|
+ list.add("<INT 2 - 32>");
|
|
+ } else {
|
|
+ list.add("true");
|
|
+ list.add("false");
|
|
+ }
|
|
+ }
|
|
+ case "remove" -> {
|
|
+ if (!Objects.equals(args[3], "cancel")) {
|
|
+ list.add("[minute]");
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ if (args.length == 5 && args[0].equals("remove")) {
|
|
+ if (!Objects.equals(args[3], "cancel")) {
|
|
+ list.add("[second]");
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (args.length >= 4 && args[0].equals("action")) {
|
|
+ BotAction action = Actions.getForName(args[2]);
|
|
+ if (action != null) {
|
|
+ list.addAll(action.getArgument().tabComplete(args.length - 4));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return list;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean execute(@NotNull CommandSender sender, @NotNull String commandLabel, String[] args) {
|
|
+ if (!testPermission(sender) || !LeavesConfig.fakeplayerSupport) return true;
|
|
+
|
|
+ if (args.length == 0) {
|
|
+ sender.sendMessage(ChatColor.RED + "Usage: " + usageMessage);
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ switch (args[0]) {
|
|
+ case "create" -> this.onCreate(sender, args);
|
|
+
|
|
+ case "remove" -> this.onRemove(sender, args);
|
|
+
|
|
+ case "action" -> this.onAction(sender, args);
|
|
+
|
|
+ case "config" -> this.onConfig(sender, args);
|
|
+
|
|
+ case "list" -> this.onList(sender, args);
|
|
+
|
|
+ default -> {
|
|
+ sender.sendMessage(ChatColor.RED + "Usage: " + usageMessage);
|
|
+ return false;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ private void onCreate(CommandSender sender, String @NotNull [] args) {
|
|
+ if (args.length < 2) {
|
|
+ sender.sendMessage(ChatColor.RED + "Use /bot create <name> [skin_name] to create a fakeplayer");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (canCreate(sender, args[1])) {
|
|
+ if (sender instanceof Player player) {
|
|
+ new ServerBot.BotCreateState(player.getLocation(), args[1], args.length < 3 ? args[1] : args[2], BotCreateEvent.CreateReason.COMMAND, player).create(null);
|
|
+ } else if (sender instanceof ConsoleCommandSender csender) {
|
|
+ if (args.length < 6) {
|
|
+ sender.sendMessage(ChatColor.RED + "Use /bot create <name> <skin_name> <bukkit_world_name> <x> <y> <z> to create a fakeplayer");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ try {
|
|
+ World world = Bukkit.getWorld(args[3]);
|
|
+ double x = Double.parseDouble(args[4]);
|
|
+ double y = Double.parseDouble(args[5]);
|
|
+ double z = Double.parseDouble(args[6]);
|
|
+
|
|
+ if (world != null) {
|
|
+ new ServerBot.BotCreateState(new Location(world, x, y, z), args[1], args[2], BotCreateEvent.CreateReason.COMMAND, csender).create(null);
|
|
+ }
|
|
+ } catch (Exception e) {
|
|
+ e.printStackTrace();
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private boolean canCreate(CommandSender sender, @NotNull String name) {
|
|
+ if (!name.matches("^[a-zA-Z0-9_]{4,16}$")) {
|
|
+ sender.sendMessage(ChatColor.RED + "This name is illegal");
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ if (Bukkit.getPlayerExact(name) != null || ServerBot.getBot(name) != null) {
|
|
+ sender.sendMessage(ChatColor.RED + "This player is in server");
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ if (org.leavesmc.leaves.LeavesConfig.unableFakeplayerNames.contains(name)) {
|
|
+ sender.sendMessage(ChatColor.RED + "This name is not allowed");
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ if (ServerBot.getBots().size() >= org.leavesmc.leaves.LeavesConfig.fakeplayerLimit) {
|
|
+ sender.sendMessage(ChatColor.RED + "Fakeplayer limit is full");
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ private void onRemove(CommandSender sender, String @NotNull [] args) {
|
|
+ if (args.length < 2 || args.length > 5) {
|
|
+ sender.sendMessage(ChatColor.RED + "Use /bot remove <name> [hour] [minute] [second] to remove a fakeplayer");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ ServerBot bot = ServerBot.getBot(args[1]);
|
|
+
|
|
+ if (bot == null) {
|
|
+ sender.sendMessage(ChatColor.RED + "This fakeplayer is not in server");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (args.length > 2) {
|
|
+ if (args[2].equals("cancel")) {
|
|
+ if (bot.removeTaskId == -1) {
|
|
+ sender.sendMessage(ChatColor.RED + "This fakeplayer is not scheduled to be removed");
|
|
+ return;
|
|
+ }
|
|
+ Bukkit.getScheduler().cancelTask(bot.removeTaskId);
|
|
+ bot.removeTaskId = -1;
|
|
+ sender.sendMessage("Remove cancel");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ long time = 0;
|
|
+ int h; // Preventing out-of-range
|
|
+ long s = 0;
|
|
+ long m = 0;
|
|
+
|
|
+ try {
|
|
+ h = Integer.parseInt(args[2]);
|
|
+ if (h < 0) {
|
|
+ throw new NumberFormatException();
|
|
+ }
|
|
+ time += ((long) h) * 3600 * 20;
|
|
+ if (args.length > 3) {
|
|
+ m = Long.parseLong(args[3]);
|
|
+ if (m > 59 || m < 0) {
|
|
+ throw new NumberFormatException();
|
|
+ }
|
|
+ time += m * 60 * 20;
|
|
+ }
|
|
+ if (args.length > 4) {
|
|
+ s = Long.parseLong(args[4]);
|
|
+ if (s > 59 || s < 0) {
|
|
+ throw new NumberFormatException();
|
|
+ }
|
|
+ time += s * 20;
|
|
+ }
|
|
+ } catch (NumberFormatException e) {
|
|
+ sender.sendMessage(ChatColor.RED + "This fakeplayer is not scheduled to be removed");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ boolean isReschedule = bot.removeTaskId != -1;
|
|
+
|
|
+ if (isReschedule) {
|
|
+ Bukkit.getScheduler().cancelTask(bot.removeTaskId);
|
|
+ }
|
|
+ bot.removeTaskId = Bukkit.getScheduler().runTaskLater(CraftScheduler.MINECRAFT, () -> {
|
|
+ bot.removeTaskId = -1;
|
|
+ bot.onRemove(BotRemoveEvent.RemoveReason.COMMAND, sender);
|
|
+ }, time).getTaskId();
|
|
+
|
|
+ sender.sendMessage("This fakeplayer will be removed in " + h + "h " + m + "m " + s + "s" + (isReschedule ? " (rescheduled)" : ""));
|
|
+
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ bot.onRemove(BotRemoveEvent.RemoveReason.COMMAND, sender);
|
|
+ }
|
|
+
|
|
+ private void onAction(CommandSender sender, String @NotNull [] args) {
|
|
+ if (!LeavesConfig.fakeplayerUseAction) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (args.length < 3) {
|
|
+ sender.sendMessage(ChatColor.RED + "Use /bot action <name> <action> to make fakeplayer do action");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ ServerBot bot = ServerBot.getBot(args[1]);
|
|
+ if (bot == null) {
|
|
+ sender.sendMessage(ChatColor.RED + "This fakeplayer is not in server");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (args[2].equals("list")) {
|
|
+ sender.sendMessage(bot.getScoreboardName() + "'s action list:");
|
|
+ for (BotAction action : bot.getBotActions()) {
|
|
+ sender.sendMessage(action.getName());
|
|
+ }
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ BotAction action = Actions.getForName(args[2]);
|
|
+ if (action == null) {
|
|
+ sender.sendMessage(ChatColor.RED + "Invalid action");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ CraftPlayer player;
|
|
+ if (sender instanceof CraftPlayer) {
|
|
+ player = (CraftPlayer) sender;
|
|
+ } else {
|
|
+ player = bot.getBukkitEntity();
|
|
+ }
|
|
+
|
|
+ String[] realArgs = new String[args.length - 3];
|
|
+ if (realArgs.length != 0) {
|
|
+ System.arraycopy(args, 3, realArgs, 0, realArgs.length);
|
|
+ }
|
|
+
|
|
+ BotAction newAction;
|
|
+ if (action instanceof CraftCustomBotAction customBotAction) {
|
|
+ newAction = customBotAction.getNew(player, realArgs);
|
|
+ } else {
|
|
+ newAction = action.getNew(player.getHandle(), action.getArgument().parse(0, realArgs));
|
|
+ }
|
|
+
|
|
+ if (newAction == null) {
|
|
+ sender.sendMessage(ChatColor.RED + "Action create error, please check your arguments");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ BotActionEvent event = new BotActionEvent(bot.getBukkitEntity(), newAction.getName(), realArgs);
|
|
+ Bukkit.getPluginManager().callEvent(event);
|
|
+
|
|
+ if (!event.isCancelled()) {
|
|
+ bot.setBotAction(newAction);
|
|
+ sender.sendMessage("Action " + action.getName() + " has been issued to " + bot.getName().getString());
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public enum BotConfig {
|
|
+ SKIP_SLEEP, SPAWN_PHANTOM, ALWAYS_SEND_DATA, SIMULATION_DISTANCE;
|
|
+
|
|
+ public final String configName;
|
|
+
|
|
+ BotConfig() {
|
|
+ this.configName = this.name().toLowerCase(Locale.ROOT);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private static final List<String> acceptConfig = Stream.of(BotConfig.values()).map(config -> config.configName).toList();
|
|
+
|
|
+ private void onConfig(CommandSender sender, String @NotNull [] args) {
|
|
+ if (!LeavesConfig.fakeplayerModifyConfig) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (args.length < 3) {
|
|
+ sender.sendMessage(ChatColor.RED + "Use /bot config <name> <config> to modify fakeplayer's config");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ ServerBot bot = ServerBot.getBot(args[1]);
|
|
+ if (bot == null) {
|
|
+ sender.sendMessage(ChatColor.RED + "This fakeplayer is not in server");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (!acceptConfig.contains(args[2])) {
|
|
+ sender.sendMessage(ChatColor.RED + "This config is not accept");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ BotConfig config = BotConfig.valueOf(args[2].toUpperCase(Locale.ROOT));
|
|
+ if (args.length < 4) {
|
|
+ String value = null;
|
|
+ switch (config) {
|
|
+ case SKIP_SLEEP -> value = String.valueOf(bot.fauxSleeping);
|
|
+ case SPAWN_PHANTOM -> {
|
|
+ sender.sendMessage(bot.getScoreboardName() + "'s spawn_phantom: " + bot.spawnPhantom);
|
|
+ if (bot.spawnPhantom) {
|
|
+ sender.sendMessage(bot.getScoreboardName() + "'s not_sleeping_ticks: " + bot.notSleepTicks);
|
|
+ }
|
|
+ return;
|
|
+ }
|
|
+ case ALWAYS_SEND_DATA -> value = String.valueOf(bot.alwaysSendData);
|
|
+ case SIMULATION_DISTANCE -> value = String.valueOf(bot.getBukkitEntity().getSimulationDistance());
|
|
+ }
|
|
+ sender.sendMessage(bot.getScoreboardName() + "'s " + config.configName + ": " + value);
|
|
+ } else {
|
|
+ String value = args[3];
|
|
+
|
|
+ BotConfigModifyEvent event = new BotConfigModifyEvent(bot.getBukkitEntity(), config.configName, value);
|
|
+ Bukkit.getPluginManager().callEvent(event);
|
|
+
|
|
+ if (event.isCancelled()) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ switch (config) {
|
|
+ case SKIP_SLEEP -> {
|
|
+ boolean realValue = value.equals("true");
|
|
+ bot.fauxSleeping = realValue;
|
|
+ value = String.valueOf(realValue);
|
|
+ }
|
|
+ case SPAWN_PHANTOM -> {
|
|
+ boolean realValue = value.equals("true");
|
|
+ bot.spawnPhantom = realValue;
|
|
+ value = String.valueOf(realValue);
|
|
+ }
|
|
+ case ALWAYS_SEND_DATA -> {
|
|
+ boolean realValue = value.equals("true");
|
|
+ bot.alwaysSendData = realValue;
|
|
+ value = String.valueOf(realValue);
|
|
+ }
|
|
+ case SIMULATION_DISTANCE -> {
|
|
+ try {
|
|
+ int realValue = Integer.parseInt(value);
|
|
+ if (realValue < 2 || realValue > 32) {
|
|
+ sender.sendMessage("simulation_distance must be a number between 2 and 32, got: " + value);
|
|
+ return;
|
|
+ }
|
|
+ bot.getBukkitEntity().setSimulationDistance(realValue);
|
|
+ } catch (NumberFormatException e) {
|
|
+ sender.sendMessage("simulation_distance must be a number between 2 and 32, got: " + value);
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ sender.sendMessage(bot.getScoreboardName() + "'s " + config.configName + " changed: " + value);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private void onList(CommandSender sender, String @NotNull [] args) {
|
|
+ if (args.length < 2) {
|
|
+ Map<World, List<String>> botMap = new HashMap<>();
|
|
+ for (World world : Bukkit.getWorlds()) {
|
|
+ botMap.put(world, new ArrayList<>());
|
|
+ }
|
|
+
|
|
+ for (ServerBot bot : ServerBot.getBots()) {
|
|
+ Bot bukkitBot = bot.getBukkitPlayer();
|
|
+ botMap.get(bukkitBot.getWorld()).add(bukkitBot.getName());
|
|
+ }
|
|
+
|
|
+ sender.sendMessage("Total number: (" + ServerBot.getBots().size() + "/" + org.leavesmc.leaves.LeavesConfig.fakeplayerLimit + ")");
|
|
+ for (World world : botMap.keySet()) {
|
|
+ sender.sendMessage(world.getName() + "(" + botMap.get(world).size() + "): " + formatPlayerNameList(botMap.get(world)));
|
|
+ }
|
|
+ } else {
|
|
+ World world = Bukkit.getWorld(args[2]);
|
|
+
|
|
+ if (world == null) {
|
|
+ sender.sendMessage(ChatColor.RED + "Unknown world");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ List<String> botList = new ArrayList<>();
|
|
+ for (ServerBot bot : ServerBot.getBots()) {
|
|
+ Bot bukkitBot = bot.getBukkitPlayer();
|
|
+ if (bukkitBot.getWorld() == world) {
|
|
+ botList.add(bukkitBot.getName());
|
|
+ }
|
|
+ }
|
|
+
|
|
+ sender.sendMessage(world.getName() + "(" + botList.size() + "): " + formatPlayerNameList(botList));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @NotNull
|
|
+ private static String formatPlayerNameList(@NotNull List<String> list) {
|
|
+ if (list.isEmpty()) {
|
|
+ return "";
|
|
+ }
|
|
+ String string = list.toString();
|
|
+ return string.substring(1, string.length() - 1);
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/leavesmc/leaves/bot/BotInventoryContainer.java b/src/main/java/org/leavesmc/leaves/bot/BotInventoryContainer.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..4f5e6e5c1b9d8bd38c98e97fd31b38338f35faa6
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/leavesmc/leaves/bot/BotInventoryContainer.java
|
|
@@ -0,0 +1,191 @@
|
|
+package org.leavesmc.leaves.bot;
|
|
+
|
|
+import com.google.common.collect.ImmutableList;
|
|
+import com.mojang.datafixers.util.Pair;
|
|
+import net.minecraft.core.NonNullList;
|
|
+import net.minecraft.core.component.DataComponentPatch;
|
|
+import net.minecraft.core.component.DataComponents;
|
|
+import net.minecraft.nbt.CompoundTag;
|
|
+import net.minecraft.network.chat.Component;
|
|
+import net.minecraft.world.ContainerHelper;
|
|
+import net.minecraft.world.SimpleContainer;
|
|
+import net.minecraft.world.entity.player.Player;
|
|
+import net.minecraft.world.item.ItemStack;
|
|
+import net.minecraft.world.item.Items;
|
|
+import net.minecraft.world.item.component.CustomData;
|
|
+
|
|
+import javax.annotation.Nonnull;
|
|
+import java.util.List;
|
|
+
|
|
+// Power by gugle-carpet-addition(https://github.com/Gu-ZT/gugle-carpet-addition)
|
|
+public class BotInventoryContainer extends SimpleContainer {
|
|
+
|
|
+ public final NonNullList<ItemStack> items;
|
|
+ public final NonNullList<ItemStack> armor;
|
|
+ public final NonNullList<ItemStack> offhand;
|
|
+ private final List<NonNullList<ItemStack>> compartments;
|
|
+ private final NonNullList<ItemStack> buttons = NonNullList.withSize(13, ItemStack.EMPTY);
|
|
+ private final ServerBot player;
|
|
+
|
|
+ public BotInventoryContainer(ServerBot player) {
|
|
+ this.player = player;
|
|
+ this.items = this.player.getInventory().items;
|
|
+ this.armor = this.player.getInventory().armor;
|
|
+ this.offhand = this.player.getInventory().offhand;
|
|
+ this.compartments = ImmutableList.of(this.items, this.armor, this.offhand, this.buttons);
|
|
+ createButton();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int getContainerSize() {
|
|
+ return this.items.size() + this.armor.size() + this.offhand.size() + this.buttons.size();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isEmpty() {
|
|
+ for (ItemStack itemStack : this.items) {
|
|
+ if (itemStack.isEmpty()) {
|
|
+ continue;
|
|
+ }
|
|
+ return false;
|
|
+ }
|
|
+ for (ItemStack itemStack : this.armor) {
|
|
+ if (itemStack.isEmpty()) {
|
|
+ continue;
|
|
+ }
|
|
+ return false;
|
|
+ }
|
|
+ for (ItemStack itemStack : this.offhand) {
|
|
+ if (itemStack.isEmpty()) {
|
|
+ continue;
|
|
+ }
|
|
+ return false;
|
|
+ }
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ @Nonnull
|
|
+ public ItemStack getItem(int slot) {
|
|
+ Pair<NonNullList<ItemStack>, Integer> pair = getItemSlot(slot);
|
|
+ if (pair != null) {
|
|
+ return pair.getFirst().get(pair.getSecond());
|
|
+ } else {
|
|
+ return ItemStack.EMPTY;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public Pair<NonNullList<ItemStack>, Integer> getItemSlot(int slot) {
|
|
+ switch (slot) {
|
|
+ case 0 -> {
|
|
+ return new Pair<>(buttons, 0);
|
|
+ }
|
|
+ case 1, 2, 3, 4 -> {
|
|
+ return new Pair<>(armor, 4 - slot);
|
|
+ }
|
|
+ case 5, 6 -> {
|
|
+ return new Pair<>(buttons, slot - 4);
|
|
+ }
|
|
+ case 7 -> {
|
|
+ return new Pair<>(offhand, 0);
|
|
+ }
|
|
+ case 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 -> {
|
|
+ return new Pair<>(buttons, slot - 5);
|
|
+ }
|
|
+ case 18, 19, 20, 21, 22, 23, 24, 25, 26,
|
|
+ 27, 28, 29, 30, 31, 32, 33, 34, 35,
|
|
+ 36, 37, 38, 39, 40, 41, 42, 43, 44 -> {
|
|
+ return new Pair<>(items, slot - 9);
|
|
+ }
|
|
+ case 45, 46, 47, 48, 49, 50, 51, 52, 53 -> {
|
|
+ return new Pair<>(items, slot - 45);
|
|
+ }
|
|
+ default -> {
|
|
+ return null;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ @Nonnull
|
|
+ public ItemStack removeItem(int slot, int amount) {
|
|
+ Pair<NonNullList<ItemStack>, Integer> pair = getItemSlot(slot);
|
|
+ NonNullList<ItemStack> list = null;
|
|
+ ItemStack itemStack = ItemStack.EMPTY;
|
|
+ if (pair != null) {
|
|
+ list = pair.getFirst();
|
|
+ slot = pair.getSecond();
|
|
+ }
|
|
+ if (list != null && !list.get(slot).isEmpty()) {
|
|
+ itemStack = ContainerHelper.removeItem(list, slot, amount);
|
|
+ player.detectEquipmentUpdatesPublic();
|
|
+ }
|
|
+ return itemStack;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ @Nonnull
|
|
+ public ItemStack removeItemNoUpdate(int slot) {
|
|
+ Pair<NonNullList<ItemStack>, Integer> pair = getItemSlot(slot);
|
|
+ NonNullList<ItemStack> list = null;
|
|
+ if (pair != null) {
|
|
+ list = pair.getFirst();
|
|
+ slot = pair.getSecond();
|
|
+ }
|
|
+ if (list != null && !list.get(slot).isEmpty()) {
|
|
+ ItemStack itemStack = list.get(slot);
|
|
+ list.set(slot, ItemStack.EMPTY);
|
|
+ return itemStack;
|
|
+ }
|
|
+ return ItemStack.EMPTY;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void setItem(int slot, @Nonnull ItemStack stack) {
|
|
+ Pair<NonNullList<ItemStack>, Integer> pair = getItemSlot(slot);
|
|
+ NonNullList<ItemStack> list = null;
|
|
+ if (pair != null) {
|
|
+ list = pair.getFirst();
|
|
+ slot = pair.getSecond();
|
|
+ }
|
|
+ if (list != null) {
|
|
+ list.set(slot, stack);
|
|
+ player.detectEquipmentUpdatesPublic();
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void setChanged() {
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean stillValid(@Nonnull Player player) {
|
|
+ if (this.player.isRemoved()) {
|
|
+ return false;
|
|
+ }
|
|
+ return !(player.distanceToSqr(this.player) > 64.0);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void clearContent() {
|
|
+ for (List<ItemStack> list : this.compartments) {
|
|
+ list.clear();
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private void createButton() {
|
|
+ CompoundTag customData = new CompoundTag();
|
|
+ customData.putBoolean("Leaves.Gui.Placeholder", true);
|
|
+
|
|
+ DataComponentPatch patch = DataComponentPatch.builder()
|
|
+ .set(DataComponents.CUSTOM_NAME, Component.empty())
|
|
+ .set(DataComponents.CUSTOM_DATA, CustomData.of(customData))
|
|
+ .build();
|
|
+
|
|
+ for (int i = 0; i < 13; i++) {
|
|
+ ItemStack button = new ItemStack(Items.STRUCTURE_VOID);
|
|
+ button.applyComponents(patch);
|
|
+ buttons.set(i, button);
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/leavesmc/leaves/bot/BotStatsCounter.java b/src/main/java/org/leavesmc/leaves/bot/BotStatsCounter.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..5bd34353b6ea86cd15ff48b8d6570167f35d75f0
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/leavesmc/leaves/bot/BotStatsCounter.java
|
|
@@ -0,0 +1,38 @@
|
|
+package org.leavesmc.leaves.bot;
|
|
+
|
|
+import com.mojang.datafixers.DataFixer;
|
|
+import net.minecraft.server.MinecraftServer;
|
|
+import net.minecraft.stats.ServerStatsCounter;
|
|
+import net.minecraft.stats.Stat;
|
|
+import net.minecraft.world.entity.player.Player;
|
|
+
|
|
+import java.io.File;
|
|
+
|
|
+public class BotStatsCounter extends ServerStatsCounter {
|
|
+
|
|
+ private static final File UNKOWN_FILE = new File("BOT_STATS_REMOVE_THIS");
|
|
+
|
|
+ public BotStatsCounter(MinecraftServer server) {
|
|
+ super(server, UNKOWN_FILE);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void save() {
|
|
+
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void setValue(Player player, Stat<?> stat, int value) {
|
|
+
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void parseLocal(DataFixer dataFixer, String json) {
|
|
+
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int getValue(Stat<?> stat) {
|
|
+ return 0;
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/leavesmc/leaves/bot/BotUtil.java b/src/main/java/org/leavesmc/leaves/bot/BotUtil.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..0882cd685a6c3a15dd8514e26cc4c31b1d5a8525
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/leavesmc/leaves/bot/BotUtil.java
|
|
@@ -0,0 +1,228 @@
|
|
+package org.leavesmc.leaves.bot;
|
|
+
|
|
+import com.google.common.base.Charsets;
|
|
+import com.google.gson.JsonArray;
|
|
+import com.google.gson.JsonElement;
|
|
+import com.google.gson.JsonObject;
|
|
+import net.minecraft.core.NonNullList;
|
|
+import net.minecraft.nbt.CompoundTag;
|
|
+import net.minecraft.nbt.ListTag;
|
|
+import net.minecraft.nbt.NbtAccounter;
|
|
+import net.minecraft.nbt.NbtIo;
|
|
+import net.minecraft.server.MinecraftServer;
|
|
+import net.minecraft.world.entity.EquipmentSlot;
|
|
+import net.minecraft.world.item.ItemStack;
|
|
+import net.minecraft.world.level.storage.LevelResource;
|
|
+import org.bukkit.Bukkit;
|
|
+import org.bukkit.Location;
|
|
+import org.jetbrains.annotations.NotNull;
|
|
+import org.leavesmc.leaves.LeavesLogger;
|
|
+import org.leavesmc.leaves.bot.agent.Actions;
|
|
+import org.leavesmc.leaves.bot.agent.BotAction;
|
|
+import org.leavesmc.leaves.event.bot.BotCreateEvent;
|
|
+
|
|
+import java.io.File;
|
|
+import java.io.IOException;
|
|
+import java.util.Collection;
|
|
+import java.util.Map;
|
|
+import java.util.UUID;
|
|
+
|
|
+public class BotUtil {
|
|
+
|
|
+ public static void replenishment(@NotNull ItemStack itemStack, NonNullList<ItemStack> itemStackList) {
|
|
+ int count = itemStack.getMaxStackSize() / 2;
|
|
+ if (itemStack.getCount() <= 8 && count > 8) {
|
|
+ for (ItemStack itemStack1 : itemStackList) {
|
|
+ if (itemStack1 == ItemStack.EMPTY || itemStack1 == itemStack) {
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ if (ItemStack.isSameItemSameComponents(itemStack1, itemStack)) {
|
|
+ if (itemStack1.getCount() > count) {
|
|
+ itemStack.setCount(itemStack.getCount() + count);
|
|
+ itemStack1.setCount(itemStack1.getCount() - count);
|
|
+ } else {
|
|
+ itemStack.setCount(itemStack.getCount() + itemStack1.getCount());
|
|
+ itemStack1.setCount(0);
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public static void replaceTool(@NotNull EquipmentSlot slot, @NotNull ServerBot bot) {
|
|
+ ItemStack itemStack = bot.getItemBySlot(slot);
|
|
+ for (int i = 0; i < 36; i++) {
|
|
+ ItemStack itemStack1 = bot.getInventory().getItem(i);
|
|
+ if (itemStack1 == ItemStack.EMPTY || itemStack1 == itemStack) {
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ if (itemStack1.getItem().getClass() == itemStack.getItem().getClass() && !isDamage(itemStack1, 10)) {
|
|
+ ItemStack itemStack2 = itemStack1.copy();
|
|
+ bot.getInventory().setItem(i, itemStack);
|
|
+ bot.setItemSlot(slot, itemStack2);
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ for (int i = 0; i < 36; i++) {
|
|
+ ItemStack itemStack1 = bot.getInventory().getItem(i);
|
|
+ if (itemStack1 == ItemStack.EMPTY && itemStack1 != itemStack) {
|
|
+ bot.getInventory().setItem(i, itemStack);
|
|
+ bot.setItemSlot(slot, ItemStack.EMPTY);
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public static boolean isDamage(@NotNull ItemStack item, int minDamage) {
|
|
+ return item.isDamageableItem() && (item.getMaxDamage() - item.getDamageValue()) <= minDamage;
|
|
+ }
|
|
+
|
|
+ @NotNull
|
|
+ public static JsonObject saveBot(@NotNull ServerBot bot) {
|
|
+ JsonObject fakePlayer = getBotJson(bot);
|
|
+
|
|
+ Collection<BotAction> actions = bot.getBotActions();
|
|
+ JsonArray botActions = new JsonArray();
|
|
+ for (BotAction action : actions) {
|
|
+ JsonObject actionObj = new JsonObject();
|
|
+ actionObj.addProperty("name", action.getName());
|
|
+ actionObj.addProperty("number", String.valueOf(action.getNumber()));
|
|
+ actionObj.addProperty("delay", String.valueOf(action.getTickDelay()));
|
|
+ botActions.add(actionObj);
|
|
+ }
|
|
+ fakePlayer.add("actions", botActions);
|
|
+
|
|
+ CompoundTag invnbt = new CompoundTag();
|
|
+ invnbt.put("Inventory", bot.getInventory().save(new ListTag()));
|
|
+
|
|
+ File file = MinecraftServer.getServer().getWorldPath(LevelResource.ROOT).resolve("fakeplayer/" + bot.getStringUUID() + ".dat").toFile();
|
|
+ File parent = file.getParentFile();
|
|
+ try {
|
|
+ if (!parent.exists() || !parent.isDirectory()) {
|
|
+ if (!parent.mkdirs()) {
|
|
+ throw new IOException("Failed to create directory: " + parent);
|
|
+ }
|
|
+ }
|
|
+ if (file.exists() && file.isFile()) {
|
|
+ if (!file.delete()) {
|
|
+ throw new IOException("Failed to delete file: " + file);
|
|
+ }
|
|
+ }
|
|
+ if (!file.createNewFile()) {
|
|
+ throw new IOException("Failed to create nbt file: " + file);
|
|
+ }
|
|
+ NbtIo.writeCompressed(invnbt, file.toPath());
|
|
+ } catch (IOException e) {
|
|
+ LeavesLogger.LOGGER.warning("Failed to save fakeplayer inv: ", e);
|
|
+ }
|
|
+
|
|
+ return fakePlayer;
|
|
+ }
|
|
+
|
|
+ private static @NotNull JsonObject getBotJson(@NotNull ServerBot bot) {
|
|
+ double pos_x = bot.getX();
|
|
+ double pos_y = bot.getY();
|
|
+ double pos_z = bot.getZ();
|
|
+ float yaw = bot.getYRot();
|
|
+ float pitch = bot.getXRot();
|
|
+ String dimension = bot.getLocation().getWorld().getName();
|
|
+ String skin = bot.createState.skinName;
|
|
+ String realName = bot.createState.getRealName();
|
|
+ String name = bot.createState.getName();
|
|
+ String[] skinValue = bot.createState.skin;
|
|
+
|
|
+ JsonObject fakePlayer = new JsonObject();
|
|
+ fakePlayer.addProperty("pos_x", pos_x);
|
|
+ fakePlayer.addProperty("pos_y", pos_y);
|
|
+ fakePlayer.addProperty("pos_z", pos_z);
|
|
+ fakePlayer.addProperty("yaw", yaw);
|
|
+ fakePlayer.addProperty("pitch", pitch);
|
|
+ fakePlayer.addProperty("dimension", dimension);
|
|
+ fakePlayer.addProperty("skin", skin);
|
|
+ fakePlayer.addProperty("real_name", realName);
|
|
+ fakePlayer.addProperty("name", name);
|
|
+
|
|
+ if (skinValue != null) {
|
|
+ JsonArray jsonArray = new JsonArray();
|
|
+ for (String str : skinValue) {
|
|
+ jsonArray.add(str);
|
|
+ }
|
|
+ fakePlayer.add("skin_value", jsonArray);
|
|
+ }
|
|
+
|
|
+ return fakePlayer;
|
|
+ }
|
|
+
|
|
+ public static void loadBot(Map.@NotNull Entry<String, JsonElement> entry) {
|
|
+ JsonObject fakePlayer = entry.getValue().getAsJsonObject();
|
|
+
|
|
+ String name = entry.getKey();
|
|
+ String realName = name;
|
|
+ if (fakePlayer.has("real_name")) {
|
|
+ realName = fakePlayer.get("real_name").getAsString();
|
|
+ name = fakePlayer.get("name").getAsString();
|
|
+ }
|
|
+
|
|
+ double pos_x = fakePlayer.get("pos_x").getAsDouble();
|
|
+ double pos_y = fakePlayer.get("pos_y").getAsDouble();
|
|
+ double pos_z = fakePlayer.get("pos_z").getAsDouble();
|
|
+ float yaw = fakePlayer.get("yaw").getAsFloat();
|
|
+ float pitch = fakePlayer.get("pitch").getAsFloat();
|
|
+ String dimension = fakePlayer.get("dimension").getAsString();
|
|
+ String skin = fakePlayer.get("skin").getAsString();
|
|
+
|
|
+ String[] skinValue = null;
|
|
+ if (fakePlayer.has("skin_value")) {
|
|
+ JsonArray jsonArray = fakePlayer.get("skin_value").getAsJsonArray();
|
|
+ skinValue = new String[jsonArray.size()];
|
|
+ for (int i = 0; i < jsonArray.size(); i++) {
|
|
+ skinValue[i] = jsonArray.get(i).getAsString();
|
|
+ }
|
|
+ }
|
|
+
|
|
+ Location location = new Location(Bukkit.getWorld(dimension), pos_x, pos_y, pos_z, yaw, pitch);
|
|
+ ServerBot.BotCreateState state = new ServerBot.BotCreateState(location, name, realName, skin, skinValue, BotCreateEvent.CreateReason.INTERNAL, null);
|
|
+
|
|
+ ListTag inv = null;
|
|
+ File file = MinecraftServer.getServer().getWorldPath(LevelResource.ROOT).resolve("fakeplayer/" + getBotUUID(state) + ".dat").toFile();
|
|
+ if (file.exists()) {
|
|
+ try {
|
|
+ CompoundTag nbt = NbtIo.readCompressed(file.toPath(), NbtAccounter.unlimitedHeap());
|
|
+ inv = nbt.getList("Inventory", 10);
|
|
+ } catch (IOException e) {
|
|
+ LeavesLogger.LOGGER.warning("Failed to load inventory: ", e);
|
|
+ }
|
|
+ if (!file.delete()) {
|
|
+ LeavesLogger.LOGGER.warning("Failed to delete file: " + file);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ final JsonArray finalActions = fakePlayer.get("actions").getAsJsonArray();
|
|
+ final ListTag finalInv = inv;
|
|
+ state.createNow(serverBot -> {
|
|
+ if (finalInv != null) {
|
|
+ serverBot.getInventory().load(finalInv);
|
|
+ }
|
|
+
|
|
+ for (JsonElement element : finalActions) {
|
|
+ JsonObject actionObj = element.getAsJsonObject();
|
|
+ BotAction action = Actions.getForName(actionObj.get("name").getAsString());
|
|
+ if (action != null) {
|
|
+ BotAction newAction = action.getNew(serverBot,
|
|
+ action.getArgument().parse(0, new String[]{actionObj.get("delay").getAsString(), actionObj.get("number").getAsString()})
|
|
+ );
|
|
+ serverBot.setBotAction(newAction);
|
|
+ }
|
|
+ }
|
|
+ });
|
|
+ }
|
|
+
|
|
+ @NotNull
|
|
+ public static UUID getBotUUID(ServerBot.@NotNull BotCreateState state) {
|
|
+ return UUID.nameUUIDFromBytes(("Fakeplayer:" + state.getRealName()).getBytes(Charsets.UTF_8));
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/leavesmc/leaves/bot/MojangAPI.java b/src/main/java/org/leavesmc/leaves/bot/MojangAPI.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..0db337866c71283464d026a4f230016b31d1a8cd
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/leavesmc/leaves/bot/MojangAPI.java
|
|
@@ -0,0 +1,41 @@
|
|
+package org.leavesmc.leaves.bot;
|
|
+
|
|
+import com.google.gson.JsonObject;
|
|
+import com.google.gson.JsonParser;
|
|
+
|
|
+import java.io.IOException;
|
|
+import java.io.InputStreamReader;
|
|
+import java.net.URL;
|
|
+import java.util.HashMap;
|
|
+import java.util.Map;
|
|
+
|
|
+public class MojangAPI {
|
|
+
|
|
+ private static final boolean CACHE_ENABLED = false;
|
|
+
|
|
+ private static final Map<String, String[]> CACHE = new HashMap<>();
|
|
+
|
|
+ public static String[] getSkin(String name) {
|
|
+ if (CACHE_ENABLED && CACHE.containsKey(name)) {
|
|
+ return CACHE.get(name);
|
|
+ }
|
|
+
|
|
+ String[] values = pullFromAPI(name);
|
|
+ CACHE.put(name, values);
|
|
+ return values;
|
|
+ }
|
|
+
|
|
+ // Laggggggggggggggggggggggggggggggggggggggggg
|
|
+ public static String[] pullFromAPI(String name) {
|
|
+ try {
|
|
+ String uuid = new JsonParser().parse(new InputStreamReader(new URL("https://api.mojang.com/users/profiles/minecraft/" + name)
|
|
+ .openStream())).getAsJsonObject().get("id").getAsString();
|
|
+ JsonObject property = new JsonParser()
|
|
+ .parse(new InputStreamReader(new URL("https://sessionserver.mojang.com/session/minecraft/profile/" + uuid + "?unsigned=false")
|
|
+ .openStream())).getAsJsonObject().get("properties").getAsJsonArray().get(0).getAsJsonObject();
|
|
+ return new String[] {property.get("value").getAsString(), property.get("signature").getAsString()};
|
|
+ } catch (IOException | IllegalStateException e) {
|
|
+ return null;
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/leavesmc/leaves/bot/ServerBot.java b/src/main/java/org/leavesmc/leaves/bot/ServerBot.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..462d58ad184ebe6bd6f161bff1481745b5723cf0
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/leavesmc/leaves/bot/ServerBot.java
|
|
@@ -0,0 +1,782 @@
|
|
+package org.leavesmc.leaves.bot;
|
|
+
|
|
+import com.google.common.collect.Lists;
|
|
+import com.google.gson.Gson;
|
|
+import com.google.gson.JsonElement;
|
|
+import com.google.gson.JsonObject;
|
|
+import com.mojang.authlib.GameProfile;
|
|
+import com.mojang.authlib.properties.Property;
|
|
+import io.papermc.paper.adventure.PaperAdventure;
|
|
+import io.papermc.paper.event.entity.EntityKnockbackEvent;
|
|
+import net.kyori.adventure.text.format.NamedTextColor;
|
|
+import net.kyori.adventure.text.format.Style;
|
|
+import net.minecraft.Util;
|
|
+import net.minecraft.core.BlockPos;
|
|
+import net.minecraft.network.Connection;
|
|
+import net.minecraft.network.PacketSendListener;
|
|
+import net.minecraft.network.chat.Component;
|
|
+import net.minecraft.network.protocol.Packet;
|
|
+import net.minecraft.network.protocol.PacketFlow;
|
|
+import net.minecraft.network.protocol.game.ClientboundPlayerInfoRemovePacket;
|
|
+import net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket;
|
|
+import net.minecraft.network.protocol.game.ClientboundRemoveEntitiesPacket;
|
|
+import net.minecraft.network.protocol.game.ClientboundRotateHeadPacket;
|
|
+import net.minecraft.network.syncher.EntityDataAccessor;
|
|
+import net.minecraft.network.syncher.EntityDataSerializers;
|
|
+import net.minecraft.server.MinecraftServer;
|
|
+import net.minecraft.server.level.ChunkMap;
|
|
+import net.minecraft.server.level.ClientInformation;
|
|
+import net.minecraft.server.level.ServerLevel;
|
|
+import net.minecraft.server.level.ServerPlayer;
|
|
+import net.minecraft.server.network.CommonListenerCookie;
|
|
+import net.minecraft.server.network.ServerGamePacketListenerImpl;
|
|
+import net.minecraft.server.network.ServerPlayerConnection;
|
|
+import net.minecraft.stats.ServerStatsCounter;
|
|
+import net.minecraft.world.InteractionHand;
|
|
+import net.minecraft.world.InteractionResult;
|
|
+import net.minecraft.world.SimpleMenuProvider;
|
|
+import net.minecraft.world.damagesource.DamageSource;
|
|
+import net.minecraft.world.entity.Entity;
|
|
+import net.minecraft.world.entity.EntityType;
|
|
+import net.minecraft.world.entity.EquipmentSlot;
|
|
+import net.minecraft.world.entity.ai.attributes.Attributes;
|
|
+import net.minecraft.world.entity.item.ItemEntity;
|
|
+import net.minecraft.world.entity.player.Player;
|
|
+import net.minecraft.world.inventory.ChestMenu;
|
|
+import net.minecraft.world.level.block.state.BlockState;
|
|
+import net.minecraft.world.level.gameevent.GameEvent;
|
|
+import net.minecraft.world.level.portal.DimensionTransition;
|
|
+import net.minecraft.world.level.storage.LevelResource;
|
|
+import net.minecraft.world.phys.AABB;
|
|
+import net.minecraft.world.phys.EntityHitResult;
|
|
+import net.minecraft.world.phys.Vec3;
|
|
+import org.bukkit.Bukkit;
|
|
+import org.bukkit.Location;
|
|
+import org.bukkit.Material;
|
|
+import org.bukkit.command.CommandSender;
|
|
+import org.bukkit.craftbukkit.CraftWorld;
|
|
+import org.bukkit.craftbukkit.scheduler.CraftScheduler;
|
|
+import org.bukkit.event.entity.CreatureSpawnEvent;
|
|
+import org.bukkit.util.Vector;
|
|
+import org.jetbrains.annotations.NotNull;
|
|
+import org.jetbrains.annotations.Nullable;
|
|
+import org.leavesmc.leaves.LeavesConfig;
|
|
+import org.leavesmc.leaves.LeavesLogger;
|
|
+import org.leavesmc.leaves.bot.agent.BotAction;
|
|
+import org.leavesmc.leaves.bot.agent.actions.StopAction;
|
|
+import org.leavesmc.leaves.entity.Bot;
|
|
+import org.leavesmc.leaves.entity.CraftBot;
|
|
+import org.leavesmc.leaves.event.bot.BotCreateEvent;
|
|
+import org.leavesmc.leaves.event.bot.BotInventoryOpenEvent;
|
|
+import org.leavesmc.leaves.event.bot.BotJoinEvent;
|
|
+import org.leavesmc.leaves.event.bot.BotRemoveEvent;
|
|
+import org.leavesmc.leaves.util.MathUtils;
|
|
+
|
|
+import java.io.BufferedReader;
|
|
+import java.io.BufferedWriter;
|
|
+import java.io.File;
|
|
+import java.io.IOException;
|
|
+import java.nio.charset.StandardCharsets;
|
|
+import java.nio.file.Files;
|
|
+import java.util.Collection;
|
|
+import java.util.EnumSet;
|
|
+import java.util.HashMap;
|
|
+import java.util.Iterator;
|
|
+import java.util.List;
|
|
+import java.util.Map;
|
|
+import java.util.UUID;
|
|
+import java.util.concurrent.CopyOnWriteArrayList;
|
|
+import java.util.function.Consumer;
|
|
+import java.util.function.Predicate;
|
|
+
|
|
+// TODO remake all
|
|
+public class ServerBot extends ServerPlayer {
|
|
+
|
|
+ private final Map<String, BotAction> actions;
|
|
+ private final boolean removeOnDeath;
|
|
+ private final int tracingRange;
|
|
+
|
|
+ private Vec3 velocity;
|
|
+ private int fireTicks;
|
|
+ private int jumpTicks;
|
|
+ private int noFallTicks;
|
|
+ public boolean waterSwim;
|
|
+ private Vec3 knockback;
|
|
+ public BotCreateState createState;
|
|
+ public UUID createPlayer;
|
|
+
|
|
+ private final ServerStatsCounter stats;
|
|
+ private final BotInventoryContainer container;
|
|
+
|
|
+ private static final List<ServerBot> bots = new CopyOnWriteArrayList<>();
|
|
+
|
|
+ public boolean spawnPhantom;
|
|
+ public int notSleepTicks;
|
|
+ public boolean alwaysSendData;
|
|
+
|
|
+ public int removeTaskId = -1;
|
|
+
|
|
+ private ServerBot(MinecraftServer server, ServerLevel world, GameProfile profile) {
|
|
+ super(server, world, profile, ClientInformation.createDefault());
|
|
+ this.entityData.set(new EntityDataAccessor<>(16, EntityDataSerializers.INT), 0xFF);
|
|
+ this.entityData.set(Player.DATA_PLAYER_MODE_CUSTOMISATION, (byte) -2);
|
|
+
|
|
+ this.gameMode = new ServerBotGameMode(this);
|
|
+ this.velocity = new Vec3(this.xxa, this.yya, this.zza);
|
|
+ this.noFallTicks = 60;
|
|
+ this.fireTicks = 0;
|
|
+ this.actions = new HashMap<>();
|
|
+ this.removeOnDeath = true;
|
|
+ this.stats = new BotStatsCounter(server);
|
|
+ this.container = new BotInventoryContainer(this);
|
|
+ this.waterSwim = true;
|
|
+ this.knockback = Vec3.ZERO;
|
|
+ this.tracingRange = world.spigotConfig.playerTrackingRange * world.spigotConfig.playerTrackingRange;
|
|
+ this.notSleepTicks = 0;
|
|
+
|
|
+ this.fauxSleeping = LeavesConfig.fakeplayerSkipSleep;
|
|
+ this.spawnPhantom = LeavesConfig.fakeplayerSpawnPhantom;
|
|
+ this.alwaysSendData = LeavesConfig.alwaysSendFakeplayerData;
|
|
+ }
|
|
+
|
|
+ public static ServerBot createBot(@NotNull BotCreateState state) {
|
|
+ if (!isCreateLegal(state.name)) {
|
|
+ return null;
|
|
+ }
|
|
+
|
|
+ MinecraftServer server = MinecraftServer.getServer();
|
|
+
|
|
+ BotCreateEvent event = new BotCreateEvent(state.name, state.skinName, state.loc, state.createReason, state.creator);
|
|
+ server.server.getPluginManager().callEvent(event);
|
|
+
|
|
+ if (event.isCancelled()) {
|
|
+ return null;
|
|
+ }
|
|
+
|
|
+ Location location = event.getCreateLocation();
|
|
+
|
|
+ ServerLevel world = ((CraftWorld) location.getWorld()).getHandle();
|
|
+ CustomGameProfile profile = new CustomGameProfile(BotUtil.getBotUUID(state), state.name, state.skin);
|
|
+
|
|
+ ServerBot bot = new ServerBot(server, world, profile);
|
|
+
|
|
+ bot.connection = new ServerGamePacketListenerImpl(server, new Connection(PacketFlow.SERVERBOUND) { // ?
|
|
+ @Override
|
|
+ public void send(@NotNull Packet<?> packet) {
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void send(@NotNull Packet<?> packet, @Nullable PacketSendListener packetsendlistener) {
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void send(@NotNull Packet<?> packet, @Nullable PacketSendListener callbacks, boolean flush) {
|
|
+ }
|
|
+ }, bot, CommonListenerCookie.createInitial(profile, false));
|
|
+ bot.isRealPlayer = true;
|
|
+ bot.createState = state;
|
|
+ if (event.getCreator().isPresent() && event.getCreator().get() instanceof org.bukkit.entity.Player player) {
|
|
+ bot.createPlayer = player.getUniqueId();
|
|
+ }
|
|
+
|
|
+ bot.teleportTo(location.getX(), location.getY(), location.getZ());
|
|
+ bot.setRot(location.getYaw(), location.getPitch());
|
|
+ world.addFreshEntity(bot, CreatureSpawnEvent.SpawnReason.COMMAND);
|
|
+
|
|
+ bot.renderAll();
|
|
+ server.getPlayerList().addNewBot(bot);
|
|
+ bots.add(bot);
|
|
+
|
|
+ BotJoinEvent event1 = new BotJoinEvent(bot.getBukkitPlayer(), PaperAdventure.asAdventure(Component.translatable("multiplayer.player.joined", bot.getDisplayName())).style(Style.style(NamedTextColor.YELLOW)));
|
|
+ server.server.getPluginManager().callEvent(event1);
|
|
+
|
|
+ net.kyori.adventure.text.Component joinMessage = event1.joinMessage();
|
|
+ if (joinMessage != null && !joinMessage.equals(net.kyori.adventure.text.Component.empty())) {
|
|
+ server.getPlayerList().broadcastSystemMessage(PaperAdventure.asVanilla(joinMessage), false);
|
|
+ }
|
|
+
|
|
+ return bot;
|
|
+ }
|
|
+
|
|
+ public static boolean isCreateLegal(@NotNull String name) {
|
|
+ if (!name.matches("^[a-zA-Z0-9_]{4,16}$")) {
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ if (Bukkit.getPlayerExact(name) != null || ServerBot.getBot(name) != null) {
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ if (org.leavesmc.leaves.LeavesConfig.unableFakeplayerNames.contains(name)) {
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ return ServerBot.getBots().size() < org.leavesmc.leaves.LeavesConfig.fakeplayerLimit;
|
|
+ }
|
|
+
|
|
+ public void renderAll() {
|
|
+ MinecraftServer.getServer().getPlayerList().getPlayers().forEach(
|
|
+ player -> {
|
|
+ this.sendPlayerInfo(player);
|
|
+ this.sendFakeData(player.connection, false);
|
|
+ }
|
|
+ );
|
|
+ }
|
|
+
|
|
+ public void sendPlayerInfo(ServerPlayer player) {
|
|
+ player.connection.send(new ClientboundPlayerInfoUpdatePacket(EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.ADD_PLAYER, ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LISTED, ClientboundPlayerInfoUpdatePacket.Action.UPDATE_DISPLAY_NAME), List.of(this)));
|
|
+ }
|
|
+
|
|
+ public boolean needSendFakeData(ServerPlayer player) {
|
|
+ return alwaysSendData && (player.level() == this.level() && player.position().distanceToSqr(this.position()) > this.tracingRange);
|
|
+ }
|
|
+
|
|
+ public void sendFakeDataIfNeed(ServerPlayer player, boolean login) {
|
|
+ if (needSendFakeData(player)) {
|
|
+ this.sendFakeData(player.connection, login);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public void sendFakeData(ServerPlayerConnection playerConnection, boolean login) {
|
|
+ ChunkMap.TrackedEntity entityTracker = ((ServerLevel) this.level()).getChunkSource().chunkMap.entityMap.get(this.getId());
|
|
+
|
|
+ if (entityTracker == null) {
|
|
+ LeavesLogger.LOGGER.warning("Fakeplayer cant get entity tracker for " + this.getId());
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ playerConnection.send(this.getAddEntityPacket(entityTracker.serverEntity));
|
|
+ if (login) {
|
|
+ Bukkit.getScheduler().runTaskLater(CraftScheduler.MINECRAFT, () -> {
|
|
+ playerConnection.send(new ClientboundRotateHeadPacket(this, (byte) ((getYRot() * 256f) / 360f)));
|
|
+ }, 10);
|
|
+ } else {
|
|
+ playerConnection.send(new ClientboundRotateHeadPacket(this, (byte) ((getYRot() * 256f) / 360f)));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private void sendPacket(Packet<?> packet) {
|
|
+ MinecraftServer.getServer().getPlayerList().getPlayers().forEach(
|
|
+ player -> player.connection.send(packet)
|
|
+ );
|
|
+ }
|
|
+
|
|
+ // die check start
|
|
+ @Override
|
|
+ public void die(@NotNull DamageSource damageSource) {
|
|
+ if (removeOnDeath) {
|
|
+ onRemove(BotRemoveEvent.RemoveReason.DEATH);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public void onRemove(BotRemoveEvent.RemoveReason reason) {
|
|
+ onRemove(reason, null);
|
|
+ }
|
|
+
|
|
+ public void onRemove(BotRemoveEvent.RemoveReason reason, @Nullable CommandSender remover) {
|
|
+ BotRemoveEvent event = new BotRemoveEvent(this.getBukkitPlayer(), reason, remover, PaperAdventure.asAdventure(Component.translatable("multiplayer.player.left", this.getDisplayName())).style(Style.style(NamedTextColor.YELLOW)));
|
|
+ this.server.server.getPluginManager().callEvent(event);
|
|
+
|
|
+ if (event.isCancelled()) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ this.dropAll();
|
|
+ if (this.removeTaskId != -1) {
|
|
+ Bukkit.getScheduler().cancelTask(this.removeTaskId);
|
|
+ this.removeTaskId = -1;
|
|
+ }
|
|
+ bots.remove(this);
|
|
+ this.server.getPlayerList().removeBot(this);
|
|
+ this.remove(RemovalReason.DISCARDED);
|
|
+ this.setDead();
|
|
+ this.removeTab();
|
|
+
|
|
+ net.kyori.adventure.text.Component removeMessage = event.removeMessage();
|
|
+ if (removeMessage != null && !removeMessage.equals(net.kyori.adventure.text.Component.empty())) {
|
|
+ server.getPlayerList().broadcastSystemMessage(PaperAdventure.asVanilla(removeMessage), false);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private void removeTab() {
|
|
+ sendPacket(new ClientboundPlayerInfoRemovePacket(List.of(this.getUUID())));
|
|
+ }
|
|
+
|
|
+ private void setDead() {
|
|
+ sendPacket(new ClientboundRemoveEntitiesPacket(getId()));
|
|
+ this.dead = true;
|
|
+ this.inventoryMenu.removed(this);
|
|
+ this.containerMenu.removed(this);
|
|
+ }
|
|
+
|
|
+ // die check end
|
|
+
|
|
+ @Nullable
|
|
+ @Override
|
|
+ public Entity changeDimension(@NotNull DimensionTransition teleportTarget) {
|
|
+ return null; // disable dimension change
|
|
+ }
|
|
+
|
|
+ public Bot getBukkitPlayer() {
|
|
+ return getBukkitEntity();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ @NotNull
|
|
+ public CraftBot getBukkitEntity() {
|
|
+ return (CraftBot) super.getBukkitEntity();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isInWater() {
|
|
+ Location loc = getLocation();
|
|
+ for (int i = 0; i <= 2; i++) {
|
|
+ Material type = loc.getBlock().getType();
|
|
+ if (type == Material.WATER || type == Material.LAVA) {
|
|
+ return true;
|
|
+ }
|
|
+ loc.add(0, 0.9, 0);
|
|
+ }
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void tick() {
|
|
+ super.tick();
|
|
+ this.doTick();
|
|
+
|
|
+ if (!isAlive()) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (spawnPhantom) {
|
|
+ notSleepTicks++;
|
|
+ }
|
|
+
|
|
+ if (fireTicks > 0) {
|
|
+ --fireTicks;
|
|
+ }
|
|
+ if (jumpTicks > 0) {
|
|
+ --jumpTicks;
|
|
+ }
|
|
+ if (noFallTicks > 0) {
|
|
+ --noFallTicks;
|
|
+ }
|
|
+ if (takeXpDelay > 0) {
|
|
+ --takeXpDelay;
|
|
+ }
|
|
+
|
|
+ this.updateLocation();
|
|
+ this.updatePlayerPose();
|
|
+ this.serverLevel().getChunkSource().move(this);
|
|
+
|
|
+ if (server.getTickCount() % 20 == 0) {
|
|
+ float health = getHealth();
|
|
+ float maxHealth = getMaxHealth();
|
|
+ float regenAmount = (float) (LeavesConfig.fakeplayerRegenAmount * 20);
|
|
+ float amount;
|
|
+
|
|
+ if (health < maxHealth - regenAmount) {
|
|
+ amount = health + regenAmount;
|
|
+ } else {
|
|
+ amount = maxHealth;
|
|
+ }
|
|
+
|
|
+ this.setHealth(amount);
|
|
+ }
|
|
+
|
|
+ BlockPos blockposition = this.getOnPosLegacy();
|
|
+ BlockState iblockdata = this.level().getBlockState(blockposition);
|
|
+ Vec3 vec3d1 = this.collide(velocity);
|
|
+ this.checkFallDamage(vec3d1.y, this.onGround(), iblockdata, blockposition);
|
|
+
|
|
+ ++this.attackStrengthTicker;
|
|
+
|
|
+ if (this.getHealth() > 0.0F) {
|
|
+ AABB axisalignedbb;
|
|
+
|
|
+ if (this.isPassenger() && !this.getVehicle().isRemoved()) {
|
|
+ axisalignedbb = this.getBoundingBox().minmax(this.getVehicle().getBoundingBox()).inflate(1.0D, 0.0D, 1.0D);
|
|
+ } else {
|
|
+ axisalignedbb = this.getBoundingBox().inflate(1.0D, 0.5D, 1.0D);
|
|
+ }
|
|
+
|
|
+ List<Entity> list = this.level().getEntities(this, axisalignedbb);
|
|
+ List<Entity> list1 = Lists.newArrayList();
|
|
+
|
|
+ for (Entity entity : list) {
|
|
+ if (entity.getType() == EntityType.EXPERIENCE_ORB) {
|
|
+ list1.add(entity);
|
|
+ } else if (!entity.isRemoved()) {
|
|
+ this.touch(entity);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (!list1.isEmpty()) {
|
|
+ this.touch(Util.getRandom(list1, this.random));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ Iterator<Map.Entry<String, BotAction>> iterator = actions.entrySet().iterator();
|
|
+ while (iterator.hasNext()) {
|
|
+ Map.Entry<String, BotAction> entry = iterator.next();
|
|
+ if (entry.getValue().isCancel()) {
|
|
+ iterator.remove();
|
|
+ } else {
|
|
+ entry.getValue().tryTick(this);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public Entity getTargetEntity(int maxDistance, Predicate<? super Entity> predicate) {
|
|
+ List<Entity> entities = this.level().getEntities((Entity) null, this.getBoundingBox(), (e -> e != this && (predicate == null || predicate.test(e))));
|
|
+ if (!entities.isEmpty()) {
|
|
+ return entities.getFirst();
|
|
+ } else {
|
|
+ EntityHitResult result = this.getBukkitEntity().rayTraceEntity(maxDistance, false);
|
|
+ if (result != null && (predicate == null || predicate.test(result.getEntity()))) {
|
|
+ return result.getEntity();
|
|
+ }
|
|
+ }
|
|
+ return null;
|
|
+ }
|
|
+
|
|
+ private void touch(@NotNull Entity entity) {
|
|
+ entity.playerTouch(this);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void onItemPickup(@NotNull ItemEntity item) {
|
|
+ super.onItemPickup(item);
|
|
+ this.updateItemInHand(InteractionHand.MAIN_HAND);
|
|
+ }
|
|
+
|
|
+ public void updateItemInHand(InteractionHand hand) {
|
|
+ net.minecraft.world.item.ItemStack item = getItemInHand(hand);
|
|
+
|
|
+ if (!item.isEmpty()) {
|
|
+ BotUtil.replenishment(item, getInventory().items);
|
|
+ if (BotUtil.isDamage(item, 10)) {
|
|
+ BotUtil.replaceTool(hand == InteractionHand.MAIN_HAND ? EquipmentSlot.MAINHAND : EquipmentSlot.OFFHAND, this);
|
|
+ }
|
|
+ }
|
|
+ detectEquipmentUpdatesPublic();
|
|
+ }
|
|
+
|
|
+ public long getEatStartTime() {
|
|
+ return eatStartTime;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void checkFallDamage(double heightDifference, boolean onGround, @NotNull BlockState state, @NotNull BlockPos landedPosition) {
|
|
+ if (onGround) {
|
|
+ if (this.fallDistance > 0.0F) {
|
|
+ state.getBlock().fallOn(this.level(), state, landedPosition, this, this.fallDistance);
|
|
+ this.level().gameEvent(GameEvent.HIT_GROUND, this.position(), GameEvent.Context.of(this, this.mainSupportingBlockPos.map((blockposition1) -> {
|
|
+ return this.level().getBlockState(blockposition1);
|
|
+ }).orElse(state)));
|
|
+ }
|
|
+
|
|
+ this.resetFallDistance();
|
|
+ } else if (heightDifference < 0.0D) {
|
|
+ this.fallDistance -= (float) heightDifference;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void doTick() {
|
|
+ if (this.hurtTime > 0) {
|
|
+ this.hurtTime -= 1;
|
|
+ }
|
|
+
|
|
+ baseTick();
|
|
+
|
|
+ this.lerpSteps = (int) this.zza;
|
|
+ this.animStep = this.run;
|
|
+ this.yRotO = this.getYRot();
|
|
+ this.xRotO = this.getXRot();
|
|
+ }
|
|
+
|
|
+ public Location getLocation() {
|
|
+ return getBukkitPlayer().getLocation();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void knockback(double strength, double x, double z, @Nullable Entity attacker, @NotNull EntityKnockbackEvent.Cause cause) {
|
|
+ strength *= 1.0D - this.getAttributeValue(Attributes.KNOCKBACK_RESISTANCE);
|
|
+ if (strength > 0.0D) {
|
|
+ this.hasImpulse = true;
|
|
+ Vec3 vec3d = this.getDeltaMovement();
|
|
+ Vec3 vec3d1 = (new Vec3(x, 0.0D, z)).normalize().scale(strength);
|
|
+ knockback = new Vec3(vec3d.x / 2.0D - vec3d1.x, this.onGround() ? Math.min(0.4D, vec3d.y / 2.0D + strength) : vec3d.y, vec3d.z / 2.0D - vec3d1.z);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private void updateLocation() {
|
|
+ this.velocity = new Vec3(this.xxa, this.yya, this.zza);
|
|
+
|
|
+ if (waterSwim && isInWater()) {
|
|
+ this.addDeltaMovement(new Vec3(0, 0.05, 0));
|
|
+ }
|
|
+ this.addDeltaMovement(knockback);
|
|
+ knockback = Vec3.ZERO;
|
|
+
|
|
+ this.travel(this.velocity);
|
|
+ }
|
|
+
|
|
+ public void faceLocation(@NotNull Location loc) {
|
|
+ look(loc.toVector().subtract(getLocation().toVector()), false);
|
|
+ }
|
|
+
|
|
+ public void look(Vector dir, boolean keepYaw) {
|
|
+ float yaw, pitch;
|
|
+
|
|
+ if (keepYaw) {
|
|
+ yaw = this.getYHeadRot();
|
|
+ pitch = MathUtils.fetchPitch(dir);
|
|
+ } else {
|
|
+ float[] vals = MathUtils.fetchYawPitch(dir);
|
|
+ yaw = vals[0];
|
|
+ pitch = vals[1];
|
|
+
|
|
+ sendPacket(new ClientboundRotateHeadPacket(this, (byte) (yaw * 256 / 360f)));
|
|
+ }
|
|
+
|
|
+ this.setRot(yaw, pitch);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void setRot(float yaw, float pitch) {
|
|
+ this.getBukkitEntity().setRotation(yaw, pitch);
|
|
+ }
|
|
+
|
|
+ public void attack(@NotNull Entity target) {
|
|
+ super.attack(target);
|
|
+ swing(InteractionHand.MAIN_HAND);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void jumpFromGround() {
|
|
+ double jumpPower = (double) this.getJumpPower() + this.getJumpBoostPower();
|
|
+ this.addDeltaMovement(new Vec3(0, jumpPower, 0));
|
|
+ }
|
|
+
|
|
+ public void dropAll() {
|
|
+ getInventory().dropAll();
|
|
+ detectEquipmentUpdatesPublic();
|
|
+ }
|
|
+
|
|
+ public void setBotAction(BotAction action) {
|
|
+ if (!LeavesConfig.fakeplayerUseAction) {
|
|
+ return;
|
|
+ }
|
|
+ if (action instanceof StopAction) {
|
|
+ this.actions.clear();
|
|
+ }
|
|
+ action.init();
|
|
+ this.actions.put(action.getName(), action);
|
|
+ }
|
|
+
|
|
+ public Collection<BotAction> getBotActions() {
|
|
+ return actions.values();
|
|
+ }
|
|
+
|
|
+ public BotAction getBotAction(String name) {
|
|
+ return actions.get(name);
|
|
+ }
|
|
+
|
|
+ @Deprecated
|
|
+ public BotAction getBotAction() {
|
|
+ return null;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public @NotNull ServerStatsCounter getStats() {
|
|
+ return stats;
|
|
+ }
|
|
+
|
|
+ public BotInventoryContainer getContainer() {
|
|
+ return container;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public @NotNull InteractionResult interact(@NotNull Player player, @NotNull InteractionHand hand) {
|
|
+ if (LeavesConfig.openFakeplayerInventory) {
|
|
+ if (player instanceof ServerPlayer player1 && player.getMainHandItem().isEmpty()) {
|
|
+ BotInventoryOpenEvent event = new BotInventoryOpenEvent(this.getBukkitEntity(), player1.getBukkitEntity());
|
|
+ server.server.getPluginManager().callEvent(event);
|
|
+ if (!event.isCancelled()) {
|
|
+ Component menuName = this.getDisplayName();
|
|
+ player.openMenu(new SimpleMenuProvider((i, inventory, p) -> ChestMenu.sixRows(i, inventory, container), menuName != null ? menuName : Component.literal(this.createState.name)));
|
|
+ return InteractionResult.SUCCESS;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ return super.interact(player, hand);
|
|
+ }
|
|
+
|
|
+ public static ServerBot getBot(ServerPlayer player) {
|
|
+ ServerBot bot = null;
|
|
+ for (ServerBot b : bots) {
|
|
+ if (b.getId() == player.getId()) {
|
|
+ bot = b;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ return bot;
|
|
+ }
|
|
+
|
|
+ public static ServerBot getBot(String name) {
|
|
+ ServerBot bot = null;
|
|
+ for (ServerBot b : bots) {
|
|
+ if (b.getName().getString().equals(name)) {
|
|
+ bot = b;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ return bot;
|
|
+ }
|
|
+
|
|
+ public static ServerBot getBot(UUID uuid) {
|
|
+ ServerBot bot = null;
|
|
+ for (ServerBot b : bots) {
|
|
+ if (b.uuid == uuid) {
|
|
+ bot = b;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ return bot;
|
|
+ }
|
|
+
|
|
+ public static void saveOrRemoveAllBot() {
|
|
+ if (LeavesConfig.fakeplayerSupport && LeavesConfig.fakeplayerResident) {
|
|
+ JsonObject fakePlayerList = new JsonObject();
|
|
+ bots.forEach(bot -> fakePlayerList.add(bot.createState.realName, BotUtil.saveBot(bot)));
|
|
+ File file = MinecraftServer.getServer().getWorldPath(LevelResource.ROOT).resolve("fake_player.leaves.json").toFile();
|
|
+ if (!file.isFile()) {
|
|
+ try {
|
|
+ if (!file.createNewFile()) {
|
|
+ throw new IOException("Failed to create fakeplayer file: " + file);
|
|
+ }
|
|
+ } catch (IOException e) {
|
|
+ LeavesLogger.LOGGER.severe("Failed to save fakeplayer", e);
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+ try (BufferedWriter bfw = Files.newBufferedWriter(file.toPath(), StandardCharsets.UTF_8)) {
|
|
+ bfw.write(new Gson().toJson(fakePlayerList));
|
|
+ } catch (IOException e) {
|
|
+ LeavesLogger.LOGGER.severe("Failed to save fakeplayer", e);
|
|
+ }
|
|
+ } else {
|
|
+ removeAllBot(BotRemoveEvent.RemoveReason.INTERNAL);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public static void loadAllBot() {
|
|
+ if (LeavesConfig.fakeplayerSupport && LeavesConfig.fakeplayerResident) {
|
|
+ JsonObject fakePlayerList = new JsonObject();
|
|
+ File file = MinecraftServer.getServer().getWorldPath(LevelResource.ROOT).resolve("fake_player.leaves.json").toFile();
|
|
+ if (!file.isFile()) {
|
|
+ return;
|
|
+ }
|
|
+ try (BufferedReader bfr = Files.newBufferedReader(file.toPath(), StandardCharsets.UTF_8)) {
|
|
+ fakePlayerList = new Gson().fromJson(bfr, JsonObject.class);
|
|
+ } catch (IOException e) {
|
|
+ LeavesLogger.LOGGER.severe("Failed to load fakeplayer", e);
|
|
+ }
|
|
+ for (Map.Entry<String, JsonElement> entry : fakePlayerList.entrySet()) {
|
|
+ BotUtil.loadBot(entry);
|
|
+ }
|
|
+ if (!file.delete()) {
|
|
+ LeavesLogger.LOGGER.warning("Failed to delete " + file);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public static void removeAllBot(BotRemoveEvent.RemoveReason reason) {
|
|
+ Iterator<ServerBot> iterator = bots.iterator();
|
|
+ while (iterator.hasNext()) {
|
|
+ ServerBot bot = iterator.next();
|
|
+ bot.onRemove(reason);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public static List<ServerBot> getBots() {
|
|
+ return bots;
|
|
+ }
|
|
+
|
|
+ public static class CustomGameProfile extends GameProfile {
|
|
+
|
|
+ public CustomGameProfile(UUID uuid, String name, String[] skin) {
|
|
+ super(uuid, name);
|
|
+ setSkin(skin);
|
|
+ }
|
|
+
|
|
+ public void setSkin(String[] skin) {
|
|
+ if (skin != null) {
|
|
+ getProperties().put("textures", new Property("textures", skin[0], skin[1]));
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public static class BotCreateState {
|
|
+
|
|
+ private final String realName;
|
|
+ private final String name;
|
|
+
|
|
+ public Location loc;
|
|
+
|
|
+ public String[] skin;
|
|
+ public String skinName;
|
|
+
|
|
+ public BotCreateEvent.CreateReason createReason;
|
|
+ public CommandSender creator;
|
|
+
|
|
+ public BotCreateState(Location loc, String realName, String skinName, BotCreateEvent.CreateReason createReason, CommandSender creator) {
|
|
+ this(loc, LeavesConfig.fakeplayerPrefix + realName + LeavesConfig.fakeplayerSuffix, realName, skinName, null, createReason, creator);
|
|
+ }
|
|
+
|
|
+ public BotCreateState(Location loc, String name, String realName, String skinName, String[] skin, BotCreateEvent.CreateReason createReason, CommandSender creator) {
|
|
+ this.loc = loc;
|
|
+ this.skinName = skinName;
|
|
+ this.skin = skin;
|
|
+ this.realName = realName;
|
|
+ this.name = name;
|
|
+ this.createReason = createReason;
|
|
+ this.creator = creator;
|
|
+ }
|
|
+
|
|
+ @Nullable
|
|
+ public Bot createNow(Consumer<ServerBot> consumer) {
|
|
+ ServerBot bot = createBot(this);
|
|
+ if (bot != null && consumer != null) {
|
|
+ consumer.accept(bot);
|
|
+ }
|
|
+ return bot != null ? bot.getBukkitEntity() : null;
|
|
+ }
|
|
+
|
|
+ public void create(Consumer<ServerBot> consumer) {
|
|
+ Bukkit.getScheduler().runTaskAsynchronously(CraftScheduler.MINECRAFT, () -> {
|
|
+ if (skin == null && skinName != null) {
|
|
+ this.skin = MojangAPI.getSkin(skinName);
|
|
+ }
|
|
+
|
|
+ Bukkit.getScheduler().runTask(CraftScheduler.MINECRAFT, () -> {
|
|
+ ServerBot bot = createBot(this);
|
|
+ if (bot != null && consumer != null) {
|
|
+ consumer.accept(bot);
|
|
+ }
|
|
+ });
|
|
+ });
|
|
+ }
|
|
+
|
|
+ public String getName() {
|
|
+ return name;
|
|
+ }
|
|
+
|
|
+ public String getRealName() {
|
|
+ return realName;
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/leavesmc/leaves/bot/ServerBotGameMode.java b/src/main/java/org/leavesmc/leaves/bot/ServerBotGameMode.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..d626ac47af400d01993c358fa5a93671dce9abe9
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/leavesmc/leaves/bot/ServerBotGameMode.java
|
|
@@ -0,0 +1,139 @@
|
|
+package org.leavesmc.leaves.bot;
|
|
+
|
|
+import net.kyori.adventure.text.Component;
|
|
+import net.minecraft.advancements.CriteriaTriggers;
|
|
+import net.minecraft.core.BlockPos;
|
|
+import net.minecraft.server.level.ServerLevel;
|
|
+import net.minecraft.server.level.ServerPlayer;
|
|
+import net.minecraft.server.level.ServerPlayerGameMode;
|
|
+import net.minecraft.world.InteractionHand;
|
|
+import net.minecraft.world.InteractionResult;
|
|
+import net.minecraft.world.ItemInteractionResult;
|
|
+import net.minecraft.world.item.ItemStack;
|
|
+import net.minecraft.world.item.context.UseOnContext;
|
|
+import net.minecraft.world.level.GameType;
|
|
+import net.minecraft.world.level.Level;
|
|
+import net.minecraft.world.level.block.Block;
|
|
+import net.minecraft.world.level.block.entity.BlockEntity;
|
|
+import net.minecraft.world.level.block.state.BlockState;
|
|
+import net.minecraft.world.phys.BlockHitResult;
|
|
+import org.bukkit.event.player.PlayerGameModeChangeEvent;
|
|
+import org.jetbrains.annotations.NotNull;
|
|
+import org.jetbrains.annotations.Nullable;
|
|
+
|
|
+public class ServerBotGameMode extends ServerPlayerGameMode {
|
|
+
|
|
+ public ServerBotGameMode(ServerBot bot) {
|
|
+ super(bot);
|
|
+ super.setGameModeForPlayer(GameType.SURVIVAL, null);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean changeGameModeForPlayer(@NotNull GameType gameMode) {
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ @Nullable
|
|
+ @Override
|
|
+ public PlayerGameModeChangeEvent changeGameModeForPlayer(@NotNull GameType gameMode, PlayerGameModeChangeEvent.@NotNull Cause cause, @Nullable Component cancelMessage) {
|
|
+ return null;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected void setGameModeForPlayer(@NotNull GameType gameMode, @Nullable GameType previousGameMode) {
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void tick() {
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void destroyAndAck(@NotNull BlockPos pos, int sequence, @NotNull String reason) {
|
|
+ this.destroyBlock(pos);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean destroyBlock(@NotNull BlockPos pos) {
|
|
+ BlockState iblockdata = this.level.getBlockState(pos);
|
|
+ BlockEntity tileentity = this.level.getBlockEntity(pos);
|
|
+ Block block = iblockdata.getBlock();
|
|
+
|
|
+ if (this.player.blockActionRestricted(this.level, pos, this.getGameModeForPlayer())) {
|
|
+ return false;
|
|
+ } else {
|
|
+ this.level.captureDrops = null;
|
|
+ BlockState iblockdata1 = block.playerWillDestroy(this.level, pos, iblockdata, this.player);
|
|
+ boolean flag = this.level.removeBlock(pos, false);
|
|
+
|
|
+ if (flag) {
|
|
+ block.destroy(this.level, pos, iblockdata1);
|
|
+ }
|
|
+
|
|
+ ItemStack itemstack = this.player.getMainHandItem();
|
|
+ ItemStack itemstack1 = itemstack.copy();
|
|
+
|
|
+ boolean flag1 = this.player.hasCorrectToolForDrops(iblockdata1);
|
|
+
|
|
+ itemstack.mineBlock(this.level, iblockdata1, pos, this.player);
|
|
+ if (flag && flag1) {
|
|
+ Block.dropResources(iblockdata1, this.level, pos, tileentity, this.player, itemstack1, true);
|
|
+ }
|
|
+
|
|
+ if (flag) {
|
|
+ iblockdata.getBlock().popExperience(this.level, pos, block.getExpDrop(iblockdata, this.level, pos, itemstack, true), this.player);
|
|
+ }
|
|
+
|
|
+ return true;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @NotNull
|
|
+ @Override
|
|
+ public InteractionResult useItemOn(@NotNull ServerPlayer player, Level world, @NotNull ItemStack stack, @NotNull InteractionHand hand, BlockHitResult hitResult) {
|
|
+ BlockPos blockposition = hitResult.getBlockPos();
|
|
+ BlockState iblockdata = world.getBlockState(blockposition);
|
|
+ InteractionResult enuminteractionresult = InteractionResult.PASS;
|
|
+
|
|
+ if (!iblockdata.getBlock().isEnabled(world.enabledFeatures())) {
|
|
+ return InteractionResult.FAIL;
|
|
+ }
|
|
+
|
|
+ if (player.getCooldowns().isOnCooldown(stack.getItem())) {
|
|
+ return InteractionResult.PASS;
|
|
+ }
|
|
+
|
|
+ this.firedInteract = true;
|
|
+ this.interactResult = false;
|
|
+ this.interactPosition = blockposition.immutable();
|
|
+ this.interactHand = hand;
|
|
+ this.interactItemStack = stack.copy();
|
|
+
|
|
+ boolean flag = !player.getMainHandItem().isEmpty() || !player.getOffhandItem().isEmpty();
|
|
+ boolean flag1 = player.isSecondaryUseActive() && flag;
|
|
+
|
|
+ if (!flag1) {
|
|
+ ItemInteractionResult iteminteractionresult = iblockdata.useItemOn(player.getItemInHand(hand), world, player, hand, hitResult);
|
|
+
|
|
+ if (iteminteractionresult.consumesAction()) {
|
|
+ return iteminteractionresult.result();
|
|
+ }
|
|
+
|
|
+ if (iteminteractionresult == ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION && hand == InteractionHand.MAIN_HAND) {
|
|
+ enuminteractionresult = iblockdata.useWithoutItem(world, player, hitResult);
|
|
+ if (enuminteractionresult.consumesAction()) {
|
|
+ return enuminteractionresult;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (!stack.isEmpty() && enuminteractionresult != InteractionResult.SUCCESS && !this.interactResult) {
|
|
+ UseOnContext itemactioncontext = new UseOnContext(player, hand, hitResult);
|
|
+ return stack.useOn(itemactioncontext);
|
|
+ }
|
|
+ return enuminteractionresult;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void setLevel(@NotNull ServerLevel world) {
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/leavesmc/leaves/bot/agent/Actions.java b/src/main/java/org/leavesmc/leaves/bot/agent/Actions.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..15cbf363f587a27d55f4bc7ec897787158a7d534
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/leavesmc/leaves/bot/agent/Actions.java
|
|
@@ -0,0 +1,66 @@
|
|
+package org.leavesmc.leaves.bot.agent;
|
|
+
|
|
+import org.jetbrains.annotations.Contract;
|
|
+import org.jetbrains.annotations.NotNull;
|
|
+import org.leavesmc.leaves.bot.agent.actions.*;
|
|
+
|
|
+import java.util.Collection;
|
|
+import java.util.HashMap;
|
|
+import java.util.Map;
|
|
+import java.util.Set;
|
|
+
|
|
+public class Actions {
|
|
+
|
|
+ private static final Map<String, BotAction> actions = new HashMap<>();
|
|
+
|
|
+ public static void registerAll() {
|
|
+ register(new AttackAction());
|
|
+ register(new BreakBlockAction());
|
|
+ register(new DropAction());
|
|
+ register(new JumpAction());
|
|
+ register(new RotateAction());
|
|
+ register(new SneakAction());
|
|
+ register(new StopAction());
|
|
+ register(new UseItemAction());
|
|
+ register(new UseItemOnAction());
|
|
+ register(new UseItemToAction());
|
|
+ register(new LookAction());
|
|
+ register(new FishAction());
|
|
+ register(new SwimAction());
|
|
+ register(new UseItemOffHandAction());
|
|
+ register(new UseItemOnOffhandAction());
|
|
+ register(new UseItemToOffhandAction());
|
|
+ register(new RotationAction());
|
|
+ }
|
|
+
|
|
+ public static boolean register(@NotNull BotAction action) {
|
|
+ if (!actions.containsKey(action.getName())) {
|
|
+ actions.put(action.getName(), action);
|
|
+ return true;
|
|
+ }
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ public static boolean unregister(@NotNull String name) {
|
|
+ if (actions.containsKey(name)) {
|
|
+ actions.remove(name);
|
|
+ return true;
|
|
+ }
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ @NotNull
|
|
+ @Contract(pure = true)
|
|
+ public static Collection<BotAction> getAll() {
|
|
+ return actions.values();
|
|
+ }
|
|
+
|
|
+ @NotNull
|
|
+ public static Set<String> getNames() {
|
|
+ return actions.keySet();
|
|
+ }
|
|
+
|
|
+ public static BotAction getForName(String name) {
|
|
+ return actions.get(name);
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/leavesmc/leaves/bot/agent/BotAction.java b/src/main/java/org/leavesmc/leaves/bot/agent/BotAction.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..9abcb8852ac031abaa991881a7cd6b33bc523b26
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/leavesmc/leaves/bot/agent/BotAction.java
|
|
@@ -0,0 +1,93 @@
|
|
+package org.leavesmc.leaves.bot.agent;
|
|
+
|
|
+import net.minecraft.server.level.ServerPlayer;
|
|
+import org.jetbrains.annotations.NotNull;
|
|
+import org.leavesmc.leaves.bot.ServerBot;
|
|
+import org.leavesmc.leaves.command.CommandArgument;
|
|
+import org.leavesmc.leaves.command.CommandArgumentResult;
|
|
+
|
|
+import java.util.List;
|
|
+
|
|
+public abstract class BotAction {
|
|
+
|
|
+ private final String name;
|
|
+ private final CommandArgument argument;
|
|
+
|
|
+ private boolean cancel;
|
|
+ private int tickDelay;
|
|
+ private int number;
|
|
+
|
|
+ private int needWaitTick;
|
|
+ private int canDoNumber;
|
|
+
|
|
+ public BotAction(String name, CommandArgument argument) {
|
|
+ this.name = name;
|
|
+ this.argument = argument;
|
|
+
|
|
+ this.cancel = false;
|
|
+ this.tickDelay = 20;
|
|
+ this.number = -1;
|
|
+ }
|
|
+
|
|
+ public String getName() {
|
|
+ return name;
|
|
+ }
|
|
+
|
|
+ public int getTickDelay() {
|
|
+ return tickDelay;
|
|
+ }
|
|
+
|
|
+ public int getNumber() {
|
|
+ return number;
|
|
+ }
|
|
+
|
|
+ public boolean isCancel() {
|
|
+ return cancel;
|
|
+ }
|
|
+
|
|
+ public BotAction setTickDelay(int tickDelay) {
|
|
+ this.tickDelay = Math.max(0, tickDelay);
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ public BotAction setTabComplete(int index, List<String> list) {
|
|
+ argument.setTabComplete(index, list);
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ public BotAction setNumber(int number) {
|
|
+ this.number = Math.max(-1, number);
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ public void setCancel(boolean cancel) {
|
|
+ this.cancel = cancel;
|
|
+ }
|
|
+
|
|
+ public void init() {
|
|
+ this.needWaitTick = 0;
|
|
+ this.canDoNumber = this.getNumber();
|
|
+ this.setCancel(false);
|
|
+ }
|
|
+
|
|
+ public void tryTick(ServerBot bot) {
|
|
+ if (canDoNumber == 0) {
|
|
+ this.setCancel(true);
|
|
+ return;
|
|
+ }
|
|
+ if (needWaitTick-- <= 0) {
|
|
+ if (this.doTick(bot)) {
|
|
+ canDoNumber--;
|
|
+ needWaitTick = this.getTickDelay();
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public CommandArgument getArgument() {
|
|
+ return argument;
|
|
+ }
|
|
+
|
|
+ public abstract BotAction getNew(@NotNull ServerPlayer player, @NotNull CommandArgumentResult result);
|
|
+
|
|
+ public abstract boolean doTick(@NotNull ServerBot bot);
|
|
+}
|
|
diff --git a/src/main/java/org/leavesmc/leaves/bot/agent/actions/AttackAction.java b/src/main/java/org/leavesmc/leaves/bot/agent/actions/AttackAction.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..1366939121876902669b264f2ffa05c039cbc4af
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/leavesmc/leaves/bot/agent/actions/AttackAction.java
|
|
@@ -0,0 +1,36 @@
|
|
+package org.leavesmc.leaves.bot.agent.actions;
|
|
+
|
|
+import net.minecraft.server.level.ServerPlayer;
|
|
+import net.minecraft.world.entity.Entity;
|
|
+import org.jetbrains.annotations.NotNull;
|
|
+import org.leavesmc.leaves.bot.ServerBot;
|
|
+import org.leavesmc.leaves.bot.agent.BotAction;
|
|
+import org.leavesmc.leaves.command.CommandArgument;
|
|
+import org.leavesmc.leaves.command.CommandArgumentResult;
|
|
+import org.leavesmc.leaves.command.CommandArgumentType;
|
|
+
|
|
+import java.util.List;
|
|
+
|
|
+public class AttackAction extends BotAction {
|
|
+
|
|
+ public AttackAction() {
|
|
+ super("attack", new CommandArgument(CommandArgumentType.INTEGER, CommandArgumentType.INTEGER));
|
|
+ setTabComplete(0, List.of("[TickDelay]"));
|
|
+ setTabComplete(1, List.of("[DoNumber]"));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public BotAction getNew(@NotNull ServerPlayer player, @NotNull CommandArgumentResult result) {
|
|
+ return new AttackAction().setTickDelay(result.readInt(20)).setNumber(result.readInt(-1));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean doTick(@NotNull ServerBot bot) {
|
|
+ Entity entity = bot.getTargetEntity(3, Entity::isAttackable);
|
|
+ if (entity != null) {
|
|
+ bot.attack(entity);
|
|
+ return true;
|
|
+ }
|
|
+ return false;
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/leavesmc/leaves/bot/agent/actions/BreakBlockAction.java b/src/main/java/org/leavesmc/leaves/bot/agent/actions/BreakBlockAction.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..adcce8f8c39b0a1e445f5552ce74436053b604e7
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/leavesmc/leaves/bot/agent/actions/BreakBlockAction.java
|
|
@@ -0,0 +1,105 @@
|
|
+package org.leavesmc.leaves.bot.agent.actions;
|
|
+
|
|
+import net.minecraft.core.BlockPos;
|
|
+import net.minecraft.server.level.ServerPlayer;
|
|
+import net.minecraft.world.InteractionHand;
|
|
+import net.minecraft.world.level.block.state.BlockState;
|
|
+import org.bukkit.block.Block;
|
|
+import org.bukkit.craftbukkit.block.CraftBlock;
|
|
+import org.jetbrains.annotations.NotNull;
|
|
+import org.leavesmc.leaves.bot.ServerBot;
|
|
+import org.leavesmc.leaves.bot.agent.BotAction;
|
|
+import org.leavesmc.leaves.command.CommandArgument;
|
|
+import org.leavesmc.leaves.command.CommandArgumentResult;
|
|
+import org.leavesmc.leaves.command.CommandArgumentType;
|
|
+
|
|
+import java.util.List;
|
|
+
|
|
+public class BreakBlockAction extends BotAction {
|
|
+
|
|
+ public BreakBlockAction() {
|
|
+ super("break", new CommandArgument(CommandArgumentType.INTEGER, CommandArgumentType.INTEGER));
|
|
+ setTabComplete(0, List.of("[TickDelay]"));
|
|
+ setTabComplete(1, List.of("[DoNumber]"));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public BotAction getNew(@NotNull ServerPlayer player, @NotNull CommandArgumentResult result) {
|
|
+ return new BreakBlockAction().setTickDelay(result.readInt(20)).setNumber(result.readInt(-1));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public BotAction setTickDelay(int tickDelay) {
|
|
+ super.setTickDelay(0);
|
|
+ this.delay = tickDelay;
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ private int delay = 0;
|
|
+ private int nowDelay = 0;
|
|
+
|
|
+ private BlockPos lastPos = null;
|
|
+ private int destroyProgressTime = 0;
|
|
+ private int lastSentState = -1;
|
|
+
|
|
+ @Override
|
|
+ public boolean doTick(@NotNull ServerBot bot) {
|
|
+ if (nowDelay > 0) {
|
|
+ nowDelay--;
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ Block block = bot.getBukkitEntity().getTargetBlockExact(5);
|
|
+ if (block != null) {
|
|
+ BlockPos pos = ((CraftBlock) block).getPosition();
|
|
+
|
|
+ if (lastPos == null || !lastPos.equals(pos)) {
|
|
+ lastPos = pos;
|
|
+ destroyProgressTime = 0;
|
|
+ lastSentState = -1;
|
|
+ }
|
|
+
|
|
+ BlockState iblockdata = bot.level().getBlockState(pos);
|
|
+ if (!iblockdata.isAir()) {
|
|
+ bot.swing(InteractionHand.MAIN_HAND);
|
|
+
|
|
+ if (iblockdata.getDestroyProgress(bot, bot.level(), pos) >= 1.0F) {
|
|
+ bot.gameMode.destroyAndAck(pos, 0, "insta mine");
|
|
+ bot.level().destroyBlockProgress(bot.getId(), pos, -1);
|
|
+ bot.updateItemInHand(InteractionHand.MAIN_HAND);
|
|
+ finalBreak();
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ float damage = this.incrementDestroyProgress(bot, iblockdata, pos);
|
|
+ if (damage >= 1.0F) {
|
|
+ bot.gameMode.destroyAndAck(pos, 0, "destroyed");
|
|
+ bot.level().destroyBlockProgress(bot.getId(), pos, -1);
|
|
+ bot.updateItemInHand(InteractionHand.MAIN_HAND);
|
|
+ finalBreak();
|
|
+ return true;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ private void finalBreak() {
|
|
+ lastPos = null;
|
|
+ destroyProgressTime = 0;
|
|
+ lastSentState = -1;
|
|
+ nowDelay = delay;
|
|
+ }
|
|
+
|
|
+ private float incrementDestroyProgress(ServerBot bot, @NotNull BlockState state, BlockPos pos) {
|
|
+ float f = state.getDestroyProgress(bot, bot.level(), pos) * (float) (++destroyProgressTime);
|
|
+ int k = (int) (f * 10.0F);
|
|
+
|
|
+ if (k != lastSentState) {
|
|
+ bot.level().destroyBlockProgress(bot.getId(), pos, k);
|
|
+ lastSentState = k;
|
|
+ }
|
|
+
|
|
+ return f;
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/leavesmc/leaves/bot/agent/actions/CraftCustomBotAction.java b/src/main/java/org/leavesmc/leaves/bot/agent/actions/CraftCustomBotAction.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..f9b48f38a9b530125849f30eeab497895da77a21
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/leavesmc/leaves/bot/agent/actions/CraftCustomBotAction.java
|
|
@@ -0,0 +1,48 @@
|
|
+package org.leavesmc.leaves.bot.agent.actions;
|
|
+
|
|
+import net.minecraft.server.level.ServerPlayer;
|
|
+import org.bukkit.entity.Player;
|
|
+import org.jetbrains.annotations.NotNull;
|
|
+import org.leavesmc.leaves.bot.ServerBot;
|
|
+import org.leavesmc.leaves.bot.agent.BotAction;
|
|
+import org.leavesmc.leaves.command.CommandArgument;
|
|
+import org.leavesmc.leaves.command.CommandArgumentResult;
|
|
+import org.leavesmc.leaves.entity.botaction.CustomBotAction;
|
|
+
|
|
+public class CraftCustomBotAction extends BotAction {
|
|
+
|
|
+ private final CustomBotAction realAction;
|
|
+
|
|
+ public CraftCustomBotAction(String name, @NotNull CustomBotAction realAction) {
|
|
+ super(name, new CommandArgument().setAllTabComplete(realAction.getTabComplete()));
|
|
+ this.realAction = realAction;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public BotAction getNew(@NotNull ServerPlayer player, @NotNull CommandArgumentResult result) {
|
|
+ throw new UnsupportedOperationException("Not supported.");
|
|
+ }
|
|
+
|
|
+ public BotAction getNew(@NotNull Player player, String[] args) {
|
|
+ CustomBotAction newRealAction = realAction.getNew(player, args);
|
|
+ if (newRealAction != null) {
|
|
+ return new CraftCustomBotAction(getName(), newRealAction);
|
|
+ }
|
|
+ return null;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int getNumber() {
|
|
+ return realAction.getNumber();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int getTickDelay() {
|
|
+ return realAction.getTickDelay();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean doTick(@NotNull ServerBot bot) {
|
|
+ return realAction.doTick(bot.getBukkitPlayer());
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/leavesmc/leaves/bot/agent/actions/DropAction.java b/src/main/java/org/leavesmc/leaves/bot/agent/actions/DropAction.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..c534e5f5a51021a5f08bae2e4bce55fcedf93cf0
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/leavesmc/leaves/bot/agent/actions/DropAction.java
|
|
@@ -0,0 +1,26 @@
|
|
+package org.leavesmc.leaves.bot.agent.actions;
|
|
+
|
|
+import net.minecraft.server.level.ServerPlayer;
|
|
+import org.jetbrains.annotations.NotNull;
|
|
+import org.leavesmc.leaves.bot.ServerBot;
|
|
+import org.leavesmc.leaves.bot.agent.BotAction;
|
|
+import org.leavesmc.leaves.command.CommandArgument;
|
|
+import org.leavesmc.leaves.command.CommandArgumentResult;
|
|
+
|
|
+public class DropAction extends BotAction {
|
|
+
|
|
+ public DropAction() {
|
|
+ super("drop", new CommandArgument());
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public BotAction getNew(@NotNull ServerPlayer player, @NotNull CommandArgumentResult result) {
|
|
+ return this.setTickDelay(0).setNumber(1);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean doTick(@NotNull ServerBot bot) {
|
|
+ bot.dropAll();
|
|
+ return true;
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/leavesmc/leaves/bot/agent/actions/FishAction.java b/src/main/java/org/leavesmc/leaves/bot/agent/actions/FishAction.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..18b2f81f9edcfc2f30dde82c832a899b84c8cd3a
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/leavesmc/leaves/bot/agent/actions/FishAction.java
|
|
@@ -0,0 +1,70 @@
|
|
+package org.leavesmc.leaves.bot.agent.actions;
|
|
+
|
|
+import net.minecraft.server.level.ServerPlayer;
|
|
+import net.minecraft.world.InteractionHand;
|
|
+import net.minecraft.world.entity.projectile.FishingHook;
|
|
+import net.minecraft.world.item.FishingRodItem;
|
|
+import net.minecraft.world.item.ItemStack;
|
|
+import org.jetbrains.annotations.NotNull;
|
|
+import org.leavesmc.leaves.bot.ServerBot;
|
|
+import org.leavesmc.leaves.bot.agent.BotAction;
|
|
+import org.leavesmc.leaves.command.CommandArgument;
|
|
+import org.leavesmc.leaves.command.CommandArgumentResult;
|
|
+import org.leavesmc.leaves.command.CommandArgumentType;
|
|
+
|
|
+import java.util.List;
|
|
+
|
|
+public class FishAction extends BotAction {
|
|
+
|
|
+ public FishAction() {
|
|
+ super("fish", new CommandArgument(CommandArgumentType.INTEGER, CommandArgumentType.INTEGER));
|
|
+ setTabComplete(0, List.of("[TickDelay]"));
|
|
+ setTabComplete(1, List.of("[DoNumber]"));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public BotAction getNew(@NotNull ServerPlayer player, @NotNull CommandArgumentResult result) {
|
|
+ return new FishAction().setTickDelay(result.readInt(20)).setNumber(result.readInt(-1));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public BotAction setTickDelay(int tickDelay) {
|
|
+ super.setTickDelay(0);
|
|
+ this.delay = tickDelay;
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ private int delay = 0;
|
|
+ private int nowDelay = 0;
|
|
+
|
|
+ @Override
|
|
+ public boolean doTick(@NotNull ServerBot bot) {
|
|
+ if (nowDelay > 0) {
|
|
+ nowDelay--;
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ ItemStack mainHand = bot.getMainHandItem();
|
|
+ if (mainHand == ItemStack.EMPTY || mainHand.getItem().getClass() != FishingRodItem.class) {
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ FishingHook fishingHook = bot.fishing;
|
|
+ if (fishingHook != null) {
|
|
+ if (fishingHook.currentState == FishingHook.FishHookState.HOOKED_IN_ENTITY) {
|
|
+ mainHand.use(bot.level(), bot, InteractionHand.MAIN_HAND);
|
|
+ nowDelay = 20;
|
|
+ return false;
|
|
+ }
|
|
+ if (fishingHook.nibble > 0) {
|
|
+ mainHand.use(bot.level(), bot, InteractionHand.MAIN_HAND);
|
|
+ nowDelay = delay;
|
|
+ return true;
|
|
+ }
|
|
+ } else {
|
|
+ mainHand.use(bot.level(), bot, InteractionHand.MAIN_HAND);
|
|
+ }
|
|
+
|
|
+ return false;
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/leavesmc/leaves/bot/agent/actions/JumpAction.java b/src/main/java/org/leavesmc/leaves/bot/agent/actions/JumpAction.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..de4c2aaf8f2f212e346ed41a514c803aa4fe7ac6
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/leavesmc/leaves/bot/agent/actions/JumpAction.java
|
|
@@ -0,0 +1,35 @@
|
|
+package org.leavesmc.leaves.bot.agent.actions;
|
|
+
|
|
+import net.minecraft.server.level.ServerPlayer;
|
|
+import org.jetbrains.annotations.NotNull;
|
|
+import org.leavesmc.leaves.bot.ServerBot;
|
|
+import org.leavesmc.leaves.bot.agent.BotAction;
|
|
+import org.leavesmc.leaves.command.CommandArgument;
|
|
+import org.leavesmc.leaves.command.CommandArgumentResult;
|
|
+import org.leavesmc.leaves.command.CommandArgumentType;
|
|
+
|
|
+import java.util.List;
|
|
+
|
|
+public class JumpAction extends BotAction {
|
|
+
|
|
+ public JumpAction() {
|
|
+ super("jump", new CommandArgument(CommandArgumentType.INTEGER, CommandArgumentType.INTEGER));
|
|
+ setTabComplete(0, List.of("[TickDelay]"));
|
|
+ setTabComplete(1, List.of("[DoNumber]"));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public BotAction getNew(@NotNull ServerPlayer player, @NotNull CommandArgumentResult result) {
|
|
+ return new JumpAction().setTickDelay(result.readInt(20)).setNumber(result.readInt(-1));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean doTick(@NotNull ServerBot bot) {
|
|
+ if (bot.onGround()) {
|
|
+ bot.jumpFromGround();
|
|
+ return true;
|
|
+ } else {
|
|
+ return false;
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/leavesmc/leaves/bot/agent/actions/LookAction.java b/src/main/java/org/leavesmc/leaves/bot/agent/actions/LookAction.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..bd8a62fb4385a00a7f532835f7d75e47ae45405b
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/leavesmc/leaves/bot/agent/actions/LookAction.java
|
|
@@ -0,0 +1,49 @@
|
|
+package org.leavesmc.leaves.bot.agent.actions;
|
|
+
|
|
+import net.minecraft.server.level.ServerPlayer;
|
|
+import org.bukkit.util.Vector;
|
|
+import org.jetbrains.annotations.NotNull;
|
|
+import org.leavesmc.leaves.bot.ServerBot;
|
|
+import org.leavesmc.leaves.bot.agent.BotAction;
|
|
+import org.leavesmc.leaves.command.CommandArgument;
|
|
+import org.leavesmc.leaves.command.CommandArgumentResult;
|
|
+import org.leavesmc.leaves.command.CommandArgumentType;
|
|
+
|
|
+import java.util.List;
|
|
+
|
|
+public class LookAction extends BotAction {
|
|
+
|
|
+ public LookAction() {
|
|
+ super("look", new CommandArgument(CommandArgumentType.DOUBLE, CommandArgumentType.DOUBLE, CommandArgumentType.DOUBLE));
|
|
+ setTabComplete(0, List.of("<X>"));
|
|
+ setTabComplete(1, List.of("<Y>"));
|
|
+ setTabComplete(2, List.of("<Z>"));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public BotAction getNew(@NotNull ServerPlayer player, @NotNull CommandArgumentResult result) {
|
|
+ Vector pos = result.readVector();
|
|
+ if (pos != null) {
|
|
+ return new LookAction().setPos(pos).setTickDelay(0).setNumber(1);
|
|
+ } else {
|
|
+ return null;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private Vector pos;
|
|
+
|
|
+ public LookAction setPos(Vector pos) {
|
|
+ if (pos != null) {
|
|
+ this.pos = pos;
|
|
+ return this;
|
|
+ } else {
|
|
+ return null;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean doTick(@NotNull ServerBot bot) {
|
|
+ bot.look(pos.subtract(bot.getLocation().toVector()), false);
|
|
+ return true;
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/leavesmc/leaves/bot/agent/actions/RotateAction.java b/src/main/java/org/leavesmc/leaves/bot/agent/actions/RotateAction.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..fe872cad0cbf9ede825e0561cdb4ecb24df32821
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/leavesmc/leaves/bot/agent/actions/RotateAction.java
|
|
@@ -0,0 +1,33 @@
|
|
+package org.leavesmc.leaves.bot.agent.actions;
|
|
+
|
|
+import net.minecraft.server.level.ServerPlayer;
|
|
+import org.jetbrains.annotations.NotNull;
|
|
+import org.leavesmc.leaves.bot.ServerBot;
|
|
+import org.leavesmc.leaves.bot.agent.BotAction;
|
|
+import org.leavesmc.leaves.command.CommandArgument;
|
|
+import org.leavesmc.leaves.command.CommandArgumentResult;
|
|
+
|
|
+public class RotateAction extends BotAction {
|
|
+
|
|
+ public RotateAction() {
|
|
+ super("rotate", new CommandArgument());
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public BotAction getNew(@NotNull ServerPlayer player, @NotNull CommandArgumentResult result) {
|
|
+ return new RotateAction().setPlayer(player).setTickDelay(0).setNumber(1);
|
|
+ }
|
|
+
|
|
+ private ServerPlayer player;
|
|
+
|
|
+ public RotateAction setPlayer(ServerPlayer player) {
|
|
+ this.player = player;
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean doTick(@NotNull ServerBot bot) {
|
|
+ bot.faceLocation(player.getBukkitEntity().getLocation());
|
|
+ return true;
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/leavesmc/leaves/bot/agent/actions/RotationAction.java b/src/main/java/org/leavesmc/leaves/bot/agent/actions/RotationAction.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..6983bf8555058cb715328f761df80d0b89c0b8f0
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/leavesmc/leaves/bot/agent/actions/RotationAction.java
|
|
@@ -0,0 +1,44 @@
|
|
+package org.leavesmc.leaves.bot.agent.actions;
|
|
+
|
|
+import net.minecraft.server.level.ServerPlayer;
|
|
+import org.jetbrains.annotations.NotNull;
|
|
+import org.leavesmc.leaves.bot.ServerBot;
|
|
+import org.leavesmc.leaves.bot.agent.BotAction;
|
|
+import org.leavesmc.leaves.command.CommandArgument;
|
|
+import org.leavesmc.leaves.command.CommandArgumentResult;
|
|
+import org.leavesmc.leaves.command.CommandArgumentType;
|
|
+
|
|
+import java.util.List;
|
|
+
|
|
+public class RotationAction extends BotAction {
|
|
+
|
|
+ public RotationAction() {
|
|
+ super("rotation", new CommandArgument(CommandArgumentType.FLOAT, CommandArgumentType.FLOAT));
|
|
+ setTabComplete(0, List.of("<yaw>"));
|
|
+ setTabComplete(1, List.of("<pitch>"));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public BotAction getNew(@NotNull ServerPlayer player, @NotNull CommandArgumentResult result) {
|
|
+ return new RotationAction().setYaw(result.readFloat(player.getYRot())).setPitch(result.readFloat(player.getXRot())).setTickDelay(0).setNumber(1);
|
|
+ }
|
|
+
|
|
+ private float yaw;
|
|
+ private float pitch;
|
|
+
|
|
+ public RotationAction setYaw(float yaw) {
|
|
+ this.yaw = yaw;
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ public RotationAction setPitch(float pitch) {
|
|
+ this.pitch = pitch;
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean doTick(@NotNull ServerBot bot) {
|
|
+ bot.setRot(yaw, pitch);
|
|
+ return true;
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/leavesmc/leaves/bot/agent/actions/SneakAction.java b/src/main/java/org/leavesmc/leaves/bot/agent/actions/SneakAction.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..9b4406bc0b418abc6a253e047c504b4ad15f059a
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/leavesmc/leaves/bot/agent/actions/SneakAction.java
|
|
@@ -0,0 +1,27 @@
|
|
+package org.leavesmc.leaves.bot.agent.actions;
|
|
+
|
|
+import net.minecraft.server.level.ServerPlayer;
|
|
+import net.minecraft.world.entity.Pose;
|
|
+import org.jetbrains.annotations.NotNull;
|
|
+import org.leavesmc.leaves.bot.ServerBot;
|
|
+import org.leavesmc.leaves.bot.agent.BotAction;
|
|
+import org.leavesmc.leaves.command.CommandArgument;
|
|
+import org.leavesmc.leaves.command.CommandArgumentResult;
|
|
+
|
|
+public class SneakAction extends BotAction {
|
|
+
|
|
+ public SneakAction() {
|
|
+ super("sneak", new CommandArgument());
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public BotAction getNew(@NotNull ServerPlayer player, @NotNull CommandArgumentResult result) {
|
|
+ return this.setTickDelay(0).setNumber(1);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean doTick(@NotNull ServerBot bot) {
|
|
+ bot.setShiftKeyDown(!bot.isShiftKeyDown());
|
|
+ return true;
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/leavesmc/leaves/bot/agent/actions/StopAction.java b/src/main/java/org/leavesmc/leaves/bot/agent/actions/StopAction.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..bd02fe0e6b1f1b6048d56ab9c9379808dcf227c4
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/leavesmc/leaves/bot/agent/actions/StopAction.java
|
|
@@ -0,0 +1,26 @@
|
|
+package org.leavesmc.leaves.bot.agent.actions;
|
|
+
|
|
+import net.minecraft.server.level.ServerPlayer;
|
|
+import org.jetbrains.annotations.NotNull;
|
|
+import org.leavesmc.leaves.bot.ServerBot;
|
|
+import org.leavesmc.leaves.bot.agent.BotAction;
|
|
+import org.leavesmc.leaves.command.CommandArgument;
|
|
+import org.leavesmc.leaves.command.CommandArgumentResult;
|
|
+
|
|
+public class StopAction extends BotAction {
|
|
+
|
|
+ public StopAction() {
|
|
+ super("stop", new CommandArgument());
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public BotAction getNew(@NotNull ServerPlayer player, @NotNull CommandArgumentResult result) {
|
|
+ return this.setTickDelay(0).setNumber(0);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean doTick(@NotNull ServerBot bot) {
|
|
+ this.setCancel(true);
|
|
+ return true;
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/leavesmc/leaves/bot/agent/actions/SwimAction.java b/src/main/java/org/leavesmc/leaves/bot/agent/actions/SwimAction.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..c91ca987eb5922b8dbcd271deb33f80be5e1a9af
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/leavesmc/leaves/bot/agent/actions/SwimAction.java
|
|
@@ -0,0 +1,26 @@
|
|
+package org.leavesmc.leaves.bot.agent.actions;
|
|
+
|
|
+import net.minecraft.server.level.ServerPlayer;
|
|
+import org.jetbrains.annotations.NotNull;
|
|
+import org.leavesmc.leaves.bot.ServerBot;
|
|
+import org.leavesmc.leaves.bot.agent.BotAction;
|
|
+import org.leavesmc.leaves.command.CommandArgument;
|
|
+import org.leavesmc.leaves.command.CommandArgumentResult;
|
|
+
|
|
+public class SwimAction extends BotAction {
|
|
+
|
|
+ public SwimAction() {
|
|
+ super("swim", new CommandArgument());
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public BotAction getNew(@NotNull ServerPlayer player, @NotNull CommandArgumentResult result) {
|
|
+ return this.setTickDelay(0).setNumber(1);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean doTick(@NotNull ServerBot bot) {
|
|
+ bot.waterSwim = !bot.waterSwim;
|
|
+ return true;
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/leavesmc/leaves/bot/agent/actions/UseItemAction.java b/src/main/java/org/leavesmc/leaves/bot/agent/actions/UseItemAction.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..cf56fbdd461ae7f3a00043dbe979421a1dd3b5a4
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/leavesmc/leaves/bot/agent/actions/UseItemAction.java
|
|
@@ -0,0 +1,34 @@
|
|
+package org.leavesmc.leaves.bot.agent.actions;
|
|
+
|
|
+import net.minecraft.core.component.DataComponents;
|
|
+import net.minecraft.server.level.ServerPlayer;
|
|
+import net.minecraft.world.InteractionHand;
|
|
+import org.jetbrains.annotations.NotNull;
|
|
+import org.leavesmc.leaves.bot.ServerBot;
|
|
+import org.leavesmc.leaves.bot.agent.BotAction;
|
|
+import org.leavesmc.leaves.command.CommandArgument;
|
|
+import org.leavesmc.leaves.command.CommandArgumentResult;
|
|
+import org.leavesmc.leaves.command.CommandArgumentType;
|
|
+
|
|
+import java.util.List;
|
|
+
|
|
+public class UseItemAction extends BotAction {
|
|
+
|
|
+ public UseItemAction() {
|
|
+ super("use", new CommandArgument(CommandArgumentType.INTEGER, CommandArgumentType.INTEGER));
|
|
+ setTabComplete(0, List.of("[TickDelay]"));
|
|
+ setTabComplete(1, List.of("[DoNumber]"));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public BotAction getNew(@NotNull ServerPlayer player, @NotNull CommandArgumentResult result) {
|
|
+ return new UseItemAction().setTickDelay(result.readInt(20)).setNumber(result.readInt(-1));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean doTick(@NotNull ServerBot bot) {
|
|
+ bot.swing(InteractionHand.MAIN_HAND);
|
|
+ bot.updateItemInHand(InteractionHand.MAIN_HAND);
|
|
+ return bot.gameMode.useItem(bot, bot.level(), bot.getItemInHand(InteractionHand.MAIN_HAND), InteractionHand.MAIN_HAND).consumesAction();
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/leavesmc/leaves/bot/agent/actions/UseItemOffHandAction.java b/src/main/java/org/leavesmc/leaves/bot/agent/actions/UseItemOffHandAction.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..475bf440d40ac35720b01da18440cf0d35166a5c
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/leavesmc/leaves/bot/agent/actions/UseItemOffHandAction.java
|
|
@@ -0,0 +1,33 @@
|
|
+package org.leavesmc.leaves.bot.agent.actions;
|
|
+
|
|
+import net.minecraft.server.level.ServerPlayer;
|
|
+import net.minecraft.world.InteractionHand;
|
|
+import org.jetbrains.annotations.NotNull;
|
|
+import org.leavesmc.leaves.bot.ServerBot;
|
|
+import org.leavesmc.leaves.bot.agent.BotAction;
|
|
+import org.leavesmc.leaves.command.CommandArgument;
|
|
+import org.leavesmc.leaves.command.CommandArgumentResult;
|
|
+import org.leavesmc.leaves.command.CommandArgumentType;
|
|
+
|
|
+import java.util.List;
|
|
+
|
|
+public class UseItemOffHandAction extends BotAction {
|
|
+
|
|
+ public UseItemOffHandAction() {
|
|
+ super("use_offhand", new CommandArgument(CommandArgumentType.INTEGER, CommandArgumentType.INTEGER));
|
|
+ setTabComplete(0, List.of("[TickDelay]"));
|
|
+ setTabComplete(1, List.of("[DoNumber]"));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public BotAction getNew(@NotNull ServerPlayer player, @NotNull CommandArgumentResult result) {
|
|
+ return new UseItemOffHandAction().setTickDelay(result.readInt(20)).setNumber(result.readInt(-1));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean doTick(@NotNull ServerBot bot) {
|
|
+ bot.swing(InteractionHand.OFF_HAND);
|
|
+ bot.updateItemInHand(InteractionHand.OFF_HAND);
|
|
+ return bot.gameMode.useItem(bot, bot.level(), bot.getItemInHand(InteractionHand.OFF_HAND), InteractionHand.OFF_HAND).consumesAction();
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/leavesmc/leaves/bot/agent/actions/UseItemOnAction.java b/src/main/java/org/leavesmc/leaves/bot/agent/actions/UseItemOnAction.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..fe8c4ba7aff74dc297bdf4e271a2d7f1229c357d
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/leavesmc/leaves/bot/agent/actions/UseItemOnAction.java
|
|
@@ -0,0 +1,56 @@
|
|
+package org.leavesmc.leaves.bot.agent.actions;
|
|
+
|
|
+import net.minecraft.server.level.ServerPlayer;
|
|
+import net.minecraft.world.InteractionHand;
|
|
+import net.minecraft.world.level.ClipContext;
|
|
+import net.minecraft.world.level.block.Blocks;
|
|
+import net.minecraft.world.level.block.entity.BlockEntity;
|
|
+import net.minecraft.world.level.block.entity.TrappedChestBlockEntity;
|
|
+import net.minecraft.world.level.block.state.BlockState;
|
|
+import net.minecraft.world.phys.BlockHitResult;
|
|
+import net.minecraft.world.phys.HitResult;
|
|
+import org.bukkit.Bukkit;
|
|
+import org.bukkit.craftbukkit.scheduler.CraftScheduler;
|
|
+import org.jetbrains.annotations.NotNull;
|
|
+import org.leavesmc.leaves.bot.ServerBot;
|
|
+import org.leavesmc.leaves.bot.agent.BotAction;
|
|
+import org.leavesmc.leaves.command.CommandArgument;
|
|
+import org.leavesmc.leaves.command.CommandArgumentResult;
|
|
+import org.leavesmc.leaves.command.CommandArgumentType;
|
|
+
|
|
+import java.util.List;
|
|
+
|
|
+public class UseItemOnAction extends BotAction {
|
|
+
|
|
+ public UseItemOnAction() {
|
|
+ super("use_on", new CommandArgument(CommandArgumentType.INTEGER, CommandArgumentType.INTEGER));
|
|
+ setTabComplete(0, List.of("[TickDelay]"));
|
|
+ setTabComplete(1, List.of("[DoNumber]"));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public BotAction getNew(@NotNull ServerPlayer player, @NotNull CommandArgumentResult result) {
|
|
+ return new UseItemOnAction().setTickDelay(result.readInt(20)).setNumber(result.readInt(-1));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean doTick(@NotNull ServerBot bot) {
|
|
+ HitResult result = bot.getRayTrace(5, ClipContext.Fluid.NONE);
|
|
+ if (result instanceof BlockHitResult blockHitResult) {
|
|
+ BlockState state = bot.serverLevel().getBlockState(blockHitResult.getBlockPos());
|
|
+ bot.swing(InteractionHand.MAIN_HAND);
|
|
+ if (state.getBlock() == Blocks.TRAPPED_CHEST) {
|
|
+ BlockEntity entity = bot.serverLevel().getBlockEntity(blockHitResult.getBlockPos());
|
|
+ if (entity instanceof TrappedChestBlockEntity chestBlockEntity) {
|
|
+ chestBlockEntity.startOpen(bot);
|
|
+ Bukkit.getScheduler().runTaskLater(CraftScheduler.MINECRAFT, () -> chestBlockEntity.stopOpen(bot), 1);
|
|
+ return true;
|
|
+ }
|
|
+ } else {
|
|
+ bot.updateItemInHand(InteractionHand.MAIN_HAND);
|
|
+ return bot.gameMode.useItemOn(bot, bot.level(), bot.getItemInHand(InteractionHand.MAIN_HAND), InteractionHand.MAIN_HAND, (BlockHitResult) result).consumesAction();
|
|
+ }
|
|
+ }
|
|
+ return false;
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/leavesmc/leaves/bot/agent/actions/UseItemOnOffhandAction.java b/src/main/java/org/leavesmc/leaves/bot/agent/actions/UseItemOnOffhandAction.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..a69ede01e5e5df31a144f98e9871183793f5f377
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/leavesmc/leaves/bot/agent/actions/UseItemOnOffhandAction.java
|
|
@@ -0,0 +1,56 @@
|
|
+package org.leavesmc.leaves.bot.agent.actions;
|
|
+
|
|
+import net.minecraft.server.level.ServerPlayer;
|
|
+import net.minecraft.world.InteractionHand;
|
|
+import net.minecraft.world.level.ClipContext;
|
|
+import net.minecraft.world.level.block.Blocks;
|
|
+import net.minecraft.world.level.block.entity.BlockEntity;
|
|
+import net.minecraft.world.level.block.entity.TrappedChestBlockEntity;
|
|
+import net.minecraft.world.level.block.state.BlockState;
|
|
+import net.minecraft.world.phys.BlockHitResult;
|
|
+import net.minecraft.world.phys.HitResult;
|
|
+import org.bukkit.Bukkit;
|
|
+import org.bukkit.craftbukkit.scheduler.CraftScheduler;
|
|
+import org.jetbrains.annotations.NotNull;
|
|
+import org.leavesmc.leaves.bot.ServerBot;
|
|
+import org.leavesmc.leaves.bot.agent.BotAction;
|
|
+import org.leavesmc.leaves.command.CommandArgument;
|
|
+import org.leavesmc.leaves.command.CommandArgumentResult;
|
|
+import org.leavesmc.leaves.command.CommandArgumentType;
|
|
+
|
|
+import java.util.List;
|
|
+
|
|
+public class UseItemOnOffhandAction extends BotAction {
|
|
+
|
|
+ public UseItemOnOffhandAction() {
|
|
+ super("use_on_offhand", new CommandArgument(CommandArgumentType.INTEGER, CommandArgumentType.INTEGER));
|
|
+ setTabComplete(0, List.of("[TickDelay]"));
|
|
+ setTabComplete(1, List.of("[DoNumber]"));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public BotAction getNew(@NotNull ServerPlayer player, @NotNull CommandArgumentResult result) {
|
|
+ return new UseItemOnOffhandAction().setTickDelay(result.readInt(20)).setNumber(result.readInt(-1));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean doTick(@NotNull ServerBot bot) {
|
|
+ HitResult result = bot.getRayTrace(5, ClipContext.Fluid.NONE);
|
|
+ if (result instanceof BlockHitResult blockHitResult) {
|
|
+ BlockState state = bot.serverLevel().getBlockState(blockHitResult.getBlockPos());
|
|
+ bot.swing(InteractionHand.OFF_HAND);
|
|
+ if (state.getBlock() == Blocks.TRAPPED_CHEST) {
|
|
+ BlockEntity entity = bot.serverLevel().getBlockEntity(blockHitResult.getBlockPos());
|
|
+ if (entity instanceof TrappedChestBlockEntity chestBlockEntity) {
|
|
+ chestBlockEntity.startOpen(bot);
|
|
+ Bukkit.getScheduler().runTaskLater(CraftScheduler.MINECRAFT, () -> chestBlockEntity.stopOpen(bot), 1);
|
|
+ return true;
|
|
+ }
|
|
+ } else {
|
|
+ bot.updateItemInHand(InteractionHand.OFF_HAND);
|
|
+ return bot.gameMode.useItemOn(bot, bot.level(), bot.getItemInHand(InteractionHand.OFF_HAND), InteractionHand.OFF_HAND, (BlockHitResult) result).consumesAction();
|
|
+ }
|
|
+ }
|
|
+ return false;
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/leavesmc/leaves/bot/agent/actions/UseItemToAction.java b/src/main/java/org/leavesmc/leaves/bot/agent/actions/UseItemToAction.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..0dbe7fd2e63cd47f892e383d71fdbff1c0d8d2ab
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/leavesmc/leaves/bot/agent/actions/UseItemToAction.java
|
|
@@ -0,0 +1,38 @@
|
|
+package org.leavesmc.leaves.bot.agent.actions;
|
|
+
|
|
+import net.minecraft.server.level.ServerPlayer;
|
|
+import net.minecraft.world.InteractionHand;
|
|
+import net.minecraft.world.entity.Entity;
|
|
+import org.jetbrains.annotations.NotNull;
|
|
+import org.leavesmc.leaves.bot.ServerBot;
|
|
+import org.leavesmc.leaves.bot.agent.BotAction;
|
|
+import org.leavesmc.leaves.command.CommandArgument;
|
|
+import org.leavesmc.leaves.command.CommandArgumentResult;
|
|
+import org.leavesmc.leaves.command.CommandArgumentType;
|
|
+
|
|
+import java.util.List;
|
|
+
|
|
+public class UseItemToAction extends BotAction {
|
|
+
|
|
+ public UseItemToAction() {
|
|
+ super("use_to", new CommandArgument(CommandArgumentType.INTEGER, CommandArgumentType.INTEGER));
|
|
+ setTabComplete(0, List.of("[TickDelay]"));
|
|
+ setTabComplete(1, List.of("[DoNumber]"));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public BotAction getNew(@NotNull ServerPlayer player, @NotNull CommandArgumentResult result) {
|
|
+ return new UseItemToAction().setTickDelay(result.readInt(20)).setNumber(result.readInt(-1));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean doTick(@NotNull ServerBot bot) {
|
|
+ Entity entity = bot.getTargetEntity(3, null);
|
|
+ if (entity != null) {
|
|
+ bot.swing(InteractionHand.MAIN_HAND);
|
|
+ bot.updateItemInHand(InteractionHand.MAIN_HAND);
|
|
+ return bot.interactOn(entity, InteractionHand.MAIN_HAND).consumesAction();
|
|
+ }
|
|
+ return false;
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/leavesmc/leaves/bot/agent/actions/UseItemToOffhandAction.java b/src/main/java/org/leavesmc/leaves/bot/agent/actions/UseItemToOffhandAction.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..0e3895073bcfb5944c7147395338d750137e1386
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/leavesmc/leaves/bot/agent/actions/UseItemToOffhandAction.java
|
|
@@ -0,0 +1,38 @@
|
|
+package org.leavesmc.leaves.bot.agent.actions;
|
|
+
|
|
+import net.minecraft.server.level.ServerPlayer;
|
|
+import net.minecraft.world.InteractionHand;
|
|
+import net.minecraft.world.entity.Entity;
|
|
+import org.jetbrains.annotations.NotNull;
|
|
+import org.leavesmc.leaves.bot.ServerBot;
|
|
+import org.leavesmc.leaves.bot.agent.BotAction;
|
|
+import org.leavesmc.leaves.command.CommandArgument;
|
|
+import org.leavesmc.leaves.command.CommandArgumentResult;
|
|
+import org.leavesmc.leaves.command.CommandArgumentType;
|
|
+
|
|
+import java.util.List;
|
|
+
|
|
+public class UseItemToOffhandAction extends BotAction {
|
|
+
|
|
+ public UseItemToOffhandAction() {
|
|
+ super("use_to_offhand", new CommandArgument(CommandArgumentType.INTEGER, CommandArgumentType.INTEGER));
|
|
+ setTabComplete(0, List.of("[TickDelay]"));
|
|
+ setTabComplete(1, List.of("[DoNumber]"));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public BotAction getNew(@NotNull ServerPlayer player, @NotNull CommandArgumentResult result) {
|
|
+ return new UseItemToOffhandAction().setTickDelay(result.readInt(20)).setNumber(result.readInt(-1));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean doTick(@NotNull ServerBot bot) {
|
|
+ Entity entity = bot.getTargetEntity(3, null);
|
|
+ if (entity != null) {
|
|
+ bot.swing(InteractionHand.OFF_HAND);
|
|
+ bot.updateItemInHand(InteractionHand.OFF_HAND);
|
|
+ return bot.interactOn(entity, InteractionHand.OFF_HAND).consumesAction();
|
|
+ }
|
|
+ return false;
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/leavesmc/leaves/entity/CraftBot.java b/src/main/java/org/leavesmc/leaves/entity/CraftBot.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..fe1df01906f15e130cf947bbecb5df4bddf98c7c
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/leavesmc/leaves/entity/CraftBot.java
|
|
@@ -0,0 +1,67 @@
|
|
+package org.leavesmc.leaves.entity;
|
|
+
|
|
+import org.bukkit.craftbukkit.CraftServer;
|
|
+import org.bukkit.craftbukkit.entity.CraftPlayer;
|
|
+import org.bukkit.entity.Player;
|
|
+import org.jetbrains.annotations.NotNull;
|
|
+import org.jetbrains.annotations.Nullable;
|
|
+import org.leavesmc.leaves.bot.ServerBot;
|
|
+import org.leavesmc.leaves.bot.agent.Actions;
|
|
+import org.leavesmc.leaves.bot.agent.BotAction;
|
|
+import org.leavesmc.leaves.entity.botaction.LeavesBotAction;
|
|
+
|
|
+import java.util.UUID;
|
|
+
|
|
+public class CraftBot extends CraftPlayer implements Bot {
|
|
+
|
|
+ public CraftBot(CraftServer server, ServerBot entity) {
|
|
+ super(server, entity);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public String getSkinName() {
|
|
+ return getHandle().createState.skinName;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public @NotNull String getRealName() {
|
|
+ return getHandle().createState.getRealName();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public @Nullable UUID getCreatePlayerUUID() {
|
|
+ return getHandle().createPlayer;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean setBotAction(@NotNull String action, @NotNull Player player, @NotNull String[] args) {
|
|
+ BotAction botAction = Actions.getForName(action);
|
|
+ if (botAction != null) {
|
|
+ BotAction newAction = botAction.getNew(((CraftPlayer) player).getHandle(), botAction.getArgument().parse(0, args));
|
|
+ if (newAction != null) {
|
|
+ getHandle().setBotAction(newAction);
|
|
+ return true;
|
|
+ }
|
|
+ }
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean setBotAction(@NotNull LeavesBotAction action, @NotNull Player player, @NotNull String[] args) {
|
|
+ return setBotAction(action.getName(), player, args);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public ServerBot getHandle() {
|
|
+ return (ServerBot) entity;
|
|
+ }
|
|
+
|
|
+ public void setHandle(final ServerBot entity) {
|
|
+ super.setHandle(entity);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public String toString() {
|
|
+ return "CraftBot{" + "name=" + getName() + '}';
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/leavesmc/leaves/entity/CraftBotManager.java b/src/main/java/org/leavesmc/leaves/entity/CraftBotManager.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..4eaee3c09372f314d8a5930b4162a7d88d7cc105
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/leavesmc/leaves/entity/CraftBotManager.java
|
|
@@ -0,0 +1,102 @@
|
|
+package org.leavesmc.leaves.entity;
|
|
+
|
|
+import com.google.common.base.Function;
|
|
+import com.google.common.collect.Lists;
|
|
+import org.bukkit.Location;
|
|
+import org.jetbrains.annotations.NotNull;
|
|
+import org.jetbrains.annotations.Nullable;
|
|
+import org.leavesmc.leaves.bot.ServerBot;
|
|
+import org.leavesmc.leaves.bot.agent.Actions;
|
|
+import org.leavesmc.leaves.bot.agent.actions.CraftCustomBotAction;
|
|
+import org.leavesmc.leaves.entity.botaction.CustomBotAction;
|
|
+import org.leavesmc.leaves.event.bot.BotCreateEvent;
|
|
+import org.leavesmc.leaves.event.bot.BotRemoveEvent;
|
|
+
|
|
+import java.util.Collection;
|
|
+import java.util.Collections;
|
|
+import java.util.UUID;
|
|
+import java.util.function.Consumer;
|
|
+
|
|
+public class CraftBotManager implements BotManager {
|
|
+
|
|
+ private final Collection<Bot> botViews = Collections.unmodifiableList(Lists.transform(ServerBot.getBots(), new Function<ServerBot, CraftBot>() {
|
|
+ @Override
|
|
+ public CraftBot apply(ServerBot bot) {
|
|
+ return bot.getBukkitEntity();
|
|
+ }
|
|
+ }));
|
|
+
|
|
+ @Override
|
|
+ public @Nullable Bot getBot(@NotNull UUID uuid) {
|
|
+ return ServerBot.getBot(uuid).getBukkitPlayer();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public @Nullable Bot getBot(@NotNull String name) {
|
|
+ return ServerBot.getBot(name).getBukkitPlayer();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public @Nullable Bot createBot(@NotNull String name, @NotNull String realName, @Nullable String[] skin, @Nullable String skinName, @NotNull Location location) {
|
|
+ return this.createBot(name, realName, skin, skinName, location, null);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public @Nullable Bot createBot(@NotNull String name, @NotNull String realName, @Nullable String[] skin, @Nullable String skinName, @NotNull Location location, @Nullable Consumer<Bot> consumer) {
|
|
+ return new ServerBot.BotCreateState(location, name, realName, skinName, skin, BotCreateEvent.CreateReason.PLUGIN, null).createNow((serverBot -> {
|
|
+ if (consumer != null) {
|
|
+ consumer.accept(serverBot.getBukkitPlayer());
|
|
+ }
|
|
+ }));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void createBot(@NotNull String name, @Nullable String skinName, @NotNull Location location, @Nullable Consumer<Bot> consumer) {
|
|
+ new ServerBot.BotCreateState(location, name, skinName, BotCreateEvent.CreateReason.PLUGIN, null).create((serverBot -> {
|
|
+ if (consumer != null) {
|
|
+ consumer.accept(serverBot.getBukkitPlayer());
|
|
+ }
|
|
+ }));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void removeBot(@NotNull String name) {
|
|
+ ServerBot bot = ServerBot.getBot(name);
|
|
+ if (bot != null) {
|
|
+ bot.onRemove(org.leavesmc.leaves.event.bot.BotRemoveEvent.RemoveReason.PLUGIN);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void removeBot(@NotNull UUID uuid) {
|
|
+ ServerBot bot = ServerBot.getBot(uuid);
|
|
+ if (bot != null) {
|
|
+ bot.onRemove(BotRemoveEvent.RemoveReason.PLUGIN);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void removeAllBots() {
|
|
+ ServerBot.removeAllBot(BotRemoveEvent.RemoveReason.PLUGIN);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void saveOrRemoveAllBots() {
|
|
+ ServerBot.saveOrRemoveAllBot();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public Collection<Bot> getBots() {
|
|
+ return botViews;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean registerCustomBotAction(String name, CustomBotAction action) {
|
|
+ return Actions.register(new CraftCustomBotAction(name, action));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean unregisterCustomBotAction(String name) {
|
|
+ return Actions.unregister(name);
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/leavesmc/leaves/plugin/MinecraftInternalPlugin.java b/src/main/java/org/leavesmc/leaves/plugin/MinecraftInternalPlugin.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..356b2de6ffd82e42a5d0f20ac1e3de7f4b5a7013
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/leavesmc/leaves/plugin/MinecraftInternalPlugin.java
|
|
@@ -0,0 +1,149 @@
|
|
+package org.leavesmc.leaves.plugin;
|
|
+
|
|
+import org.bukkit.Server;
|
|
+import org.bukkit.command.Command;
|
|
+import org.bukkit.command.CommandSender;
|
|
+import org.bukkit.configuration.file.FileConfiguration;
|
|
+import org.bukkit.generator.BiomeProvider;
|
|
+import org.bukkit.generator.ChunkGenerator;
|
|
+import org.bukkit.plugin.PluginBase;
|
|
+import org.bukkit.plugin.PluginDescriptionFile;
|
|
+import org.bukkit.plugin.PluginLoader;
|
|
+import org.bukkit.plugin.PluginLogger;
|
|
+import org.jetbrains.annotations.NotNull;
|
|
+import org.jetbrains.annotations.Nullable;
|
|
+
|
|
+import java.io.File;
|
|
+import java.io.InputStream;
|
|
+import java.util.List;
|
|
+
|
|
+public class MinecraftInternalPlugin extends PluginBase {
|
|
+ private boolean enabled = true;
|
|
+
|
|
+ private final PluginDescriptionFile pdf;
|
|
+
|
|
+ public MinecraftInternalPlugin() {
|
|
+ String pluginName = "Minecraft";
|
|
+ pdf = new PluginDescriptionFile(pluginName, "1.0", "nms");
|
|
+ }
|
|
+
|
|
+ public void setEnabled(boolean enabled) {
|
|
+ this.enabled = enabled;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public File getDataFolder() {
|
|
+ throw new UnsupportedOperationException("Not supported.");
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public PluginDescriptionFile getDescription() {
|
|
+ return pdf;
|
|
+ }
|
|
+ // Paper start
|
|
+ @Override
|
|
+ public io.papermc.paper.plugin.configuration.PluginMeta getPluginMeta() {
|
|
+ return pdf;
|
|
+ }
|
|
+ // Paper end
|
|
+
|
|
+ @Override
|
|
+ public FileConfiguration getConfig() {
|
|
+ throw new UnsupportedOperationException("Not supported.");
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public InputStream getResource(String filename) {
|
|
+ throw new UnsupportedOperationException("Not supported.");
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void saveConfig() {
|
|
+ throw new UnsupportedOperationException("Not supported.");
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void saveDefaultConfig() {
|
|
+ throw new UnsupportedOperationException("Not supported.");
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void saveResource(String resourcePath, boolean replace) {
|
|
+ throw new UnsupportedOperationException("Not supported.");
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void reloadConfig() {
|
|
+ throw new UnsupportedOperationException("Not supported.");
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public PluginLogger getLogger() {
|
|
+ throw new UnsupportedOperationException("Not supported.");
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public PluginLoader getPluginLoader() {
|
|
+ throw new UnsupportedOperationException("Not supported.");
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public Server getServer() {
|
|
+ throw new UnsupportedOperationException("Not supported.");
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isEnabled() {
|
|
+ return enabled;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void onDisable() {
|
|
+ throw new UnsupportedOperationException("Not supported.");
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void onLoad() {
|
|
+ throw new UnsupportedOperationException("Not supported.");
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void onEnable() {
|
|
+ throw new UnsupportedOperationException("Not supported.");
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isNaggable() {
|
|
+ throw new UnsupportedOperationException("Not supported.");
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void setNaggable(boolean canNag) {
|
|
+ throw new UnsupportedOperationException("Not supported.");
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public ChunkGenerator getDefaultWorldGenerator(String worldName, String id) {
|
|
+ throw new UnsupportedOperationException("Not supported.");
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public @Nullable BiomeProvider getDefaultBiomeProvider(@NotNull String worldName, @Nullable String id) {
|
|
+ throw new UnsupportedOperationException("Not supported.");
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
|
+ throw new UnsupportedOperationException("Not supported.");
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
|
|
+ throw new UnsupportedOperationException("Not supported.");
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public @NotNull io.papermc.paper.plugin.lifecycle.event.LifecycleEventManager<org.bukkit.plugin.Plugin> getLifecycleManager() {
|
|
+ throw new UnsupportedOperationException("Not supported.");
|
|
+ }
|
|
+}
|