diff --git a/purpur-server/minecraft-patches/features/0003-Barrels-and-enderchests-6-rows.patch b/purpur-server/minecraft-patches/features/0003-Barrels-and-enderchests-6-rows.patch index 9af3fd2cc..d2bb62595 100644 --- a/purpur-server/minecraft-patches/features/0003-Barrels-and-enderchests-6-rows.patch +++ b/purpur-server/minecraft-patches/features/0003-Barrels-and-enderchests-6-rows.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Barrels and enderchests 6 rows diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java -index 23181b6192d3d69f263bf57bfd0d2fac000ce15b..f2fe9a8369b923edf6abe8af01d8a34e995e0b9b 100644 +index ac70293264dfcb9db0a0ca29f545be8e87cfa1b2..374c9007cc371d0dd0602dd5e8b5fdbbbcc925eb 100644 --- a/net/minecraft/server/players/PlayerList.java +++ b/net/minecraft/server/players/PlayerList.java -@@ -912,6 +912,27 @@ public abstract class PlayerList { +@@ -929,6 +929,27 @@ public abstract class PlayerList { player.getBukkitEntity().recalculatePermissions(); // CraftBukkit this.server.getCommands().sendCommands(player); } // Paper - Add sendOpLevel API diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/commands/Commands.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/commands/Commands.java.patch new file mode 100644 index 000000000..cd2d954a9 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/commands/Commands.java.patch @@ -0,0 +1,48 @@ +--- a/net/minecraft/commands/Commands.java ++++ b/net/minecraft/commands/Commands.java +@@ -255,11 +_,11 @@ + JfrCommand.register(this.dispatcher); + } + +- if (SharedConstants.DEBUG_CHASE_COMMAND) { ++ if (org.purpurmc.purpur.PurpurConfig.registerMinecraftDisabledCommands || SharedConstants.DEBUG_CHASE_COMMAND) { // Purpur - register disabled minecraft commands + ChaseCommand.register(this.dispatcher); + } + +- if (SharedConstants.DEBUG_DEV_COMMANDS || SharedConstants.IS_RUNNING_IN_IDE) { ++ if (org.purpurmc.purpur.PurpurConfig.registerMinecraftDebugCommands || SharedConstants.DEBUG_DEV_COMMANDS || SharedConstants.IS_RUNNING_IN_IDE) { // Purpur - register minecraft debug commands + RaidCommand.register(this.dispatcher, context); + DebugPathCommand.register(this.dispatcher); + DebugMobSpawningCommand.register(this.dispatcher); +@@ -287,6 +_,14 @@ + StopCommand.register(this.dispatcher); + TransferCommand.register(this.dispatcher); + WhitelistCommand.register(this.dispatcher); ++ org.purpurmc.purpur.command.CreditsCommand.register(this.dispatcher); // Purpur - Add credits command ++ org.purpurmc.purpur.command.DemoCommand.register(this.dispatcher); // Purpur - Add demo command ++ org.purpurmc.purpur.command.PingCommand.register(this.dispatcher); // Purpur - Add ping command ++ org.purpurmc.purpur.command.UptimeCommand.register(this.dispatcher); // Purpur - Add uptime command ++ org.purpurmc.purpur.command.TPSBarCommand.register(this.dispatcher); // Purpur - Implement TPSBar ++ org.purpurmc.purpur.command.CompassCommand.register(this.dispatcher); // Purpur - Add compass command ++ org.purpurmc.purpur.command.RamBarCommand.register(this.dispatcher); // Purpur - Add rambar command ++ org.purpurmc.purpur.command.RamCommand.register(this.dispatcher); // Purpur - Add ram command + } + + if (selection.includeIntegrated) { +@@ -511,6 +_,7 @@ + private void runSync(ServerPlayer player, java.util.Collection bukkit, RootCommandNode rootCommandNode) { + // Paper end - Perf: Async command map building + new com.destroystokyo.paper.event.brigadier.AsyncPlayerSendCommandsEvent(player.getBukkitEntity(), (RootCommandNode) rootCommandNode, true).callEvent(); // Paper - Brigadier API ++ if (org.bukkit.event.player.PlayerCommandSendEvent.getHandlerList().getRegisteredListeners().length > 0) { // Purpur - Skip events if there's no listeners + org.bukkit.event.player.PlayerCommandSendEvent event = new org.bukkit.event.player.PlayerCommandSendEvent(player.getBukkitEntity(), new java.util.LinkedHashSet<>(bukkit)); + event.getPlayer().getServer().getPluginManager().callEvent(event); + +@@ -521,6 +_,8 @@ + } + } + // CraftBukkit end ++ } // Purpur - Skip events if there's no listeners ++ + player.connection.send(new ClientboundCommandsPacket(rootCommandNode, COMMAND_NODE_INSPECTOR)); + } + diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/server/dedicated/DedicatedServer.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/server/dedicated/DedicatedServer.java.patch new file mode 100644 index 000000000..96432590f --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/server/dedicated/DedicatedServer.java.patch @@ -0,0 +1,67 @@ +--- a/net/minecraft/server/dedicated/DedicatedServer.java ++++ b/net/minecraft/server/dedicated/DedicatedServer.java +@@ -198,6 +_,7 @@ + public void run() { + if (!org.bukkit.craftbukkit.Main.useConsole) return; // CraftBukkit + // Paper start - Use TerminalConsoleAppender ++ if (DedicatedServer.this.gui == null || System.console() != null) // Purpur - GUI Improvements - has no GUI or has console (did not double-click) + new com.destroystokyo.paper.console.PaperConsole(DedicatedServer.this).start(); + /* + BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in, StandardCharsets.UTF_8)); +@@ -276,6 +_,15 @@ + io.papermc.paper.command.PaperCommands.registerCommands(this); // Paper - setup /paper command + this.server.spark.registerCommandBeforePlugins(this.server); // Paper - spark + com.destroystokyo.paper.Metrics.PaperMetrics.startMetrics(); // Paper - start metrics ++ // Purpur start - Purpur config files ++ try { ++ org.purpurmc.purpur.PurpurConfig.init((java.io.File) options.valueOf("purpur-settings")); ++ } catch (Exception e) { ++ DedicatedServer.LOGGER.error("Unable to load server configuration", e); ++ return false; ++ } ++ org.purpurmc.purpur.PurpurConfig.registerCommands(); ++ // Purpur end - Purpur config files + com.destroystokyo.paper.VersionHistoryManager.INSTANCE.getClass(); // Paper - load version history now + + // this.worldData.setGameType(properties.gameMode.get()); // CraftBukkit - moved to world loading +@@ -318,6 +_,30 @@ + if (true) throw new IllegalStateException("Failed to bind to port", var11); // Paper - Propagate failed to bind to port error + return false; + } ++ // Purpur start - UPnP Port Forwarding ++ if (org.purpurmc.purpur.PurpurConfig.useUPnP) { ++ LOGGER.info("[UPnP] Attempting to start UPnP port forwarding service..."); ++ if (dev.omega24.upnp4j.UPnP4J.isUPnPAvailable()) { ++ if (dev.omega24.upnp4j.UPnP4J.isOpen(this.getPort(), dev.omega24.upnp4j.util.Protocol.TCP)) { ++ this.upnp = false; ++ LOGGER.info("[UPnP] Port {} is already open", this.getPort()); ++ } else if (dev.omega24.upnp4j.UPnP4J.open(this.getPort(), dev.omega24.upnp4j.util.Protocol.TCP)) { ++ this.upnp = true; ++ LOGGER.info("[UPnP] Successfully opened port {}", this.getPort()); ++ } else { ++ this.upnp = false; ++ LOGGER.info("[UPnP] Failed to open port {}", this.getPort()); ++ } ++ ++ if (upnp) { ++ LOGGER.info("[UPnP] {}:{}", dev.omega24.upnp4j.UPnP4J.getExternalIP(), this.getPort()); ++ } ++ } else { ++ this.upnp = false; ++ LOGGER.error("[UPnP] Service is unavailable"); ++ } ++ } ++ // Purpur end - UPnP Port Forwarding + + // CraftBukkit start + this.server.loadPlugins(); +@@ -392,6 +_,9 @@ + MinecraftServerStatistics.registerJmxMonitoring(this); + LOGGER.info("JMX monitoring enabled"); + } ++ ++ org.purpurmc.purpur.task.BossBarTask.startAll(); // Purpur - Implement TPSBar ++ if (org.purpurmc.purpur.PurpurConfig.beeCountPayload) org.purpurmc.purpur.task.BeehiveTask.instance().register(); // Purpur - Give bee counts in beehives to Purpur clients + + this.notificationManager().serverStarted(); + return true; diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/server/dedicated/DedicatedServerProperties.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/server/dedicated/DedicatedServerProperties.java.patch new file mode 100644 index 000000000..7bb7af06e --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/server/dedicated/DedicatedServerProperties.java.patch @@ -0,0 +1,10 @@ +--- a/net/minecraft/server/dedicated/DedicatedServerProperties.java ++++ b/net/minecraft/server/dedicated/DedicatedServerProperties.java +@@ -55,6 +_,7 @@ + public final boolean onlineMode = this.get("online-mode", true); + public final boolean preventProxyConnections = this.get("prevent-proxy-connections", false); + public final String serverIp = this.get("server-ip", ""); ++ public final String serverName = this.get("server-name", "Unknown Server"); // Purpur - Bring back server name + public final Settings.MutableValue allowFlight = this.getMutable("allow-flight", false); + public final Settings.MutableValue motd = this.getMutable("motd", "A Minecraft Server"); + public final boolean codeOfConduct = this.get("enable-code-of-conduct", false); diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/server/players/PlayerList.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/server/players/PlayerList.java.patch new file mode 100644 index 000000000..c407cb56c --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/server/players/PlayerList.java.patch @@ -0,0 +1,56 @@ +--- a/net/minecraft/server/players/PlayerList.java ++++ b/net/minecraft/server/players/PlayerList.java +@@ -304,6 +_,7 @@ + scoreboard.addPlayerToTeam(player.getScoreboardName(), collideRuleTeam); + } + // Paper end - Configurable player collision ++ org.purpurmc.purpur.task.BossBarTask.addToAll(player); // Purpur - Implement TPSBar + // CraftBukkit start - moved down + LOGGER.info( + "{}[{}] logged in with entity id {} at ([{}]{}, {}, {})", // CraftBukkit - add world name +@@ -424,6 +_,7 @@ + } + public @Nullable net.kyori.adventure.text.Component remove(ServerPlayer player, net.kyori.adventure.text.Component leaveMessage) { + // Paper end - Fix kick event leave message not being sent ++ org.purpurmc.purpur.task.BossBarTask.removeFromAll(player.getBukkitEntity()); // Purpur - Implement TPSBar + ServerLevel serverLevel = player.level(); + player.awardStat(Stats.LEAVE_GAME); + // CraftBukkit start - Quitting must be before we do final save of data, in case plugins need to modify it +@@ -810,6 +_,20 @@ + } + } + ++ // Purpur start - Component related conveniences ++ public void broadcastMiniMessage(@Nullable String message, boolean overlay) { ++ if (message != null && !message.isEmpty()) { ++ this.broadcastMessage(net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(message), overlay); ++ } ++ } ++ ++ public void broadcastMessage(@Nullable net.kyori.adventure.text.Component message, boolean overlay) { ++ if (message != null) { ++ this.broadcastSystemMessage(io.papermc.paper.adventure.PaperAdventure.asVanilla(message), overlay); ++ } ++ } ++ // Purpur end - Component related conveniences ++ + public void broadcastAll(Packet packet, ResourceKey dimension) { + for (ServerPlayer serverPlayer : this.players) { + if (serverPlayer.level().dimension() == dimension) { +@@ -904,6 +_,7 @@ + } else { + b = (byte)(24 + permLevel); + } ++ if (b < 28 && player.getBukkitEntity().hasPermission("purpur.debug.f3n")) b = 28; // Purpur - Add permission for F3+N debug + + player.connection.send(new ClientboundEntityEventPacket(player, b)); + } +@@ -916,7 +_,7 @@ + + // Paper start - whitelist verify event / login event + public LoginResult canBypassFullServerLogin(final NameAndId nameAndId, final LoginResult currentResult) { +- final boolean shouldKick = this.players.size() >= this.getMaxPlayers() && !this.canBypassPlayerLimit(nameAndId); ++ final boolean shouldKick = this.players.size() >= this.getMaxPlayers() && !(/*player.hasPermission("purpur.joinfullserver") || */this.canBypassPlayerLimit(nameAndId)); // Purpur - Allow player join full server by permission TODO: this hasn't worked for a while, so comment it out until we can reliably check perms of the player joining + final io.papermc.paper.event.player.PlayerServerFullCheckEvent fullCheckEvent = new io.papermc.paper.event.player.PlayerServerFullCheckEvent( + new com.destroystokyo.paper.profile.CraftPlayerProfile(nameAndId), + io.papermc.paper.adventure.PaperAdventure.asAdventure(currentResult.message), diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/Skeleton.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/Skeleton.java.patch new file mode 100644 index 000000000..fcae77e76 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/Skeleton.java.patch @@ -0,0 +1,67 @@ +--- a/net/minecraft/world/entity/monster/Skeleton.java ++++ b/net/minecraft/world/entity/monster/Skeleton.java +@@ -129,4 +_,64 @@ + SoundEvent getStepSound() { + return SoundEvents.SKELETON_STEP; + } ++ ++ // Purpur start - Skeletons eat wither roses ++ private int witherRosesFed = 0; ++ ++ @Override ++ public net.minecraft.world.InteractionResult mobInteract(net.minecraft.world.entity.player.Player player, net.minecraft.world.InteractionHand hand) { ++ net.minecraft.world.item.ItemStack stack = player.getItemInHand(hand); ++ ++ if (level().purpurConfig.skeletonFeedWitherRoses > 0 && this.getType() != EntityType.WITHER_SKELETON && stack.getItem() == net.minecraft.world.level.block.Blocks.WITHER_ROSE.asItem()) { ++ return this.feedWitherRose(player, stack); ++ } ++ ++ return super.mobInteract(player, hand); ++ } ++ ++ private net.minecraft.world.InteractionResult feedWitherRose(net.minecraft.world.entity.player.Player player, net.minecraft.world.item.ItemStack stack) { ++ if (++witherRosesFed < level().purpurConfig.skeletonFeedWitherRoses) { ++ if (!player.getAbilities().instabuild) { ++ stack.shrink(1); ++ } ++ return net.minecraft.world.InteractionResult.CONSUME; ++ } ++ ++ WitherSkeleton skeleton = EntityType.WITHER_SKELETON.create(level(), net.minecraft.world.entity.EntitySpawnReason.CONVERSION); ++ if (skeleton == null) { ++ return net.minecraft.world.InteractionResult.PASS; ++ } ++ ++ skeleton.snapTo(this.getX(), this.getY(), this.getZ(), this.getYRot(), this.getXRot()); ++ skeleton.setHealth(this.getHealth()); ++ skeleton.setAggressive(this.isAggressive()); ++ skeleton.copyPosition(this); ++ skeleton.setYBodyRot(this.yBodyRot); ++ skeleton.setYHeadRot(this.getYHeadRot()); ++ skeleton.yRotO = this.yRotO; ++ skeleton.xRotO = this.xRotO; ++ ++ if (this.hasCustomName()) { ++ skeleton.setCustomName(this.getCustomName()); ++ } ++ ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTransformEvent(this, skeleton, org.bukkit.event.entity.EntityTransformEvent.TransformReason.INFECTION).isCancelled()) { ++ return net.minecraft.world.InteractionResult.PASS; ++ } ++ ++ this.level().addFreshEntity(skeleton); ++ this.remove(RemovalReason.DISCARDED, org.bukkit.event.entity.EntityRemoveEvent.Cause.DISCARD); ++ if (!player.getAbilities().instabuild) { ++ stack.shrink(1); ++ } ++ ++ for (int i = 0; i < 15; ++i) { ++ ((net.minecraft.server.level.ServerLevel) level()).sendParticlesSource(((net.minecraft.server.level.ServerLevel) level()).players(), null, net.minecraft.core.particles.ParticleTypes.HAPPY_VILLAGER, ++ false, true, ++ getX() + random.nextFloat(), getY() + (random.nextFloat() * 2), getZ() + random.nextFloat(), 1, ++ random.nextGaussian() * 0.05D, random.nextGaussian() * 0.05D, random.nextGaussian() * 0.05D, 0); ++ } ++ return net.minecraft.world.InteractionResult.SUCCESS; ++ } ++ // Purpur end - Skeletons eat wither roses + } diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/ZombieVillager.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/ZombieVillager.java.patch new file mode 100644 index 000000000..1daf00dde --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/ZombieVillager.java.patch @@ -0,0 +1,15 @@ +--- a/net/minecraft/world/entity/monster/ZombieVillager.java ++++ b/net/minecraft/world/entity/monster/ZombieVillager.java +@@ -138,10 +_,10 @@ + public InteractionResult mobInteract(Player player, InteractionHand hand) { + ItemStack itemInHand = player.getItemInHand(hand); + if (itemInHand.is(Items.GOLDEN_APPLE)) { +- if (this.hasEffect(MobEffects.WEAKNESS)) { ++ if (this.hasEffect(MobEffects.WEAKNESS) && level().purpurConfig.zombieVillagerCureEnabled) { // Purpur - Add option to disable zombie villagers cure + itemInHand.consume(1, player); + if (!this.level().isClientSide()) { +- this.startConverting(player.getUUID(), this.random.nextInt(2401) + 3600); ++ this.startConverting(player.getUUID(), this.random.nextInt(level().purpurConfig.zombieVillagerCuringTimeMax - level().purpurConfig.zombieVillagerCuringTimeMin + 1) + level().purpurConfig.zombieVillagerCuringTimeMin); // Purpur - Customizable Zombie Villager curing times + } + + return InteractionResult.SUCCESS_SERVER; diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/npc/CatSpawner.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/npc/CatSpawner.java.patch new file mode 100644 index 000000000..6d573d3e2 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/npc/CatSpawner.java.patch @@ -0,0 +1,41 @@ +--- a/net/minecraft/world/entity/npc/CatSpawner.java ++++ b/net/minecraft/world/entity/npc/CatSpawner.java +@@ -23,7 +_,7 @@ + public void tick(ServerLevel level, boolean spawnEnemies) { + this.nextTick--; + if (this.nextTick <= 0) { +- this.nextTick = 1200; ++ this.nextTick = level.purpurConfig.catSpawnDelay; // Purpur - Cat spawning options + Player randomPlayer = level.getRandomPlayer(); + if (randomPlayer != null) { + RandomSource randomSource = level.random; +@@ -45,9 +_,12 @@ + } + + private void spawnInVillage(ServerLevel level, BlockPos pos) { +- int i = 48; +- if (level.getPoiManager().getCountInRange(holder -> holder.is(PoiTypes.HOME), pos, 48, PoiManager.Occupancy.IS_OCCUPIED) > 4L) { +- List entitiesOfClass = level.getEntitiesOfClass(Cat.class, new AABB(pos).inflate(48.0, 8.0, 48.0)); ++ // Purpur start - Cat spawning options ++ int range = level.purpurConfig.catSpawnVillageScanRange; ++ if (range <= 0) return; ++ if (level.getPoiManager().getCountInRange(holder -> holder.is(PoiTypes.HOME), pos, range, PoiManager.Occupancy.IS_OCCUPIED) > 4L) { ++ List entitiesOfClass = level.getEntitiesOfClass(Cat.class, new AABB(pos).inflate(range, 8.0, range)); ++ // Purpur end - Cat spawning options + if (entitiesOfClass.size() < 5) { + this.spawnCat(pos, level, false); + } +@@ -55,8 +_,11 @@ + } + + private void spawnInHut(ServerLevel level, BlockPos pos) { +- int i = 16; +- List entitiesOfClass = level.getEntitiesOfClass(Cat.class, new AABB(pos).inflate(16.0, 8.0, 16.0)); ++ // Purpur start - Cat spawning options ++ int range = level.purpurConfig.catSpawnSwampHutScanRange; ++ if (range <= 0) return; ++ List entitiesOfClass = level.getEntitiesOfClass(Cat.class, new AABB(pos).inflate(range, 8.0, range)); ++ // Purpur end - Cat spawning options + if (entitiesOfClass.isEmpty()) { + this.spawnCat(pos, level, true); + }