mirror of
https://github.com/PurpurMC/Purpur.git
synced 2026-06-23 02:37:47 +02:00
apply almost all the source file patches
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
--- a/net/minecraft/core/dispenser/DispenseItemBehavior.java
|
||||
+++ b/net/minecraft/core/dispenser/DispenseItemBehavior.java
|
||||
@@ -625,5 +_,22 @@
|
||||
DispenserBlock.registerBehavior(Items.TNT_MINECART, new MinecartDispenseItemBehavior(EntityTypes.TNT_MINECART));
|
||||
DispenserBlock.registerBehavior(Items.HOPPER_MINECART, new MinecartDispenseItemBehavior(EntityTypes.HOPPER_MINECART));
|
||||
DispenserBlock.registerBehavior(Items.COMMAND_BLOCK_MINECART, new MinecartDispenseItemBehavior(EntityTypes.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,135 @@
|
||||
--- a/net/minecraft/server/gui/MinecraftServerGui.java
|
||||
+++ b/net/minecraft/server/gui/MinecraftServerGui.java
|
||||
@@ -41,6 +_,11 @@
|
||||
private Thread logAppenderThread;
|
||||
private final Collection<Runnable> finalizers = Lists.newArrayList();
|
||||
private final AtomicBoolean isClosing = new AtomicBoolean();
|
||||
+ // Purpur start - GUI Improvements
|
||||
+ private final CommandHistory history = new CommandHistory();
|
||||
+ private String currentCommand = "";
|
||||
+ private int historyIndex = 0;
|
||||
+ // Purpur end - GUI Improvements
|
||||
|
||||
public static MinecraftServerGui showFrameFor(final DedicatedServer server) {
|
||||
try {
|
||||
@@ -48,7 +_,7 @@
|
||||
} catch (Exception var3) {
|
||||
}
|
||||
|
||||
- final JFrame frame = new JFrame("Minecraft server");
|
||||
+ final JFrame frame = new JFrame("Purpur Minecraft server"); // Purpur - Improve GUI
|
||||
final MinecraftServerGui gui = new MinecraftServerGui(server);
|
||||
frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
|
||||
frame.add(gui);
|
||||
@@ -56,7 +_,7 @@
|
||||
frame.setLocationRelativeTo(null);
|
||||
frame.setVisible(true);
|
||||
// Paper start - Improve ServerGUI
|
||||
- frame.setName("Minecraft server");
|
||||
+ frame.setName("Purpur Minecraft server"); // Purpur - Improve GUI
|
||||
try {
|
||||
frame.setIconImage(javax.imageio.ImageIO.read(java.util.Objects.requireNonNull(MinecraftServerGui.class.getClassLoader().getResourceAsStream("logo.png"))));
|
||||
} catch (java.io.IOException ignore) {
|
||||
@@ -66,7 +_,7 @@
|
||||
@Override
|
||||
public void windowClosing(final WindowEvent event) {
|
||||
if (!gui.isClosing.getAndSet(true)) {
|
||||
- frame.setTitle("Minecraft server - shutting down!");
|
||||
+ frame.setTitle("Purpur Minecraft server - shutting down!"); // Purpur - Improve GUI
|
||||
server.halt(true);
|
||||
gui.runFinalizers();
|
||||
}
|
||||
@@ -114,7 +_,7 @@
|
||||
|
||||
private JComponent buildChatPanel() {
|
||||
JPanel panel = new JPanel(new BorderLayout());
|
||||
- JTextArea chatArea = new JTextArea();
|
||||
+ org.purpurmc.purpur.gui.JColorTextPane chatArea = new org.purpurmc.purpur.gui.JColorTextPane(); // Purpur - GUI Improvements
|
||||
JScrollPane scrollPane = new JScrollPane(chatArea, ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
|
||||
chatArea.setEditable(false);
|
||||
chatArea.setFont(MONOSPACED);
|
||||
@@ -123,10 +_,43 @@
|
||||
String text = chatField.getText().trim();
|
||||
if (!text.isEmpty()) {
|
||||
this.server.handleConsoleInput(text, this.server.createCommandSourceStack());
|
||||
+ // Purpur start - GUI Improvements
|
||||
+ history.add(text);
|
||||
+ historyIndex = -1;
|
||||
+ // Purpur end - GUI Improvements
|
||||
}
|
||||
|
||||
chatField.setText("");
|
||||
});
|
||||
+ // Purpur start - GUI Improvements
|
||||
+ chatField.getInputMap().put(javax.swing.KeyStroke.getKeyStroke("UP"), "up");
|
||||
+ chatField.getInputMap().put(javax.swing.KeyStroke.getKeyStroke("DOWN"), "down");
|
||||
+ chatField.getActionMap().put("up", new javax.swing.AbstractAction() {
|
||||
+ @Override
|
||||
+ public void actionPerformed(java.awt.event.ActionEvent actionEvent) {
|
||||
+ if (historyIndex < 0) {
|
||||
+ currentCommand = chatField.getText();
|
||||
+ }
|
||||
+ if (historyIndex < history.size() - 1) {
|
||||
+ chatField.setText(history.get(++historyIndex));
|
||||
+ }
|
||||
+ }
|
||||
+ });
|
||||
+ chatField.getActionMap().put("down", new javax.swing.AbstractAction() {
|
||||
+ @Override
|
||||
+ public void actionPerformed(java.awt.event.ActionEvent actionEvent) {
|
||||
+ if (historyIndex >= 0) {
|
||||
+ if (historyIndex == 0) {
|
||||
+ --historyIndex;
|
||||
+ chatField.setText(currentCommand);
|
||||
+ } else {
|
||||
+ --historyIndex;
|
||||
+ chatField.setText(history.get(historyIndex));
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ });
|
||||
+ // Purpur end - GUI Improvements
|
||||
chatArea.addFocusListener(new FocusAdapter() {
|
||||
@Override
|
||||
public void focusGained(final FocusEvent arg0) {
|
||||
@@ -161,7 +_,7 @@
|
||||
}
|
||||
|
||||
private static final java.util.regex.Pattern ANSI = java.util.regex.Pattern.compile("\\e\\[[\\d;]*[^\\d;]"); // CraftBukkit // Paper
|
||||
- public void print(final JTextArea console, final JScrollPane scrollPane, final String line) {
|
||||
+ public void print(final org.purpurmc.purpur.gui.JColorTextPane console, final JScrollPane scrollPane, final String line) {
|
||||
if (!SwingUtilities.isEventDispatchThread()) {
|
||||
SwingUtilities.invokeLater(() -> this.print(console, scrollPane, line));
|
||||
} else {
|
||||
@@ -172,16 +_,29 @@
|
||||
shouldScroll = scrollBar.getValue() + scrollBar.getSize().getHeight() + MONOSPACED.getSize() * 4 > scrollBar.getMaximum();
|
||||
}
|
||||
|
||||
- try {
|
||||
+ /*try { // Purpur - GUI Improvements
|
||||
document.insertString(document.getLength(), MinecraftServerGui.ANSI.matcher(line).replaceAll(""), null); // CraftBukkit
|
||||
} catch (BadLocationException var8) {
|
||||
- }
|
||||
+ }*/ // Purpur - GUI Improvements
|
||||
+ console.append(line); // Purpur - GUI Improvements
|
||||
|
||||
if (shouldScroll) {
|
||||
scrollBar.setValue(Integer.MAX_VALUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
+
|
||||
+ // Purpur start - GUI Improvements
|
||||
+ public static class CommandHistory extends java.util.LinkedList<String> {
|
||||
+ @Override
|
||||
+ public boolean add(String command) {
|
||||
+ if (size() > 1000) {
|
||||
+ remove();
|
||||
+ }
|
||||
+ return super.offerFirst(command);
|
||||
+ }
|
||||
+ }
|
||||
+ // Purpur end - GUI Improvements
|
||||
|
||||
// Paper start - Add onboarding message for initial server start
|
||||
private JComponent buildOnboardingPanel() {
|
||||
@@ -0,0 +1,181 @@
|
||||
--- a/net/minecraft/server/level/ServerLevel.java
|
||||
+++ b/net/minecraft/server/level/ServerLevel.java
|
||||
@@ -220,6 +_,8 @@
|
||||
private final StructureManager structureManager;
|
||||
private final StructureCheck structureCheck;
|
||||
private final boolean tickTime;
|
||||
+ private double preciseTime; // Purpur - Configurable daylight cycle
|
||||
+ private boolean forceTime; // Purpur - Configurable daylight cycle
|
||||
private final LevelDebugSynchronizers debugSynchronizers = new LevelDebugSynchronizers(this);
|
||||
|
||||
// CraftBukkit start
|
||||
@@ -389,8 +_,25 @@
|
||||
// CraftBukkit end
|
||||
this.tickTime = tickTime;
|
||||
this.server = server;
|
||||
- this.customSpawners = customSpawners;
|
||||
+ this.customSpawners = new ArrayList<>(); // Purpur - Allow toggling special MobSpawners per world
|
||||
this.serverLevelData = levelData;
|
||||
+ // Purpur start - Allow toggling special MobSpawners per world
|
||||
+ if (purpurConfig.phantomSpawning) {
|
||||
+ this.customSpawners.add(new net.minecraft.world.level.levelgen.PhantomSpawner());
|
||||
+ }
|
||||
+ if (purpurConfig.patrolSpawning) {
|
||||
+ this.customSpawners.add(new net.minecraft.world.level.levelgen.PatrolSpawner());
|
||||
+ }
|
||||
+ if (purpurConfig.catSpawning) {
|
||||
+ this.customSpawners.add(new net.minecraft.world.entity.npc.CatSpawner());
|
||||
+ }
|
||||
+ if (purpurConfig.villageSiegeSpawning) {
|
||||
+ this.customSpawners.add(new net.minecraft.world.entity.ai.village.VillageSiege());
|
||||
+ }
|
||||
+ if (purpurConfig.villagerTraderSpawning) {
|
||||
+ this.customSpawners.add(new net.minecraft.world.entity.npc.wanderingtrader.WanderingTraderSpawner(savedDataStorage));
|
||||
+ }
|
||||
+ // Purpur end - Allow toggling special MobSpawners per world
|
||||
ChunkGenerator generator = levelStem.generator();
|
||||
// CraftBukkit start
|
||||
// Paper start - per-world time
|
||||
@@ -481,6 +_,7 @@
|
||||
this.environmentAttributes = EnvironmentAttributeSystem.builder().addDefaultLayers(this).build();
|
||||
this.updateSkyBrightness();
|
||||
this.getCraftServer().addWorld(this.getWorld()); // CraftBukkit
|
||||
+ this.preciseTime = this.serverLevelData.getGameTime(); // Purpur - Configurable daylight cycle
|
||||
}
|
||||
|
||||
// Paper start
|
||||
@@ -549,7 +_,7 @@
|
||||
}
|
||||
|
||||
int percentage = this.getGameRules().get(GameRules.PLAYERS_SLEEPING_PERCENTAGE);
|
||||
- if (this.sleepStatus.areEnoughSleeping(percentage) && this.sleepStatus.areEnoughDeepSleeping(percentage, this.players)) {
|
||||
+ if (this.purpurConfig.playersSkipNight && this.sleepStatus.areEnoughSleeping(percentage) && this.sleepStatus.areEnoughDeepSleeping(percentage, this.players)) { // Purpur - Config for skipping night
|
||||
Optional<Holder<WorldClock>> defaultClock = this.dimensionType().defaultClock();
|
||||
org.bukkit.event.world.ClockTimeSkipEvent event = null; // Paper - per-world time
|
||||
if (this.getGameRules().get(GameRules.ADVANCE_TIME) && defaultClock.isPresent()) {
|
||||
@@ -759,9 +_,18 @@
|
||||
&& this.random.nextDouble() < difficulty.getEffectiveDifficulty() * this.paperConfig().entities.spawning.skeletonHorseThunderSpawnChance.or(0.01) // Paper - Configurable spawn chances for skeleton horses
|
||||
&& !this.getBlockState(pos.below()).is(BlockTags.LIGHTNING_RODS);
|
||||
if (isTrap) {
|
||||
+ // Purpur start - Special mobs naturally spawn
|
||||
+ net.minecraft.world.entity.animal.equine.AbstractHorse skeletonHorse;
|
||||
+ if (purpurConfig.zombieHorseSpawnChance > 0D && random.nextDouble() <= purpurConfig.zombieHorseSpawnChance) {
|
||||
+ skeletonHorse = EntityTypes.ZOMBIE_HORSE.create(this, EntitySpawnReason.EVENT);
|
||||
+ } else {
|
||||
+ skeletonHorse = EntityTypes.SKELETON_HORSE.create(this, EntitySpawnReason.EVENT);
|
||||
+ if (skeletonHorse != null) ((SkeletonHorse) skeletonHorse).setTrap(true);
|
||||
+ }
|
||||
+ // Purpur end - Special mobs naturally spawn
|
||||
SkeletonHorse horse = EntityTypes.SKELETON_HORSE.create(this, EntitySpawnReason.EVENT);
|
||||
if (horse != null) {
|
||||
- horse.setTrap(true);
|
||||
+ //horse.setTrap(true); // Purpur - Special mobs naturally spawn - moved up
|
||||
horse.setAge(0);
|
||||
horse.setPos(pos.getX(), pos.getY(), pos.getZ());
|
||||
this.addFreshEntity(horse, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.LIGHTNING); // CraftBukkit
|
||||
@@ -796,9 +_,35 @@
|
||||
if (state.is(Blocks.SNOW)) {
|
||||
int currentLayers = state.getValue(SnowLayerBlock.LAYERS);
|
||||
if (currentLayers < Math.min(maxHeight, 8)) {
|
||||
+ // Purpur start - Smooth snow accumulation
|
||||
+ boolean canSnow = true;
|
||||
+ // Ensure snow doesn't get more than N layers taller than its neighbors
|
||||
+ // We only need to check blocks that are taller than the minimum step height
|
||||
+ if (org.purpurmc.purpur.PurpurConfig.smoothSnowAccumulationStep > 0 && currentLayers >= org.purpurmc.purpur.PurpurConfig.smoothSnowAccumulationStep) {
|
||||
+ int layersValueMin = currentLayers - org.purpurmc.purpur.PurpurConfig.smoothSnowAccumulationStep;
|
||||
+ for (Direction direction : Direction.Plane.HORIZONTAL) {
|
||||
+ BlockPos blockPosNeighbor = topPos.relative(direction);
|
||||
+ BlockState blockStateNeighbor = this.getBlockState(blockPosNeighbor);
|
||||
+ if (blockStateNeighbor.is(Blocks.SNOW)) {
|
||||
+ // Special check for snow layers, if neighbors are too short, don't accumulate
|
||||
+ int layersValueNeighbor = blockStateNeighbor.getValue(SnowLayerBlock.LAYERS);
|
||||
+ if (layersValueNeighbor <= layersValueMin) {
|
||||
+ canSnow = false;
|
||||
+ break;
|
||||
+ }
|
||||
+ } else if (!Block.isFaceFull(blockStateNeighbor.getCollisionShape(this, blockPosNeighbor), direction.getOpposite())) {
|
||||
+ // Since our layer is tall enough already, if we have a non-full neighbor block, don't accumulate
|
||||
+ canSnow = false;
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ if (canSnow) {
|
||||
+ // Purpur end - Smooth snow accumulation
|
||||
BlockState newState = state.setValue(SnowLayerBlock.LAYERS, currentLayers + 1);
|
||||
Block.pushEntitiesUp(state, newState, this, topPos);
|
||||
org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(this, topPos, newState, Block.UPDATE_ALL, null); // CraftBukkit
|
||||
+ } // Purpur - Smooth snow accumulation
|
||||
}
|
||||
} else {
|
||||
org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(this, topPos, Blocks.SNOW.defaultBlockState(), Block.UPDATE_ALL, null); // CraftBukkit
|
||||
@@ -819,7 +_,7 @@
|
||||
p -> p.is(PoiTypes.LIGHTNING_ROD),
|
||||
lightningRodPos -> lightningRodPos.getY() == this.getHeight(Heightmap.Types.WORLD_SURFACE, lightningRodPos.getX(), lightningRodPos.getZ()) - 1,
|
||||
center,
|
||||
- 128,
|
||||
+ org.purpurmc.purpur.PurpurConfig.lightningRodRange, // Purpur - Make lightning rod range configurable
|
||||
PoiManager.Occupancy.ANY
|
||||
);
|
||||
return nearbyLightningRod.map(blockPos -> blockPos.above(1));
|
||||
@@ -867,8 +_,26 @@
|
||||
int percentage = this.getGameRules().get(GameRules.PLAYERS_SLEEPING_PERCENTAGE);
|
||||
Component message;
|
||||
if (this.sleepStatus.areEnoughSleeping(percentage)) {
|
||||
+ // Purpur start - Customizable sleeping actionbar messages
|
||||
+ if (org.purpurmc.purpur.PurpurConfig.sleepSkippingNight.isBlank()) {
|
||||
+ return;
|
||||
+ }
|
||||
+ if (!org.purpurmc.purpur.PurpurConfig.sleepSkippingNight.equalsIgnoreCase("default")) {
|
||||
+ message = io.papermc.paper.adventure.PaperAdventure.asVanilla(net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(org.purpurmc.purpur.PurpurConfig.sleepSkippingNight));
|
||||
+ } else
|
||||
+ // Purpur end - Customizable sleeping actionbar messages
|
||||
message = Component.translatable("sleep.skipping_night");
|
||||
} else {
|
||||
+ // Purpur start - Customizable sleeping actionbar messages
|
||||
+ if (org.purpurmc.purpur.PurpurConfig.sleepingPlayersPercent.isBlank()) {
|
||||
+ return;
|
||||
+ }
|
||||
+ if (!org.purpurmc.purpur.PurpurConfig.sleepingPlayersPercent.equalsIgnoreCase("default")) {
|
||||
+ message = io.papermc.paper.adventure.PaperAdventure.asVanilla(net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(org.purpurmc.purpur.PurpurConfig.sleepingPlayersPercent,
|
||||
+ net.kyori.adventure.text.minimessage.tag.resolver.Placeholder.parsed("count", Integer.toString(this.sleepStatus.amountSleeping())),
|
||||
+ net.kyori.adventure.text.minimessage.tag.resolver.Placeholder.parsed("total", Integer.toString(this.sleepStatus.sleepersNeeded(percentage)))));
|
||||
+ } else
|
||||
+ // Purpur end - Customizable sleeping actionbar messages
|
||||
message = Component.translatable("sleep.players_sleeping", this.sleepStatus.amountSleeping(), this.sleepStatus.sleepersNeeded(percentage));
|
||||
}
|
||||
|
||||
@@ -1034,6 +_,7 @@
|
||||
public void resetWeatherCycle() {
|
||||
WeatherData weatherData = this.getWeatherData();
|
||||
// CraftBukkit start
|
||||
+ if (this.purpurConfig.rainStopsAfterSleep) // Purpur - Option for if rain and thunder should stop on sleep
|
||||
weatherData.setRaining(false, org.bukkit.event.weather.WeatherChangeEvent.Cause.SLEEP); // Paper - Add cause to Weather/ThunderChangeEvents
|
||||
// If we stop due to everyone sleeping we should reset the weather duration to some other random value.
|
||||
// Not that everyone ever manages to get the whole server to sleep at the same time....
|
||||
@@ -1041,6 +_,7 @@
|
||||
weatherData.setRainTime(0);
|
||||
}
|
||||
// CraftBukkit end
|
||||
+ if (this.purpurConfig.thunderStopsAfterSleep) // Purpur - Option for if rain and thunder should stop on sleep
|
||||
weatherData.setThundering(false, org.bukkit.event.weather.ThunderChangeEvent.Cause.SLEEP); // Paper - Add cause to Weather/ThunderChangeEvents
|
||||
// CraftBukkit start
|
||||
// If we stop due to everyone sleeping we should reset the weather duration to some other random value.
|
||||
@@ -1690,7 +_,7 @@
|
||||
Explosion.BlockInteraction blockInteraction = switch (interactionType) {
|
||||
case NONE -> Explosion.BlockInteraction.KEEP;
|
||||
case BLOCK -> this.getDestroyType(GameRules.BLOCK_EXPLOSION_DROP_DECAY);
|
||||
- case MOB -> this.getGameRules().get(GameRules.MOB_GRIEFING)
|
||||
+ case MOB -> ((source instanceof net.minecraft.world.entity.projectile.hurtingprojectile.LargeFireball) ? this.getGameRules().get(GameRules.MOB_GRIEFING, this.purpurConfig.fireballsMobGriefingOverride) : this.getGameRules().get(GameRules.MOB_GRIEFING)) // Purpur - Add mobGriefing override to everything affected
|
||||
? this.getDestroyType(GameRules.MOB_EXPLOSION_DROP_DECAY)
|
||||
: Explosion.BlockInteraction.KEEP;
|
||||
case TNT -> this.getDestroyType(GameRules.TNT_EXPLOSION_DROP_DECAY);
|
||||
@@ -2586,7 +_,7 @@
|
||||
// Spigot start
|
||||
if (entity.getBukkitEntity() instanceof org.bukkit.inventory.InventoryHolder && (!(entity instanceof ServerPlayer) || entity.getRemovalReason() != Entity.RemovalReason.KILLED)) { // SPIGOT-6876: closeInventory clears death message
|
||||
// Paper start - Fix merchant inventory not closing on entity removal
|
||||
- if (entity.getBukkitEntity() instanceof org.bukkit.inventory.Merchant merchant && merchant.getTrader() != null) {
|
||||
+ if (!entity.level().purpurConfig.playerVoidTrading && entity.getBukkitEntity() instanceof org.bukkit.inventory.Merchant merchant && merchant.getTrader() != null) { // Purpur - Allow void trading
|
||||
merchant.getTrader().closeInventory(org.bukkit.event.inventory.InventoryCloseEvent.Reason.UNLOADED);
|
||||
}
|
||||
// Paper end - Fix merchant inventory not closing on entity removal
|
||||
@@ -0,0 +1,171 @@
|
||||
--- a/net/minecraft/world/entity/Entity.java
|
||||
+++ b/net/minecraft/world/entity/Entity.java
|
||||
@@ -165,6 +_,7 @@
|
||||
SlotProvider,
|
||||
DebugValueSource,
|
||||
TypedInstance<EntityType<?>> {
|
||||
+ public static javax.script.ScriptEngine scriptEngine = new javax.script.ScriptEngineManager().getEngineByName("rhino"); // Purpur - Configurable entity base attributes
|
||||
// CraftBukkit start
|
||||
private static final int CURRENT_LEVEL = 2;
|
||||
static boolean isLevelAtLeast(ValueInput input, int level) {
|
||||
@@ -341,8 +_,9 @@
|
||||
public double xOld;
|
||||
public double yOld;
|
||||
public double zOld;
|
||||
+ public float maxUpStep; // Purpur - Add option to set armorstand step height
|
||||
public boolean noPhysics;
|
||||
- public final RandomSource random = SHARED_RANDOM; // Paper - Share random for entities to make them more random
|
||||
+ public final RandomSource random; // Paper - Share random for entities to make them more random // Purpur - Add toggle for RNG manipulation
|
||||
public int tickCount;
|
||||
private int remainingFireTicks;
|
||||
private final EntityFluidInteraction fluidInteraction = new EntityFluidInteraction(Set.of(FluidTags.WATER, FluidTags.LAVA));
|
||||
@@ -375,8 +_,8 @@
|
||||
public @Nullable PortalProcessor portalProcess;
|
||||
public int portalCooldown;
|
||||
private boolean invulnerable;
|
||||
- protected UUID uuid = Mth.createInsecureUUID(this.random);
|
||||
- protected String stringUUID = this.uuid.toString();
|
||||
+ protected UUID uuid; // Purpur - Add toggle for RNG manipulation
|
||||
+ protected String stringUUID; // Purpur - Add toggle for RNG manipulation
|
||||
private boolean hasGlowingTag;
|
||||
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};
|
||||
@@ -427,6 +_,7 @@
|
||||
private final int despawnTime; // Paper - entity despawn time limit
|
||||
public int totalEntityAge; // Paper - age-like counter for all entities
|
||||
public final io.papermc.paper.entity.activation.ActivationType activationType = io.papermc.paper.entity.activation.ActivationType.activationTypeFor(this); // Paper - EAR 2/tracking ranges
|
||||
+ public @Nullable Boolean immuneToFire = null; // Purpur - Fire immune API
|
||||
// Paper start - EAR 2
|
||||
public final boolean defaultActivationState;
|
||||
public long activatedTick = Integer.MIN_VALUE;
|
||||
@@ -444,11 +_,23 @@
|
||||
}
|
||||
// Paper end
|
||||
|
||||
+ // Purpur start - Add canSaveToDisk to Entity
|
||||
+ public boolean canSaveToDisk() {
|
||||
+ return true;
|
||||
+ }
|
||||
+ // Purpur end - Add canSaveToDisk to Entity
|
||||
+
|
||||
public Entity(final EntityType<?> type, final Level level) {
|
||||
this.type = type;
|
||||
this.level = level;
|
||||
this.id = level.getNextEntityId();
|
||||
this.dimensions = type.getDimensions();
|
||||
+ this.maxAirTicks = level == null || this.getDefaultMaxAirSupply() != Entity.TOTAL_AIR_SUPPLY ? this.getDefaultMaxAirSupply() : this.level.purpurConfig.drowningAirTicks; // Purpur - Drowning Settings
|
||||
+ // Purpur start - Add toggle for RNG manipulation
|
||||
+ this.random = level == null || level.purpurConfig.entitySharedRandom ? SHARED_RANDOM : RandomSource.create();
|
||||
+ this.uuid = Mth.createInsecureUUID(this.random);
|
||||
+ this.stringUUID = this.uuid.toString();
|
||||
+ // Purpur end - Add toggle for RNG manipulation
|
||||
this.position = Vec3.ZERO;
|
||||
this.blockPosition = BlockPos.ZERO;
|
||||
this.chunkPosition = ChunkPos.ZERO;
|
||||
@@ -850,6 +_,7 @@
|
||||
&& this.level.paperConfig().environment.netherCeilingVoidDamageHeight.test(v -> this.getY() >= v)
|
||||
&& (!(this instanceof Player player) || !player.getAbilities().invulnerable))) {
|
||||
// Paper end - Configurable nether ceiling damage
|
||||
+ if (this.level.purpurConfig.teleportOnNetherCeilingDamage && this.level.getWorld().getEnvironment() == org.bukkit.World.Environment.NETHER && this instanceof ServerPlayer player) player.teleport(org.bukkit.craftbukkit.util.CraftLocation.toBukkit(this.level.levelData.getRespawnData().pos(), this.level)); else // Purpur - Add option to teleport to spawn on nether ceiling damage
|
||||
this.onBelowWorld();
|
||||
}
|
||||
}
|
||||
@@ -1945,7 +_,7 @@
|
||||
}
|
||||
|
||||
public boolean fireImmune() {
|
||||
- return this.getType().fireImmune();
|
||||
+ return this.immuneToFire != null ? immuneToFire : this.getType().fireImmune(); // Purpur - add fire immune API
|
||||
}
|
||||
|
||||
public boolean causeFallDamage(final double fallDistance, final float damageModifier, final DamageSource damageSource) {
|
||||
@@ -2531,7 +_,7 @@
|
||||
output.putBoolean("Bukkit.invisible", this.persistentInvisibility);
|
||||
}
|
||||
// SPIGOT-6907: re-implement LivingEntity#setMaximumAir()
|
||||
- if (this.maxAirTicks != this.getDefaultMaxAirSupply()) {
|
||||
+ if (this.maxAirTicks != this.getDefaultMaxAirSupply() && this.getDefaultMaxAirSupply() != this.level().purpurConfig.drowningAirTicks) { // Purpur - Drowning Settings
|
||||
output.putInt("Bukkit.MaxAirSupply", this.getMaxAirSupply());
|
||||
}
|
||||
output.putInt("Spigot.ticksLived", this.totalEntityAge); // Paper
|
||||
@@ -2617,6 +_,11 @@
|
||||
output.putBoolean("Paper.FreezeLock", true);
|
||||
}
|
||||
// Paper end
|
||||
+ // Purpur start - Fire immune API
|
||||
+ if (immuneToFire != null) {
|
||||
+ output.putBoolean("Purpur.FireImmune", immuneToFire);
|
||||
+ }
|
||||
+ // Purpur end - Fire immune API
|
||||
} catch (Throwable t) {
|
||||
CrashReport report = CrashReport.forThrowable(t, "Saving entity NBT");
|
||||
CrashReportCategory category = report.addCategory("Entity being saved");
|
||||
@@ -2740,6 +_,9 @@
|
||||
}
|
||||
freezeLocked = input.getBooleanOr("Paper.FreezeLock", false);
|
||||
// Paper end
|
||||
+
|
||||
+ immuneToFire = input.read("Purpur.FireImmune", com.mojang.serialization.Codec.BOOL).orElse(null); // Purpur - Fire immune API
|
||||
+
|
||||
} catch (Throwable t) {
|
||||
CrashReport report = CrashReport.forThrowable(t, "Loading entity NBT");
|
||||
CrashReportCategory category = report.addCategory("Entity being loaded");
|
||||
@@ -2916,6 +_,7 @@
|
||||
if (this.isAlive() && this instanceof Leashable leashable) {
|
||||
if (leashable.getLeashHolder() == player) {
|
||||
if (!this.level().isClientSide()) {
|
||||
+ if (hand == InteractionHand.OFF_HAND && (level().purpurConfig.villagerCanBeLeashed || level().purpurConfig.wanderingTraderCanBeLeashed) && this instanceof net.minecraft.world.entity.npc.villager.AbstractVillager) return InteractionResult.CONSUME; // Purpur - Allow leashing villagers
|
||||
// Paper start - EntityUnleashEvent
|
||||
if (!org.bukkit.craftbukkit.event.CraftEventFactory.handlePlayerUnleashEntityEvent(
|
||||
leashable, player, hand, !player.hasInfiniteMaterials(), true
|
||||
@@ -3323,15 +_,18 @@
|
||||
return Vec3.directionFromRotation(this.getRotationVector());
|
||||
}
|
||||
|
||||
+ public BlockPos portalPos = BlockPos.ZERO; // Purpur - Fix stuck in portals
|
||||
public void setAsInsidePortal(final Portal portal, final BlockPos pos) {
|
||||
if (this.isOnPortalCooldown()) {
|
||||
+ if (!(level().purpurConfig.playerFixStuckPortal && this instanceof Player && !pos.equals(this.portalPos))) // Purpur - Fix stuck in portals
|
||||
this.setPortalCooldown();
|
||||
- } else {
|
||||
+ } else if (this.level.purpurConfig.entitiesCanUsePortals || this instanceof ServerPlayer) { // Purpur - Entities can use portals
|
||||
if (this.portalProcess == null || !this.portalProcess.isSamePortal(portal)) {
|
||||
this.portalProcess = new PortalProcessor(portal, pos.immutable());
|
||||
} else if (!this.portalProcess.isInsidePortalThisTick()) {
|
||||
this.portalProcess.updateEntryPosition(pos.immutable());
|
||||
this.portalProcess.setAsInsidePortalThisTick(true);
|
||||
+ this.portalPos = BlockPos.ZERO; // Purpur - Fix stuck in portals
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4065,7 +_,7 @@
|
||||
}
|
||||
|
||||
public boolean canUsePortal(final boolean ignorePassenger) {
|
||||
- return (ignorePassenger || !this.isPassenger()) && this.isAlive();
|
||||
+ return (ignorePassenger || !this.isPassenger()) && this.isAlive() && (this.level.purpurConfig.entitiesCanUsePortals || this instanceof ServerPlayer); // Purpur - Entities can use portals
|
||||
}
|
||||
|
||||
public boolean canTeleport(final Level from, final Level to) {
|
||||
@@ -4615,6 +_,12 @@
|
||||
return Mth.lerp(partial, this.yRotO, this.yRot);
|
||||
}
|
||||
|
||||
+ // Purpur start - Stop squids floating on top of water
|
||||
+ public AABB getAxisForFluidCheck() {
|
||||
+ return this.getBoundingBox().deflate(0.001D);
|
||||
+ }
|
||||
+ // Purpur end - Stop squids floating on top of water
|
||||
+
|
||||
public boolean touchingUnloadedChunk() {
|
||||
AABB box = this.getBoundingBox().inflate(1.0);
|
||||
int x0 = Mth.floor(box.minX);
|
||||
@@ -4909,7 +_,7 @@
|
||||
}
|
||||
|
||||
public float maxUpStep() {
|
||||
- return 0.0F;
|
||||
+ return maxUpStep; // Purpur - Add option to set armorstand step height
|
||||
}
|
||||
|
||||
public void onExplosionHit(final @Nullable Entity explosionCausedBy) {
|
||||
@@ -0,0 +1,44 @@
|
||||
--- a/net/minecraft/world/entity/EntityType.java
|
||||
+++ b/net/minecraft/world/entity/EntityType.java
|
||||
@@ -74,6 +_,16 @@
|
||||
private final FeatureFlagSet requiredFeatures;
|
||||
private final boolean allowedInPeaceful;
|
||||
|
||||
+ // Purpur start - PlayerSetSpawnerTypeWithEggEvent
|
||||
+ public static EntityType<?> getFromBukkitType(org.bukkit.entity.EntityType bukkitType) {
|
||||
+ return getFromKey(Identifier.parse(bukkitType.getKey().toString()));
|
||||
+ }
|
||||
+
|
||||
+ public static EntityType<?> getFromKey(Identifier location) {
|
||||
+ return BuiltInRegistries.ENTITY_TYPE.getValue(location);
|
||||
+ }
|
||||
+ // Purpur end - PlayerSetSpawnerTypeWithEggEvent
|
||||
+
|
||||
public static Identifier getKey(final EntityType<?> type) {
|
||||
return BuiltInRegistries.ENTITY_TYPE.getKey(type);
|
||||
}
|
||||
@@ -329,6 +_,16 @@
|
||||
return this.category;
|
||||
}
|
||||
|
||||
+ // Purpur start - PlayerSetSpawnerTypeWithEggEvent
|
||||
+ public String getName() {
|
||||
+ return BuiltInRegistries.ENTITY_TYPE.getKey(this).getPath();
|
||||
+ }
|
||||
+
|
||||
+ public String getTranslatedName() {
|
||||
+ return getDescription().getString();
|
||||
+ }
|
||||
+ // Purpur end - PlayerSetSpawnerTypeWithEggEvent
|
||||
+
|
||||
public String getDescriptionId() {
|
||||
return this.descriptionId;
|
||||
}
|
||||
@@ -405,6 +_,7 @@
|
||||
// Paper start - Add logging for debugging entity tags with invalid ids
|
||||
() -> {
|
||||
LOGGER.warn("Skipping Entity with id {}", input.getStringOr("id", "[invalid]"));
|
||||
+ LOGGER.warn("Location: {} {}", level.getWorld().getName(), input.read("Pos", net.minecraft.world.phys.Vec3.CODEC).orElse(net.minecraft.world.phys.Vec3.ZERO)); // Purpur - log skipped entity's position
|
||||
if ((DEBUG_ENTITIES_WITH_INVALID_IDS || level.getCraftServer().getServer().isDebugging()) && input instanceof TagValueInput tagInput) {
|
||||
LOGGER.warn("Skipped entity tag: {}", tagInput.input);
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
--- a/net/minecraft/world/entity/LivingEntity.java
|
||||
+++ b/net/minecraft/world/entity/LivingEntity.java
|
||||
@@ -460,6 +_,12 @@
|
||||
if (dist < 0.0) {
|
||||
double damagePerBlock = level.getWorldBorder().getDamagePerBlock();
|
||||
if (damagePerBlock > 0.0) {
|
||||
+ // Purpur start - Add option to teleport to spawn if outside world border
|
||||
+ if (this.level().purpurConfig.teleportIfOutsideBorder && this instanceof ServerPlayer serverPlayer) {
|
||||
+ serverPlayer.teleport(org.bukkit.craftbukkit.util.CraftLocation.toBukkit(this.level().levelData.getRespawnData().pos(), this.level()));
|
||||
+ return;
|
||||
+ }
|
||||
+ // Purpur end - Add option to teleport to spawn if outside world border
|
||||
this.hurtServer(level, this.damageSources().outOfBorder(), Math.max(1, Mth.floor(-dist * damagePerBlock)));
|
||||
}
|
||||
}
|
||||
@@ -475,7 +_,7 @@
|
||||
if (this.shouldTakeDrowningDamage()) {
|
||||
this.setAirSupply(0);
|
||||
level.broadcastEntityEvent(this, EntityEvent.DROWN_PARTICLES);
|
||||
- this.hurtServer(level, this.damageSources().drown(), 2.0F);
|
||||
+ this.hurtServer(level, this.damageSources().drown(), (float) this.level().purpurConfig.damageFromDrowning); // Purpur - Drowning Settings
|
||||
}
|
||||
} else if (this.getAirSupply() < this.getMaxAirSupply() && MobEffectUtil.shouldEffectsRefillAirsupply(this)) {
|
||||
this.setAirSupply(this.increaseAirSupply(this.getAirSupply()));
|
||||
@@ -535,7 +_,7 @@
|
||||
}
|
||||
|
||||
protected boolean shouldTakeDrowningDamage() {
|
||||
- return this.getAirSupply() <= -20;
|
||||
+ return this.getAirSupply() <= -this.level().purpurConfig.drowningDamageInterval; // Purpur - Drowning Settings
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1070,14 +_,32 @@
|
||||
|
||||
if (targetingEntity != null) {
|
||||
ItemStack itemStack = this.getItemBySlot(EquipmentSlot.HEAD);
|
||||
- if (targetingEntity.is(EntityTypes.SKELETON) && itemStack.is(Items.SKELETON_SKULL)
|
||||
- || targetingEntity.is(EntityTypes.ZOMBIE) && itemStack.is(Items.ZOMBIE_HEAD)
|
||||
- || targetingEntity.is(EntityTypes.PIGLIN) && itemStack.is(Items.PIGLIN_HEAD)
|
||||
- || targetingEntity.is(EntityTypes.PIGLIN_BRUTE) && itemStack.is(Items.PIGLIN_HEAD)
|
||||
- || targetingEntity.is(EntityTypes.CREEPER) && itemStack.is(Items.CREEPER_HEAD)) {
|
||||
- visibilityPercent *= 0.5;
|
||||
- }
|
||||
- }
|
||||
+ // Purpur start - Mob head visibility percent
|
||||
+ if (targetingEntity.is(EntityTypes.SKELETON) && itemStack.is(Items.SKELETON_SKULL)) {
|
||||
+ visibilityPercent *= targetingEntity.level().purpurConfig.skeletonHeadVisibilityPercent;
|
||||
+ }
|
||||
+ else if (targetingEntity.is(EntityTypes.ZOMBIE) && itemStack.is(Items.ZOMBIE_HEAD)) {
|
||||
+ visibilityPercent *= targetingEntity.level().purpurConfig.zombieHeadVisibilityPercent;
|
||||
+ }
|
||||
+ else if ((targetingEntity.is(EntityTypes.PIGLIN) || targetingEntity.is(EntityTypes.PIGLIN_BRUTE)) && itemStack.is(Items.PIGLIN_HEAD)) {
|
||||
+ visibilityPercent *= targetingEntity.level().purpurConfig.piglinHeadVisibilityPercent;
|
||||
+ }
|
||||
+ else if (targetingEntity.is(EntityTypes.CREEPER) && itemStack.is(Items.CREEPER_HEAD)) {
|
||||
+ visibilityPercent *= targetingEntity.level().purpurConfig.creeperHeadVisibilityPercent;
|
||||
+ }
|
||||
+ // Purpur end - Mob head visibility percent
|
||||
+ }
|
||||
+
|
||||
+ // Purpur start - Configurable mob blindness
|
||||
+ if (targetingEntity instanceof LivingEntity entityliving) {
|
||||
+ if (entityliving.hasEffect(MobEffects.BLINDNESS)) {
|
||||
+ int amplifier = entityliving.getEffect(MobEffects.BLINDNESS).getAmplifier();
|
||||
+ for (int i = 0; i < amplifier; i++) {
|
||||
+ amplifier *= this.level().purpurConfig.mobsBlindnessMultiplier;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ // Purpur end - Configurable mob blindness
|
||||
|
||||
return visibilityPercent;
|
||||
}
|
||||
@@ -1127,6 +_,7 @@
|
||||
Iterator<MobEffectInstance> iterator = this.activeEffects.values().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
MobEffectInstance effect = iterator.next();
|
||||
+ if (cause == EntityPotionEffectEvent.Cause.MILK && !this.level().purpurConfig.milkClearsBeneficialEffects && effect.getEffect().value().isBeneficial()) continue; // Purpur - Milk Keeps Beneficial Effects
|
||||
EntityPotionEffectEvent event = CraftEventFactory.callEntityPotionEffectChangeEvent(this, effect, null, cause, EntityPotionEffectEvent.Action.CLEARED);
|
||||
if (event.isCancelled()) {
|
||||
continue;
|
||||
@@ -1459,7 +_,23 @@
|
||||
this.stopSleeping();
|
||||
}
|
||||
|
||||
- this.noActionTime = 0;
|
||||
+ // Purpur start - One Punch Man!
|
||||
+ if (source.getEntity() instanceof net.minecraft.world.entity.player.Player player && source.getEntity().level().purpurConfig.creativeOnePunch && !source.is(DamageTypeTags.IS_PROJECTILE)) {
|
||||
+ if (player.isCreative()) {
|
||||
+ org.apache.commons.lang3.mutable.MutableDouble attackDamage = new org.apache.commons.lang3.mutable.MutableDouble();
|
||||
+ player.getMainHandItem().forEachModifier(EquipmentSlot.MAINHAND, (attributeHolder, attributeModifier) -> {
|
||||
+ if (attributeModifier.operation() == AttributeModifier.Operation.ADD_VALUE) {
|
||||
+ attackDamage.addAndGet(attributeModifier.amount());
|
||||
+ }
|
||||
+ });
|
||||
+
|
||||
+ if (attackDamage.doubleValue() == 0.0D) {
|
||||
+ // One punch!
|
||||
+ damage = this.getHealth();
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ // Purpur end - One Punch Man!this.noActionTime = 0;
|
||||
if (damage < 0.0F) {
|
||||
damage = 0.0F;
|
||||
}
|
||||
@@ -1724,10 +_,10 @@
|
||||
protected @Nullable Player resolvePlayerResponsibleForDamage(final DamageSource source) {
|
||||
Entity sourceEntity = source.getEntity();
|
||||
if (sourceEntity instanceof Player playerSource) {
|
||||
- this.setLastHurtByPlayer(playerSource, 100);
|
||||
+ this.setLastHurtByPlayer(playerSource, this.level().purpurConfig.mobLastHurtByPlayerTime); // Purpur - Config for mob last hurt by player time
|
||||
} else if (sourceEntity instanceof Wolf wolf && wolf.isTame()) {
|
||||
if (wolf.getOwnerReference() != null) {
|
||||
- this.setLastHurtByPlayer(wolf.getOwnerReference().getUUID(), 100);
|
||||
+ this.setLastHurtByPlayer(wolf.getOwnerReference().getUUID(), this.level().purpurConfig.mobLastHurtByPlayerTime); // Purpur - Config for mob last hurt by player time
|
||||
} else {
|
||||
this.lastHurtByPlayer = null;
|
||||
this.lastHurtByPlayerMemoryTime = 0;
|
||||
@@ -1779,6 +_,17 @@
|
||||
}
|
||||
}
|
||||
|
||||
+ // Purpur start - Totems work in inventory
|
||||
+ if (level().purpurConfig.totemOfUndyingWorksInInventory && this instanceof ServerPlayer player && (itemStack == null || itemStack.getItem() != Items.TOTEM_OF_UNDYING) && player.getBukkitEntity().hasPermission("purpur.inventory_totem")) {
|
||||
+ for (ItemStack item : player.getInventory().getNonEquipmentItems()) {
|
||||
+ if (item.getItem() == Items.TOTEM_OF_UNDYING) {
|
||||
+ itemStack = item;
|
||||
+ protectionItem = item.copy();
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ // Purpur end - Totems work in inventory
|
||||
final org.bukkit.inventory.EquipmentSlot handSlot = (usedHand != null) ? org.bukkit.craftbukkit.CraftEquipmentSlot.getHand(usedHand) : null;
|
||||
final EntityResurrectEvent event = new EntityResurrectEvent((org.bukkit.entity.LivingEntity) this.getBukkitEntity(), handSlot);
|
||||
event.setCancelled(protectionItem == null);
|
||||
@@ -1956,6 +_,7 @@
|
||||
boolean playerKilled = this.lastHurtByPlayerMemoryTime > 0;
|
||||
this.dropEquipment(level); // CraftBukkit - from below
|
||||
if (this.shouldDropLoot(level)) {
|
||||
+ if (!(source.is(net.minecraft.world.damagesource.DamageTypes.CRAMMING) && level().purpurConfig.disableDropsOnCrammingDeath)) { // Purpur - Disable loot drops on death by cramming
|
||||
this.dropFromLootTable(level, source, playerKilled);
|
||||
// Paper start
|
||||
final boolean prev = this.clearEquipmentSlots;
|
||||
@@ -1964,6 +_,7 @@
|
||||
// Paper end
|
||||
this.dropCustomDeathLoot(level, source, playerKilled);
|
||||
this.clearEquipmentSlots = prev; // Paper
|
||||
+ } // Purpur - Disable loot drops on death by cramming
|
||||
}
|
||||
|
||||
// CraftBukkit start - Call death event // Paper start - call advancement triggers with correct entity equipment
|
||||
@@ -3313,6 +_,7 @@
|
||||
float dmg = (float)(diff * 10.0 - 3.0);
|
||||
if (dmg > 0.0F) {
|
||||
this.playSound(this.getFallDamageSound((int)dmg), 1.0F, 1.0F);
|
||||
+ if (level().purpurConfig.elytraKineticDamage) // Purpur - Toggle for kinetic damage
|
||||
this.hurt(this.damageSources().flyIntoWall(), dmg);
|
||||
}
|
||||
}
|
||||
@@ -4801,6 +_,12 @@
|
||||
? slot == EquipmentSlot.MAINHAND && this.canUseSlot(EquipmentSlot.MAINHAND)
|
||||
: slot == equippable.slot() && this.canUseSlot(equippable.slot()) && equippable.canBeEquippedBy(this.typeHolder());
|
||||
}
|
||||
+
|
||||
+ // Purpur start - Dispenser curse of binding protection
|
||||
+ public @Nullable EquipmentSlot getEquipmentSlotForDispenserItem(ItemStack itemstack) {
|
||||
+ return EnchantmentHelper.getItemEnchantmentLevel(net.minecraft.world.item.enchantment.Enchantments.BINDING_CURSE, itemstack) > 0 ? null : this.getEquipmentSlotForItem(itemstack);
|
||||
+ }
|
||||
+ // Purpur end - Dispenser curse of binding protection
|
||||
|
||||
private static SlotAccess createEquipmentSlotAccess(final LivingEntity entity, final EquipmentSlot equipmentSlot) {
|
||||
return equipmentSlot != EquipmentSlot.HEAD && equipmentSlot != EquipmentSlot.MAINHAND && equipmentSlot != EquipmentSlot.OFFHAND
|
||||
@@ -0,0 +1,11 @@
|
||||
--- a/net/minecraft/world/entity/ai/behavior/AcquirePoi.java
|
||||
+++ b/net/minecraft/world/entity/ai/behavior/AcquirePoi.java
|
||||
@@ -95,7 +_,7 @@
|
||||
return true;
|
||||
};
|
||||
Set<Pair<Holder<PoiType>, BlockPos>> poiPositions = poiManager.findAllClosestFirstWithType(
|
||||
- poiType, cacheTest, body.blockPosition(), SCAN_RANGE, PoiManager.Occupancy.HAS_SPACE
|
||||
+ poiType, cacheTest, body.blockPosition(), level.purpurConfig.villagerAcquirePoiSearchRadius, PoiManager.Occupancy.HAS_SPACE // Purpur - Configurable villager search radius
|
||||
)
|
||||
.limit(5L)
|
||||
.filter(px -> validPoi.test(level, (BlockPos)px.getSecond()))
|
||||
@@ -0,0 +1,73 @@
|
||||
--- a/net/minecraft/world/entity/animal/bee/Bee.java
|
||||
+++ b/net/minecraft/world/entity/animal/bee/Bee.java
|
||||
@@ -169,7 +_,7 @@
|
||||
// Paper end - Fix MC-167279
|
||||
this.lookControl = new Bee.BeeLookControl(this);
|
||||
this.setPathfindingMalus(PathType.FIRE, -1.0F);
|
||||
- this.setPathfindingMalus(PathType.WATER, -1.0F);
|
||||
+ if (this.level().purpurConfig.beeCanInstantlyStartDrowning) this.setPathfindingMalus(PathType.WATER, -1.0F); // Purpur - bee can instantly start drowning in water option
|
||||
this.setPathfindingMalus(PathType.WATER_BORDER, 16.0F);
|
||||
this.setPathfindingMalus(PathType.COCOA, -1.0F);
|
||||
this.setPathfindingMalus(PathType.FENCE, -1.0F);
|
||||
@@ -360,13 +_,19 @@
|
||||
if (this.stayOutOfHiveCountdown <= 0 && !this.beePollinateGoal.isPollinating() && !this.hasStung() && this.getTarget() == null) {
|
||||
boolean wantsToEnterHive = this.hasNectar()
|
||||
|| this.isTiredOfLookingForNectar()
|
||||
- || this.level().environmentAttributes().getValue(EnvironmentAttributes.BEES_STAY_IN_HIVE, this.position());
|
||||
+ || this.level().environmentAttributes().getValue(EnvironmentAttributes.BEES_STAY_IN_HIVE, this.position()) || isNightOrRaining(this.level()); // Purpur - Bee can work when raining or at night
|
||||
return wantsToEnterHive && !this.isHiveNearFire();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
+ // Purpur start - Bee can work when raining or at night
|
||||
+ public static boolean isNightOrRaining(Level level) {
|
||||
+ return level.dimensionType().hasSkyLight() && (level.isDarkOutside() && !level.purpurConfig.beeCanWorkAtNight || level.isRaining() && !level.purpurConfig.beeCanWorkInRain); // Purpur - Bee can work when raining or at night
|
||||
+ }
|
||||
+ // Purpur end - Bee can work when raining or at night
|
||||
+
|
||||
public void setStayOutOfHiveCountdown(final int ticks) {
|
||||
this.stayOutOfHiveCountdown = ticks;
|
||||
}
|
||||
@@ -387,7 +_,7 @@
|
||||
@Override
|
||||
protected void customServerAiStep(final ServerLevel level) {
|
||||
boolean hasStung = this.hasStung();
|
||||
- if (this.isInWater()) {
|
||||
+ if (this.level().purpurConfig.beeCanInstantlyStartDrowning && this.isInWater()) { // Purpur - bee can instantly start drowning in water option
|
||||
this.underWaterTicks++;
|
||||
} else {
|
||||
this.underWaterTicks = 0;
|
||||
@@ -397,6 +_,7 @@
|
||||
this.hurtServer(level, this.damageSources().drown(), 1.0F);
|
||||
}
|
||||
|
||||
+ if (hasStung && !this.level().purpurConfig.beeDiesAfterSting) setHasStung(false); else // Purpur - Stop bees from dying after stinging
|
||||
if (hasStung) {
|
||||
this.timeSinceSting++;
|
||||
if (this.timeSinceSting % 5 == 0 && this.random.nextInt(Mth.clamp(1200 - this.timeSinceSting, 1, 1200)) == 0) {
|
||||
@@ -1128,6 +_,7 @@
|
||||
Bee.this.savedFlowerPos = nearbyPos.get();
|
||||
Bee.this.navigation
|
||||
.moveTo(Bee.this.savedFlowerPos.getX() + 0.5, Bee.this.savedFlowerPos.getY() + 0.5, Bee.this.savedFlowerPos.getZ() + 0.5, 1.2F);
|
||||
+ new org.purpurmc.purpur.event.entity.BeeFoundFlowerEvent((org.bukkit.entity.Bee) Bee.this.getBukkitEntity(), org.bukkit.craftbukkit.util.CraftLocation.toBukkit(Bee.this.savedFlowerPos, Bee.this.level())).callEvent(); // Purpur - Bee API
|
||||
return true;
|
||||
} else {
|
||||
Bee.this.remainingCooldownBeforeLocatingNewFlower = Mth.nextInt(Bee.this.random, 20, 60);
|
||||
@@ -1174,6 +_,7 @@
|
||||
this.pollinating = false;
|
||||
Bee.this.navigation.stop();
|
||||
Bee.this.remainingCooldownBeforeLocatingNewFlower = 200;
|
||||
+ new org.purpurmc.purpur.event.entity.BeeStopPollinatingEvent((org.bukkit.entity.Bee) Bee.this.getBukkitEntity(), Bee.this.savedFlowerPos == null ? null : org.bukkit.craftbukkit.util.CraftLocation.toBukkit(Bee.this.savedFlowerPos, Bee.this.level()), Bee.this.hasNectar()).callEvent(); // Purpur - Bee API
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1220,6 +_,7 @@
|
||||
this.setWantedPos();
|
||||
}
|
||||
|
||||
+ if (this.successfulPollinatingTicks == 0) new org.purpurmc.purpur.event.entity.BeeStartedPollinatingEvent((org.bukkit.entity.Bee) Bee.this.getBukkitEntity(), org.bukkit.craftbukkit.util.CraftLocation.toBukkit(Bee.this.savedFlowerPos, Bee.this.level())).callEvent(); // Purpur - Bee API
|
||||
this.successfulPollinatingTicks++;
|
||||
if (Bee.this.random.nextFloat() < 0.05F && this.successfulPollinatingTicks > this.lastSoundPlayedTick + 60) {
|
||||
this.lastSoundPlayedTick = this.successfulPollinatingTicks;
|
||||
@@ -0,0 +1,45 @@
|
||||
--- a/net/minecraft/world/entity/animal/squid/Squid.java
|
||||
+++ b/net/minecraft/world/entity/animal/squid/Squid.java
|
||||
@@ -55,6 +_,25 @@
|
||||
this.tentacleSpeed = 1.0F / (this.random.nextFloat() + 1.0F) * 0.2F;
|
||||
}
|
||||
|
||||
+ // Purpur start - Stop squids floating on top of water
|
||||
+ @Override
|
||||
+ public net.minecraft.world.phys.AABB getAxisForFluidCheck() {
|
||||
+ // Stops squids from floating just over the water
|
||||
+ return super.getAxisForFluidCheck().offsetY(level().purpurConfig.squidOffsetWaterCheck);
|
||||
+ }
|
||||
+ // Purpur end - Stop squids floating on top of water
|
||||
+
|
||||
+ // Purpur start - Flying squids! Oh my!
|
||||
+ public boolean canFly() {
|
||||
+ return this.level().purpurConfig.squidsCanFly;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public boolean isInWater() {
|
||||
+ return this.wasTouchingWater || canFly();
|
||||
+ }
|
||||
+ // Purpur end - Flying squids! Oh my!
|
||||
+
|
||||
@Override
|
||||
protected void registerGoals() {
|
||||
this.goalSelector.addGoal(0, new Squid.SquidRandomMovementGoal(this));
|
||||
@@ -131,6 +_,7 @@
|
||||
}
|
||||
|
||||
if (this.isInWater()) {
|
||||
+ if (canFly()) setNoGravity(!wasTouchingWater); // Purpur - Flying squids! Oh my!
|
||||
if (this.tentacleMovement < Mth.PI) {
|
||||
float tentacleScale = this.tentacleMovement / Mth.PI;
|
||||
this.tentacleAngle = Mth.sin(tentacleScale * tentacleScale * Mth.PI) * Mth.PI * 0.25F;
|
||||
@@ -316,7 +_,7 @@
|
||||
int noActionTime = this.squid.getNoActionTime();
|
||||
if (noActionTime > 100) {
|
||||
this.squid.movementVector = Vec3.ZERO;
|
||||
- } else if (this.squid.getRandom().nextInt(reducedTickDelay(50)) == 0 || !this.squid.wasTouchingWater || !this.squid.hasMovementVector()) {
|
||||
+ } else if (this.squid.getRandom().nextInt(reducedTickDelay(50)) == 0 || !this.squid.isInWater() || !this.squid.hasMovementVector()) { // Purpur - Flying squids! Oh my!
|
||||
float angle = this.squid.getRandom().nextFloat() * (float) (Math.PI * 2);
|
||||
this.squid.movementVector = new Vec3(Mth.cos(angle) * 0.2F, -0.1F + this.squid.getRandom().nextFloat() * 0.2F, Mth.sin(angle) * 0.2F);
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
--- a/net/minecraft/world/entity/animal/wolf/Wolf.java
|
||||
+++ b/net/minecraft/world/entity/animal/wolf/Wolf.java
|
||||
@@ -104,6 +_,37 @@
|
||||
private static final EntityDimensions BABY_DIMENSIONS = EntityDimensions.scalable(0.3F, 0.425F)
|
||||
.withEyeHeight(0.34375F)
|
||||
.withAttachments(EntityAttachments.builder().attach(EntityAttachment.PASSENGER, 0.0F, 0.4375F, 0.0F));
|
||||
+ // Purpur start - Configurable chance for wolves to spawn rabid
|
||||
+ private boolean isRabid = false;
|
||||
+ private static final TargetingConditions.Selector RABID_PREDICATE = (entity, ignored) -> entity instanceof net.minecraft.server.level.ServerPlayer || entity instanceof net.minecraft.world.entity.Mob;
|
||||
+ private final net.minecraft.world.entity.ai.goal.Goal PATHFINDER_VANILLA = new NonTameRandomTargetGoal<>(this, Animal.class, false, PREY_SELECTOR);
|
||||
+ private final net.minecraft.world.entity.ai.goal.Goal PATHFINDER_RABID = new NonTameRandomTargetGoal<>(this, LivingEntity.class, false, RABID_PREDICATE);
|
||||
+ private static final class AvoidRabidWolfGoal extends AvoidEntityGoal<Wolf> {
|
||||
+ private final Wolf wolf;
|
||||
+
|
||||
+ public AvoidRabidWolfGoal(Wolf wolf, float distance, double minSpeed, double maxSpeed) {
|
||||
+ super(wolf, Wolf.class, distance, minSpeed, maxSpeed);
|
||||
+ this.wolf = wolf;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public boolean canUse() {
|
||||
+ return super.canUse() && !this.wolf.isRabid() && this.toAvoid != null && this.toAvoid.isRabid(); // wolves which are not rabid run away from rabid wolves
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void start() {
|
||||
+ this.wolf.setTarget(null);
|
||||
+ super.start();
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void tick() {
|
||||
+ this.wolf.setTarget(null);
|
||||
+ super.tick();
|
||||
+ }
|
||||
+ }
|
||||
+ // Purpur end - Configurable chance for wolves to spawn rabid
|
||||
private static final float START_HEALTH = 8.0F;
|
||||
private static final float TAME_HEALTH = 40.0F;
|
||||
private static final float ARMOR_REPAIR_UNIT = 0.125F;
|
||||
@@ -125,12 +_,47 @@
|
||||
this.setPathfindingMalus(PathType.ON_TOP_OF_POWDER_SNOW, -1.0F);
|
||||
}
|
||||
|
||||
+ // Purpur start - Configurable chance for wolves to spawn rabid
|
||||
+ public boolean isRabid() {
|
||||
+ return this.isRabid;
|
||||
+ }
|
||||
+
|
||||
+ public void setRabid(boolean isRabid) {
|
||||
+ this.isRabid = isRabid;
|
||||
+ updatePathfinders(true);
|
||||
+ }
|
||||
+
|
||||
+ public void updatePathfinders(boolean modifyEffects) {
|
||||
+ this.targetSelector.removeGoal(PATHFINDER_VANILLA);
|
||||
+ this.targetSelector.removeGoal(PATHFINDER_RABID);
|
||||
+ if (this.isRabid) {
|
||||
+ this.setOwnerReference(null);
|
||||
+ setTame(false, true);
|
||||
+ this.targetSelector.addGoal(5, PATHFINDER_RABID);
|
||||
+ if (modifyEffects) this.addEffect(new net.minecraft.world.effect.MobEffectInstance(net.minecraft.world.effect.MobEffects.NAUSEA, 1200));
|
||||
+ } else {
|
||||
+ this.targetSelector.addGoal(5, PATHFINDER_VANILLA);
|
||||
+ this.stopBeingAngry();
|
||||
+ if (modifyEffects) this.removeEffect(net.minecraft.world.effect.MobEffects.NAUSEA);
|
||||
+ }
|
||||
+ }
|
||||
+ // Purpur end - Configurable chance for wolves to spawn rabid
|
||||
+
|
||||
+ // Purpur start - Configurable default collar color
|
||||
+ @Override
|
||||
+ public void tame(Player player) {
|
||||
+ setCollarColor(level().purpurConfig.wolfDefaultCollarColor);
|
||||
+ super.tame(player);
|
||||
+ }
|
||||
+ // Purpur end - Configurable default collar color
|
||||
+
|
||||
@Override
|
||||
protected void registerGoals() {
|
||||
this.goalSelector.addGoal(1, new FloatGoal(this));
|
||||
this.goalSelector.addGoal(1, new TamableAnimal.TamableAnimalPanicGoal(1.5, DamageTypeTags.PANIC_ENVIRONMENTAL_CAUSES));
|
||||
this.goalSelector.addGoal(2, new SitWhenOrderedToGoal(this));
|
||||
this.goalSelector.addGoal(3, new Wolf.WolfAvoidEntityGoal<>(this, Llama.class, 24.0F, 1.5, 1.5));
|
||||
+ this.goalSelector.addGoal(3, new AvoidRabidWolfGoal(this, 24.0F, 1.5D, 1.5D)); // Purpur - Configurable chance for wolves to spawn rabid
|
||||
this.goalSelector.addGoal(4, new LeapAtTargetGoal(this, 0.4F));
|
||||
this.goalSelector.addGoal(5, new MeleeAttackGoal(this, 1.0, true));
|
||||
this.goalSelector.addGoal(6, new FollowOwnerGoal(this, 1.0, 10.0F, 2.0F));
|
||||
@@ -143,7 +_,7 @@
|
||||
this.targetSelector.addGoal(2, new OwnerHurtTargetGoal(this));
|
||||
this.targetSelector.addGoal(3, new HurtByTargetGoal(this).setAlertOthers());
|
||||
this.targetSelector.addGoal(4, new NearestAttackableTargetGoal<>(this, Player.class, 10, true, false, this::isAngryAt));
|
||||
- this.targetSelector.addGoal(5, new NonTameRandomTargetGoal<>(this, Animal.class, false, PREY_SELECTOR));
|
||||
+ //this.targetSelector.addGoal(5, new NonTameRandomTargetGoal<>(this, Animal.class, false, PREY_SELECTOR)); // Purpur - Configurable chance for wolves to spawn rabid - moved to updatePathfinders()
|
||||
this.targetSelector.addGoal(6, new NonTameRandomTargetGoal<>(this, Turtle.class, false, Turtle.BABY_ON_LAND_SELECTOR));
|
||||
this.targetSelector.addGoal(7, new NearestAttackableTargetGoal<>(this, AbstractSkeleton.class, false));
|
||||
this.targetSelector.addGoal(8, new ResetUniversalAngerTargetGoal<>(this, true));
|
||||
@@ -238,6 +_,7 @@
|
||||
protected void addAdditionalSaveData(final ValueOutput output) {
|
||||
super.addAdditionalSaveData(output);
|
||||
output.store("CollarColor", DyeColor.LEGACY_ID_CODEC, this.getCollarColor());
|
||||
+ output.putBoolean("Purpur.IsRabid", this.isRabid); // Purpur - Configurable chance for wolves to spawn rabid
|
||||
VariantUtils.writeVariant(output, this.getVariant());
|
||||
this.addPersistentAngerSaveData(output);
|
||||
this.getSoundVariant()
|
||||
@@ -252,6 +_,10 @@
|
||||
super.readAdditionalSaveData(input);
|
||||
VariantUtils.readVariant(input, Registries.WOLF_VARIANT).ifPresent(this::setVariant);
|
||||
this.setCollarColor(input.read("CollarColor", DyeColor.LEGACY_ID_CODEC).orElse(DEFAULT_COLLAR_COLOR));
|
||||
+ // Purpur start - Configurable chance for wolves to spawn rabid
|
||||
+ this.isRabid = input.getBooleanOr("Purpur.IsRabid", false);
|
||||
+ this.updatePathfinders(false);
|
||||
+ // Purpur end - Configurable chance for wolves to spawn rabid
|
||||
this.readPersistentAngerSaveData(this.level(), input);
|
||||
input.read("sound_variant", ResourceKey.codec(Registries.WOLF_SOUND_VARIANT))
|
||||
.flatMap(soundVariant -> this.registryAccess().lookupOrThrow(Registries.WOLF_SOUND_VARIANT).get((ResourceKey<WolfSoundVariant>)soundVariant))
|
||||
@@ -275,6 +_,10 @@
|
||||
}
|
||||
|
||||
this.setSoundVariant(WolfSoundVariants.pickRandomSoundVariant(this.registryAccess(), level.getRandom()));
|
||||
+ // Purpur start - Configurable chance for wolves to spawn rabid
|
||||
+ this.isRabid = level.getLevel().purpurConfig.wolfNaturalRabid > 0.0D && random.nextDouble() <= level.getLevel().purpurConfig.wolfNaturalRabid;
|
||||
+ this.updatePathfinders(false);
|
||||
+ // Purpur end - Configurable chance for wolves to spawn rabid
|
||||
return super.finalizeSpawn(level, difficulty, spawnReason, groupData);
|
||||
}
|
||||
|
||||
@@ -323,6 +_,11 @@
|
||||
public void tick() {
|
||||
super.tick();
|
||||
if (this.isAlive()) {
|
||||
+ // Purpur start - Configurable chance for wolves to spawn rabid
|
||||
+ if (this.age % 300 == 0 && this.isRabid()) {
|
||||
+ this.addEffect(new net.minecraft.world.effect.MobEffectInstance(net.minecraft.world.effect.MobEffects.NAUSEA, 400));
|
||||
+ }
|
||||
+ // Purpur end - Configurable chance for wolves to spawn rabid
|
||||
this.interestedAngleO = this.interestedAngle;
|
||||
if (this.isInterested()) {
|
||||
this.interestedAngle = this.interestedAngle + (1.0F - this.interestedAngle) * 0.4F;
|
||||
@@ -524,13 +_,27 @@
|
||||
itemStack.consume(1, player);
|
||||
this.tryToTame(player);
|
||||
return InteractionResult.SUCCESS_SERVER;
|
||||
+ // Purpur start - Configurable chance for wolves to spawn rabid
|
||||
+ } else if (this.level().purpurConfig.wolfMilkCuresRabies && itemStack.getItem() == Items.MILK_BUCKET && this.isRabid()) {
|
||||
+ if (!player.isCreative()) {
|
||||
+ player.setItemInHand(hand, new ItemStack(Items.BUCKET));
|
||||
+ }
|
||||
+ this.setRabid(false);
|
||||
+ for (int i = 0; i < 10; ++i) {
|
||||
+ ((ServerLevel) level()).sendParticlesSource(((ServerLevel) level()).players(), null, ParticleTypes.HAPPY_VILLAGER,
|
||||
+ false, true,
|
||||
+ getX() + random.nextFloat(), getY() + (random.nextFloat() * 1.5), getZ() + random.nextFloat(), 1,
|
||||
+ random.nextGaussian() * 0.05D, random.nextGaussian() * 0.05D, random.nextGaussian() * 0.05D, 0);
|
||||
+ }
|
||||
+ return InteractionResult.SUCCESS_SERVER;
|
||||
+ // Purpur end - Configurable chance for wolves to spawn rabid
|
||||
}
|
||||
|
||||
return super.mobInteract(player, hand);
|
||||
}
|
||||
|
||||
private void tryToTame(final Player player) {
|
||||
- if (this.random.nextInt(3) == 0 && !org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTameEvent(this, player).isCancelled()) { // CraftBukkit - added event call
|
||||
+ if (((this.level().purpurConfig.alwaysTameInCreative && player.hasInfiniteMaterials()) || this.random.nextInt(3) == 0) && !org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTameEvent(this, player).isCancelled()) { // CraftBukkit - added event call // Purpur - Config to always tame in Creative
|
||||
this.tame(player);
|
||||
this.navigation.stop();
|
||||
this.setTarget(null);
|
||||
@@ -0,0 +1,39 @@
|
||||
--- a/net/minecraft/world/entity/monster/Monster.java
|
||||
+++ b/net/minecraft/world/entity/monster/Monster.java
|
||||
@@ -83,6 +_,11 @@
|
||||
}
|
||||
|
||||
public static boolean isDarkEnoughToSpawn(final ServerLevelAccessor level, 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
|
||||
if (level.getBrightness(LightLayer.SKY, pos) > random.nextInt(32)) {
|
||||
return false;
|
||||
}
|
||||
@@ -111,6 +_,11 @@
|
||||
public static boolean checkAnyLightMonsterSpawnRules(
|
||||
final EntityType<? extends Monster> 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 checkMobSpawnRules(type, level, spawnReason, pos, random);
|
||||
}
|
||||
|
||||
@@ -152,4 +_,12 @@
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
}
|
||||
+
|
||||
+ // Purpur start - Config to disable hostile mob spawn on ice
|
||||
+ public static boolean canSpawnInBlueAndPackedIce(LevelAccessor level, BlockPos pos) {
|
||||
+ net.minecraft.world.level.block.state.BlockState spawnBlock = level.getBlockState(pos.below());
|
||||
+
|
||||
+ return (!level.getMinecraftWorld().purpurConfig.mobsSpawnOnPackedIce && spawnBlock.is(net.minecraft.world.level.block.Blocks.PACKED_ICE)) || (!level.getMinecraftWorld().purpurConfig.mobsSpawnOnBlueIce && spawnBlock.is(net.minecraft.world.level.block.Blocks.BLUE_ICE));
|
||||
+ }
|
||||
+ // Purpur end - Config to disable hostile mob spawn on ice
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
--- a/net/minecraft/world/entity/monster/Shulker.java
|
||||
+++ b/net/minecraft/world/entity/monster/Shulker.java
|
||||
@@ -93,6 +_,22 @@
|
||||
this.lookControl = new Shulker.ShulkerLookControl(this);
|
||||
}
|
||||
|
||||
+ // Purpur start - Shulker change color with dye
|
||||
+ @Override
|
||||
+ protected net.minecraft.world.InteractionResult mobInteract(Player player, net.minecraft.world.InteractionHand hand) {
|
||||
+ net.minecraft.world.item.ItemStack itemstack = player.getItemInHand(hand);
|
||||
+ DyeColor dyeColor = itemstack.get(DataComponents.DYE);
|
||||
+ if (player.level().purpurConfig.shulkerChangeColorWithDye && dyeColor != null && dyeColor != this.getColor()) {
|
||||
+ this.setVariant(Optional.of(dyeColor));
|
||||
+ if (!player.getAbilities().instabuild) {
|
||||
+ itemstack.shrink(1);
|
||||
+ }
|
||||
+ return net.minecraft.world.InteractionResult.SUCCESS;
|
||||
+ }
|
||||
+ return super.mobInteract(player, hand);
|
||||
+ }
|
||||
+ // Purpur end - Shulker change color with dye
|
||||
+
|
||||
@Override
|
||||
protected void registerGoals() {
|
||||
this.goalSelector.addGoal(1, new LookAtPlayerGoal(this, Player.class, 8.0F, 0.02F, true));
|
||||
@@ -466,11 +_,21 @@
|
||||
private void hitByShulkerBullet() {
|
||||
Vec3 oldPosition = this.position();
|
||||
AABB oldAabb = this.getBoundingBox();
|
||||
- if (!this.isClosed() && this.teleportSomewhere()) {
|
||||
- int shulkerCount = this.level().getEntities(EntityTypes.SHULKER, oldAabb.inflate(8.0), Entity::isAlive).size();
|
||||
- float failureChance = (shulkerCount - 1) / 5.0F;
|
||||
- if (!(this.level().getRandom().nextFloat() < failureChance)) {
|
||||
+ // Purpur start - Shulker spawn from bullet options
|
||||
+ if ((!this.level().purpurConfig.shulkerSpawnFromBulletRequireOpenLid || !this.isClosed()) && this.teleportSomewhere()) {
|
||||
+ float failureChance = this.level().purpurConfig.shulkerSpawnFromBulletBaseChance;
|
||||
+ if (!this.level().purpurConfig.shulkerSpawnFromBulletNearbyEquation.isBlank()) {
|
||||
+ int shulkerCount = this.level().getEntities(EntityTypes.SHULKER, oldAabb.inflate(this.level().purpurConfig.shulkerSpawnFromBulletNearbyRange), Entity::isAlive).size();
|
||||
+ try {
|
||||
+ failureChance -= ((Number) scriptEngine.eval("let nearby = " + shulkerCount + "; " + this.level().purpurConfig.shulkerSpawnFromBulletNearbyEquation)).floatValue();
|
||||
+ } catch (javax.script.ScriptException e) {
|
||||
+ e.printStackTrace();
|
||||
+ failureChance -= (shulkerCount - 1) / 5.0F;
|
||||
+ }
|
||||
+ }
|
||||
+ if (this.level().getRandom().nextFloat() <= failureChance) {
|
||||
Shulker baby = EntityTypes.SHULKER.create(this.level(), EntitySpawnReason.BREEDING);
|
||||
+ // Purpur end - Shulker spawn from bullet options
|
||||
if (baby != null) {
|
||||
baby.setVariant(this.getVariant());
|
||||
baby.snapTo(oldPosition);
|
||||
@@ -577,7 +_,7 @@
|
||||
}
|
||||
|
||||
public Optional<DyeColor> getVariant() {
|
||||
- return Optional.ofNullable(this.getColor());
|
||||
+ return Optional.ofNullable(this.level().purpurConfig.shulkerSpawnFromBulletRandomColor ? DyeColor.random(this.level().getRandom()) : this.getColor()); // Purpur - Shulker spawn from bullet options
|
||||
}
|
||||
|
||||
public @Nullable DyeColor getColor() {
|
||||
@@ -0,0 +1,14 @@
|
||||
--- a/net/minecraft/world/entity/monster/cubemob/MagmaCube.java
|
||||
+++ b/net/minecraft/world/entity/monster/cubemob/MagmaCube.java
|
||||
@@ -53,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,14 @@
|
||||
--- a/net/minecraft/world/entity/monster/cubemob/Slime.java
|
||||
+++ b/net/minecraft/world/entity/monster/cubemob/Slime.java
|
||||
@@ -73,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,21 @@
|
||||
--- a/net/minecraft/world/entity/npc/wanderingtrader/WanderingTraderSpawner.java
|
||||
+++ b/net/minecraft/world/entity/npc/wanderingtrader/WanderingTraderSpawner.java
|
||||
@@ -135,7 +_,17 @@
|
||||
int xPosition = referencePosition.getX() + this.random.nextInt(radius * 2) - radius;
|
||||
int zPosition = referencePosition.getZ() + this.random.nextInt(radius * 2) - radius;
|
||||
int yPosition = level.getHeight(SpawnPlacements.getHeightmapType(EntityTypes.WANDERING_TRADER), xPosition, zPosition);
|
||||
- BlockPos spawnPos = new BlockPos(xPosition, yPosition, zPosition);
|
||||
+ // Purpur start - Allow toggling special MobSpawners per world - allow traders to spawn below nether roof
|
||||
+ BlockPos.MutableBlockPos spawnPos = new BlockPos.MutableBlockPos(xPosition, yPosition, zPosition);
|
||||
+ if (level.dimensionType().hasCeiling()) {
|
||||
+ do {
|
||||
+ spawnPos.relative(net.minecraft.core.Direction.DOWN);
|
||||
+ } while (!level.getBlockState(spawnPos).isAir());
|
||||
+ do {
|
||||
+ spawnPos.relative(net.minecraft.core.Direction.DOWN);
|
||||
+ } while (level.getBlockState(spawnPos).isAir() && spawnPos.getY() > 0);
|
||||
+ }
|
||||
+ // Purpur end - Allow toggling special MobSpawners per world
|
||||
if (wanderingTraderSpawnType.isSpawnPositionOk(level, spawnPos, EntityTypes.WANDERING_TRADER)) {
|
||||
spawnPosition = spawnPos;
|
||||
break;
|
||||
@@ -0,0 +1,111 @@
|
||||
--- a/net/minecraft/world/entity/player/Player.java
|
||||
+++ b/net/minecraft/world/entity/player/Player.java
|
||||
@@ -174,11 +_,20 @@
|
||||
public float hurtDir;
|
||||
public boolean affectsSpawning = true; // Paper - Affects Spawning API
|
||||
public net.kyori.adventure.util.TriState flyingFallDamage = net.kyori.adventure.util.TriState.NOT_SET; // Paper - flying fall damage
|
||||
+ public int burpDelay = 0; // Purpur - Burp delay
|
||||
+ public boolean canPortalInstant = false; // Purpur - Add portal permission bypass
|
||||
|
||||
// CraftBukkit start
|
||||
public boolean fauxSleeping;
|
||||
public int oldLevel = -1;
|
||||
|
||||
+ // Purpur start - AFK API
|
||||
+ public abstract void setAfk(boolean afk);
|
||||
+
|
||||
+ public boolean isAfk() {
|
||||
+ return false;
|
||||
+ }
|
||||
+ // Purpur end - AFK API
|
||||
@Override
|
||||
public org.bukkit.craftbukkit.entity.CraftHumanEntity getBukkitEntity() {
|
||||
return (org.bukkit.craftbukkit.entity.CraftHumanEntity) super.getBukkitEntity();
|
||||
@@ -243,6 +_,12 @@
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
+ // Purpur start - Burp delay
|
||||
+ if (this.burpDelay > 0 && --this.burpDelay == 0) {
|
||||
+ this.level().playSound(null, getX(), getY(), getZ(), SoundEvents.PLAYER_BURP, SoundSource.PLAYERS, 1.0F, this.level().getRandom().nextFloat() * 0.1F + 0.9F);
|
||||
+ }
|
||||
+ // Purpur end - Burp delay
|
||||
+
|
||||
this.noPhysics = this.isSpectator();
|
||||
if (this.isSpectator() || this.isPassenger()) {
|
||||
this.setOnGround(false);
|
||||
@@ -300,6 +_,17 @@
|
||||
this.turtleHelmetTick();
|
||||
}
|
||||
|
||||
+ // Purpur start - Full netherite armor grants fire resistance
|
||||
+ if (this.level().purpurConfig.playerNetheriteFireResistanceDuration > 0 && this.level().getGameTime() % 20 == 0) {
|
||||
+ if (this.getItemBySlot(EquipmentSlot.HEAD).is(Items.NETHERITE_HELMET)
|
||||
+ && this.getItemBySlot(EquipmentSlot.CHEST).is(Items.NETHERITE_CHESTPLATE)
|
||||
+ && this.getItemBySlot(EquipmentSlot.LEGS).is(Items.NETHERITE_LEGGINGS)
|
||||
+ && this.getItemBySlot(EquipmentSlot.FEET).is(Items.NETHERITE_BOOTS)) {
|
||||
+ this.addEffect(new MobEffectInstance(MobEffects.FIRE_RESISTANCE, this.level().purpurConfig.playerNetheriteFireResistanceDuration, this.level().purpurConfig.playerNetheriteFireResistanceAmplifier, this.level().purpurConfig.playerNetheriteFireResistanceAmbient, this.level().purpurConfig.playerNetheriteFireResistanceShowParticles, this.level().purpurConfig.playerNetheriteFireResistanceShowIcon), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.NETHERITE_ARMOR);
|
||||
+ }
|
||||
+ }
|
||||
+ // Purpur end - Full netherite armor grants fire resistance
|
||||
+
|
||||
this.cooldowns.tick();
|
||||
this.updatePlayerPose();
|
||||
}
|
||||
@@ -505,7 +_,7 @@
|
||||
List<Entity> orbs = Lists.newArrayList();
|
||||
|
||||
for (Entity entity : entities) {
|
||||
- if (entity.is(EntityTypes.EXPERIENCE_ORB)) {
|
||||
+ if (entity.is(EntityTypes.EXPERIENCE_ORB) && entity.level().purpurConfig.playerExpPickupDelay >= 0) { // Purpur - Configurable player pickup exp delay
|
||||
orbs.add(entity);
|
||||
} else if (!entity.isRemoved()) {
|
||||
this.touch(entity);
|
||||
@@ -1053,7 +_,7 @@
|
||||
criticalAttack = criticalAttack && !this.level().paperConfig().entities.behavior.disablePlayerCrits; // Paper - Toggleable player crits
|
||||
if (criticalAttack) {
|
||||
damageSource = damageSource.critical(); // Paper - critical damage API
|
||||
- baseDamage *= 1.5F;
|
||||
+ baseDamage *= (float) this.level().purpurConfig.playerCriticalDamageMultiplier; // Purpur - Add config change multiplier critical damage value
|
||||
}
|
||||
|
||||
float totalDamage = baseDamage + magicBoost;
|
||||
@@ -1757,7 +_,23 @@
|
||||
|
||||
@Override
|
||||
protected int getBaseExperienceReward(final ServerLevel level) {
|
||||
- return !level.getGameRules().get(GameRules.KEEP_INVENTORY) && !this.isSpectator() ? Math.min(this.experienceLevel * 7, 100) : 0;
|
||||
+ // Purpur start - Add player death exp control options
|
||||
+ if (!level.getGameRules().get(GameRules.KEEP_INVENTORY) && !this.isSpectator()) {
|
||||
+ int toDrop;
|
||||
+ try {
|
||||
+ toDrop = Math.round(((Number) scriptEngine.eval("let expLevel = " + experienceLevel + "; " +
|
||||
+ "let expTotal = " + totalExperience + "; " +
|
||||
+ "let exp = " + experienceProgress + "; " +
|
||||
+ level().purpurConfig.playerDeathExpDropEquation)).floatValue());
|
||||
+ } catch (javax.script.ScriptException e) {
|
||||
+ e.printStackTrace();
|
||||
+ toDrop = experienceLevel * 7;
|
||||
+ }
|
||||
+ return Math.min(toDrop, level().purpurConfig.playerDeathExpDropMax);
|
||||
+ } else {
|
||||
+ return 0;
|
||||
+ }
|
||||
+ // Purpur end - Add player death exp control options
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1800,6 +_,13 @@
|
||||
public boolean addItem(final ItemStack itemStack) {
|
||||
return this.inventory.add(itemStack);
|
||||
}
|
||||
+
|
||||
+ // Purpur start - Player ridable in water option
|
||||
+ @Override
|
||||
+ public boolean dismountsUnderwater() {
|
||||
+ return !level().purpurConfig.playerRidableInWater;
|
||||
+ }
|
||||
+ // Purpur end - Player ridable in water option
|
||||
|
||||
public abstract @Nullable GameType gameMode();
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
--- a/net/minecraft/world/entity/projectile/throwableitemprojectile/ThrownEnderpearl.java
|
||||
+++ b/net/minecraft/world/entity/projectile/throwableitemprojectile/ThrownEnderpearl.java
|
||||
@@ -115,9 +_,10 @@
|
||||
return;
|
||||
}
|
||||
// CraftBukkit end
|
||||
- if (this.random.nextFloat() < 0.05F && level.isSpawningMonsters() && level.getLevelData().getDifficulty() != Difficulty.PEACEFUL) {
|
||||
+ if (this.random.nextFloat() < level.purpurConfig.enderPearlEndermiteChance && level.isSpawningMonsters() && level.getLevelData().getDifficulty() != Difficulty.PEACEFUL) { // Purpur - Configurable Ender Pearl RNG
|
||||
Endermite endermite = EntityTypes.ENDERMITE.create(level, EntitySpawnReason.TRIGGERED);
|
||||
if (endermite != null) {
|
||||
+ endermite.setPlayerSpawned(true); // Purpur - Add back player spawned endermite API
|
||||
endermite.snapTo(preTeleportX, preTeleportY, preTeleportZ, preTeleportYRot, preTeleportXRot); // Paper - spawn endermite at pre teleport position as teleport has been moved up
|
||||
level.addFreshEntity(endermite, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.ENDER_PEARL); // Paper - add reason
|
||||
}
|
||||
@@ -137,7 +_,7 @@
|
||||
if (newOwner != null) {
|
||||
newOwner.resetFallDistance();
|
||||
newOwner.resetCurrentImpulseContext();
|
||||
- newOwner.hurtServer(player.level(), this.damageSources().enderPearl().eventEntityDamager(this), 5.0F); // CraftBukkit // Paper - fix DamageSource API
|
||||
+ newOwner.hurtServer(player.level(), this.damageSources().enderPearl().eventEntityDamager(this), this.level().purpurConfig.enderPearlDamage); // CraftBukkit // Paper - fix DamageSource API // Purpur - Configurable Ender Pearl damage
|
||||
}
|
||||
|
||||
this.playSound(level, teleportPos);
|
||||
@@ -0,0 +1,138 @@
|
||||
--- a/net/minecraft/world/inventory/GrindstoneMenu.java
|
||||
+++ b/net/minecraft/world/inventory/GrindstoneMenu.java
|
||||
@@ -92,11 +_,13 @@
|
||||
@Override
|
||||
public void onTake(final Player player, final ItemStack carried) {
|
||||
access.execute((level, pos) -> {
|
||||
+ ItemStack itemstack = activeQuickItem == null ? carried : activeQuickItem; // Purpur - Grindstone API
|
||||
if (level instanceof ServerLevel serverLevel) {
|
||||
// Paper start - Fire BlockExpEvent on grindstone use
|
||||
org.bukkit.event.block.BlockExpEvent event = new org.bukkit.event.block.BlockExpEvent(org.bukkit.craftbukkit.block.CraftBlock.at(level, pos), this.getExperienceAmount(level));
|
||||
event.callEvent();
|
||||
- ExperienceOrb.awardWithDirection(serverLevel, Vec3.atCenterOf(pos), Vec3.ZERO, event.getExpToDrop(), org.bukkit.entity.ExperienceOrb.SpawnReason.GRINDSTONE, player, null);
|
||||
+ org.purpurmc.purpur.event.inventory.GrindstoneTakeResultEvent grindstoneTakeResultEvent = new org.purpurmc.purpur.event.inventory.GrindstoneTakeResultEvent(player.getBukkitEntity(), getBukkitView(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemstack), event.getExpToDrop()); grindstoneTakeResultEvent.callEvent(); // Purpur - Grindstone API
|
||||
+ ExperienceOrb.awardWithDirection(serverLevel, Vec3.atCenterOf(pos), Vec3.ZERO, grindstoneTakeResultEvent.getExperienceAmount(), org.bukkit.entity.ExperienceOrb.SpawnReason.GRINDSTONE, player, null); // Purpur - Grindstone API
|
||||
// Paper end - Fire BlockExpEvent on grindstone use
|
||||
}
|
||||
|
||||
@@ -125,7 +_,7 @@
|
||||
for (Entry<Holder<Enchantment>> entry : enchantments.entrySet()) {
|
||||
Holder<Enchantment> enchant = entry.getKey();
|
||||
int lvl = entry.getIntValue();
|
||||
- if (!enchant.is(EnchantmentTags.CURSE)) {
|
||||
+ if (!org.purpurmc.purpur.PurpurConfig.grindstoneIgnoredEnchants.contains(enchant.value())) { // Purpur - Config for grindstones
|
||||
amount += enchant.value().getMinCost(lvl);
|
||||
}
|
||||
}
|
||||
@@ -205,16 +_,76 @@
|
||||
|
||||
for (Entry<Holder<Enchantment>> entry : enchantments.entrySet()) {
|
||||
Holder<Enchantment> enchant = entry.getKey();
|
||||
- if (!enchant.is(EnchantmentTags.CURSE) || newEnchantments.getLevel(enchant) == 0) {
|
||||
+ if (!org.purpurmc.purpur.PurpurConfig.grindstoneIgnoredEnchants.contains(enchant.value()) || newEnchantments.getLevel(enchant) == 0) { // Purpur - Config for grindstones
|
||||
newEnchantments.upgrade(enchant, entry.getIntValue());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
+ // Purpur start - Config for grindstones
|
||||
+ private java.util.List<net.minecraft.core.component.DataComponentType<?>> GRINDSTONE_REMOVE_ATTRIBUTES_REMOVAL_LIST = java.util.List.of(
|
||||
+ // DataComponents.MAX_STACK_SIZE,
|
||||
+ // DataComponents.DAMAGE,
|
||||
+ // DataComponents.BLOCK_STATE,
|
||||
+ DataComponents.CUSTOM_DATA,
|
||||
+ // DataComponents.MAX_DAMAGE,
|
||||
+ // DataComponents.UNBREAKABLE,
|
||||
+ // DataComponents.CUSTOM_NAME,
|
||||
+ // DataComponents.ITEM_NAME,
|
||||
+ // DataComponents.LORE,
|
||||
+ // DataComponents.RARITY,
|
||||
+ // DataComponents.ENCHANTMENTS,
|
||||
+ // DataComponents.CAN_PLACE_ON,
|
||||
+ // DataComponents.CAN_BREAK,
|
||||
+ DataComponents.ATTRIBUTE_MODIFIERS,
|
||||
+ DataComponents.CUSTOM_MODEL_DATA,
|
||||
+ // DataComponents.HIDE_ADDITIONAL_TOOLTIP,
|
||||
+ // DataComponents.HIDE_TOOLTIP,
|
||||
+ // DataComponents.REPAIR_COST,
|
||||
+ // DataComponents.CREATIVE_SLOT_LOCK,
|
||||
+ // DataComponents.ENCHANTMENT_GLINT_OVERRIDE,
|
||||
+ // DataComponents.INTANGIBLE_PROJECTILE,
|
||||
+ // DataComponents.FOOD,
|
||||
+ // DataComponents.FIRE_RESISTANT,
|
||||
+ // DataComponents.TOOL,
|
||||
+ // DataComponents.STORED_ENCHANTMENTS,
|
||||
+ DataComponents.DYED_COLOR,
|
||||
+ // DataComponents.MAP_COLOR,
|
||||
+ // DataComponents.MAP_ID,
|
||||
+ // DataComponents.MAP_DECORATIONS,
|
||||
+ // DataComponents.MAP_POST_PROCESSING,
|
||||
+ // DataComponents.CHARGED_PROJECTILES,
|
||||
+ // DataComponents.BUNDLE_CONTENTS,
|
||||
+ // DataComponents.POTION_CONTENTS,
|
||||
+ DataComponents.SUSPICIOUS_STEW_EFFECTS
|
||||
+ // DataComponents.WRITABLE_BOOK_CONTENT,
|
||||
+ // DataComponents.WRITTEN_BOOK_CONTENT,
|
||||
+ // DataComponents.TRIM,
|
||||
+ // DataComponents.DEBUG_STICK_STATE,
|
||||
+ // DataComponents.ENTITY_DATA,
|
||||
+ // DataComponents.BUCKET_ENTITY_DATA,
|
||||
+ // DataComponents.BLOCK_ENTITY_DATA,
|
||||
+ // DataComponents.INSTRUMENT,
|
||||
+ // DataComponents.OMINOUS_BOTTLE_AMPLIFIER,
|
||||
+ // DataComponents.RECIPES,
|
||||
+ // DataComponents.LODESTONE_TRACKER,
|
||||
+ // DataComponents.FIREWORK_EXPLOSION,
|
||||
+ // DataComponents.FIREWORKS,
|
||||
+ // DataComponents.PROFILE,
|
||||
+ // DataComponents.NOTE_BLOCK_SOUND,
|
||||
+ // DataComponents.BANNER_PATTERNS,
|
||||
+ // DataComponents.BASE_COLOR,
|
||||
+ // DataComponents.POT_DECORATIONS,
|
||||
+ // DataComponents.CONTAINER,
|
||||
+ // DataComponents.BEES,
|
||||
+ // DataComponents.LOCK,
|
||||
+ // DataComponents.CONTAINER_LOOT,
|
||||
+ );
|
||||
+ // Purpur end - Config for grindstones
|
||||
private ItemStack removeNonCursesFrom(ItemStack item) {
|
||||
ItemEnchantments newEnchantments = EnchantmentHelper.updateEnchantments(
|
||||
- item, enchantments -> enchantments.removeIf(enchantment -> !enchantment.is(EnchantmentTags.CURSE))
|
||||
+ item, enchantments -> enchantments.removeIf(enchantment -> !org.purpurmc.purpur.PurpurConfig.grindstoneIgnoredEnchants.contains(enchantment.value())) // Purpur - Config for grindstones
|
||||
);
|
||||
if (item.is(Items.ENCHANTED_BOOK) && newEnchantments.isEmpty()) {
|
||||
item = item.transmuteCopy(Items.BOOK);
|
||||
@@ -227,6 +_,22 @@
|
||||
}
|
||||
|
||||
item.set(DataComponents.REPAIR_COST, repairCost);
|
||||
+
|
||||
+ // Purpur start - Config for grindstones
|
||||
+ net.minecraft.core.component.DataComponentPatch.Builder builder = net.minecraft.core.component.DataComponentPatch.builder();
|
||||
+ if (org.purpurmc.purpur.PurpurConfig.grindstoneRemoveAttributes) {
|
||||
+ item.getComponents().forEach(typedDataComponent -> {
|
||||
+ if (GRINDSTONE_REMOVE_ATTRIBUTES_REMOVAL_LIST.contains(typedDataComponent.type())) {
|
||||
+ builder.remove(typedDataComponent.type());
|
||||
+ }
|
||||
+ });
|
||||
+ }
|
||||
+ if (org.purpurmc.purpur.PurpurConfig.grindstoneRemoveDisplay) {
|
||||
+ builder.remove(DataComponents.CUSTOM_NAME);
|
||||
+ builder.remove(DataComponents.LORE);
|
||||
+ }
|
||||
+ item.applyComponents(builder.build());
|
||||
+ // Purpur end - Config for grindstones
|
||||
return item;
|
||||
}
|
||||
|
||||
@@ -283,7 +_,9 @@
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
+ this.activeQuickItem = clicked; // Purpur - Grindstone API
|
||||
slot.onTake(player, item);
|
||||
+ this.activeQuickItem = null; // Purpur - Grindstone API
|
||||
}
|
||||
|
||||
return clicked;
|
||||
@@ -0,0 +1,20 @@
|
||||
--- a/net/minecraft/world/item/Items.java
|
||||
+++ b/net/minecraft/world/item/Items.java
|
||||
@@ -416,7 +_,7 @@
|
||||
public static final Item PURPUR_BLOCK = registerBlock(BlockItemIds.PURPUR_BLOCK, Blocks.PURPUR_BLOCK);
|
||||
public static final Item PURPUR_PILLAR = registerBlock(BlockItemIds.PURPUR_PILLAR, Blocks.PURPUR_PILLAR);
|
||||
public static final Item PURPUR_STAIRS = registerBlock(BlockItemIds.PURPUR_STAIRS, Blocks.PURPUR_STAIRS);
|
||||
- public static final Item SPAWNER = registerBlock(BlockItemIds.SPAWNER, Blocks.SPAWNER);
|
||||
+ public static final Item SPAWNER = registerBlock(BlockItemIds.SPAWNER, 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(BlockItemIds.CREAKING_HEART, Blocks.CREAKING_HEART);
|
||||
public static final Item CHEST = registerBlock(BlockItemIds.CHEST, Blocks.CHEST, p -> p.component(DataComponents.CONTAINER, ItemContainerContents.EMPTY));
|
||||
public static final Item CRAFTING_TABLE = registerBlock(BlockItemIds.CRAFTING_TABLE, Blocks.CRAFTING_TABLE);
|
||||
@@ -1903,7 +_,7 @@
|
||||
BlockItemIds.SWEET_BERRY_CROP, createBlockItemWithCustomItemName(Blocks.SWEET_BERRY_BUSH), new Item.Properties().food(Foods.SWEET_BERRIES)
|
||||
);
|
||||
public static final Item GLOW_BERRIES = registerItem(
|
||||
- BlockItemIds.GLOW_BERRY_CROP, createBlockItemWithCustomItemName(Blocks.CAVE_VINES), new Item.Properties().food(Foods.GLOW_BERRIES)
|
||||
+ BlockItemIds.GLOW_BERRY_CROP, 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(
|
||||
BlockItemIds.CAMPFIRE, Blocks.CAMPFIRE, p -> p.component(DataComponents.CONTAINER, ItemContainerContents.EMPTY)
|
||||
@@ -0,0 +1,26 @@
|
||||
--- a/net/minecraft/world/level/ServerExplosion.java
|
||||
+++ b/net/minecraft/world/level/ServerExplosion.java
|
||||
@@ -342,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,29 @@
|
||||
--- a/net/minecraft/world/level/block/BedBlock.java
|
||||
+++ b/net/minecraft/world/level/block/BedBlock.java
|
||||
@@ -99,7 +_,7 @@
|
||||
}
|
||||
|
||||
Vec3 boomPos = Vec3.atCenterOf(pos);
|
||||
- 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
|
||||
@@ -151,7 +_,7 @@
|
||||
}
|
||||
|
||||
Vec3 boomPos = Vec3.atCenterOf(pos);
|
||||
- level.explode(null, level.damageSources().badRespawnPointExplosion(boomPos).causingBlockSnapshot(blockState), null, boomPos, 5.0F, true, Level.ExplosionInteraction.BLOCK); // CraftBukkit - add state
|
||||
+ if (level.purpurConfig.bedExplode) level.explode(null, level.damageSources().badRespawnPointExplosion(boomPos).causingBlockSnapshot(blockState), null, boomPos, (float) level.purpurConfig.bedExplosionPower, level.purpurConfig.bedExplosionFire, level.purpurConfig.bedExplosionEffect); // CraftBukkit - add state // Purpur - Implement bed explosion options
|
||||
return InteractionResult.SUCCESS_SERVER;
|
||||
}
|
||||
// CraftBukkit end
|
||||
@@ -168,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/RespawnAnchorBlock.java
|
||||
+++ b/net/minecraft/world/level/block/RespawnAnchorBlock.java
|
||||
@@ -176,7 +_,7 @@
|
||||
}
|
||||
};
|
||||
Vec3 boomPos = Vec3.atCenterOf(pos);
|
||||
- level.explode(null, level.damageSources().badRespawnPointExplosion(boomPos).causingBlockSnapshot(blockState), damageCalculator, boomPos, 5.0F, true, Level.ExplosionInteraction.BLOCK); // CraftBukkit - add state
|
||||
+ level.explode(null, level.damageSources().badRespawnPointExplosion(boomPos).causingBlockSnapshot(blockState), damageCalculator, boomPos, (float) level.purpurConfig.respawnAnchorExplosionPower, level.purpurConfig.respawnAnchorExplosionFire, level.purpurConfig.respawnAnchorExplosionEffect); // CraftBukkit - add state // Purpur - Implement respawn anchor explosion options
|
||||
}
|
||||
|
||||
public static boolean canSetSpawn(final ServerLevel level, final BlockPos pos) {
|
||||
@@ -0,0 +1,11 @@
|
||||
--- a/net/minecraft/world/level/portal/PortalShape.java
|
||||
+++ b/net/minecraft/world/level/portal/PortalShape.java
|
||||
@@ -29,7 +_,7 @@
|
||||
public static final int MAX_WIDTH = 21;
|
||||
private static final int MIN_HEIGHT = 3;
|
||||
public static final int MAX_HEIGHT = 21;
|
||||
- public static final BlockBehaviour.StatePredicate FRAME = (state, level, pos) -> state.is(Blocks.OBSIDIAN);
|
||||
+ public static final BlockBehaviour.StatePredicate FRAME = (state, level, pos) -> state.is(Blocks.OBSIDIAN) || (org.purpurmc.purpur.PurpurConfig.cryingObsidianValidForPortalFrame && state.is(Blocks.CRYING_OBSIDIAN)); // Purpur - Crying obsidian valid for portal frames
|
||||
private static final float SAFE_TRAVEL_MAX_ENTITY_XY = 4.0F;
|
||||
private static final double SAFE_TRAVEL_MAX_VERTICAL_DELTA = 1.0;
|
||||
private final Direction.Axis axis;
|
||||
Reference in New Issue
Block a user