mirror of
https://github.com/PurpurMC/Purpur.git
synced 2026-04-22 11:18:15 +02:00
60/103 minecraft source files applied
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
--- a/net/minecraft/CrashReport.java
|
||||
+++ b/net/minecraft/CrashReport.java
|
||||
@@ -30,6 +_,7 @@
|
||||
private boolean trackingStackTrace = true;
|
||||
private StackTraceElement[] uncategorizedStackTrace = new StackTraceElement[0];
|
||||
private final SystemReport systemReport = new SystemReport();
|
||||
+ private List<String> extraInfo = List.of("", "DO NOT REPORT THIS TO PAPER! REPORT TO PURPUR INSTEAD!", ""); // Purpur - Rebrand
|
||||
|
||||
public CrashReport(final String title, final Throwable t) {
|
||||
this.title = title;
|
||||
@@ -129,7 +_,7 @@
|
||||
}
|
||||
|
||||
public String getFriendlyReport(final ReportType reportType) {
|
||||
- return this.getFriendlyReport(reportType, List.of());
|
||||
+ return this.getFriendlyReport(reportType, extraInfo); // Purpur - Rebrand
|
||||
}
|
||||
|
||||
public @Nullable Path getSaveFile() {
|
||||
@@ -159,7 +_,7 @@
|
||||
}
|
||||
|
||||
public boolean saveToFile(final Path file, final ReportType reportType) {
|
||||
- return this.saveToFile(file, reportType, List.of());
|
||||
+ return this.saveToFile(file, reportType, extraInfo); // Purpur - Rebrand
|
||||
}
|
||||
|
||||
public SystemReport getSystemReport() {
|
||||
@@ -0,0 +1,25 @@
|
||||
--- a/net/minecraft/core/dispenser/DispenseItemBehavior.java
|
||||
+++ b/net/minecraft/core/dispenser/DispenseItemBehavior.java
|
||||
@@ -690,5 +_,22 @@
|
||||
DispenserBlock.registerBehavior(Items.TNT_MINECART, new MinecartDispenseItemBehavior(EntityType.TNT_MINECART));
|
||||
DispenserBlock.registerBehavior(Items.HOPPER_MINECART, new MinecartDispenseItemBehavior(EntityType.HOPPER_MINECART));
|
||||
DispenserBlock.registerBehavior(Items.COMMAND_BLOCK_MINECART, new MinecartDispenseItemBehavior(EntityType.COMMAND_BLOCK_MINECART));
|
||||
+ // Purpur start - Dispensers place anvils option
|
||||
+ DispenserBlock.registerBehavior(Items.ANVIL, (new OptionalDispenseItemBehavior() {
|
||||
+ @Override
|
||||
+ public ItemStack execute(BlockSource dispenser, ItemStack stack) {
|
||||
+ net.minecraft.world.level.Level level = dispenser.level();
|
||||
+ if (!level.purpurConfig.dispenserPlaceAnvils) return super.execute(dispenser, stack);
|
||||
+ Direction facing = dispenser.blockEntity().getBlockState().getValue(DispenserBlock.FACING);
|
||||
+ BlockPos pos = dispenser.pos().relative(facing);
|
||||
+ BlockState state = level.getBlockState(pos);
|
||||
+ if (state.isAir()) {
|
||||
+ level.setBlockAndUpdate(pos, Blocks.ANVIL.defaultBlockState().setValue(net.minecraft.world.level.block.AnvilBlock.FACING, facing.getAxis() == Direction.Axis.Y ? Direction.NORTH : facing.getClockWise()));
|
||||
+ stack.shrink(1);
|
||||
+ }
|
||||
+ return stack;
|
||||
+ }
|
||||
+ }));
|
||||
+ // Purpur end - Dispensers place anvils option
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
--- a/net/minecraft/gametest/framework/GameTestHelper.java
|
||||
+++ b/net/minecraft/gametest/framework/GameTestHelper.java
|
||||
@@ -349,6 +_,8 @@
|
||||
return gameType;
|
||||
}
|
||||
|
||||
+ public void setAfk(final boolean afk) {} // Purpur - AFK API
|
||||
+
|
||||
@Override
|
||||
public boolean isClientAuthoritative() {
|
||||
return false;
|
||||
@@ -0,0 +1,23 @@
|
||||
--- a/net/minecraft/network/Connection.java
|
||||
+++ b/net/minecraft/network/Connection.java
|
||||
@@ -460,11 +_,20 @@
|
||||
private static final int MAX_PER_TICK = io.papermc.paper.configuration.GlobalConfiguration.get().misc.maxJoinsPerTick; // Paper - Buffer joins to world
|
||||
private static int joinAttemptsThisTick; // Paper - Buffer joins to world
|
||||
private static int currTick; // Paper - Buffer joins to world
|
||||
+ private static int tickSecond; // Purpur - Max joins per second
|
||||
public void tick() {
|
||||
this.flushQueue();
|
||||
// Paper start - Buffer joins to world
|
||||
if (Connection.currTick != net.minecraft.server.MinecraftServer.currentTick) {
|
||||
Connection.currTick = net.minecraft.server.MinecraftServer.currentTick;
|
||||
+ // Purpur start - Max joins per second
|
||||
+ if (org.purpurmc.purpur.PurpurConfig.maxJoinsPerSecond) {
|
||||
+ if (++Connection.tickSecond > 20) {
|
||||
+ Connection.tickSecond = 0;
|
||||
+ Connection.joinAttemptsThisTick = 0;
|
||||
+ }
|
||||
+ } else
|
||||
+ // Purpur end - Max joins per second
|
||||
Connection.joinAttemptsThisTick = 0;
|
||||
}
|
||||
// Paper end - Buffer joins to world
|
||||
@@ -0,0 +1,11 @@
|
||||
--- a/net/minecraft/network/chat/SignedMessageChain.java
|
||||
+++ b/net/minecraft/network/chat/SignedMessageChain.java
|
||||
@@ -49,7 +_,7 @@
|
||||
SignedMessageLink link = SignedMessageChain.this.nextLink;
|
||||
if (link == null) {
|
||||
throw new SignedMessageChain.DecodeException(SignedMessageChain.DecodeException.CHAIN_BROKEN);
|
||||
- } else if (body.timeStamp().isBefore(SignedMessageChain.this.lastTimeStamp)) {
|
||||
+ } else if (org.purpurmc.purpur.PurpurConfig.kickForOutOfOrderChat && body.timeStamp().isBefore(SignedMessageChain.this.lastTimeStamp)) { // Purpur - Option to disable kick for out of order chat
|
||||
this.setChainBroken();
|
||||
throw new SignedMessageChain.DecodeException(SignedMessageChain.DecodeException.OUT_OF_ORDER_CHAT, org.bukkit.event.player.PlayerKickEvent.Cause.OUT_OF_ORDER_CHAT); // Paper - kick event causes
|
||||
} else {
|
||||
@@ -0,0 +1,67 @@
|
||||
--- a/net/minecraft/server/dedicated/DedicatedServer.java
|
||||
+++ b/net/minecraft/server/dedicated/DedicatedServer.java
|
||||
@@ -216,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 reader = new BufferedReader(new InputStreamReader(System.in, StandardCharsets.UTF_8));
|
||||
@@ -294,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
|
||||
@@ -336,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();
|
||||
@@ -410,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;
|
||||
@@ -0,0 +1,10 @@
|
||||
--- a/net/minecraft/server/dedicated/DedicatedServerProperties.java
|
||||
+++ b/net/minecraft/server/dedicated/DedicatedServerProperties.java
|
||||
@@ -57,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<DedicatedServerProperties>.MutableValue<Boolean> allowFlight = this.getMutable("allow-flight", false);
|
||||
public final Settings<DedicatedServerProperties>.MutableValue<String> motd = this.getMutable("motd", "A Minecraft Server");
|
||||
public final boolean codeOfConduct = this.get("enable-code-of-conduct", false);
|
||||
@@ -0,0 +1,11 @@
|
||||
--- a/net/minecraft/server/gui/StatsComponent.java
|
||||
+++ b/net/minecraft/server/gui/StatsComponent.java
|
||||
@@ -45,7 +_,7 @@
|
||||
+ Runtime.getRuntime().freeMemory() * 100L / Runtime.getRuntime().maxMemory()
|
||||
+ "% free)";
|
||||
this.msgs[1] = "Avg tick: " + DECIMAL_FORMAT.format((double)this.server.getAverageTickTimeNanos() / TimeUtil.NANOSECONDS_PER_MILLISECOND) + " ms";
|
||||
- this.msgs[2] = "TPS from last 1m, 5m, 15m: " + String.join(", ", tpsAvg);
|
||||
+ this.msgs[2] = "TPS from last 5s, 1m, 5m, 15m: " + String.join(", ", tpsAvg); // Purpur - Add 5 second tps average in /tps
|
||||
// Paper end - Improve ServerGUI
|
||||
this.values[this.vp++ & 0xFF] = (int)(usedRam * 100L / Runtime.getRuntime().maxMemory());
|
||||
this.repaint();
|
||||
@@ -0,0 +1,10 @@
|
||||
--- a/net/minecraft/server/level/WorldGenRegion.java
|
||||
+++ b/net/minecraft/server/level/WorldGenRegion.java
|
||||
@@ -262,6 +_,7 @@
|
||||
return true;
|
||||
} else {
|
||||
// Paper start - Buffer OOB setBlock calls
|
||||
+ if (!org.purpurmc.purpur.PurpurConfig.loggerSuppressSetBlockFarChunk) // Purpur - Logger settings (suppressing pointless logs)
|
||||
if (!hasSetFarWarned) {
|
||||
Util.logAndPauseIfInIde(
|
||||
"Detected setBlock in a far chunk ["
|
||||
@@ -0,0 +1,10 @@
|
||||
--- a/net/minecraft/server/network/ServerStatusPacketListenerImpl.java
|
||||
+++ b/net/minecraft/server/network/ServerStatusPacketListenerImpl.java
|
||||
@@ -37,6 +_,7 @@
|
||||
} else {
|
||||
this.hasRequestedStatus = true;
|
||||
// this.connection.send(new ClientboundStatusResponsePacket(this.status)); // Paper
|
||||
+ if (net.minecraft.server.MinecraftServer.getServer().getStatus().version().isEmpty()) return; // Purpur - Fix 'outdated server' showing in ping before server fully boots - do not respond to pings before we know the protocol version
|
||||
com.destroystokyo.paper.network.StandardPaperServerListPingEventImpl.processRequest(net.minecraft.server.MinecraftServer.getServer(), this.connection); // Paper - handle status request
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
--- a/net/minecraft/util/StringUtil.java
|
||||
+++ b/net/minecraft/util/StringUtil.java
|
||||
@@ -87,6 +_,7 @@
|
||||
|
||||
// Paper start - Username validation
|
||||
public static boolean isReasonablePlayerName(final String name) {
|
||||
+ if (true) return org.purpurmc.purpur.PurpurConfig.usernameValidCharactersPattern.matcher(name).matches(); // Purpur - Configurable valid characters for usernames
|
||||
if (name.isEmpty() || name.length() > 16) {
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
--- a/net/minecraft/world/damagesource/DamageSources.java
|
||||
+++ b/net/minecraft/world/damagesource/DamageSources.java
|
||||
@@ -42,6 +_,8 @@
|
||||
private final DamageSource stalagmite;
|
||||
private final DamageSource outsideBorder;
|
||||
private final DamageSource genericKill;
|
||||
+ private final DamageSource scissors; // Purpur - Dont run with scissors!
|
||||
+ private final DamageSource stonecutter; // Purpur - Stonecutter damage
|
||||
|
||||
public DamageSources(final RegistryAccess registries) {
|
||||
this.damageTypes = registries.lookupOrThrow(Registries.DAMAGE_TYPE);
|
||||
@@ -70,6 +_,8 @@
|
||||
this.stalagmite = this.source(DamageTypes.STALAGMITE);
|
||||
this.outsideBorder = this.source(DamageTypes.OUTSIDE_BORDER);
|
||||
this.genericKill = this.source(DamageTypes.GENERIC_KILL);
|
||||
+ this.scissors = this.source(DamageTypes.MAGIC).scissors(); // Purpur - Dont run with scissors!
|
||||
+ this.stonecutter = this.source(DamageTypes.MAGIC).stonecutter(); // Purpur - Stonecutter damage
|
||||
}
|
||||
|
||||
private DamageSource source(final ResourceKey<DamageType> key) {
|
||||
@@ -83,6 +_,18 @@
|
||||
private DamageSource source(final ResourceKey<DamageType> key, final @Nullable Entity directEntity, final @Nullable Entity causingEntity) {
|
||||
return new DamageSource(this.damageTypes.getOrThrow(key), directEntity, causingEntity);
|
||||
}
|
||||
+
|
||||
+ // Purpur start - Dont run with scissor
|
||||
+ public DamageSource scissors() {
|
||||
+ return this.scissors;
|
||||
+ }
|
||||
+ // Purpur end - Dont run with scissors!
|
||||
+
|
||||
+ // Purpur start - Stonecutter damage
|
||||
+ public DamageSource stonecutter() {
|
||||
+ return this.stonecutter;
|
||||
+ }
|
||||
+ // Purpur end - Stonecutter damage
|
||||
|
||||
public DamageSource inFire() {
|
||||
return this.inFire;
|
||||
@@ -0,0 +1,12 @@
|
||||
--- a/net/minecraft/world/effect/SaturationMobEffect.java
|
||||
+++ b/net/minecraft/world/effect/SaturationMobEffect.java
|
||||
@@ -16,7 +_,8 @@
|
||||
int oldFoodLevel = player.getFoodData().foodLevel;
|
||||
org.bukkit.event.entity.FoodLevelChangeEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callFoodLevelChangeEvent(player, amplification + 1 + oldFoodLevel);
|
||||
if (!event.isCancelled()) {
|
||||
- player.getFoodData().eat(event.getFoodLevel() - oldFoodLevel, 1.0F);
|
||||
+ if (player.level().purpurConfig.playerBurpWhenFull && event.getFoodLevel() == 20 && oldFoodLevel < 20) player.burpDelay = player.level().purpurConfig.playerBurpDelay; // Purpur - Burp delay
|
||||
+ player.getFoodData().eat(event.getFoodLevel() - oldFoodLevel, entity.level().purpurConfig.humanSaturationRegenAmount); // Purpur - Config MobEffect by world
|
||||
}
|
||||
|
||||
((org.bukkit.craftbukkit.entity.CraftPlayer) player.getBukkitEntity()).sendHealthUpdate();
|
||||
@@ -0,0 +1,11 @@
|
||||
--- a/net/minecraft/world/entity/Entity.java
|
||||
+++ b/net/minecraft/world/entity/Entity.java
|
||||
@@ -380,7 +_,7 @@
|
||||
private final Set<String> tags = new io.papermc.paper.util.SizeLimitedSet<>(new it.unimi.dsi.fastutil.objects.ObjectOpenHashSet<>(), MAX_ENTITY_TAG_COUNT); // Paper - fully limit tag size - replace set impl
|
||||
private final double[] pistonDeltas = new double[]{0.0, 0.0, 0.0};
|
||||
private long pistonDeltasGameTime;
|
||||
- protected EntityDimensions dimensions;
|
||||
+ private EntityDimensions dimensions;
|
||||
private float eyeHeight;
|
||||
public boolean isInPowderSnow;
|
||||
public boolean wasInPowderSnow;
|
||||
@@ -0,0 +1,11 @@
|
||||
--- a/net/minecraft/world/entity/EntitySelector.java
|
||||
+++ b/net/minecraft/world/entity/EntitySelector.java
|
||||
@@ -28,6 +_,8 @@
|
||||
return net.minecraft.util.Mth.clamp(serverPlayer.getStats().getValue(net.minecraft.stats.Stats.CUSTOM.get(net.minecraft.stats.Stats.TIME_SINCE_REST)), 1, Integer.MAX_VALUE) >= playerInsomniaTicks;
|
||||
};
|
||||
// Paper end - Ability to control player's insomnia and phantoms
|
||||
+ public static Predicate<Player> notAfk = (player) -> !player.isAfk(); // Purpur - AFK API
|
||||
+
|
||||
// Paper start - Affects Spawning API
|
||||
public static final Predicate<Entity> PLAYER_AFFECTS_SPAWNING = (entity) -> {
|
||||
return !entity.isSpectator() && entity.isAlive() && entity instanceof Player player && player.affectsSpawning;
|
||||
@@ -0,0 +1,11 @@
|
||||
--- a/net/minecraft/world/entity/EntityType.java
|
||||
+++ b/net/minecraft/world/entity/EntityType.java
|
||||
@@ -1221,7 +_,7 @@
|
||||
private final String descriptionId;
|
||||
private @Nullable Component description;
|
||||
private final Optional<ResourceKey<LootTable>> lootTable;
|
||||
- public EntityDimensions dimensions;
|
||||
+ private final EntityDimensions dimensions;
|
||||
private final float spawnDimensionsScale;
|
||||
private final FeatureFlagSet requiredFeatures;
|
||||
private final boolean allowedInPeaceful;
|
||||
@@ -0,0 +1,11 @@
|
||||
--- a/net/minecraft/world/entity/LivingEntity.java
|
||||
+++ b/net/minecraft/world/entity/LivingEntity.java
|
||||
@@ -3852,7 +_,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
- public boolean canGlide() {
|
||||
+ protected boolean canGlide() {
|
||||
if (!this.onGround() && !this.isPassenger() && !this.hasEffect(MobEffects.LEVITATION)) {
|
||||
for (EquipmentSlot slot : EquipmentSlot.VALUES) {
|
||||
if (canGlideUsing(this.getItemBySlot(slot), slot)) {
|
||||
@@ -0,0 +1,18 @@
|
||||
--- a/net/minecraft/world/entity/ai/goal/LlamaFollowCaravanGoal.java
|
||||
+++ b/net/minecraft/world/entity/ai/goal/LlamaFollowCaravanGoal.java
|
||||
@@ -22,6 +_,7 @@
|
||||
|
||||
@Override
|
||||
public boolean canUse() {
|
||||
+ if (!this.llama.level().purpurConfig.llamaJoinCaravans || !this.llama.shouldJoinCaravan) return false; // Purpur - Llama API // Purpur - Config to disable Llama caravans
|
||||
if (!this.llama.isLeashed() && !this.llama.inCaravan()) {
|
||||
List<Entity> llamas = this.llama
|
||||
.level()
|
||||
@@ -70,6 +_,7 @@
|
||||
|
||||
@Override
|
||||
public boolean canContinueToUse() {
|
||||
+ if (!this.llama.shouldJoinCaravan) return false; // Purpur - Llama API
|
||||
if (this.llama.inCaravan() && this.llama.getCaravanHead().isAlive() && this.firstIsLeashed(this.llama, 0)) {
|
||||
double distSqr = this.llama.distanceToSqr(this.llama.getCaravanHead());
|
||||
if (distSqr > 676.0) {
|
||||
@@ -0,0 +1,14 @@
|
||||
--- a/net/minecraft/world/entity/ai/goal/RangedBowAttackGoal.java
|
||||
+++ b/net/minecraft/world/entity/ai/goal/RangedBowAttackGoal.java
|
||||
@@ -116,9 +_,9 @@
|
||||
}
|
||||
|
||||
this.mob.lookAt(target, 30.0F, 30.0F);
|
||||
- } else {
|
||||
+ } //else { // Purpur - MC-121706 - Fix mobs not looking up and down when strafing
|
||||
this.mob.getLookControl().setLookAt(target, 30.0F, 30.0F);
|
||||
- }
|
||||
+ //} // Purpur - MC-121706 - Fix mobs not looking up and down when strafing
|
||||
|
||||
if (this.mob.isUsingItem()) {
|
||||
if (!hasLineOfSight && this.seeTime < -60) {
|
||||
@@ -0,0 +1,11 @@
|
||||
--- a/net/minecraft/world/entity/ai/goal/RunAroundLikeCrazyGoal.java
|
||||
+++ b/net/minecraft/world/entity/ai/goal/RunAroundLikeCrazyGoal.java
|
||||
@@ -59,7 +_,7 @@
|
||||
if (passenger instanceof Player player) {
|
||||
int temper = this.horse.getTemper();
|
||||
int maxTemper = this.horse.getMaxTemper();
|
||||
- if (maxTemper > 0 && this.horse.getRandom().nextInt(maxTemper) < temper && !org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTameEvent(this.horse, ((org.bukkit.craftbukkit.entity.CraftHumanEntity) this.horse.getBukkitEntity().getPassenger()).getHandle()).isCancelled()) { // CraftBukkit - fire EntityTameEvent
|
||||
+ if (((this.horse.level().purpurConfig.alwaysTameInCreative && player.hasInfiniteMaterials()) || (maxTemper > 0 && this.horse.getRandom().nextInt(maxTemper) < temper)) && !org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTameEvent(this.horse, ((org.bukkit.craftbukkit.entity.CraftHumanEntity) this.horse.getBukkitEntity().getPassenger()).getHandle()).isCancelled()) { // CraftBukkit - fire EntityTameEvent // Purpur - Config to always tame in Creative
|
||||
this.horse.tameWithName(player);
|
||||
return;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
--- a/net/minecraft/world/entity/ai/targeting/TargetingConditions.java
|
||||
+++ b/net/minecraft/world/entity/ai/targeting/TargetingConditions.java
|
||||
@@ -63,6 +_,10 @@
|
||||
return false;
|
||||
} else if (this.selector != null && !this.selector.test(target, level)) {
|
||||
return false;
|
||||
+ // Purpur start - AFK API
|
||||
+ } else if (!level.purpurConfig.idleTimeoutTargetPlayer && target instanceof net.minecraft.server.level.ServerPlayer player && player.isAfk()) {
|
||||
+ return false;
|
||||
+ // Purpur end - AFK API
|
||||
} else {
|
||||
if (targeter == null) {
|
||||
if (this.isCombat && (!target.canBeSeenAsEnemy() || level.getDifficulty() == Difficulty.PEACEFUL)) {
|
||||
@@ -0,0 +1,16 @@
|
||||
--- a/net/minecraft/world/entity/animal/cow/MushroomCow.java
|
||||
+++ b/net/minecraft/world/entity/animal/cow/MushroomCow.java
|
||||
@@ -199,6 +_,13 @@
|
||||
level.playSound(null, this, SoundEvents.MOOSHROOM_SHEAR, soundSource, 1.0F, 1.0F);
|
||||
this.convertTo(EntityType.COW, ConversionParams.single(this, false, false), cow -> {
|
||||
level.sendParticles(ParticleTypes.EXPLOSION, this.getX(), this.getY(0.5), this.getZ(), 1, 0.0, 0.0, 0.0, 0.0);
|
||||
+ // Purpur start - Fix cow rotation when shearing mooshroom
|
||||
+ cow.copyPosition(this);
|
||||
+ cow.yBodyRot = this.yBodyRot;
|
||||
+ cow.setYHeadRot(this.getYHeadRot());
|
||||
+ cow.yRotO = this.yRotO;
|
||||
+ cow.xRotO = this.xRotO;
|
||||
+ // Purpur end - Fix cow rotation when shearing mooshroom
|
||||
// Paper start - custom shear drops; moved drop generation to separate method
|
||||
drops.forEach(drop -> {
|
||||
this.spawnAtLocation(level, new ItemEntity(this.level(), this.getX(), this.getY(1.0), this.getZ(), drop));
|
||||
@@ -0,0 +1,52 @@
|
||||
--- a/net/minecraft/world/entity/animal/fox/Fox.java
|
||||
+++ b/net/minecraft/world/entity/animal/fox/Fox.java
|
||||
@@ -364,6 +_,11 @@
|
||||
}
|
||||
|
||||
private void setTargetGoals() {
|
||||
+ // Purpur start - Tulips change fox type - do not add duplicate goals
|
||||
+ this.targetSelector.removeGoal(this.landTargetGoal);
|
||||
+ this.targetSelector.removeGoal(this.turtleEggTargetGoal);
|
||||
+ this.targetSelector.removeGoal(this.fishTargetGoal);
|
||||
+ // Purpur end - Tulips change fox type
|
||||
if (this.getVariant() == Fox.Variant.RED) {
|
||||
this.targetSelector.addGoal(4, this.landTargetGoal);
|
||||
this.targetSelector.addGoal(4, this.turtleEggTargetGoal);
|
||||
@@ -391,6 +_,7 @@
|
||||
|
||||
public void setVariant(final Fox.Variant variant) {
|
||||
this.entityData.set(DATA_TYPE_ID, variant.getId());
|
||||
+ this.setTargetGoals(); // Purpur - Tulips change fox type - fix API bug not updating pathfinders on type change
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -716,6 +_,29 @@
|
||||
return slot == EquipmentSlot.MAINHAND;
|
||||
}
|
||||
// Paper end
|
||||
+
|
||||
+ // Purpur start - Tulips change fox type
|
||||
+ @Override
|
||||
+ public net.minecraft.world.InteractionResult mobInteract(Player player, net.minecraft.world.InteractionHand hand) {
|
||||
+ if (level().purpurConfig.foxTypeChangesWithTulips) {
|
||||
+ ItemStack itemstack = player.getItemInHand(hand);
|
||||
+ if (getVariant() == Variant.RED && itemstack.getItem() == Items.WHITE_TULIP) {
|
||||
+ setVariant(Variant.SNOW);
|
||||
+ if (!player.getAbilities().instabuild) {
|
||||
+ itemstack.shrink(1);
|
||||
+ }
|
||||
+ return net.minecraft.world.InteractionResult.SUCCESS;
|
||||
+ } else if (getVariant() == Variant.SNOW && itemstack.getItem() == Items.ORANGE_TULIP) {
|
||||
+ setVariant(Variant.RED);
|
||||
+ if (!player.getAbilities().instabuild) {
|
||||
+ itemstack.shrink(1);
|
||||
+ }
|
||||
+ return net.minecraft.world.InteractionResult.SUCCESS;
|
||||
+ }
|
||||
+ }
|
||||
+ return super.mobInteract(player, hand);
|
||||
+ }
|
||||
+ // Purpur end - Tulips change fox type
|
||||
|
||||
@Override
|
||||
// Paper start - Cancellable death event
|
||||
@@ -0,0 +1,10 @@
|
||||
--- a/net/minecraft/world/entity/animal/goat/Goat.java
|
||||
+++ b/net/minecraft/world/entity/animal/goat/Goat.java
|
||||
@@ -363,6 +_,7 @@
|
||||
|
||||
// Paper start - Goat ram API
|
||||
public void ram(net.minecraft.world.entity.LivingEntity entity) {
|
||||
+ if(!new org.purpurmc.purpur.event.entity.GoatRamEntityEvent((org.bukkit.entity.Goat) getBukkitEntity(), entity.getBukkitLivingEntity()).callEvent()) return; // Purpur - Added goat ram event
|
||||
Brain<Goat> brain = this.getBrain();
|
||||
brain.setMemory(net.minecraft.world.entity.ai.memory.MemoryModuleType.RAM_TARGET, entity.position());
|
||||
brain.eraseMemory(net.minecraft.world.entity.ai.memory.MemoryModuleType.RAM_COOLDOWN_TICKS);
|
||||
@@ -0,0 +1,44 @@
|
||||
--- a/net/minecraft/world/entity/animal/golem/CopperGolem.java
|
||||
+++ b/net/minecraft/world/entity/animal/golem/CopperGolem.java
|
||||
@@ -87,6 +_,7 @@
|
||||
private final AnimationState interactionDropItemAnimationState = new AnimationState();
|
||||
private final AnimationState interactionDropNoItemAnimationState = new AnimationState();
|
||||
public static final EquipmentSlot EQUIPMENT_SLOT_ANTENNA = EquipmentSlot.SADDLE;
|
||||
+ @Nullable private UUID summoner; // Purpur - Summoner API
|
||||
|
||||
public CopperGolem(final EntityType<? extends AbstractGolem> type, final Level level) {
|
||||
super(type, level);
|
||||
@@ -100,6 +_,17 @@
|
||||
this.getBrain().setMemory(MemoryModuleType.TRANSPORT_ITEMS_COOLDOWN_TICKS, this.getRandom().nextInt(60, 100));
|
||||
}
|
||||
|
||||
+ // Purpur start - Summoner API
|
||||
+ @Nullable
|
||||
+ public UUID getSummoner() {
|
||||
+ return summoner;
|
||||
+ }
|
||||
+
|
||||
+ public void setSummoner(@Nullable UUID summoner) {
|
||||
+ this.summoner = summoner;
|
||||
+ }
|
||||
+ // Purpur end - Summoner API
|
||||
+
|
||||
public static AttributeSupplier.Builder createAttributes() {
|
||||
return Mob.createMobAttributes().add(Attributes.MOVEMENT_SPEED, 0.2F).add(Attributes.STEP_HEIGHT, 1.0).add(Attributes.MAX_HEALTH, 12.0);
|
||||
}
|
||||
@@ -170,6 +_,7 @@
|
||||
super.addAdditionalSaveData(output);
|
||||
output.putLong("next_weather_age", this.nextWeatheringTick);
|
||||
output.store("weather_state", WeatheringCopper.WeatherState.CODEC, this.getWeatherState());
|
||||
+ output.storeNullable("Purpur.Summoner", net.minecraft.core.UUIDUtil.CODEC, getSummoner()); // Purpur - Summoner API
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -177,6 +_,7 @@
|
||||
super.readAdditionalSaveData(input);
|
||||
this.nextWeatheringTick = input.getLongOr("next_weather_age", -1L);
|
||||
this.setWeatherState(input.read("weather_state", WeatheringCopper.WeatherState.CODEC).orElse(WeatheringCopper.WeatherState.UNAFFECTED));
|
||||
+ this.setSummoner(input.read("Purpur.Summoner", net.minecraft.core.UUIDUtil.CODEC).orElse(null)); // Purpur - Summoner API
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -0,0 +1,61 @@
|
||||
--- a/net/minecraft/world/entity/animal/golem/SnowGolem.java
|
||||
+++ b/net/minecraft/world/entity/animal/golem/SnowGolem.java
|
||||
@@ -46,15 +_,26 @@
|
||||
private static final EntityDataAccessor<Byte> DATA_PUMPKIN_ID = SynchedEntityData.defineId(SnowGolem.class, EntityDataSerializers.BYTE);
|
||||
private static final byte PUMPKIN_FLAG = 16;
|
||||
private static final boolean DEFAULT_PUMPKIN = true;
|
||||
+ private java.util.@Nullable UUID summoner; // Purpur - Summoner API
|
||||
|
||||
public SnowGolem(final EntityType<? extends SnowGolem> type, final Level level) {
|
||||
super(type, level);
|
||||
}
|
||||
|
||||
+ // Purpur start - Summoner API
|
||||
+ public java.util.@Nullable UUID getSummoner() {
|
||||
+ return summoner;
|
||||
+ }
|
||||
+
|
||||
+ public void setSummoner(java.util.@Nullable UUID summoner) {
|
||||
+ this.summoner = summoner;
|
||||
+ }
|
||||
+ // Purpur end - Summoner API
|
||||
+
|
||||
@Override
|
||||
protected void registerGoals() {
|
||||
- this.goalSelector.addGoal(1, new RangedAttackGoal(this, 1.25, 20, 10.0F));
|
||||
- this.goalSelector.addGoal(2, new WaterAvoidingRandomStrollGoal(this, 1.0, 1.0000001E-5F));
|
||||
+ this.goalSelector.addGoal(1, new RangedAttackGoal(this, level().purpurConfig.snowGolemAttackDistance, level().purpurConfig.snowGolemSnowBallMin, level().purpurConfig.snowGolemSnowBallMax, level().purpurConfig.snowGolemSnowBallModifier)); // Purpur - Snow Golem rate of fire config
|
||||
+ this.goalSelector.addGoal(2, new WaterAvoidingRandomStrollGoal(this, 1.0D, 1.0000001E-5F));
|
||||
this.goalSelector.addGoal(3, new LookAtPlayerGoal(this, Player.class, 6.0F));
|
||||
this.goalSelector.addGoal(4, new RandomLookAroundGoal(this));
|
||||
this.targetSelector.addGoal(1, new NearestAttackableTargetGoal<>(this, Mob.class, 10, true, false, (target, level) -> target instanceof Enemy));
|
||||
@@ -74,12 +_,14 @@
|
||||
protected void addAdditionalSaveData(final ValueOutput output) {
|
||||
super.addAdditionalSaveData(output);
|
||||
output.putBoolean("Pumpkin", this.hasPumpkin());
|
||||
+ output.storeNullable("Purpur.Summoner", net.minecraft.core.UUIDUtil.CODEC, getSummoner()); // Purpur - Summoner API
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void readAdditionalSaveData(final ValueInput input) {
|
||||
super.readAdditionalSaveData(input);
|
||||
this.setPumpkin(input.getBooleanOr("Pumpkin", true));
|
||||
+ this.setSummoner(input.read("Purpur.Summoner", net.minecraft.core.UUIDUtil.CODEC).orElse(null)); // Purpur - Summoner API
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -156,6 +_,14 @@
|
||||
}
|
||||
|
||||
return InteractionResult.SUCCESS;
|
||||
+ // Purpur start - Snowman drop and put back pumpkin
|
||||
+ } else if (level().purpurConfig.snowGolemPutPumpkinBack && !hasPumpkin() && itemInHand.getItem() == Blocks.CARVED_PUMPKIN.asItem()) {
|
||||
+ setPumpkin(true);
|
||||
+ if (!player.getAbilities().instabuild) {
|
||||
+ itemInHand.shrink(1);
|
||||
+ }
|
||||
+ return InteractionResult.SUCCESS;
|
||||
+ // Purpur end - Snowman drop and put back pumpkin
|
||||
} else {
|
||||
return InteractionResult.PASS;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
--- a/net/minecraft/world/entity/animal/squid/GlowSquid.java
|
||||
+++ b/net/minecraft/world/entity/animal/squid/GlowSquid.java
|
||||
@@ -30,6 +_,13 @@
|
||||
super(type, level);
|
||||
}
|
||||
|
||||
+ // Purpur start - Flying squids! Oh my!
|
||||
+ @Override
|
||||
+ public boolean canFly() {
|
||||
+ return this.level().purpurConfig.glowSquidsCanFly;
|
||||
+ }
|
||||
+ // Purpur end - Flying squids! Oh my!
|
||||
+
|
||||
@Override
|
||||
protected ParticleOptions getInkParticle() {
|
||||
return ParticleTypes.GLOW_SQUID_INK;
|
||||
@@ -0,0 +1,43 @@
|
||||
--- a/net/minecraft/world/entity/decoration/ArmorStand.java
|
||||
+++ b/net/minecraft/world/entity/decoration/ArmorStand.java
|
||||
@@ -92,10 +_,13 @@
|
||||
public boolean canTickSetByAPI = false;
|
||||
private boolean noTickEquipmentDirty = false;
|
||||
// Paper end - Allow ArmorStands not to tick
|
||||
+ public boolean canMovementTick = true; // Purpur - Movement options for armor stands
|
||||
|
||||
public ArmorStand(final EntityType<? extends ArmorStand> type, final Level level) {
|
||||
super(type, level);
|
||||
if (level != null) this.canTick = level.paperConfig().entities.armorStands.tick; // Paper - Allow ArmorStands not to tick
|
||||
+ if (level != null) this.canMovementTick = level.purpurConfig.armorstandMovement; // Purpur - Movement options for armor stands
|
||||
+ this.setShowArms(level != null && level.purpurConfig.armorstandPlaceWithArms); // Purpur - Config to show Armor Stand arms on spawn
|
||||
}
|
||||
|
||||
public ArmorStand(final Level level, final double x, final double y, final double z) {
|
||||
@@ -521,6 +_,7 @@
|
||||
// Paper start - Allow ArmorStands not to tick
|
||||
@Override
|
||||
public void tick() {
|
||||
+ maxUpStep = level().purpurConfig.armorstandStepHeight; // Purpur - Add option to set armorstand step height
|
||||
if (!this.canTick) {
|
||||
if (this.noTickEquipmentDirty) {
|
||||
this.noTickEquipmentDirty = false;
|
||||
@@ -807,4 +_,18 @@
|
||||
}
|
||||
}
|
||||
// Paper end
|
||||
+
|
||||
+ // Purpur start - Movement options for armor stands
|
||||
+ @Override
|
||||
+ public void updateInWaterStateAndDoWaterCurrentPushing() {
|
||||
+ if (this.level().purpurConfig.armorstandWaterMovement &&
|
||||
+ (this.level().purpurConfig.armorstandWaterFence || !(level().getBlockState(blockPosition().below()).getBlock() instanceof net.minecraft.world.level.block.FenceBlock)))
|
||||
+ super.updateInWaterStateAndDoWaterCurrentPushing();
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void aiStep() {
|
||||
+ if (this.canMovementTick && this.canMove) super.aiStep();
|
||||
+ }
|
||||
+ // Purpur end - Movement options for armor stands
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
--- a/net/minecraft/world/entity/item/PrimedTnt.java
|
||||
+++ b/net/minecraft/world/entity/item/PrimedTnt.java
|
||||
@@ -237,4 +_,32 @@
|
||||
return !this.level().paperConfig().fixes.preventTntFromMovingInWater && super.isPushedByFluid();
|
||||
}
|
||||
// Paper end - Option to prevent TNT from moving in water
|
||||
+
|
||||
+ // Purpur start - Shears can defuse TNT
|
||||
+ @Override
|
||||
+ public net.minecraft.world.InteractionResult interact(net.minecraft.world.entity.player.Player player, net.minecraft.world.InteractionHand hand) {
|
||||
+ Level world = this.level();
|
||||
+
|
||||
+ if (world instanceof ServerLevel serverWorld && level().purpurConfig.shearsCanDefuseTnt) {
|
||||
+ final net.minecraft.world.item.ItemStack inHand = player.getItemInHand(hand);
|
||||
+
|
||||
+ if (!inHand.is(net.minecraft.world.item.Items.SHEARS) || !player.getBukkitEntity().hasPermission("purpur.tnt.defuse") ||
|
||||
+ serverWorld.random.nextFloat() > serverWorld.purpurConfig.shearsCanDefuseTntChance) return net.minecraft.world.InteractionResult.PASS;
|
||||
+
|
||||
+ net.minecraft.world.entity.item.ItemEntity tntItem = new net.minecraft.world.entity.item.ItemEntity(serverWorld, getX(), getY(), getZ(),
|
||||
+ new net.minecraft.world.item.ItemStack(net.minecraft.world.item.Items.TNT));
|
||||
+ tntItem.setPickUpDelay(10);
|
||||
+
|
||||
+ inHand.hurtAndBreak(1, player, hand.asEquipmentSlot());
|
||||
+ serverWorld.addFreshEntity(tntItem, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.CUSTOM);
|
||||
+
|
||||
+ this.playSound(net.minecraft.sounds.SoundEvents.SHEEP_SHEAR);
|
||||
+
|
||||
+ this.kill(serverWorld);
|
||||
+ return net.minecraft.world.InteractionResult.SUCCESS;
|
||||
+ }
|
||||
+
|
||||
+ return super.interact(player, hand);
|
||||
+ }
|
||||
+ // Purpur end - Shears can defuse TNT
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
--- a/net/minecraft/world/entity/monster/EnderMan.java
|
||||
+++ b/net/minecraft/world/entity/monster/EnderMan.java
|
||||
@@ -101,7 +_,7 @@
|
||||
this.goalSelector.addGoal(11, new EnderMan.EndermanTakeBlockGoal(this));
|
||||
this.targetSelector.addGoal(1, new EnderMan.EndermanLookForPlayerGoal(this, this::isAngryAt));
|
||||
this.targetSelector.addGoal(2, new HurtByTargetGoal(this));
|
||||
- this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, Endermite.class, true, false));
|
||||
+ this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, Endermite.class, 10, true, false, (entityliving, ignored) -> entityliving.level().purpurConfig.endermanAggroEndermites && entityliving instanceof Endermite endermite && (!entityliving.level().purpurConfig.endermanAggroEndermitesOnlyIfPlayerSpawned || endermite.isPlayerSpawned()))); // Purpur
|
||||
this.targetSelector.addGoal(4, new ResetUniversalAngerTargetGoal<>(this, false));
|
||||
}
|
||||
|
||||
@@ -220,7 +_,7 @@
|
||||
|
||||
private boolean isBeingStaredBy(final Player player) {
|
||||
// Paper start - EndermanAttackPlayerEvent
|
||||
- final boolean shouldAttack = this.isBeingStaredBy0(player);
|
||||
+ final boolean shouldAttack = !this.level().purpurConfig.endermanDisableStareAggro && this.isBeingStaredBy0(player); // Purpur - Config to ignore Dragon Head wearers and stare aggro
|
||||
final com.destroystokyo.paper.event.entity.EndermanAttackPlayerEvent event = new com.destroystokyo.paper.event.entity.EndermanAttackPlayerEvent((org.bukkit.entity.Enderman) getBukkitEntity(), (org.bukkit.entity.Player) player.getBukkitEntity());
|
||||
event.setCancelled(!shouldAttack);
|
||||
return event.callEvent();
|
||||
@@ -373,6 +_,7 @@
|
||||
public boolean hurtServer(final ServerLevel level, final DamageSource source, final float damage) {
|
||||
if (this.isInvulnerableTo(level, source)) {
|
||||
return false;
|
||||
+ } else if (org.purpurmc.purpur.PurpurConfig.endermanShortHeight && damageSource.is(net.minecraft.world.damagesource.DamageTypes.IN_WALL)) { return false; // Purpur - no suffocation damage if short height - Short enderman height
|
||||
} else {
|
||||
AbstractThrownPotion thrownPotion = source.getDirectEntity() instanceof AbstractThrownPotion potion ? potion : null;
|
||||
if (!source.is(DamageTypeTags.IS_PROJECTILE) && thrownPotion == null) { // Paper - EndermanEscapeEvent - diff on change - below logic relies on this path covering non-projectile damage.
|
||||
@@ -387,6 +_,7 @@
|
||||
} else {
|
||||
boolean hurtWithCleanWater = thrownPotion != null && this.hurtWithCleanWater(level, source, thrownPotion, damage);
|
||||
|
||||
+ if (!flag && level.purpurConfig.endermanIgnoreProjectiles) return super.hurtServer(level, damageSource, amount); // Purpur - Config to disable Enderman teleport on projectile hit
|
||||
if (this.tryEscape(com.destroystokyo.paper.event.entity.EndermanEscapeEvent.Reason.INDIRECT)) { // Paper - EndermanEscapeEvent
|
||||
for (int i = 0; i < 64; i++) {
|
||||
if (this.teleport()) {
|
||||
@@ -430,7 +_,7 @@
|
||||
|
||||
@Override
|
||||
public boolean requiresCustomPersistence() {
|
||||
- return super.requiresCustomPersistence() || this.getCarriedBlock() != null;
|
||||
+ return super.requiresCustomPersistence() || (!this.level().purpurConfig.endermanDespawnEvenWithBlock && this.getCarriedBlock() != null); // Purpur - Add config for allowing Endermen to despawn even while holding a block
|
||||
}
|
||||
|
||||
private static class EndermanFreezeWhenLookedAt extends Goal {
|
||||
@@ -473,6 +_,7 @@
|
||||
|
||||
@Override
|
||||
public boolean canUse() {
|
||||
+ if (!enderman.level().purpurConfig.endermanAllowGriefing) return false; // Purpur - Add enderman and creeper griefing controls
|
||||
return this.enderman.getCarriedBlock() != null
|
||||
&& getServerLevel(this.enderman).getGameRules().get(GameRules.MOB_GRIEFING)
|
||||
&& this.enderman.getRandom().nextInt(reducedTickDelay(2000)) == 0;
|
||||
@@ -616,6 +_,7 @@
|
||||
|
||||
@Override
|
||||
public boolean canUse() {
|
||||
+ if (!enderman.level().purpurConfig.endermanAllowGriefing) return false; // Purpur - Add enderman and creeper griefing controls
|
||||
return this.enderman.getCarriedBlock() == null
|
||||
&& getServerLevel(this.enderman).getGameRules().get(GameRules.MOB_GRIEFING)
|
||||
&& this.enderman.getRandom().nextInt(reducedTickDelay(20)) == 0;
|
||||
@@ -0,0 +1,41 @@
|
||||
--- a/net/minecraft/world/entity/monster/Endermite.java
|
||||
+++ b/net/minecraft/world/entity/monster/Endermite.java
|
||||
@@ -30,12 +_,23 @@
|
||||
private static final int MAX_LIFE = 2400;
|
||||
private static final int DEFAULT_LIFE = 0;
|
||||
public int life = 0;
|
||||
+ private boolean isPlayerSpawned; // Purpur - Add back player spawned endermite API
|
||||
|
||||
public Endermite(final EntityType<? extends Endermite> type, final Level level) {
|
||||
super(type, level);
|
||||
this.xpReward = 3;
|
||||
}
|
||||
|
||||
+ // Purpur start - Add back player spawned endermite API
|
||||
+ public boolean isPlayerSpawned() {
|
||||
+ return this.isPlayerSpawned;
|
||||
+ }
|
||||
+
|
||||
+ public void setPlayerSpawned(boolean playerSpawned) {
|
||||
+ this.isPlayerSpawned = playerSpawned;
|
||||
+ }
|
||||
+ // Purpur end - Add back player spawned endermite API
|
||||
+
|
||||
@Override
|
||||
protected void registerGoals() {
|
||||
this.goalSelector.addGoal(1, new FloatGoal(this));
|
||||
@@ -81,12 +_,14 @@
|
||||
protected void readAdditionalSaveData(final ValueInput input) {
|
||||
super.readAdditionalSaveData(input);
|
||||
this.life = input.getIntOr("Lifetime", 0);
|
||||
+ this.isPlayerSpawned = input.getBooleanOr("PlayerSpawned", false); // Purpur - Add back player spawned endermite API
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addAdditionalSaveData(final ValueOutput output) {
|
||||
super.addAdditionalSaveData(output);
|
||||
output.putInt("Lifetime", this.life);
|
||||
+ output.putBoolean("PlayerSpawned", this.isPlayerSpawned); // Purpur - Add back player spawned endermite API
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -0,0 +1,14 @@
|
||||
--- a/net/minecraft/world/entity/monster/Guardian.java
|
||||
+++ b/net/minecraft/world/entity/monster/Guardian.java
|
||||
@@ -308,6 +_,11 @@
|
||||
final BlockPos pos,
|
||||
final RandomSource random
|
||||
) {
|
||||
+ // Purpur start - Config to disable hostile mob spawn on ice
|
||||
+ if (canSpawnInBlueAndPackedIce(level, pos)) {
|
||||
+ return false;
|
||||
+ }
|
||||
+ // Purpur end - Config to disable hostile mob spawn on ice
|
||||
return (random.nextInt(20) == 0 || !level.canSeeSkyFromBelowWater(pos))
|
||||
&& level.getDifficulty() != Difficulty.PEACEFUL
|
||||
&& (EntitySpawnReason.isSpawner(spawnReason) || level.getFluidState(pos).is(FluidTags.WATER))
|
||||
@@ -0,0 +1,14 @@
|
||||
--- a/net/minecraft/world/entity/monster/MagmaCube.java
|
||||
+++ b/net/minecraft/world/entity/monster/MagmaCube.java
|
||||
@@ -31,6 +_,11 @@
|
||||
public static boolean checkMagmaCubeSpawnRules(
|
||||
final EntityType<MagmaCube> type, final LevelAccessor level, final EntitySpawnReason spawnReason, final BlockPos pos, final RandomSource random
|
||||
) {
|
||||
+ // Purpur start - Config to disable hostile mob spawn on ice
|
||||
+ if (net.minecraft.world.entity.monster.Monster.canSpawnInBlueAndPackedIce(level, pos)) {
|
||||
+ return false;
|
||||
+ }
|
||||
+ // Purpur end - Config to disable hostile mob spawn on ice
|
||||
return level.getDifficulty() != Difficulty.PEACEFUL;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
--- a/net/minecraft/world/entity/monster/Shulker.java
|
||||
+++ b/net/minecraft/world/entity/monster/Shulker.java
|
||||
@@ -81,7 +_,7 @@
|
||||
Vec3i forwardNormal = Direction.SOUTH.getUnitVec3i();
|
||||
return new Vector3f(forwardNormal.getX(), forwardNormal.getY(), forwardNormal.getZ());
|
||||
});
|
||||
- public static final float MAX_SCALE = 3.0F;
|
||||
+ private static final float MAX_SCALE = 3.0F;
|
||||
private float currentPeekAmountO;
|
||||
private float currentPeekAmount;
|
||||
private @Nullable BlockPos clientOldAttachPosition;
|
||||
@@ -0,0 +1,14 @@
|
||||
--- a/net/minecraft/world/entity/monster/Slime.java
|
||||
+++ b/net/minecraft/world/entity/monster/Slime.java
|
||||
@@ -299,6 +_,11 @@
|
||||
public static boolean checkSlimeSpawnRules(
|
||||
final EntityType<Slime> type, final LevelAccessor level, final EntitySpawnReason spawnReason, final BlockPos pos, final RandomSource random
|
||||
) {
|
||||
+ // Purpur start - Config to disable hostile mob spawn on ice
|
||||
+ if (net.minecraft.world.entity.monster.Monster.canSpawnInBlueAndPackedIce(level, pos)) {
|
||||
+ return false;
|
||||
+ }
|
||||
+ // Purpur end - Config to disable hostile mob spawn on ice
|
||||
if (level.getDifficulty() != Difficulty.PEACEFUL) {
|
||||
if (EntitySpawnReason.isSpawner(spawnReason)) {
|
||||
return checkMobSpawnRules(type, level, spawnReason, pos, random);
|
||||
@@ -0,0 +1,14 @@
|
||||
--- a/net/minecraft/world/entity/monster/hoglin/Hoglin.java
|
||||
+++ b/net/minecraft/world/entity/monster/hoglin/Hoglin.java
|
||||
@@ -173,6 +_,11 @@
|
||||
public static boolean checkHoglinSpawnRules(
|
||||
final EntityType<Hoglin> type, final LevelAccessor level, final EntitySpawnReason spawnReason, final BlockPos pos, final RandomSource random
|
||||
) {
|
||||
+ // Purpur start - Config to disable hostile mob spawn on ice
|
||||
+ if (net.minecraft.world.entity.monster.Monster.canSpawnInBlueAndPackedIce(level, pos)) {
|
||||
+ return false;
|
||||
+ }
|
||||
+ // Purpur end - Config to disable hostile mob spawn on ice
|
||||
return !level.getBlockState(pos.below()).is(Blocks.NETHER_WART_BLOCK);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
--- a/net/minecraft/world/entity/monster/piglin/Piglin.java
|
||||
+++ b/net/minecraft/world/entity/monster/piglin/Piglin.java
|
||||
@@ -181,6 +_,11 @@
|
||||
public static boolean checkPiglinSpawnRules(
|
||||
final EntityType<Piglin> type, final LevelAccessor level, final EntitySpawnReason spawnReason, final BlockPos pos, final RandomSource random
|
||||
) {
|
||||
+ // Purpur start - Config to disable hostile mob spawn on ice
|
||||
+ if (canSpawnInBlueAndPackedIce(level, pos)) {
|
||||
+ return false;
|
||||
+ }
|
||||
+ // Purpur end - Config to disable hostile mob spawn on ice
|
||||
return !level.getBlockState(pos.below()).is(Blocks.NETHER_WART_BLOCK);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
--- a/net/minecraft/world/entity/monster/skeleton/Skeleton.java
|
||||
+++ b/net/minecraft/world/entity/monster/skeleton/Skeleton.java
|
||||
@@ -130,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
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
--- a/net/minecraft/world/entity/monster/warden/WardenAi.java
|
||||
+++ b/net/minecraft/world/entity/monster/warden/WardenAi.java
|
||||
@@ -139,15 +_,16 @@
|
||||
return ActivityData.create(
|
||||
Activity.FIGHT,
|
||||
10,
|
||||
- ImmutableList.of(
|
||||
+ ImmutableList.copyOf(java.util.stream.Stream.<BehaviorControl<? super Warden>>of( // Purpur - configurable warden sonic boom
|
||||
DIG_COOLDOWN_SETTER,
|
||||
StopAttackingIfTargetInvalid.<Warden>create(
|
||||
(level, target) -> !body.getAngerLevel().isAngry() || !body.canTargetEntity(target), WardenAi::onTargetInvalid, false
|
||||
),
|
||||
SetEntityLookTarget.create(entity -> isTarget(body, entity), (float)body.getAttributeValue(Attributes.FOLLOW_RANGE)),
|
||||
SetWalkTargetFromAttackTargetIfTargetOutOfReach.create(1.2F),
|
||||
- new SonicBoom(),
|
||||
+ warden.level().purpurConfig.wardenCanUseSonicBoom ? new SonicBoom() : null, // Purpur - configurable warden sonic boom
|
||||
MeleeAttack.create(18)
|
||||
+ ).filter(java.util.Objects::nonNull).toList() // Purpur - configurable warden sonic boom
|
||||
),
|
||||
MemoryModuleType.ATTACK_TARGET
|
||||
);
|
||||
@@ -0,0 +1,32 @@
|
||||
--- a/net/minecraft/world/entity/monster/zombie/Zombie.java
|
||||
+++ b/net/minecraft/world/entity/monster/zombie/Zombie.java
|
||||
@@ -127,7 +_,19 @@
|
||||
this.goalSelector.addGoal(7, new WaterAvoidingRandomStrollGoal(this, 1.0));
|
||||
this.targetSelector.addGoal(1, new HurtByTargetGoal(this).setAlertOthers(ZombifiedPiglin.class));
|
||||
this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, true));
|
||||
- if (this.level().spigotConfig.zombieAggressiveTowardsVillager) this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, AbstractVillager.class, false)); // Spigot
|
||||
+ // Purpur start - Add option to disable zombie aggressiveness towards villagers
|
||||
+ if (this.level().spigotConfig.zombieAggressiveTowardsVillager) this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, AbstractVillager.class, false) { // Spigot
|
||||
+ @Override
|
||||
+ public boolean canUse() {
|
||||
+ return (level().purpurConfig.zombieAggressiveTowardsVillagerWhenLagging || !level().getServer().server.isLagging()) && super.canUse();
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public boolean canContinueToUse() {
|
||||
+ return (level().purpurConfig.zombieAggressiveTowardsVillagerWhenLagging || !level().getServer().server.isLagging()) && super.canContinueToUse();
|
||||
+ }
|
||||
+ });
|
||||
+ // Purpur end - Add option to disable zombie aggressiveness towards villagers
|
||||
this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, IronGolem.class, true));
|
||||
this.targetSelector.addGoal(5, new NearestAttackableTargetGoal<>(this, Turtle.class, 10, true, false, Turtle.BABY_ON_LAND_SELECTOR));
|
||||
}
|
||||
@@ -542,7 +_,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
- if (this.getItemBySlot(EquipmentSlot.HEAD).isEmpty() && SpecialDates.isHalloween() && random.nextFloat() < 0.25F) {
|
||||
+ if (this.getItemBySlot(EquipmentSlot.HEAD).isEmpty() && (level.getLevel().purpurConfig.forceHalloweenSeason || SpecialDates.isHalloween()) && random.nextFloat() < level.getLevel().purpurConfig.chanceHeadHalloweenOnEntity) { // Purpur - Halloween options and optimizations
|
||||
this.setItemSlot(EquipmentSlot.HEAD, new ItemStack(random.nextFloat() < 0.1F ? Blocks.JACK_O_LANTERN : Blocks.CARVED_PUMPKIN));
|
||||
this.setDropChance(EquipmentSlot.HEAD, 0.0F);
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
--- a/net/minecraft/world/entity/monster/zombie/ZombifiedPiglin.java
|
||||
+++ b/net/minecraft/world/entity/monster/zombie/ZombifiedPiglin.java
|
||||
@@ -113,6 +_,12 @@
|
||||
this.maybeAlertOthers();
|
||||
}
|
||||
|
||||
+ // Purpur start - Toggle for Zombified Piglin death always counting as player kill when angry
|
||||
+ if (this.isAngry() && this.level().purpurConfig.zombifiedPiglinCountAsPlayerKillWhenAngry) {
|
||||
+ this.lastHurtByPlayerMemoryTime = this.tickCount;
|
||||
+ }
|
||||
+ // Purpur end - Toggle for Zombified Piglin death always counting as player kill when angry
|
||||
+
|
||||
super.customServerAiStep(level);
|
||||
}
|
||||
|
||||
@@ -160,6 +_,12 @@
|
||||
this.ticksUntilNextAlert = ALERT_INTERVAL.sample(this.random);
|
||||
}
|
||||
|
||||
+ // Purpur start - Toggle for Zombified Piglin death always counting as player kill when angry
|
||||
+ if (target instanceof Player player && this.level().purpurConfig.zombifiedPiglinCountAsPlayerKillWhenAngry) {
|
||||
+ this.setLastHurtByPlayer(player, this.tickCount);
|
||||
+ }
|
||||
+ // Purpur end - Toggle for Zombified Piglin death always counting as player kill when angry
|
||||
+
|
||||
return super.setTarget(target, reason); // CraftBukkit
|
||||
}
|
||||
|
||||
@@ -180,6 +_,11 @@
|
||||
public static boolean checkZombifiedPiglinSpawnRules(
|
||||
final EntityType<ZombifiedPiglin> type, final LevelAccessor level, final EntitySpawnReason spawnReason, final BlockPos pos, final RandomSource random
|
||||
) {
|
||||
+ // Purpur start - Config to disable hostile mob spawn on ice
|
||||
+ if (canSpawnInBlueAndPackedIce(level, pos)) {
|
||||
+ return false;
|
||||
+ }
|
||||
+ // Purpur end - Config to disable hostile mob spawn on ice
|
||||
return level.getDifficulty() != Difficulty.PEACEFUL && !level.getBlockState(pos.below()).is(Blocks.NETHER_WART_BLOCK);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
--- a/net/minecraft/world/entity/player/Player.java
|
||||
+++ b/net/minecraft/world/entity/player/Player.java
|
||||
@@ -1519,7 +_,7 @@
|
||||
}
|
||||
|
||||
@Override
|
||||
- public boolean canGlide() {
|
||||
+ protected boolean canGlide() {
|
||||
return !this.abilities.flying && super.canGlide();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
--- a/net/minecraft/world/entity/projectile/arrow/AbstractArrow.java
|
||||
+++ b/net/minecraft/world/entity/projectile/arrow/AbstractArrow.java
|
||||
@@ -78,6 +_,7 @@
|
||||
private @Nullable List<Entity> piercedAndKilledEntities;
|
||||
public ItemStack pickupItemStack = this.getDefaultPickupItem();
|
||||
public @Nullable ItemStack firedFromWeapon = null;
|
||||
+ public net.minecraft.world.item.enchantment.ItemEnchantments actualEnchantments = net.minecraft.world.item.enchantment.ItemEnchantments.EMPTY; // Purpur - Add an option to fix MC-3304 projectile looting
|
||||
|
||||
protected AbstractArrow(final EntityType<? extends AbstractArrow> type, final Level level) {
|
||||
super(type, level);
|
||||
@@ -605,6 +_,12 @@
|
||||
public @Nullable ItemStack getWeaponItem() {
|
||||
return this.firedFromWeapon;
|
||||
}
|
||||
+
|
||||
+ // Purpur start - Add an option to fix MC-3304 projectile looting
|
||||
+ public void setActualEnchantments(net.minecraft.world.item.enchantment.ItemEnchantments actualEnchantments) {
|
||||
+ this.actualEnchantments = actualEnchantments;
|
||||
+ }
|
||||
+ // Purpur end - Add an option to fix MC-3304 projectile looting
|
||||
|
||||
protected SoundEvent getDefaultHitGroundSoundEvent() {
|
||||
return SoundEvents.ARROW_HIT;
|
||||
@@ -0,0 +1,25 @@
|
||||
--- a/net/minecraft/world/entity/projectile/hurtingprojectile/WitherSkull.java
|
||||
+++ b/net/minecraft/world/entity/projectile/hurtingprojectile/WitherSkull.java
|
||||
@@ -94,7 +_,7 @@
|
||||
super.onHit(hitResult);
|
||||
if (!this.level().isClientSide()) {
|
||||
// CraftBukkit start
|
||||
- org.bukkit.event.entity.ExplosionPrimeEvent event = new org.bukkit.event.entity.ExplosionPrimeEvent(this.getBukkitEntity(), 1.0F, false);
|
||||
+ org.bukkit.event.entity.ExplosionPrimeEvent event = new org.bukkit.event.entity.ExplosionPrimeEvent(this.getBukkitEntity(), this.level().purpurConfig.witherExplosionRadius, false); // Purpur - Config for wither explosion radius
|
||||
if (event.callEvent()) {
|
||||
this.level().explode(this, this.getX(), this.getY(), this.getZ(), event.getRadius(), event.getFire(), Level.ExplosionInteraction.MOB);
|
||||
}
|
||||
@@ -102,6 +_,13 @@
|
||||
this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.HIT); // CraftBukkit - add Bukkit remove cause
|
||||
}
|
||||
}
|
||||
+
|
||||
+ // Purpur start - Add canSaveToDisk to Entity
|
||||
+ @Override
|
||||
+ public boolean canSaveToDisk() {
|
||||
+ return false;
|
||||
+ }
|
||||
+ // Purpur end - Add canSaveToDisk to Entity
|
||||
|
||||
@Override
|
||||
protected void defineSynchedData(final SynchedEntityData.Builder entityData) {
|
||||
@@ -0,0 +1,17 @@
|
||||
--- a/net/minecraft/world/food/FoodProperties.java
|
||||
+++ b/net/minecraft/world/food/FoodProperties.java
|
||||
@@ -42,9 +_,11 @@
|
||||
level.playSound(null, user.getX(), user.getY(), user.getZ(), consumable.sound().value(), SoundSource.NEUTRAL, 1.0F, random.triangle(1.0F, 0.4F));
|
||||
if (user instanceof Player player) {
|
||||
player.getFoodData().eat(this, stack, (net.minecraft.server.level.ServerPlayer) player); // CraftBukkit
|
||||
- level.playSound(
|
||||
- null, player.getX(), player.getY(), player.getZ(), SoundEvents.PLAYER_BURP, SoundSource.PLAYERS, 0.5F, Mth.randomBetween(random, 0.9F, 1.0F)
|
||||
- );
|
||||
+ // Purpur start - Burp delay - moved to Player#tick()
|
||||
+ //level.playSound(
|
||||
+ // null, player.getX(), player.getY(), player.getZ(), SoundEvents.PLAYER_BURP, SoundSource.PLAYERS, 0.5F, Mth.randomBetween(random, 0.9F, 1.0F)
|
||||
+ //);
|
||||
+ // Purpur end - Burp delay - moved to Player#tick()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
--- a/net/minecraft/world/inventory/AbstractContainerMenu.java
|
||||
+++ b/net/minecraft/world/inventory/AbstractContainerMenu.java
|
||||
@@ -64,6 +_,7 @@
|
||||
private final List<ContainerListener> containerListeners = Lists.newArrayList();
|
||||
private @Nullable ContainerSynchronizer synchronizer;
|
||||
private boolean suppressRemoteUpdates;
|
||||
+ @Nullable protected ItemStack activeQuickItem = null; // Purpur - Anvil API
|
||||
// CraftBukkit start
|
||||
public boolean checkReachable = true;
|
||||
public abstract org.bukkit.inventory.InventoryView getBukkitView();
|
||||
@@ -0,0 +1,72 @@
|
||||
--- a/net/minecraft/world/item/AxeItem.java
|
||||
+++ b/net/minecraft/world/item/AxeItem.java
|
||||
@@ -65,13 +_,15 @@
|
||||
if (playerHasBlockingItemUseIntent(context)) {
|
||||
return InteractionResult.PASS;
|
||||
} else {
|
||||
- Optional<BlockState> newBlock = this.evaluateNewBlockState(level, pos, player, level.getBlockState(pos));
|
||||
+ Optional<org.purpurmc.purpur.tool.Actionable> newBlock = this.evaluateActionable(level, pos, player, level.getBlockState(pos)); // Purpur - Tool actionable options
|
||||
if (newBlock.isEmpty()) {
|
||||
return InteractionResult.PASS;
|
||||
} else {
|
||||
+ org.purpurmc.purpur.tool.Actionable actionable = newBlock.get(); // Purpur - Tool actionable options
|
||||
+ BlockState state = actionable.into().withPropertiesOf(level.getBlockState(pos)); // Purpur - Tool actionable options
|
||||
ItemStack itemInHand = context.getItemInHand();
|
||||
// Paper start - EntityChangeBlockEvent
|
||||
- if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(player, pos, newBlock.get())) {
|
||||
+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(player, pos, state)) { // Purpur - Tool actionable options
|
||||
return InteractionResult.PASS;
|
||||
}
|
||||
// Paper end
|
||||
@@ -79,8 +_,15 @@
|
||||
CriteriaTriggers.ITEM_USED_ON_BLOCK.trigger((ServerPlayer)player, pos, itemInHand);
|
||||
}
|
||||
|
||||
- level.setBlock(pos, newBlock.get(), Block.UPDATE_ALL_IMMEDIATE);
|
||||
- level.gameEvent(GameEvent.BLOCK_CHANGE, pos, GameEvent.Context.of(player, newBlock.get()));
|
||||
+ // Purpur start - Tool actionable options
|
||||
+ level.setBlock(pos, state, Block.UPDATE_ALL_IMMEDIATE);
|
||||
+ actionable.drops().forEach((drop, chance) -> {
|
||||
+ if (level.random.nextDouble() < chance) {
|
||||
+ Block.popResourceFromFace(level, pos, context.getClickedFace(), new ItemStack(drop));
|
||||
+ }
|
||||
+ });
|
||||
+ level.gameEvent(GameEvent.BLOCK_CHANGE, pos, GameEvent.Context.of(player, state));
|
||||
+ // Purpur end - Tool actionable options
|
||||
if (player != null) {
|
||||
itemInHand.hurtAndBreak(1, player, context.getHand().asEquipmentSlot());
|
||||
}
|
||||
@@ -97,21 +_,23 @@
|
||||
&& !player.isSecondaryUseActive();
|
||||
}
|
||||
|
||||
- private Optional<BlockState> evaluateNewBlockState(final Level level, final BlockPos pos, final @Nullable Player player, final BlockState oldState) {
|
||||
- Optional<BlockState> strippedBlock = this.getStripped(oldState);
|
||||
- if (strippedBlock.isPresent()) {
|
||||
- level.playSound(player, pos, SoundEvents.AXE_STRIP, SoundSource.BLOCKS, 1.0F, 1.0F);
|
||||
- return strippedBlock;
|
||||
+ private Optional<org.purpurmc.purpur.tool.Actionable> evaluateActionable(final Level level, final BlockPos pos, final @Nullable Player player, final BlockState oldState) { // Purpur - Tool actionable options
|
||||
+ Optional<org.purpurmc.purpur.tool.Actionable> stripped = Optional.ofNullable(level.purpurConfig.axeStrippables.get(oldState.getBlock())); // Purpur - Tool actionable options
|
||||
+ if (stripped.isPresent()) {
|
||||
+ level.playSound(STRIPPABLES.containsKey(oldState.getBlock()) ? player : null, pos, SoundEvents.AXE_STRIP, SoundSource.BLOCKS, 1.0F, 1.0F); // Purpur - force sound
|
||||
+ return stripped;
|
||||
} else {
|
||||
- Optional<BlockState> scrapedBlock = WeatheringCopper.getPrevious(oldState);
|
||||
+ Optional<org.purpurmc.purpur.tool.Actionable> scrapedBlock = Optional.ofNullable(level.purpurConfig.axeWeatherables.get(oldState.getBlock())); // Purpur - Tool actionable options
|
||||
if (scrapedBlock.isPresent()) {
|
||||
- spawnSoundAndParticle(level, pos, player, oldState, SoundEvents.AXE_SCRAPE, LevelEvent.PARTICLES_SCRAPE);
|
||||
+ spawnSoundAndParticle(level, pos, WeatheringCopper.getPrevious(oldState).isPresent() ? player : null, oldState, SoundEvents.AXE_SCRAPE, LevelEvent.PARTICLES_SCRAPE); // Purpur - Tool actionable options - force sound
|
||||
return scrapedBlock;
|
||||
} else {
|
||||
- Optional<BlockState> waxoffBlock = Optional.ofNullable(HoneycombItem.WAX_OFF_BY_BLOCK.get().get(oldState.getBlock()))
|
||||
- .map(b -> b.withPropertiesOf(oldState));
|
||||
+ // Purpur start - Tool actionable options
|
||||
+ Optional<org.purpurmc.purpur.tool.Actionable> waxoffBlock = Optional.ofNullable(level.purpurConfig.axeWaxables.get(oldState.getBlock()));
|
||||
+ // .map(b -> b.withPropertiesOf(oldState));
|
||||
+ // Purpur end - Tool actionable options
|
||||
if (waxoffBlock.isPresent()) {
|
||||
- spawnSoundAndParticle(level, pos, player, oldState, SoundEvents.AXE_WAX_OFF, LevelEvent.PARTICLES_WAX_OFF);
|
||||
+ spawnSoundAndParticle(level, pos, HoneycombItem.WAX_OFF_BY_BLOCK.get().containsKey(oldState.getBlock()) ? player : null, oldState, SoundEvents.AXE_WAX_OFF, LevelEvent.PARTICLES_WAX_OFF); // Purpur - Tool actionable options - force sound
|
||||
return waxoffBlock;
|
||||
} else {
|
||||
return Optional.empty();
|
||||
@@ -0,0 +1,36 @@
|
||||
--- a/net/minecraft/world/item/BlockItem.java
|
||||
+++ b/net/minecraft/world/item/BlockItem.java
|
||||
@@ -139,7 +_,16 @@
|
||||
protected boolean updateCustomBlockEntityTag(
|
||||
final BlockPos pos, final Level level, final @Nullable Player player, final ItemStack itemStack, final BlockState placedState
|
||||
) {
|
||||
- return updateCustomBlockEntityTag(level, player, pos, itemStack);
|
||||
+ // Purpur start - Persistent BlockEntity Lore and DisplayName
|
||||
+ boolean handled = updateCustomBlockEntityTag(level, player, pos, itemStack);
|
||||
+ if (level.purpurConfig.persistentTileEntityLore) {
|
||||
+ BlockEntity blockEntity1 = level.getBlockEntity(pos);
|
||||
+ if (blockEntity1 != null) {
|
||||
+ blockEntity1.setPersistentLore(stack.getOrDefault(DataComponents.LORE, net.minecraft.world.item.component.ItemLore.EMPTY));
|
||||
+ }
|
||||
+ }
|
||||
+ return handled;
|
||||
+ // Purpur end - Persistent BlockEntity Lore and DisplayName
|
||||
}
|
||||
|
||||
protected @Nullable BlockState getPlacementState(final BlockPlaceContext context) {
|
||||
@@ -201,6 +_,7 @@
|
||||
}
|
||||
|
||||
if (!type.onlyOpCanSetNbt() || player != null && (player.canUseGameMasterBlocks() || (player.getAbilities().instabuild && player.getBukkitEntity().hasPermission("minecraft.nbt.place")))) { // Spigot - add permission
|
||||
+ if (!(level.purpurConfig.silkTouchEnabled && blockEntity instanceof net.minecraft.world.level.block.entity.SpawnerBlockEntity && player.getBukkitEntity().hasPermission("purpur.drop.spawners"))) // Purpur - Silk touch spawners
|
||||
return customData.loadInto(blockEntity, level.registryAccess());
|
||||
}
|
||||
|
||||
@@ -241,6 +_,7 @@
|
||||
public void onDestroyed(final ItemEntity entity) {
|
||||
ItemContainerContents container = entity.getItem().set(DataComponents.CONTAINER, ItemContainerContents.EMPTY);
|
||||
if (container != null) {
|
||||
+ if (entity.level().purpurConfig.shulkerBoxItemDropContentsWhenDestroyed && this.getBlock() instanceof ShulkerBoxBlock) // Purpur - option to disable shulker box items from dropping contents when destroyed
|
||||
ItemUtils.onContainerDestroyed(entity, container.nonEmptyItemCopyStream());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
--- a/net/minecraft/world/item/BowItem.java
|
||||
+++ b/net/minecraft/world/item/BowItem.java
|
||||
@@ -28,6 +_,11 @@
|
||||
return false;
|
||||
} else {
|
||||
ItemStack projectile = player.getProjectile(itemStack);
|
||||
+ // Purpur start - Infinity bow settings
|
||||
+ if (level.purpurConfig.infinityWorksWithoutArrows && projectile.isEmpty() && net.minecraft.world.item.enchantment.EnchantmentHelper.getItemEnchantmentLevel(net.minecraft.world.item.enchantment.Enchantments.INFINITY, itemStack) > 0) {
|
||||
+ projectile = new ItemStack(Items.ARROW);
|
||||
+ }
|
||||
+ // Purpur end - Infinity bow settings
|
||||
if (projectile.isEmpty()) {
|
||||
return false;
|
||||
} else {
|
||||
@@ -38,7 +_,7 @@
|
||||
} else {
|
||||
List<ItemStack> firedProjectiles = draw(itemStack, projectile, player);
|
||||
if (level instanceof ServerLevel serverLevel && !firedProjectiles.isEmpty()) {
|
||||
- this.shoot(serverLevel, player, player.getUsedItemHand(), itemStack, firedProjectiles, pow * 3.0F, 1.0F, pow == 1.0F, null, pow); // Paper - Pass draw strength
|
||||
+ this.shoot(serverLevel, player, player.getUsedItemHand(), itemStack, firedProjectiles, pow * 3.0F, (float) serverLevel.purpurConfig.bowProjectileOffset, pow == 1.0F, null, pow); // Paper - Pass draw strength // Purpur - Projectile offset config
|
||||
}
|
||||
|
||||
level.playSound(
|
||||
@@ -95,7 +_,7 @@
|
||||
public InteractionResult use(final Level level, final Player player, final InteractionHand hand) {
|
||||
ItemStack itemStack = player.getItemInHand(hand);
|
||||
boolean foundProjectile = !player.getProjectile(itemStack).isEmpty();
|
||||
- if (!player.hasInfiniteMaterials() && !foundProjectile) {
|
||||
+ if (!player.hasInfiniteMaterials() && !foundProjectile && !(level.purpurConfig.infinityWorksWithoutArrows && net.minecraft.world.item.enchantment.EnchantmentHelper.getItemEnchantmentLevel(net.minecraft.world.item.enchantment.Enchantments.INFINITY, itemStack) > 0)) { // Purpur - Infinity bow settings
|
||||
return InteractionResult.FAIL;
|
||||
} else {
|
||||
player.startUsingItem(hand);
|
||||
@@ -0,0 +1,11 @@
|
||||
--- a/net/minecraft/world/item/BucketItem.java
|
||||
+++ b/net/minecraft/world/item/BucketItem.java
|
||||
@@ -146,7 +_,7 @@
|
||||
// CraftBukkit end
|
||||
if (!canPlaceFluidInsideBlock) {
|
||||
return hitResult != null && this.emptyContents(user, level, hitResult.getBlockPos().relative(hitResult.getDirection()), null, direction, clicked, itemStack, hand); // CraftBukkit
|
||||
- } else if (level.environmentAttributes().getValue(EnvironmentAttributes.WATER_EVAPORATES, pos) && this.content.is(FluidTags.WATER)) {
|
||||
+ } else if ((level.environmentAttributes().getValue(EnvironmentAttributes.WATER_EVAPORATES, pos) || (level.isTheEnd() && !org.purpurmc.purpur.PurpurConfig.allowWaterPlacementInTheEnd)) && this.content.is(FluidTags.WATER)) { // Purpur - Add allow water in end world option
|
||||
int x = pos.getX();
|
||||
int y = pos.getY();
|
||||
int z = pos.getZ();
|
||||
@@ -0,0 +1,11 @@
|
||||
--- a/net/minecraft/world/item/CrossbowItem.java
|
||||
+++ b/net/minecraft/world/item/CrossbowItem.java
|
||||
@@ -66,7 +_,7 @@
|
||||
ItemStack itemStack = player.getItemInHand(hand);
|
||||
ChargedProjectiles chargedProjectiles = itemStack.get(DataComponents.CHARGED_PROJECTILES);
|
||||
if (chargedProjectiles != null && !chargedProjectiles.isEmpty()) {
|
||||
- this.performShooting(level, player, hand, itemStack, getShootingPower(chargedProjectiles), 1.0F, null);
|
||||
+ this.performShooting(level, player, hand, itemStack, getShootingPower(chargedProjectiles), (float) level.purpurConfig.crossbowProjectileOffset, null); // Purpur - Projectile offset config
|
||||
return InteractionResult.CONSUME;
|
||||
} else if (!player.getProjectile(itemStack).isEmpty()) {
|
||||
this.startSoundPlayed = false;
|
||||
@@ -0,0 +1,13 @@
|
||||
--- a/net/minecraft/world/item/DyeColor.java
|
||||
+++ b/net/minecraft/world/item/DyeColor.java
|
||||
@@ -154,4 +_,10 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
+
|
||||
+ // Purpur start - Shulker spawn from bullet options
|
||||
+ public static DyeColor random(net.minecraft.util.RandomSource random) {
|
||||
+ return values()[random.nextInt(values().length)];
|
||||
+ }
|
||||
+ // Purpur end - Shulker spawn from bullet options
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
--- a/net/minecraft/world/item/EggItem.java
|
||||
+++ b/net/minecraft/world/item/EggItem.java
|
||||
@@ -24,7 +_,7 @@
|
||||
public InteractionResult use(final Level level, final Player player, final InteractionHand hand) {
|
||||
ItemStack itemStack = player.getItemInHand(hand);
|
||||
// Paper start
|
||||
- final Projectile.Delayed<ThrownEgg> thrownEgg = Projectile.spawnProjectileFromRotationDelayed(ThrownEgg::new, (ServerLevel) level, itemStack, player, 0.0F, EggItem.PROJECTILE_SHOOT_POWER, 1.0F);
|
||||
+ final Projectile.Delayed<ThrownEgg> thrownEgg = Projectile.spawnProjectileFromRotationDelayed(ThrownEgg::new, (ServerLevel) level, itemStack, player, 0.0F, EggItem.PROJECTILE_SHOOT_POWER, (float) level.purpurConfig.eggProjectileOffset); // Purpur - Projectile offset config
|
||||
com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent event = new com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent((org.bukkit.entity.Player) player.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemStack), (org.bukkit.entity.Projectile) thrownEgg.projectile().getBukkitEntity());
|
||||
if (event.callEvent() && thrownEgg.attemptSpawn()) {
|
||||
if (event.shouldConsume()) {
|
||||
@@ -0,0 +1,11 @@
|
||||
--- a/net/minecraft/world/item/EndCrystalItem.java
|
||||
+++ b/net/minecraft/world/item/EndCrystalItem.java
|
||||
@@ -24,7 +_,7 @@
|
||||
Level level = context.getLevel();
|
||||
BlockPos pos = context.getClickedPos();
|
||||
BlockState blockState = level.getBlockState(pos);
|
||||
- if (!blockState.is(Blocks.OBSIDIAN) && !blockState.is(Blocks.BEDROCK)) {
|
||||
+ if (!level.purpurConfig.endCrystalPlaceAnywhere && !blockState.is(Blocks.OBSIDIAN) && !blockState.is(Blocks.BEDROCK)) { // Purpur - place end crystal on any block
|
||||
return InteractionResult.FAIL;
|
||||
} else {
|
||||
BlockPos above = pos.above();
|
||||
@@ -0,0 +1,19 @@
|
||||
--- a/net/minecraft/world/item/EnderpearlItem.java
|
||||
+++ b/net/minecraft/world/item/EnderpearlItem.java
|
||||
@@ -24,7 +_,7 @@
|
||||
if (level instanceof ServerLevel serverLevel) {
|
||||
// CraftBukkit start
|
||||
// Paper start - PlayerLaunchProjectileEvent
|
||||
- final Projectile.Delayed<ThrownEnderpearl> thrownEnderpearl = Projectile.spawnProjectileFromRotationDelayed(ThrownEnderpearl::new, serverLevel, itemStack, player, 0.0F, EnderpearlItem.PROJECTILE_SHOOT_POWER, 1.0F);
|
||||
+ final Projectile.Delayed<ThrownEnderpearl> thrownEnderpearl = Projectile.spawnProjectileFromRotationDelayed(ThrownEnderpearl::new, serverLevel, itemStack, player, 0.0F, EnderpearlItem.PROJECTILE_SHOOT_POWER, (float) serverLevel.purpurConfig.enderPearlProjectileOffset); // Purpur - Projectile offset config
|
||||
com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent event = new com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent((org.bukkit.entity.Player) player.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemStack), (org.bukkit.entity.Projectile) thrownEnderpearl.projectile().getBukkitEntity());
|
||||
if (event.callEvent() && thrownEnderpearl.attemptSpawn()) {
|
||||
if (event.shouldConsume()) {
|
||||
@@ -44,6 +_,7 @@
|
||||
0.4F / (level.getRandom().nextFloat() * 0.4F + 0.8F)
|
||||
);
|
||||
player.awardStat(Stats.ITEM_USED.get(this));
|
||||
+ player.getCooldowns().addCooldown(itemInHand, player.getAbilities().instabuild ? level.purpurConfig.enderPearlCooldownCreative : level.purpurConfig.enderPearlCooldown); // Purpur - Configurable Ender Pearl cooldown
|
||||
} else {
|
||||
if (player instanceof net.minecraft.server.level.ServerPlayer serverPlayer) {
|
||||
serverPlayer.deregisterEnderPearl(thrownEnderpearl.projectile());
|
||||
@@ -0,0 +1,33 @@
|
||||
--- a/net/minecraft/world/item/HoeItem.java
|
||||
+++ b/net/minecraft/world/item/HoeItem.java
|
||||
@@ -44,15 +_,25 @@
|
||||
public InteractionResult useOn(final UseOnContext context) {
|
||||
Level level = context.getLevel();
|
||||
BlockPos pos = context.getClickedPos();
|
||||
- Pair<Predicate<UseOnContext>, Consumer<UseOnContext>> logicPair = TILLABLES.get(level.getBlockState(pos).getBlock());
|
||||
- if (logicPair == null) {
|
||||
+ // Purpur start - Tool actionable options
|
||||
+ Block clickedBlock = level.getBlockState(pos).getBlock();
|
||||
+ org.purpurmc.purpur.tool.Tillable tillable = level.purpurConfig.hoeTillables.get(clickedBlock);
|
||||
+ if (tillable == null) {
|
||||
return InteractionResult.PASS;
|
||||
} else {
|
||||
- Predicate<UseOnContext> predicate = logicPair.getFirst();
|
||||
- Consumer<UseOnContext> action = logicPair.getSecond();
|
||||
+ Predicate<UseOnContext> predicate = tillable.condition().predicate();
|
||||
+ Consumer<UseOnContext> action = (ctx) -> {
|
||||
+ level.setBlock(pos, tillable.into().defaultBlockState(), 11);
|
||||
+ tillable.drops().forEach((drop, chance) -> {
|
||||
+ if (level.random.nextDouble() < chance) {
|
||||
+ Block.popResourceFromFace(level, pos, ctx.getClickedFace(), new ItemStack(drop));
|
||||
+ }
|
||||
+ });
|
||||
+ };
|
||||
+ // Purpur end - Tool actionable options
|
||||
if (predicate.test(context)) {
|
||||
Player player = context.getPlayer();
|
||||
- level.playSound(player, pos, SoundEvents.HOE_TILL, SoundSource.BLOCKS, 1.0F, 1.0F);
|
||||
+ if (!TILLABLES.containsKey(clickedBlock)) level.playSound(null, pos, SoundEvents.HOE_TILL, SoundSource.BLOCKS, 1.0F, 1.0F); // Purpur - Tool actionable options - force sound
|
||||
if (!level.isClientSide()) {
|
||||
action.accept(context);
|
||||
if (player != null) {
|
||||
@@ -0,0 +1,58 @@
|
||||
--- a/net/minecraft/world/item/ItemStack.java
|
||||
+++ b/net/minecraft/world/item/ItemStack.java
|
||||
@@ -453,6 +_,7 @@
|
||||
// revert back all captured blocks
|
||||
for (org.bukkit.block.BlockState blockstate : blocks) {
|
||||
((org.bukkit.craftbukkit.block.CraftBlockState) blockstate).revertPlace();
|
||||
+ ((org.bukkit.craftbukkit.block.CraftBlock) blockstate.getBlock()).getNMS().getBlock().forgetPlacer(); // Purpur - Store placer on Block when placed
|
||||
}
|
||||
|
||||
SignItem.openSign = null; // SPIGOT-6758 - Reset on early return
|
||||
@@ -476,6 +_,7 @@
|
||||
if (!(block.getBlock() instanceof net.minecraft.world.level.block.BaseEntityBlock)) { // Containers get placed automatically
|
||||
block.onPlace(serverLevel, newPos, oldBlock, true, context);
|
||||
}
|
||||
+ block.getBlock().forgetPlacer(); // Purpur - Store placer on Block when placed
|
||||
|
||||
serverLevel.notifyAndUpdatePhysics(newPos, null, oldBlock, block, serverLevel.getBlockState(newPos), updateFlags, net.minecraft.world.level.block.Block.UPDATE_LIMIT); // send null chunk as chunk.k() returns false by this point
|
||||
}
|
||||
@@ -592,6 +_,26 @@
|
||||
return this.isDamageableItem() && this.getDamageValue() > 0;
|
||||
}
|
||||
|
||||
+ // Purpur start - Add option to mend the most damaged equipment first
|
||||
+ public float getDamagePercent() {
|
||||
+ if (this.has(DataComponents.UNBREAKABLE)) {
|
||||
+ return 0.0F;
|
||||
+ }
|
||||
+
|
||||
+ final int maxDamage = this.getOrDefault(DataComponents.MAX_DAMAGE, 0);
|
||||
+ if (maxDamage == 0) {
|
||||
+ return 0.0F;
|
||||
+ }
|
||||
+
|
||||
+ final int damage = this.getOrDefault(DataComponents.DAMAGE, 0);
|
||||
+ if (damage == 0) {
|
||||
+ return 0.0F;
|
||||
+ }
|
||||
+
|
||||
+ return (float) damage / maxDamage;
|
||||
+ }
|
||||
+ // Purpur end - Add option to mend the most damaged equipment first
|
||||
+
|
||||
public int getDamageValue() {
|
||||
return Mth.clamp(this.getOrDefault(DataComponents.DAMAGE, 0), 0, this.getMaxDamage());
|
||||
}
|
||||
@@ -1241,6 +_,12 @@
|
||||
public boolean isEnchanted() {
|
||||
return !this.getOrDefault(DataComponents.ENCHANTMENTS, ItemEnchantments.EMPTY).isEmpty();
|
||||
}
|
||||
+
|
||||
+ // Purpur start - Config to allow unsafe enchants
|
||||
+ public boolean hasEnchantment(Holder<Enchantment> enchantment) {
|
||||
+ return this.getOrDefault(DataComponents.ENCHANTMENTS, ItemEnchantments.EMPTY).getLevel(enchantment) > 0;
|
||||
+ }
|
||||
+ // Purpur end - Config to allow unsafe enchants
|
||||
|
||||
public ItemEnchantments getEnchantments() {
|
||||
return this.getOrDefault(DataComponents.ENCHANTMENTS, ItemEnchantments.EMPTY);
|
||||
@@ -0,0 +1,20 @@
|
||||
--- a/net/minecraft/world/item/Items.java
|
||||
+++ b/net/minecraft/world/item/Items.java
|
||||
@@ -396,7 +_,7 @@
|
||||
public static final Item PURPUR_BLOCK = registerBlock(Blocks.PURPUR_BLOCK);
|
||||
public static final Item PURPUR_PILLAR = registerBlock(Blocks.PURPUR_PILLAR);
|
||||
public static final Item PURPUR_STAIRS = registerBlock(Blocks.PURPUR_STAIRS);
|
||||
- public static final Item SPAWNER = registerBlock(Blocks.SPAWNER);
|
||||
+ public static final Item SPAWNER = registerBlock(Blocks.SPAWNER, org.purpurmc.purpur.item.SpawnerItem::new, new Item.Properties().rarity(Rarity.EPIC)); // Purpur - Silk touch spawners
|
||||
public static final Item CREAKING_HEART = registerBlock(Blocks.CREAKING_HEART);
|
||||
public static final Item CHEST = registerBlock(Blocks.CHEST, p -> p.component(DataComponents.CONTAINER, ItemContainerContents.EMPTY));
|
||||
public static final Item CRAFTING_TABLE = registerBlock(Blocks.CRAFTING_TABLE);
|
||||
@@ -2077,7 +_,7 @@
|
||||
"sweet_berries", createBlockItemWithCustomItemName(Blocks.SWEET_BERRY_BUSH), new Item.Properties().food(Foods.SWEET_BERRIES)
|
||||
);
|
||||
public static final Item GLOW_BERRIES = registerItem(
|
||||
- "glow_berries", createBlockItemWithCustomItemName(Blocks.CAVE_VINES), new Item.Properties().food(Foods.GLOW_BERRIES)
|
||||
+ "glow_berries", settings -> new org.purpurmc.purpur.item.GlowBerryItem(Blocks.CAVE_VINES, settings.useItemDescriptionPrefix()), new Item.Properties().food(Foods.GLOW_BERRIES) // Purpur - Eating glow berries adds glow effect
|
||||
);
|
||||
public static final Item CAMPFIRE = registerBlock(Blocks.CAMPFIRE, p -> p.component(DataComponents.CONTAINER, ItemContainerContents.EMPTY));
|
||||
public static final Item SOUL_CAMPFIRE = registerBlock(Blocks.SOUL_CAMPFIRE, p -> p.component(DataComponents.CONTAINER, ItemContainerContents.EMPTY));
|
||||
@@ -0,0 +1,10 @@
|
||||
--- a/net/minecraft/world/item/MapItem.java
|
||||
+++ b/net/minecraft/world/item/MapItem.java
|
||||
@@ -202,6 +_,7 @@
|
||||
public static void renderBiomePreviewMap(final ServerLevel level, final ItemStack mapItemStack) {
|
||||
MapItemSavedData data = getSavedData(mapItemStack, level);
|
||||
if (data != null) {
|
||||
+ data.isExplorerMap = true; // Purpur - Explorer Map API
|
||||
if (level.dimension() == data.dimension) {
|
||||
int scale = 1 << data.scale;
|
||||
int centerX = data.centerX;
|
||||
@@ -0,0 +1,10 @@
|
||||
--- a/net/minecraft/world/item/NameTagItem.java
|
||||
+++ b/net/minecraft/world/item/NameTagItem.java
|
||||
@@ -24,6 +_,7 @@
|
||||
|
||||
LivingEntity newEntity = ((org.bukkit.craftbukkit.entity.CraftLivingEntity) event.getEntity()).getHandle();
|
||||
newEntity.setCustomName(event.getName() != null ? io.papermc.paper.adventure.PaperAdventure.asVanilla(event.getName()) : null);
|
||||
+ if (player.level().purpurConfig.armorstandFixNametags && target instanceof net.minecraft.world.entity.decoration.ArmorStand) target.setCustomNameVisible(true); // Purpur - Set name visible when using a Name Tag on an Armor Stand
|
||||
if (event.isPersistent() && newEntity instanceof Mob mob) {
|
||||
// Paper end - Add PlayerNameEntityEvent
|
||||
mob.setPersistenceRequired();
|
||||
@@ -0,0 +1,11 @@
|
||||
--- a/net/minecraft/world/item/ProjectileWeaponItem.java
|
||||
+++ b/net/minecraft/world/item/ProjectileWeaponItem.java
|
||||
@@ -114,6 +_,8 @@
|
||||
arrowx.setCritArrow(true);
|
||||
}
|
||||
|
||||
+ arrowx.setActualEnchantments(weapon.getEnchantments()); // Purpur - Add an option to fix MC-3304 projectile looting
|
||||
+
|
||||
return arrowx;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
--- a/net/minecraft/world/item/ShovelItem.java
|
||||
+++ b/net/minecraft/world/item/ShovelItem.java
|
||||
@@ -46,9 +_,12 @@
|
||||
BlockState newState = FLATTENABLES.get(blockState.getBlock());
|
||||
BlockState updatedState = null;
|
||||
Runnable afterAction = null; // Paper
|
||||
+ org.purpurmc.purpur.tool.Flattenable flattenable = level.purpurConfig.shovelFlattenables.get(blockState.getBlock()); // Purpur - Tool actionable options
|
||||
if (newState != null && level.getBlockState(pos.above()).isAir()) {
|
||||
- afterAction = () -> level.playSound(player, pos, SoundEvents.SHOVEL_FLATTEN, SoundSource.BLOCKS, 1.0F, 1.0F); // Paper
|
||||
- updatedState = newState;
|
||||
+ // Purpur start - Tool actionable options
|
||||
+ afterAction = () -> {if (!FLATTENABLES.containsKey(blockState.getBlock())) level.playSound(player, pos, SoundEvents.SHOVEL_FLATTEN, SoundSource.BLOCKS, 1.0F, 1.0F);}; // Paper
|
||||
+ updatedState = flattenable.into().defaultBlockState();
|
||||
+ // Purpur end - Tool actionable options
|
||||
} else if (blockState.getBlock() instanceof CampfireBlock && blockState.getValue(CampfireBlock.LIT)) {
|
||||
afterAction = () -> { // Paper
|
||||
if (!level.isClientSide()) {
|
||||
@@ -0,0 +1,11 @@
|
||||
--- a/net/minecraft/world/item/SnowballItem.java
|
||||
+++ b/net/minecraft/world/item/SnowballItem.java
|
||||
@@ -26,7 +_,7 @@
|
||||
// CraftBukkit start - moved down
|
||||
if (level instanceof ServerLevel serverLevel) {
|
||||
// Paper start - PlayerLaunchProjectileEvent
|
||||
- final Projectile.Delayed<Snowball> snowball = Projectile.spawnProjectileFromRotationDelayed(Snowball::new, serverLevel, itemStack, player, 0.0F, SnowballItem.PROJECTILE_SHOOT_POWER, 1.0F);
|
||||
+ final Projectile.Delayed<Snowball> snowball = Projectile.spawnProjectileFromRotationDelayed(Snowball::new, serverLevel, itemStack, player, 0.0F, SnowballItem.PROJECTILE_SHOOT_POWER, (float) serverLevel.purpurConfig.snowballProjectileOffset); // Purpur - Projectile offset config
|
||||
com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent event = new com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent((org.bukkit.entity.Player) player.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemStack), (org.bukkit.entity.Projectile) snowball.projectile().getBukkitEntity());
|
||||
if (event.callEvent() && snowball.attemptSpawn()) {
|
||||
player.awardStat(Stats.ITEM_USED.get(this));
|
||||
@@ -0,0 +1,26 @@
|
||||
--- a/net/minecraft/world/item/SpawnEggItem.java
|
||||
+++ b/net/minecraft/world/item/SpawnEggItem.java
|
||||
@@ -63,6 +_,23 @@
|
||||
return InteractionResult.FAIL;
|
||||
} else {
|
||||
if (level.paperConfig().entities.spawning.disableMobSpawnerSpawnEggTransformation) return InteractionResult.FAIL; // Paper - Allow disabling mob spawner spawn egg transformation
|
||||
+ // Purpur start - PlayerSetSpawnerTypeWithEggEvent
|
||||
+ if (spawnerHolder instanceof net.minecraft.world.level.block.entity.SpawnerBlockEntity) {
|
||||
+ org.bukkit.block.Block bukkitBlock = level.getWorld().getBlockAt(pos.getX(), pos.getY(), pos.getZ());
|
||||
+ org.purpurmc.purpur.event.PlayerSetSpawnerTypeWithEggEvent event = new org.purpurmc.purpur.event.PlayerSetSpawnerTypeWithEggEvent((org.bukkit.entity.Player) context.getPlayer().getBukkitEntity(), bukkitBlock, (org.bukkit.block.CreatureSpawner) bukkitBlock.getState(), org.bukkit.entity.EntityType.fromName(type.getName()));
|
||||
+ if (!event.callEvent()) {
|
||||
+ return InteractionResult.FAIL;
|
||||
+ }
|
||||
+ type = EntityType.getFromBukkitType(event.getEntityType());
|
||||
+ } else if (spawnerHolder instanceof net.minecraft.world.level.block.entity.TrialSpawnerBlockEntity) {
|
||||
+ org.bukkit.block.Block bukkitBlock = level.getWorld().getBlockAt(pos.getX(), pos.getY(), pos.getZ());
|
||||
+ org.purpurmc.purpur.event.PlayerSetTrialSpawnerTypeWithEggEvent event = new org.purpurmc.purpur.event.PlayerSetTrialSpawnerTypeWithEggEvent((org.bukkit.entity.Player) context.getPlayer().getBukkitEntity(), bukkitBlock, (org.bukkit.block.TrialSpawner) bukkitBlock.getState(), org.bukkit.entity.EntityType.fromName(type.getName()));
|
||||
+ if (!event.callEvent()) {
|
||||
+ return InteractionResult.FAIL;
|
||||
+ }
|
||||
+ type = EntityType.getFromBukkitType(event.getEntityType());
|
||||
+ }
|
||||
+ // Purpur end - PlayerSetSpawnerTypeWithEggEvent
|
||||
spawnerHolder.setEntityId(type, level.getRandom());
|
||||
level.sendBlockUpdated(pos, blockState, blockState, Block.UPDATE_ALL);
|
||||
level.gameEvent(context.getPlayer(), GameEvent.BLOCK_CHANGE, pos);
|
||||
@@ -0,0 +1,11 @@
|
||||
--- a/net/minecraft/world/item/ThrowablePotionItem.java
|
||||
+++ b/net/minecraft/world/item/ThrowablePotionItem.java
|
||||
@@ -24,7 +_,7 @@
|
||||
ItemStack itemStack = player.getItemInHand(hand);
|
||||
if (level instanceof ServerLevel serverLevel) {
|
||||
// Paper start - PlayerLaunchProjectileEvent
|
||||
- final Projectile.Delayed<AbstractThrownPotion> thrownPotion = Projectile.spawnProjectileFromRotationDelayed(this::createPotion, serverLevel, itemStack, player, -20.0F, 0.5F, 1.0F);
|
||||
+ final Projectile.Delayed<AbstractThrownPotion> thrownPotion = Projectile.spawnProjectileFromRotationDelayed(this::createPotion, serverLevel, itemStack, player, -20.0F, 0.5F, (float) serverLevel.purpurConfig.throwablePotionProjectileOffset); // Purpur - Projectile offset config
|
||||
com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent event = new com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent((org.bukkit.entity.Player) player.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemStack), (org.bukkit.entity.Projectile) thrownPotion.projectile().getBukkitEntity());
|
||||
if (event.callEvent() && thrownPotion.attemptSpawn()) {
|
||||
if (event.shouldConsume()) {
|
||||
@@ -0,0 +1,19 @@
|
||||
--- a/net/minecraft/world/item/TridentItem.java
|
||||
+++ b/net/minecraft/world/item/TridentItem.java
|
||||
@@ -82,7 +_,7 @@
|
||||
if (riptideStrength == 0.0F) {
|
||||
ItemStack thrownItemStack = itemStack.copyWithCount(1); // Paper
|
||||
Projectile.Delayed<ThrownTrident> tridentDelayed = Projectile.spawnProjectileFromRotationDelayed( // Paper - PlayerLaunchProjectileEvent(
|
||||
- ThrownTrident::new, serverLevel, thrownItemStack, player, 0.0F, 2.5F, 1.0F
|
||||
+ ThrownTrident::new, serverLevel, thrownItemStack, player, 0.0F, 2.5F, (float) serverLevel.purpurConfig.tridentProjectileOffset // Purpur - Projectile offset config
|
||||
);
|
||||
// Paper start - PlayerLaunchProjectileEvent
|
||||
com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent event = new com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent((org.bukkit.entity.Player) player.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemStack), (org.bukkit.entity.Projectile) tridentDelayed.projectile().getBukkitEntity());
|
||||
@@ -92,6 +_,7 @@
|
||||
return false;
|
||||
}
|
||||
ThrownTrident trident = tridentDelayed.projectile(); // Paper - PlayerLaunchProjectileEvent
|
||||
+ trident.setActualEnchantments(itemStack.getEnchantments()); // Purpur - Add an option to fix MC-3304 projectile looting
|
||||
if (event.shouldConsume()) {
|
||||
thrownItemStack.hurtWithoutBreaking(1, player); // Paper - PlayerLaunchProjectileEvent - use thrownItemStack; pickup item damage
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
--- a/net/minecraft/world/item/consume_effects/ClearAllStatusEffectsConsumeEffect.java
|
||||
+++ b/net/minecraft/world/item/consume_effects/ClearAllStatusEffectsConsumeEffect.java
|
||||
@@ -20,6 +_,12 @@
|
||||
@Override
|
||||
// CraftBukkit start
|
||||
public boolean apply(final Level level, final ItemStack stack, final LivingEntity user, org.bukkit.event.entity.EntityPotionEffectEvent.Cause cause) {
|
||||
+ // Purpur start - Option to toggle milk curing bad omen
|
||||
+ net.minecraft.world.effect.MobEffectInstance badOmen = user.getEffect(net.minecraft.world.effect.MobEffects.BAD_OMEN);
|
||||
+ if (!level.purpurConfig.milkCuresBadOmen && stack.is(net.minecraft.world.item.Items.MILK_BUCKET) && badOmen != null) {
|
||||
+ return user.removeAllEffects(cause) && user.addEffect(badOmen);
|
||||
+ }
|
||||
+ // Purpur end - Option to toggle milk curing bad omen
|
||||
return user.removeAllEffects(cause);
|
||||
// CraftBukkit end
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
--- a/net/minecraft/world/item/crafting/Ingredient.java
|
||||
+++ b/net/minecraft/world/item/crafting/Ingredient.java
|
||||
@@ -35,6 +_,7 @@
|
||||
public final HolderSet<Item> values;
|
||||
// CraftBukkit start
|
||||
private java.util.@org.jspecify.annotations.Nullable List<ItemStack> itemStacks;
|
||||
+ public Predicate<org.bukkit.inventory.ItemStack> predicate; // Purpur - Add predicate to recipe's ExactChoice ingredient
|
||||
|
||||
public boolean isExact() {
|
||||
return this.itemStacks != null;
|
||||
@@ -88,6 +_,11 @@
|
||||
return false;
|
||||
}
|
||||
// CraftBukkit end
|
||||
+ // Purpur start - Add predicate to recipe's ExactChoice ingredient
|
||||
+ if (predicate != null) {
|
||||
+ return predicate.test(input.asBukkitCopy());
|
||||
+ }
|
||||
+ // Purpur end - Add predicate to recipe's ExactChoice ingredient
|
||||
return input.is(this.values);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
--- a/net/minecraft/world/item/enchantment/EnchantmentHelper.java
|
||||
+++ b/net/minecraft/world/item/enchantment/EnchantmentHelper.java
|
||||
@@ -643,4 +_,58 @@
|
||||
private interface EnchantmentVisitor {
|
||||
void accept(Holder<Enchantment> enchantment, int level);
|
||||
}
|
||||
+
|
||||
+ // Purpur start - Enchantment convenience methods
|
||||
+ public static Holder.Reference<Enchantment> getEnchantmentHolder(ResourceKey<Enchantment> enchantment) {
|
||||
+ return net.minecraft.server.MinecraftServer.getServer().registryAccess().lookupOrThrow(Registries.ENCHANTMENT).getOrThrow(enchantment);
|
||||
+ }
|
||||
+
|
||||
+ public static int getItemEnchantmentLevel(ResourceKey<Enchantment> enchantment, ItemStack stack) {
|
||||
+ return getItemEnchantmentLevel(getEnchantmentHolder(enchantment), stack);
|
||||
+ }
|
||||
+ // Purpur end - Enchantment convenience methods
|
||||
+
|
||||
+ // Purpur start - Add option to mend the most damaged equipment first
|
||||
+ public static Optional<EnchantedItemInUse> getMostDamagedItemWith(DataComponentType<?> componentType, LivingEntity entity) {
|
||||
+ ItemStack maxStack = null;
|
||||
+ EquipmentSlot maxSlot = null;
|
||||
+ float maxPercent = 0.0F;
|
||||
+
|
||||
+ equipmentSlotLoop:
|
||||
+ for (EquipmentSlot equipmentSlot : EquipmentSlot.values()) {
|
||||
+ ItemStack stack = entity.getItemBySlot(equipmentSlot);
|
||||
+
|
||||
+ // do not even check enchantments for item with lower or equal damage percent
|
||||
+ float percent = stack.getDamagePercent();
|
||||
+ if (percent <= maxPercent) {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ ItemEnchantments itemEnchantments = stack.getOrDefault(DataComponents.ENCHANTMENTS, ItemEnchantments.EMPTY);
|
||||
+
|
||||
+ for (Entry<Holder<Enchantment>> entry : itemEnchantments.entrySet()) {
|
||||
+ Enchantment enchantment = entry.getKey().value();
|
||||
+
|
||||
+ net.minecraft.core.component.DataComponentMap effects = enchantment.effects();
|
||||
+ if (!effects.has(componentType)) {
|
||||
+ // try with another enchantment
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ if (enchantment.matchingSlot(equipmentSlot)) {
|
||||
+ maxStack = stack;
|
||||
+ maxSlot = equipmentSlot;
|
||||
+ maxPercent = percent;
|
||||
+
|
||||
+ // check another slot now
|
||||
+ continue equipmentSlotLoop;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return maxStack != null
|
||||
+ ? Optional.of(new EnchantedItemInUse(maxStack, maxSlot, entity))
|
||||
+ : Optional.empty();
|
||||
+ }
|
||||
+ // Purpur end - Add option to mend the most damaged equipment first
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
--- a/net/minecraft/world/item/enchantment/ItemEnchantments.java
|
||||
+++ b/net/minecraft/world/item/enchantment/ItemEnchantments.java
|
||||
@@ -32,7 +_,7 @@
|
||||
private static final java.util.Comparator<Holder<Enchantment>> ENCHANTMENT_ORDER = java.util.Comparator.comparing(Holder::getRegisteredName);
|
||||
public static final ItemEnchantments EMPTY = new ItemEnchantments(new it.unimi.dsi.fastutil.objects.Object2IntAVLTreeMap<>(ENCHANTMENT_ORDER));
|
||||
// Paper end - sort enchantments
|
||||
- private static final Codec<Integer> LEVEL_CODEC = Codec.intRange(1, 255);
|
||||
+ private static final Codec<Integer> LEVEL_CODEC = Codec.intRange(1, (org.purpurmc.purpur.PurpurConfig.clampEnchantLevels ? 255 : 32767)); // Purpur - Add toggle for enchant level clamping
|
||||
public static final Codec<ItemEnchantments> CODEC = Codec.unboundedMap(Enchantment.CODEC, LEVEL_CODEC)
|
||||
.xmap(
|
||||
map -> new net.minecraft.world.item.enchantment.ItemEnchantments(net.minecraft.util.Util.make(new it.unimi.dsi.fastutil.objects.Object2IntAVLTreeMap<>(ENCHANTMENT_ORDER), m -> m.putAll(map))), // Paper - sort enchantments
|
||||
@@ -48,7 +_,7 @@
|
||||
|
||||
for (Entry<Holder<Enchantment>> entry : enchantments.object2IntEntrySet()) {
|
||||
int level = entry.getIntValue();
|
||||
- if (level < 0 || level > 255) {
|
||||
+ if (level < 0 || level > (org.purpurmc.purpur.PurpurConfig.clampEnchantLevels ? 255 : 32767)) { // Purpur - Add toggle for enchant level clamping
|
||||
throw new IllegalArgumentException("Enchantment " + entry.getKey() + " has invalid level " + level);
|
||||
}
|
||||
}
|
||||
@@ -135,13 +_,13 @@
|
||||
if (level <= 0) {
|
||||
this.enchantments.removeInt(enchantment);
|
||||
} else {
|
||||
- this.enchantments.put(enchantment, Math.min(level, 255));
|
||||
+ this.enchantments.put(enchantment, Math.min(level, (org.purpurmc.purpur.PurpurConfig.clampEnchantLevels ? 255 : 32767))); // Purpur - Add toggle for enchant level clamping
|
||||
}
|
||||
}
|
||||
|
||||
public void upgrade(final Holder<Enchantment> enchantment, final int level) {
|
||||
if (level > 0) {
|
||||
- this.enchantments.merge(enchantment, Math.min(level, 255), Integer::max);
|
||||
+ this.enchantments.merge(enchantment, Math.min(level, (org.purpurmc.purpur.PurpurConfig.clampEnchantLevels ? 255 : 32767)), Integer::max); // Purpur - Add toggle for enchant level clamping
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
--- a/net/minecraft/world/item/trading/MerchantOffer.java
|
||||
+++ b/net/minecraft/world/item/trading/MerchantOffer.java
|
||||
@@ -162,8 +_,13 @@
|
||||
}
|
||||
|
||||
public void updateDemand() {
|
||||
+ // Purpur start - Configurable minimum demand for trades
|
||||
+ this.updateDemand(0);
|
||||
+ }
|
||||
+ public void updateDemand(int minimumDemand) {
|
||||
+ // Purpur end - Configurable minimum demand for trades
|
||||
this.demand = this.demand + this.uses - (this.maxUses - this.uses);
|
||||
- if (io.papermc.paper.configuration.GlobalConfiguration.get().misc.preventNegativeVillagerDemand) this.demand = Math.max(0, this.demand); // Paper - Fix MC-163962
|
||||
+ if (io.papermc.paper.configuration.GlobalConfiguration.get().misc.preventNegativeVillagerDemand) this.demand = Math.max(minimumDemand, this.demand); // Paper - Fix MC-163962 // Purpur - Configurable minimum demand for trades
|
||||
}
|
||||
|
||||
public ItemStack assemble() {
|
||||
@@ -0,0 +1,10 @@
|
||||
--- a/net/minecraft/world/level/BaseSpawner.java
|
||||
+++ b/net/minecraft/world/level/BaseSpawner.java
|
||||
@@ -60,6 +_,7 @@
|
||||
}
|
||||
|
||||
public boolean isNearPlayer(final Level level, final BlockPos pos) {
|
||||
+ if (level.purpurConfig.spawnerDeactivateByRedstone && level.hasNeighborSignal(pos)) return false; // Purpur - Redstone deactivates spawners
|
||||
return level.hasNearbyAlivePlayerThatAffectsSpawning(pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5, this.requiredPlayerRange); // Paper - Affects Spawning API
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
--- a/net/minecraft/world/level/EntityGetter.java
|
||||
+++ b/net/minecraft/world/level/EntityGetter.java
|
||||
@@ -146,7 +_,7 @@
|
||||
|
||||
default boolean hasNearbyAlivePlayer(final double x, final double y, final double z, final double range) {
|
||||
for (Player player : this.players()) {
|
||||
- if (EntitySelector.NO_SPECTATORS.test(player) && EntitySelector.LIVING_ENTITY_STILL_ALIVE.test(player)) {
|
||||
+ if (EntitySelector.NO_SPECTATORS.test(player) && EntitySelector.LIVING_ENTITY_STILL_ALIVE.test(player) && EntitySelector.notAfk.test(player)) { // Purpur - AFK API
|
||||
double playerDist = player.distanceToSqr(x, y, z);
|
||||
if (range < 0.0 || playerDist < range * range) {
|
||||
return true;
|
||||
@@ -0,0 +1,81 @@
|
||||
--- a/net/minecraft/world/level/Level.java
|
||||
+++ b/net/minecraft/world/level/Level.java
|
||||
@@ -162,10 +_,54 @@
|
||||
}
|
||||
// Paper end - add paper world config
|
||||
|
||||
+ public final org.purpurmc.purpur.PurpurWorldConfig purpurConfig; // Purpur - Purpur config files
|
||||
public static @Nullable BlockPos lastPhysicsProblem; // Spigot
|
||||
public final Map<ServerExplosion.CacheKey, Float> explosionDensityCache = new java.util.HashMap<>(); // Paper - Optimize explosions
|
||||
public java.util.ArrayDeque<net.minecraft.world.level.block.RedstoneTorchBlock.Toggle> redstoneUpdateInfos; // Paper - Faster redstone torch rapid clock removal; Move from Map in BlockRedstoneTorch to here
|
||||
|
||||
+ // Purpur start - Add adjustable breeding cooldown to config
|
||||
+ private com.google.common.cache.Cache<BreedingCooldownPair, Object> playerBreedingCooldowns;
|
||||
+
|
||||
+ private com.google.common.cache.Cache<BreedingCooldownPair, Object> getNewBreedingCooldownCache() {
|
||||
+ return com.google.common.cache.CacheBuilder.newBuilder().expireAfterWrite(this.purpurConfig.animalBreedingCooldownSeconds, java.util.concurrent.TimeUnit.SECONDS).build();
|
||||
+ }
|
||||
+
|
||||
+ public void resetBreedingCooldowns() {
|
||||
+ this.playerBreedingCooldowns = this.getNewBreedingCooldownCache();
|
||||
+ }
|
||||
+
|
||||
+ public boolean hasBreedingCooldown(java.util.UUID player, Class<? extends net.minecraft.world.entity.animal.Animal> animalType) { // Purpur
|
||||
+ return this.playerBreedingCooldowns.getIfPresent(new BreedingCooldownPair(player, animalType)) != null;
|
||||
+ }
|
||||
+
|
||||
+ public void addBreedingCooldown(java.util.UUID player, Class<? extends net.minecraft.world.entity.animal.Animal> animalType) {
|
||||
+ this.playerBreedingCooldowns.put(new BreedingCooldownPair(player, animalType), new Object());
|
||||
+ }
|
||||
+
|
||||
+ private static final class BreedingCooldownPair {
|
||||
+ private final java.util.UUID playerUUID;
|
||||
+ private final Class<? extends net.minecraft.world.entity.animal.Animal> animalType;
|
||||
+
|
||||
+ public BreedingCooldownPair(java.util.UUID playerUUID, Class<? extends net.minecraft.world.entity.animal.Animal> animalType) {
|
||||
+ this.playerUUID = playerUUID;
|
||||
+ this.animalType = animalType;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public boolean equals(Object o) {
|
||||
+ if (this == o) return true;
|
||||
+ if (o == null || getClass() != o.getClass()) return false;
|
||||
+ BreedingCooldownPair that = (BreedingCooldownPair) o;
|
||||
+ return playerUUID.equals(that.playerUUID) && animalType.equals(that.animalType);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public int hashCode() {
|
||||
+ return java.util.Objects.hash(playerUUID, animalType);
|
||||
+ }
|
||||
+ }
|
||||
+ // Purpur end - Add adjustable breeding cooldown to config
|
||||
+
|
||||
public CraftWorld getWorld() {
|
||||
return this.world;
|
||||
}
|
||||
@@ -208,6 +_,8 @@
|
||||
) {
|
||||
this.spigotConfig = new org.spigotmc.SpigotWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) levelData).getLevelName()); // Spigot
|
||||
this.paperConfig = paperWorldConfigCreator.apply(this.spigotConfig); // Paper - create paper world config
|
||||
+ this.purpurConfig = new org.purpurmc.purpur.PurpurWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) levelData).getLevelName(), environment); // Purpur - Purpur config files
|
||||
+ this.playerBreedingCooldowns = this.getNewBreedingCooldownCache(); // Purpur - Add adjustable breeding cooldown to config
|
||||
this.generator = generator;
|
||||
this.world = new CraftWorld((ServerLevel) this, generator, biomeProvider, environment);
|
||||
|
||||
@@ -1472,4 +_,14 @@
|
||||
return ret;
|
||||
}
|
||||
// Paper end - allow patching this logic
|
||||
+
|
||||
+ // Purpur start - Add allow water in end world option
|
||||
+ public boolean isNether() {
|
||||
+ return getWorld().getEnvironment() == org.bukkit.World.Environment.NETHER;
|
||||
+ }
|
||||
+
|
||||
+ public boolean isTheEnd() {
|
||||
+ return getWorld().getEnvironment() == org.bukkit.World.Environment.THE_END;
|
||||
+ }
|
||||
+ // Purpur end - Add allow water in end world option
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
--- a/net/minecraft/world/level/NaturalSpawner.java
|
||||
+++ b/net/minecraft/world/level/NaturalSpawner.java
|
||||
@@ -216,7 +_,7 @@
|
||||
pos.set(x, yStart, z);
|
||||
double xx = x + 0.5;
|
||||
double zz = z + 0.5;
|
||||
- Player nearestPlayer = level.getNearestPlayer(xx, yStart, zz, -1.0, false);
|
||||
+ Player nearestPlayer = level.getNearestPlayer(xx, yStart, zz, -1.0, level.purpurConfig.mobSpawningIgnoreCreativePlayers); // Purpur - mob spawning option to ignore creative players
|
||||
if (nearestPlayer != null) {
|
||||
double nearestPlayerDistanceSqr = nearestPlayer.distanceToSqr(xx, yStart, zz);
|
||||
if (level.isLoadedAndInBounds(pos) && isRightDistanceToPlayerAndSpawnPoint(level, chunk, pos, nearestPlayerDistanceSqr)) { // Paper - don't load chunks for mob spawn
|
||||
@@ -0,0 +1,26 @@
|
||||
--- a/net/minecraft/world/level/ServerExplosion.java
|
||||
+++ b/net/minecraft/world/level/ServerExplosion.java
|
||||
@@ -340,6 +_,23 @@
|
||||
}
|
||||
|
||||
public int explode() {
|
||||
+ // Purpur start - add PreExplodeEvents
|
||||
+ if (this.source != null) {
|
||||
+ org.bukkit.Location location = new org.bukkit.Location(this.level.getWorld(), this.center.x, this.center.y, this.center.z);
|
||||
+ if(!new org.purpurmc.purpur.event.entity.PreEntityExplodeEvent(this.source.getBukkitEntity(), location, this.blockInteraction == Explosion.BlockInteraction.DESTROY_WITH_DECAY ? 1.0F / this.radius : 1.0F, org.bukkit.craftbukkit.CraftExplosionResult.toExplosionResult(getBlockInteraction())).callEvent()) {
|
||||
+ this.wasCanceled = true;
|
||||
+ return 0;
|
||||
+ }
|
||||
+ } else {
|
||||
+ org.bukkit.Location location = new org.bukkit.Location(this.level.getWorld(), this.center.x, this.center.y, this.center.z);
|
||||
+ org.bukkit.block.Block block = location.getBlock();
|
||||
+ org.bukkit.block.BlockState blockState = (this.damageSource.causingBlockSnapshot() != null) ? this.damageSource.causingBlockSnapshot() : block.getState();
|
||||
+ if(!new org.purpurmc.purpur.event.PreBlockExplodeEvent(location.getBlock(), this.blockInteraction == Explosion.BlockInteraction.DESTROY_WITH_DECAY ? 1.0F / this.radius : 1.0F, blockState, org.bukkit.craftbukkit.CraftExplosionResult.toExplosionResult(getBlockInteraction())).callEvent()) {
|
||||
+ this.wasCanceled = true;
|
||||
+ return 0;
|
||||
+ }
|
||||
+ }
|
||||
+ // Purpur end - Add PreExplodeEvents
|
||||
this.level.gameEvent(this.source, GameEvent.EXPLODE, this.center);
|
||||
List<BlockPos> toBlow = this.calculateExplodedPositions();
|
||||
this.hurtEntities();
|
||||
@@ -0,0 +1,56 @@
|
||||
--- a/net/minecraft/world/level/block/AnvilBlock.java
|
||||
+++ b/net/minecraft/world/level/block/AnvilBlock.java
|
||||
@@ -54,6 +_,53 @@
|
||||
return this.defaultBlockState().setValue(FACING, context.getHorizontalDirection().getClockWise());
|
||||
}
|
||||
|
||||
+ // Purpur start - Anvil repair/damage options
|
||||
+ @Override
|
||||
+ protected net.minecraft.world.InteractionResult useItemOn(final net.minecraft.world.item.ItemStack stack, final BlockState state, final Level world, final BlockPos pos, final Player player, final net.minecraft.world.InteractionHand hand, final BlockHitResult hit) {
|
||||
+ if (world.purpurConfig.anvilRepairIngotsAmount > 0 && stack.is(net.minecraft.world.item.Items.IRON_INGOT)) {
|
||||
+ if (stack.getCount() < world.purpurConfig.anvilRepairIngotsAmount) {
|
||||
+ // not enough iron ingots, play "error" sound and consume
|
||||
+ world.playSound(null, pos, net.minecraft.sounds.SoundEvents.ANVIL_HIT, net.minecraft.sounds.SoundSource.BLOCKS, 1.0F, 1.0F);
|
||||
+ return net.minecraft.world.InteractionResult.CONSUME;
|
||||
+ }
|
||||
+ if (state.is(Blocks.DAMAGED_ANVIL)) {
|
||||
+ world.setBlock(pos, Blocks.CHIPPED_ANVIL.defaultBlockState().setValue(FACING, state.getValue(FACING)), 3);
|
||||
+ } else if (state.is(Blocks.CHIPPED_ANVIL)) {
|
||||
+ world.setBlock(pos, Blocks.ANVIL.defaultBlockState().setValue(FACING, state.getValue(FACING)), 3);
|
||||
+ } else if (state.is(Blocks.ANVIL)) {
|
||||
+ // anvil is already fully repaired, play "error" sound and consume
|
||||
+ world.playSound(null, pos, net.minecraft.sounds.SoundEvents.ANVIL_HIT, net.minecraft.sounds.SoundSource.BLOCKS, 1.0F, 1.0F);
|
||||
+ return net.minecraft.world.InteractionResult.CONSUME;
|
||||
+ }
|
||||
+ if (!player.getAbilities().instabuild) {
|
||||
+ stack.shrink(world.purpurConfig.anvilRepairIngotsAmount);
|
||||
+ }
|
||||
+ world.playSound(null, pos, net.minecraft.sounds.SoundEvents.ANVIL_PLACE, net.minecraft.sounds.SoundSource.BLOCKS, 1.0F, 1.0F);
|
||||
+ return net.minecraft.world.InteractionResult.CONSUME;
|
||||
+ }
|
||||
+ if (world.purpurConfig.anvilDamageObsidianAmount > 0 && stack.is(net.minecraft.world.item.Items.OBSIDIAN)) {
|
||||
+ if (stack.getCount() < world.purpurConfig.anvilDamageObsidianAmount) {
|
||||
+ // not enough obsidian, play "error" sound and consume
|
||||
+ world.playSound(null, pos, net.minecraft.sounds.SoundEvents.ANVIL_HIT, net.minecraft.sounds.SoundSource.BLOCKS, 1.0F, 1.0F);
|
||||
+ return net.minecraft.world.InteractionResult.CONSUME;
|
||||
+ }
|
||||
+ if (state.is(Blocks.DAMAGED_ANVIL)) {
|
||||
+ world.destroyBlock(pos, false);
|
||||
+ } else if (state.is(Blocks.CHIPPED_ANVIL)) {
|
||||
+ world.setBlock(pos, Blocks.DAMAGED_ANVIL.defaultBlockState().setValue(FACING, state.getValue(FACING)), 3);
|
||||
+ } else if (state.is(Blocks.ANVIL)) {
|
||||
+ world.setBlock(pos, Blocks.CHIPPED_ANVIL.defaultBlockState().setValue(FACING, state.getValue(FACING)), 3);
|
||||
+ }
|
||||
+ if (!player.getAbilities().instabuild) {
|
||||
+ stack.shrink(world.purpurConfig.anvilDamageObsidianAmount);
|
||||
+ }
|
||||
+ world.playSound(null, pos, net.minecraft.sounds.SoundEvents.ANVIL_LAND, net.minecraft.sounds.SoundSource.BLOCKS, 1.0F, 1.0F);
|
||||
+ return net.minecraft.world.InteractionResult.CONSUME;
|
||||
+ }
|
||||
+ return net.minecraft.world.InteractionResult.TRY_WITH_EMPTY_HAND;
|
||||
+ }
|
||||
+ // Purpur end - Anvil repair/damage options
|
||||
+
|
||||
@Override
|
||||
protected InteractionResult useWithoutItem(
|
||||
final BlockState state, final Level level, final BlockPos pos, final Player player, final BlockHitResult hitResult
|
||||
@@ -0,0 +1,23 @@
|
||||
--- a/net/minecraft/world/level/block/AzaleaBlock.java
|
||||
+++ b/net/minecraft/world/level/block/AzaleaBlock.java
|
||||
@@ -56,6 +_,20 @@
|
||||
|
||||
@Override
|
||||
public void performBonemeal(final ServerLevel level, final RandomSource random, final BlockPos pos, final BlockState state) {
|
||||
+ // Purpur start - Chance for azalea blocks to grow into trees naturally
|
||||
+ growTree(level, random, pos, state);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void randomTick(net.minecraft.world.level.block.state.BlockState state, ServerLevel world, BlockPos pos, RandomSource random) {
|
||||
+ double chance = state.getBlock() == Blocks.FLOWERING_AZALEA ? world.purpurConfig.floweringAzaleaGrowthChance : world.purpurConfig.azaleaGrowthChance;
|
||||
+ if (chance > 0.0D && world.getMaxLocalRawBrightness(pos.above()) > 9 && random.nextDouble() < chance) {
|
||||
+ growTree(world, random, pos, state);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ private void growTree(ServerLevel level, RandomSource random, BlockPos pos, net.minecraft.world.level.block.state.BlockState state) {
|
||||
+ // Purpur end - Chance for azalea blocks to grow into trees naturally
|
||||
TreeGrower.AZALEA.growTree(level, level.getChunkSource().getGenerator(), pos, state, random);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
--- a/net/minecraft/world/level/block/BaseCoralPlantTypeBlock.java
|
||||
+++ b/net/minecraft/world/level/block/BaseCoralPlantTypeBlock.java
|
||||
@@ -41,6 +_,7 @@
|
||||
}
|
||||
|
||||
protected static boolean scanForWater(final BlockState state, final BlockGetter level, final BlockPos blockPos) {
|
||||
+ if (!((net.minecraft.world.level.LevelAccessor) level).getMinecraftWorld().purpurConfig.coralDieOutsideWater) return true; // Purpur - Config to not let coral die
|
||||
if (state.getValue(WATERLOGGED)) {
|
||||
return true;
|
||||
} else {
|
||||
@@ -0,0 +1,11 @@
|
||||
--- a/net/minecraft/world/level/block/BaseFireBlock.java
|
||||
+++ b/net/minecraft/world/level/block/BaseFireBlock.java
|
||||
@@ -215,7 +_,7 @@
|
||||
boolean hasObsidian = false;
|
||||
|
||||
for (Direction face : Direction.values()) {
|
||||
- if (level.getBlockState(testPos.set(pos).move(face)).is(Blocks.OBSIDIAN)) {
|
||||
+ if (PortalShape.FRAME.test(level.getBlockState(testPos.set(pos).move(face)), level, testPos)) { // Purpur - Crying obsidian valid for portal frames
|
||||
hasObsidian = true;
|
||||
break;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
--- a/net/minecraft/world/level/block/BedBlock.java
|
||||
+++ b/net/minecraft/world/level/block/BedBlock.java
|
||||
@@ -100,7 +_,7 @@
|
||||
}
|
||||
|
||||
Vec3 boomPos = pos.getCenter();
|
||||
- level.explode(null, level.damageSources().badRespawnPointExplosion(boomPos), null, boomPos, 5.0F, true, Level.ExplosionInteraction.BLOCK);
|
||||
+ if (level.purpurConfig.bedExplode) level.explode(null, level.damageSources().badRespawnPointExplosion(boomPos), null, boomPos, (float) level.purpurConfig.bedExplosionPower, level.purpurConfig.bedExplosionFire, level.purpurConfig.bedExplosionEffect); // Purpur - Implement bed explosion options
|
||||
return InteractionResult.SUCCESS_SERVER;
|
||||
} else if (state.getValue(OCCUPIED)) {
|
||||
if (bedRule.explodes()) return this.explodeBed(state, level, pos); // Paper - check explode first
|
||||
@@ -153,7 +_,7 @@
|
||||
}
|
||||
|
||||
Vec3 center = pos.getCenter();
|
||||
- level.explode(null, level.damageSources().badRespawnPointExplosion(center).causingBlockSnapshot(blockState), null, center, 5.0F, true, Level.ExplosionInteraction.BLOCK); // CraftBukkit - add state
|
||||
+ if (level.purpurConfig.bedExplode) level.explode(null, level.damageSources().badRespawnPointExplosion(center).causingBlockSnapshot(blockState), null, center, (float) level.purpurConfig.bedExplosionPower, level.purpurConfig.bedExplosionFire, level.purpurConfig.bedExplosionEffect); // CraftBukkit - add state // Purpur - Implement bed explosion options
|
||||
return InteractionResult.SUCCESS_SERVER;
|
||||
}
|
||||
// CraftBukkit end
|
||||
@@ -170,7 +_,7 @@
|
||||
|
||||
@Override
|
||||
public void fallOn(final Level level, final BlockState state, final BlockPos pos, final Entity entity, final double fallDistance) {
|
||||
- super.fallOn(level, state, pos, entity, fallDistance * 0.5);
|
||||
+ super.fallOn(level, state, pos, entity, fallDistance); // Purpur - Configurable block fall damage modifiers
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -0,0 +1,11 @@
|
||||
--- a/net/minecraft/world/level/block/BigDripleafBlock.java
|
||||
+++ b/net/minecraft/world/level/block/BigDripleafBlock.java
|
||||
@@ -259,7 +_,7 @@
|
||||
playTiltSound(level, pos, sound);
|
||||
}
|
||||
|
||||
- int tickDelay = DELAY_UNTIL_NEXT_TILT_STATE.getInt(tilt);
|
||||
+ int tickDelay = level.purpurConfig.bigDripleafTiltDelay.getOrDefault(tilt, -1); // Purpur - Big dripleaf tilt delay
|
||||
if (tickDelay != -1) {
|
||||
level.scheduleTick(pos, this, tickDelay);
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
--- a/net/minecraft/world/level/block/Block.java
|
||||
+++ b/net/minecraft/world/level/block/Block.java
|
||||
@@ -114,6 +_,10 @@
|
||||
public static final int UPDATE_LIMIT = 512;
|
||||
protected final StateDefinition<Block, BlockState> stateDefinition;
|
||||
private BlockState defaultBlockState;
|
||||
+ // Purpur start - Configurable block fall damage modifiers
|
||||
+ public float fallDamageMultiplier = 1.0F;
|
||||
+ public float fallDistanceMultiplier = 1.0F;
|
||||
+ // Purpur end - Configurable block fall damage modifiers
|
||||
// Paper start - Protect Bedrock and End Portal/Frames from being destroyed
|
||||
public final boolean isDestroyable() {
|
||||
return io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.allowPermanentBlockBreakExploits ||
|
||||
@@ -411,7 +_,7 @@
|
||||
event.setExpToDrop(block.getExpDrop(state, serverLevel, pos, net.minecraft.world.item.ItemStack.EMPTY, true)); // Paper - Properly handle xp dropping
|
||||
event.callEvent();
|
||||
for (org.bukkit.inventory.ItemStack drop : event.getDrops()) {
|
||||
- popResource(serverLevel, pos, org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(drop));
|
||||
+ popResource(serverLevel, pos, applyLoreFromTile(org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(drop), blockEntity)); // Purpur - Persistent BlockEntity Lore and DisplayName
|
||||
}
|
||||
state.spawnAfterBreak(serverLevel, pos, ItemStack.EMPTY, false); // Paper - Properly handle xp dropping
|
||||
block.popExperience(serverLevel, pos, event.getExpToDrop()); // Paper - Properly handle xp dropping
|
||||
@@ -429,7 +_,7 @@
|
||||
|
||||
public static void dropResources(final BlockState state, final LevelAccessor level, final BlockPos pos, final @Nullable BlockEntity blockEntity) {
|
||||
if (level instanceof ServerLevel serverLevel) {
|
||||
- getDrops(state, serverLevel, pos, blockEntity).forEach(stack -> popResource(serverLevel, pos, stack));
|
||||
+ getDrops(state, serverLevel, pos, blockEntity).forEach(stack -> popResource(serverLevel, pos, applyLoreFromTile(stack, blockEntity))); // Purpur - Persistent BlockEntity Lore and DisplayName
|
||||
state.spawnAfterBreak(serverLevel, pos, ItemStack.EMPTY, true);
|
||||
}
|
||||
}
|
||||
@@ -450,11 +_,30 @@
|
||||
, final boolean dropExperience // Paper - Properly handle xp dropping
|
||||
) {
|
||||
if (level instanceof ServerLevel serverLevel) {
|
||||
- getDrops(state, serverLevel, pos, blockEntity, breaker, tool).forEach(stack -> popResource(level, pos, stack));
|
||||
+ getDrops(state, serverLevel, pos, blockEntity, breaker, tool).forEach(stack -> popResource(level, pos, applyLoreFromTile(stack, blockEntity))); // Purpur - Persistent BlockEntity Lore and DisplayName
|
||||
state.spawnAfterBreak(serverLevel, pos, tool, dropExperience); // Paper - Properly handle xp dropping
|
||||
}
|
||||
}
|
||||
|
||||
+ // Purpur start - Persistent BlockEntity Lore and DisplayName
|
||||
+ private static ItemStack applyLoreFromTile(ItemStack stack, @Nullable BlockEntity blockEntity) {
|
||||
+ if (stack.getItem() instanceof BlockItem) {
|
||||
+ if (blockEntity != null && blockEntity.getLevel() instanceof ServerLevel) {
|
||||
+ net.minecraft.world.item.component.ItemLore lore = blockEntity.getPersistentLore();
|
||||
+ net.minecraft.core.component.DataComponentPatch.Builder builder = net.minecraft.core.component.DataComponentPatch.builder();
|
||||
+ if (blockEntity.getLevel().purpurConfig.persistentTileEntityLore && lore != null) {
|
||||
+ builder.set(net.minecraft.core.component.DataComponents.LORE, lore);
|
||||
+ }
|
||||
+ if (!blockEntity.getLevel().purpurConfig.persistentTileEntityDisplayName) {
|
||||
+ builder.remove(net.minecraft.core.component.DataComponents.CUSTOM_NAME);
|
||||
+ }
|
||||
+ stack.applyComponents(builder.build());
|
||||
+ }
|
||||
+ }
|
||||
+ return stack;
|
||||
+ }
|
||||
+ // Purpur end - Persistent BlockEntity Lore and DisplayName
|
||||
+
|
||||
public static void popResource(final Level level, final BlockPos pos, final ItemStack itemStack) {
|
||||
double halfHeight = EntityType.ITEM.getHeight() / 2.0;
|
||||
RandomSource random = level.getRandom();
|
||||
@@ -544,7 +_,15 @@
|
||||
}
|
||||
|
||||
public void setPlacedBy(final Level level, final BlockPos pos, final BlockState state, final @Nullable LivingEntity by, final ItemStack itemStack) {
|
||||
- }
|
||||
+ this.placer = placer; // Purpur - Store placer on Block when placed
|
||||
+ }
|
||||
+
|
||||
+ // Purpur start - Store placer on Block when placed
|
||||
+ @Nullable protected LivingEntity placer = null;
|
||||
+ public void forgetPlacer() {
|
||||
+ this.placer = null;
|
||||
+ }
|
||||
+ // Purpur end - Store placer on Block when placed
|
||||
|
||||
public boolean isPossibleToRespawnInThis(final BlockState state) {
|
||||
return !state.isSolid() && !state.liquid();
|
||||
@@ -555,7 +_,7 @@
|
||||
}
|
||||
|
||||
public void fallOn(final Level level, final BlockState state, final BlockPos pos, final Entity entity, final double fallDistance) {
|
||||
- entity.causeFallDamage(fallDistance, 1.0F, entity.damageSources().fall());
|
||||
+ entity.causeFallDamage(fallDistance * fallDistanceMultiplier, fallDamageMultiplier, entity.damageSources().fall()); // Purpur - Configurable block fall damage modifiers
|
||||
}
|
||||
|
||||
public void updateEntityMovementAfterFallOn(final BlockGetter level, final Entity entity) {
|
||||
@@ -0,0 +1,18 @@
|
||||
--- a/net/minecraft/world/level/block/Blocks.java
|
||||
+++ b/net/minecraft/world/level/block/Blocks.java
|
||||
@@ -6726,6 +_,7 @@
|
||||
BlockBehaviour.Properties.of()
|
||||
.mapColor(MapColor.PLANT)
|
||||
.forceSolidOff()
|
||||
+ .randomTicks() // Purpur - Chance for azalea blocks to grow into trees naturally
|
||||
.instabreak()
|
||||
.sound(SoundType.AZALEA)
|
||||
.noOcclusion()
|
||||
@@ -6737,6 +_,7 @@
|
||||
BlockBehaviour.Properties.of()
|
||||
.mapColor(MapColor.PLANT)
|
||||
.forceSolidOff()
|
||||
+ .randomTicks() // Purpur - Chance for azalea blocks to grow into trees naturally
|
||||
.instabreak()
|
||||
.sound(SoundType.FLOWERING_AZALEA)
|
||||
.noOcclusion()
|
||||
@@ -0,0 +1,17 @@
|
||||
--- a/net/minecraft/world/level/block/BubbleColumnBlock.java
|
||||
+++ b/net/minecraft/world/level/block/BubbleColumnBlock.java
|
||||
@@ -118,11 +_,11 @@
|
||||
if (belowState.is(bubbleColumn)) {
|
||||
return belowState;
|
||||
} else if (belowState.is(BlockTags.ENABLES_BUBBLE_COLUMN_PUSH_UP)) {
|
||||
- return bubbleColumn.defaultBlockState().setValue(DRAG_DOWN, false);
|
||||
+ return bubbleColumn.defaultBlockState().setValue(DRAG_DOWN, org.purpurmc.purpur.PurpurConfig.soulSandBlockReverseBubbleColumnFlow); // Purpur - Config to reverse bubble column flow
|
||||
} else if (belowState.is(BlockTags.ENABLES_BUBBLE_COLUMN_DRAG_DOWN)) {
|
||||
- return bubbleColumn.defaultBlockState().setValue(DRAG_DOWN, true);
|
||||
+ return bubbleColumn.defaultBlockState().setValue(DRAG_DOWN, !org.purpurmc.purpur.PurpurConfig.magmaBlockReverseBubbleColumnFlow);
|
||||
} else {
|
||||
- return occupyState.is(bubbleColumn) ? Blocks.WATER.defaultBlockState() : occupyState;
|
||||
+ return occupyState.is(bubbleColumn) ? Blocks.WATER.defaultBlockState() : occupyState; // Purpur - Config to reverse bubble column flow
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
--- a/net/minecraft/world/level/block/CactusBlock.java
|
||||
+++ b/net/minecraft/world/level/block/CactusBlock.java
|
||||
@@ -22,7 +_,7 @@
|
||||
import net.minecraft.world.phys.shapes.CollisionContext;
|
||||
import net.minecraft.world.phys.shapes.VoxelShape;
|
||||
|
||||
-public class CactusBlock extends Block {
|
||||
+public class CactusBlock extends Block implements BonemealableBlock { // Purpur - bonemealable cactus
|
||||
public static final MapCodec<CactusBlock> CODEC = simpleCodec(CactusBlock::new);
|
||||
public static final IntegerProperty AGE = BlockStateProperties.AGE_15;
|
||||
public static final int MAX_AGE = 15;
|
||||
@@ -117,7 +_,7 @@
|
||||
protected boolean canSurvive(final BlockState state, final LevelReader level, final BlockPos pos) {
|
||||
for (Direction direction : Direction.Plane.HORIZONTAL) {
|
||||
BlockState neighbor = level.getBlockState(pos.relative(direction));
|
||||
- if (neighbor.isSolid() || level.getFluidState(pos.relative(direction)).is(FluidTags.LAVA)) {
|
||||
+ if ((level.getWorldBorder().world == null || level.getWorldBorder().world.purpurConfig.cactusBreaksFromSolidNeighbors) && neighbor.isSolid() || level.getFluidState(pos.relative(direction)).is(FluidTags.LAVA)) { // Purpur - Cactus breaks from solid neighbors config
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -148,4 +_,34 @@
|
||||
protected boolean isPathfindable(final BlockState state, final PathComputationType type) {
|
||||
return false;
|
||||
}
|
||||
+
|
||||
+ // Purpur start - bonemealable cactus
|
||||
+ @Override
|
||||
+ public boolean isValidBonemealTarget(final LevelReader world, final BlockPos pos, final BlockState state) {
|
||||
+ if (!((Level) world).purpurConfig.cactusAffectedByBonemeal || !world.isEmptyBlock(pos.above())) return false;
|
||||
+
|
||||
+ int cactusHeight = 0;
|
||||
+ while (world.getBlockState(pos.below(cactusHeight)).is(this)) {
|
||||
+ cactusHeight++;
|
||||
+ }
|
||||
+
|
||||
+ return cactusHeight < ((Level) world).paperConfig().maxGrowthHeight.cactus;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public boolean isBonemealSuccess(Level world, RandomSource random, BlockPos pos, BlockState state) {
|
||||
+ return true;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void performBonemeal(ServerLevel world, RandomSource random, BlockPos pos, BlockState state) {
|
||||
+ int cactusHeight = 0;
|
||||
+ while (world.getBlockState(pos.below(cactusHeight)).is(this)) {
|
||||
+ cactusHeight++;
|
||||
+ }
|
||||
+ for (int i = 0; i <= world.paperConfig().maxGrowthHeight.cactus - cactusHeight; i++) {
|
||||
+ world.setBlockAndUpdate(pos.above(i), state.setValue(CactusBlock.AGE, 0));
|
||||
+ }
|
||||
+ }
|
||||
+ // Purpur end - bonemealable cactus
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
--- a/net/minecraft/world/level/block/CakeBlock.java
|
||||
+++ b/net/minecraft/world/level/block/CakeBlock.java
|
||||
@@ -118,6 +_,7 @@
|
||||
org.bukkit.event.entity.FoodLevelChangeEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callFoodLevelChangeEvent(player, 2 + oldFoodLevel);
|
||||
|
||||
if (!event.isCancelled()) {
|
||||
+ if (player.level().purpurConfig.playerBurpWhenFull && event.getFoodLevel() == 20 && oldFoodLevel < 20) player.burpDelay = player.level().purpurConfig.playerBurpDelay; // Purpur - Burp after eating food fills hunger bar completely
|
||||
player.getFoodData().eat(event.getFoodLevel() - oldFoodLevel, 0.1F);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
--- a/net/minecraft/world/level/block/CampfireBlock.java
|
||||
+++ b/net/minecraft/world/level/block/CampfireBlock.java
|
||||
@@ -136,7 +_,7 @@
|
||||
return this.defaultBlockState()
|
||||
.setValue(WATERLOGGED, replacedWater)
|
||||
.setValue(SIGNAL_FIRE, this.isSmokeSource(level.getBlockState(pos.below())))
|
||||
- .setValue(LIT, !replacedWater)
|
||||
+ .setValue(LIT, level.getMinecraftWorld().purpurConfig.campFireLitWhenPlaced && !replacedWater) // Purpur - Campfire option for lit when placed
|
||||
.setValue(FACING, context.getHorizontalDirection());
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
--- a/net/minecraft/world/level/block/CarvedPumpkinBlock.java
|
||||
+++ b/net/minecraft/world/level/block/CarvedPumpkinBlock.java
|
||||
@@ -67,7 +_,7 @@
|
||||
if (snowGolemMatch != null) {
|
||||
SnowGolem snowGolem = EntityType.SNOW_GOLEM.create(level, EntitySpawnReason.TRIGGERED);
|
||||
if (snowGolem != null) {
|
||||
- spawnGolemInWorld(level, snowGolemMatch, snowGolem, snowGolemMatch.getBlock(0, 2, 0).getPos());
|
||||
+ spawnGolemInWorld(level, snowGolemMatch, snowGolem, snowGolemMatch.getBlock(0, 2, 0).getPos(), this.placer); // Purpur - Summoner API
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -77,7 +_,7 @@
|
||||
IronGolem ironGolem = EntityType.IRON_GOLEM.create(level, EntitySpawnReason.TRIGGERED);
|
||||
if (ironGolem != null) {
|
||||
ironGolem.setPlayerCreated(true);
|
||||
- spawnGolemInWorld(level, ironGolemMatch, ironGolem, ironGolemMatch.getBlock(1, 2, 0).getPos());
|
||||
+ spawnGolemInWorld(level, ironGolemMatch, ironGolem, ironGolemMatch.getBlock(1, 2, 0).getPos(), this.placer); // Purpur - Summoner API
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -86,7 +_,7 @@
|
||||
if (copperGolemMatch != null) {
|
||||
CopperGolem copperGolem = EntityType.COPPER_GOLEM.create(level, EntitySpawnReason.TRIGGERED);
|
||||
if (copperGolem != null) {
|
||||
- spawnGolemInWorld(level, copperGolemMatch, copperGolem, copperGolemMatch.getBlock(0, 0, 0).getPos());
|
||||
+ spawnGolemInWorld(level, copperGolemMatch, copperGolem, copperGolemMatch.getBlock(0, 0, 0).getPos(), this.placer); // Purpur - Summoner API
|
||||
if (!copperGolem.valid) return; // Paper - entityspawnevent - entity was not added to the world so prevent world mutation
|
||||
this.replaceCopperBlockWithChest(level, copperGolemMatch);
|
||||
copperGolem.spawn(this.getWeatherStateFromPattern(copperGolemMatch));
|
||||
@@ -105,7 +_,20 @@
|
||||
.getAge();
|
||||
}
|
||||
|
||||
+ @io.papermc.paper.annotation.DoNotUse
|
||||
private static void spawnGolemInWorld(final Level level, final BlockPattern.BlockPatternMatch match, final Entity golem, final BlockPos spawnPos) {
|
||||
+ // Purpur start - Summoner API
|
||||
+ spawnGolemInWorld(level, match, golem, spawnPos, null);
|
||||
+ }
|
||||
+ private static void spawnGolemInWorld(final Level level, final BlockPattern.BlockPatternMatch match, final Entity golem, final BlockPos spawnPos, final net.minecraft.world.entity.LivingEntity placer) {
|
||||
+ java.util.UUID summoner = placer == null ? null : placer.getUUID();
|
||||
+ switch (golem) {
|
||||
+ case SnowGolem snowGolem -> snowGolem.setSummoner(summoner);
|
||||
+ case IronGolem ironGolem -> ironGolem.setSummoner(summoner);
|
||||
+ case CopperGolem copperGolem -> copperGolem.setSummoner(summoner);
|
||||
+ default -> throw new IllegalStateException("Unexpected value: " + golem);
|
||||
+ }
|
||||
+ // Purpur end - Summoner API
|
||||
// clearPatternBlocks(level, patternMatch); // Paper - moved down
|
||||
golem.snapTo(spawnPos.getX() + 0.5, spawnPos.getY() + 0.05, spawnPos.getZ() + 0.5, 0.0F, 0.0F);
|
||||
// Paper start
|
||||
@@ -0,0 +1,13 @@
|
||||
--- a/net/minecraft/world/level/block/CauldronBlock.java
|
||||
+++ b/net/minecraft/world/level/block/CauldronBlock.java
|
||||
@@ -32,8 +_,8 @@
|
||||
|
||||
protected static boolean shouldHandlePrecipitation(final Level level, final Biome.Precipitation precipitation) {
|
||||
return precipitation == Biome.Precipitation.RAIN
|
||||
- ? level.getRandom().nextFloat() < 0.05F
|
||||
- : precipitation == Biome.Precipitation.SNOW && level.getRandom().nextFloat() < 0.1F;
|
||||
+ ? level.getRandom().nextFloat() < level.purpurConfig.cauldronRainChance // Purpur - Cauldron fill chances
|
||||
+ : precipitation == Biome.Precipitation.SNOW && level.getRandom().nextFloat() < level.purpurConfig.cauldronPowderSnowChance; // Purpur - Cauldron fill chances
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -0,0 +1,14 @@
|
||||
--- a/net/minecraft/world/level/block/CaveVinesBlock.java
|
||||
+++ b/net/minecraft/world/level/block/CaveVinesBlock.java
|
||||
@@ -95,4 +_,11 @@
|
||||
public void performBonemeal(final ServerLevel level, final RandomSource random, final BlockPos pos, final BlockState state) {
|
||||
level.setBlock(pos, state.setValue(BERRIES, true), Block.UPDATE_CLIENTS);
|
||||
}
|
||||
+
|
||||
+ // Purpur start - cave vines configurable max growth age
|
||||
+ @Override
|
||||
+ public int getMaxGrowthAge() {
|
||||
+ return org.purpurmc.purpur.PurpurConfig.caveVinesMaxGrowthAge;
|
||||
+ }
|
||||
+ // Purpur end - cave vines configurable max growth age
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
--- a/net/minecraft/world/level/block/ChangeOverTimeBlock.java
|
||||
+++ b/net/minecraft/world/level/block/ChangeOverTimeBlock.java
|
||||
@@ -51,7 +_,7 @@
|
||||
}
|
||||
|
||||
float chance = (float)(olderCount + 1) / (olderCount + sameAgeCount + 1);
|
||||
- float actualChance = chance * chance * this.getChanceModifier();
|
||||
+ float actualChance = level.purpurConfig.disableOxidationProximityPenalty ? this.getChanceModifier() : chance * chance * this.getChanceModifier(); // Purpur - option to disable the copper oxidation proximity penalty
|
||||
return random.nextFloat() < actualChance ? this.getNext(state) : Optional.empty();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
--- a/net/minecraft/world/level/block/ChestBlock.java
|
||||
+++ b/net/minecraft/world/level/block/ChestBlock.java
|
||||
@@ -380,6 +_,7 @@
|
||||
}
|
||||
|
||||
public static boolean isBlockedChestByBlock(final BlockGetter level, final BlockPos pos) {
|
||||
+ if (level instanceof Level level1 && level1.purpurConfig.chestOpenWithBlockOnTop) return false; // Purpur - Option for chests to open even with a solid block on top
|
||||
BlockPos above = pos.above();
|
||||
return level.getBlockState(above).isRedstoneConductor(level, above);
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
--- a/net/minecraft/world/level/block/ComposterBlock.java
|
||||
+++ b/net/minecraft/world/level/block/ComposterBlock.java
|
||||
@@ -256,23 +_,52 @@
|
||||
) {
|
||||
int fillLevel = state.getValue(LEVEL);
|
||||
if (fillLevel < 8 && COMPOSTABLES.containsKey(itemStack.getItem())) {
|
||||
- if (fillLevel < 7 && !level.isClientSide()) {
|
||||
- BlockState newState = addItem(player, state, level, pos, itemStack);
|
||||
- // Paper start - handle cancelled events
|
||||
- if (newState == null) {
|
||||
- return InteractionResult.PASS;
|
||||
- }
|
||||
- // Paper end
|
||||
- level.levelEvent(LevelEvent.COMPOSTER_FILL, pos, state != newState ? 1 : 0);
|
||||
- player.awardStat(Stats.ITEM_USED.get(itemStack.getItem()));
|
||||
- itemStack.consume(1, player);
|
||||
- }
|
||||
+ // Purpur start - sneak to bulk process composter
|
||||
+ BlockState newState = process(fillLevel, player, state, level, pos, itemStack);
|
||||
+ if (newState == null) {
|
||||
+ return InteractionResult.PASS;
|
||||
+ }
|
||||
+ if (level.purpurConfig.composterBulkProcess && player.isShiftKeyDown() && newState != state) {
|
||||
+ BlockState oldState;
|
||||
+ int oldCount, newCount, oldLevel, newLevel;
|
||||
+ do {
|
||||
+ oldState = newState;
|
||||
+ oldCount = itemStack.getCount();
|
||||
+ oldLevel = oldState.getValue(ComposterBlock.LEVEL);
|
||||
+ newState = process(oldLevel, player, oldState, level, pos, itemStack);
|
||||
+ if (newState == null) {
|
||||
+ return InteractionResult.PASS;
|
||||
+ }
|
||||
+ newCount = itemStack.getCount();
|
||||
+ newLevel = newState.getValue(ComposterBlock.LEVEL);
|
||||
+ } while (newCount > 0 && (newCount != oldCount || newLevel != oldLevel || newState != oldState));
|
||||
+ }
|
||||
+ // Purpur end - Sneak to bulk process composter
|
||||
|
||||
return InteractionResult.SUCCESS;
|
||||
} else {
|
||||
return super.useItemOn(itemStack, state, level, pos, player, hand, hitResult);
|
||||
}
|
||||
}
|
||||
+
|
||||
+ // Purpur start - sneak to bulk process composter
|
||||
+ private static @Nullable BlockState process(int fillLevel, Player player, BlockState state, Level level, BlockPos pos, ItemStack itemStack) {
|
||||
+ if (fillLevel < 7 && !level.isClientSide()) {
|
||||
+ BlockState newState = ComposterBlock.addItem(player, state, level, pos, itemStack);
|
||||
+ // Paper start - handle cancelled events
|
||||
+ if (newState == null) {
|
||||
+ return null;
|
||||
+ }
|
||||
+ // Paper end
|
||||
+
|
||||
+ level.levelEvent(LevelEvent.COMPOSTER_FILL, pos, state != newState ? 1 : 0);
|
||||
+ player.awardStat(Stats.ITEM_USED.get(itemStack.getItem()));
|
||||
+ itemStack.consume(1, player);
|
||||
+ return newState;
|
||||
+ }
|
||||
+ return state;
|
||||
+ }
|
||||
+ // Purpur end - Sneak to bulk process composter
|
||||
|
||||
@Override
|
||||
protected InteractionResult useWithoutItem(
|
||||
@@ -0,0 +1,10 @@
|
||||
--- a/net/minecraft/world/level/block/CoralBlock.java
|
||||
+++ b/net/minecraft/world/level/block/CoralBlock.java
|
||||
@@ -65,6 +_,7 @@
|
||||
}
|
||||
|
||||
protected boolean scanForWater(final BlockGetter level, final BlockPos blockPos) {
|
||||
+ if (!((net.minecraft.world.level.LevelAccessor) level).getMinecraftWorld().purpurConfig.coralDieOutsideWater) return true; // Purpur - Config to not let coral die
|
||||
for (Direction direction : Direction.values()) {
|
||||
FluidState fluidState = level.getFluidState(blockPos.relative(direction));
|
||||
if (fluidState.is(FluidTags.WATER)) {
|
||||
@@ -0,0 +1,27 @@
|
||||
--- a/net/minecraft/world/level/block/CropBlock.java
|
||||
+++ b/net/minecraft/world/level/block/CropBlock.java
|
||||
@@ -177,7 +_,7 @@
|
||||
final boolean isPrecise
|
||||
) {
|
||||
if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(level, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent
|
||||
- if (level instanceof ServerLevel serverLevel && entity instanceof Ravager && org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(entity, pos, Blocks.AIR.defaultBlockState(), !serverLevel.getGameRules().get(GameRules.MOB_GRIEFING))) { // CraftBukkit
|
||||
+ if (level instanceof ServerLevel serverLevel && entity instanceof Ravager && serverLevel.purpurConfig.ravagerGriefableBlocks.contains(serverLevel.getBlockState(pos).getBlock()) && org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(entity, pos, Blocks.AIR.defaultBlockState(), !serverLevel.getGameRules().get(GameRules.MOB_GRIEFING))) { // CraftBukkit // Purpur - Configurable ravager griefable blocks list
|
||||
serverLevel.destroyBlock(pos, true, entity);
|
||||
}
|
||||
|
||||
@@ -212,4 +_,15 @@
|
||||
protected void createBlockStateDefinition(final StateDefinition.Builder<Block, BlockState> builder) {
|
||||
builder.add(AGE);
|
||||
}
|
||||
+
|
||||
+ // Purpur start - Ability for hoe to replant crops
|
||||
+ @Override
|
||||
+ public void playerDestroy(Level world, net.minecraft.world.entity.player.Player player, BlockPos pos, BlockState state, @javax.annotation.Nullable net.minecraft.world.level.block.entity.BlockEntity blockEntity, ItemStack itemInHand, boolean includeDrops, boolean dropExp) {
|
||||
+ if (world.purpurConfig.hoeReplantsCrops && itemInHand.getItem() instanceof net.minecraft.world.item.HoeItem) {
|
||||
+ super.playerDestroyAndReplant(world, player, pos, state, blockEntity, itemInHand, getBaseSeedId());
|
||||
+ } else {
|
||||
+ super.playerDestroy(world, player, pos, state, blockEntity, itemInHand, includeDrops, dropExp);
|
||||
+ }
|
||||
+ }
|
||||
+ // Purpur end - Ability for hoe to replant crops
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
--- a/net/minecraft/world/level/block/DoorBlock.java
|
||||
+++ b/net/minecraft/world/level/block/DoorBlock.java
|
||||
@@ -200,6 +_,7 @@
|
||||
protected InteractionResult useWithoutItem(BlockState state, final Level level, final BlockPos pos, final Player player, final BlockHitResult hitResult) {
|
||||
if (!this.type.canOpenByHand()) {
|
||||
return InteractionResult.PASS;
|
||||
+ } else if (requiresRedstone(level, state, pos)) { return InteractionResult.CONSUME; // Purpur - Option to make doors require redstone
|
||||
} else {
|
||||
state = state.cycle(OPEN);
|
||||
level.setBlock(pos, state, Block.UPDATE_CLIENTS | Block.UPDATE_IMMEDIATE);
|
||||
@@ -288,4 +_,18 @@
|
||||
public static boolean isWoodenDoor(final BlockState state) {
|
||||
return state.getBlock() instanceof DoorBlock door && door.type().canOpenByHand();
|
||||
}
|
||||
+
|
||||
+ // Purpur start - Option to make doors require redstone
|
||||
+ public static boolean requiresRedstone(Level level, BlockState state, BlockPos pos) {
|
||||
+ if (level.purpurConfig.doorRequiresRedstone.contains(state.getBlock())) {
|
||||
+ // force update client
|
||||
+ BlockPos otherPos = pos.relative(state.getValue(DoorBlock.HALF) == DoubleBlockHalf.LOWER ? Direction.UP : Direction.DOWN);
|
||||
+ BlockState otherState = level.getBlockState(otherPos);
|
||||
+ level.sendBlockUpdated(pos, state, state, Block.UPDATE_ALL);
|
||||
+ level.sendBlockUpdated(otherPos, otherState, otherState, Block.UPDATE_ALL);
|
||||
+ return true;
|
||||
+ }
|
||||
+ return false;
|
||||
+ }
|
||||
+ // Purpur end - Option to make doors require redstone
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
--- a/net/minecraft/world/level/block/DragonEggBlock.java
|
||||
+++ b/net/minecraft/world/level/block/DragonEggBlock.java
|
||||
@@ -49,6 +_,7 @@
|
||||
}
|
||||
|
||||
private void teleport(final BlockState state, final Level level, final BlockPos pos) {
|
||||
+ if (!level.purpurConfig.dragonEggTeleport) return; // Purpur - Option to disable dragon egg teleporting
|
||||
WorldBorder worldBorder = level.getWorldBorder();
|
||||
RandomSource random = level.getRandom();
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
--- a/net/minecraft/world/level/block/GrowingPlantHeadBlock.java
|
||||
+++ b/net/minecraft/world/level/block/GrowingPlantHeadBlock.java
|
||||
@@ -38,12 +_,12 @@
|
||||
|
||||
@Override
|
||||
public BlockState getStateForPlacement(final RandomSource random) {
|
||||
- return this.defaultBlockState().setValue(AGE, random.nextInt(25));
|
||||
+ return this.defaultBlockState().setValue(AGE, getMaxGrowthAge() == 0 ? 0 : random.nextInt(getMaxGrowthAge())); // Purpur - kelp, cave, weeping, and twisting configurable max growth age
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isRandomlyTicking(final BlockState state) {
|
||||
- return state.getValue(AGE) < 25;
|
||||
+ return state.getValue(AGE) < getMaxGrowthAge(); // Purpur - kelp, cave, weeping, and twisting configurable max growth age
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -59,7 +_,7 @@
|
||||
} else if (this == Blocks.CAVE_VINES) {
|
||||
modifier = level.spigotConfig.caveVinesModifier;
|
||||
}
|
||||
- if (state.getValue(AGE) < 25 && random.nextDouble() < ((modifier / 100.0D) * this.growPerTickProbability)) { // Spigot - SPIGOT-7159: Better modifier resolution
|
||||
+ if (state.getValue(AGE) < getMaxGrowthAge() && random.nextDouble() < ((modifier / 100.0D) * this.growPerTickProbability)) { // Spigot - SPIGOT-7159: Better modifier resolution // Purpur - kelp, cave, weeping, and twisting configurable max growth age
|
||||
// Spigot end
|
||||
BlockPos growthPos = pos.relative(this.growthDirection);
|
||||
if (this.canGrowInto(level.getBlockState(growthPos))) {
|
||||
@@ -79,11 +_,11 @@
|
||||
}
|
||||
|
||||
public BlockState getMaxAgeState(final BlockState fromState) {
|
||||
- return fromState.setValue(AGE, 25);
|
||||
+ return fromState.setValue(AGE, getMaxGrowthAge()); // Purpur - kelp, cave, weeping, and twisting configurable max growth age
|
||||
}
|
||||
|
||||
public boolean isMaxAge(final BlockState state) {
|
||||
- return state.getValue(AGE) == 25;
|
||||
+ return state.getValue(AGE) >= getMaxGrowthAge(); // Purpur - kelp, cave, weeping, and twisting configurable max growth age
|
||||
}
|
||||
|
||||
protected BlockState updateBodyAfterConvertedFromHead(final BlockState headState, final BlockState bodyState) {
|
||||
@@ -142,13 +_,13 @@
|
||||
@Override
|
||||
public void performBonemeal(final ServerLevel level, final RandomSource random, final BlockPos pos, final BlockState state) {
|
||||
BlockPos forwardPos = pos.relative(this.growthDirection);
|
||||
- int nextAge = Math.min(state.getValue(AGE) + 1, 25);
|
||||
+ int nextAge = Math.min(state.getValue(AGE) + 1, getMaxGrowthAge()); // Purpur - kelp, cave, weeping, and twisting configurable max growth age
|
||||
int blocksToGrow = this.getBlocksToGrowWhenBonemealed(random);
|
||||
|
||||
for (int i = 0; i < blocksToGrow && this.canGrowInto(level.getBlockState(forwardPos)) && !level.isOutsideBuildHeight(forwardPos); i++) {
|
||||
level.setBlockAndUpdate(forwardPos, state.setValue(AGE, nextAge));
|
||||
forwardPos = forwardPos.relative(this.growthDirection);
|
||||
- nextAge = Math.min(nextAge + 1, 25);
|
||||
+ nextAge = Math.min(nextAge + 1, getMaxGrowthAge()); // Purpur - kelp, cave, weeping, and twisting configurable max growth age
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,4 +_,6 @@
|
||||
protected GrowingPlantHeadBlock getHeadBlock() {
|
||||
return this;
|
||||
}
|
||||
+
|
||||
+ public abstract int getMaxGrowthAge(); // Purpur - kelp, cave, weeping, and twisting configurable max growth age
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
--- a/net/minecraft/world/level/block/HayBlock.java
|
||||
+++ b/net/minecraft/world/level/block/HayBlock.java
|
||||
@@ -23,6 +_,6 @@
|
||||
|
||||
@Override
|
||||
public void fallOn(final Level level, final BlockState state, final BlockPos pos, final Entity entity, final double fallDistance) {
|
||||
- entity.causeFallDamage(fallDistance, 0.2F, level.damageSources().fall());
|
||||
+ super.fallOn(level, state, pos, entity, fallDistance); // Purpur - Configurable block fall damage modifiers
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user