Add Fakeplayer
This commit is contained in:
@@ -0,0 +1,821 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Bacteriawa <A3167717663@hotmail.com>
|
||||
Date: Sat, 30 Aug 2025 17:16:32 +0800
|
||||
Subject: [PATCH] Leaves Fakeplayer
|
||||
|
||||
|
||||
diff --git a/net/minecraft/advancements/critereon/SimpleCriterionTrigger.java b/net/minecraft/advancements/critereon/SimpleCriterionTrigger.java
|
||||
index a82d84283632342bd30bc3449983431ba43583e0..f59526f6bfa1b4af5b474f0b438513c96afb491c 100644
|
||||
--- a/net/minecraft/advancements/critereon/SimpleCriterionTrigger.java
|
||||
+++ b/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> testTrigger) {
|
||||
+ if (player instanceof org.leavesmc.leaves.bot.ServerBot) return; // Leaves - bot skip
|
||||
PlayerAdvancements advancements = player.getAdvancements();
|
||||
Set<CriterionTrigger.Listener<T>> set = (Set) advancements.criterionData.get(this); // Paper - fix PlayerAdvancements leak
|
||||
if (set != null && !set.isEmpty()) {
|
||||
diff --git a/net/minecraft/network/Connection.java b/net/minecraft/network/Connection.java
|
||||
index 66ec0424a46dcd49cf44467357d80b1a2d84d3b2..04ae8de63af0a8abe578f14c8ef85fd4beca0969 100644
|
||||
--- a/net/minecraft/network/Connection.java
|
||||
+++ b/net/minecraft/network/Connection.java
|
||||
@@ -96,7 +96,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
@Nullable
|
||||
private volatile PacketListener disconnectListener;
|
||||
@Nullable
|
||||
- private volatile PacketListener packetListener;
|
||||
+ protected volatile PacketListener packetListener; // Leaves - private -> protected
|
||||
@Nullable
|
||||
private DisconnectionDetails disconnectionDetails;
|
||||
private boolean encrypted;
|
||||
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
|
||||
index af13707933af14171447d5ab6faacb64ddf194a7..e5e3a9c6443019d31496b1cf2154f5c3eaf6f972 100644
|
||||
--- a/net/minecraft/server/MinecraftServer.java
|
||||
+++ b/net/minecraft/server/MinecraftServer.java
|
||||
@@ -348,6 +348,8 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
}
|
||||
// Folia end - regionised ticking
|
||||
|
||||
+ private org.leavesmc.leaves.bot.BotList botList; // Leaves - fakeplayer
|
||||
+
|
||||
public static <S extends MinecraftServer> S spin(Function<Thread, S> threadFunction) {
|
||||
ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry.init(); // Paper - rewrite data converter system
|
||||
AtomicReference<S> atomicReference = new AtomicReference<>();
|
||||
@@ -1066,6 +1068,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
|
||||
LOGGER.info("Stopping server");
|
||||
Commands.COMMAND_SENDING_POOL.shutdownNow(); // Paper - Perf: Async command map building; Shutdown and don't bother finishing
|
||||
+ this.getBotList().removeAll(); // Leaves - save or remove bot
|
||||
// CraftBukkit start
|
||||
if (this.server != null) {
|
||||
if (false) this.server.spark.disable(); // Paper - spark // Luminol - Force disable builtin spark
|
||||
@@ -1588,7 +1591,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
int i = this.pauseWhileEmptySeconds() * 20;
|
||||
this.removeDisabledPluginsBlockingSleep(); // Paper - API to allow/disallow tick sleeping
|
||||
if (false && i > 0) { // Folia - region threading - this is complicated to implement, and even if done correctly is messy
|
||||
- if (this.playerList.getPlayerCount() == 0 && !this.tickRateManager.isSprinting() && this.pluginsBlockingSleep.isEmpty()) { // Paper - API to allow/disallow tick sleeping
|
||||
+ if (this.playerList.getPlayerCount() == 0 && this.botList.bots.isEmpty() && !this.tickRateManager.isSprinting() && this.pluginsBlockingSleep.isEmpty()) { // Paper - API to allow/disallow tick sleeping // Leaves - fakeplayer
|
||||
this.emptyTicks++;
|
||||
} else {
|
||||
this.emptyTicks = 0;
|
||||
@@ -1912,6 +1915,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
|
||||
public void tickConnection() {
|
||||
this.getConnection().tick();
|
||||
+ this.botList.networkTick(); // Leaves - fakeplayer
|
||||
}
|
||||
|
||||
private void synchronizeTime(ServerLevel level) {
|
||||
@@ -2990,6 +2994,16 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
return 0;
|
||||
}
|
||||
|
||||
+ // Leaves start - fakeplayer
|
||||
+ protected void setBotList(org.leavesmc.leaves.bot.BotList botList) {
|
||||
+ this.botList = botList;
|
||||
+ }
|
||||
+
|
||||
+ public org.leavesmc.leaves.bot.BotList getBotList() {
|
||||
+ return botList;
|
||||
+ }
|
||||
+ // Leaves end - fakeplayer
|
||||
+
|
||||
public record ReloadableResources(CloseableResourceManager resourceManager, ReloadableServerResources managers) implements AutoCloseable {
|
||||
@Override
|
||||
public void close() {
|
||||
diff --git a/net/minecraft/server/PlayerAdvancements.java b/net/minecraft/server/PlayerAdvancements.java
|
||||
index fdeca41d40705f28864ce4443d01cd872c9d51b0..5c0e338dc1b0eb5724d10a73d6fc7975f9d2e5e5 100644
|
||||
--- a/net/minecraft/server/PlayerAdvancements.java
|
||||
+++ b/net/minecraft/server/PlayerAdvancements.java
|
||||
@@ -167,6 +167,11 @@ public class PlayerAdvancements {
|
||||
}
|
||||
|
||||
public boolean award(AdvancementHolder advancement, String criterionKey) {
|
||||
+ // 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 orStartProgress = this.getOrStartProgress(advancement);
|
||||
boolean isDone = orStartProgress.isDone();
|
||||
diff --git a/net/minecraft/server/commands/BanIpCommands.java b/net/minecraft/server/commands/BanIpCommands.java
|
||||
index bb5dbfeb6915a808d6f70e332bf9f0a3f9b7d19a..62f23d47b55eb59956d69153d508b5c9ab544adf 100644
|
||||
--- a/net/minecraft/server/commands/BanIpCommands.java
|
||||
+++ b/net/minecraft/server/commands/BanIpCommands.java
|
||||
@@ -44,6 +44,12 @@ public class BanIpCommands {
|
||||
return banIp(source, username, reason);
|
||||
} else {
|
||||
ServerPlayer playerByName = source.getServer().getPlayerList().getPlayerByName(username);
|
||||
+ // Leaves start - disable ban
|
||||
+ if (playerByName instanceof org.leavesmc.leaves.bot.ServerBot) {
|
||||
+ source.sendFailure(Component.literal("Permission denied"));
|
||||
+ return 0;
|
||||
+ }
|
||||
+ // Leaves end - disable ban
|
||||
if (playerByName != null) {
|
||||
return banIp(source, playerByName.getIpAddress(), reason);
|
||||
} else {
|
||||
diff --git a/net/minecraft/server/commands/BanPlayerCommands.java b/net/minecraft/server/commands/BanPlayerCommands.java
|
||||
index ac3ba9d0ea344fa189912d359b718fbe05e7aa49..61a4db144f721b47ac1000df72263bceb6e38ec0 100644
|
||||
--- a/net/minecraft/server/commands/BanPlayerCommands.java
|
||||
+++ b/net/minecraft/server/commands/BanPlayerCommands.java
|
||||
@@ -44,8 +44,16 @@ public class BanPlayerCommands {
|
||||
private static int banPlayers(CommandSourceStack source, Collection<GameProfile> gameProfiles, @Nullable Component reason) throws CommandSyntaxException {
|
||||
UserBanList bans = source.getServer().getPlayerList().getBans();
|
||||
int i = 0;
|
||||
-
|
||||
+ boolean hasBot = false; // Leaves - disable kick
|
||||
for (GameProfile gameProfile : gameProfiles) {
|
||||
+ // Leaves start - disable ban
|
||||
+ if (gameProfile instanceof org.leavesmc.leaves.bot.BotList.CustomGameProfile) {
|
||||
+ source.sendFailure(Component.literal("Permission denied"));
|
||||
+ hasBot = true;
|
||||
+ continue;
|
||||
+ }
|
||||
+ // Leaves end - disable ban
|
||||
+ ServerPlayer player = source.getServer().getPlayerList().getPlayer(gameProfile.getId());
|
||||
if (!bans.isBanned(gameProfile)) {
|
||||
UserBanListEntry userBanListEntry = new UserBanListEntry(
|
||||
gameProfile, null, source.getTextName(), null, reason == null ? null : reason.getString()
|
||||
@@ -55,7 +63,6 @@ public class BanPlayerCommands {
|
||||
source.sendSuccess(
|
||||
() -> Component.translatable("commands.ban.success", Component.literal(gameProfile.getName()), userBanListEntry.getReason()), true
|
||||
);
|
||||
- ServerPlayer player = source.getServer().getPlayerList().getPlayer(gameProfile.getId());
|
||||
if (player != null) {
|
||||
player.connection.disconnect(Component.translatable("multiplayer.disconnect.banned"), org.bukkit.event.player.PlayerKickEvent.Cause.BANNED); // Paper - kick event cause
|
||||
}
|
||||
@@ -63,7 +70,13 @@ public class BanPlayerCommands {
|
||||
}
|
||||
|
||||
if (i == 0) {
|
||||
- throw ERROR_ALREADY_BANNED.create();
|
||||
+ // Leaves start - disable kick
|
||||
+ if (hasBot) {
|
||||
+ return i;
|
||||
+ } else {
|
||||
+ throw ERROR_ALREADY_BANNED.create();
|
||||
+ }
|
||||
+ // Leaves end - disable kick
|
||||
} else {
|
||||
return i;
|
||||
}
|
||||
diff --git a/net/minecraft/server/commands/KickCommand.java b/net/minecraft/server/commands/KickCommand.java
|
||||
index 14e2e0fcf20c8fa875bbefb97a673be4928d099a..8af40a77dc3da1599da2793168488f88686b8785 100644
|
||||
--- a/net/minecraft/server/commands/KickCommand.java
|
||||
+++ b/net/minecraft/server/commands/KickCommand.java
|
||||
@@ -46,9 +46,17 @@ public class KickCommand {
|
||||
if (!source.getServer().isPublished()) {
|
||||
throw ERROR_SINGLEPLAYER.create();
|
||||
} else {
|
||||
+ boolean hasBot = false; // Leaves - disable kick
|
||||
int i = 0;
|
||||
|
||||
for (ServerPlayer serverPlayer : players) {
|
||||
+ // Leaves start - disable kick
|
||||
+ if (serverPlayer instanceof org.leavesmc.leaves.bot.ServerBot) {
|
||||
+ source.sendFailure(Component.literal("Permission denied"));
|
||||
+ hasBot = true;
|
||||
+ continue;
|
||||
+ }
|
||||
+ // Leaves end - disable kick
|
||||
if (!source.getServer().isSingleplayerOwner(serverPlayer.getGameProfile())) {
|
||||
serverPlayer.connection.disconnect(reason, org.bukkit.event.player.PlayerKickEvent.Cause.KICK_COMMAND); // Paper - kick event cause
|
||||
source.sendSuccess(() -> Component.translatable("commands.kick.success", serverPlayer.getDisplayName(), reason), true);
|
||||
@@ -57,7 +65,13 @@ public class KickCommand {
|
||||
}
|
||||
|
||||
if (i == 0) {
|
||||
- throw ERROR_KICKING_OWNER.create();
|
||||
+ // Leaves start - disable kick
|
||||
+ if (hasBot) {
|
||||
+ return i;
|
||||
+ } else {
|
||||
+ throw ERROR_KICKING_OWNER.create();
|
||||
+ }
|
||||
+ // Leaves end - disable kick
|
||||
} else {
|
||||
return i;
|
||||
}
|
||||
diff --git a/net/minecraft/server/commands/OpCommand.java b/net/minecraft/server/commands/OpCommand.java
|
||||
index f2286b96b8f40b4588f817913c42ae7b4a92340f..e6c7bbb023000b9de90c1256274ff5aba4a6478a 100644
|
||||
--- a/net/minecraft/server/commands/OpCommand.java
|
||||
+++ b/net/minecraft/server/commands/OpCommand.java
|
||||
@@ -43,6 +43,7 @@ public class OpCommand {
|
||||
int i = 0;
|
||||
|
||||
for (GameProfile gameProfile : gameProfiles) {
|
||||
+ if (gameProfile instanceof org.leavesmc.leaves.bot.BotList.CustomGameProfile) continue; // Leaves - disable op
|
||||
if (!playerList.isOp(gameProfile)) {
|
||||
playerList.op(gameProfile);
|
||||
i++;
|
||||
diff --git a/net/minecraft/server/dedicated/DedicatedServer.java b/net/minecraft/server/dedicated/DedicatedServer.java
|
||||
index 79c911003c7b1c3d94e7e9d3974eeadcc16bde3a..328428f0ef8a2305f661c167e6fe1f604b0d6890 100644
|
||||
--- a/net/minecraft/server/dedicated/DedicatedServer.java
|
||||
+++ b/net/minecraft/server/dedicated/DedicatedServer.java
|
||||
@@ -162,6 +162,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
|
||||
}
|
||||
|
||||
// Spigot start
|
||||
+ this.setBotList(new org.leavesmc.leaves.bot.BotList(this)); // Leaves - fakeplayer
|
||||
this.setPlayerList(new DedicatedPlayerList(this, this.registries(), this.playerDataStorage));
|
||||
org.spigotmc.SpigotConfig.init((java.io.File) this.options.valueOf("spigot-settings"));
|
||||
org.spigotmc.SpigotConfig.registerCommands();
|
||||
@@ -173,6 +174,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
|
||||
// Paper end - initialize global and world-defaults configuration
|
||||
me.earthme.luminol.config.ConfigManager.loadConfigFiles(); // Luminol - load config file
|
||||
fun.bm.lophine.utils.ServerI18nUtil.init(); // Lophine - I18n support
|
||||
+ this.getBotList().loadBotInfo(); // Leaves - load resident bot info
|
||||
if (false) this.server.spark.enableEarlyIfRequested(); // Paper - spark // Luminol - Force disable builtin spark
|
||||
// Paper start - fix converting txt to json file; convert old users earlier after PlayerList creation but before file load/save
|
||||
if (this.convertOldUsers()) {
|
||||
diff --git a/net/minecraft/server/level/ChunkMap.java b/net/minecraft/server/level/ChunkMap.java
|
||||
index 70740381c6501c1a518c52b24381edd16792507f..5e31b499a894113b4be1982a1071348fcbaf9ded 100644
|
||||
--- a/net/minecraft/server/level/ChunkMap.java
|
||||
+++ b/net/minecraft/server/level/ChunkMap.java
|
||||
@@ -134,7 +134,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
||||
public final AtomicInteger tickingGenerated = new AtomicInteger(); // Paper - public
|
||||
private final String storageName;
|
||||
//private final PlayerMap playerMap = new PlayerMap(); // Folia - region threading
|
||||
- //public final Int2ObjectMap<ChunkMap.TrackedEntity> entityMap = new Int2ObjectOpenHashMap<>(); // Folia - region threading
|
||||
+ public final Int2ObjectMap<ChunkMap.TrackedEntity> entityMap = new Int2ObjectOpenHashMap<>(); // Folia - region threading
|
||||
private final Long2ByteMap chunkTypeCache = new Long2ByteOpenHashMap();
|
||||
// Paper - rewrite chunk system
|
||||
public int serverViewDistance;
|
||||
@@ -1309,6 +1309,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.needSendFakeData(player)) {
|
||||
+ bot.sendFakeData(player.connection, false);
|
||||
+ }
|
||||
+ }
|
||||
+ // Leaves end - render bot
|
||||
}
|
||||
}
|
||||
}
|
||||
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
|
||||
index 576dde41a4b48aac9079cca902f4371a31470af3..952b92c55d7192791a19ecebe80894f6cf992049 100644
|
||||
--- a/net/minecraft/server/level/ServerLevel.java
|
||||
+++ b/net/minecraft/server/level/ServerLevel.java
|
||||
@@ -207,6 +207,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
||||
private final StructureCheck structureCheck;
|
||||
public final boolean tickTime; // Folia - region threading
|
||||
private final RandomSequences randomSequences;
|
||||
+ final List<ServerPlayer> realPlayers; // Leaves - skip
|
||||
|
||||
// CraftBukkit start
|
||||
public final LevelStorageSource.LevelStorageAccess levelStorageAccess;
|
||||
@@ -635,6 +636,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
||||
// Paper end - rewrite chunk system
|
||||
this.getCraftServer().addWorld(this.getWorld()); // CraftBukkit
|
||||
this.updateTickData(); // Folia - region threading - make sure it is initialised before ticked
|
||||
+ this.realPlayers = Lists.newArrayList(); // Leaves - skip
|
||||
}
|
||||
|
||||
// Folia start - region threading
|
||||
@@ -2389,6 +2391,12 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
||||
return this.players;
|
||||
}
|
||||
|
||||
+ // Leaves start - fakeplayer skip
|
||||
+ public List<ServerPlayer> realPlayers() {
|
||||
+ return this.realPlayers;
|
||||
+ }
|
||||
+ // Leaves end - fakeplayer skip
|
||||
+
|
||||
@Override
|
||||
public void updatePOIOnBlockStateChange(BlockPos pos, BlockState oldState, BlockState newState) {
|
||||
Optional<Holder<PoiType>> optional = PoiTypes.forState(oldState);
|
||||
@@ -2826,6 +2834,11 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
||||
// 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 serverPlayer) {
|
||||
ServerLevel.this.players.add(serverPlayer);
|
||||
+ // Leaves start - skip
|
||||
+ if (!(serverPlayer instanceof org.leavesmc.leaves.bot.ServerBot)) {
|
||||
+ ServerLevel.this.realPlayers.add(serverPlayer);
|
||||
+ }
|
||||
+ // Leaves end - skip
|
||||
if (serverPlayer.isReceivingWaypoints()) {
|
||||
ServerLevel.this.getWaypointManager().addPlayer(serverPlayer);
|
||||
}
|
||||
@@ -2913,6 +2926,11 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
||||
ServerLevel.this.getChunkSource().removeEntity(entity);
|
||||
if (entity instanceof ServerPlayer serverPlayer) {
|
||||
ServerLevel.this.players.remove(serverPlayer);
|
||||
+ // Leaves start - skip
|
||||
+ if (!(serverPlayer instanceof org.leavesmc.leaves.bot.ServerBot)) {
|
||||
+ ServerLevel.this.realPlayers.remove(serverPlayer);
|
||||
+ }
|
||||
+ // Leaves end - skip
|
||||
ServerLevel.this.getWaypointManager().removePlayer(serverPlayer);
|
||||
ServerLevel.this.updateSleepingPlayerList();
|
||||
}
|
||||
diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java
|
||||
index c29e9c6cb2513fe4b01a619e16433893efb44e6f..58ffb996e0f8dce56c4def265dc51ed49ee31df4 100644
|
||||
--- a/net/minecraft/server/level/ServerPlayer.java
|
||||
+++ b/net/minecraft/server/level/ServerPlayer.java
|
||||
@@ -219,7 +219,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
|
||||
private static final boolean DEFAULT_SPAWN_EXTRA_PARTICLES_ON_FALL = false;
|
||||
public ServerGamePacketListenerImpl connection;
|
||||
private final MinecraftServer server;
|
||||
- public final ServerPlayerGameMode gameMode;
|
||||
+ public ServerPlayerGameMode gameMode; // Leaves - not final
|
||||
private final PlayerAdvancements advancements;
|
||||
private final ServerStatsCounter stats;
|
||||
private float lastRecordedHealthAndAbsorption = Float.MIN_VALUE;
|
||||
@@ -232,7 +232,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
|
||||
private int lastSentFood = -99999999;
|
||||
private boolean lastFoodSaturationZero = true;
|
||||
public int lastSentExp = -99999999;
|
||||
- private int spawnInvulnerableTime = 60; // Lophine - spawn invulnerable time
|
||||
+ public int spawnInvulnerableTime = 60; // Lophine - spawn invulnerable time
|
||||
private ChatVisiblity chatVisibility = ChatVisiblity.FULL;
|
||||
public ParticleStatus particleStatus = ParticleStatus.ALL;
|
||||
private boolean canChatColor = true;
|
||||
@@ -241,7 +241,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
|
||||
private Entity camera;
|
||||
public boolean isChangingDimension;
|
||||
public boolean seenCredits = false;
|
||||
- private final ServerRecipeBook recipeBook;
|
||||
+ protected ServerRecipeBook recipeBook; // Leaves - not final and private -> protected
|
||||
@Nullable
|
||||
private Vec3 levitationStartPos;
|
||||
private int levitationStartTime;
|
||||
@@ -436,6 +436,10 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
|
||||
// Paper start - rewrite chunk system
|
||||
private ca.spottedleaf.moonrise.patches.chunk_system.player.RegionizedPlayerChunkLoader.PlayerChunkLoaderData chunkLoader;
|
||||
private final ca.spottedleaf.moonrise.patches.chunk_system.player.RegionizedPlayerChunkLoader.ViewDistanceHolder viewDistanceHolder = new ca.spottedleaf.moonrise.patches.chunk_system.player.RegionizedPlayerChunkLoader.ViewDistanceHolder();
|
||||
+ // Leaves start - player operation limiter
|
||||
+ private int instaBreakCountPerTick = 0;
|
||||
+ private int placeBlockCountPerTick = 0;
|
||||
+ // Leaves end - player operation limiter
|
||||
|
||||
@Override
|
||||
public final boolean moonrise$isRealPlayer() {
|
||||
@@ -924,6 +928,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
|
||||
}
|
||||
// CraftBukkit end
|
||||
this.tickClientLoadTimeout();
|
||||
+ this.resetOperationCountPerTick(); // Leaves - player operation limiter
|
||||
this.gameMode.tick();
|
||||
this.wardenSpawnTracker.tick();
|
||||
if (this.spawnInvulnerableTime > 0) --this.spawnInvulnerableTime; // Lophine - spawn invulnerable time
|
||||
@@ -2029,6 +2034,11 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
|
||||
this.lastSentHealth = -1.0F;
|
||||
this.lastSentFood = -1;
|
||||
this.teleportSpectators(teleportTransition, serverLevel);
|
||||
+ // Leaves start - bot support
|
||||
+ if (fun.bm.lophine.config.modules.misc.FakeplayerConfig.enable) {
|
||||
+ this.server.getBotList().bots.forEach(bot -> bot.sendFakeDataIfNeed(this, true)); // Leaves - render bot
|
||||
+ }
|
||||
+ // Leaves end - bot support
|
||||
// CraftBukkit start
|
||||
org.bukkit.event.player.PlayerChangedWorldEvent changeEvent = new org.bukkit.event.player.PlayerChangedWorldEvent(this.getBukkitEntity(), serverLevel.getWorld());
|
||||
this.level().getCraftServer().getPluginManager().callEvent(changeEvent);
|
||||
@@ -3577,4 +3587,31 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
|
||||
return (org.bukkit.craftbukkit.entity.CraftPlayer) super.getBukkitEntity();
|
||||
}
|
||||
// CraftBukkit end
|
||||
+
|
||||
+ // Leaves start - player operation limiter
|
||||
+ protected void resetOperationCountPerTick() {
|
||||
+ instaBreakCountPerTick = 0;
|
||||
+ placeBlockCountPerTick = 0;
|
||||
+ }
|
||||
+
|
||||
+ public int getInstaBreakCountPerTick() {
|
||||
+ return instaBreakCountPerTick;
|
||||
+ }
|
||||
+
|
||||
+ public int getPlaceBlockCountPerTick() {
|
||||
+ return placeBlockCountPerTick;
|
||||
+ }
|
||||
+
|
||||
+ public void addInstaBreakCountPerTick() {
|
||||
+ ++instaBreakCountPerTick;
|
||||
+ }
|
||||
+
|
||||
+ public void addPlaceBlockCountPerTick() {
|
||||
+ ++placeBlockCountPerTick;
|
||||
+ }
|
||||
+
|
||||
+ public boolean allowOperation() {
|
||||
+ return (instaBreakCountPerTick == 0 || placeBlockCountPerTick == 0) && (instaBreakCountPerTick <= 1 && placeBlockCountPerTick <= 2);
|
||||
+ }
|
||||
+ // Leaves end - player operation limiter
|
||||
}
|
||||
diff --git a/net/minecraft/server/players/GameProfileCache.java b/net/minecraft/server/players/GameProfileCache.java
|
||||
index 144a2644c15f276f02bb3be859dc5d05a677ac55..c6a89e8936cffa49b3de153ed3c40e3cc9b8ae61 100644
|
||||
--- a/net/minecraft/server/players/GameProfileCache.java
|
||||
+++ b/net/minecraft/server/players/GameProfileCache.java
|
||||
@@ -125,6 +125,12 @@ public class GameProfileCache {
|
||||
// Paper end
|
||||
|
||||
public Optional<GameProfile> get(String name) {
|
||||
+ // Leaves start - fix bot
|
||||
+ org.leavesmc.leaves.bot.ServerBot bot = org.leavesmc.leaves.bot.BotList.INSTANCE.getBotByName(name);
|
||||
+ if (bot != null) {
|
||||
+ return Optional.of(bot.getGameProfile());
|
||||
+ }
|
||||
+ // Leaves end - fix bot
|
||||
String string = name.toLowerCase(Locale.ROOT);
|
||||
boolean stateLocked = true; try { this.stateLock.lock(); // Paper - Fix GameProfileCache concurrency
|
||||
GameProfileCache.GameProfileInfo gameProfileInfo = this.profilesByName.get(string);
|
||||
diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java
|
||||
index cb863a36fc67e993956ec3be655e6e4b9764c051..cd50b39a910f6a4bc07a4065cc4e6a84ab1e8750 100644
|
||||
--- a/net/minecraft/server/players/PlayerList.java
|
||||
+++ b/net/minecraft/server/players/PlayerList.java
|
||||
@@ -425,6 +425,19 @@ public abstract class PlayerList {
|
||||
|
||||
org.leavesmc.leaves.protocol.core.LeavesProtocolManager.handlePlayerJoin(player);
|
||||
|
||||
+ // Leaves start - bot support
|
||||
+ if (fun.bm.lophine.config.modules.misc.FakeplayerConfig.enable) {
|
||||
+ org.leavesmc.leaves.bot.ServerBot bot = this.server.getBotList().getBotByName(player.getScoreboardName());
|
||||
+ if (bot != null) {
|
||||
+ this.server.getBotList().removeBot(bot, org.leavesmc.leaves.event.bot.BotRemoveEvent.RemoveReason.INTERNAL, player.getBukkitEntity(), false);
|
||||
+ }
|
||||
+ this.server.getBotList().bots.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
|
||||
@@ -922,6 +935,12 @@ public abstract class PlayerList {
|
||||
}
|
||||
// Paper end - Add PlayerPostRespawnEvent
|
||||
|
||||
+ // Leaves start - bot support
|
||||
+ if (fun.bm.lophine.config.modules.misc.FakeplayerConfig.enable) {
|
||||
+ this.server.getBotList().bots.forEach(bot -> bot.sendFakeDataIfNeed(serverPlayer, true)); // Leaves - render bot
|
||||
+ }
|
||||
+ // Leaves end - bot support
|
||||
+
|
||||
// CraftBukkit end
|
||||
|
||||
return serverPlayer;
|
||||
@@ -1026,11 +1045,16 @@ public abstract class PlayerList {
|
||||
|
||||
public String[] getPlayerNamesArray() {
|
||||
List<ServerPlayer> players = new java.util.ArrayList<>(this.players); // Folia - region threading
|
||||
- String[] strings = new String[this.players.size()];
|
||||
+ String[] strings = new String[this.players.size() + this.server.getBotList().bots.size()]; // Leaves - fakeplayer support
|
||||
|
||||
for (int i = 0; i < players.size(); i++) { // Folia - region threading
|
||||
strings[i] = players.get(i).getGameProfile().getName(); // Folia - region threading
|
||||
}
|
||||
+ // Leaves start - fakeplayer support
|
||||
+ for (int i = this.players.size(); i < strings.length; ++i) {
|
||||
+ strings[i] = this.server.getBotList().bots.get(i - this.players.size()).getGameProfile().getName();
|
||||
+ }
|
||||
+ // Leaves end - fakeplayer support
|
||||
|
||||
return strings;
|
||||
}
|
||||
@@ -1141,7 +1165,14 @@ public abstract class PlayerList {
|
||||
|
||||
@Nullable
|
||||
public ServerPlayer getPlayerByName(String username) {
|
||||
- return this.playersByName.get(username.toLowerCase(java.util.Locale.ROOT)); // Spigot
|
||||
+ // Leaves start - fakeplayer support
|
||||
+ username = username.toLowerCase(java.util.Locale.ROOT);
|
||||
+ ServerPlayer player = this.playersByName.get(username);
|
||||
+ if (player == null) {
|
||||
+ player = this.server.getBotList().getBotByName(username);
|
||||
+ }
|
||||
+ return player; // Spigot
|
||||
+ // Leaves end - fakeplayer support
|
||||
}
|
||||
|
||||
public void broadcast(@Nullable Player except, double x, double y, double z, double radius, ResourceKey<Level> dimension, Packet<?> packet) {
|
||||
@@ -1482,7 +1513,13 @@ public abstract class PlayerList {
|
||||
|
||||
@Nullable
|
||||
public ServerPlayer getPlayer(UUID playerUUID) {
|
||||
- return this.playersByUUID.get(playerUUID);
|
||||
+ // Leaves start - fakeplayer support
|
||||
+ ServerPlayer player = this.playersByUUID.get(playerUUID);
|
||||
+ if (player == null) {
|
||||
+ player = this.server.getBotList().getBot(playerUUID);
|
||||
+ }
|
||||
+ return player;
|
||||
+ // Leaves start - fakeplayer support
|
||||
}
|
||||
|
||||
public boolean canBypassPlayerLimit(GameProfile profile) {
|
||||
diff --git a/net/minecraft/server/waypoints/ServerWaypointManager.java b/net/minecraft/server/waypoints/ServerWaypointManager.java
|
||||
index 36e9b027ce5213f8914728293633e78e9905c1b2..a8c2a93cc12bd5aa6760bed91baa680f3801ee5e 100644
|
||||
--- a/net/minecraft/server/waypoints/ServerWaypointManager.java
|
||||
+++ b/net/minecraft/server/waypoints/ServerWaypointManager.java
|
||||
@@ -22,6 +22,11 @@ public class ServerWaypointManager implements WaypointManager<WaypointTransmitte
|
||||
|
||||
@Override
|
||||
public void trackWaypoint(WaypointTransmitter waypoint) {
|
||||
+ // Leaves start - fakeplayer
|
||||
+ if (waypoint instanceof org.leavesmc.leaves.bot.ServerBot bot && !bot.getConfigValue(org.leavesmc.leaves.bot.agent.Configs.ENABLE_LOCATOR_BAR)) {
|
||||
+ return;
|
||||
+ }
|
||||
+ // Leaves end - fakeplayer
|
||||
// Lophine start - unsafe waypoint bar
|
||||
if (!fun.bm.lophine.config.modules.experiment.WaypointConfig.forceEnableWaypointUnsafe) return;
|
||||
this.waypoints.add(waypoint);
|
||||
@@ -57,6 +62,11 @@ public class ServerWaypointManager implements WaypointManager<WaypointTransmitte
|
||||
|
||||
public void addPlayer(ServerPlayer player) {
|
||||
// Folia - region threading
|
||||
+ // Leaves start - fakeplayer
|
||||
+ if (player instanceof org.leavesmc.leaves.bot.ServerBot) {
|
||||
+ return;
|
||||
+ }
|
||||
+ // Leaves end - fakeplayer
|
||||
// Lophine start - unsafe waypoint bar
|
||||
if (!fun.bm.lophine.config.modules.experiment.WaypointConfig.forceEnableWaypointUnsafe) return;
|
||||
this.players.add(player);
|
||||
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
|
||||
index ee4a459700d75676eae3422d4a0e10be73c45871..16c323ead67a9a4ef48d25670b89297a15457849 100644
|
||||
--- a/net/minecraft/world/entity/Entity.java
|
||||
+++ b/net/minecraft/world/entity/Entity.java
|
||||
@@ -1225,7 +1225,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
|
||||
BlockPos onPosLegacy = this.getOnPosLegacy();
|
||||
BlockState blockState = this.level().getBlockState(onPosLegacy);
|
||||
- if (this.isLocalInstanceAuthoritative()) {
|
||||
+ if (this.isLocalInstanceAuthoritative() || this instanceof org.leavesmc.leaves.bot.ServerBot) { // Leaves - ServerBot needs check fall damage
|
||||
this.checkFallDamage(vec3.y, this.onGround(), blockState, onPosLegacy);
|
||||
}
|
||||
|
||||
@@ -1539,7 +1539,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
}
|
||||
|
||||
// Paper start - optimise collisions
|
||||
- private Vec3 collide(Vec3 movement) {
|
||||
+ public Vec3 collide(Vec3 movement) { // Leaves - private -> public
|
||||
final boolean xZero = movement.x == 0.0;
|
||||
final boolean yZero = movement.y == 0.0;
|
||||
final boolean zZero = movement.z == 0.0;
|
||||
diff --git a/net/minecraft/world/entity/LivingEntity.java b/net/minecraft/world/entity/LivingEntity.java
|
||||
index a8348a073682eec31990e9ab963b11ad32cf8bf8..8ce6f3f1a931628e018ce9a2e0c404b8c8a4d992 100644
|
||||
--- a/net/minecraft/world/entity/LivingEntity.java
|
||||
+++ b/net/minecraft/world/entity/LivingEntity.java
|
||||
@@ -3212,7 +3212,7 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin
|
||||
private void travelRidden(Player player, Vec3 travelVector) {
|
||||
Vec3 riddenInput = this.getRiddenInput(player, travelVector);
|
||||
this.tickRidden(player, riddenInput);
|
||||
- if (this.canSimulateMovement()) {
|
||||
+ if (this.canSimulateMovement() || this.getControllingPassenger() instanceof org.leavesmc.leaves.bot.ServerBot) { // Leaves - Fakeplayer
|
||||
this.setSpeed(this.getRiddenSpeed(player));
|
||||
this.travel(riddenInput);
|
||||
} else {
|
||||
@@ -4046,7 +4046,7 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin
|
||||
// Paper start - lag compensate eating
|
||||
// we add 1 to the expected time to avoid lag compensating when we should not
|
||||
final boolean shouldLagCompensate = this.useItem.has(DataComponents.FOOD) && this.eatStartTime != -1 && (System.nanoTime() - this.eatStartTime) > ((1L + this.totalEatTimeTicks) * 50L * (1000L * 1000L));
|
||||
- if ((--this.useItemRemaining == 0 || shouldLagCompensate) && !this.level().isClientSide && !usingItem.useOnRelease()) {
|
||||
+ if ((--this.useItemRemaining == 0 || shouldLagCompensate) && !(this instanceof org.leavesmc.leaves.bot.ServerBot) && !this.level().isClientSide && !usingItem.useOnRelease()) { // Leaves - Fakeplayer skip this check
|
||||
this.useItemRemaining = 0;
|
||||
// Paper end - lag compensate eating
|
||||
this.completeUsingItem();
|
||||
@@ -4223,6 +4223,23 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin
|
||||
this.stopUsingItem();
|
||||
}
|
||||
|
||||
+ // Leaves start - Fakeplayer
|
||||
+ public boolean releaseUsingItemWithResult() {
|
||||
+ ItemStack itemInHand = this.getItemInHand(this.getUsedItemHand());
|
||||
+ boolean result = false;
|
||||
+ if (!this.useItem.isEmpty() && ItemStack.isSameItem(itemInHand, this.useItem)) {
|
||||
+ this.useItem = itemInHand;
|
||||
+ result = this.useItem.releaseUsingWithResult(this.level(), this, this.getUseItemRemainingTicks());
|
||||
+ if (this.useItem.useOnRelease()) {
|
||||
+ this.updatingUsingItem();
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ this.stopUsingItem();
|
||||
+ return result;
|
||||
+ }
|
||||
+ // Leaves end - Fakeplayer
|
||||
+
|
||||
public void stopUsingItem() {
|
||||
if (!this.level().isClientSide) {
|
||||
boolean isUsingItem = this.isUsingItem();
|
||||
diff --git a/net/minecraft/world/entity/player/Player.java b/net/minecraft/world/entity/player/Player.java
|
||||
index 6c933c27ce155eb516a9ede96fb2c3bd19251bfa..6e0784af90aab5c25100613f97d4492fa7d1b277 100644
|
||||
--- a/net/minecraft/world/entity/player/Player.java
|
||||
+++ b/net/minecraft/world/entity/player/Player.java
|
||||
@@ -205,7 +205,7 @@ public abstract class Player extends LivingEntity {
|
||||
private int lastLevelUpTime;
|
||||
public GameProfile gameProfile;
|
||||
private boolean reducedDebugInfo;
|
||||
- private ItemStack lastItemInMainHand = ItemStack.EMPTY;
|
||||
+ protected ItemStack lastItemInMainHand = ItemStack.EMPTY;
|
||||
private final ItemCooldowns cooldowns = this.createItemCooldowns();
|
||||
private Optional<GlobalPos> lastDeathLocation = Optional.empty();
|
||||
@Nullable
|
||||
@@ -412,6 +412,12 @@ public abstract class Player extends LivingEntity {
|
||||
}
|
||||
}
|
||||
|
||||
+ // Leaves start - fakeplayer
|
||||
+ protected void livingEntityTick() {
|
||||
+ super.tick();
|
||||
+ }
|
||||
+ // Leaves end - fakeplayer
|
||||
+
|
||||
@Override
|
||||
protected float getMaxHeadRotationRelativeToBody() {
|
||||
return this.isBlocking() ? 15.0F : super.getMaxHeadRotationRelativeToBody();
|
||||
@@ -714,7 +720,7 @@ public abstract class Player extends LivingEntity {
|
||||
}
|
||||
}
|
||||
|
||||
- private void touch(Entity entity) {
|
||||
+ public void touch(Entity entity) { // Leaves - private -> public
|
||||
entity.playerTouch(this);
|
||||
}
|
||||
|
||||
@@ -1339,7 +1345,7 @@ public abstract class Player extends LivingEntity {
|
||||
this.sweepAttack();
|
||||
}
|
||||
|
||||
- if (target instanceof ServerPlayer && target.hurtMarked) {
|
||||
+ if ((target instanceof ServerPlayer && !(target instanceof org.leavesmc.leaves.bot.ServerBot)) && target.hurtMarked) { // Leaves - bot knockback
|
||||
// CraftBukkit start - Add Velocity Event
|
||||
boolean cancelled = false;
|
||||
org.bukkit.entity.Player player = (org.bukkit.entity.Player) target.getBukkitEntity();
|
||||
diff --git a/net/minecraft/world/entity/projectile/FishingHook.java b/net/minecraft/world/entity/projectile/FishingHook.java
|
||||
index 7760ab1fe365b4bf1ab6c2ff953e8659a28008a8..76f6b0932633bc5670a1cdc76ff78b3bc725401d 100644
|
||||
--- a/net/minecraft/world/entity/projectile/FishingHook.java
|
||||
+++ b/net/minecraft/world/entity/projectile/FishingHook.java
|
||||
@@ -58,7 +58,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/net/minecraft/world/entity/vehicle/AbstractBoat.java b/net/minecraft/world/entity/vehicle/AbstractBoat.java
|
||||
index 91ac54fb0ae9a06945476cbd1caa20be32c4a9fe..be380db0cb2f243b4a3cf8bd1b06d38debc5f6fd 100644
|
||||
--- a/net/minecraft/world/entity/vehicle/AbstractBoat.java
|
||||
+++ b/net/minecraft/world/entity/vehicle/AbstractBoat.java
|
||||
@@ -271,6 +271,11 @@ public abstract class AbstractBoat extends VehicleEntity implements Leashable {
|
||||
}
|
||||
|
||||
this.move(MoverType.SELF, this.getDeltaMovement());
|
||||
+ } else if (this.getControllingPassenger() instanceof org.leavesmc.leaves.bot.ServerBot) { // Leaves start - Fakeplayer
|
||||
+ this.floatBoat();
|
||||
+ this.controlBoat();
|
||||
+ this.move(MoverType.SELF, this.getDeltaMovement());
|
||||
+ // Leaves end - Fakeplayer
|
||||
} else {
|
||||
this.setDeltaMovement(Vec3.ZERO);
|
||||
}
|
||||
@@ -383,6 +388,13 @@ public abstract class AbstractBoat extends VehicleEntity implements Leashable {
|
||||
}
|
||||
}
|
||||
|
||||
+ // Leaves start - Fakeplayer
|
||||
+ @Override
|
||||
+ public boolean canSimulateMovement() {
|
||||
+ return super.canSimulateMovement() || this.getControllingPassenger() instanceof org.leavesmc.leaves.bot.ServerBot;
|
||||
+ }
|
||||
+ // Leaves end - Fakeplayer
|
||||
+
|
||||
@Nullable
|
||||
protected SoundEvent getPaddleSound() {
|
||||
return switch (this.getStatus()) {
|
||||
diff --git a/net/minecraft/world/inventory/AbstractContainerMenu.java b/net/minecraft/world/inventory/AbstractContainerMenu.java
|
||||
index bdc0ca1f625dc264bef6e5722ec66121ba48625a..525610be29d5077a3199287347b1b71cf9fd8abf 100644
|
||||
--- a/net/minecraft/world/inventory/AbstractContainerMenu.java
|
||||
+++ b/net/minecraft/world/inventory/AbstractContainerMenu.java
|
||||
@@ -407,6 +407,7 @@ public abstract class AbstractContainerMenu {
|
||||
|
||||
private void doClick(int slotId, int button, ClickType clickType, Player player) {
|
||||
Inventory inventory = player.getInventory();
|
||||
+ if (!doClickCheck(slotId, button, clickType, player)) return; // Leaves - doClick check
|
||||
if (clickType == ClickType.QUICK_CRAFT) {
|
||||
int i = this.quickcraftStatus;
|
||||
this.quickcraftStatus = getQuickcraftHeader(button);
|
||||
@@ -685,6 +686,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").orElse(false);
|
||||
+ }
|
||||
+ return true;
|
||||
+ }
|
||||
+ // Leaves end - doClick check
|
||||
+
|
||||
private boolean tryItemClickBehaviourOverride(Player player, ClickAction action, Slot slot, ItemStack clickedItem, ItemStack carriedItem) {
|
||||
FeatureFlagSet featureFlagSet = player.level().enabledFeatures();
|
||||
return carriedItem.isItemEnabled(featureFlagSet) && carriedItem.overrideStackedOnOther(slot, action, player)
|
||||
diff --git a/net/minecraft/world/item/ItemStack.java b/net/minecraft/world/item/ItemStack.java
|
||||
index a27e1d53abc2d8b8912226add33510067e10ed52..be0a267e5564a30c16f408cb311a448f354d60b4 100644
|
||||
--- a/net/minecraft/world/item/ItemStack.java
|
||||
+++ b/net/minecraft/world/item/ItemStack.java
|
||||
@@ -450,7 +450,7 @@ public final class ItemStack implements DataComponentHolder, ChangePublisher<net
|
||||
placeEvent = org.bukkit.craftbukkit.event.CraftEventFactory.callBlockPlaceEvent(serverLevel, player, hand, blocks.getFirst(), clickedPos);
|
||||
}
|
||||
|
||||
- if (placeEvent != null && (placeEvent.isCancelled() || !placeEvent.canBuild())) {
|
||||
+ if (placeEvent != null && (placeEvent.isCancelled() || !placeEvent.canBuild()) && (!(player instanceof org.leavesmc.leaves.bot.ServerBot))) { // Leaves - Fakeplayer skip this check
|
||||
interactionResult = InteractionResult.FAIL; // cancel placement
|
||||
// PAIL: Remove this when MC-99075 fixed
|
||||
player.containerMenu.forceHeldSlot(hand);
|
||||
@@ -946,6 +946,20 @@ public final class ItemStack implements DataComponentHolder, ChangePublisher<net
|
||||
}
|
||||
}
|
||||
|
||||
+ // Leaves start - Fakeplayer
|
||||
+ public boolean releaseUsingWithResult(Level level, LivingEntity livingEntity, int timeLeft) {
|
||||
+ ItemStack itemStack = this.copy();
|
||||
+ if (this.getItem().releaseUsing(this, level, livingEntity, timeLeft)) {
|
||||
+ ItemStack itemStack1 = this.applyAfterUseComponentSideEffects(livingEntity, itemStack);
|
||||
+ if (itemStack1 != this) {
|
||||
+ livingEntity.setItemInHand(livingEntity.getUsedItemHand(), itemStack1);
|
||||
+ }
|
||||
+ return true;
|
||||
+ }
|
||||
+ return false;
|
||||
+ }
|
||||
+ // Leaves end - Fakeplayer
|
||||
+
|
||||
public boolean useOnRelease() {
|
||||
return this.getItem().useOnRelease(this);
|
||||
}
|
||||
diff --git a/net/minecraft/world/level/block/piston/PistonMovingBlockEntity.java b/net/minecraft/world/level/block/piston/PistonMovingBlockEntity.java
|
||||
index bff9574bece5f5581d9cc51a3f2b68b5c13c25c9..4bfe53c15c9b2effd24dddc48565efc9c102e56a 100644
|
||||
--- a/net/minecraft/world/level/block/piston/PistonMovingBlockEntity.java
|
||||
+++ b/net/minecraft/world/level/block/piston/PistonMovingBlockEntity.java
|
||||
@@ -145,7 +145,7 @@ public class PistonMovingBlockEntity extends BlockEntity {
|
||||
break;
|
||||
}
|
||||
|
||||
- if (!(entity instanceof ServerPlayer)) {
|
||||
+ if (!(entity instanceof ServerPlayer) || (entity instanceof org.leavesmc.leaves.bot.ServerBot)) { // Leaves - bot slime block
|
||||
Vec3 deltaMovement = entity.getDeltaMovement();
|
||||
double d1 = deltaMovement.x;
|
||||
double d2 = deltaMovement.y;
|
||||
diff --git a/net/minecraft/world/level/levelgen/PhantomSpawner.java b/net/minecraft/world/level/levelgen/PhantomSpawner.java
|
||||
index ecfd6a04ea1bac75bbb7e88554b73fc67e7f7358..2c9a55eecf44eaa62ef57597a94d657ad0b9cfc4 100644
|
||||
--- a/net/minecraft/world/level/levelgen/PhantomSpawner.java
|
||||
+++ b/net/minecraft/world/level/levelgen/PhantomSpawner.java
|
||||
@@ -49,6 +49,11 @@ public class PhantomSpawner implements CustomSpawner {
|
||||
ServerStatsCounter stats = serverPlayer.getStats();
|
||||
int i = Mth.clamp(stats.getValue(Stats.CUSTOM.get(Stats.TIME_SINCE_REST)), 1, Integer.MAX_VALUE);
|
||||
int i1 = 24000;
|
||||
+ // Leaves start - fakeplayer spawn
|
||||
+ if (serverPlayer instanceof org.leavesmc.leaves.bot.ServerBot bot && bot.getConfigValue(org.leavesmc.leaves.bot.agent.Configs.SPAWN_PHANTOM)) {
|
||||
+ i1 = Math.max(bot.notSleepTicks, 1);
|
||||
+ }
|
||||
+ // Leaves end - fakeplayer spawn
|
||||
if (randomSource.nextInt(i) >= 72000) {
|
||||
BlockPos blockPos1 = blockPos.above(20 + randomSource.nextInt(15))
|
||||
.east(-10 + randomSource.nextInt(21))
|
||||
diff --git a/net/minecraft/world/level/storage/LevelResource.java b/net/minecraft/world/level/storage/LevelResource.java
|
||||
index bef794c3f58c41d910aa0bcc63fbdeea7225fddf..a601da588e6973cc5b87d3e3eeba49b53f6d9a6d 100644
|
||||
--- a/net/minecraft/world/level/storage/LevelResource.java
|
||||
+++ b/net/minecraft/world/level/storage/LevelResource.java
|
||||
@@ -15,7 +15,7 @@ public class LevelResource {
|
||||
public static final LevelResource ROOT = new LevelResource(".");
|
||||
private final String id;
|
||||
|
||||
- private LevelResource(String id) {
|
||||
+ public LevelResource(String id) { // Leaves - private -> public
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
diff --git a/net/minecraft/world/level/storage/PlayerDataStorage.java b/net/minecraft/world/level/storage/PlayerDataStorage.java
|
||||
index fe44d8d17d2622b3d6021c11579af85ef96737bb..0aae211dc2048f8cd14213c2a868394d1ed16070 100644
|
||||
--- a/net/minecraft/world/level/storage/PlayerDataStorage.java
|
||||
+++ b/net/minecraft/world/level/storage/PlayerDataStorage.java
|
||||
@@ -19,7 +19,7 @@ import net.minecraft.util.datafix.DataFixTypes;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
-public class PlayerDataStorage {
|
||||
+public class PlayerDataStorage implements org.leavesmc.leaves.bot.IPlayerDataStorage {
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
private final File playerDir;
|
||||
protected final DataFixer fixerUpper;
|
||||
@@ -8,7 +8,7 @@ As a part of : Leaves (https://github.com/LeavesMC/Leaves/blob/ea91106ae57fc4cc1
|
||||
Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html)
|
||||
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
||||
index 4f7077b592f84334483f8edf3656fccd5b90a9ca..26d5a96edd283a8f8fd50222e2671cef2c4b257b 100644
|
||||
index 5d05a8a843e605b6cb6d16883d17dd73d4c21e06..57ea85aebc72eb002d3dcf04144499feeb82081a 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
||||
@@ -497,6 +497,7 @@ public final class CraftServer implements Server {
|
||||
|
||||
@@ -0,0 +1,171 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Bacteriawa <A3167717663@hotmail.com>
|
||||
Date: Sat, 30 Aug 2025 17:07:02 +0800
|
||||
Subject: [PATCH] Leaves Fakeplayer
|
||||
|
||||
|
||||
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 9180f23529a31b6b0a5b38bb7cda3e32d487f691..5a047006895cf6e0d58502f9502d1b49abaf84bf 100644
|
||||
--- a/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java
|
||||
+++ b/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java
|
||||
@@ -47,6 +47,23 @@ 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.Bot) {
|
||||
+ return;
|
||||
+ }
|
||||
+ // Leaves end - skip bot
|
||||
+
|
||||
+ // Leaves start - process bot load/save
|
||||
+ if (fun.bm.lophine.config.modules.misc.FakeplayerConfig.enable && fun.bm.lophine.config.modules.misc.FakeplayerConfig.canResident) {
|
||||
+ if (event instanceof org.bukkit.event.world.WorldLoadEvent worldLoadEvent) {
|
||||
+ org.leavesmc.leaves.bot.BotList.INSTANCE.loadResume(worldLoadEvent.getWorld().getUID().toString());
|
||||
+ } else if (event instanceof org.bukkit.event.world.WorldUnloadEvent worldUnloadEvent) {
|
||||
+ org.leavesmc.leaves.bot.BotList.INSTANCE.removeAllIn(worldUnloadEvent.getWorld().getUID().toString());
|
||||
+ }
|
||||
+ }
|
||||
+ // Leaves end - process bot load/save
|
||||
+
|
||||
+
|
||||
for (RegisteredListener registration : listeners) {
|
||||
if (!registration.getPlugin().isEnabled()) {
|
||||
continue;
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftRegionAccessor.java b/src/main/java/org/bukkit/craftbukkit/CraftRegionAccessor.java
|
||||
index 0a10f49ee410d93e95ceb90108200a1a9d12b54b..d2eee37d810a6d5cf514bc71dea66a4d2b0d09e2 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/CraftRegionAccessor.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/CraftRegionAccessor.java
|
||||
@@ -423,6 +423,7 @@ public abstract class CraftRegionAccessor implements RegionAccessor {
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends Entity> T addEntity(T entity) {
|
||||
Preconditions.checkArgument(!entity.isInWorld(), "Entity has already been added to a world");
|
||||
+ Preconditions.checkState(!(entity instanceof org.leavesmc.leaves.entity.bot.CraftBot), "[Leaves] Fakeplayers do not support changing world, Please use leaves fakeplayer-api instead!");
|
||||
net.minecraft.world.entity.Entity nmsEntity = ((CraftEntity) entity).getHandle();
|
||||
if (nmsEntity.level() != this.getHandle().getLevel()) {
|
||||
nmsEntity = nmsEntity.teleport(new TeleportTransition(this.getHandle().getLevel(), nmsEntity, TeleportTransition.DO_NOTHING));
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
||||
index 57ea85aebc72eb002d3dcf04144499feeb82081a..e03de696e7652b7e211bf61ff6d6cdf44a587146 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
||||
@@ -316,6 +316,7 @@ public final class CraftServer implements Server {
|
||||
private final io.papermc.paper.potion.PaperPotionBrewer potionBrewer;
|
||||
public io.papermc.paper.SparksFly spark; // Paper - spark // Luminol - Force disable builtin spark
|
||||
private final ServerConfiguration serverConfig = new PaperServerConfiguration();
|
||||
+ private final org.leavesmc.leaves.entity.bot.CraftBotManager botManager; // Leaves
|
||||
|
||||
// Paper start - Folia region threading API
|
||||
private final io.papermc.paper.threadedregions.scheduler.FoliaRegionScheduler regionizedScheduler = new io.papermc.paper.threadedregions.scheduler.FoliaRegionScheduler(); // Folia - region threading
|
||||
@@ -498,6 +499,7 @@ public final class CraftServer implements Server {
|
||||
datapackManager = new io.papermc.paper.datapack.PaperDatapackManager(console.getPackRepository()); // Paper
|
||||
if (false) this.spark = new io.papermc.paper.SparksFly(this); // Paper - spark // Luminol - Force disable builtin spark
|
||||
org.leavesmc.leaves.protocol.core.LeavesProtocolManager.init(); // Leaves - protocol
|
||||
+ this.botManager = new org.leavesmc.leaves.entity.bot.CraftBotManager(); // Leaves
|
||||
}
|
||||
|
||||
public boolean getCommandBlockOverride(String command) {
|
||||
@@ -1482,7 +1484,7 @@ public final class CraftServer implements Server {
|
||||
return false;
|
||||
}
|
||||
|
||||
- if (!handle.players().isEmpty()) {
|
||||
+ if (!handle.realPlayers().isEmpty()) { // Leaves - skip
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -3260,6 +3262,13 @@ public final class CraftServer implements Server {
|
||||
this.console.addPluginAllowingSleep(plugin.getName(), value);
|
||||
}
|
||||
|
||||
+ // Leaves start - Bot API
|
||||
+ @Override
|
||||
+ public org.leavesmc.leaves.entity.bot.CraftBotManager getBotManager() {
|
||||
+ return botManager;
|
||||
+ }
|
||||
+ // Leaves end - Bot API
|
||||
+
|
||||
// Folia start - region TPS API
|
||||
/**
|
||||
* Gets the TPS from the region which owns the specified location, or {@code null} if no region owns
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
||||
index 9b9eea4babcd67a088e9400b369d168d11c06ac1..3177db282013e6dc3c7215fc2a851e2630a29409 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
||||
@@ -249,7 +249,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
|
||||
|
||||
@Override
|
||||
public int getPlayerCount() {
|
||||
- return world.players().size();
|
||||
+ return world.realPlayers().size(); // Leaves - skip
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1279,9 +1279,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)) {
|
||||
@@ -1988,7 +1988,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));
|
||||
}
|
||||
}
|
||||
@@ -2016,7 +2016,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) {
|
||||
@@ -2248,7 +2248,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().sendParticlesSource(
|
||||
- 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
|
||||
force,
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
|
||||
index 852e1ffef6a022caad7c8eff34091e50112a2290..8eb5d014d9ed688ffebaffb4ce0bb40896961a04 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
|
||||
@@ -126,6 +126,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.bot.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 ffb10b412d19907e6f63b7e3ca67ef061eec96ba..de14df49b11f1243de9aa6870a3c8e3f429502fd 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
|
||||
@@ -897,7 +897,11 @@ 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 - disable bot death event
|
||||
// Paper start - make cancellable
|
||||
if (event.isCancelled()) {
|
||||
return event;
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
package fun.bm.lophine.config.modules.misc;
|
||||
|
||||
import com.electronwill.nightconfig.core.file.CommentedFileConfig;
|
||||
import me.earthme.luminol.config.IConfigModule;
|
||||
import me.earthme.luminol.config.flags.ConfigClassInfo;
|
||||
import me.earthme.luminol.config.flags.ConfigInfo;
|
||||
import me.earthme.luminol.enums.EnumConfigCategory;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.leavesmc.leaves.bot.BotCommand;
|
||||
import org.leavesmc.leaves.bot.ServerBot;
|
||||
import org.leavesmc.leaves.bot.agent.Actions;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
@ConfigClassInfo(configAttribution = EnumConfigCategory.MISC, mainName = "fakeplayer")
|
||||
public class FakeplayerConfig implements IConfigModule {
|
||||
@ConfigInfo(baseName = "enable", comments = """
|
||||
Enable fakeplayer functionality""")
|
||||
public static boolean enable = true;
|
||||
|
||||
@ConfigInfo(baseName = "unable-fakeplayer-names", comments = """
|
||||
List of names that cannot be used for fakeplayers""")
|
||||
public static List<String> unableNames = List.of("player-name");
|
||||
|
||||
@ConfigInfo(baseName = "limit", comments = """
|
||||
Maximum number of fakeplayers allowed""")
|
||||
public static int limit = 10;
|
||||
|
||||
@ConfigInfo(baseName = "prefix", comments = """
|
||||
Prefix for fakeplayer names""")
|
||||
public static String prefix = "";
|
||||
|
||||
@ConfigInfo(baseName = "suffix", comments = """
|
||||
Suffix for fakeplayer names""")
|
||||
public static String suffix = "";
|
||||
|
||||
@ConfigInfo(baseName = "regen-amount", comments = """
|
||||
Regeneration amount for fakeplayers""")
|
||||
public static double regenAmount = 0.0;
|
||||
|
||||
@ConfigInfo(baseName = "resident-fakeplayer", comments = """
|
||||
Allow fakeplayers to be resident""")
|
||||
public static boolean canResident = false;
|
||||
|
||||
@ConfigInfo(baseName = "open-fakeplayer-inventory", comments = """
|
||||
Allow opening fakeplayer inventory""")
|
||||
public static boolean canOpenInventory = false;
|
||||
|
||||
@ConfigInfo(baseName = "use-action", comments = """
|
||||
Allow fakeplayers to use actions""")
|
||||
public static boolean canUseAction = true;
|
||||
|
||||
@ConfigInfo(baseName = "modify-config", comments = """
|
||||
Allow modifying fakeplayer config""")
|
||||
public static boolean canModifyConfig = false;
|
||||
|
||||
@ConfigInfo(baseName = "manual-save-and-load", comments = """
|
||||
Allow manual save and load of fakeplayers""")
|
||||
public static boolean canManualSaveAndLoad = false;
|
||||
|
||||
@ConfigInfo(baseName = "cache-skin", comments = """
|
||||
Use skin cache for fakeplayers""")
|
||||
public static boolean useSkinCache = false;
|
||||
|
||||
@ConfigInfo(baseName = "always-send-data", comments = """
|
||||
Always send data for fakeplayers""")
|
||||
public static boolean canSendDataAlways = true;
|
||||
|
||||
@ConfigInfo(baseName = "skip-sleep-check", comments = """
|
||||
Skip sleep check for fakeplayers""")
|
||||
public static boolean canSkipSleep = false;
|
||||
|
||||
@ConfigInfo(baseName = "spawn-phantom", comments = """
|
||||
Allow phantoms to spawn for fakeplayers""")
|
||||
public static boolean canSpawnPhantom = false;
|
||||
|
||||
@ConfigInfo(baseName = "simulation-distance", comments = """
|
||||
Simulation distance for fakeplayers (-1 for default)""")
|
||||
public static int simulationDistance = -1;
|
||||
|
||||
@ConfigInfo(baseName = "enable-locator-bar", comments = """
|
||||
Enable locator bar for fakeplayers""")
|
||||
public static boolean enableLocatorBar = false;
|
||||
|
||||
public static ServerBot.TickType tickType = ServerBot.TickType.ENTITY_LIST;
|
||||
|
||||
public static void unregisterCommand(String name) {
|
||||
name = name.toLowerCase(Locale.ENGLISH).trim();
|
||||
MinecraftServer.getServer().server.getCommandMap().getKnownCommands().remove(name);
|
||||
MinecraftServer.getServer().server.getCommandMap().getKnownCommands().remove("leaves:" + name);
|
||||
MinecraftServer.getServer().server.syncCommands();
|
||||
}
|
||||
|
||||
public static int getSimulationDistance(ServerBot bot) {
|
||||
return simulationDistance == -1 ? bot.getBukkitEntity().getSimulationDistance() : simulationDistance;
|
||||
}
|
||||
|
||||
public void onLoaded(CommentedFileConfig configInstance) {
|
||||
if (enable) {
|
||||
Bukkit.getCommandMap().register("bot", "lophine", new BotCommand());
|
||||
Actions.registerAll();
|
||||
} else {
|
||||
unregisterCommand("bot");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,486 @@
|
||||
package org.leavesmc.leaves.bot;
|
||||
|
||||
import com.mojang.logging.LogUtils;
|
||||
import fun.bm.lophine.config.modules.misc.FakeplayerConfig;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.kyori.adventure.text.format.TextColor;
|
||||
import org.bukkit.Bukkit;
|
||||
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.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.leavesmc.leaves.bot.agent.AbstractBotConfig;
|
||||
import org.leavesmc.leaves.bot.agent.Actions;
|
||||
import org.leavesmc.leaves.bot.agent.Configs;
|
||||
import org.leavesmc.leaves.bot.agent.actions.ServerBotAction;
|
||||
import org.leavesmc.leaves.command.CommandArgumentResult;
|
||||
import org.leavesmc.leaves.entity.bot.Bot;
|
||||
import org.leavesmc.leaves.event.bot.BotActionStopEvent;
|
||||
import org.leavesmc.leaves.event.bot.BotConfigModifyEvent;
|
||||
import org.leavesmc.leaves.event.bot.BotCreateEvent;
|
||||
import org.leavesmc.leaves.event.bot.BotRemoveEvent;
|
||||
import org.leavesmc.leaves.plugin.MinecraftInternalPlugin;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static net.kyori.adventure.text.Component.text;
|
||||
|
||||
public class BotCommand extends Command {
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
public BotCommand() {
|
||||
super("bot");
|
||||
this.setPermission("lophine.bot");
|
||||
this.setDescription("FakePlayer Command");
|
||||
this.setUsage("/bot <create|remove|list|action|config|save|load> [args...]");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(@NotNull CommandSender sender, @NotNull String commandLabel, @NotNull String[] args) {
|
||||
if (!testPermission(sender)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!FakeplayerConfig.enable) {
|
||||
sender.sendMessage(Component.text("Fakeplayer feature is disabled!").color(TextColor.color(255, 0, 0)));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (args.length == 0) {
|
||||
sender.sendMessage(Component.text("Usage: /bot <create|remove|list|action|config|save|load> [args...]").color(TextColor.color(255, 255, 0)));
|
||||
return true;
|
||||
}
|
||||
|
||||
String subCommand = args[0].toLowerCase();
|
||||
switch (subCommand) {
|
||||
case "create" -> {
|
||||
return handleCreate(sender, args);
|
||||
}
|
||||
case "remove" -> {
|
||||
return handleRemove(sender, args);
|
||||
}
|
||||
case "list" -> {
|
||||
return handleList(sender, args);
|
||||
}
|
||||
case "action" -> {
|
||||
return handleAction(sender, args);
|
||||
}
|
||||
case "config" -> {
|
||||
return handleConfig(sender, args);
|
||||
}
|
||||
case "save" -> {
|
||||
return handleSave(sender, args);
|
||||
}
|
||||
case "load" -> {
|
||||
return handleLoad(sender, args);
|
||||
}
|
||||
default -> {
|
||||
sender.sendMessage(Component.text("Unknown subcommand: " + subCommand).color(TextColor.color(255, 0, 0)));
|
||||
sender.sendMessage(Component.text("Available commands: create, remove, list, action, config, save, load").color(TextColor.color(255, 255, 0)));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean handleCreate(CommandSender sender, String[] args) {
|
||||
if (args.length < 2) {
|
||||
sender.sendMessage(text("Use /bot create <name> [skin_name] to create a fakeplayer", NamedTextColor.RED));
|
||||
return true;
|
||||
}
|
||||
|
||||
String botName = args[1];
|
||||
String fullName = BotUtil.getFullName(botName);
|
||||
if (this.canCreate(sender, fullName)) {
|
||||
BotCreateState.Builder builder = BotCreateState.builder(botName, Bukkit.getWorlds().getFirst().getSpawnLocation())
|
||||
.createReason(BotCreateEvent.CreateReason.COMMAND)
|
||||
.creator(sender);
|
||||
|
||||
if (args.length >= 3) {
|
||||
builder.skinName(args[2]);
|
||||
}
|
||||
|
||||
if (sender instanceof Player player) {
|
||||
builder.location(player.getLocation());
|
||||
} else if (sender instanceof ConsoleCommandSender) {
|
||||
if (args.length >= 7) {
|
||||
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) {
|
||||
builder.location(new Location(world, x, y, z));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOGGER.warn("Can't build location", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
builder.spawnWithSkin(null);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean handleRemove(CommandSender sender, String[] args) {
|
||||
if (args.length < 2 || args.length > 5) {
|
||||
sender.sendMessage(text("Usage: /bot remove <name> [hour] [minute] [second]", NamedTextColor.RED));
|
||||
return true;
|
||||
}
|
||||
|
||||
String botName = args[1];
|
||||
BotList botList = BotList.INSTANCE;
|
||||
ServerBot bot = botList.getBotByName(BotUtil.getFullName(botName));
|
||||
|
||||
if (bot == null) {
|
||||
sender.sendMessage(text("This fakeplayer is not in server", NamedTextColor.RED));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (args.length == 3 && args[2].equals("cancel")) {
|
||||
if (bot.removeTaskId == -1) {
|
||||
sender.sendMessage(text("This fakeplayer is not scheduled to be removed", NamedTextColor.RED));
|
||||
return true;
|
||||
}
|
||||
Bukkit.getScheduler().cancelTask(bot.removeTaskId);
|
||||
bot.removeTaskId = -1;
|
||||
sender.sendMessage(text("Remove cancel"));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (args.length > 2) {
|
||||
long time = 0;
|
||||
int h;
|
||||
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(text("Usage: /bot remove <name> [hour] [minute] [second]", NamedTextColor.RED));
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean isReschedule = bot.removeTaskId != -1;
|
||||
|
||||
if (isReschedule) {
|
||||
Bukkit.getScheduler().cancelTask(bot.removeTaskId);
|
||||
}
|
||||
bot.removeTaskId = Bukkit.getScheduler().runTaskLater(MinecraftInternalPlugin.INSTANCE, () -> {
|
||||
bot.removeTaskId = -1;
|
||||
botList.removeBot(bot, BotRemoveEvent.RemoveReason.COMMAND, sender, false);
|
||||
}, time).getTaskId();
|
||||
|
||||
sender.sendMessage("This fakeplayer will be removed in " + h + "h " + m + "m " + s + "s" + (isReschedule ? " (rescheduled)" : ""));
|
||||
return true;
|
||||
}
|
||||
|
||||
botList.removeBot(bot, BotRemoveEvent.RemoveReason.COMMAND, sender, false);
|
||||
sender.sendMessage(text("Removed fakeplayer: " + botName, NamedTextColor.GREEN));
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean handleList(CommandSender sender, String[] args) {
|
||||
BotList botList = BotList.INSTANCE;
|
||||
|
||||
if (args.length < 2) {
|
||||
Map<World, List<String>> botMap = new HashMap<>();
|
||||
for (World world : Bukkit.getWorlds()) {
|
||||
botMap.put(world, new ArrayList<>());
|
||||
}
|
||||
|
||||
for (ServerBot bot : botList.bots) {
|
||||
Bot bukkitBot = bot.getBukkitEntity();
|
||||
botMap.get(bukkitBot.getWorld()).add(bukkitBot.getName());
|
||||
}
|
||||
|
||||
sender.sendMessage("Total number: (" + botList.bots.size() + "/" + FakeplayerConfig.limit + ")");
|
||||
for (World world : botMap.keySet()) {
|
||||
sender.sendMessage(world.getName() + "(" + botMap.get(world).size() + "): " + formatPlayerNameList(botMap.get(world)));
|
||||
}
|
||||
} else {
|
||||
World world = Bukkit.getWorld(args[1]);
|
||||
|
||||
if (world == null) {
|
||||
sender.sendMessage(text("Unknown world", NamedTextColor.RED));
|
||||
return true;
|
||||
}
|
||||
|
||||
List<String> snowBotList = new ArrayList<>();
|
||||
for (ServerBot bot : botList.bots) {
|
||||
Bot bukkitBot = bot.getBukkitEntity();
|
||||
if (bukkitBot.getWorld() == world) {
|
||||
snowBotList.add(bukkitBot.getName());
|
||||
}
|
||||
}
|
||||
|
||||
sender.sendMessage(world.getName() + "(" + snowBotList.size() + "): " + formatPlayerNameList(snowBotList));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean handleAction(CommandSender sender, String[] args) {
|
||||
if (!FakeplayerConfig.canUseAction) {
|
||||
sender.sendMessage(text("Bot action feature is disabled!", NamedTextColor.RED));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (args.length < 3) {
|
||||
sender.sendMessage(text("Use /bot action <name> <action> to make fakeplayer do action", NamedTextColor.RED));
|
||||
return true;
|
||||
}
|
||||
|
||||
ServerBot bot = BotList.INSTANCE.getBotByName(args[1]);
|
||||
if (bot == null) {
|
||||
sender.sendMessage(text("This fakeplayer is not in server", NamedTextColor.RED));
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (args[2].toLowerCase()) {
|
||||
case "list" -> {
|
||||
sender.sendMessage(bot.getScoreboardName() + "'s action list:");
|
||||
for (int i = 0; i < bot.getBotActions().size(); i++) {
|
||||
sender.sendMessage(i + " " + bot.getBotActions().get(i).getName());
|
||||
}
|
||||
}
|
||||
case "start" -> executeActionStart(bot, sender, args);
|
||||
case "stop" -> executeActionStop(bot, sender, args);
|
||||
default -> sender.sendMessage(text("Unknown action command. Use: list, start, stop", NamedTextColor.RED));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void executeActionStart(ServerBot bot, CommandSender sender, String[] args) {
|
||||
if (args.length < 4) {
|
||||
sender.sendMessage(text("Invalid action", NamedTextColor.RED));
|
||||
return;
|
||||
}
|
||||
ServerBotAction<?> action = Actions.getForName(args[3]);
|
||||
if (action == null) {
|
||||
sender.sendMessage(text("Invalid action", NamedTextColor.RED));
|
||||
return;
|
||||
}
|
||||
|
||||
CraftPlayer player;
|
||||
if (sender instanceof CraftPlayer) {
|
||||
player = (CraftPlayer) sender;
|
||||
} else {
|
||||
player = bot.getBukkitEntity();
|
||||
}
|
||||
|
||||
String[] realArgs = Arrays.copyOfRange(args, 4, args.length);
|
||||
ServerBotAction<?> newAction;
|
||||
try {
|
||||
newAction = action.create();
|
||||
newAction.loadCommand(player.getHandle(), action.getArgument().parse(0, realArgs));
|
||||
} catch (IllegalArgumentException e) {
|
||||
sender.sendMessage(text("Action create error, please check your arguments, " + e.getMessage(), NamedTextColor.RED));
|
||||
return;
|
||||
}
|
||||
|
||||
if (bot.addBotAction(newAction, sender)) {
|
||||
sender.sendMessage("Action " + action.getName() + " has been issued to " + bot.getName().getString());
|
||||
}
|
||||
}
|
||||
|
||||
private void executeActionStop(ServerBot bot, CommandSender sender, String[] args) {
|
||||
if (args.length < 4) {
|
||||
sender.sendMessage(text("Invalid index", NamedTextColor.RED));
|
||||
return;
|
||||
}
|
||||
|
||||
String index = args[3];
|
||||
if (index.equals("all")) {
|
||||
Set<ServerBotAction<?>> forRemoval = new HashSet<>();
|
||||
for (int i = 0; i < bot.getBotActions().size(); i++) {
|
||||
ServerBotAction<?> action = bot.getBotActions().get(i);
|
||||
BotActionStopEvent event = new BotActionStopEvent(
|
||||
bot.getBukkitEntity(), action.getName(), action.getUUID(), BotActionStopEvent.Reason.COMMAND, sender
|
||||
);
|
||||
event.callEvent();
|
||||
if (!event.isCancelled()) {
|
||||
forRemoval.add(action);
|
||||
action.stop(bot, BotActionStopEvent.Reason.COMMAND);
|
||||
}
|
||||
}
|
||||
bot.getBotActions().removeAll(forRemoval);
|
||||
sender.sendMessage(bot.getScoreboardName() + "'s action list cleared.");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
int i = Integer.parseInt(index);
|
||||
if (i < 0 || i >= bot.getBotActions().size()) {
|
||||
sender.sendMessage(text("Invalid index", NamedTextColor.RED));
|
||||
return;
|
||||
}
|
||||
|
||||
ServerBotAction<?> action = bot.getBotActions().get(i);
|
||||
BotActionStopEvent event = new BotActionStopEvent(
|
||||
bot.getBukkitEntity(), action.getName(), action.getUUID(), BotActionStopEvent.Reason.COMMAND, sender
|
||||
);
|
||||
event.callEvent();
|
||||
if (!event.isCancelled()) {
|
||||
action.stop(bot, BotActionStopEvent.Reason.COMMAND);
|
||||
bot.getBotActions().remove(i);
|
||||
sender.sendMessage(bot.getScoreboardName() + "'s " + action.getName() + " stopped.");
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
sender.sendMessage(text("Invalid index", NamedTextColor.RED));
|
||||
}
|
||||
}
|
||||
|
||||
private boolean handleConfig(CommandSender sender, String[] args) {
|
||||
if (!FakeplayerConfig.canModifyConfig) {
|
||||
sender.sendMessage(text("Bot config feature is disabled!", NamedTextColor.RED));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (args.length < 3) {
|
||||
sender.sendMessage(text("Use /bot config <name> <config> to modify fakeplayer's config", NamedTextColor.RED));
|
||||
return true;
|
||||
}
|
||||
|
||||
ServerBot bot = BotList.INSTANCE.getBotByName(args[1]);
|
||||
if (bot == null) {
|
||||
sender.sendMessage(text("This fakeplayer is not in server", NamedTextColor.RED));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!Configs.getConfigNames().contains(args[2])) {
|
||||
sender.sendMessage(text("This config is not accept", NamedTextColor.RED));
|
||||
return true;
|
||||
}
|
||||
|
||||
AbstractBotConfig<?> config = bot.getConfig(Objects.requireNonNull(Configs.getConfig(args[2])));
|
||||
if (args.length < 4) {
|
||||
config.getMessage().forEach(sender::sendMessage);
|
||||
} else {
|
||||
String[] realArgs = Arrays.copyOfRange(args, 3, args.length);
|
||||
|
||||
BotConfigModifyEvent event = new BotConfigModifyEvent(bot.getBukkitEntity(), config.getName(), realArgs, sender);
|
||||
Bukkit.getPluginManager().callEvent(event);
|
||||
|
||||
if (event.isCancelled()) {
|
||||
return true;
|
||||
}
|
||||
CommandArgumentResult result = config.getArgument().parse(0, realArgs);
|
||||
|
||||
try {
|
||||
config.setFromCommand(result);
|
||||
config.getChangeMessage().forEach(sender::sendMessage);
|
||||
} catch (IllegalArgumentException e) {
|
||||
sender.sendMessage(text(e.getMessage(), NamedTextColor.RED));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean handleSave(CommandSender sender, String[] args) {
|
||||
if (!FakeplayerConfig.canManualSaveAndLoad) {
|
||||
sender.sendMessage(text("Bot save/load feature is disabled!", NamedTextColor.RED));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (args.length < 2) {
|
||||
sender.sendMessage(text("Use /bot save <name> to save a fakeplayer", NamedTextColor.RED));
|
||||
return true;
|
||||
}
|
||||
|
||||
BotList botList = BotList.INSTANCE;
|
||||
ServerBot bot = botList.getBotByName(args[1]);
|
||||
|
||||
if (bot == null) {
|
||||
sender.sendMessage(text("This fakeplayer is not in server", NamedTextColor.RED));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (botList.removeBot(bot, BotRemoveEvent.RemoveReason.COMMAND, sender, true)) {
|
||||
sender.sendMessage(bot.getScoreboardName() + " saved to " + bot.createState.realName());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean handleLoad(CommandSender sender, String[] args) {
|
||||
if (!FakeplayerConfig.canManualSaveAndLoad) {
|
||||
sender.sendMessage(text("Bot save/load feature is disabled!", NamedTextColor.RED));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (args.length < 2) {
|
||||
sender.sendMessage(text("Use /bot load <name> to load a fakeplayer", NamedTextColor.RED));
|
||||
return true;
|
||||
}
|
||||
|
||||
String realName = args[1];
|
||||
BotList botList = BotList.INSTANCE;
|
||||
if (!botList.getSavedBotList().contains(realName)) {
|
||||
sender.sendMessage(text("This fakeplayer is not saved", NamedTextColor.RED));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (botList.loadNewBot(realName) == null) {
|
||||
sender.sendMessage(text("Can't load bot, please check", NamedTextColor.RED));
|
||||
} else {
|
||||
sender.sendMessage(text("Successfully loaded fakeplayer: " + realName, NamedTextColor.GREEN));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String formatPlayerNameList(@NotNull List<String> list) {
|
||||
if (list.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
String string = list.toString();
|
||||
return string.substring(1, string.length() - 1);
|
||||
}
|
||||
|
||||
private boolean canCreate(CommandSender sender, @NotNull String name) {
|
||||
BotList botList = BotList.INSTANCE;
|
||||
if (!name.matches("^[a-zA-Z0-9_]{4,16}$")) {
|
||||
sender.sendMessage(text("This name is illegal", NamedTextColor.RED));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Bukkit.getPlayerExact(name) != null || botList.getBotByName(name) != null) {
|
||||
sender.sendMessage(text("This player is in server", NamedTextColor.RED));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (FakeplayerConfig.unableNames.contains(name)) {
|
||||
sender.sendMessage(text("This name is not allowed", NamedTextColor.RED));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (botList.bots.size() >= FakeplayerConfig.limit) {
|
||||
sender.sendMessage(text("Fakeplayer limit is full", NamedTextColor.RED));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
package org.leavesmc.leaves.bot;
|
||||
|
||||
import fun.bm.lophine.config.modules.misc.FakeplayerConfig;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.leavesmc.leaves.entity.bot.Bot;
|
||||
import org.leavesmc.leaves.entity.bot.BotCreator;
|
||||
import org.leavesmc.leaves.entity.bot.CraftBot;
|
||||
import org.leavesmc.leaves.event.bot.BotCreateEvent;
|
||||
import org.leavesmc.leaves.plugin.MinecraftInternalPlugin;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public record BotCreateState(String realName, String name, String skinName, String[] skin, Location location, BotCreateEvent.CreateReason createReason, CommandSender creator) {
|
||||
|
||||
private static final MinecraftServer server = MinecraftServer.getServer();
|
||||
|
||||
public ServerBot createNow() {
|
||||
return server.getBotList().createNewBot(this);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static Builder builder(@NotNull String realName, @Nullable Location location) {
|
||||
return new Builder(realName, location);
|
||||
}
|
||||
|
||||
public static class Builder implements BotCreator {
|
||||
|
||||
private final String realName;
|
||||
|
||||
private String name;
|
||||
private Location location;
|
||||
|
||||
private String skinName;
|
||||
private String[] skin;
|
||||
|
||||
private BotCreateEvent.CreateReason createReason;
|
||||
private CommandSender creator;
|
||||
|
||||
private Builder(@NotNull String realName, @Nullable Location location) {
|
||||
Objects.requireNonNull(realName);
|
||||
|
||||
this.realName = realName;
|
||||
this.location = location;
|
||||
|
||||
this.name = FakeplayerConfig.prefix + realName + FakeplayerConfig.suffix;
|
||||
this.skinName = this.realName;
|
||||
this.skin = null;
|
||||
this.createReason = BotCreateEvent.CreateReason.UNKNOWN;
|
||||
this.creator = null;
|
||||
}
|
||||
|
||||
public Builder name(@NotNull String name) {
|
||||
Objects.requireNonNull(name);
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder skinName(@Nullable String skinName) {
|
||||
this.skinName = skinName;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder skin(@Nullable String[] skin) {
|
||||
this.skin = skin;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder mojangAPISkin() {
|
||||
if (this.skinName != null) {
|
||||
this.skin = MojangAPI.getSkin(this.skinName);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder location(@NotNull Location location) {
|
||||
this.location = location;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder createReason(@NotNull BotCreateEvent.CreateReason createReason) {
|
||||
Objects.requireNonNull(createReason);
|
||||
this.createReason = createReason;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder creator(CommandSender creator) {
|
||||
this.creator = creator;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BotCreateState build() {
|
||||
return new BotCreateState(realName, name, skinName, skin, location, createReason, creator);
|
||||
}
|
||||
|
||||
public void spawnWithSkin(Consumer<Bot> consumer) {
|
||||
Bukkit.getRegionScheduler().execute(
|
||||
MinecraftInternalPlugin.INSTANCE,
|
||||
location.getWorld(),
|
||||
location.getBlockX() >> 4,
|
||||
location.getBlockZ() >> 4,
|
||||
() -> {
|
||||
this.mojangAPISkin();
|
||||
Bukkit.getRegionScheduler().execute(
|
||||
MinecraftInternalPlugin.INSTANCE,
|
||||
location.getWorld(),
|
||||
location.getBlockX() >> 4,
|
||||
location.getBlockZ() >> 4,
|
||||
() -> {
|
||||
CraftBot bot = this.spawn();
|
||||
if (bot != null && consumer != null) {
|
||||
consumer.accept(bot);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public CraftBot spawn() {
|
||||
Objects.requireNonNull(this.location);
|
||||
ServerBot bot = this.build().createNow();
|
||||
return bot != null ? bot.getBukkitEntity() : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
package org.leavesmc.leaves.bot;
|
||||
|
||||
import com.mojang.logging.LogUtils;
|
||||
import net.minecraft.core.UUIDUtil;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.NbtAccounter;
|
||||
import net.minecraft.nbt.NbtIo;
|
||||
import net.minecraft.util.ProblemReporter;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.level.storage.LevelResource;
|
||||
import net.minecraft.world.level.storage.LevelStorageSource;
|
||||
import net.minecraft.world.level.storage.TagValueInput;
|
||||
import net.minecraft.world.level.storage.ValueInput;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.leavesmc.leaves.util.TagUtil;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Optional;
|
||||
|
||||
public class BotDataStorage implements IPlayerDataStorage {
|
||||
|
||||
private static final LevelResource BOT_DATA_DIR = new LevelResource("fakeplayerdata");
|
||||
private static final LevelResource BOT_LIST_FILE = new LevelResource("fakeplayer.dat");
|
||||
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
private final File botDir;
|
||||
private final File botListFile;
|
||||
|
||||
private CompoundTag savedBotList;
|
||||
|
||||
public BotDataStorage(LevelStorageSource.@NotNull LevelStorageAccess session) {
|
||||
this.botDir = session.getLevelPath(BOT_DATA_DIR).toFile();
|
||||
this.botListFile = session.getLevelPath(BOT_LIST_FILE).toFile();
|
||||
this.botDir.mkdirs();
|
||||
|
||||
this.savedBotList = new CompoundTag();
|
||||
if (this.botListFile.exists() && this.botListFile.isFile()) {
|
||||
try {
|
||||
Optional.of(NbtIo.readCompressed(this.botListFile.toPath(), NbtAccounter.unlimitedHeap())).ifPresent(tag -> this.savedBotList = tag);
|
||||
} catch (Exception exception) {
|
||||
BotDataStorage.LOGGER.warn("Failed to load player data list");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(Player player) {
|
||||
boolean flag = true;
|
||||
try {
|
||||
CompoundTag nbt = TagUtil.saveEntityWithoutId(player);
|
||||
File file = new File(this.botDir, player.getStringUUID() + ".dat");
|
||||
|
||||
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(nbt, file.toPath());
|
||||
} catch (Exception exception) {
|
||||
BotDataStorage.LOGGER.warn("Failed to save fakeplayer data for {}", player.getScoreboardName(), exception);
|
||||
flag = false;
|
||||
}
|
||||
|
||||
if (flag && player instanceof ServerBot bot) {
|
||||
CompoundTag nbt = new CompoundTag();
|
||||
nbt.putString("name", bot.createState.name());
|
||||
nbt.store("uuid", UUIDUtil.CODEC, bot.getUUID());
|
||||
nbt.putBoolean("resume", bot.resume);
|
||||
this.savedBotList.put(bot.createState.realName(), nbt);
|
||||
this.saveBotList();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<ValueInput> load(Player player, ProblemReporter reporter) {
|
||||
return this.load(player.getScoreboardName(), player.getStringUUID()).map(nbt -> {
|
||||
ValueInput valueInput = TagValueInput.create(reporter, player.registryAccess(), nbt);
|
||||
player.load(valueInput);
|
||||
return valueInput;
|
||||
});
|
||||
}
|
||||
|
||||
private Optional<CompoundTag> load(String name, String uuid) {
|
||||
File file = new File(this.botDir, uuid + ".dat");
|
||||
|
||||
if (file.exists() && file.isFile()) {
|
||||
try {
|
||||
Optional<CompoundTag> optional = Optional.of(NbtIo.readCompressed(file.toPath(), NbtAccounter.unlimitedHeap()));
|
||||
if (!file.delete()) {
|
||||
throw new IOException("Failed to delete fakeplayer data");
|
||||
}
|
||||
this.savedBotList.remove(name);
|
||||
this.saveBotList();
|
||||
return optional;
|
||||
} catch (Exception exception) {
|
||||
BotDataStorage.LOGGER.warn("Failed to load fakeplayer data for {}", name);
|
||||
}
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
public Optional<CompoundTag> read(String uuid) {
|
||||
File file = new File(this.botDir, uuid + ".dat");
|
||||
if (file.exists() && file.isFile()) {
|
||||
try {
|
||||
return Optional.of(NbtIo.readCompressed(file.toPath(), NbtAccounter.unlimitedHeap()));
|
||||
} catch (Exception exception) {
|
||||
BotDataStorage.LOGGER.warn("Failed to read fakeplayer data for {}", uuid);
|
||||
}
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
private void saveBotList() {
|
||||
try {
|
||||
if (this.botListFile.exists() && this.botListFile.isFile()) {
|
||||
if (!this.botListFile.delete()) {
|
||||
throw new IOException("Failed to delete file: " + this.botListFile);
|
||||
}
|
||||
}
|
||||
if (!this.botListFile.createNewFile()) {
|
||||
throw new IOException("Failed to create nbt file: " + this.botListFile);
|
||||
}
|
||||
NbtIo.writeCompressed(this.savedBotList, this.botListFile.toPath());
|
||||
} catch (Exception exception) {
|
||||
BotDataStorage.LOGGER.warn("Failed to save player data list");
|
||||
}
|
||||
}
|
||||
|
||||
public CompoundTag getSavedBotList() {
|
||||
return savedBotList;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
package org.leavesmc.leaves.bot;
|
||||
|
||||
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.entity.player.Inventory;
|
||||
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;
|
||||
|
||||
public class BotInventoryContainer extends Inventory {
|
||||
|
||||
private static final ItemStack button;
|
||||
|
||||
static {
|
||||
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();
|
||||
|
||||
button = new ItemStack(Items.STRUCTURE_VOID);
|
||||
button.applyComponents(patch);
|
||||
}
|
||||
|
||||
private final Inventory original;
|
||||
|
||||
public BotInventoryContainer(Inventory original) {
|
||||
super(original.player, original.equipment);
|
||||
this.original = original;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getContainerSize() {
|
||||
return 54;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nonnull
|
||||
public ItemStack getItem(int slot) {
|
||||
int realSlot = convertSlot(slot);
|
||||
if (realSlot == -999) {
|
||||
// buttons are the same
|
||||
return button;
|
||||
}
|
||||
return original.getItem(realSlot);
|
||||
}
|
||||
|
||||
public int convertSlot(int slot) {
|
||||
return switch (slot) {
|
||||
// Mainhand is always store at slot 0
|
||||
case 6 -> 0;
|
||||
|
||||
// Offhand
|
||||
case 7 -> 40;
|
||||
|
||||
// Equipment slot start at 36
|
||||
case 1, 2, 3, 4 -> 40 - slot;
|
||||
|
||||
// Inventory storage
|
||||
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 -> slot - 9;
|
||||
|
||||
// Hotbar, 45 -> Mainhand (0)
|
||||
case 45, 46, 47, 48, 49, 50, 51, 52, 53 -> slot - 45;
|
||||
|
||||
// Buttons
|
||||
default -> -999;
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nonnull
|
||||
public ItemStack removeItem(int slot, int amount) {
|
||||
int realSlot = convertSlot(slot);
|
||||
if (realSlot == -999) {
|
||||
// Don't remove buttons
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
ItemStack removed = original.removeItem(realSlot, amount);
|
||||
player.detectEquipmentUpdates();
|
||||
return removed;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nonnull
|
||||
public ItemStack removeItemNoUpdate(int slot) {
|
||||
int realSlot = convertSlot(slot);
|
||||
if (realSlot == -999) {
|
||||
// Don't remove buttons
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
return original.removeItemNoUpdate(realSlot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItem(int slot, @Nonnull ItemStack stack) {
|
||||
int realSlot = convertSlot(slot);
|
||||
if (realSlot == -999) {
|
||||
// Don't modify buttons
|
||||
return;
|
||||
}
|
||||
original.setItem(realSlot, stack);
|
||||
player.detectEquipmentUpdates();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChanged() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean stillValid(@Nonnull Player player) {
|
||||
if (this.player.isRemoved()) {
|
||||
return false;
|
||||
}
|
||||
return !(player.distanceToSqr(this.player) > 64.0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,359 @@
|
||||
package org.leavesmc.leaves.bot;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import com.mojang.authlib.properties.Property;
|
||||
import com.mojang.logging.LogUtils;
|
||||
import fun.bm.lophine.config.modules.misc.FakeplayerConfig;
|
||||
import io.papermc.paper.adventure.PaperAdventure;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.kyori.adventure.text.format.Style;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.protocol.game.ClientboundRemoveEntitiesPacket;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.util.ProblemReporter;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.npc.AbstractVillager;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.entity.projectile.ThrownEnderpearl;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.storage.ValueInput;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.craftbukkit.CraftWorld;
|
||||
import org.bukkit.event.entity.EntityRemoveEvent;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.leavesmc.leaves.event.bot.*;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
public class BotList {
|
||||
|
||||
public static BotList INSTANCE;
|
||||
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
|
||||
private final MinecraftServer server;
|
||||
|
||||
public final List<ServerBot> bots = new CopyOnWriteArrayList<>();
|
||||
private final BotDataStorage dataStorage;
|
||||
|
||||
private final Map<UUID, ServerBot> botsByUUID = Maps.newHashMap();
|
||||
private final Map<String, ServerBot> botsByName = Maps.newHashMap();
|
||||
private final Map<String, Set<String>> botsNameByWorldUuid = Maps.newHashMap();
|
||||
|
||||
public BotList(MinecraftServer server) {
|
||||
this.server = server;
|
||||
this.dataStorage = new BotDataStorage(server.storageSource);
|
||||
INSTANCE = this;
|
||||
}
|
||||
|
||||
public ServerBot createNewBot(BotCreateState state) {
|
||||
BotCreateEvent event = new BotCreateEvent(state.name(), state.skinName(), state.location(), state.createReason(), state.creator());
|
||||
event.setCancelled(!BotUtil.isCreateLegal(state.name()));
|
||||
this.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(this.server, world, profile);
|
||||
bot.createState = state;
|
||||
if (event.getCreator() instanceof org.bukkit.entity.Player player) {
|
||||
bot.createPlayer = player.getUniqueId();
|
||||
}
|
||||
|
||||
return this.placeNewBot(bot, world, location, null);
|
||||
}
|
||||
|
||||
public ServerBot loadNewBot(String realName) {
|
||||
return this.loadNewBot(realName, this.dataStorage);
|
||||
}
|
||||
|
||||
public ServerBot loadNewBot(String realName, IPlayerDataStorage playerIO) {
|
||||
UUID uuid = BotUtil.getBotUUID(realName);
|
||||
|
||||
BotLoadEvent event = new BotLoadEvent(realName, uuid);
|
||||
this.server.server.getPluginManager().callEvent(event);
|
||||
if (event.isCancelled()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ServerBot bot = new ServerBot(this.server, this.server.getLevel(Level.OVERWORLD), new GameProfile(uuid, realName));
|
||||
bot.connection = new ServerBotPacketListenerImpl(this.server, bot);
|
||||
Optional<ValueInput> optional;
|
||||
try (ProblemReporter.ScopedCollector scopedCollector = new ProblemReporter.ScopedCollector(bot.problemPath(), LOGGER)) {
|
||||
optional = playerIO.load(bot, scopedCollector);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
if (optional.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
ValueInput nbt = optional.get();
|
||||
|
||||
ResourceKey<Level> resourcekey = null;
|
||||
if (nbt.getLong("WorldUUIDMost").isPresent() && nbt.getLong("WorldUUIDLeast").isPresent()) {
|
||||
org.bukkit.World bWorld = Bukkit.getServer().getWorld(new UUID(nbt.getLong("WorldUUIDMost").orElseThrow(), nbt.getLong("WorldUUIDLeast").orElseThrow()));
|
||||
if (bWorld != null) {
|
||||
resourcekey = ((CraftWorld) bWorld).getHandle().dimension();
|
||||
}
|
||||
}
|
||||
if (resourcekey == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ServerLevel world = this.server.getLevel(resourcekey);
|
||||
return this.placeNewBot(bot, world, bot.getLocation(), nbt);
|
||||
}
|
||||
|
||||
public ServerBot placeNewBot(@NotNull ServerBot bot, ServerLevel world, Location location, ValueInput save) {
|
||||
Optional<ValueInput> optional = Optional.ofNullable(save);
|
||||
|
||||
bot.isRealPlayer = true;
|
||||
bot.loginTime = System.currentTimeMillis();
|
||||
bot.connection = new ServerBotPacketListenerImpl(this.server, bot);
|
||||
bot.setServerLevel(world);
|
||||
|
||||
BotSpawnLocationEvent event = new BotSpawnLocationEvent(bot.getBukkitEntity(), location);
|
||||
this.server.server.getPluginManager().callEvent(event);
|
||||
location = event.getSpawnLocation();
|
||||
|
||||
bot.spawnIn(world);
|
||||
bot.gameMode.setLevel(bot.level());
|
||||
|
||||
bot.setPosRaw(location.getX(), location.getY(), location.getZ());
|
||||
bot.setRot(location.getYaw(), location.getPitch());
|
||||
|
||||
bot.connection.teleport(bot.getX(), bot.getY(), bot.getZ(), bot.getYRot(), bot.getXRot());
|
||||
|
||||
this.bots.add(bot);
|
||||
this.botsByName.put(bot.getScoreboardName().toLowerCase(Locale.ROOT), bot);
|
||||
this.botsByUUID.put(bot.getUUID(), bot);
|
||||
|
||||
bot.supressTrackerForLogin = true;
|
||||
world.addNewPlayer(bot);
|
||||
optional.ifPresent(nbt -> {
|
||||
bot.loadAndSpawnEnderPearls(nbt);
|
||||
bot.loadAndSpawnParentVehicle(nbt);
|
||||
});
|
||||
|
||||
BotJoinEvent event1 = new BotJoinEvent(bot.getBukkitEntity(), PaperAdventure.asAdventure(Component.translatable("multiplayer.player.joined", bot.getDisplayName())).style(Style.style(NamedTextColor.YELLOW)));
|
||||
this.server.server.getPluginManager().callEvent(event1);
|
||||
|
||||
net.kyori.adventure.text.Component joinMessage = event1.joinMessage();
|
||||
if (joinMessage != null && !joinMessage.equals(net.kyori.adventure.text.Component.empty())) {
|
||||
this.server.getPlayerList().broadcastSystemMessage(PaperAdventure.asVanilla(joinMessage), false);
|
||||
}
|
||||
|
||||
bot.renderInfo();
|
||||
bot.supressTrackerForLogin = false;
|
||||
|
||||
bot.level().getChunkSource().chunkMap.addEntity(bot);
|
||||
bot.renderData();
|
||||
bot.initInventoryMenu();
|
||||
botsNameByWorldUuid
|
||||
.computeIfAbsent(bot.level().uuid.toString(), (k) -> new HashSet<>())
|
||||
.add(bot.getBukkitEntity().getRealName());
|
||||
BotList.LOGGER.info("{}[{}] logged in with entity id {} at ([{}]{}, {}, {})", bot.getName().getString(), "Local", bot.getId(), bot.level().serverLevelData.getLevelName(), bot.getX(), bot.getY(), bot.getZ());
|
||||
return bot;
|
||||
}
|
||||
|
||||
public boolean removeBot(@NotNull ServerBot bot, @NotNull BotRemoveEvent.RemoveReason reason, @Nullable CommandSender remover, boolean saved) {
|
||||
return this.removeBot(bot, reason, remover, saved, this.dataStorage);
|
||||
}
|
||||
|
||||
public boolean removeBot(@NotNull ServerBot bot, @NotNull BotRemoveEvent.RemoveReason reason, @Nullable CommandSender remover, boolean saved, IPlayerDataStorage playerIO) {
|
||||
BotRemoveEvent event = new BotRemoveEvent(bot.getBukkitEntity(), reason, remover, PaperAdventure.asAdventure(Component.translatable("multiplayer.player.left", bot.getDisplayName())).style(Style.style(NamedTextColor.YELLOW)), saved);
|
||||
this.server.server.getPluginManager().callEvent(event);
|
||||
|
||||
if (event.isCancelled() && event.getReason() != BotRemoveEvent.RemoveReason.INTERNAL) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (bot.removeTaskId != -1) {
|
||||
Bukkit.getScheduler().cancelTask(bot.removeTaskId);
|
||||
bot.removeTaskId = -1;
|
||||
}
|
||||
|
||||
bot.disconnect();
|
||||
|
||||
if (event.shouldSave()) {
|
||||
playerIO.save(bot);
|
||||
} else {
|
||||
bot.dropAll(true);
|
||||
botsNameByWorldUuid.getOrDefault(bot.level().uuid.toString(), new HashSet<>()).remove(bot.getBukkitEntity().getRealName());
|
||||
}
|
||||
|
||||
if (bot.isPassenger() && event.shouldSave()) {
|
||||
Entity entity = bot.getRootVehicle();
|
||||
if (entity.hasExactlyOnePlayerPassenger()) {
|
||||
bot.stopRiding();
|
||||
entity.getPassengersAndSelf().forEach((entity1) -> {
|
||||
if (!false && entity1 instanceof AbstractVillager villager) {
|
||||
final Player human = villager.getTradingPlayer();
|
||||
if (human != null) {
|
||||
villager.setTradingPlayer(null);
|
||||
}
|
||||
}
|
||||
entity1.setRemoved(Entity.RemovalReason.UNLOADED_WITH_PLAYER);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
bot.unRide();
|
||||
for (ThrownEnderpearl thrownEnderpearl : bot.getEnderPearls()) {
|
||||
if (!thrownEnderpearl.level().paperConfig().misc.legacyEnderPearlBehavior) {
|
||||
thrownEnderpearl.setRemoved(Entity.RemovalReason.UNLOADED_WITH_PLAYER, EntityRemoveEvent.Cause.PLAYER_QUIT);
|
||||
} else {
|
||||
thrownEnderpearl.setOwner(null);
|
||||
}
|
||||
}
|
||||
|
||||
ServerLevel level = bot.level();
|
||||
int chunkX = bot.getBlockX() >> 4;
|
||||
int chunkZ = bot.getBlockZ() >> 4;
|
||||
if (ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(level, chunkX, chunkZ)) {
|
||||
level.removePlayerImmediately(bot, Entity.RemovalReason.UNLOADED_WITH_PLAYER);
|
||||
} else {
|
||||
io.papermc.paper.threadedregions.RegionizedServer.getInstance().taskQueue.queueTickTaskQueue(
|
||||
level, chunkX, chunkZ, () -> {
|
||||
level.removePlayerImmediately(bot, Entity.RemovalReason.UNLOADED_WITH_PLAYER);
|
||||
});
|
||||
}
|
||||
|
||||
this.bots.remove(bot);
|
||||
this.botsByName.remove(bot.getScoreboardName().toLowerCase(Locale.ROOT));
|
||||
|
||||
UUID uuid = bot.getUUID();
|
||||
ServerBot bot1 = this.botsByUUID.get(uuid);
|
||||
if (bot1 == bot) {
|
||||
this.botsByUUID.remove(uuid);
|
||||
}
|
||||
|
||||
bot.removeTab();
|
||||
ClientboundRemoveEntitiesPacket packet = new ClientboundRemoveEntitiesPacket(bot.getId());
|
||||
for (ServerPlayer player : bot.level().players()) {
|
||||
if (!(player instanceof ServerBot)) {
|
||||
player.connection.send(packet);
|
||||
}
|
||||
}
|
||||
|
||||
net.kyori.adventure.text.Component removeMessage = event.removeMessage();
|
||||
if (removeMessage != null && !removeMessage.equals(net.kyori.adventure.text.Component.empty())) {
|
||||
this.server.getPlayerList().broadcastSystemMessage(PaperAdventure.asVanilla(removeMessage), false);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void removeAllIn(String worldUuid) {
|
||||
for (String realName : this.botsNameByWorldUuid.getOrDefault(worldUuid, new HashSet<>())) {
|
||||
ServerBot bot = this.getBotByName(realName);
|
||||
if (bot != null) {
|
||||
this.removeBot(bot, BotRemoveEvent.RemoveReason.INTERNAL, null, FakeplayerConfig.canResident);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void removeAll() {
|
||||
for (ServerBot bot : this.bots) {
|
||||
bot.resume = FakeplayerConfig.canResident;
|
||||
this.removeBot(bot, BotRemoveEvent.RemoveReason.INTERNAL, null, FakeplayerConfig.canResident);
|
||||
}
|
||||
}
|
||||
|
||||
public void loadBotInfo() {
|
||||
if (!FakeplayerConfig.enable || !FakeplayerConfig.canResident) {
|
||||
return;
|
||||
}
|
||||
CompoundTag savedBotList = this.getSavedBotList().copy();
|
||||
for (String realName : savedBotList.keySet()) {
|
||||
CompoundTag nbt = savedBotList.getCompound(realName).orElseThrow();
|
||||
if (!nbt.getBoolean("resume").orElse(false)) {
|
||||
continue;
|
||||
}
|
||||
UUID levelUuid = BotUtil.getBotLevel(realName, this.dataStorage);
|
||||
if (levelUuid == null) {
|
||||
LOGGER.warn("Bot {} has no world UUID, skipping loading.", realName);
|
||||
continue;
|
||||
}
|
||||
this.botsNameByWorldUuid
|
||||
.computeIfAbsent(levelUuid.toString(), (k) -> new HashSet<>())
|
||||
.add(realName);
|
||||
}
|
||||
}
|
||||
|
||||
public void loadResume(String worldUuid) {
|
||||
if (!FakeplayerConfig.enable || !FakeplayerConfig.canResident) {
|
||||
return;
|
||||
}
|
||||
Set<String> bots = this.botsNameByWorldUuid.get(worldUuid);
|
||||
if (bots == null) {
|
||||
return;
|
||||
}
|
||||
Set<String> botsCopy = new HashSet<>(bots);
|
||||
botsCopy.forEach(this::loadNewBot);
|
||||
}
|
||||
|
||||
public void updateBotLevel(ServerBot bot, ServerLevel level) {
|
||||
String prevUuid = bot.level().uuid.toString();
|
||||
String newUuid = level.uuid.toString();
|
||||
this.botsNameByWorldUuid
|
||||
.computeIfAbsent(newUuid, (k) -> new HashSet<>())
|
||||
.add(bot.getBukkitEntity().getRealName());
|
||||
this.botsNameByWorldUuid
|
||||
.computeIfAbsent(prevUuid, (k) -> new HashSet<>())
|
||||
.remove(bot.getBukkitEntity().getRealName());
|
||||
}
|
||||
|
||||
public void networkTick() {
|
||||
this.bots.forEach(ServerBot::networkTick);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ServerBot getBot(@NotNull UUID uuid) {
|
||||
return this.botsByUUID.get(uuid);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ServerBot getBotByName(@NotNull String name) {
|
||||
return this.botsByName.get(name.toLowerCase(Locale.ROOT));
|
||||
}
|
||||
|
||||
public CompoundTag getSavedBotList() {
|
||||
return this.dataStorage.getSavedBotList();
|
||||
}
|
||||
|
||||
public static class CustomGameProfile extends GameProfile {
|
||||
|
||||
public CustomGameProfile(UUID uuid, String name, String[] skin) {
|
||||
super(uuid, name);
|
||||
this.setSkin(skin);
|
||||
}
|
||||
|
||||
public void setSkin(String[] skin) {
|
||||
if (skin != null) {
|
||||
this.getProperties().put("textures", new Property("textures", skin[0], skin[1]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package org.leavesmc.leaves.bot;
|
||||
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.stats.ServerRecipeBook;
|
||||
import net.minecraft.world.item.crafting.Recipe;
|
||||
import net.minecraft.world.item.crafting.RecipeHolder;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class BotRecipeBook extends ServerRecipeBook {
|
||||
|
||||
public BotRecipeBook() {
|
||||
super(($, $1) -> {
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(@NotNull ResourceKey<Recipe<?>> recipe) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(@NotNull ResourceKey<Recipe<?>> recipe) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(@NotNull ResourceKey<Recipe<?>> recipe) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeHighlight(@NotNull ResourceKey<Recipe<?>> recipe) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int addRecipes(@NotNull Collection<RecipeHolder<?>> recipes, @NotNull ServerPlayer player) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int removeRecipes(@NotNull Collection<RecipeHolder<?>> recipes, @NotNull ServerPlayer player) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadUntrusted(@NotNull Packed recipeBook, @NotNull Predicate<ResourceKey<Recipe<?>>> predicate) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Packed pack() {
|
||||
return new ServerRecipeBook.Packed(this.bookSettings.copy(), List.of(), List.of());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
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 org.jetbrains.annotations.NotNull;
|
||||
|
||||
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(@NotNull Player player, @NotNull Stat<?> stat, int value) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parseLocal(@NotNull DataFixer dataFixer, @NotNull String json) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getValue(@NotNull Stat<?> stat) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
package org.leavesmc.leaves.bot;
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
import fun.bm.lophine.config.modules.misc.FakeplayerConfig;
|
||||
import net.minecraft.core.NonNullList;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.world.entity.EquipmentSlot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Optional;
|
||||
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 UUID getBotUUID(@NotNull BotCreateState state) {
|
||||
return getBotUUID(state.realName());
|
||||
}
|
||||
|
||||
public static UUID getBotUUID(@NotNull String realName) {
|
||||
return UUID.nameUUIDFromBytes(("Fakeplayer:" + realName).getBytes(Charsets.UTF_8));
|
||||
}
|
||||
|
||||
public static UUID getBotLevel(@NotNull String realName, BotDataStorage botDataStorage) {
|
||||
UUID uuid = BotUtil.getBotUUID(realName);
|
||||
Optional<CompoundTag> tagOptional = botDataStorage.read(uuid.toString());
|
||||
if (tagOptional.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
CompoundTag tag = tagOptional.get();
|
||||
Optional<Long> worldUUIDMost = tag.getLong("WorldUUIDMost");
|
||||
Optional<Long> worldUUIDLeast = tag.getLong("WorldUUIDLeast");
|
||||
if (worldUUIDMost.isEmpty() || worldUUIDLeast.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return new UUID(worldUUIDMost.get(), worldUUIDLeast.get());
|
||||
}
|
||||
|
||||
public static String getFullName(String inputName) {
|
||||
return FakeplayerConfig.prefix + inputName + FakeplayerConfig.suffix;
|
||||
}
|
||||
|
||||
public static boolean isCreateLegal(@NotNull String name) {
|
||||
if (!name.matches("^[a-zA-Z0-9_]{4,16}$")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Bukkit.getPlayerExact(name) != null || BotList.INSTANCE.getBotByName(name) != null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (FakeplayerConfig.unableNames.contains(name)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return BotList.INSTANCE.bots.size() < FakeplayerConfig.limit;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package org.leavesmc.leaves.bot;
|
||||
|
||||
import net.minecraft.util.ProblemReporter;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.level.storage.ValueInput;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public interface IPlayerDataStorage {
|
||||
|
||||
void save(Player player);
|
||||
|
||||
Optional<ValueInput> load(Player player, ProblemReporter reporter);
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package org.leavesmc.leaves.bot;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import fun.bm.lophine.config.modules.misc.FakeplayerConfig;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.URI;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class MojangAPI {
|
||||
|
||||
private static final Map<String, String[]> CACHE = new HashMap<>();
|
||||
|
||||
public static String[] getSkin(String name) {
|
||||
if (FakeplayerConfig.useSkinCache && 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 = JsonParser.parseReader(new InputStreamReader(URI.create("https://api.mojang.com/users/profiles/minecraft/" + name).toURL().openStream()))
|
||||
.getAsJsonObject().get("id").getAsString();
|
||||
JsonObject property = JsonParser.parseReader(new InputStreamReader(URI.create("https://sessionserver.mojang.com/session/minecraft/profile/" + uuid + "?unsigned=false").toURL().openStream()))
|
||||
.getAsJsonObject().get("properties").getAsJsonArray().get(0).getAsJsonObject();
|
||||
return new String[]{property.get("value").getAsString(), property.get("signature").getAsString()};
|
||||
} catch (IOException | IllegalStateException | IllegalArgumentException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,684 @@
|
||||
package org.leavesmc.leaves.bot;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import com.mojang.logging.LogUtils;
|
||||
import fun.bm.lophine.config.modules.misc.FakeplayerConfig;
|
||||
import io.papermc.paper.adventure.PaperAdventure;
|
||||
import io.papermc.paper.event.entity.EntityKnockbackEvent;
|
||||
import net.minecraft.core.NonNullList;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.ListTag;
|
||||
import net.minecraft.nbt.StringTag;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.protocol.Packet;
|
||||
import net.minecraft.network.protocol.game.ClientboundPlayerInfoRemovePacket;
|
||||
import net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket;
|
||||
import net.minecraft.network.protocol.game.ClientboundRotateHeadPacket;
|
||||
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.ServerPlayerConnection;
|
||||
import net.minecraft.stats.ServerStatsCounter;
|
||||
import net.minecraft.stats.Stat;
|
||||
import net.minecraft.util.Mth;
|
||||
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.EntitySelector;
|
||||
import net.minecraft.world.entity.EquipmentSlot;
|
||||
import net.minecraft.world.entity.PositionMoveRotation;
|
||||
import net.minecraft.world.entity.item.ItemEntity;
|
||||
import net.minecraft.world.entity.player.Input;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.entity.projectile.ProjectileUtil;
|
||||
import net.minecraft.world.entity.vehicle.AbstractBoat;
|
||||
import net.minecraft.world.inventory.ChestMenu;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.crafting.RecipeHolder;
|
||||
import net.minecraft.world.level.GameRules;
|
||||
import net.minecraft.world.level.gameevent.GameEvent;
|
||||
import net.minecraft.world.level.portal.TeleportTransition;
|
||||
import net.minecraft.world.level.storage.ValueInput;
|
||||
import net.minecraft.world.level.storage.ValueOutput;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import net.minecraft.world.phys.BlockHitResult;
|
||||
import net.minecraft.world.phys.EntityHitResult;
|
||||
import net.minecraft.world.phys.HitResult;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.util.Vector;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.leavesmc.leaves.bot.agent.AbstractBotConfig;
|
||||
import org.leavesmc.leaves.bot.agent.Actions;
|
||||
import org.leavesmc.leaves.bot.agent.Configs;
|
||||
import org.leavesmc.leaves.bot.agent.actions.ServerBotAction;
|
||||
import org.leavesmc.leaves.entity.bot.CraftBot;
|
||||
import org.leavesmc.leaves.event.bot.*;
|
||||
import org.leavesmc.leaves.plugin.MinecraftInternalPlugin;
|
||||
import org.leavesmc.leaves.util.MathUtils;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class ServerBot extends ServerPlayer {
|
||||
|
||||
private final List<ServerBotAction<?>> actions;
|
||||
private final Map<Configs<?>, AbstractBotConfig<?>> configs;
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
|
||||
public boolean resume = false;
|
||||
public BotCreateState createState;
|
||||
public UUID createPlayer;
|
||||
public boolean handsBusy = false;
|
||||
|
||||
private final int tracingRange;
|
||||
private final BotStatsCounter stats;
|
||||
private final BotInventoryContainer container;
|
||||
|
||||
public int notSleepTicks;
|
||||
|
||||
public int removeTaskId = -1;
|
||||
|
||||
public ServerBot(MinecraftServer server, ServerLevel world, GameProfile profile) {
|
||||
super(server, world, profile, ClientInformation.createDefault());
|
||||
this.entityData.set(Player.DATA_PLAYER_MODE_CUSTOMISATION, (byte) -2);
|
||||
|
||||
this.gameMode = new ServerBotGameMode(this);
|
||||
|
||||
this.actions = new ArrayList<>();
|
||||
ImmutableMap.Builder<Configs<?>, AbstractBotConfig<?>> configBuilder = ImmutableMap.builder();
|
||||
for (Configs<?> config : Configs.getConfigs()) {
|
||||
configBuilder.put(config, config.createConfig(this));
|
||||
}
|
||||
this.configs = configBuilder.build();
|
||||
|
||||
this.stats = new BotStatsCounter(server);
|
||||
this.recipeBook = new BotRecipeBook();
|
||||
this.container = new BotInventoryContainer(this.getInventory());
|
||||
this.tracingRange = world.spigotConfig.playerTrackingRange * world.spigotConfig.playerTrackingRange;
|
||||
|
||||
this.notSleepTicks = 0;
|
||||
this.fauxSleeping = FakeplayerConfig.canSkipSleep;
|
||||
this.getBukkitEntity().setSimulationDistance(FakeplayerConfig.getSimulationDistance(this));
|
||||
this.setClientLoaded(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
if (!this.isAlive()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.getConfigValue(Configs.TICK_TYPE) == TickType.ENTITY_LIST) {
|
||||
this.runAction();
|
||||
}
|
||||
|
||||
// copy ServerPlayer start
|
||||
if (this.joining) {
|
||||
this.joining = false;
|
||||
}
|
||||
|
||||
this.resetOperationCountPerTick(); // Leaves - player operation limiter
|
||||
this.wardenSpawnTracker.tick();
|
||||
if (this.invulnerableTime > 0) {
|
||||
this.invulnerableTime--;
|
||||
}
|
||||
if (this.spawnInvulnerableTime > 0) {
|
||||
--this.spawnInvulnerableTime; // Leaves - spawn invulnerable time
|
||||
}
|
||||
// copy ServerPlayer end
|
||||
|
||||
if (this.getConfigValue(Configs.SPAWN_PHANTOM)) {
|
||||
this.notSleepTicks++;
|
||||
}
|
||||
|
||||
if (FakeplayerConfig.regenAmount > 0.0 && getServer().getTickCount() % 20 == 0) {
|
||||
float regenAmount = (float) (FakeplayerConfig.regenAmount * 20);
|
||||
this.setHealth(Math.min(this.getHealth() + regenAmount, this.getMaxHealth()));
|
||||
}
|
||||
|
||||
if (this.getConfigValue(Configs.TICK_TYPE) == TickType.ENTITY_LIST) {
|
||||
this.doTick();
|
||||
}
|
||||
|
||||
Input input = this.getLastClientInput();
|
||||
this.setLastClientInput(
|
||||
new Input(
|
||||
this.zza > 0,
|
||||
this.zza < 0,
|
||||
this.xxa > 0,
|
||||
this.xxa < 0,
|
||||
input.jump(),
|
||||
input.shift(),
|
||||
input.sprint()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doTick() {
|
||||
if (!this.isAlive()) {
|
||||
this.die(this.damageSources().generic());
|
||||
return;
|
||||
}
|
||||
|
||||
this.absSnapTo(this.getX(), this.getY(), this.getZ(), this.getYRot(), this.getXRot());
|
||||
|
||||
if (this.isPassenger()) {
|
||||
this.setOnGround(false);
|
||||
}
|
||||
|
||||
if (this.takeXpDelay > 0) {
|
||||
--this.takeXpDelay;
|
||||
}
|
||||
|
||||
if (this.isSleeping()) {
|
||||
++this.sleepCounter;
|
||||
if (this.sleepCounter > 100) {
|
||||
this.sleepCounter = 100;
|
||||
this.notSleepTicks = 0;
|
||||
}
|
||||
|
||||
if (!this.level().isClientSide && this.level().isBrightOutside()) {
|
||||
this.stopSleepInBed(false, true);
|
||||
}
|
||||
} else if (this.sleepCounter > 0) {
|
||||
++this.sleepCounter;
|
||||
if (this.sleepCounter >= 110) {
|
||||
this.sleepCounter = 0;
|
||||
}
|
||||
}
|
||||
|
||||
this.updateIsUnderwater();
|
||||
|
||||
if (this.getConfigValue(Configs.TICK_TYPE) == TickType.NETWORK) {
|
||||
try {
|
||||
Bukkit.getRegionScheduler().execute(
|
||||
MinecraftInternalPlugin.INSTANCE,
|
||||
this.level().getWorld(),
|
||||
this.getBlockX() >> 4,
|
||||
this.getBlockZ() >> 4,
|
||||
this::runAction
|
||||
);
|
||||
} catch (Exception e) {
|
||||
this.runAction();
|
||||
}
|
||||
}
|
||||
|
||||
this.livingEntityTick();
|
||||
|
||||
this.foodData.tick(this);
|
||||
|
||||
double d = Mth.clamp(this.getX(), -2.9999999E7, 2.9999999E7);
|
||||
double d1 = Mth.clamp(this.getZ(), -2.9999999E7, 2.9999999E7);
|
||||
if (d != this.getX() || d1 != this.getZ()) {
|
||||
this.setPos(d, this.getY(), d1);
|
||||
}
|
||||
|
||||
++this.attackStrengthTicker;
|
||||
ItemStack itemstack = this.getMainHandItem();
|
||||
if (!ItemStack.matches(this.lastItemInMainHand, itemstack)) {
|
||||
if (!ItemStack.isSameItem(this.lastItemInMainHand, itemstack)) {
|
||||
this.resetAttackStrengthTicker();
|
||||
}
|
||||
|
||||
this.lastItemInMainHand = itemstack.copy();
|
||||
}
|
||||
|
||||
this.getCooldowns().tick();
|
||||
this.updatePlayerPose();
|
||||
}
|
||||
|
||||
public void networkTick() {
|
||||
if (this.getConfigValue(Configs.TICK_TYPE) == TickType.NETWORK) {
|
||||
this.doTick();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canSimulateMovement() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeVehicle() {
|
||||
super.removeVehicle();
|
||||
this.handsBusy = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rideTick() {
|
||||
super.rideTick();
|
||||
this.handsBusy = false;
|
||||
if (this.getControlledVehicle() instanceof AbstractBoat abstractBoat) {
|
||||
Input input = this.getLastClientInput();
|
||||
abstractBoat.setInput(input.left(), input.right(), input.forward(), input.backward());
|
||||
this.handsBusy = this.handsBusy | (input.left() || input.right() || input.forward() || input.backward());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ServerBot teleport(@NotNull TeleportTransition teleportTransition) {
|
||||
if (this.isSleeping() || this.isRemoved()) {
|
||||
return null;
|
||||
}
|
||||
if (!teleportTransition.asPassenger()) {
|
||||
this.removeVehicle();
|
||||
}
|
||||
|
||||
ServerLevel fromLevel = this.level();
|
||||
ServerLevel toLevel = teleportTransition.newLevel();
|
||||
|
||||
if (toLevel.dimension() == fromLevel.dimension()) {
|
||||
this.teleportSetPosition(PositionMoveRotation.of(teleportTransition), teleportTransition.relatives());
|
||||
teleportTransition.postTeleportTransition().onTransition(this);
|
||||
return this;
|
||||
} else {
|
||||
this.isChangingDimension = true;
|
||||
fromLevel.removePlayerImmediately(this, Entity.RemovalReason.CHANGED_DIMENSION);
|
||||
this.unsetRemoved();
|
||||
this.setServerLevel(toLevel);
|
||||
this.teleportSetPosition(PositionMoveRotation.of(teleportTransition), teleportTransition.relatives());
|
||||
toLevel.addDuringTeleport(this);
|
||||
this.stopUsingItem();
|
||||
teleportTransition.postTeleportTransition().onTransition(this);
|
||||
this.isChangingDimension = false;
|
||||
|
||||
if (this.isBlocking()) {
|
||||
this.stopUsingItem();
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setServerLevel(@NotNull ServerLevel level) {
|
||||
BotList.INSTANCE.updateBotLevel(this, level);
|
||||
super.setServerLevel(level);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void knockback(double strength, double x, double z, @Nullable Entity attacker, EntityKnockbackEvent.@NotNull Cause eventCause) {
|
||||
if (!this.hurtMarked) {
|
||||
return;
|
||||
}
|
||||
super.knockback(strength, x, z, attacker, eventCause);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemPickup(@NotNull ItemEntity item) {
|
||||
super.onItemPickup(item);
|
||||
this.updateItemInHand(InteractionHand.MAIN_HAND);
|
||||
}
|
||||
|
||||
public void updateItemInHand(InteractionHand hand) {
|
||||
ItemStack item = this.getItemInHand(hand);
|
||||
|
||||
if (!item.isEmpty()) {
|
||||
BotUtil.replenishment(item, getInventory().getNonEquipmentItems());
|
||||
if (BotUtil.isDamage(item, 10)) {
|
||||
BotUtil.replaceTool(hand == InteractionHand.MAIN_HAND ? EquipmentSlot.MAINHAND : EquipmentSlot.OFFHAND, this);
|
||||
}
|
||||
}
|
||||
this.detectEquipmentUpdates();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull InteractionResult interact(@NotNull Player player, @NotNull InteractionHand hand) {
|
||||
if (FakeplayerConfig.canOpenInventory) {
|
||||
if (player instanceof ServerPlayer player1 && player.getMainHandItem().isEmpty()) {
|
||||
BotInventoryOpenEvent event = new BotInventoryOpenEvent(this.getBukkitEntity(), player1.getBukkitEntity());
|
||||
this.getServer().server.getPluginManager().callEvent(event);
|
||||
if (!event.isCancelled()) {
|
||||
player.openMenu(new SimpleMenuProvider((i, inventory, p) -> ChestMenu.sixRows(i, inventory, this.container), this.getDisplayName()));
|
||||
return InteractionResult.SUCCESS;
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.interact(player, hand);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void attack(@NotNull Entity target) {
|
||||
super.attack(target);
|
||||
this.swing(InteractionHand.MAIN_HAND);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAdditionalSaveData(@NotNull ValueOutput nbt) {
|
||||
super.addAdditionalSaveData(nbt);
|
||||
nbt.putBoolean("isShiftKeyDown", this.isShiftKeyDown());
|
||||
|
||||
CompoundTag createNbt = new CompoundTag();
|
||||
createNbt.putString("realName", this.createState.realName());
|
||||
createNbt.putString("name", this.createState.name());
|
||||
|
||||
createNbt.putString("skinName", this.createState.skinName());
|
||||
if (this.createState.skin() != null) {
|
||||
ListTag skin = new ListTag();
|
||||
for (String s : this.createState.skin()) {
|
||||
skin.add(StringTag.valueOf(s));
|
||||
}
|
||||
createNbt.put("skin", skin);
|
||||
}
|
||||
|
||||
nbt.store("createStatus", CompoundTag.CODEC, createNbt);
|
||||
|
||||
if (!this.actions.isEmpty()) {
|
||||
ValueOutput.TypedOutputList<CompoundTag> actionNbt = nbt.list("actions", CompoundTag.CODEC);
|
||||
for (ServerBotAction<?> action : this.actions) {
|
||||
actionNbt.add(action.save(new CompoundTag()));
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.configs.isEmpty()) {
|
||||
ValueOutput.TypedOutputList<CompoundTag> configNbt = nbt.list("configs", CompoundTag.CODEC);
|
||||
for (AbstractBotConfig<?> config : this.configs.values()) {
|
||||
configNbt.add(config.save(new CompoundTag()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readAdditionalSaveData(@NotNull ValueInput nbt) {
|
||||
super.readAdditionalSaveData(nbt);
|
||||
this.setShiftKeyDown(nbt.getBooleanOr("isShiftKeyDown", false));
|
||||
|
||||
CompoundTag createNbt = nbt.read("createStatus", CompoundTag.CODEC).orElseThrow();
|
||||
BotCreateState.Builder createBuilder = BotCreateState.builder(createNbt.getString("realName").orElseThrow(), null).name(createNbt.getString("name").orElseThrow());
|
||||
|
||||
String[] skin = null;
|
||||
if (createNbt.contains("skin")) {
|
||||
ListTag skinTag = createNbt.getList("skin").orElseThrow();
|
||||
skin = new String[skinTag.size()];
|
||||
for (int i = 0; i < skinTag.size(); i++) {
|
||||
skin[i] = skinTag.getString(i).orElseThrow();
|
||||
}
|
||||
}
|
||||
|
||||
createBuilder.skinName(createNbt.getString("skinName").orElseThrow()).skin(skin);
|
||||
createBuilder.createReason(BotCreateEvent.CreateReason.INTERNAL).creator(null);
|
||||
|
||||
this.createState = createBuilder.build();
|
||||
this.gameProfile = new BotList.CustomGameProfile(this.getUUID(), this.createState.name(), this.createState.skin());
|
||||
|
||||
|
||||
if (nbt.list("actions", CompoundTag.CODEC).isPresent()) {
|
||||
ValueInput.TypedInputList<CompoundTag> actionNbt = nbt.list("actions", CompoundTag.CODEC).orElseThrow();
|
||||
actionNbt.forEach(actionTag -> {
|
||||
ServerBotAction<?> action = Actions.getForName(actionTag.getString("actionName").orElseThrow());
|
||||
if (action != null) {
|
||||
ServerBotAction<?> newAction = action.create();
|
||||
newAction.load(actionTag);
|
||||
this.actions.add(newAction);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (nbt.list("configs", CompoundTag.CODEC).isPresent()) {
|
||||
ValueInput.TypedInputList<CompoundTag> configNbt = nbt.list("configs", CompoundTag.CODEC).orElseThrow();
|
||||
for (CompoundTag configTag : configNbt) {
|
||||
Configs<?> configKey = Configs.getConfig(configTag.getString("configName").orElseThrow());
|
||||
if (configKey != null) {
|
||||
this.configs.get(configKey).load(configTag);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 this.getConfigValue(Configs.ALWAYS_SEND_DATA) && (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 = this.level().getChunkSource().chunkMap.entityMap.get(this.getId());
|
||||
|
||||
if (entityTracker == null) {
|
||||
LOGGER.warn("Fakeplayer cant get entity tracker for " + this.getId());
|
||||
return;
|
||||
}
|
||||
|
||||
playerConnection.send(this.getAddEntityPacket(entityTracker.serverEntity));
|
||||
if (login) {
|
||||
Bukkit.getScheduler().runTaskLater(MinecraftInternalPlugin.INSTANCE, () -> playerConnection.send(new ClientboundRotateHeadPacket(this, (byte) ((getYRot() * 256f) / 360f))), 10);
|
||||
} else {
|
||||
playerConnection.send(new ClientboundRotateHeadPacket(this, (byte) ((getYRot() * 256f) / 360f)));
|
||||
}
|
||||
}
|
||||
|
||||
public void renderInfo() {
|
||||
this.getServer().getPlayerList().getPlayers().forEach(this::sendPlayerInfo);
|
||||
}
|
||||
|
||||
public void renderData() {
|
||||
this.getServer().getPlayerList().getPlayers().forEach(
|
||||
player -> this.sendFakeDataIfNeed(player, false)
|
||||
);
|
||||
}
|
||||
|
||||
private void sendPacket(Packet<?> packet) {
|
||||
this.getServer().getPlayerList().getPlayers().forEach(player -> player.connection.send(packet));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void die(@NotNull DamageSource damageSource) {
|
||||
boolean flag = this.level().getGameRules().getBoolean(GameRules.RULE_SHOWDEATHMESSAGES);
|
||||
Component defaultMessage = this.getCombatTracker().getDeathMessage();
|
||||
|
||||
BotDeathEvent event = new BotDeathEvent(this.getBukkitEntity(), PaperAdventure.asAdventure(defaultMessage), flag);
|
||||
this.getServer().server.getPluginManager().callEvent(event);
|
||||
|
||||
if (event.isCancelled()) {
|
||||
if (this.getHealth() <= 0) {
|
||||
this.setHealth(0.1f);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
this.gameEvent(GameEvent.ENTITY_DIE);
|
||||
|
||||
net.kyori.adventure.text.Component deathMessage = event.deathMessage();
|
||||
if (event.isSendDeathMessage() && deathMessage != null && !deathMessage.equals(net.kyori.adventure.text.Component.empty())) {
|
||||
this.getServer().getPlayerList().broadcastSystemMessage(PaperAdventure.asVanilla(deathMessage), false);
|
||||
}
|
||||
|
||||
this.getServer().getBotList().removeBot(this, BotRemoveEvent.RemoveReason.DEATH, null, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean startRiding(@NotNull Entity vehicle, boolean force) {
|
||||
if (super.startRiding(vehicle, force)) {
|
||||
if (vehicle.getControllingPassenger() == this) { // see net.minecraft.server.networkServerGamePacketListenerImpl#handleMoveVehicle
|
||||
this.setDeltaMovement(Vec3.ZERO);
|
||||
this.setYRot(vehicle.yRotO);
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int awardRecipes(@NotNull Collection<RecipeHolder<?>> recipes) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int resetRecipes(@NotNull Collection<RecipeHolder<?>> recipes) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void triggerRecipeCrafted(@NotNull RecipeHolder<?> recipe, @NotNull List<ItemStack> items) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void awardKillScore(@NotNull Entity entity, @NotNull DamageSource damageSource) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void awardStat(@NotNull Stat<?> stat) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetStat(@NotNull Stat<?> stat) {
|
||||
}
|
||||
|
||||
public void removeTab() {
|
||||
this.sendPacket(new ClientboundPlayerInfoRemovePacket(List.of(this.getUUID())));
|
||||
}
|
||||
|
||||
public void faceLocation(@NotNull Location loc) {
|
||||
this.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];
|
||||
|
||||
this.sendPacket(new ClientboundRotateHeadPacket(this, (byte) (yaw * 256 / 360f)));
|
||||
}
|
||||
|
||||
this.setRot(yaw, pitch);
|
||||
}
|
||||
|
||||
public Location getLocation() {
|
||||
return this.getBukkitEntity().getLocation();
|
||||
}
|
||||
|
||||
public EntityHitResult getEntityHitResult() {
|
||||
return this.getEntityHitResult(null);
|
||||
}
|
||||
|
||||
public EntityHitResult getEntityHitResult(Predicate<? super Entity> predicate) {
|
||||
EntityHitResult result = this.pick(this, this.entityInteractionRange());
|
||||
if (result != null && (predicate == null || predicate.test(result.getEntity()))) {
|
||||
return result;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public BlockHitResult getBlockHitResult() {
|
||||
return (BlockHitResult) this.pick(this.blockInteractionRange(), 1.0f, false);
|
||||
}
|
||||
|
||||
private EntityHitResult pick(Entity entity, double maxDistance) {
|
||||
double d = maxDistance;
|
||||
double d1 = Mth.square(maxDistance);
|
||||
Vec3 vec3 = entity.getEyePosition(1.0f);
|
||||
HitResult hitResult = entity.pick(maxDistance, 1.0f, false);
|
||||
double d2 = hitResult.getLocation().distanceToSqr(vec3);
|
||||
if (hitResult.getType() != HitResult.Type.MISS) {
|
||||
d1 = d2;
|
||||
d = Math.sqrt(d2);
|
||||
}
|
||||
|
||||
Vec3 viewStart = entity.getViewVector(1.0f);
|
||||
Vec3 viewEnd = vec3.add(viewStart.x * d, viewStart.y * d, viewStart.z * d);
|
||||
AABB aABB = entity.getBoundingBox().expandTowards(viewStart.scale(d)).inflate(1.0, 1.0, 1.0);
|
||||
return ProjectileUtil.getEntityHitResult(entity, vec3, viewEnd, aABB, EntitySelector.CAN_BE_PICKED, d1);
|
||||
}
|
||||
|
||||
public void dropAll(boolean death) {
|
||||
NonNullList<ItemStack> items = this.getInventory().getNonEquipmentItems();
|
||||
for (int i = 0; i < items.size(); i++) {
|
||||
ItemStack itemStack = items.get(i);
|
||||
if (!itemStack.isEmpty()) {
|
||||
this.drop(itemStack, death, false);
|
||||
items.set(i, ItemStack.EMPTY);
|
||||
}
|
||||
}
|
||||
for (EquipmentSlot slot : EquipmentSlot.values()) {
|
||||
ItemStack itemStack;
|
||||
if (!(itemStack = this.equipment.get(slot)).isEmpty()) {
|
||||
this.drop(itemStack, death, false);
|
||||
this.equipment.set(slot, ItemStack.EMPTY);
|
||||
}
|
||||
}
|
||||
this.detectEquipmentUpdates();
|
||||
}
|
||||
|
||||
private void runAction() {
|
||||
if (FakeplayerConfig.canUseAction) {
|
||||
this.actions.forEach(action -> action.tryTick(this));
|
||||
this.actions.removeIf(ServerBotAction::isCancelled);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean addBotAction(ServerBotAction<?> action, CommandSender sender) {
|
||||
if (!FakeplayerConfig.canUseAction) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!new BotActionScheduleEvent(this.getBukkitEntity(), action.getName(), action.getUUID(), sender).callEvent()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
action.init();
|
||||
this.actions.add(action);
|
||||
return true;
|
||||
}
|
||||
|
||||
public List<ServerBotAction<?>> getBotActions() {
|
||||
return actions;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public ServerStatsCounter getStats() {
|
||||
return stats;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <E> AbstractBotConfig<E> getConfig(Configs<E> config) {
|
||||
return (AbstractBotConfig<E>) Objects.requireNonNull(this.configs.get(config));
|
||||
}
|
||||
|
||||
public <E> E getConfigValue(Configs<E> config) {
|
||||
return this.getConfig(config).getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public CraftBot getBukkitEntity() {
|
||||
return (CraftBot) super.getBukkitEntity();
|
||||
}
|
||||
|
||||
public enum TickType {
|
||||
NETWORK,
|
||||
ENTITY_LIST
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
package org.leavesmc.leaves.bot;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.minecraft.core.BlockPos;
|
||||
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.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.GameMasterBlock;
|
||||
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 blockState = this.level.getBlockState(pos);
|
||||
if (!this.player.getMainHandItem().canDestroyBlock(blockState, this.level, pos, this.player)) {
|
||||
return false;
|
||||
} else {
|
||||
BlockEntity blockEntity = this.level.getBlockEntity(pos);
|
||||
Block block = blockState.getBlock();
|
||||
if (block instanceof GameMasterBlock) {
|
||||
this.level.sendBlockUpdated(pos, blockState, blockState, 3);
|
||||
return false;
|
||||
} else {
|
||||
BlockState blockState1 = block.playerWillDestroy(this.level, pos, blockState, this.player); // Leaves - no block update
|
||||
boolean flag = this.level.removeBlock(pos, false);
|
||||
if (flag) {
|
||||
block.destroy(this.level, pos, blockState1);
|
||||
}
|
||||
|
||||
ItemStack mainHandItem = this.player.getMainHandItem();
|
||||
ItemStack itemStack = mainHandItem.copy();
|
||||
boolean hasCorrectToolForDrops = this.player.hasCorrectToolForDrops(blockState1);
|
||||
mainHandItem.getItem().mineBlock(mainHandItem, this.level, blockState1, pos, this.player);
|
||||
if (flag && hasCorrectToolForDrops) {
|
||||
block.playerDestroy(this.level, this.player, pos, blockState1, blockEntity, itemStack, true, true);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public InteractionResult useItemOn(@NotNull ServerPlayer player, Level level, @NotNull ItemStack stack, @NotNull InteractionHand hand, BlockHitResult hitResult) {
|
||||
BlockPos blockPos = hitResult.getBlockPos();
|
||||
BlockState blockState = level.getBlockState(blockPos);
|
||||
|
||||
if (!blockState.getBlock().isEnabled(level.enabledFeatures())) {
|
||||
return InteractionResult.FAIL;
|
||||
}
|
||||
|
||||
boolean flag = !player.getMainHandItem().isEmpty() || !player.getOffhandItem().isEmpty();
|
||||
boolean flag1 = player.isSecondaryUseActive() && flag;
|
||||
|
||||
if (!flag1) {
|
||||
InteractionResult iteminteractionresult = blockState.useItemOn(player.getItemInHand(hand), level, player, hand, hitResult);
|
||||
|
||||
if (iteminteractionresult.consumesAction()) {
|
||||
return iteminteractionresult;
|
||||
}
|
||||
|
||||
if (iteminteractionresult instanceof InteractionResult.TryEmptyHandInteraction && hand == InteractionHand.MAIN_HAND) {
|
||||
InteractionResult interactionResult = blockState.useWithoutItem(level, player, hitResult);
|
||||
if (interactionResult.consumesAction()) {
|
||||
return interactionResult;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!stack.isEmpty() && !player.getCooldowns().isOnCooldown(stack)) {
|
||||
UseOnContext itemactioncontext = new UseOnContext(player, hand, hitResult);
|
||||
return stack.useOn(itemactioncontext);
|
||||
} else {
|
||||
return InteractionResult.PASS;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package org.leavesmc.leaves.bot;
|
||||
|
||||
import io.netty.channel.ChannelFutureListener;
|
||||
import net.minecraft.network.Connection;
|
||||
import net.minecraft.network.DisconnectionDetails;
|
||||
import net.minecraft.network.protocol.Packet;
|
||||
import net.minecraft.network.protocol.PacketFlow;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.network.CommonListenerCookie;
|
||||
import net.minecraft.server.network.ServerGamePacketListenerImpl;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class ServerBotPacketListenerImpl extends ServerGamePacketListenerImpl {
|
||||
|
||||
public ServerBotPacketListenerImpl(MinecraftServer server, ServerBot bot) {
|
||||
super(server, BotConnection.INSTANCE, bot, CommonListenerCookie.createInitial(bot.gameProfile, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(@NotNull Packet<?> packet, @Nullable ChannelFutureListener listener) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnect(@NotNull DisconnectionDetails disconnectionInfo) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAcceptingMessages() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
}
|
||||
|
||||
public static class BotConnection extends Connection {
|
||||
|
||||
private static final BotConnection INSTANCE = new BotConnection();
|
||||
|
||||
public BotConnection() {
|
||||
super(PacketFlow.SERVERBOUND);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConnected() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConnecting() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMemoryConnection() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(@NotNull Packet<?> packet, @javax.annotation.Nullable ChannelFutureListener channelFutureListener, boolean flag) {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package org.leavesmc.leaves.bot.agent;
|
||||
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
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 AbstractBotConfig<E> {
|
||||
|
||||
private final String name;
|
||||
private final CommandArgument argument;
|
||||
|
||||
protected ServerBot bot;
|
||||
|
||||
public AbstractBotConfig(String name, CommandArgument argument) {
|
||||
this.name = name;
|
||||
this.argument = argument;
|
||||
}
|
||||
|
||||
public AbstractBotConfig<E> setBot(ServerBot bot) {
|
||||
this.bot = bot;
|
||||
return this;
|
||||
}
|
||||
|
||||
public abstract E getValue();
|
||||
|
||||
public abstract void setValue(E value) throws IllegalArgumentException;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void setFromCommand(@NotNull CommandArgumentResult result) throws IllegalArgumentException {
|
||||
if (argument == CommandArgument.EMPTY) {
|
||||
throw new IllegalArgumentException("No argument for " + this.getName());
|
||||
}
|
||||
try {
|
||||
this.setValue((E) result.read(argument.getArgumentTypes().getFirst().getType()));
|
||||
} catch (ClassCastException e) {
|
||||
throw new IllegalArgumentException("Invalid argument type for " + this.getName() + ": " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public List<String> getMessage() {
|
||||
return List.of(this.bot.getScoreboardName() + "'s " + this.getName() + ": " + this.getValue());
|
||||
}
|
||||
|
||||
public List<String> getChangeMessage() {
|
||||
return List.of(this.bot.getScoreboardName() + "'s " + this.getName() + " changed: " + this.getValue());
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public CommandArgument getArgument() {
|
||||
return argument;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public CompoundTag save(@NotNull CompoundTag nbt) {
|
||||
nbt.putString("configName", this.name);
|
||||
return nbt;
|
||||
}
|
||||
|
||||
public abstract void load(@NotNull CompoundTag nbt);
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package org.leavesmc.leaves.bot.agent;
|
||||
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.leavesmc.leaves.bot.agent.actions.*;
|
||||
import org.leavesmc.leaves.entity.bot.action.*;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class Actions {
|
||||
|
||||
private static final Map<String, ServerBotAction<?>> actionsByName = new HashMap<>();
|
||||
private static final Map<Class<?>, ServerBotAction<?>> actionsByClass = new HashMap<>();
|
||||
|
||||
public static void registerAll() {
|
||||
register(new ServerAttackAction(), AttackAction.class);
|
||||
register(new ServerBreakBlockAction(), BreakBlockAction.class);
|
||||
register(new ServerDropAction(), DropAction.class);
|
||||
register(new ServerJumpAction(), JumpAction.class);
|
||||
register(new ServerSneakAction(), SneakAction.class);
|
||||
register(new ServerUseItemAutoAction(), UseItemAutoAction.class);
|
||||
register(new ServerUseItemAction(), UseItemAction.class);
|
||||
register(new ServerUseItemOnAction(), UseItemOnAction.class);
|
||||
register(new ServerUseItemToAction(), UseItemToAction.class);
|
||||
register(new ServerUseItemOffhandAction(), UseItemOffhandAction.class);
|
||||
register(new ServerUseItemOnOffhandAction(), UseItemOnOffhandAction.class);
|
||||
register(new ServerUseItemToOffhandAction(), UseItemToOffhandAction.class);
|
||||
register(new ServerLookAction(), LookAction.class);
|
||||
register(new ServerFishAction(), FishAction.class);
|
||||
register(new ServerSwimAction(), SwimAction.class);
|
||||
register(new ServerRotationAction(), RotationAction.class);
|
||||
register(new ServerMoveAction(), MoveAction.class);
|
||||
register(new ServerMountAction(), MountAction.class);
|
||||
register(new ServerSwapAction(), SwapAction.class);
|
||||
}
|
||||
|
||||
public static boolean register(@NotNull ServerBotAction<?> action, Class<? extends BotAction<?>> type) {
|
||||
if (!actionsByName.containsKey(action.getName())) {
|
||||
actionsByName.put(action.getName(), action);
|
||||
actionsByClass.put(type, action);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean unregister(@NotNull String name) {
|
||||
// TODO add in custom action api
|
||||
return true;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Contract(pure = true)
|
||||
public static Collection<ServerBotAction<?>> getAll() {
|
||||
return actionsByName.values();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static Set<String> getNames() {
|
||||
return actionsByName.keySet();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static ServerBotAction<?> getForName(String name) {
|
||||
return actionsByName.get(name);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static ServerBotAction<?> getForClass(@NotNull Class<?> type) {
|
||||
return actionsByClass.get(type);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package org.leavesmc.leaves.bot.agent;
|
||||
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.leavesmc.leaves.bot.ServerBot;
|
||||
import org.leavesmc.leaves.bot.agent.configs.AlwaysSendDataConfig;
|
||||
import org.leavesmc.leaves.bot.agent.configs.LocatorBarConfig;
|
||||
import org.leavesmc.leaves.bot.agent.configs.SimulationDistanceConfig;
|
||||
import org.leavesmc.leaves.bot.agent.configs.SkipSleepConfig;
|
||||
import org.leavesmc.leaves.bot.agent.configs.SpawnPhantomConfig;
|
||||
import org.leavesmc.leaves.bot.agent.configs.TickTypeConfig;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class Configs<E> {
|
||||
|
||||
private static final Map<String, Configs<?>> configs = new HashMap<>();
|
||||
|
||||
public static final Configs<Boolean> SKIP_SLEEP = register(SkipSleepConfig.class, SkipSleepConfig::new);
|
||||
public static final Configs<Boolean> ALWAYS_SEND_DATA = register(AlwaysSendDataConfig.class, AlwaysSendDataConfig::new);
|
||||
public static final Configs<Boolean> SPAWN_PHANTOM = register(SpawnPhantomConfig.class, SpawnPhantomConfig::new);
|
||||
public static final Configs<Integer> SIMULATION_DISTANCE = register(SimulationDistanceConfig.class, SimulationDistanceConfig::new);
|
||||
public static final Configs<ServerBot.TickType> TICK_TYPE = register(TickTypeConfig.class, TickTypeConfig::new);
|
||||
public static final Configs<Boolean> ENABLE_LOCATOR_BAR = register(LocatorBarConfig.class, LocatorBarConfig::new);
|
||||
|
||||
private final Class<? extends AbstractBotConfig<E>> configClass;
|
||||
private final Supplier<? extends AbstractBotConfig<E>> configCreator;
|
||||
|
||||
private Configs(Class<? extends AbstractBotConfig<E>> configClass, Supplier<? extends AbstractBotConfig<E>> configCreator) {
|
||||
this.configClass = configClass;
|
||||
this.configCreator = configCreator;
|
||||
}
|
||||
|
||||
public Class<? extends AbstractBotConfig<E>> getConfigClass() {
|
||||
return configClass;
|
||||
}
|
||||
|
||||
public AbstractBotConfig<E> createConfig(ServerBot bot) {
|
||||
return configCreator.get().setBot(bot);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Configs<?> getConfig(String name) {
|
||||
return configs.get(name);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Contract(pure = true)
|
||||
public static Collection<Configs<?>> getConfigs() {
|
||||
return configs.values();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Contract(pure = true)
|
||||
public static Collection<String> getConfigNames() {
|
||||
return configs.keySet();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static <E> Configs<E> register(Class<? extends AbstractBotConfig<E>> configClass, Supplier<? extends AbstractBotConfig<E>> configCreator) {
|
||||
Configs<E> config = new Configs<>(configClass, configCreator);
|
||||
configs.put(config.createConfig(null).getName(), config);
|
||||
return config;
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
package org.leavesmc.leaves.bot.agent.actions;
|
||||
|
||||
import net.minecraft.world.phys.EntityHitResult;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.leavesmc.leaves.bot.ServerBot;
|
||||
import org.leavesmc.leaves.entity.bot.actions.CraftAttackAction;
|
||||
|
||||
public class ServerAttackAction extends ServerTimerBotAction<ServerAttackAction> {
|
||||
|
||||
public ServerAttackAction() {
|
||||
super("attack", ServerAttackAction::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doTick(@NotNull ServerBot bot) {
|
||||
EntityHitResult hitResult = bot.getEntityHitResult(target -> target.isAttackable() && !target.skipAttackInteraction(bot));
|
||||
if (hitResult == null) {
|
||||
return false;
|
||||
} else {
|
||||
bot.attack(hitResult.getEntity());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object asCraft() {
|
||||
return new CraftAttackAction(this);
|
||||
}
|
||||
}
|
||||
+237
@@ -0,0 +1,237 @@
|
||||
package org.leavesmc.leaves.bot.agent.actions;
|
||||
|
||||
import com.mojang.logging.LogUtils;
|
||||
import net.minecraft.core.UUIDUtil;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.leavesmc.leaves.bot.ServerBot;
|
||||
import org.leavesmc.leaves.command.CommandArgument;
|
||||
import org.leavesmc.leaves.command.CommandArgumentResult;
|
||||
import org.leavesmc.leaves.event.bot.BotActionExecuteEvent;
|
||||
import org.leavesmc.leaves.event.bot.BotActionStopEvent;
|
||||
import org.leavesmc.leaves.util.UpdateSuppressionException;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public abstract class ServerBotAction<E extends ServerBotAction<E>> {
|
||||
|
||||
private final String name;
|
||||
private final CommandArgument argument;
|
||||
private final Supplier<E> creator;
|
||||
private UUID uuid;
|
||||
|
||||
private int initialTickDelay;
|
||||
private int initialTickInterval;
|
||||
private int initialNumber;
|
||||
|
||||
private int tickToNext;
|
||||
private int numberRemaining;
|
||||
private boolean cancel;
|
||||
|
||||
private Consumer<E> onFail;
|
||||
private Consumer<E> onSuccess;
|
||||
private Consumer<E> onStop;
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
|
||||
public ServerBotAction(String name, CommandArgument argument, Supplier<E> creator) {
|
||||
this.name = name;
|
||||
this.argument = argument;
|
||||
this.uuid = UUID.randomUUID();
|
||||
this.creator = creator;
|
||||
|
||||
this.cancel = false;
|
||||
this.setStartDelayTick(0);
|
||||
this.setDoIntervalTick(1);
|
||||
this.setDoNumber(1);
|
||||
}
|
||||
|
||||
public abstract boolean doTick(@NotNull ServerBot bot);
|
||||
|
||||
public abstract Object asCraft();
|
||||
|
||||
public void init() {
|
||||
this.tickToNext = initialTickDelay;
|
||||
this.numberRemaining = this.getDoNumber();
|
||||
this.setCancelled(false);
|
||||
}
|
||||
|
||||
public void tryTick(ServerBot bot) {
|
||||
if (this.numberRemaining == 0) {
|
||||
this.stop(bot, BotActionStopEvent.Reason.DONE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.cancel) {
|
||||
this.stop(bot, BotActionStopEvent.Reason.PLUGIN);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.tickToNext <= 0) {
|
||||
BotActionExecuteEvent event = new BotActionExecuteEvent(bot.getBukkitEntity(), name, uuid);
|
||||
|
||||
event.callEvent();
|
||||
if (event.getResult() == BotActionExecuteEvent.Result.SOFT_CANCEL) {
|
||||
this.tickToNext = this.getDoIntervalTick();
|
||||
return;
|
||||
} else if (event.getResult() == BotActionExecuteEvent.Result.HARD_CANCEL) {
|
||||
if (this.numberRemaining > 0) {
|
||||
this.numberRemaining--;
|
||||
}
|
||||
this.tickToNext = this.getDoIntervalTick();
|
||||
return;
|
||||
}
|
||||
|
||||
boolean result = false;
|
||||
try {
|
||||
result = this.doTick(bot);
|
||||
} catch (UpdateSuppressionException e) {
|
||||
e.providePlayer(bot);
|
||||
e.consume();
|
||||
} catch (Exception e) {
|
||||
LOGGER.warn("An error occurred while executing bot " + bot.displayName + ", action " + this.name, e);
|
||||
}
|
||||
|
||||
if (result) {
|
||||
if (this.numberRemaining > 0) {
|
||||
this.numberRemaining--;
|
||||
}
|
||||
this.tickToNext = this.getDoIntervalTick();
|
||||
if (this.onSuccess != null) {
|
||||
this.onSuccess.accept((E) this);
|
||||
}
|
||||
} else if (this.onFail != null) {
|
||||
this.onFail.accept((E) this);
|
||||
}
|
||||
} else {
|
||||
this.tickToNext--;
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public CompoundTag save(@NotNull CompoundTag nbt) {
|
||||
if (!this.cancel) {
|
||||
nbt.putString("actionName", this.name);
|
||||
nbt.store("actionUUID", UUIDUtil.CODEC, this.uuid);
|
||||
|
||||
nbt.putInt("initialTickDelay", this.initialTickDelay);
|
||||
nbt.putInt("initialTickInterval", this.initialTickInterval);
|
||||
nbt.putInt("initialNumber", this.initialNumber);
|
||||
|
||||
nbt.putInt("tickToNext", this.tickToNext);
|
||||
nbt.putInt("numberRemaining", this.numberRemaining);
|
||||
}
|
||||
return nbt;
|
||||
}
|
||||
|
||||
public void load(@NotNull CompoundTag nbt) {
|
||||
this.uuid = nbt.read("actionUUID", UUIDUtil.CODEC).orElse(UUID.randomUUID());
|
||||
|
||||
this.initialTickDelay = nbt.getInt("initialTickDelay").orElse(0);
|
||||
this.initialTickInterval = nbt.getInt("initialTickInterval").orElse(0);
|
||||
this.initialNumber = nbt.getInt("initialNumber").orElse(0);
|
||||
|
||||
this.tickToNext = nbt.getInt("tickToNext").orElse(0);
|
||||
this.numberRemaining = nbt.getInt("numberRemaining").orElse(0);
|
||||
}
|
||||
|
||||
public void stop(@NotNull ServerBot bot, BotActionStopEvent.Reason reason) {
|
||||
new BotActionStopEvent(bot.getBukkitEntity(), this.name, this.uuid, reason, null).callEvent();
|
||||
this.setCancelled(true);
|
||||
if (this.onStop != null) {
|
||||
this.onStop.accept((E) this);
|
||||
}
|
||||
}
|
||||
|
||||
public void loadCommand(ServerPlayer player, @NotNull CommandArgumentResult result) {
|
||||
}
|
||||
|
||||
public void setSuggestion(int n, BiFunction<CommandSender, String, Pair<List<String>, String>> suggestion) {
|
||||
this.argument.setSuggestion(n, suggestion);
|
||||
}
|
||||
|
||||
public void setSuggestion(int n, Pair<List<String>, String> suggestion) {
|
||||
this.setSuggestion(n, (sender, arg) -> suggestion);
|
||||
}
|
||||
|
||||
public void setSuggestion(int n, List<String> tabComplete) {
|
||||
this.setSuggestion(n, Pair.of(tabComplete, null));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public E create() {
|
||||
return this.creator.get();
|
||||
}
|
||||
|
||||
public CommandArgument getArgument() {
|
||||
return this.argument;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public UUID getUUID() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
public void setStartDelayTick(int initialTickDelay) {
|
||||
this.initialTickDelay = initialTickDelay;
|
||||
}
|
||||
|
||||
public int getStartDelayTick() {
|
||||
return this.initialTickDelay;
|
||||
}
|
||||
|
||||
public void setDoIntervalTick(int initialTickInterval) {
|
||||
this.initialTickInterval = Math.max(0, initialTickInterval);
|
||||
}
|
||||
|
||||
public int getDoIntervalTick() {
|
||||
return this.initialTickInterval;
|
||||
}
|
||||
|
||||
public void setDoNumber(int initialNumber) {
|
||||
this.initialNumber = Math.max(-1, initialNumber);
|
||||
}
|
||||
|
||||
public int getDoNumber() {
|
||||
return this.initialNumber;
|
||||
}
|
||||
|
||||
public int getTickToNext() {
|
||||
return this.tickToNext;
|
||||
}
|
||||
|
||||
public int getDoNumberRemaining() {
|
||||
return this.numberRemaining;
|
||||
}
|
||||
|
||||
public boolean isCancelled() {
|
||||
return cancel;
|
||||
}
|
||||
|
||||
public void setCancelled(boolean cancel) {
|
||||
this.cancel = cancel;
|
||||
}
|
||||
|
||||
public void setOnFail(Consumer<E> onFail) {
|
||||
this.onFail = onFail;
|
||||
}
|
||||
|
||||
public void setOnSuccess(Consumer<E> onSuccess) {
|
||||
this.onSuccess = onSuccess;
|
||||
}
|
||||
|
||||
public void setOnStop(Consumer<E> onStop) {
|
||||
this.onStop = onStop;
|
||||
}
|
||||
}
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
package org.leavesmc.leaves.bot.agent.actions;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.entity.EquipmentSlot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.enchantment.EnchantmentHelper;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
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.entity.bot.actions.CraftBreakBlockAction;
|
||||
|
||||
public class ServerBreakBlockAction extends ServerTimerBotAction<ServerBreakBlockAction> {
|
||||
|
||||
public ServerBreakBlockAction() {
|
||||
super("break", ServerBreakBlockAction::new);
|
||||
}
|
||||
|
||||
private ItemStack lastItem = null;
|
||||
private BlockPos lastPos = null;
|
||||
private int destroyProgressTime = 0;
|
||||
private int lastSentState = -1;
|
||||
|
||||
@Override
|
||||
public boolean doTick(@NotNull ServerBot bot) {
|
||||
Block block = bot.getBukkitEntity().getTargetBlockExact(5);
|
||||
if (block != null) {
|
||||
BlockPos pos = ((CraftBlock) block).getPosition();
|
||||
|
||||
BlockState iblockdata = bot.level().getBlockState(pos);
|
||||
if (lastPos == null || !lastPos.equals(pos) || lastItem == null || !lastItem.equals(bot.getMainHandItem())) {
|
||||
if (lastPos != null && destroyProgressTime > 0) {
|
||||
bot.level().destroyBlockProgress(bot.getId(), lastPos, -1);
|
||||
}
|
||||
lastItem = bot.getMainHandItem();
|
||||
lastPos = pos;
|
||||
destroyProgressTime = 0;
|
||||
lastSentState = -1;
|
||||
|
||||
if (!iblockdata.isAir()) {
|
||||
bot.swing(InteractionHand.MAIN_HAND);
|
||||
EnchantmentHelper.onHitBlock(
|
||||
bot.level(), bot.getMainHandItem(), bot, bot, EquipmentSlot.MAINHAND, Vec3.atCenterOf(pos), iblockdata,
|
||||
item -> bot.onEquippedItemBroken(item, EquipmentSlot.MAINHAND)
|
||||
);
|
||||
iblockdata.attack(bot.level(), pos, bot);
|
||||
float f = iblockdata.getDestroyProgress(bot, bot.level(), pos);
|
||||
if (f >= 1.0F) {
|
||||
bot.gameMode.destroyAndAck(pos, 0, "insta mine");
|
||||
bot.updateItemInHand(InteractionHand.MAIN_HAND);
|
||||
finalBreak();
|
||||
return true;
|
||||
} else {
|
||||
destroyProgressTime++;
|
||||
int k = (int) (f * 10.0F);
|
||||
bot.level().destroyBlockProgress(bot.getId(), pos, k);
|
||||
lastSentState = k;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!iblockdata.isAir()) {
|
||||
bot.swing(InteractionHand.MAIN_HAND);
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (lastPos != null) {
|
||||
bot.level().destroyBlockProgress(bot.getId(), lastPos, -1);
|
||||
}
|
||||
finalBreak();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void finalBreak() {
|
||||
lastPos = null;
|
||||
lastItem = null;
|
||||
destroyProgressTime = 0;
|
||||
lastSentState = -1;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object asCraft() {
|
||||
return new CraftBreakBlockAction(this);
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
package org.leavesmc.leaves.bot.agent.actions;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.leavesmc.leaves.bot.ServerBot;
|
||||
import org.leavesmc.leaves.entity.bot.actions.CraftDropAction;
|
||||
|
||||
public class ServerDropAction extends ServerTimerBotAction<ServerDropAction> {
|
||||
|
||||
public ServerDropAction() {
|
||||
super("drop", ServerDropAction::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doTick(@NotNull ServerBot bot) {
|
||||
bot.dropAll(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object asCraft() {
|
||||
return new CraftDropAction(this);
|
||||
}
|
||||
}
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
package org.leavesmc.leaves.bot.agent.actions;
|
||||
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
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.entity.bot.actions.CraftFishAction;
|
||||
|
||||
public class ServerFishAction extends ServerTimerBotAction<ServerFishAction> {
|
||||
|
||||
public ServerFishAction() {
|
||||
super("fish", ServerFishAction::new);
|
||||
}
|
||||
|
||||
private static final int CATCH_ENTITY_DELAY = 20;
|
||||
|
||||
private int initialFishInterval = 0;
|
||||
private int tickToNextFish = 0;
|
||||
|
||||
@Override
|
||||
public void setDoIntervalTick(int initialTickInterval) {
|
||||
super.setDoIntervalTick(0);
|
||||
this.initialFishInterval = initialTickInterval;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public CompoundTag save(@NotNull CompoundTag nbt) {
|
||||
super.save(nbt);
|
||||
nbt.putInt("initialFishInterval", this.initialFishInterval);
|
||||
nbt.putInt("tickToNextFish", this.tickToNextFish);
|
||||
return nbt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(@NotNull CompoundTag nbt) {
|
||||
super.load(nbt);
|
||||
this.initialFishInterval = nbt.getInt("initialFishInterval").orElseThrow();
|
||||
this.tickToNextFish = nbt.getInt("tickToNextFish").orElseThrow();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doTick(@NotNull ServerBot bot) {
|
||||
if (this.tickToNextFish > 0) {
|
||||
this.tickToNextFish--;
|
||||
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);
|
||||
this.tickToNextFish = CATCH_ENTITY_DELAY;
|
||||
return false;
|
||||
}
|
||||
if (fishingHook.nibble > 0) {
|
||||
mainHand.use(bot.level(), bot, InteractionHand.MAIN_HAND);
|
||||
this.tickToNextFish = this.initialFishInterval - 1;
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
mainHand.use(bot.level(), bot, InteractionHand.MAIN_HAND);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object asCraft() {
|
||||
return new CraftFishAction(this);
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package org.leavesmc.leaves.bot.agent.actions;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.leavesmc.leaves.bot.ServerBot;
|
||||
import org.leavesmc.leaves.entity.bot.actions.CraftJumpAction;
|
||||
|
||||
public class ServerJumpAction extends ServerTimerBotAction<ServerJumpAction> {
|
||||
|
||||
public ServerJumpAction() {
|
||||
super("jump", ServerJumpAction::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doTick(@NotNull ServerBot bot) {
|
||||
if (!bot.onGround()) {
|
||||
return false;
|
||||
} else {
|
||||
bot.jumpFromGround();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object asCraft() {
|
||||
return new CraftJumpAction(this);
|
||||
}
|
||||
}
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
package org.leavesmc.leaves.bot.agent.actions;
|
||||
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.util.Vector;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.leavesmc.leaves.bot.ServerBot;
|
||||
import org.leavesmc.leaves.command.CommandArgument;
|
||||
import org.leavesmc.leaves.command.CommandArgumentResult;
|
||||
import org.leavesmc.leaves.command.CommandArgumentType;
|
||||
import org.leavesmc.leaves.entity.bot.actions.CraftLookAction;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class ServerLookAction extends ServerBotAction<ServerLookAction> {
|
||||
|
||||
private static final Vector ZERO_VECTOR = new Vector(0, 0, 0);
|
||||
private static final DecimalFormat DF = new DecimalFormat("0.0");
|
||||
|
||||
private Vector pos = ZERO_VECTOR;
|
||||
private ServerPlayer target = null;
|
||||
|
||||
public ServerLookAction() {
|
||||
super("look", CommandArgument.of(CommandArgumentType.STRING, CommandArgumentType.DOUBLE, CommandArgumentType.DOUBLE), ServerLookAction::new);
|
||||
this.setSuggestion(0, (sender, arg) -> sender instanceof Player player ?
|
||||
Pair.of(Stream.concat(Arrays.stream(MinecraftServer.getServer().getPlayerNames()), Stream.of(DF.format(player.getX()))).toList(), "<Player>|<X>") :
|
||||
Pair.of(Stream.concat(Arrays.stream(MinecraftServer.getServer().getPlayerNames()), Stream.of("0")).toList(), "<Player>|<X>")
|
||||
);
|
||||
this.setSuggestion(1, (sender, arg) -> sender instanceof Player player ? Pair.of(List.of(DF.format(player.getY())), "<Y>") : Pair.of(List.of("0"), "<Y>"));
|
||||
this.setSuggestion(2, (sender, arg) -> sender instanceof Player player ? Pair.of(List.of(DF.format(player.getZ())), "<Z>") : Pair.of(List.of("0"), "<Z>"));
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public CompoundTag save(@NotNull CompoundTag nbt) {
|
||||
super.save(nbt);
|
||||
if (target != null) {
|
||||
this.pos.setX(this.target.getX());
|
||||
this.pos.setY(this.target.getY());
|
||||
this.pos.setZ(this.target.getZ());
|
||||
}
|
||||
nbt.putDouble("x", this.pos.getX());
|
||||
nbt.putDouble("y", this.pos.getY());
|
||||
nbt.putDouble("z", this.pos.getZ());
|
||||
return nbt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(@NotNull CompoundTag nbt) {
|
||||
super.load(nbt);
|
||||
this.setPos(new Vector(
|
||||
nbt.getDouble("x").orElse(0.0),
|
||||
nbt.getDouble("y").orElse(0.0),
|
||||
nbt.getDouble("z").orElse(0.0)
|
||||
));
|
||||
}
|
||||
|
||||
public void setPos(Vector pos) {
|
||||
this.pos = pos;
|
||||
}
|
||||
|
||||
public Vector getPos() {
|
||||
return this.pos;
|
||||
}
|
||||
|
||||
public void setTarget(ServerPlayer player) {
|
||||
this.target = player;
|
||||
}
|
||||
|
||||
public ServerPlayer getTarget() {
|
||||
return target;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doTick(@NotNull ServerBot bot) {
|
||||
if (target != null) {
|
||||
bot.faceLocation(target.getBukkitEntity().getLocation());
|
||||
} else {
|
||||
bot.look(pos.subtract(bot.getLocation().toVector()), false);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadCommand(ServerPlayer player, @NotNull CommandArgumentResult result) {
|
||||
String nameOrX = result.readString(player.getScoreboardName());
|
||||
ServerPlayer player1 = player.getServer().getPlayerList().getPlayerByName(nameOrX);
|
||||
if (player1 != null) {
|
||||
this.setTarget(player1);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
Vector vector = result.readVectorYZ(Double.parseDouble(nameOrX));
|
||||
this.setPos(vector);
|
||||
} catch (Exception e) {
|
||||
throw new IllegalArgumentException("Invalid vector");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object asCraft() {
|
||||
return new CraftLookAction(this);
|
||||
}
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
package org.leavesmc.leaves.bot.agent.actions;
|
||||
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.craftbukkit.entity.CraftEntity;
|
||||
import org.bukkit.entity.Vehicle;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.leavesmc.leaves.bot.ServerBot;
|
||||
import org.leavesmc.leaves.command.CommandArgument;
|
||||
import org.leavesmc.leaves.entity.bot.actions.CraftMountAction;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
public class ServerMountAction extends ServerBotAction<ServerMountAction> {
|
||||
|
||||
public ServerMountAction() {
|
||||
super("mount", CommandArgument.EMPTY, ServerMountAction::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doTick(@NotNull ServerBot bot) {
|
||||
Location center = bot.getBukkitEntity().getLocation();
|
||||
List<Vehicle> vehicles = center.getNearbyEntitiesByType(
|
||||
Vehicle.class,
|
||||
3,
|
||||
vehicle -> manhattanDistance(bot, ((CraftEntity) vehicle).getHandle()) <= 2
|
||||
).stream().sorted(Comparator.comparingDouble(
|
||||
(vehicle) -> center.distanceSquared(vehicle.getLocation())
|
||||
)).toList();
|
||||
|
||||
for (Vehicle vehicle : vehicles) {
|
||||
if (bot.startRiding(((CraftEntity) vehicle).getHandle(), false)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object asCraft() {
|
||||
return new CraftMountAction(this);
|
||||
}
|
||||
|
||||
private double manhattanDistance(@NotNull Entity entity1, @NotNull Entity entity2) {
|
||||
return Math.abs(entity1.getX() - entity2.getX()) +
|
||||
Math.abs(entity1.getY() - entity2.getY()) +
|
||||
Math.abs(entity1.getZ() - entity2.getZ());
|
||||
}
|
||||
}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
package org.leavesmc.leaves.bot.agent.actions;
|
||||
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.leavesmc.leaves.bot.ServerBot;
|
||||
import org.leavesmc.leaves.command.CommandArgument;
|
||||
import org.leavesmc.leaves.command.CommandArgumentResult;
|
||||
import org.leavesmc.leaves.command.CommandArgumentType;
|
||||
import org.leavesmc.leaves.entity.bot.action.MoveAction.MoveDirection;
|
||||
import org.leavesmc.leaves.entity.bot.actions.CraftMoveAction;
|
||||
import org.leavesmc.leaves.event.bot.BotActionStopEvent;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class ServerMoveAction extends ServerStateBotAction<ServerMoveAction> {
|
||||
|
||||
private static final Pair<List<String>, String> suggestions = Pair.of(
|
||||
Arrays.stream(MoveDirection.values()).map((it) -> it.name).toList(),
|
||||
"<Direction>"
|
||||
);
|
||||
private MoveDirection direction = MoveDirection.FORWARD;
|
||||
|
||||
public ServerMoveAction() {
|
||||
super("move", CommandArgument.of(CommandArgumentType.ofEnum(MoveDirection.class)), ServerMoveAction::new);
|
||||
this.setSuggestion(0, suggestions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadCommand(ServerPlayer player, @NotNull CommandArgumentResult result) {
|
||||
this.direction = result.read(MoveDirection.class);
|
||||
if (direction == null) {
|
||||
throw new IllegalArgumentException("Invalid direction");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop(@NotNull ServerBot bot, BotActionStopEvent.Reason reason) {
|
||||
super.stop(bot, reason);
|
||||
switch (direction) {
|
||||
case FORWARD, BACKWARD -> bot.zza = 0.0f;
|
||||
case LEFT, RIGHT -> bot.xxa = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doTick(@NotNull ServerBot bot) {
|
||||
boolean isSneaking = bot.isShiftKeyDown();
|
||||
float velocity = isSneaking ? 0.3f : 1.0f;
|
||||
switch (direction) {
|
||||
case FORWARD -> bot.zza = velocity;
|
||||
case BACKWARD -> bot.zza = -velocity;
|
||||
case LEFT -> bot.xxa = velocity;
|
||||
case RIGHT -> bot.xxa = -velocity;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public MoveDirection getDirection() {
|
||||
return direction;
|
||||
}
|
||||
|
||||
public void setDirection(MoveDirection direction) {
|
||||
this.direction = direction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object asCraft() {
|
||||
return new CraftMoveAction(this);
|
||||
}
|
||||
}
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
package org.leavesmc.leaves.bot.agent.actions;
|
||||
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.leavesmc.leaves.bot.ServerBot;
|
||||
import org.leavesmc.leaves.command.CommandArgument;
|
||||
import org.leavesmc.leaves.command.CommandArgumentResult;
|
||||
import org.leavesmc.leaves.command.CommandArgumentType;
|
||||
import org.leavesmc.leaves.entity.bot.actions.CraftRotationAction;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public class ServerRotationAction extends ServerBotAction<ServerRotationAction> {
|
||||
|
||||
private static final DecimalFormat DF = new DecimalFormat("0.00");
|
||||
|
||||
public ServerRotationAction() {
|
||||
super("rotation", CommandArgument.of(CommandArgumentType.FLOAT, CommandArgumentType.FLOAT), ServerRotationAction::new);
|
||||
this.setSuggestion(0, (sender, arg) -> sender instanceof Player player ? Pair.of(List.of(DF.format(player.getYaw())), "[yaw]") : Pair.of(List.of("0"), "<yaw>"));
|
||||
this.setSuggestion(1, (sender, arg) -> sender instanceof Player player ? Pair.of(List.of(DF.format(player.getPitch())), "[pitch]") : Pair.of(List.of("0"), "<pitch>"));
|
||||
}
|
||||
|
||||
private float yaw = 0.0f;
|
||||
private float pitch = 0.0f;
|
||||
|
||||
@Override
|
||||
public void loadCommand(ServerPlayer player, @NotNull CommandArgumentResult result) {
|
||||
try {
|
||||
this.yaw = result.readFloat(Objects.requireNonNull(player).getYRot());
|
||||
this.pitch = result.readFloat(player.getXRot());
|
||||
} catch (Exception e) {
|
||||
throw new IllegalArgumentException("No valid rotation specified", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void setYaw(float yaw) {
|
||||
this.yaw = yaw;
|
||||
}
|
||||
|
||||
public void setPitch(float pitch) {
|
||||
this.pitch = pitch;
|
||||
}
|
||||
|
||||
public float getYaw() {
|
||||
return this.yaw;
|
||||
}
|
||||
|
||||
public float getPitch() {
|
||||
return this.pitch;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public CompoundTag save(@NotNull CompoundTag nbt) {
|
||||
super.save(nbt);
|
||||
nbt.putFloat("yaw", this.yaw);
|
||||
nbt.putFloat("pitch", this.pitch);
|
||||
return nbt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(@NotNull CompoundTag nbt) {
|
||||
super.load(nbt);
|
||||
this.setYaw(nbt.getFloat("yaw").orElseThrow());
|
||||
this.setPitch(nbt.getFloat("pitch").orElseThrow());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doTick(@NotNull ServerBot bot) {
|
||||
bot.setRot(yaw, pitch);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object asCraft() {
|
||||
return new CraftRotationAction(this);
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
package org.leavesmc.leaves.bot.agent.actions;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.leavesmc.leaves.bot.ServerBot;
|
||||
import org.leavesmc.leaves.command.CommandArgument;
|
||||
import org.leavesmc.leaves.entity.bot.actions.CraftSneakAction;
|
||||
import org.leavesmc.leaves.event.bot.BotActionStopEvent;
|
||||
|
||||
public class ServerSneakAction extends ServerStateBotAction<ServerSneakAction> {
|
||||
|
||||
public ServerSneakAction() {
|
||||
super("sneak", CommandArgument.EMPTY, ServerSneakAction::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doTick(@NotNull ServerBot bot) {
|
||||
if (bot.isShiftKeyDown()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bot.setShiftKeyDown(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop(@NotNull ServerBot bot, BotActionStopEvent.Reason reason) {
|
||||
super.stop(bot, reason);
|
||||
bot.setShiftKeyDown(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object asCraft() {
|
||||
return new CraftSneakAction(this);
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package org.leavesmc.leaves.bot.agent.actions;
|
||||
|
||||
import org.leavesmc.leaves.command.CommandArgument;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public abstract class ServerStateBotAction<E extends ServerStateBotAction<E>> extends ServerBotAction<E> {
|
||||
|
||||
public ServerStateBotAction(String name, CommandArgument argument, Supplier<E> creator) {
|
||||
super(name, argument, creator);
|
||||
this.setDoNumber(-1);
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
package org.leavesmc.leaves.bot.agent.actions;
|
||||
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.leavesmc.leaves.bot.ServerBot;
|
||||
import org.leavesmc.leaves.command.CommandArgument;
|
||||
import org.leavesmc.leaves.entity.bot.actions.CraftSwapAction;
|
||||
|
||||
public class ServerSwapAction extends ServerBotAction<ServerSwapAction> {
|
||||
|
||||
public ServerSwapAction() {
|
||||
super("swap", CommandArgument.EMPTY, ServerSwapAction::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doTick(@NotNull ServerBot bot) {
|
||||
ItemStack mainHandItem = bot.getMainHandItem();
|
||||
ItemStack offHandItem = bot.getOffhandItem();
|
||||
bot.setItemInHand(InteractionHand.MAIN_HAND, offHandItem);
|
||||
bot.setItemInHand(InteractionHand.OFF_HAND, mainHandItem);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object asCraft() {
|
||||
return new CraftSwapAction(this);
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package org.leavesmc.leaves.bot.agent.actions;
|
||||
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.leavesmc.leaves.bot.ServerBot;
|
||||
import org.leavesmc.leaves.command.CommandArgument;
|
||||
import org.leavesmc.leaves.entity.bot.actions.CraftSwimAction;
|
||||
|
||||
public class ServerSwimAction extends ServerStateBotAction<ServerSwimAction> {
|
||||
|
||||
public ServerSwimAction() {
|
||||
super("swim", CommandArgument.EMPTY, ServerSwimAction::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doTick(@NotNull ServerBot bot) {
|
||||
if (bot.isInWater()) {
|
||||
bot.addDeltaMovement(new Vec3(0, 0.03, 0));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object asCraft() {
|
||||
return new CraftSwimAction(this);
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
package org.leavesmc.leaves.bot.agent.actions;
|
||||
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.leavesmc.leaves.command.CommandArgument;
|
||||
import org.leavesmc.leaves.command.CommandArgumentResult;
|
||||
import org.leavesmc.leaves.command.CommandArgumentType;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public abstract class ServerTimerBotAction<E extends ServerTimerBotAction<E>> extends ServerBotAction<E> {
|
||||
|
||||
public ServerTimerBotAction(String name, Supplier<E> creator) {
|
||||
this(name, CommandArgument.of(CommandArgumentType.INTEGER, CommandArgumentType.INTEGER, CommandArgumentType.INTEGER), creator);
|
||||
}
|
||||
|
||||
public ServerTimerBotAction(String name, CommandArgument argument, Supplier<E> creator) {
|
||||
super(name, argument, creator);
|
||||
this.setSuggestion(0, Pair.of(List.of("0"), "[TickDelay]"));
|
||||
this.setSuggestion(1, Pair.of(List.of("20"), "[TickInterval]"));
|
||||
this.setSuggestion(2, Pair.of(List.of("1", "-1"), "[DoNumber]"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadCommand(ServerPlayer player, @NotNull CommandArgumentResult result) {
|
||||
this.setStartDelayTick(result.readInt(0));
|
||||
this.setDoIntervalTick(result.readInt(20));
|
||||
this.setDoNumber(result.readInt(1));
|
||||
}
|
||||
}
|
||||
+127
@@ -0,0 +1,127 @@
|
||||
package org.leavesmc.leaves.bot.agent.actions;
|
||||
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.InteractionResult;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.leavesmc.leaves.bot.ServerBot;
|
||||
import org.leavesmc.leaves.command.CommandArgument;
|
||||
import org.leavesmc.leaves.command.CommandArgumentResult;
|
||||
import org.leavesmc.leaves.command.CommandArgumentType;
|
||||
import org.leavesmc.leaves.event.bot.BotActionStopEvent;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public abstract class ServerUseBotAction<T extends ServerUseBotAction<T>> extends ServerTimerBotAction<T> {
|
||||
private int useTickTimeout = -1;
|
||||
private int alreadyUsedTick = 0;
|
||||
private int useItemRemainingTicks = 0;
|
||||
|
||||
public ServerUseBotAction(String name, Supplier<T> supplier) {
|
||||
super(name, CommandArgument.of(CommandArgumentType.INTEGER, CommandArgumentType.INTEGER, CommandArgumentType.INTEGER, CommandArgumentType.INTEGER), supplier);
|
||||
this.setSuggestion(3, Pair.of(List.of("-1"), "[UseTickTimeout]"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadCommand(ServerPlayer player, @NotNull CommandArgumentResult result) {
|
||||
super.loadCommand(player, result);
|
||||
this.useTickTimeout = result.readInt(-1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doTick(@NotNull ServerBot bot) {
|
||||
if (shouldStartUseItem()) {
|
||||
boolean isSuccess = interact(bot);
|
||||
syncUseItemRemainingTicks(bot);
|
||||
if (alreadyUseOver()) {
|
||||
resetAlreadyUsedTick();
|
||||
return isSuccess;
|
||||
}
|
||||
} else {
|
||||
syncUseItemRemainingTicks(bot);
|
||||
}
|
||||
|
||||
if (alreadyUseOver()) {
|
||||
resetAlreadyUsedTick();
|
||||
bot.completeUsingItem();
|
||||
return true;
|
||||
} else {
|
||||
increaseAlreadyUsedTick();
|
||||
if (isUseTickLimitExceeded()) {
|
||||
resetAlreadyUsedTick();
|
||||
shouldStartUseItemNextTick();
|
||||
return bot.releaseUsingItemWithResult();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract boolean interact(ServerBot bot);
|
||||
|
||||
public static boolean shouldSwing(InteractionResult result) {
|
||||
return result instanceof InteractionResult.Success success && success.swingSource() != InteractionResult.SwingSource.NONE;
|
||||
}
|
||||
|
||||
private boolean shouldStartUseItem() {
|
||||
return useItemRemainingTicks == 0;
|
||||
}
|
||||
|
||||
private boolean alreadyUseOver() {
|
||||
return useItemRemainingTicks == 0;
|
||||
}
|
||||
|
||||
private boolean isUseTickLimitExceeded() {
|
||||
int useTickLimit = useTickTimeout == -1 ? Integer.MAX_VALUE : useTickTimeout;
|
||||
return alreadyUsedTick > useTickLimit;
|
||||
}
|
||||
|
||||
private void shouldStartUseItemNextTick() {
|
||||
this.useItemRemainingTicks = 0;
|
||||
}
|
||||
|
||||
private void resetAlreadyUsedTick() {
|
||||
this.alreadyUsedTick = 0;
|
||||
}
|
||||
|
||||
private void syncUseItemRemainingTicks(@NotNull ServerBot bot) {
|
||||
this.useItemRemainingTicks = bot.getUseItemRemainingTicks();
|
||||
}
|
||||
|
||||
private void increaseAlreadyUsedTick() {
|
||||
this.alreadyUsedTick++;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public CompoundTag save(@NotNull CompoundTag nbt) {
|
||||
super.save(nbt);
|
||||
nbt.putInt("useTick", this.useTickTimeout);
|
||||
nbt.putInt("alreadyUsedTick", this.alreadyUsedTick);
|
||||
return nbt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(@NotNull CompoundTag nbt) {
|
||||
super.load(nbt);
|
||||
this.useTickTimeout = nbt.getInt("useTick").orElseThrow();
|
||||
this.alreadyUsedTick = nbt.getInt("alreadyUsedTick").orElseGet(
|
||||
() -> this.useTickTimeout - nbt.getInt("tickToRelease").orElseThrow()
|
||||
);
|
||||
}
|
||||
|
||||
public int getUseTickTimeout() {
|
||||
return useTickTimeout;
|
||||
}
|
||||
|
||||
public void setUseTickTimeout(int useTickTimeout) {
|
||||
this.useTickTimeout = useTickTimeout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop(@NotNull ServerBot bot, BotActionStopEvent.Reason reason) {
|
||||
super.stop(bot, reason);
|
||||
bot.releaseUsingItem();
|
||||
}
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
package org.leavesmc.leaves.bot.agent.actions;
|
||||
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.InteractionResult;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.leavesmc.leaves.bot.ServerBot;
|
||||
import org.leavesmc.leaves.entity.bot.actions.CraftUseItemAction;
|
||||
|
||||
public class ServerUseItemAction extends ServerUseBotAction<ServerUseItemAction> {
|
||||
|
||||
public ServerUseItemAction() {
|
||||
super("use", ServerUseItemAction::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean interact(@NotNull ServerBot bot) {
|
||||
return useItem(bot, InteractionHand.MAIN_HAND).consumesAction();
|
||||
}
|
||||
|
||||
public static @NotNull InteractionResult useItem(@NotNull ServerBot bot, InteractionHand hand) {
|
||||
bot.updateItemInHand(hand);
|
||||
InteractionResult result = bot.gameMode.useItem(bot, bot.level(), bot.getItemInHand(hand), hand);
|
||||
if (shouldSwing(result)) {
|
||||
bot.swing(hand);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object asCraft() {
|
||||
return new CraftUseItemAction(this);
|
||||
}
|
||||
}
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
package org.leavesmc.leaves.bot.agent.actions;
|
||||
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.InteractionResult;
|
||||
import net.minecraft.world.entity.decoration.ArmorStand;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.phys.BlockHitResult;
|
||||
import net.minecraft.world.phys.EntityHitResult;
|
||||
import net.minecraft.world.phys.HitResult;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.leavesmc.leaves.bot.ServerBot;
|
||||
import org.leavesmc.leaves.entity.bot.actions.CraftUseItemAutoAction;
|
||||
|
||||
import static org.leavesmc.leaves.bot.agent.actions.ServerUseItemAction.useItem;
|
||||
import static org.leavesmc.leaves.bot.agent.actions.ServerUseItemOnAction.useItemOn;
|
||||
import static org.leavesmc.leaves.bot.agent.actions.ServerUseItemToAction.useItemTo;
|
||||
|
||||
public class ServerUseItemAutoAction extends ServerUseBotAction<ServerUseItemAutoAction> {
|
||||
|
||||
public ServerUseItemAutoAction() {
|
||||
super("use_auto", ServerUseItemAutoAction::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean interact(ServerBot bot) {
|
||||
HitResult hitResult = getHitResult(bot);
|
||||
for (InteractionHand hand : InteractionHand.values()) {
|
||||
ItemStack itemStack = bot.getItemInHand(hand);
|
||||
if (!itemStack.isItemEnabled(bot.level().enabledFeatures())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hitResult != null) {
|
||||
switch (hitResult.getType()) {
|
||||
case ENTITY -> {
|
||||
EntityHitResult entityHitResult = (EntityHitResult) hitResult;
|
||||
InteractionResult entityResult = useItemTo(bot, entityHitResult, hand);
|
||||
if (entityResult instanceof InteractionResult.Success) {
|
||||
return true;
|
||||
} else if (entityResult instanceof InteractionResult.Pass && entityHitResult.getEntity() instanceof ArmorStand) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
case BLOCK -> {
|
||||
InteractionResult blockResult = useItemOn(bot, (BlockHitResult) hitResult, hand);
|
||||
if (blockResult instanceof InteractionResult.Success) {
|
||||
return true;
|
||||
} else if (blockResult instanceof InteractionResult.Fail) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!itemStack.isEmpty() && useItem(bot, hand) instanceof InteractionResult.Success) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static @Nullable HitResult getHitResult(@NotNull ServerBot bot) {
|
||||
Vec3 eyePos = bot.getEyePosition();
|
||||
|
||||
EntityHitResult entityHitResult = bot.getEntityHitResult();
|
||||
double entityDistance = entityHitResult != null ? entityHitResult.getLocation().distanceToSqr(eyePos) : Double.MAX_VALUE;
|
||||
|
||||
BlockHitResult blockHitResult = bot.getBlockHitResult();
|
||||
double blockDistance = blockHitResult != null ? blockHitResult.getLocation().distanceToSqr(eyePos) : Double.MAX_VALUE;
|
||||
|
||||
if (entityDistance == Double.MAX_VALUE && blockDistance == Double.MAX_VALUE) {
|
||||
return null;
|
||||
} else if (entityDistance < blockDistance) {
|
||||
return entityHitResult;
|
||||
} else {
|
||||
return blockHitResult;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object asCraft() {
|
||||
return new CraftUseItemAutoAction(this);
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package org.leavesmc.leaves.bot.agent.actions;
|
||||
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.leavesmc.leaves.bot.ServerBot;
|
||||
import org.leavesmc.leaves.entity.bot.actions.CraftUseItemOffhandAction;
|
||||
|
||||
import static org.leavesmc.leaves.bot.agent.actions.ServerUseItemAction.useItem;
|
||||
|
||||
public class ServerUseItemOffhandAction extends ServerUseBotAction<ServerUseItemOffhandAction> {
|
||||
|
||||
public ServerUseItemOffhandAction() {
|
||||
super("use_offhand", ServerUseItemOffhandAction::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean interact(@NotNull ServerBot bot) {
|
||||
return useItem(bot, InteractionHand.OFF_HAND).consumesAction();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object asCraft() {
|
||||
return new CraftUseItemOffhandAction(this);
|
||||
}
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
package org.leavesmc.leaves.bot.agent.actions;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.InteractionResult;
|
||||
import net.minecraft.world.phys.BlockHitResult;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.leavesmc.leaves.bot.ServerBot;
|
||||
import org.leavesmc.leaves.entity.bot.actions.CraftUseItemOnAction;
|
||||
|
||||
public class ServerUseItemOnAction extends ServerUseBotAction<ServerUseItemOnAction> {
|
||||
|
||||
public ServerUseItemOnAction() {
|
||||
super("use_on", ServerUseItemOnAction::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean interact(@NotNull ServerBot bot) {
|
||||
BlockHitResult hitResult = bot.getBlockHitResult();
|
||||
return useItemOn(bot, hitResult, InteractionHand.MAIN_HAND).consumesAction();
|
||||
}
|
||||
|
||||
public static InteractionResult useItemOn(ServerBot bot, BlockHitResult hitResult, InteractionHand hand) {
|
||||
if (hitResult == null) {
|
||||
return InteractionResult.FAIL;
|
||||
}
|
||||
|
||||
BlockPos blockPos = hitResult.getBlockPos();
|
||||
if (!bot.level().getWorldBorder().isWithinBounds(blockPos)) {
|
||||
return InteractionResult.FAIL;
|
||||
}
|
||||
|
||||
bot.updateItemInHand(hand);
|
||||
InteractionResult interactionResult = bot.gameMode.useItemOn(bot, bot.level(), bot.getItemInHand(hand), hand, hitResult);
|
||||
if (shouldSwing(interactionResult)) {
|
||||
bot.swing(hand);
|
||||
}
|
||||
|
||||
return interactionResult;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object asCraft() {
|
||||
return new CraftUseItemOnAction(this);
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package org.leavesmc.leaves.bot.agent.actions;
|
||||
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.phys.BlockHitResult;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.leavesmc.leaves.bot.ServerBot;
|
||||
import org.leavesmc.leaves.entity.bot.actions.CraftUseItemOnOffhandAction;
|
||||
|
||||
import static org.leavesmc.leaves.bot.agent.actions.ServerUseItemOnAction.useItemOn;
|
||||
|
||||
public class ServerUseItemOnOffhandAction extends ServerUseBotAction<ServerUseItemOnOffhandAction> {
|
||||
|
||||
public ServerUseItemOnOffhandAction() {
|
||||
super("use_on_offhand", ServerUseItemOnOffhandAction::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean interact(@NotNull ServerBot bot) {
|
||||
BlockHitResult hitResult = bot.getBlockHitResult();
|
||||
return useItemOn(bot, hitResult, InteractionHand.OFF_HAND).consumesAction();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object asCraft() {
|
||||
return new CraftUseItemOnOffhandAction(this);
|
||||
}
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
package org.leavesmc.leaves.bot.agent.actions;
|
||||
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.InteractionResult;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.phys.EntityHitResult;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.leavesmc.leaves.bot.ServerBot;
|
||||
import org.leavesmc.leaves.entity.bot.actions.CraftUseItemToAction;
|
||||
|
||||
public class ServerUseItemToAction extends ServerUseBotAction<ServerUseItemToAction> {
|
||||
|
||||
public ServerUseItemToAction() {
|
||||
super("use_to", ServerUseItemToAction::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean interact(@NotNull ServerBot bot) {
|
||||
EntityHitResult hitResult = bot.getEntityHitResult();
|
||||
return useItemTo(bot, hitResult, InteractionHand.MAIN_HAND).consumesAction();
|
||||
}
|
||||
|
||||
public static InteractionResult useItemTo(ServerBot bot, EntityHitResult hitResult, InteractionHand hand) {
|
||||
if (hitResult == null) {
|
||||
return InteractionResult.FAIL;
|
||||
}
|
||||
|
||||
Entity entity = hitResult.getEntity();
|
||||
if (!bot.level().getWorldBorder().isWithinBounds(entity.blockPosition())) {
|
||||
return InteractionResult.FAIL;
|
||||
}
|
||||
|
||||
Vec3 vec3 = hitResult.getLocation().subtract(entity.getX(), entity.getY(), entity.getZ());
|
||||
bot.updateItemInHand(hand);
|
||||
InteractionResult interactionResult = entity.interactAt(bot, vec3, hand);
|
||||
if (!interactionResult.consumesAction()) {
|
||||
interactionResult = bot.interactOn(hitResult.getEntity(), hand);
|
||||
}
|
||||
|
||||
if (shouldSwing(interactionResult)) {
|
||||
bot.swing(hand);
|
||||
}
|
||||
|
||||
return interactionResult;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object asCraft() {
|
||||
return new CraftUseItemToAction(this);
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package org.leavesmc.leaves.bot.agent.actions;
|
||||
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.phys.EntityHitResult;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.leavesmc.leaves.bot.ServerBot;
|
||||
import org.leavesmc.leaves.entity.bot.actions.CraftUseItemToOffhandAction;
|
||||
|
||||
import static org.leavesmc.leaves.bot.agent.actions.ServerUseItemToAction.useItemTo;
|
||||
|
||||
public class ServerUseItemToOffhandAction extends ServerUseBotAction<ServerUseItemToOffhandAction> {
|
||||
|
||||
public ServerUseItemToOffhandAction() {
|
||||
super("use_to_offhand", ServerUseItemToOffhandAction::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean interact(@NotNull ServerBot bot) {
|
||||
EntityHitResult hitResult = bot.getEntityHitResult();
|
||||
return useItemTo(bot, hitResult, InteractionHand.OFF_HAND).consumesAction();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object asCraft() {
|
||||
return new CraftUseItemToOffhandAction(this);
|
||||
}
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
package org.leavesmc.leaves.bot.agent.configs;
|
||||
|
||||
import fun.bm.lophine.config.modules.misc.FakeplayerConfig;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.leavesmc.leaves.bot.agent.AbstractBotConfig;
|
||||
import org.leavesmc.leaves.command.CommandArgument;
|
||||
import org.leavesmc.leaves.command.CommandArgumentType;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class AlwaysSendDataConfig extends AbstractBotConfig<Boolean> {
|
||||
|
||||
public static final String NAME = "always_send_data";
|
||||
|
||||
private boolean value;
|
||||
|
||||
public AlwaysSendDataConfig() {
|
||||
super(NAME, CommandArgument.of(CommandArgumentType.BOOLEAN).setSuggestion(0, List.of("true", "false")));
|
||||
this.value = FakeplayerConfig.canSendDataAlways;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(Boolean value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public CompoundTag save(@NotNull CompoundTag nbt) {
|
||||
super.save(nbt);
|
||||
nbt.putBoolean(NAME, this.getValue());
|
||||
return nbt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(@NotNull CompoundTag nbt) {
|
||||
this.setValue(nbt.getBooleanOr(NAME, FakeplayerConfig.canSendDataAlways));
|
||||
}
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
package org.leavesmc.leaves.bot.agent.configs;
|
||||
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.server.waypoints.ServerWaypointManager;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import fun.bm.lophine.config.modules.misc.FakeplayerConfig;
|
||||
import org.leavesmc.leaves.bot.agent.AbstractBotConfig;
|
||||
import org.leavesmc.leaves.command.CommandArgument;
|
||||
import org.leavesmc.leaves.command.CommandArgumentType;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class LocatorBarConfig extends AbstractBotConfig<Boolean> {
|
||||
|
||||
public static final String NAME = "enable_locator_bar";
|
||||
|
||||
private boolean value;
|
||||
|
||||
public LocatorBarConfig() {
|
||||
super(NAME, CommandArgument.of(CommandArgumentType.BOOLEAN).setSuggestion(0, List.of("true", "false")));
|
||||
this.value = FakeplayerConfig.enableLocatorBar;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(Boolean value) throws IllegalArgumentException {
|
||||
this.value = value;
|
||||
ServerWaypointManager manager = this.bot.level().getWaypointManager();
|
||||
if (value) {
|
||||
manager.trackWaypoint(this.bot);
|
||||
} else {
|
||||
manager.untrackWaypoint(this.bot);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull CompoundTag save(@NotNull CompoundTag nbt) {
|
||||
super.save(nbt);
|
||||
nbt.putBoolean(NAME, this.getValue());
|
||||
return nbt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(@NotNull CompoundTag nbt) {
|
||||
this.setValue(nbt.getBooleanOr(NAME, FakeplayerConfig.enableLocatorBar));
|
||||
}
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
package org.leavesmc.leaves.bot.agent.configs;
|
||||
|
||||
import fun.bm.lophine.config.modules.misc.FakeplayerConfig;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.leavesmc.leaves.bot.agent.AbstractBotConfig;
|
||||
import org.leavesmc.leaves.command.CommandArgument;
|
||||
import org.leavesmc.leaves.command.CommandArgumentType;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class SimulationDistanceConfig extends AbstractBotConfig<Integer> {
|
||||
|
||||
public static final String NAME = "simulation_distance";
|
||||
|
||||
public SimulationDistanceConfig() {
|
||||
super(NAME, CommandArgument.of(CommandArgumentType.INTEGER).setSuggestion(0, Pair.of(List.of("2", "10"), "<INT 2 - 32>")));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getValue() {
|
||||
return this.bot.getBukkitEntity().getSimulationDistance();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(Integer value) {
|
||||
if (value < 2 || value > 32) {
|
||||
throw new IllegalArgumentException("simulation_distance must be a number between 2 and 32, got: " + value);
|
||||
}
|
||||
this.bot.getBukkitEntity().setSimulationDistance(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public CompoundTag save(@NotNull CompoundTag nbt) {
|
||||
super.save(nbt);
|
||||
nbt.putInt(NAME, this.getValue());
|
||||
return nbt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(@NotNull CompoundTag nbt) {
|
||||
this.setValue(nbt.getIntOr(NAME, FakeplayerConfig.getSimulationDistance(this.bot)));
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
package org.leavesmc.leaves.bot.agent.configs;
|
||||
|
||||
import fun.bm.lophine.config.modules.misc.FakeplayerConfig;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.leavesmc.leaves.bot.agent.AbstractBotConfig;
|
||||
import org.leavesmc.leaves.command.CommandArgument;
|
||||
import org.leavesmc.leaves.command.CommandArgumentType;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class SkipSleepConfig extends AbstractBotConfig<Boolean> {
|
||||
|
||||
public static final String NAME = "skip_sleep";
|
||||
|
||||
public SkipSleepConfig() {
|
||||
super(NAME, CommandArgument.of(CommandArgumentType.BOOLEAN).setSuggestion(0, List.of("true", "false")));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean getValue() {
|
||||
return bot.fauxSleeping;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(Boolean value) throws IllegalArgumentException {
|
||||
bot.fauxSleeping = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull CompoundTag save(@NotNull CompoundTag nbt) {
|
||||
super.save(nbt);
|
||||
nbt.putBoolean(NAME, this.getValue());
|
||||
return nbt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(@NotNull CompoundTag nbt) {
|
||||
this.setValue(nbt.getBooleanOr(NAME, FakeplayerConfig.canSkipSleep));
|
||||
}
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
package org.leavesmc.leaves.bot.agent.configs;
|
||||
|
||||
import fun.bm.lophine.config.modules.misc.FakeplayerConfig;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.leavesmc.leaves.bot.agent.AbstractBotConfig;
|
||||
import org.leavesmc.leaves.command.CommandArgument;
|
||||
import org.leavesmc.leaves.command.CommandArgumentType;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class SpawnPhantomConfig extends AbstractBotConfig<Boolean> {
|
||||
|
||||
public static final String NAME = "spawn_phantom";
|
||||
|
||||
private boolean value;
|
||||
|
||||
public SpawnPhantomConfig() {
|
||||
super(NAME, CommandArgument.of(CommandArgumentType.BOOLEAN).setSuggestion(0, List.of("true", "false")));
|
||||
this.value = FakeplayerConfig.canSpawnPhantom;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(Boolean value) throws IllegalArgumentException {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getMessage() {
|
||||
return List.of(
|
||||
bot.getScoreboardName() + "'s spawn_phantom: " + this.getValue(),
|
||||
bot.getScoreboardName() + "'s not_sleeping_ticks: " + bot.notSleepTicks
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull CompoundTag save(@NotNull CompoundTag nbt) {
|
||||
super.save(nbt);
|
||||
nbt.putBoolean(NAME, this.getValue());
|
||||
return nbt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(@NotNull CompoundTag nbt) {
|
||||
this.setValue(nbt.getBooleanOr(NAME, FakeplayerConfig.canSpawnPhantom));
|
||||
}
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
package org.leavesmc.leaves.bot.agent.configs;
|
||||
|
||||
import fun.bm.lophine.config.modules.misc.FakeplayerConfig;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.leavesmc.leaves.bot.ServerBot;
|
||||
import org.leavesmc.leaves.bot.agent.AbstractBotConfig;
|
||||
import org.leavesmc.leaves.command.CommandArgument;
|
||||
import org.leavesmc.leaves.command.CommandArgumentType;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class TickTypeConfig extends AbstractBotConfig<ServerBot.TickType> {
|
||||
|
||||
private static final String NAME = "tick_type";
|
||||
private static final CommandArgumentType<ServerBot.TickType> TICK_TYPE_ARGUMENT = CommandArgumentType.ofEnum(ServerBot.TickType.class);
|
||||
|
||||
private ServerBot.TickType value;
|
||||
|
||||
public TickTypeConfig() {
|
||||
super(NAME, CommandArgument.of(TICK_TYPE_ARGUMENT).setSuggestion(0, List.of("network", "entity_list")));
|
||||
this.value = FakeplayerConfig.tickType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServerBot.TickType getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(ServerBot.TickType value) throws IllegalArgumentException {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public CompoundTag save(@NotNull CompoundTag nbt) {
|
||||
super.save(nbt);
|
||||
nbt.putString(NAME, this.getValue().toString());
|
||||
return nbt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(@NotNull CompoundTag nbt) {
|
||||
this.setValue(TICK_TYPE_ARGUMENT.parse(nbt.getStringOr(NAME, FakeplayerConfig.tickType.name())));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package org.leavesmc.leaves.command;
|
||||
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
public class CommandArgument {
|
||||
|
||||
public static final CommandArgument EMPTY = new CommandArgument();
|
||||
|
||||
private static final Pair<List<String>, String> EMPTY_SUGGESTION_RESULT = Pair.of(List.of(), null);
|
||||
private static final BiFunction<CommandSender, String, Pair<List<String>, String>> EMPTY_SUGGESTION = (sender, arg) -> EMPTY_SUGGESTION_RESULT;
|
||||
|
||||
private final List<BiFunction<CommandSender, String, Pair<List<String>, String>>> suggestions;
|
||||
private final List<CommandArgumentType<?>> argumentTypes;
|
||||
|
||||
private CommandArgument(CommandArgumentType<?>... argumentTypes) {
|
||||
this.argumentTypes = List.of(argumentTypes);
|
||||
this.suggestions = new ArrayList<>();
|
||||
for (int i = 0; i < argumentTypes.length; i++) {
|
||||
suggestions.add(EMPTY_SUGGESTION);
|
||||
}
|
||||
}
|
||||
|
||||
public static CommandArgument of(CommandArgumentType<?>... argumentTypes) {
|
||||
return new CommandArgument(argumentTypes);
|
||||
}
|
||||
|
||||
public List<CommandArgumentType<?>> getArgumentTypes() {
|
||||
return argumentTypes;
|
||||
}
|
||||
|
||||
public CommandArgument setSuggestion(int n, BiFunction<CommandSender, String, Pair<List<String>, String>> suggestion) {
|
||||
this.suggestions.set(n, suggestion);
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandArgument setSuggestion(int n, Pair<List<String>, String> suggestion) {
|
||||
return this.setSuggestion(n, (sender, arg) -> suggestion);
|
||||
}
|
||||
|
||||
public CommandArgument setSuggestion(int n, List<String> tabComplete) {
|
||||
return this.setSuggestion(n, Pair.of(tabComplete, null));
|
||||
}
|
||||
|
||||
public Pair<List<String>, String> suggestion(int n, CommandSender sender, String arg) {
|
||||
if (suggestions.size() > n) {
|
||||
return suggestions.get(n).apply(sender, arg);
|
||||
} else {
|
||||
return EMPTY_SUGGESTION.apply(sender, arg);
|
||||
}
|
||||
}
|
||||
|
||||
public CommandArgumentResult parse(int index, String @NotNull [] args) {
|
||||
Object[] result = new Object[argumentTypes.size()];
|
||||
Arrays.fill(result, null);
|
||||
for (int i = index, j = 0; i < args.length && j < result.length; i++, j++) {
|
||||
result[j] = argumentTypes.get(j).parse(args[i]);
|
||||
}
|
||||
return new CommandArgumentResult(new ArrayList<>(Arrays.asList(result)));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package org.leavesmc.leaves.command;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import org.bukkit.util.Vector;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public class CommandArgumentResult {
|
||||
|
||||
private final List<Object> result;
|
||||
|
||||
public CommandArgumentResult(List<Object> result) {
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
public int readInt(int def) {
|
||||
return Objects.requireNonNullElse(read(Integer.class), def);
|
||||
}
|
||||
|
||||
public double readDouble(double def) {
|
||||
return Objects.requireNonNullElse(read(Double.class), def);
|
||||
}
|
||||
|
||||
public float readFloat(float def) {
|
||||
return Objects.requireNonNullElse(read(Float.class), def);
|
||||
}
|
||||
|
||||
public String readString(String def) {
|
||||
return Objects.requireNonNullElse(read(String.class), def);
|
||||
}
|
||||
|
||||
public boolean readBoolean(boolean def) {
|
||||
return Objects.requireNonNullElse(read(Boolean.class), def);
|
||||
}
|
||||
|
||||
public BlockPos readPos() {
|
||||
Integer[] pos = {read(Integer.class), read(Integer.class), read(Integer.class)};
|
||||
for (Integer po : pos) {
|
||||
if (po == null) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return new BlockPos(pos[0], pos[1], pos[2]);
|
||||
}
|
||||
|
||||
public @Nullable Vector readVector() {
|
||||
Double[] pos = {read(Double.class), read(Double.class), read(Double.class)};
|
||||
for (Double po : pos) {
|
||||
if (po == null) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return new Vector(pos[0], pos[1], pos[2]);
|
||||
}
|
||||
|
||||
public @NotNull Vector readVectorYZ(double x) {
|
||||
Double[] pos = {x, read(Double.class), read(Double.class)};
|
||||
for (Double po : pos) {
|
||||
if (po == null) {
|
||||
throw new IllegalArgumentException("Failed to read vector!");
|
||||
}
|
||||
}
|
||||
return new Vector(pos[0], pos[1], pos[2]);
|
||||
}
|
||||
|
||||
public Object readObject() {
|
||||
if (result.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return result.removeFirst();
|
||||
}
|
||||
|
||||
public <T> T read(Class<T> tClass, T def) {
|
||||
return Objects.requireNonNullElse(read(tClass), def);
|
||||
}
|
||||
|
||||
public <T> T read(Class<T> tClass) {
|
||||
if (result.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Object obj = result.removeFirst();
|
||||
if (tClass.isInstance(obj)) {
|
||||
return tClass.cast(obj);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package org.leavesmc.leaves.command;
|
||||
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
public abstract class CommandArgumentType<E> {
|
||||
|
||||
public static final CommandArgumentType<String> STRING = CommandArgumentType.string();
|
||||
public static final CommandArgumentType<Integer> INTEGER = CommandArgumentType.of(Integer.class, Integer::parseInt);
|
||||
public static final CommandArgumentType<Double> DOUBLE = CommandArgumentType.of(Double.class, Double::parseDouble);
|
||||
public static final CommandArgumentType<Float> FLOAT = CommandArgumentType.of(Float.class, Float::parseFloat);
|
||||
public static final CommandArgumentType<Boolean> BOOLEAN = CommandArgumentType.of(Boolean.class, Boolean::parseBoolean);
|
||||
|
||||
private final Class<E> type;
|
||||
|
||||
private CommandArgumentType(Class<E> type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Contract(value = "_, _ -> new", pure = true)
|
||||
public static <E> CommandArgumentType<E> of(Class<E> type, Function<String, E> parse) {
|
||||
return new CommandArgumentType<>(type) {
|
||||
@Override
|
||||
public E parse(@NotNull String arg) {
|
||||
try {
|
||||
return parse.apply(arg);
|
||||
} catch (Exception ignore) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Contract(value = "_ -> new", pure = true)
|
||||
public static <E extends Enum<E>> CommandArgumentType<E> ofEnum(Class<E> type) {
|
||||
return of(type, (string -> Enum.valueOf(type, string.toUpperCase())));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Contract(value = " -> new", pure = true)
|
||||
private static CommandArgumentType<String> string() {
|
||||
return new CommandArgumentType<>(String.class) {
|
||||
@Override
|
||||
public String parse(@NotNull String arg) {
|
||||
return arg;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public Class<E> getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public abstract E parse(@NotNull String arg);
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
package org.leavesmc.leaves.entity.bot;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.craftbukkit.CraftServer;
|
||||
import org.bukkit.craftbukkit.entity.CraftPlayer;
|
||||
import org.bukkit.event.player.PlayerTeleportEvent;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.leavesmc.leaves.bot.BotList;
|
||||
import org.leavesmc.leaves.bot.ServerBot;
|
||||
import org.leavesmc.leaves.bot.agent.actions.ServerBotAction;
|
||||
import org.leavesmc.leaves.entity.bot.action.BotAction;
|
||||
import org.leavesmc.leaves.entity.bot.actions.CraftBotAction;
|
||||
import org.leavesmc.leaves.event.bot.BotActionStopEvent;
|
||||
import org.leavesmc.leaves.event.bot.BotRemoveEvent;
|
||||
|
||||
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 this.getHandle().createState.skinName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getRealName() {
|
||||
return this.getHandle().createState.realName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable UUID getCreatePlayerUUID() {
|
||||
return this.getHandle().createPlayer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends BotAction<T>> void addAction(@NotNull T action) {
|
||||
switch (action) {
|
||||
case CraftBotAction act -> this.getHandle().addBotAction(act.getHandle(), null);
|
||||
default -> throw new IllegalArgumentException("Action " + action.getClass().getName() + " is not a valid BotAction type!");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public BotAction<?> getAction(int index) {
|
||||
return (BotAction<?>) this.getHandle().getBotActions().get(index).asCraft();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getActionSize() {
|
||||
return this.getHandle().getBotActions().size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopAction(int index) {
|
||||
this.getHandle().getBotActions().get(index).stop(this.getHandle(), BotActionStopEvent.Reason.PLUGIN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopAllActions() {
|
||||
for (ServerBotAction<?> action : this.getHandle().getBotActions()) {
|
||||
action.stop(this.getHandle(), BotActionStopEvent.Reason.PLUGIN);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(boolean save) {
|
||||
BotList.INSTANCE.removeBot(this.getHandle(), BotRemoveEvent.RemoveReason.PLUGIN, null, save);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean teleport(Location location, PlayerTeleportEvent.@NotNull TeleportCause cause, io.papermc.paper.entity.TeleportFlag @NotNull ... flags) {
|
||||
Preconditions.checkArgument(location != null, "location cannot be null");
|
||||
Preconditions.checkState(location.getWorld().equals(this.getWorld()), "[Leaves] Fakeplayers do not support changing world, Please use leaves fakeplayer-api instead!");
|
||||
return super.teleport(location, cause, flags);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServerBot getHandle() {
|
||||
return (ServerBot) entity;
|
||||
}
|
||||
|
||||
public void setHandle(final ServerBot entity) {
|
||||
super.setHandle(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CraftBot{" + "name=" + getName() + '}';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package org.leavesmc.leaves.entity.bot;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import org.bukkit.Location;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.leavesmc.leaves.bot.BotCreateState;
|
||||
import org.leavesmc.leaves.bot.BotList;
|
||||
import org.leavesmc.leaves.bot.ServerBot;
|
||||
import org.leavesmc.leaves.bot.agent.Actions;
|
||||
import org.leavesmc.leaves.bot.agent.actions.ServerBotAction;
|
||||
import org.leavesmc.leaves.entity.bot.action.BotAction;
|
||||
import org.leavesmc.leaves.event.bot.BotCreateEvent;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.UUID;
|
||||
|
||||
public class CraftBotManager implements BotManager {
|
||||
|
||||
private final BotList botList;
|
||||
private final Collection<Bot> botViews;
|
||||
|
||||
public CraftBotManager() {
|
||||
this.botList = MinecraftServer.getServer().getBotList();
|
||||
this.botViews = Collections.unmodifiableList(Lists.transform(botList.bots, ServerBot::getBukkitEntity));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Bot getBot(@NotNull UUID uuid) {
|
||||
ServerBot bot = botList.getBot(uuid);
|
||||
if (bot != null) {
|
||||
return bot.getBukkitEntity();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Bot getBot(@NotNull String name) {
|
||||
ServerBot bot = botList.getBotByName(name);
|
||||
if (bot != null) {
|
||||
return bot.getBukkitEntity();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Bot> getBots() {
|
||||
return botViews;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T extends BotAction<T>> T newAction(@NotNull Class<T> type) {
|
||||
ServerBotAction<?> action = Actions.getForClass(type);
|
||||
if (action == null) {
|
||||
throw new IllegalArgumentException("No action registered for type: " + type.getName());
|
||||
} else {
|
||||
try {
|
||||
return (T) action.create().asCraft();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to create action of type: " + type.getName(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public BotCreator botCreator(@NotNull String realName, @NotNull Location location) {
|
||||
return BotCreateState.builder(realName, location).createReason(BotCreateEvent.CreateReason.PLUGIN);
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
package org.leavesmc.leaves.entity.bot.actions;
|
||||
|
||||
import org.leavesmc.leaves.bot.agent.actions.ServerAttackAction;
|
||||
import org.leavesmc.leaves.entity.bot.action.AttackAction;
|
||||
|
||||
public class CraftAttackAction extends CraftTimerBotAction<AttackAction, ServerAttackAction> implements AttackAction {
|
||||
|
||||
public CraftAttackAction(ServerAttackAction serverAction) {
|
||||
super(serverAction, CraftAttackAction::new);
|
||||
}
|
||||
}
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
package org.leavesmc.leaves.entity.bot.actions;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.leavesmc.leaves.bot.ServerBot;
|
||||
import org.leavesmc.leaves.bot.agent.actions.ServerBotAction;
|
||||
import org.leavesmc.leaves.entity.bot.action.BotAction;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
public abstract class CraftBotAction<T extends BotAction<T>, S extends ServerBotAction<S>> implements BotAction<T> {
|
||||
|
||||
protected final S serverAction;
|
||||
protected final Function<S, T> creator;
|
||||
|
||||
protected Consumer<T> onFail = null;
|
||||
protected Consumer<T> onSuccess = null;
|
||||
protected Consumer<T> onStop = null;
|
||||
|
||||
public CraftBotAction(S serverAction, Function<S, T> creator) {
|
||||
this.serverAction = serverAction;
|
||||
this.creator = creator;
|
||||
}
|
||||
|
||||
public ServerBotAction<?> getHandle() {
|
||||
return serverAction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return serverAction.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getUUID() {
|
||||
return serverAction.getUUID();
|
||||
}
|
||||
|
||||
public boolean doTick(@NotNull ServerBot bot) {
|
||||
return serverAction.doTick(bot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOnFail(Consumer<T> onFail) {
|
||||
this.onFail = onFail;
|
||||
serverAction.setOnFail(it -> onFail.accept(creator.apply(it)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Consumer<T> getOnFail() {
|
||||
return onFail;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOnSuccess(Consumer<T> onSuccess) {
|
||||
this.onSuccess = onSuccess;
|
||||
serverAction.setOnSuccess(it -> onSuccess.accept(creator.apply(it)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Consumer<T> getOnSuccess() {
|
||||
return onSuccess;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOnStop(Consumer<T> onStop) {
|
||||
this.onStop = onStop;
|
||||
serverAction.setOnStop(it -> onStop.accept(creator.apply(it)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Consumer<T> getOnStop() {
|
||||
return onStop;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCancelled(boolean cancel) {
|
||||
serverAction.setCancelled(cancel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return serverAction.isCancelled();
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
package org.leavesmc.leaves.entity.bot.actions;
|
||||
|
||||
import org.leavesmc.leaves.bot.agent.actions.ServerBreakBlockAction;
|
||||
import org.leavesmc.leaves.entity.bot.action.BreakBlockAction;
|
||||
|
||||
public class CraftBreakBlockAction extends CraftTimerBotAction<BreakBlockAction, ServerBreakBlockAction> implements BreakBlockAction {
|
||||
|
||||
public CraftBreakBlockAction(ServerBreakBlockAction serverAction) {
|
||||
super(serverAction, CraftBreakBlockAction::new);
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
package org.leavesmc.leaves.entity.bot.actions;
|
||||
|
||||
import org.leavesmc.leaves.bot.agent.actions.ServerDropAction;
|
||||
import org.leavesmc.leaves.entity.bot.action.DropAction;
|
||||
|
||||
public class CraftDropAction extends CraftTimerBotAction<DropAction, ServerDropAction> implements DropAction {
|
||||
|
||||
public CraftDropAction(ServerDropAction serverAction) {
|
||||
super(serverAction, CraftDropAction::new);
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
package org.leavesmc.leaves.entity.bot.actions;
|
||||
|
||||
import org.leavesmc.leaves.bot.agent.actions.ServerFishAction;
|
||||
import org.leavesmc.leaves.entity.bot.action.FishAction;
|
||||
|
||||
public class CraftFishAction extends CraftTimerBotAction<FishAction, ServerFishAction> implements FishAction {
|
||||
|
||||
public CraftFishAction(ServerFishAction serverAction) {
|
||||
super(serverAction, CraftFishAction::new);
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
package org.leavesmc.leaves.entity.bot.actions;
|
||||
|
||||
import org.leavesmc.leaves.bot.agent.actions.ServerJumpAction;
|
||||
import org.leavesmc.leaves.entity.bot.action.JumpAction;
|
||||
|
||||
public class CraftJumpAction extends CraftTimerBotAction<JumpAction, ServerJumpAction> implements JumpAction {
|
||||
|
||||
public CraftJumpAction(ServerJumpAction serverAction) {
|
||||
super(serverAction, CraftJumpAction::new);
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
package org.leavesmc.leaves.entity.bot.actions;
|
||||
|
||||
import org.bukkit.craftbukkit.entity.CraftPlayer;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.util.Vector;
|
||||
import org.leavesmc.leaves.bot.agent.actions.ServerLookAction;
|
||||
import org.leavesmc.leaves.entity.bot.action.LookAction;
|
||||
|
||||
public class CraftLookAction extends CraftBotAction<LookAction, ServerLookAction> implements LookAction {
|
||||
|
||||
public CraftLookAction(ServerLookAction serverAction) {
|
||||
super(serverAction, CraftLookAction::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LookAction setPos(Vector pos) {
|
||||
serverAction.setPos(pos);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector getPos() {
|
||||
return serverAction.getPos();
|
||||
}
|
||||
|
||||
@Override
|
||||
public LookAction setTarget(Player player) {
|
||||
serverAction.setTarget(((CraftPlayer) player).getHandle());
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Player getTarget() {
|
||||
return serverAction.getTarget().getBukkitEntity();
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
package org.leavesmc.leaves.entity.bot.actions;
|
||||
|
||||
import org.leavesmc.leaves.bot.agent.actions.ServerMountAction;
|
||||
import org.leavesmc.leaves.entity.bot.action.MountAction;
|
||||
|
||||
public class CraftMountAction extends CraftBotAction<MountAction, ServerMountAction> implements MountAction {
|
||||
|
||||
public CraftMountAction(ServerMountAction serverAction) {
|
||||
super(serverAction, CraftMountAction::new);
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package org.leavesmc.leaves.entity.bot.actions;
|
||||
|
||||
import org.leavesmc.leaves.bot.agent.actions.ServerMoveAction;
|
||||
import org.leavesmc.leaves.entity.bot.action.MoveAction;
|
||||
|
||||
public class CraftMoveAction extends CraftBotAction<MoveAction, ServerMoveAction> implements MoveAction {
|
||||
|
||||
public CraftMoveAction(ServerMoveAction serverAction) {
|
||||
super(serverAction, CraftMoveAction::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MoveDirection getDirection() {
|
||||
return serverAction.getDirection();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MoveAction setDirection(MoveDirection direction) {
|
||||
serverAction.setDirection(direction);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
package org.leavesmc.leaves.entity.bot.actions;
|
||||
|
||||
import org.leavesmc.leaves.bot.agent.actions.ServerRotationAction;
|
||||
import org.leavesmc.leaves.entity.bot.action.RotationAction;
|
||||
|
||||
public class CraftRotationAction extends CraftBotAction<RotationAction, ServerRotationAction> implements RotationAction {
|
||||
|
||||
public CraftRotationAction(ServerRotationAction serverAction) {
|
||||
super(serverAction, CraftRotationAction::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RotationAction setYaw(float yaw) {
|
||||
serverAction.setYaw(yaw);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RotationAction setPitch(float pitch) {
|
||||
serverAction.setPitch(pitch);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getYaw() {
|
||||
return serverAction.getYaw();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getPitch() {
|
||||
return serverAction.getPitch();
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
package org.leavesmc.leaves.entity.bot.actions;
|
||||
|
||||
import org.leavesmc.leaves.bot.agent.actions.ServerSneakAction;
|
||||
import org.leavesmc.leaves.entity.bot.action.SneakAction;
|
||||
|
||||
public class CraftSneakAction extends CraftBotAction<SneakAction, ServerSneakAction> implements SneakAction {
|
||||
|
||||
public CraftSneakAction(ServerSneakAction serverAction) {
|
||||
super(serverAction, CraftSneakAction::new);
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
package org.leavesmc.leaves.entity.bot.actions;
|
||||
|
||||
import org.leavesmc.leaves.bot.agent.actions.ServerSwapAction;
|
||||
import org.leavesmc.leaves.entity.bot.action.SwapAction;
|
||||
|
||||
public class CraftSwapAction extends CraftBotAction<SwapAction, ServerSwapAction> implements SwapAction {
|
||||
|
||||
public CraftSwapAction(ServerSwapAction serverAction) {
|
||||
super(serverAction, CraftSwapAction::new);
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
package org.leavesmc.leaves.entity.bot.actions;
|
||||
|
||||
import org.leavesmc.leaves.bot.agent.actions.ServerSwimAction;
|
||||
import org.leavesmc.leaves.entity.bot.action.SwimAction;
|
||||
|
||||
public class CraftSwimAction extends CraftBotAction<SwimAction, ServerSwimAction> implements SwimAction {
|
||||
|
||||
public CraftSwimAction(ServerSwimAction serverAction) {
|
||||
super(serverAction, CraftSwimAction::new);
|
||||
}
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
package org.leavesmc.leaves.entity.bot.actions;
|
||||
|
||||
import org.leavesmc.leaves.bot.agent.actions.ServerTimerBotAction;
|
||||
import org.leavesmc.leaves.entity.bot.action.TimerBotAction;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
public class CraftTimerBotAction<T extends TimerBotAction<T>, S extends ServerTimerBotAction<S>> extends CraftBotAction<T, S> implements TimerBotAction<T> {
|
||||
|
||||
public CraftTimerBotAction(S serverAction, Function<S, T> creator) {
|
||||
super(serverAction, creator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStartDelayTick(int delayTick) {
|
||||
serverAction.setStartDelayTick(delayTick);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getStartDelayTick() {
|
||||
return serverAction.getStartDelayTick();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDoIntervalTick(int intervalTick) {
|
||||
serverAction.setDoIntervalTick(intervalTick);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDoIntervalTick() {
|
||||
return serverAction.getDoIntervalTick();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDoNumber(int doNumber) {
|
||||
serverAction.setDoNumber(doNumber);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDoNumber() {
|
||||
return serverAction.getDoNumber();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTickToNext() {
|
||||
return serverAction.getTickToNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDoNumberRemaining() {
|
||||
return serverAction.getDoNumberRemaining();
|
||||
}
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
package org.leavesmc.leaves.entity.bot.actions;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.leavesmc.leaves.bot.ServerBot;
|
||||
import org.leavesmc.leaves.bot.agent.actions.ServerUseItemAction;
|
||||
import org.leavesmc.leaves.entity.bot.action.UseItemAction;
|
||||
|
||||
public class CraftUseItemAction extends CraftTimerBotAction<UseItemAction, ServerUseItemAction> implements UseItemAction {
|
||||
|
||||
public CraftUseItemAction(ServerUseItemAction serverAction) {
|
||||
super(serverAction, CraftUseItemAction::new);
|
||||
}
|
||||
|
||||
public boolean doTick(@NotNull ServerBot bot) {
|
||||
return serverAction.doTick(bot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getUseTickTimeout() {
|
||||
return serverAction.getUseTickTimeout();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CraftUseItemAction setUseTickTimeout(int timeout) {
|
||||
serverAction.setUseTickTimeout(timeout);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package org.leavesmc.leaves.entity.bot.actions;
|
||||
|
||||
import org.leavesmc.leaves.bot.agent.actions.ServerUseItemAutoAction;
|
||||
import org.leavesmc.leaves.entity.bot.action.UseItemAutoAction;
|
||||
|
||||
public class CraftUseItemAutoAction extends CraftTimerBotAction<UseItemAutoAction, ServerUseItemAutoAction> implements UseItemAutoAction {
|
||||
|
||||
public CraftUseItemAutoAction(ServerUseItemAutoAction serverAction) {
|
||||
super(serverAction, CraftUseItemAutoAction::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getUseTickTimeout() {
|
||||
return serverAction.getUseTickTimeout();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CraftUseItemAutoAction setUseTickTimeout(int timeout) {
|
||||
serverAction.setUseTickTimeout(timeout);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package org.leavesmc.leaves.entity.bot.actions;
|
||||
|
||||
import org.leavesmc.leaves.bot.agent.actions.ServerUseItemOffhandAction;
|
||||
import org.leavesmc.leaves.entity.bot.action.UseItemOffhandAction;
|
||||
|
||||
public class CraftUseItemOffhandAction extends CraftTimerBotAction<UseItemOffhandAction, ServerUseItemOffhandAction> implements UseItemOffhandAction {
|
||||
|
||||
public CraftUseItemOffhandAction(ServerUseItemOffhandAction serverAction) {
|
||||
super(serverAction, CraftUseItemOffhandAction::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getUseTickTimeout() {
|
||||
return serverAction.getUseTickTimeout();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CraftUseItemOffhandAction setUseTickTimeout(int timeout) {
|
||||
serverAction.setUseTickTimeout(timeout);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package org.leavesmc.leaves.entity.bot.actions;
|
||||
|
||||
import org.leavesmc.leaves.bot.agent.actions.ServerUseItemOnAction;
|
||||
import org.leavesmc.leaves.entity.bot.action.UseItemOnAction;
|
||||
|
||||
public class CraftUseItemOnAction extends CraftTimerBotAction<UseItemOnAction, ServerUseItemOnAction> implements UseItemOnAction {
|
||||
|
||||
public CraftUseItemOnAction(ServerUseItemOnAction serverAction) {
|
||||
super(serverAction, CraftUseItemOnAction::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getUseTickTimeout() {
|
||||
return serverAction.getUseTickTimeout();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CraftUseItemOnAction setUseTickTimeout(int timeout) {
|
||||
serverAction.setUseTickTimeout(timeout);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package org.leavesmc.leaves.entity.bot.actions;
|
||||
|
||||
import org.leavesmc.leaves.bot.agent.actions.ServerUseItemOnOffhandAction;
|
||||
import org.leavesmc.leaves.entity.bot.action.UseItemOnOffhandAction;
|
||||
|
||||
public class CraftUseItemOnOffhandAction extends CraftTimerBotAction<UseItemOnOffhandAction, ServerUseItemOnOffhandAction> implements UseItemOnOffhandAction {
|
||||
|
||||
public CraftUseItemOnOffhandAction(ServerUseItemOnOffhandAction serverAction) {
|
||||
super(serverAction, CraftUseItemOnOffhandAction::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getUseTickTimeout() {
|
||||
return serverAction.getUseTickTimeout();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CraftUseItemOnOffhandAction setUseTickTimeout(int timeout) {
|
||||
serverAction.setUseTickTimeout(timeout);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package org.leavesmc.leaves.entity.bot.actions;
|
||||
|
||||
import org.leavesmc.leaves.bot.agent.actions.ServerUseItemToAction;
|
||||
import org.leavesmc.leaves.entity.bot.action.UseItemToAction;
|
||||
|
||||
public class CraftUseItemToAction extends CraftTimerBotAction<UseItemToAction, ServerUseItemToAction> implements UseItemToAction {
|
||||
|
||||
public CraftUseItemToAction(ServerUseItemToAction serverAction) {
|
||||
super(serverAction, CraftUseItemToAction::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getUseTickTimeout() {
|
||||
return serverAction.getUseTickTimeout();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CraftUseItemToAction setUseTickTimeout(int timeout) {
|
||||
serverAction.setUseTickTimeout(timeout);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package org.leavesmc.leaves.entity.bot.actions;
|
||||
|
||||
import org.leavesmc.leaves.bot.agent.actions.ServerUseItemToOffhandAction;
|
||||
import org.leavesmc.leaves.entity.bot.action.UseItemToOffhandAction;
|
||||
|
||||
public class CraftUseItemToOffhandAction extends CraftTimerBotAction<UseItemToOffhandAction, ServerUseItemToOffhandAction> implements UseItemToOffhandAction {
|
||||
|
||||
public CraftUseItemToOffhandAction(ServerUseItemToOffhandAction serverAction) {
|
||||
super(serverAction, CraftUseItemToOffhandAction::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getUseTickTimeout() {
|
||||
return serverAction.getUseTickTimeout();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CraftUseItemToOffhandAction setUseTickTimeout(int timeout) {
|
||||
serverAction.setUseTickTimeout(timeout);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
package org.leavesmc.leaves.util;
|
||||
|
||||
import org.bukkit.util.NumberConversions;
|
||||
import org.bukkit.util.Vector;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class MathUtils {
|
||||
|
||||
private static final Pattern numericPattern = Pattern.compile("^-?[1-9]\\d*$|^0$");
|
||||
|
||||
public static boolean isNumeric(String str) {
|
||||
return numericPattern.matcher(str).matches();
|
||||
}
|
||||
|
||||
public static float @NotNull [] fetchYawPitch(@NotNull Vector dir) {
|
||||
double x = dir.getX();
|
||||
double z = dir.getZ();
|
||||
|
||||
float[] out = new float[2];
|
||||
|
||||
if (x == 0.0D && z == 0.0D) {
|
||||
out[1] = (float) (dir.getY() > 0.0D ? -90 : 90);
|
||||
} else {
|
||||
double theta = Math.atan2(-x, z);
|
||||
out[0] = (float) Math.toDegrees((theta + 6.283185307179586D) % 6.283185307179586D);
|
||||
|
||||
double x2 = NumberConversions.square(x);
|
||||
double z2 = NumberConversions.square(z);
|
||||
double xz = Math.sqrt(x2 + z2);
|
||||
out[1] = (float) Math.toDegrees(Math.atan(-dir.getY() / xz));
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
public static float fetchPitch(@NotNull Vector dir) {
|
||||
double x = dir.getX();
|
||||
double z = dir.getZ();
|
||||
|
||||
float result;
|
||||
|
||||
if (x == 0.0D && z == 0.0D) {
|
||||
result = (float) (dir.getY() > 0.0D ? -90 : 90);
|
||||
} else {
|
||||
double x2 = NumberConversions.square(x);
|
||||
double z2 = NumberConversions.square(z);
|
||||
double xz = Math.sqrt(x2 + z2);
|
||||
result = (float) Math.toDegrees(Math.atan(-dir.getY() / xz));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static Vector getDirection(double rotX, double rotY) {
|
||||
Vector vector = new Vector();
|
||||
|
||||
rotX = Math.toRadians(rotX);
|
||||
rotY = Math.toRadians(rotY);
|
||||
|
||||
double xz = Math.abs(Math.cos(rotY));
|
||||
|
||||
vector.setX(-Math.sin(rotX) * xz);
|
||||
vector.setZ(Math.cos(rotX) * xz);
|
||||
vector.setY(-Math.sin(rotY));
|
||||
|
||||
return vector;
|
||||
}
|
||||
|
||||
private static final int[] MULTIPLY_DE_BRUIJN_BIT_POSITION = new int[]{0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9};
|
||||
|
||||
public static int floorLog2(int value) {
|
||||
return ceilLog2(value) - (isPowerOfTwo(value) ? 0 : 1);
|
||||
}
|
||||
|
||||
public static int ceilLog2(int value) {
|
||||
value = isPowerOfTwo(value) ? value : smallestEncompassingPowerOfTwo(value);
|
||||
return MULTIPLY_DE_BRUIJN_BIT_POSITION[(int) ((long) value * 125613361L >> 27) & 31];
|
||||
}
|
||||
|
||||
public static boolean isPowerOfTwo(int value) {
|
||||
return value != 0 && (value & value - 1) == 0;
|
||||
}
|
||||
|
||||
public static int smallestEncompassingPowerOfTwo(int value) {
|
||||
int i = value - 1;
|
||||
i |= i >> 1;
|
||||
i |= i >> 2;
|
||||
i |= i >> 4;
|
||||
i |= i >> 8;
|
||||
i |= i >> 16;
|
||||
return i + 1;
|
||||
}
|
||||
}
|
||||
+127
@@ -0,0 +1,127 @@
|
||||
package org.leavesmc.leaves.util;
|
||||
|
||||
import com.mojang.logging.LogUtils;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.leavesmc.leaves.bot.ServerBot;
|
||||
import org.leavesmc.leaves.event.player.UpdateSuppressionEvent;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class UpdateSuppressionException extends RuntimeException {
|
||||
private @Nullable BlockPos pos;
|
||||
private @Nullable Level level;
|
||||
private @Nullable Block source;
|
||||
private @Nullable ServerPlayer player;
|
||||
private final @NotNull Throwable throwable;
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
|
||||
public UpdateSuppressionException(
|
||||
@Nullable BlockPos pos,
|
||||
@Nullable Level level,
|
||||
@Nullable Block source,
|
||||
@Nullable ServerPlayer player,
|
||||
@NotNull Throwable throwable
|
||||
) {
|
||||
super("Update Suppression");
|
||||
this.pos = pos;
|
||||
this.level = level;
|
||||
this.source = source;
|
||||
this.player = player;
|
||||
this.throwable = throwable;
|
||||
}
|
||||
|
||||
public void providePlayer(@NotNull ServerPlayer player) {
|
||||
if (this.level == null) {
|
||||
this.level = player.level();
|
||||
}
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
public void provideLevel(@NotNull Level level) {
|
||||
if (this.level != null) {
|
||||
this.level = level;
|
||||
}
|
||||
}
|
||||
|
||||
public void provideBlock(@NotNull Level level, @NotNull BlockPos pos, @NotNull Block source) {
|
||||
provideLevel(level);
|
||||
provideBlock(pos, source);
|
||||
}
|
||||
|
||||
public void provideBlock(@NotNull BlockPos pos, @NotNull Block source) {
|
||||
if (this.pos != null) {
|
||||
this.pos = pos;
|
||||
}
|
||||
if (this.source != null) {
|
||||
this.source = source;
|
||||
}
|
||||
}
|
||||
|
||||
public void consume() {
|
||||
submitEvent();
|
||||
LOGGER.info(getMessage());
|
||||
}
|
||||
|
||||
private void submitEvent() {
|
||||
Location location = null;
|
||||
if (pos != null && level != null) {
|
||||
location = new Location(level.getWorld(), pos.getX(), pos.getY(), pos.getZ());
|
||||
}
|
||||
Material material = null;
|
||||
if (source != null) {
|
||||
material = source.defaultBlockState().getBukkitMaterial();
|
||||
}
|
||||
Player bukkitPlayer = null;
|
||||
if (player != null) {
|
||||
bukkitPlayer = player.getBukkitEntity();
|
||||
}
|
||||
new UpdateSuppressionEvent(bukkitPlayer, location, material, throwable).callEvent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
List<String> messages = new ArrayList<>();
|
||||
messages.add("An %s update suppression was triggered".formatted(getTypeName()));
|
||||
if (source != null) {
|
||||
messages.add("from %s".formatted(source.defaultBlockState().getBukkitMaterial().name()));
|
||||
}
|
||||
if (pos != null) {
|
||||
messages.add("at [x:%d,y:%d,z:%d]".formatted(pos.getX(), pos.getY(), pos.getZ()));
|
||||
}
|
||||
if (level != null) {
|
||||
messages.add("in %s".formatted(level.dimension().location()));
|
||||
}
|
||||
if (player != null) {
|
||||
if (player instanceof ServerBot) {
|
||||
messages.add("by %s[bot]".formatted(player.displayName));
|
||||
} else {
|
||||
messages.add("by %s".formatted(player.displayName));
|
||||
}
|
||||
}
|
||||
return String.join(" ", messages);
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
private @NotNull String getTypeName() {
|
||||
Class<? extends Throwable> type = throwable.getClass();
|
||||
if (type == ClassCastException.class) {
|
||||
return "CCE";
|
||||
} else if (type == StackOverflowError.class) {
|
||||
return "SOE";
|
||||
} else if (type == IllegalArgumentException.class) {
|
||||
return "IAE";
|
||||
}
|
||||
return type.getSimpleName();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user