From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Bacteriawa Date: Sat, 30 Aug 2025 17:16:32 +0800 Subject: [PATCH] Leaves: Fakeplayer Co-authored by: violetc <58360096+s-yh-china@users.noreply.github.com> As a part of : Leaves (https://github.com/LeavesMC/Leaves) Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html) diff --git a/net/minecraft/advancements/triggers/SimpleCriterionTrigger.java b/net/minecraft/advancements/triggers/SimpleCriterionTrigger.java index d76a338ab4f326e6105adc451eee3d4601403c98..b827d51d23ceaebef2191a9f349bbd346e73f1af 100644 --- a/net/minecraft/advancements/triggers/SimpleCriterionTrigger.java +++ b/net/minecraft/advancements/triggers/SimpleCriterionTrigger.java @@ -17,6 +17,7 @@ import net.minecraft.world.level.storage.loot.ValidationContextSource; public abstract class SimpleCriterionTrigger implements CriterionTrigger { protected void trigger(final ServerPlayer player, final Predicate matcher) { + if (player instanceof org.leavesmc.leaves.bot.ServerBot) return; // Leaves - bot skip PlayerAdvancements advancements = player.getAdvancements(); Map listenersForType = advancements.getTriggerMapForType(this); if (listenersForType != null && !listenersForType.isEmpty()) { diff --git a/net/minecraft/network/Connection.java b/net/minecraft/network/Connection.java index d2e9d46bbd9ee4ce45419b11499d7a7bc50f0219..848967dbe41694411603ef369cfdc3a0b00240d3 100644 --- a/net/minecraft/network/Connection.java +++ b/net/minecraft/network/Connection.java @@ -74,7 +74,7 @@ public class Connection extends SimpleChannelInboundHandler> { public boolean preparing = true; // Spigot end private volatile @Nullable PacketListener disconnectListener; - private volatile @Nullable PacketListener packetListener; + protected volatile @Nullable PacketListener packetListener; // Leaves - private -> protected private @Nullable DisconnectionDetails disconnectionDetails; private final java.util.concurrent.atomic.AtomicBoolean disconnectionHandled = new java.util.concurrent.atomic.AtomicBoolean(false); // Folia - region threading - may be called concurrently during configuration stage private int receivedPackets; diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java index 1f13f7b016b4256e92989d83003a983d6ba1c750..f76e40f8bbea076ad620f7525d4d8666603a5175 100644 --- a/net/minecraft/server/MinecraftServer.java +++ b/net/minecraft/server/MinecraftServer.java @@ -296,6 +296,8 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop S spin(final Function factory) { ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry.init(); // Paper - rewrite data converter system AtomicReference serverReference = new AtomicReference<>(); @@ -1195,6 +1197,16 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop 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; @@ -1806,6 +1818,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop 0) { this.playerList.saveAll(playerSaveInterval); + org.leavesmc.leaves.bot.BotList.INSTANCE.saveAllResumeBots(playerSaveInterval); // Leaves - resident fakeplayer } if (region == null && fullSave) { // Folia - region threading - don't auto save level data this.saveGlobalData(false); @@ -2044,6 +2057,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop players, final @Nullable Component reason) throws CommandSyntaxException { UserBanList list = source.getServer().getPlayerList().getBans(); int count = 0; - + boolean hasBot = false; // Leaves - disable kick for (NameAndId player : players) { + // Leaves start - disable ban + if (player.isBot()) { + source.sendFailure(Component.literal("Permission denied")); + hasBot = true; + continue; + } + // Leaves end - disable ban if (!list.isBanned(player)) { UserBanListEntry entry = new UserBanListEntry(player, null, source.getTextName(), null, reason == null ? null : reason.getString()); list.add(entry); @@ -53,7 +60,13 @@ public class BanPlayerCommands { } if (count == 0) { - throw ERROR_ALREADY_BANNED.create(); + // Leaves start - disable kick + if (hasBot) { + return count; + } else { + throw ERROR_ALREADY_BANNED.create(); + } + // Leaves end - disable kick } else { return count; } diff --git a/net/minecraft/server/commands/KickCommand.java b/net/minecraft/server/commands/KickCommand.java index bf361ef4cbf2a55b8bba5a75111257fb2d6d471f..82629ecca8b0e216db4ccb6f4c38de53ac1a6419 100644 --- a/net/minecraft/server/commands/KickCommand.java +++ b/net/minecraft/server/commands/KickCommand.java @@ -39,9 +39,17 @@ public class KickCommand { throw ERROR_SINGLEPLAYER.create(); } - int count = 0; + boolean hasBot = false; // Leaves - disable kick + int count = 0; for (ServerPlayer player : players) { + // Leaves start - disable kick + if (player instanceof org.leavesmc.leaves.bot.ServerBot) { + source.sendFailure(Component.literal("Permission denied")); + hasBot = true; + continue; + } + // Leaves end - disable kick if (!source.getServer().isSingleplayerOwner(player.nameAndId())) { player.connection.disconnect(reason, org.bukkit.event.player.PlayerKickEvent.Cause.KICKED); // Paper - kick event cause source.sendSuccess(() -> Component.translatable("commands.kick.success", player.getDisplayName(), reason), true); @@ -50,7 +58,13 @@ public class KickCommand { } if (count == 0) { - throw ERROR_KICKING_OWNER.create(); + // Leaves start - disable kick + if (hasBot) { + return count; + } else { + throw ERROR_KICKING_OWNER.create(); + } + // Leaves end - disable kick } else { return count; } diff --git a/net/minecraft/server/commands/OpCommand.java b/net/minecraft/server/commands/OpCommand.java index 636f56bfc0e9075b257e2eb14e858975bdedd801..20cbc0e65fea3fc2d0033259c2f35e158ea81926 100644 --- a/net/minecraft/server/commands/OpCommand.java +++ b/net/minecraft/server/commands/OpCommand.java @@ -39,6 +39,7 @@ public class OpCommand { int count = 0; for (NameAndId player : players) { + if (player.isBot()) continue; // Leaves - disable op if (!list.isOp(player)) { list.op(player); count++; diff --git a/net/minecraft/server/commands/StopCommand.java b/net/minecraft/server/commands/StopCommand.java index 3021b70b941ed09c5c58b98d30830a2279b8339c..d289f59475b617ae527d123239297f695468b80c 100644 --- a/net/minecraft/server/commands/StopCommand.java +++ b/net/minecraft/server/commands/StopCommand.java @@ -12,6 +12,10 @@ public class StopCommand { c.getSource().sendSuccess(() -> Component.translatable("commands.stop.stopping"), true); c.getSource().getServer().halt(false); return Command.SINGLE_SUCCESS; - })); + }).then(Commands.literal("force").requires(Commands.hasPermission(Commands.LEVEL_OWNERS)).executes(c -> { + c.getSource().sendSuccess(() -> Component.translatable("commands.stop.stopping"), true); + c.getSource().getServer().halt(false, false, true); + return 1; + }))); } } diff --git a/net/minecraft/server/dedicated/DedicatedServer.java b/net/minecraft/server/dedicated/DedicatedServer.java index 2dae0f4249eade60ac55c9fa289dd146dc3675e1..239cad02f85f0d14d4abfa746ecdc4dd82b04657 100644 --- a/net/minecraft/server/dedicated/DedicatedServer.java +++ b/net/minecraft/server/dedicated/DedicatedServer.java @@ -230,6 +230,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(); @@ -251,6 +252,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface consoleThread.start(); // Paper - Enhance console tab completions for brigadier commands; start console thread after MinecraftServer.console & PaperConfig are initialized io.papermc.paper.command.PaperCommands.registerCommands(this); // Paper - setup /paper command if (false) this.server.spark.registerCommandBeforePlugins(this.server); // Paper - spark // Luminol - Force disable builtin spark + this.getBotList().loadResumeBotInfo(); // Leaves - load resident bot info com.destroystokyo.paper.Metrics.PaperMetrics.startMetrics(); // Paper - start metrics com.destroystokyo.paper.VersionHistoryManager.INSTANCE.getClass(); // Paper - load version history now diff --git a/net/minecraft/server/level/ChunkMap.java b/net/minecraft/server/level/ChunkMap.java index 5196431f52e81608a5575f8a262cd5c855b06182..6d43ca34f38192faf5bb4254d849c5ac51197f48 100644 --- a/net/minecraft/server/level/ChunkMap.java +++ b/net/minecraft/server/level/ChunkMap.java @@ -972,7 +972,7 @@ public class ChunkMap extends SimpleRegionStorage implements ChunkHolder.PlayerP return this.level.moonrise$getEntityLookup().hasEntity(id); // Folia - region threading } - protected void addEntity(final Entity entity) { + public void addEntity(final Entity entity) { org.spigotmc.AsyncCatcher.catchOp("entity track"); // Spigot // Paper start - ignore and warn about illegal addEntity calls instead of crashing server if (!entity.valid || entity.level() != this.level || entity.moonrise$getTrackedEntity() != null) { // Folia - region threading @@ -1389,6 +1389,13 @@ public class ChunkMap extends SimpleRegionStorage implements ChunkHolder.PlayerP } } else { this.removePlayer(player); + // Leaves start - render bot + if (this.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 cb11830c2f399c9706b20f8104686dc0d9b686f9..1c4c67410849f844946e4883585910fc8fe97048 100644 --- a/net/minecraft/server/level/ServerLevel.java +++ b/net/minecraft/server/level/ServerLevel.java @@ -243,6 +243,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet private static final org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry DATA_TYPE_REGISTRY = new org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry(); public final org.bukkit.craftbukkit.persistence.CraftPersistentDataContainer persistentDataContainer = new org.bukkit.craftbukkit.persistence.CraftPersistentDataContainer(DATA_TYPE_REGISTRY); //private final alternate.current.wire.WireHandler wireHandler = new alternate.current.wire.WireHandler(this); // Paper - optimize redstone (Alternate Current) // Folia - region threading - move to regionised world data + final List realPlayers; // Leaves - skip @Override public @Nullable LevelChunk getChunkIfLoaded(int x, int z) { @@ -710,6 +711,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet this.chunkDataController = new ca.spottedleaf.moonrise.patches.chunk_system.io.datacontroller.ChunkDataController((ServerLevel)(Object)this, this.chunkTaskScheduler); // Paper end - rewrite chunk system this.getCraftServer().addWorld(this.getWorld()); // CraftBukkit + this.realPlayers = Lists.newArrayList(); // Leaves - skip this.updateTickData(); // Folia - region threading - make sure it is initialised before ticked } @@ -2596,6 +2598,12 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet return this.players; } + // Leaves start - fakeplayer skip + public List realPlayers() { + return this.realPlayers; + } + // Leaves end - fakeplayer skip + @Override public void updatePOIOnBlockStateChange(final BlockPos pos, final BlockState oldState, final BlockState newState) { Optional> oldType = PoiTypes.forState(oldState); @@ -3069,6 +3077,11 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet // 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 player) { ServerLevel.this.players.add(player); + // Leaves start - skip + if (!(player instanceof org.leavesmc.leaves.bot.ServerBot)) { + ServerLevel.this.realPlayers.add(player); + } + // Leaves end - skip if (player.isReceivingWaypoints()) { ServerLevel.this.getWaypointManager().addPlayer(player); } @@ -3150,6 +3163,11 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet ServerLevel.this.getChunkSource().removeEntity(entity); if (entity instanceof ServerPlayer player) { ServerLevel.this.players.remove(player); + // Leaves start - skip + if (!(player instanceof org.leavesmc.leaves.bot.ServerBot)) { + ServerLevel.this.realPlayers.remove(player); + } + // Leaves end - skip ServerLevel.this.getWaypointManager().removePlayer(player); ServerLevel.this.updateSleepingPlayerList(); } diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java index e64db3045ebf08600283a47960005827c1920f94..a3d3a602ab86e27ae0c6e6207384dc0043279d47 100644 --- a/net/minecraft/server/level/ServerPlayer.java +++ b/net/minecraft/server/level/ServerPlayer.java @@ -232,7 +232,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; @@ -251,9 +251,9 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc private boolean canChatColor = true; private long lastActionTime = Util.getMillis(); private @Nullable Entity camera; - private boolean isChangingDimension; + protected boolean isChangingDimension; public boolean seenCredits = false; - private final ServerRecipeBook recipeBook; + protected ServerRecipeBook recipeBook; // Leaves - not final and private -> protected private @Nullable Vec3 levitationStartPos; private int levitationStartTime; private boolean disconnected; @@ -1489,7 +1489,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc } // Lophine End - Cross Region Damage trace - private void tellNeutralMobsThatIDied() { + protected void tellNeutralMobsThatIDied() { // Leaves private -> protected AABB aabb = new AABB(this.blockPosition()).inflate(32.0, 10.0, 32.0); this.level() .getEntitiesOfClass(Mob.class, aabb, EntitySelector.NO_SPECTATORS) @@ -2140,6 +2140,11 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc this.lastSentHealth = -1.0F; this.lastSentFood = -1; this.teleportSpectators(transition, oldLevel); + // Leaves start - bot support + if (fun.bm.lophine.config.modules.function.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(), oldLevel.getWorld()); this.level().getCraftServer().getPluginManager().callEvent(changeEvent); diff --git a/net/minecraft/server/players/CachedUserNameToIdResolver.java b/net/minecraft/server/players/CachedUserNameToIdResolver.java index b7553611723f7120c66e1f3e51d89df28bd2756b..7109a22a9911d452242207ab0286ee985dafd439 100644 --- a/net/minecraft/server/players/CachedUserNameToIdResolver.java +++ b/net/minecraft/server/players/CachedUserNameToIdResolver.java @@ -122,6 +122,12 @@ public class CachedUserNameToIdResolver implements UserNameToIdResolver { @Override public Optional get(final 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.nameAndId()); + } + // Leaves end - fix bot String userName = name.toLowerCase(Locale.ROOT); boolean stateLocked = true; try { this.stateLock.lock(); // Paper - Fix GameProfileCache concurrency CachedUserNameToIdResolver.GameProfileInfo profileInfo = this.profilesByName.get(userName); diff --git a/net/minecraft/server/players/NameAndId.java b/net/minecraft/server/players/NameAndId.java index 12190f2a2856b0d0cf95144d0b1622f1b9600bf6..be01f06d4ee02854345842c97382e8b6401ab948 100644 --- a/net/minecraft/server/players/NameAndId.java +++ b/net/minecraft/server/players/NameAndId.java @@ -8,18 +8,22 @@ import java.util.UUID; import net.minecraft.core.UUIDUtil; import org.jspecify.annotations.Nullable; -public record NameAndId(UUID id, String name) { +public record NameAndId(UUID id, String name, boolean isBot) { // Leaves - fakeplayer public static final Codec CODEC = RecordCodecBuilder.create( i -> i.group(UUIDUtil.STRING_CODEC.fieldOf("id").forGetter(NameAndId::id), Codec.STRING.fieldOf("name").forGetter(NameAndId::name)) .apply(i, NameAndId::new) ); public NameAndId(final GameProfile profile) { - this(profile.id(), profile.name()); + this(profile.id(), profile.name(), com.google.common.collect.Iterables.getFirst(profile.properties().get("is_bot"), "false").equals("true")); // Leaves - fakeplayer } public NameAndId(final com.mojang.authlib.yggdrasil.response.NameAndId profile) { - this(profile.id(), profile.name()); + this(profile.id(), profile.name(), false); // Leaves - fakeplayer + } + + public NameAndId(UUID uuid, String name) { + this(uuid, name, false); // Leaves - fakeplayer } public static @Nullable NameAndId fromJson(final JsonObject object) { @@ -33,7 +37,7 @@ public record NameAndId(UUID id, String name) { return null; } - return new NameAndId(uuid, object.get("name").getAsString()); + return new NameAndId(uuid, object.get("name").getAsString(), false); // Leaves - fakeplayer } else { return null; } @@ -46,7 +50,7 @@ public record NameAndId(UUID id, String name) { public static NameAndId createOffline(final String name) { UUID id = UUIDUtil.createOfflinePlayerUUID(name); - return new NameAndId(id, name); + return new NameAndId(id, name, false); // Leaves - fakeplayer } // Paper start - utility method for common conversion back to the game profile diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java index ca1e635b0fdf3d0d17447b43a558fe030536d297..ed50484ca24ef24b1e5bef9f6369dfa4b30c359c 100644 --- a/net/minecraft/server/players/PlayerList.java +++ b/net/minecraft/server/players/PlayerList.java @@ -320,6 +320,19 @@ public abstract class PlayerList { org.leavesmc.leaves.protocol.core.LeavesProtocolManager.handlePlayerJoin(player); // Leaves - protocol + // Leaves start - bot support + if (fun.bm.lophine.config.modules.function.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, 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 @@ -791,6 +804,11 @@ public abstract class PlayerList { respawnReason ).callEvent(); // Paper end + // Leaves start - bot support + if (fun.bm.lophine.config.modules.function.FakeplayerConfig.enable) { + this.server.getBotList().bots.forEach(bot -> bot.sendFakeDataIfNeed(player, true)); // Leaves - render bot + } + // Leaves end - bot support return player; } @@ -893,11 +911,16 @@ public abstract class PlayerList { public String[] getPlayerNamesArray() { List players = new java.util.ArrayList<>(this.players); // Folia - region threading - String[] names = new String[players.size()]; // Folia - region threading + String[] names = new String[players.size() + this.server.getBotList().bots.size()]; // Leaves - fakeplayer support for (int i = 0; i < players.size(); i++) { // Folia - region threading names[i] = players.get(i).getGameProfile().name(); // Folia - region threading } + // Leaves start - fakeplayer support + for (int i = this.players.size(); i < names.length; ++i) { + names[i] = this.server.getBotList().bots.get(i - this.players.size()).gameProfile.name(); + } + // Leaves end - fakeplayer support return names; } @@ -1018,8 +1041,15 @@ public abstract class PlayerList { } } - public @Nullable ServerPlayer getPlayerByName(final String name) { - return this.playersByName.get(name.toLowerCase(java.util.Locale.ROOT)); // Spigot + public @Nullable ServerPlayer getPlayerByName(String name) { + // Leaves start - fakeplayer support + name = name.toLowerCase(java.util.Locale.ROOT); + ServerPlayer player = this.playersByName.get(name); + if (player == null) { + player = this.server.getBotList().getBotByName(name); + } + return player; // Spigot + // Leaves end - fakeplayer support } public void broadcast( @@ -1366,7 +1396,13 @@ public abstract class PlayerList { } public @Nullable ServerPlayer getPlayer(final UUID uuid) { - return this.playersByUUID.get(uuid); + // Leaves start - fakeplayer support + ServerPlayer player = this.playersByUUID.get(uuid); + if (player == null) { + player = this.server.getBotList().getBot(uuid); + } + return player; + // Leaves start - fakeplayer support } public @Nullable ServerPlayer getPlayer(final String playerName) { diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java index cddf44f5da5cba5603dcff4913b4658dcfcc48db..9eaed81a330a22e7fd6585cd286ec61371e97272 100644 --- a/net/minecraft/world/entity/Entity.java +++ b/net/minecraft/world/entity/Entity.java @@ -1263,7 +1263,7 @@ public abstract class Entity BlockPos effectPos = this.getOnPosLegacy(); BlockState effectState = this.level().getBlockState(effectPos); - if (this.isLocalInstanceAuthoritative()) { + if (this.isLocalInstanceAuthoritative() || this instanceof org.leavesmc.leaves.bot.ServerBot) { // Leaves - ServerBot needs check fall damage this.checkFallDamage(movement.y, this.onGround(), effectState, effectPos); } @@ -1664,7 +1664,7 @@ public abstract class Entity return colliders.isEmpty() ? maxDistance : -Shapes.collide(Direction.Axis.Y, aabb, colliders, -maxDistance); } - private Vec3 collide(final Vec3 movement) { + public Vec3 collide(final Vec3 movement) { // Leaves - private -> public // Paper start - optimise collisions final boolean xZero = movement.x == 0.0; final boolean yZero = movement.y == 0.0; diff --git a/net/minecraft/world/entity/LivingEntity.java b/net/minecraft/world/entity/LivingEntity.java index cb1589e3aa663ca058fb5c3a24da700d4482cf35..d64c68651b7142869a06ce0200fb625cd45ae235 100644 --- a/net/minecraft/world/entity/LivingEntity.java +++ b/net/minecraft/world/entity/LivingEntity.java @@ -3348,7 +3348,7 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin private void travelRidden(final Player controller, final Vec3 selfInput) { Vec3 riddenInput = this.getRiddenInput(controller, selfInput); this.tickRidden(controller, riddenInput); - if (this.canSimulateMovement()) { + if (this.canSimulateMovement() || this.getControllingPassenger() instanceof org.leavesmc.leaves.bot.ServerBot) { // Leaves - Fakeplayer this.setSpeed(this.getRiddenSpeed(controller)); this.travel(riddenInput); } else { @@ -4280,7 +4280,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() && !useItem.useOnRelease()) { + if ((--this.useItemRemaining == 0 || shouldLagCompensate) && !(this instanceof org.leavesmc.leaves.bot.ServerBot) && !this.level().isClientSide() && !useItem.useOnRelease()) { this.useItemRemaining = 0; // Paper end - lag compensate eating this.completeUsingItem(); @@ -4469,6 +4469,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 wasUsingItem = this.isUsingItem(); diff --git a/net/minecraft/world/entity/player/Player.java b/net/minecraft/world/entity/player/Player.java index c6fa9dff800b1491b1e0fc5d7e1dc16b99b8c7db..f063e9b7601480d0e66da22ea8e5bec2e8619fee 100644 --- a/net/minecraft/world/entity/player/Player.java +++ b/net/minecraft/world/entity/player/Player.java @@ -156,7 +156,7 @@ public abstract class Player extends Avatar implements ContainerUser { protected FoodData foodData = new FoodData(); protected int jumpTriggerTime; public int takeXpDelay; - private int sleepCounter = 0; + protected int sleepCounter = 0; protected boolean wasUnderwater; private final Abilities abilities = new Abilities(); public int experienceLevel = 0; @@ -167,7 +167,7 @@ public abstract class Player extends Avatar implements ContainerUser { private int lastLevelUpTime; public GameProfile gameProfile; private boolean reducedDebugInfo; - private ItemStack lastItemInMainHand = ItemStack.EMPTY; + protected ItemStack lastItemInMainHand = ItemStack.EMPTY; // Leaves - fakeplayer private final ItemCooldowns cooldowns = this.createItemCooldowns(); private Optional lastDeathLocation = Optional.empty(); public me.earthme.luminol.functions.bars.TickableStatusBarList statusBarList = new me.earthme.luminol.functions.bars.TickableStatusBarList(this); // Luminol status bars @@ -345,6 +345,12 @@ public abstract class Player extends Avatar implements ContainerUser { this.updatePlayerPose(); } + // Leaves start - fakeplayer + protected void livingEntityTick() { + super.tick(); + } + // Leaves end - fakeplayer + @Override protected float getMaxHeadRotationRelativeToBody() { return this.isBlocking() ? 15.0F : super.getMaxHeadRotationRelativeToBody(); @@ -570,7 +576,7 @@ public abstract class Player extends Avatar implements ContainerUser { public void removeEntitiesOnShoulder() { } - private void touch(final Entity entity) { + public void touch(final Entity entity) { // Leaves - private -> public entity.playerTouch(this); } @@ -1270,7 +1276,7 @@ public abstract class Player extends Avatar implements ContainerUser { // Paper end - Configurable sprint interruption on attack } - if (entity instanceof ServerPlayer serverPlayer && entity.hurtMarked) { + if ((entity instanceof ServerPlayer serverPlayer && !(entity instanceof org.leavesmc.leaves.bot.ServerBot)) && entity.hurtMarked) { // Leaves - bot knockback // CraftBukkit start - Add Velocity Event boolean cancelled = false; org.bukkit.entity.Player player = (org.bukkit.entity.Player) entity.getBukkitEntity(); diff --git a/net/minecraft/world/entity/projectile/FishingHook.java b/net/minecraft/world/entity/projectile/FishingHook.java index d7217fd075c6d6fae3622c56e940e247008dcc27..0958e7f93494766e97e87ed4d39c8e4550a641f6 100644 --- a/net/minecraft/world/entity/projectile/FishingHook.java +++ b/net/minecraft/world/entity/projectile/FishingHook.java @@ -59,7 +59,7 @@ public class FishingHook extends Projectile { private static final EntityDataAccessor DATA_HOOKED_ENTITY = SynchedEntityData.defineId(FishingHook.class, EntityDataSerializers.INT); private static final EntityDataAccessor 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/boat/AbstractBoat.java b/net/minecraft/world/entity/vehicle/boat/AbstractBoat.java index 9b887b40236a212de513e3ceece1abfec5372ae2..2f8f8fa1bef9f333978b47c2ce729c291bec66fb 100644 --- a/net/minecraft/world/entity/vehicle/boat/AbstractBoat.java +++ b/net/minecraft/world/entity/vehicle/boat/AbstractBoat.java @@ -270,6 +270,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); } @@ -380,6 +385,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 + protected @Nullable SoundEvent getPaddleSound() { return switch (this.getStatus()) { case IN_WATER, UNDER_WATER, UNDER_FLOWING_WATER -> SoundEvents.BOAT_PADDLE_WATER; diff --git a/net/minecraft/world/inventory/AbstractContainerMenu.java b/net/minecraft/world/inventory/AbstractContainerMenu.java index f6d37cba8ab5688501be9170720a0b6ded810819..fdd03c9d73a8a0527d0ead3fbbba20083c617e2c 100644 --- a/net/minecraft/world/inventory/AbstractContainerMenu.java +++ b/net/minecraft/world/inventory/AbstractContainerMenu.java @@ -406,6 +406,7 @@ public abstract class AbstractContainerMenu { private void doClick(final int slotIndex, final int buttonNum, final ContainerInput containerInput, final Player player) { Inventory inventory = player.getInventory(); + if (!doClickCheck(slotIndex, buttonNum, containerInput, player)) return; // Leaves - doClick check if (containerInput == ContainerInput.QUICK_CRAFT) { int expectedStatus = this.quickcraftStatus; this.quickcraftStatus = getQuickcraftHeader(buttonNum); @@ -680,6 +681,22 @@ public abstract class AbstractContainerMenu { } } + // Leaves start - doClick check + private boolean doClickCheck(int slotIndex, int buttonNum, ContainerInput containerInput, 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( final Player player, final ClickAction clickAction, final Slot slot, final ItemStack clicked, final ItemStack carried ) { diff --git a/net/minecraft/world/item/ItemStack.java b/net/minecraft/world/item/ItemStack.java index f15ee24339b04e4fe395755bf3503c17886f1782..2ba711dc758d0d8530b0fb6f0939ed88ec5df169 100644 --- a/net/minecraft/world/item/ItemStack.java +++ b/net/minecraft/world/item/ItemStack.java @@ -458,7 +458,7 @@ public final class ItemStack implements DataComponentHolder, ItemInstance, Chang placeEvent = org.bukkit.craftbukkit.event.CraftEventFactory.callBlockPlaceEvent(level, player, hand, capturedBlockStates.getFirst(), pos); } - 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 result = InteractionResult.FAIL; // cancel placement // PAIL: Remove this when MC-99075 fixed player.containerMenu.forceHeldSlot(hand); @@ -962,6 +962,20 @@ public final class ItemStack implements DataComponentHolder, ItemInstance, Chang } } + // 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 8d761640af4ce3afb4de5b46d3627614fff0ece9..15605dacaec3860e476a70dbdd3743e4197f67a6 100644 --- a/net/minecraft/world/level/block/piston/PistonMovingBlockEntity.java +++ b/net/minecraft/world/level/block/piston/PistonMovingBlockEntity.java @@ -152,7 +152,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 dx = deltaMovement.x; double dy = deltaMovement.y; diff --git a/net/minecraft/world/level/levelgen/PhantomSpawner.java b/net/minecraft/world/level/levelgen/PhantomSpawner.java index db0324595d7f5b6f92bd7724daf181b0903e1da4..ec3a437bd8a7d0e95e1e5fa0ff4462a70391ecf9 100644 --- a/net/minecraft/world/level/levelgen/PhantomSpawner.java +++ b/net/minecraft/world/level/levelgen/PhantomSpawner.java @@ -49,6 +49,10 @@ public class PhantomSpawner implements CustomSpawner { ServerStatsCounter stats = player.getStats(); int value = Mth.clamp(stats.getValue(Stats.CUSTOM.get(Stats.TIME_SINCE_REST)), 1, Integer.MAX_VALUE); int dayLength = 24000; + if (player instanceof org.leavesmc.leaves.bot.ServerBot bot && bot.getConfigValue(org.leavesmc.leaves.bot.agent.Configs.SPAWN_PHANTOM)) { + dayLength = Math.max(bot.notSleepTicks, 1); + } + // Leaves end - fakeplayer spawn if (random.nextInt(value) >= level.paperConfig().entities.behavior.playerInsomniaStartTicks) { // Paper - Ability to control player's insomnia and phantoms BlockPos spawnPos = playerPos.above(20 + random.nextInt(15)) .east(-10 + random.nextInt(21))