From 75baf3dfaf52ccac4d6cfd675b52bf9ee6de18e0 Mon Sep 17 00:00:00 2001 From: Helvetica Volubi Date: Fri, 15 Aug 2025 03:06:55 +0800 Subject: [PATCH] Update some patches from Leaves --- ...to-enable-Cross-Region-Damage-trace.patch} | 57 +---- ...04-Add-config-to-revert-raid-changes.patch | 207 ----------------- ...config-to-enable-Raytracing-tracker.patch} | 0 ...-Add-config-to-allow-infinite-trade.patch} | 0 ...onfig-to-enable-unsafe-waypoint-bar.patch} | 0 ...=> 0008-Leaves-Leaves-Protocol-Core.patch} | 0 ...=> 0009-Leaves-Disable-packet-limit.patch} | 0 ... => 0010-Leaves-Item-overstack-util.patch} | 0 .../0011-Leaves-Redstone-Shears-Wrench.patch | 44 ++++ .../0012-Leaves-Redstone-Shears-Wrench.patch | 153 ------------- ...atch => 0012-Leaves-Servux-Protocol.patch} | 0 ...ves-Old-Explosion-Damage-Calculator.patch} | 2 +- .../0014-Leaves-Old-raid-behavior.patch | 216 ++++++++++++++++++ ...rpur-Barrels-and-enderchests-6-rows.patch} | 0 ...tch => 0016-Spawn-invulnerable-time.patch} | 0 .../features/0018-I18n-support.patch | 26 +++ ...x-for-Raid-Revert-and-Cross-Region-D.patch | 54 +++++ .../config/modules/misc/OldFeatureConfig.java | 5 + .../modules/misc/RaidChangesConfig.java | 54 ----- .../config/modules/misc/RedStoneConfig.java | 3 - .../config/modules/removed/RemovedConfig.java | 6 + .../fun/bm/lophine/utils/ServerI18nUtil.java | 145 ++++++++---- .../leaves/util/ShearsWrenchUtil.java | 141 ++++++++++++ 23 files changed, 601 insertions(+), 512 deletions(-) rename lophine-server/minecraft-patches/features/{0005-Add-config-to-enable-Cross-Region-Damage-trace.patch => 0004-Add-config-to-enable-Cross-Region-Damage-trace.patch} (56%) delete mode 100644 lophine-server/minecraft-patches/features/0004-Add-config-to-revert-raid-changes.patch rename lophine-server/minecraft-patches/features/{0006-Add-config-to-enable-Raytracing-tracker.patch => 0005-Add-config-to-enable-Raytracing-tracker.patch} (100%) rename lophine-server/minecraft-patches/features/{0007-Add-config-to-allow-infinite-trade.patch => 0006-Add-config-to-allow-infinite-trade.patch} (100%) rename lophine-server/minecraft-patches/features/{0008-Add-config-to-enable-unsafe-waypoint-bar.patch => 0007-Add-config-to-enable-unsafe-waypoint-bar.patch} (100%) rename lophine-server/minecraft-patches/features/{0009-Leaves-Leaves-Protocol-Core.patch => 0008-Leaves-Leaves-Protocol-Core.patch} (100%) rename lophine-server/minecraft-patches/features/{0010-Leaves-Disable-packet-limit.patch => 0009-Leaves-Disable-packet-limit.patch} (100%) rename lophine-server/minecraft-patches/features/{0011-Leaves-Item-overstack-util.patch => 0010-Leaves-Item-overstack-util.patch} (100%) create mode 100644 lophine-server/minecraft-patches/features/0011-Leaves-Redstone-Shears-Wrench.patch delete mode 100644 lophine-server/minecraft-patches/features/0012-Leaves-Redstone-Shears-Wrench.patch rename lophine-server/minecraft-patches/features/{0013-Leaves-Servux-Protocol.patch => 0012-Leaves-Servux-Protocol.patch} (100%) rename lophine-server/minecraft-patches/features/{0016-Old-Explosion-Damage-Calculator.patch => 0013-Leaves-Old-Explosion-Damage-Calculator.patch} (96%) create mode 100644 lophine-server/minecraft-patches/features/0014-Leaves-Old-raid-behavior.patch rename lophine-server/minecraft-patches/features/{0014-Purpur-Barrels-and-enderchests-6-rows.patch => 0015-Purpur-Barrels-and-enderchests-6-rows.patch} (100%) rename lophine-server/minecraft-patches/features/{0015-Spawn-invulnerable-time.patch => 0016-Spawn-invulnerable-time.patch} (100%) create mode 100644 lophine-server/minecraft-patches/features/0020-Compatibility-fix-for-Raid-Revert-and-Cross-Region-D.patch delete mode 100644 lophine-server/src/main/java/fun/bm/lophine/config/modules/misc/RaidChangesConfig.java create mode 100644 lophine-server/src/main/java/org/leavesmc/leaves/util/ShearsWrenchUtil.java diff --git a/lophine-server/minecraft-patches/features/0005-Add-config-to-enable-Cross-Region-Damage-trace.patch b/lophine-server/minecraft-patches/features/0004-Add-config-to-enable-Cross-Region-Damage-trace.patch similarity index 56% rename from lophine-server/minecraft-patches/features/0005-Add-config-to-enable-Cross-Region-Damage-trace.patch rename to lophine-server/minecraft-patches/features/0004-Add-config-to-enable-Cross-Region-Damage-trace.patch index d13e04d..ecec090 100644 --- a/lophine-server/minecraft-patches/features/0005-Add-config-to-enable-Cross-Region-Damage-trace.patch +++ b/lophine-server/minecraft-patches/features/0004-Add-config-to-enable-Cross-Region-Damage-trace.patch @@ -48,40 +48,10 @@ index 11e6197dec541b28733715f0d7eaa4c7b834d632..6b7c92fdf562dfa46eee8e8acbc82b02 AABB aabb = new AABB(this.blockPosition()).inflate(32.0, 10.0, 32.0); this.level() diff --git a/net/minecraft/world/entity/LivingEntity.java b/net/minecraft/world/entity/LivingEntity.java -index 567d63ae589705e13403814b36e35d00c241a69e..919628d93add71d9b30cae29587a0049cce6973d 100644 +index 567d63ae589705e13403814b36e35d00c241a69e..3f4b1c54f9207ba1854a8c8da965cbaca5ea517d 100644 --- a/net/minecraft/world/entity/LivingEntity.java +++ b/net/minecraft/world/entity/LivingEntity.java -@@ -1216,6 +1216,29 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin - } - } - -+ // Luminol Start - raid revert adapt Cross Region Damage trace -+ public boolean addEffect(MobEffectInstance effectInstance, @Nullable Entity entity, EntityPotionEffectEvent.Cause cause, boolean fireEvent, boolean async) { -+ if (ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(entity)) { -+ return addEffect(effectInstance, entity, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.PATROL_CAPTAIN, true); -+ } else if (fun.bm.lophine.config.modules.experiment.EntityDamageSourceTraceConfig.enabled) { -+ postToEntityThreadAddEffect(effectInstance, entity, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.PATROL_CAPTAIN, true); -+ return true; -+ } -+ return false; -+ } -+ -+ private void postToEntityThreadAddEffect(MobEffectInstance effectInstance, @Nullable Entity entity, EntityPotionEffectEvent.Cause cause, boolean fireEvent) { -+ if (entity != null) -+ entity.getBukkitEntity().taskScheduler.schedule((Entity nmsEntity) -> { -+ try { -+ addEffect(effectInstance, nmsEntity, cause, fireEvent); -+ } catch (Throwable ex) { -+ LOGGER.error(ex.getMessage(), ex); -+ } -+ }, null, 1L ); -+ } -+ // Luminol End - raid revert adapt Cross Region Damage trace -+ - public boolean canBeAffected(MobEffectInstance effectInstance) { - if (this.getType().is(EntityTypeTags.IMMUNE_TO_INFESTED)) { - return !effectInstance.is(MobEffects.INFESTED); -@@ -1948,6 +1971,13 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin +@@ -1948,6 +1948,13 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin final LivingEntity killer = this.getKillCredit(); if (killer != null) { killer.awardKillScore(this, damageSource); @@ -95,7 +65,7 @@ index 567d63ae589705e13403814b36e35d00c241a69e..919628d93add71d9b30cae29587a0049 } }); // Paper end this.postDeathDropItems(deathEvent); // Paper -@@ -1958,6 +1988,18 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin +@@ -1958,6 +1965,18 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin return deathEvent; // Paper } @@ -114,7 +84,7 @@ index 567d63ae589705e13403814b36e35d00c241a69e..919628d93add71d9b30cae29587a0049 protected void dropEquipment(ServerLevel level) { } protected void postDeathDropItems(org.bukkit.event.entity.EntityDeathEvent event) {} // Paper - method for post death logic that cannot be ran before the event is potentially cancelled -@@ -2556,6 +2598,18 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin +@@ -2556,6 +2575,18 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin } } @@ -133,22 +103,3 @@ index 567d63ae589705e13403814b36e35d00c241a69e..919628d93add71d9b30cae29587a0049 public final float getMaxHealth() { return (float)this.getAttributeValue(Attributes.MAX_HEALTH); } -diff --git a/net/minecraft/world/entity/raid/Raider.java b/net/minecraft/world/entity/raid/Raider.java -index 510514e363f4816a6e286fb9b5488153e03c9e55..ce5602283757c564b45ec0765b5f1868ffcd180b 100644 ---- a/net/minecraft/world/entity/raid/Raider.java -+++ b/net/minecraft/world/entity/raid/Raider.java -@@ -157,7 +157,13 @@ public abstract class Raider extends PatrollingMonster { - net.minecraft.world.effect.MobEffectInstance mobeffect1 = new net.minecraft.world.effect.MobEffectInstance(net.minecraft.world.effect.MobEffects.BAD_OMEN, fun.bm.lophine.config.modules.misc.RaidChangesConfig.infinite ? net.minecraft.world.effect.MobEffectInstance.INFINITE_DURATION : 120000, i, false, false, true); - - if (!serverLevel.getGameRules().getBoolean(net.minecraft.world.level.GameRules.RULE_DISABLE_RAIDS)) { -- entityhuman.addEffect(mobeffect1, entityhuman, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.PATROL_CAPTAIN, true); // CraftBukkit -+ // Luminol start - raid revert adapt Cross Region Damage trace -+ if (fun.bm.lophine.config.modules.experiment.EntityDamageSourceTraceConfig.enabled) { -+ entityhuman.addEffect(mobeffect1, entityhuman, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.PATROL_CAPTAIN, true, true); -+ } else { -+ entityhuman.addEffect(mobeffect1, entityhuman, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.PATROL_CAPTAIN, true); // CraftBukkit -+ } -+ // Luminol end - raid revert adapt Cross Region Damage trace - } - this.setPatrolLeader(false); - } diff --git a/lophine-server/minecraft-patches/features/0004-Add-config-to-revert-raid-changes.patch b/lophine-server/minecraft-patches/features/0004-Add-config-to-revert-raid-changes.patch deleted file mode 100644 index 9ab0b9d..0000000 --- a/lophine-server/minecraft-patches/features/0004-Add-config-to-revert-raid-changes.patch +++ /dev/null @@ -1,207 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Helvetica Volubi -Date: Fri, 11 Apr 2025 16:53:57 +0800 -Subject: [PATCH] Add config to revert raid changes - -Co-authored by: huanli233 <392352840@qq.com> -Some of changes is a part of : Leaves (https://github.com/LeavesMC/Leaves/blob/b5793e809b6346e16b7ea218496806a312562e27/leaves-server/minecraft-patches/features/0106-Revert-raid-changes.patch) -Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html) - -diff --git a/net/minecraft/world/effect/BadOmenMobEffect.java b/net/minecraft/world/effect/BadOmenMobEffect.java -index 80f17f33f670018240c854df589cf90cdeab6e70..985f27d249810bf133a520d937b7006e57f553ec 100644 ---- a/net/minecraft/world/effect/BadOmenMobEffect.java -+++ b/net/minecraft/world/effect/BadOmenMobEffect.java -@@ -22,6 +22,11 @@ class BadOmenMobEffect extends MobEffect { - && !serverPlayer.isSpectator() - && level.getDifficulty() != Difficulty.PEACEFUL - && level.isVillage(serverPlayer.blockPosition())) { -+ // Leaves start - Revert raid changes -+ if (fun.bm.lophine.config.modules.misc.RaidChangesConfig.trigger) { -+ return level.getRaids().createOrExtendRaid(serverPlayer, serverPlayer.blockPosition()) == null; -+ } -+ // Leaves end - Revert raid changes - Raid raidAt = level.getRaidAt(serverPlayer.blockPosition()); - if (raidAt == null || raidAt.getRaidOmenLevel() < raidAt.getMaxRaidOmenLevel()) { - serverPlayer.addEffect(new MobEffectInstance(MobEffects.RAID_OMEN, 600, amplifier)); -diff --git a/net/minecraft/world/entity/raid/Raid.java b/net/minecraft/world/entity/raid/Raid.java -index 1910bff3f08147bfd63703097e30b9f88c84e337..26f4d7359f638a11ea2d768d18091e261d6b1791 100644 ---- a/net/minecraft/world/entity/raid/Raid.java -+++ b/net/minecraft/world/entity/raid/Raid.java -@@ -340,7 +340,20 @@ public class Raid { - } - - if (flag1) { -- this.waveSpawnPos = this.getValidSpawnPos(level); -+ // Luminol Start - Raid revert -+ if (!fun.bm.lophine.config.modules.misc.RaidChangesConfig.posRevert) { -+ this.waveSpawnPos = this.getValidSpawnPos(level); -+ } else { -+ int n4 = 0; -+ if (this.raidCooldownTicks < 100) { -+ n4 = 1; -+ } -+ if (this.raidCooldownTicks < 40) { -+ n4 = 2; -+ } -+ this.waveSpawnPos = this.getValidSpawnPos(level, n4); -+ } -+ // Luminol End - Raid revert - } - - if (this.raidCooldownTicks == 300 || this.raidCooldownTicks % 20 == 0) { -@@ -375,7 +388,14 @@ public class Raid { - int i = 0; - - while (this.shouldSpawnGroup()) { -- BlockPos blockPos = this.waveSpawnPos.orElseGet(() -> this.findRandomSpawnPos(level, 20)); -+ // Luminol Start - Raid revert -+ BlockPos blockPos; -+ if (!fun.bm.lophine.config.modules.misc.RaidChangesConfig.posRevert) { -+ blockPos = this.waveSpawnPos.orElseGet(() -> this.findRandomSpawnPos(level, 20)); -+ } else { -+ blockPos = this.waveSpawnPos.isPresent() ? this.waveSpawnPos.get() : this.findRandomSpawnPos(level, i, 20); -+ } -+ // Luminol End - Raid revert - if (blockPos != null) { - this.started = true; - this.spawnGroup(level, blockPos); -@@ -387,7 +407,7 @@ public class Raid { - i++; - } - -- if (i > 5) { -+ if (i > (fun.bm.lophine.config.modules.misc.RaidChangesConfig.posRevert ? 3 : 5)) { // Luminol - Raid revert - org.bukkit.craftbukkit.event.CraftEventFactory.callRaidStopEvent(level, this, org.bukkit.event.raid.RaidStopEvent.Reason.UNSPAWNABLE); // CraftBukkit - this.stop(); - break; -@@ -458,6 +478,17 @@ public class Raid { - return blockPos != null ? Optional.of(blockPos) : Optional.empty(); - } - -+ // Luminol Start - Raid revert -+ private Optional getValidSpawnPos(ServerLevel level, int n) { -+ for (int i = 0; i < 3; ++i) { -+ BlockPos blockPos = this.findRandomSpawnPos(level, n, 1); -+ if (blockPos == null) continue; -+ return Optional.of(blockPos); -+ } -+ return Optional.empty(); -+ } -+ // Luminol End - Raid revert -+ - private boolean hasMoreWaves() { - return this.hasBonusWave() ? !this.hasSpawnedBonusWave() : !this.isFinalWave(); - } -@@ -683,7 +714,7 @@ public class Raid { - int i2 = this.center.getX() + Mth.floor(Mth.cos(f2) * 32.0F * f) + level.random.nextInt(3) * Mth.floor(f); - int i3 = this.center.getZ() + Mth.floor(Mth.sin(f2) * 32.0F * f) + level.random.nextInt(3) * Mth.floor(f); - int height = level.getHeight(Heightmap.Types.WORLD_SURFACE, i2, i3); -- if (Mth.abs(height - this.center.getY()) <= 96) { -+ if (fun.bm.lophine.config.modules.misc.RaidChangesConfig.heightCheck || Mth.abs(height - this.center.getY()) <= 96) { // Leaves - Disable height check - mutableBlockPos.set(i2, height, i3); - if (!level.isVillage(mutableBlockPos) || i <= 7) { - int i4 = 10; -@@ -702,6 +733,26 @@ public class Raid { - return null; - } - -+ // Luminol Start - Raid revert -+ @Nullable -+ private BlockPos findRandomSpawnPos(ServerLevel level, int n, int n2) { -+ int n3 = 2 - n; -+ BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos(); -+ SpawnPlacementType spawnPlacementType = SpawnPlacements.getPlacementType(EntityType.RAVAGER); -+ for (int i = 0; i < n2; ++i) { -+ float f = level.random.nextFloat() * ((float)Math.PI * 2); -+ int n4 = this.center.getX() + Mth.floor(Mth.cos(f) * 32.0f * (float)n3) + level.random.nextInt(5); -+ int n5 = this.center.getZ() + Mth.floor(Mth.sin(f) * 32.0f * (float)n3) + level.random.nextInt(5); -+ int n6 = level.getHeight(Heightmap.Types.WORLD_SURFACE, n4, n5); -+ mutableBlockPos.set(n4, n6, n5); -+ if (level.isVillage(mutableBlockPos) && n < 2) continue; -+ if (!level.hasChunksAt(mutableBlockPos.getX() - 10, mutableBlockPos.getZ() - 10, mutableBlockPos.getX() + 10, mutableBlockPos.getZ() + 10) || !level.isPositionEntityTicking(mutableBlockPos) || !spawnPlacementType.isSpawnPositionOk(level, mutableBlockPos, EntityType.RAVAGER) && (!level.getBlockState(mutableBlockPos.below()).is(Blocks.SNOW) || !level.getBlockState(mutableBlockPos).isAir())) continue; -+ return mutableBlockPos; -+ } -+ return null; -+ } -+ // Luminol End - Raid revert -+ - private boolean addWaveMob(ServerLevel level, int wave, Raider raider) { - // Folia start - make raids thread-safe - if (!this.ownsRaid(level)) { -diff --git a/net/minecraft/world/entity/raid/Raider.java b/net/minecraft/world/entity/raid/Raider.java -index 3e30989c99cd11c8c94d6fd194d2969e3a7efda6..510514e363f4816a6e286fb9b5488153e03c9e55 100644 ---- a/net/minecraft/world/entity/raid/Raider.java -+++ b/net/minecraft/world/entity/raid/Raider.java -@@ -128,6 +128,41 @@ public abstract class Raider extends PatrollingMonster { - - currentRaid.removeFromRaid(serverLevel, this, false); - } -+ -+ // Leaves start - Revert raid changes -+ if (fun.bm.lophine.config.modules.misc.RaidChangesConfig.effect && !this.hasRaid()) { -+ ItemStack itemstack = this.getItemBySlot(EquipmentSlot.HEAD); -+ net.minecraft.world.entity.player.Player entityhuman = null; -+ if (entity instanceof net.minecraft.world.entity.player.Player player) { -+ entityhuman = player; -+ } else if (entity instanceof net.minecraft.world.entity.animal.wolf.Wolf wolf) { -+ LivingEntity entityliving = wolf.getOwner(); -+ if (wolf.isTame() && entityliving instanceof net.minecraft.world.entity.player.Player player) { -+ entityhuman = player; -+ } -+ } -+ -+ if (entityhuman != null && !itemstack.isEmpty() && this.isCaptain()) { -+ net.minecraft.world.effect.MobEffectInstance mobeffect = entityhuman.getEffect(net.minecraft.world.effect.MobEffects.BAD_OMEN); -+ int i = 1; -+ -+ if (mobeffect != null) { -+ i += mobeffect.getAmplifier(); -+ entityhuman.removeEffectNoUpdate(net.minecraft.world.effect.MobEffects.BAD_OMEN); -+ } else { -+ --i; -+ } -+ -+ i = net.minecraft.util.Mth.clamp(i, 0, 4); -+ net.minecraft.world.effect.MobEffectInstance mobeffect1 = new net.minecraft.world.effect.MobEffectInstance(net.minecraft.world.effect.MobEffects.BAD_OMEN, fun.bm.lophine.config.modules.misc.RaidChangesConfig.infinite ? net.minecraft.world.effect.MobEffectInstance.INFINITE_DURATION : 120000, i, false, false, true); -+ -+ if (!serverLevel.getGameRules().getBoolean(net.minecraft.world.level.GameRules.RULE_DISABLE_RAIDS)) { -+ entityhuman.addEffect(mobeffect1, entityhuman, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.PATROL_CAPTAIN, true); // CraftBukkit -+ } -+ this.setPatrolLeader(false); -+ } -+ } -+ // Leaves end - Revert raid changes - } - - super.die(cause); -@@ -156,7 +191,7 @@ public abstract class Raider extends PatrollingMonster { - } - - public boolean hasRaid() { -- return this.level() instanceof ServerLevel serverLevel && (this.getCurrentRaid() != null || serverLevel.getRaidAt(this.blockPosition()) != null); -+ return !fun.bm.lophine.config.modules.misc.RaidChangesConfig.selfCheck && (this.level() instanceof ServerLevel serverLevel && (this.getCurrentRaid() != null || serverLevel.getRaidAt(this.blockPosition()) != null)); // Leaves - Disable raid self check - } - - public boolean hasActiveRaid() { -diff --git a/net/minecraft/world/item/component/OminousBottleAmplifier.java b/net/minecraft/world/item/component/OminousBottleAmplifier.java -index 33907bb190ffa22ccf9ea424b1e536297878711a..2fca459f2c888079322f6b957bbee3ac0e33c4bc 100644 ---- a/net/minecraft/world/item/component/OminousBottleAmplifier.java -+++ b/net/minecraft/world/item/component/OminousBottleAmplifier.java -@@ -29,7 +29,7 @@ public record OminousBottleAmplifier(int value) implements ConsumableListener, T - - @Override - public void onConsume(Level level, LivingEntity entity, ItemStack stack, Consumable consumable) { -- entity.addEffect(new MobEffectInstance(MobEffects.BAD_OMEN, 120000, this.value, false, false, true)); // Paper - properly resend entities - diff on change for below -+ entity.addEffect(new MobEffectInstance(MobEffects.BAD_OMEN, fun.bm.lophine.config.modules.misc.RaidChangesConfig.infinite ? net.minecraft.world.effect.MobEffectInstance.INFINITE_DURATION : 120000, this.value, false, false, true)); // Paper - properly resend entities - diff on change for below // Luminol - Raid effect infinite - } - - // Paper start - properly resend entities - collect packets for bundle -@@ -41,7 +41,7 @@ public record OminousBottleAmplifier(int value) implements ConsumableListener, T - - @Override - public void addToTooltip(Item.TooltipContext context, Consumer tooltipAdder, TooltipFlag flag, DataComponentGetter componentGetter) { -- List list = List.of(new MobEffectInstance(MobEffects.BAD_OMEN, 120000, this.value, false, false, true)); -+ List list = List.of(new MobEffectInstance(MobEffects.BAD_OMEN, fun.bm.lophine.config.modules.misc.RaidChangesConfig.infinite ? net.minecraft.world.effect.MobEffectInstance.INFINITE_DURATION : 120000, this.value, false, false, true)); // Luminol - Raid effect infinite - PotionContents.addPotionTooltip(list, tooltipAdder, 1.0F, context.tickRate()); - } - } diff --git a/lophine-server/minecraft-patches/features/0006-Add-config-to-enable-Raytracing-tracker.patch b/lophine-server/minecraft-patches/features/0005-Add-config-to-enable-Raytracing-tracker.patch similarity index 100% rename from lophine-server/minecraft-patches/features/0006-Add-config-to-enable-Raytracing-tracker.patch rename to lophine-server/minecraft-patches/features/0005-Add-config-to-enable-Raytracing-tracker.patch diff --git a/lophine-server/minecraft-patches/features/0007-Add-config-to-allow-infinite-trade.patch b/lophine-server/minecraft-patches/features/0006-Add-config-to-allow-infinite-trade.patch similarity index 100% rename from lophine-server/minecraft-patches/features/0007-Add-config-to-allow-infinite-trade.patch rename to lophine-server/minecraft-patches/features/0006-Add-config-to-allow-infinite-trade.patch diff --git a/lophine-server/minecraft-patches/features/0008-Add-config-to-enable-unsafe-waypoint-bar.patch b/lophine-server/minecraft-patches/features/0007-Add-config-to-enable-unsafe-waypoint-bar.patch similarity index 100% rename from lophine-server/minecraft-patches/features/0008-Add-config-to-enable-unsafe-waypoint-bar.patch rename to lophine-server/minecraft-patches/features/0007-Add-config-to-enable-unsafe-waypoint-bar.patch diff --git a/lophine-server/minecraft-patches/features/0009-Leaves-Leaves-Protocol-Core.patch b/lophine-server/minecraft-patches/features/0008-Leaves-Leaves-Protocol-Core.patch similarity index 100% rename from lophine-server/minecraft-patches/features/0009-Leaves-Leaves-Protocol-Core.patch rename to lophine-server/minecraft-patches/features/0008-Leaves-Leaves-Protocol-Core.patch diff --git a/lophine-server/minecraft-patches/features/0010-Leaves-Disable-packet-limit.patch b/lophine-server/minecraft-patches/features/0009-Leaves-Disable-packet-limit.patch similarity index 100% rename from lophine-server/minecraft-patches/features/0010-Leaves-Disable-packet-limit.patch rename to lophine-server/minecraft-patches/features/0009-Leaves-Disable-packet-limit.patch diff --git a/lophine-server/minecraft-patches/features/0011-Leaves-Item-overstack-util.patch b/lophine-server/minecraft-patches/features/0010-Leaves-Item-overstack-util.patch similarity index 100% rename from lophine-server/minecraft-patches/features/0011-Leaves-Item-overstack-util.patch rename to lophine-server/minecraft-patches/features/0010-Leaves-Item-overstack-util.patch diff --git a/lophine-server/minecraft-patches/features/0011-Leaves-Redstone-Shears-Wrench.patch b/lophine-server/minecraft-patches/features/0011-Leaves-Redstone-Shears-Wrench.patch new file mode 100644 index 0000000..07091eb --- /dev/null +++ b/lophine-server/minecraft-patches/features/0011-Leaves-Redstone-Shears-Wrench.patch @@ -0,0 +1,44 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Helvetica Volubi +Date: Wed, 23 Jul 2025 20:28:40 +0800 +Subject: [PATCH] Leaves: Redstone Shears Wrench + +Co-authored by: violetc <58360096+s-yh-china@users.noreply.github.com> +As a part of : Leaves (https://github.com/LeavesMC/Leaves/blob/79d9ef74c2684eb49060f07e1ab31c827326c9a5/leaves-server/minecraft-patches/features/0009-Redstone-Shears-Wrench.patch) +Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html) + +diff --git a/net/minecraft/world/item/BlockItem.java b/net/minecraft/world/item/BlockItem.java +index 6db566adf2d0df1d26221eda04aa01738df6d3d2..24ebfdf98e343a7892af621d032c35819156deec 100644 +--- a/net/minecraft/world/item/BlockItem.java ++++ b/net/minecraft/world/item/BlockItem.java +@@ -56,6 +56,14 @@ public class BlockItem extends Item { + if (blockPlaceContext == null) { + return InteractionResult.FAIL; + } else { ++ // Leaves start - shears wrench ++ if (org.leavesmc.leaves.util.ShearsWrenchUtil.shouldSkipPlace(context)) { ++ if (context.getPlayer() != null) { ++ context.getPlayer().containerMenu.forceHeldSlot(blockPlaceContext.getHand()); ++ } ++ return InteractionResult.FAIL; ++ } ++ // Leaves end - shears wrench + BlockState placementState = this.getPlacementState(blockPlaceContext); + // CraftBukkit start - special case for handling block placement with water lilies and snow buckets + org.bukkit.block.BlockState bukkitState = null; +diff --git a/net/minecraft/world/item/ShearsItem.java b/net/minecraft/world/item/ShearsItem.java +index 8cf3e51e12f9cf98836657e722edb23943f9e866..5fd5e9fd4e1a79dd1a9d62a5cf0c308805979420 100644 +--- a/net/minecraft/world/item/ShearsItem.java ++++ b/net/minecraft/world/item/ShearsItem.java +@@ -80,7 +80,10 @@ public class ShearsItem extends Item { + + return InteractionResult.SUCCESS; + } else { +- return super.useOn(context); ++ // Leaves start - shears wrench ++ InteractionResult result = org.leavesmc.leaves.util.ShearsWrenchUtil.tryApplyRotate(context, blockState); ++ return result == null ? super.useOn(context) : result; ++ // Leaves end - shears wrench + } + } + } diff --git a/lophine-server/minecraft-patches/features/0012-Leaves-Redstone-Shears-Wrench.patch b/lophine-server/minecraft-patches/features/0012-Leaves-Redstone-Shears-Wrench.patch deleted file mode 100644 index 070a069..0000000 --- a/lophine-server/minecraft-patches/features/0012-Leaves-Redstone-Shears-Wrench.patch +++ /dev/null @@ -1,153 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Helvetica Volubi -Date: Wed, 23 Jul 2025 20:28:40 +0800 -Subject: [PATCH] Leaves: Redstone Shears Wrench - -Co-authored by: violetc <58360096+s-yh-china@users.noreply.github.com> -As a part of : Leaves (https://github.com/LeavesMC/Leaves/blob/bfde470a867b74d177fe1b9a2a3ed5c49cd126c7/leaves-server/minecraft-patches/features/0009-Redstone-Shears-Wrench.patch) -Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html) - -diff --git a/net/minecraft/world/item/ShearsItem.java b/net/minecraft/world/item/ShearsItem.java -index 8cf3e51e12f9cf98836657e722edb23943f9e866..155693a18bb641a930a000bc9b1ccfc853e1bae5 100644 ---- a/net/minecraft/world/item/ShearsItem.java -+++ b/net/minecraft/world/item/ShearsItem.java -@@ -24,6 +24,30 @@ import net.minecraft.world.level.block.GrowingPlantHeadBlock; - import net.minecraft.world.level.block.state.BlockState; - import net.minecraft.world.level.gameevent.GameEvent; - -+// Leaves start - shears wrench -+import net.minecraft.Util; -+import net.minecraft.network.chat.Component; -+import net.minecraft.world.level.block.ComparatorBlock; -+import net.minecraft.world.level.block.DispenserBlock; -+import net.minecraft.world.level.block.HopperBlock; -+import net.minecraft.world.level.block.ObserverBlock; -+import net.minecraft.world.level.block.RepeaterBlock; -+import net.minecraft.world.level.block.CrafterBlock; -+import net.minecraft.world.level.block.LeverBlock; -+import net.minecraft.world.level.block.CocoaBlock; -+import net.minecraft.world.level.block.TrapDoorBlock; -+import net.minecraft.world.level.block.FenceGateBlock; -+import net.minecraft.world.level.block.LightningRodBlock; -+import net.minecraft.world.level.block.RailBlock; -+import net.minecraft.world.level.block.BaseRailBlock; -+import net.minecraft.world.level.block.PoweredRailBlock; -+import net.minecraft.world.level.block.CalibratedSculkSensorBlock; -+import net.minecraft.world.level.block.piston.PistonBaseBlock; -+import net.minecraft.world.level.block.state.StateDefinition; -+import net.minecraft.world.level.block.state.properties.RailShape; -+import net.minecraft.world.level.block.state.properties.Property; -+// Leaves end - shears wrench -+ - public class ShearsItem extends Item { - public ShearsItem(Item.Properties properties) { - super(properties); -@@ -80,7 +104,108 @@ public class ShearsItem extends Item { - - return InteractionResult.SUCCESS; - } else { -+ // Leaves start - shears wrench -+ Block block = blockState.getBlock(); -+ if (fun.bm.lophine.config.modules.misc.RedStoneConfig.shears && ( -+ block instanceof ObserverBlock || -+ block instanceof DispenserBlock || -+ block instanceof PistonBaseBlock || -+ block instanceof HopperBlock || -+ block instanceof RepeaterBlock || -+ block instanceof ComparatorBlock || -+ block instanceof CrafterBlock || -+ block instanceof LeverBlock || -+ block instanceof CocoaBlock || -+ block instanceof TrapDoorBlock || -+ block instanceof FenceGateBlock || -+ block instanceof LightningRodBlock || -+ block instanceof CalibratedSculkSensorBlock || -+ block instanceof BaseRailBlock -+ )) { -+ StateDefinition blockstatelist = block.getStateDefinition(); -+ Property iblockstate; -+ if (block instanceof CrafterBlock) iblockstate = blockstatelist.getProperty("orientation"); -+ else if (block instanceof BaseRailBlock) iblockstate = blockstatelist.getProperty("shape"); -+ else iblockstate = blockstatelist.getProperty("facing"); -+ Player player = context.getPlayer(); -+ -+ if (iblockstate == null || player == null) { -+ return InteractionResult.FAIL; -+ } -+ -+ if (block instanceof BaseRailBlock) { -+ if (block instanceof RailBlock) { -+ if (blockState.getValue(RailBlock.SHAPE).isSlope()) { -+ return InteractionResult.FAIL; -+ } -+ } else { -+ if (getNameHelper(blockState, PoweredRailBlock.POWERED).equals("true")) { -+ return InteractionResult.FAIL; -+ } -+ if (blockState.getValue(PoweredRailBlock.SHAPE).isSlope()) { -+ return InteractionResult.FAIL; -+ } -+ } -+ } -+ -+ if (block instanceof PistonBaseBlock) { -+ if (getNameHelper(blockState, PistonBaseBlock.EXTENDED).equals("true")) { -+ return InteractionResult.FAIL; -+ } -+ } -+ -+ if (block instanceof RepeaterBlock || block instanceof ComparatorBlock) { -+ if (getNameHelper(blockState, ComparatorBlock.POWERED).equals("true")) { -+ return InteractionResult.FAIL; -+ } -+ if (block instanceof RepeaterBlock) { -+ if (getNameHelper(blockState, RepeaterBlock.LOCKED).equals("true")) { -+ return InteractionResult.FAIL; -+ } -+ } -+ } -+ -+ if (block instanceof CrafterBlock) { -+ if (getNameHelper(blockState, CrafterBlock.CRAFTING).equals("true")) { -+ return InteractionResult.FAIL; -+ } -+ } -+ -+ BlockState iblockdata1 = ShearsItem.cycleState(blockState, iblockstate, player.isSecondaryUseActive()); -+ level.setBlock(clickedPos, iblockdata1, 18); -+ ShearsItem.message(player, Component.translatable("item.minecraft.debug_stick.update", iblockstate.getName(), ShearsItem.getNameHelper(iblockdata1, iblockstate))); -+ return InteractionResult.CONSUME; -+ } -+ // Leaves end - shears wrench -+ - return super.useOn(context); - } - } -+ -+ // Leaves start - shears wrench -+ private static > BlockState cycleState(BlockState state, Property property, boolean inverse) { -+ List possibleValues = property.getPossibleValues(); -+ if (possibleValues.getFirst() instanceof RailShape) { -+ boolean isRailBlock = state.getBlock() instanceof RailBlock; -+ possibleValues = possibleValues.stream().filter(possibleValue -> { -+ RailShape shape = (RailShape) possibleValue; -+ if (isRailBlock) return !shape.isSlope(); -+ return !shape.isSlope() && shape != RailShape.NORTH_EAST && shape != RailShape.NORTH_WEST && shape != RailShape.SOUTH_EAST && shape != RailShape.SOUTH_WEST; -+ }).toList(); -+ } -+ return state.setValue(property, ShearsItem.getRelative(possibleValues, state.getValue(property), inverse)); // CraftBukkit - decompile error -+ } -+ -+ private static T getRelative(Iterable elements, T current, boolean inverse) { -+ return inverse ? Util.findPreviousInIterable(elements, current) : Util.findNextInIterable(elements, current); -+ } -+ -+ private static void message(Player player, Component message) { -+ ((ServerPlayer) player).sendSystemMessage(message, true); -+ } -+ -+ private static > String getNameHelper(BlockState state, Property property) { -+ return property.getName(state.getValue(property)); -+ } -+ // Leaves end - shears wrench - } diff --git a/lophine-server/minecraft-patches/features/0013-Leaves-Servux-Protocol.patch b/lophine-server/minecraft-patches/features/0012-Leaves-Servux-Protocol.patch similarity index 100% rename from lophine-server/minecraft-patches/features/0013-Leaves-Servux-Protocol.patch rename to lophine-server/minecraft-patches/features/0012-Leaves-Servux-Protocol.patch diff --git a/lophine-server/minecraft-patches/features/0016-Old-Explosion-Damage-Calculator.patch b/lophine-server/minecraft-patches/features/0013-Leaves-Old-Explosion-Damage-Calculator.patch similarity index 96% rename from lophine-server/minecraft-patches/features/0016-Old-Explosion-Damage-Calculator.patch rename to lophine-server/minecraft-patches/features/0013-Leaves-Old-Explosion-Damage-Calculator.patch index 5ee68d7..3c4ac89 100644 --- a/lophine-server/minecraft-patches/features/0016-Old-Explosion-Damage-Calculator.patch +++ b/lophine-server/minecraft-patches/features/0013-Leaves-Old-Explosion-Damage-Calculator.patch @@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Helvetica Volubi Date: Mon, 7 Jul 2025 15:33:57 +0800 -Subject: [PATCH] Old Explosion Damage Calculator +Subject: [PATCH] Leaves: Old Explosion Damage Calculator Co-authored by: MC_XiaoHei As a part of : Leaves (https://github.com/LeavesMC/Leaves/blob/3e96b237749a960f297f211d439ffc9ea7fd2381/leaves-server/minecraft-patches/features/0134-Old-wet-tnt-explode-behavior.patch) diff --git a/lophine-server/minecraft-patches/features/0014-Leaves-Old-raid-behavior.patch b/lophine-server/minecraft-patches/features/0014-Leaves-Old-raid-behavior.patch new file mode 100644 index 0000000..f53572e --- /dev/null +++ b/lophine-server/minecraft-patches/features/0014-Leaves-Old-raid-behavior.patch @@ -0,0 +1,216 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Helvetica Volubi +Date: Fri, 11 Apr 2025 16:53:57 +0800 +Subject: [PATCH] Leaves: Old raid behavior + +Co-authored by: huanli233 <392352840@qq.com> +As a part of : Leaves (https://github.com/LeavesMC/Leaves/blob/79d9ef74c2684eb49060f07e1ab31c827326c9a5/leaves-server/minecraft-patches/features/0106-Old-raid-behavior.patch) +Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html) + +diff --git a/net/minecraft/world/effect/BadOmenMobEffect.java b/net/minecraft/world/effect/BadOmenMobEffect.java +index 80f17f33f670018240c854df589cf90cdeab6e70..cbfb4ae2dc30c2ee98ef37f44a95434a7968e320 100644 +--- a/net/minecraft/world/effect/BadOmenMobEffect.java ++++ b/net/minecraft/world/effect/BadOmenMobEffect.java +@@ -22,6 +22,11 @@ class BadOmenMobEffect extends MobEffect { + && !serverPlayer.isSpectator() + && level.getDifficulty() != Difficulty.PEACEFUL + && level.isVillage(serverPlayer.blockPosition())) { ++ // Leaves start - Revert raid changes ++ if (fun.bm.lophine.config.modules.misc.OldFeatureConfig.oldRaidBehavior) { ++ return level.getRaids().createOrExtendRaid(serverPlayer, serverPlayer.blockPosition()) == null; ++ } ++ // Leaves end - Revert raid changes + Raid raidAt = level.getRaidAt(serverPlayer.blockPosition()); + if (raidAt == null || raidAt.getRaidOmenLevel() < raidAt.getMaxRaidOmenLevel()) { + serverPlayer.addEffect(new MobEffectInstance(MobEffects.RAID_OMEN, 600, amplifier)); +diff --git a/net/minecraft/world/entity/raid/Raid.java b/net/minecraft/world/entity/raid/Raid.java +index 1910bff3f08147bfd63703097e30b9f88c84e337..5188ae86aff3051d45fe5e7d2fcc51233b7074d4 100644 +--- a/net/minecraft/world/entity/raid/Raid.java ++++ b/net/minecraft/world/entity/raid/Raid.java +@@ -263,7 +263,7 @@ public class Raid { + } + + public boolean absorbRaidOmen(ServerPlayer player) { +- MobEffectInstance effect = player.getEffect(MobEffects.RAID_OMEN); ++ MobEffectInstance effect = player.getEffect(fun.bm.lophine.config.modules.misc.OldFeatureConfig.oldRaidBehavior ? MobEffects.BAD_OMEN : MobEffects.RAID_OMEN); // Leaves - old Raid Behavior + if (effect == null) { + return false; + } else { +@@ -340,7 +340,13 @@ public class Raid { + } + + if (flag1) { +- this.waveSpawnPos = this.getValidSpawnPos(level); ++ // Leaves Start - old FindSpawnPosition ++ if (fun.bm.lophine.config.modules.misc.OldFeatureConfig.oldRaidBehavior) { ++ this.waveSpawnPos = this.preCalcRavagerSpawnLocation(level, this.raidCooldownTicks < 100 ? 1 : 0); ++ } else { ++ this.waveSpawnPos = this.getValidSpawnPos(level); ++ } ++ // Leaves End - old FindSpawnPosition + } + + if (this.raidCooldownTicks == 300 || this.raidCooldownTicks % 20 == 0) { +@@ -375,7 +381,14 @@ public class Raid { + int i = 0; + + while (this.shouldSpawnGroup()) { +- BlockPos blockPos = this.waveSpawnPos.orElseGet(() -> this.findRandomSpawnPos(level, 20)); ++ // Leaves Start - old FindSpawnPosition ++ BlockPos blockPos; ++ if (fun.bm.lophine.config.modules.misc.OldFeatureConfig.oldRaidBehavior) { ++ blockPos = this.getRavagerSpawnLocation(level, i, 20); ++ } else { ++ blockPos = this.waveSpawnPos.orElseGet(() -> this.findRandomSpawnPos(level, 20)); ++ } ++ // Leaves End - old FindSpawnPosition + if (blockPos != null) { + this.started = true; + this.spawnGroup(level, blockPos); +@@ -387,7 +400,7 @@ public class Raid { + i++; + } + +- if (i > 5) { ++ if (i > (fun.bm.lophine.config.modules.misc.OldFeatureConfig.oldRaidBehavior ? 3 : 5)) { // Leaves - old FindSpawnPosition + org.bukkit.craftbukkit.event.CraftEventFactory.callRaidStopEvent(level, this, org.bukkit.event.raid.RaidStopEvent.Reason.UNSPAWNABLE); // CraftBukkit + this.stop(); + break; +@@ -683,7 +696,7 @@ public class Raid { + int i2 = this.center.getX() + Mth.floor(Mth.cos(f2) * 32.0F * f) + level.random.nextInt(3) * Mth.floor(f); + int i3 = this.center.getZ() + Mth.floor(Mth.sin(f2) * 32.0F * f) + level.random.nextInt(3) * Mth.floor(f); + int height = level.getHeight(Heightmap.Types.WORLD_SURFACE, i2, i3); +- if (Mth.abs(height - this.center.getY()) <= 96) { ++ if (fun.bm.lophine.config.modules.misc.OldFeatureConfig.oldRaidBehavior || Mth.abs(height - this.center.getY()) <= 96) { // Leaves - skippable + mutableBlockPos.set(i2, height, i3); + if (!level.isVillage(mutableBlockPos) || i <= 7) { + int i4 = 10; +@@ -702,6 +715,36 @@ public class Raid { + return null; + } + ++ // Leaves Start - old FindSpawnPosition ++ @Nullable ++ private BlockPos findRandomSpawnPos(ServerLevel level, int n, int n2) { ++ int n3 = 2 - n; ++ BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos(); ++ SpawnPlacementType spawnPlacementType = SpawnPlacements.getPlacementType(EntityType.RAVAGER); ++ for (int i = 0; i < n2; ++i) { ++ float f = level.random.nextFloat() * ((float)Math.PI * 2); ++ int n4 = this.center.getX() + Mth.floor(Mth.cos(f) * 32.0f * (float)n3) + level.random.nextInt(5); ++ int n5 = this.center.getZ() + Mth.floor(Mth.sin(f) * 32.0f * (float)n3) + level.random.nextInt(5); ++ int n6 = level.getHeight(Heightmap.Types.WORLD_SURFACE, n4, n5); ++ mutableBlockPos.set(n4, n6, n5); ++ if (level.isVillage(mutableBlockPos) && n < 2) continue; ++ if (!level.hasChunksAt(mutableBlockPos.getX() - 10, mutableBlockPos.getZ() - 10, mutableBlockPos.getX() + 10, mutableBlockPos.getZ() + 10) || !level.isPositionEntityTicking(mutableBlockPos) || !spawnPlacementType.isSpawnPositionOk(level, mutableBlockPos, EntityType.RAVAGER) && (!level.getBlockState((BlockPos)mutableBlockPos.below()).is(Blocks.SNOW) || !level.getBlockState(mutableBlockPos).isAir())) continue; ++ return mutableBlockPos; ++ } ++ return null; ++ } ++ ++ private Optional getValidSpawnPos(ServerLevel level, int n) { ++ for (int i = 0; i < 3; ++i) { ++ BlockPos blockPos = this.findRandomSpawnPos(level, n, 1); ++ if (blockPos == null) continue; ++ return Optional.of(blockPos); ++ } ++ return Optional.empty(); ++ } ++ // Leaves End - old FindSpawnPosition ++ ++ + private boolean addWaveMob(ServerLevel level, int wave, Raider raider) { + // Folia start - make raids thread-safe + if (!this.ownsRaid(level)) { +@@ -864,4 +907,43 @@ public class Raid { + this.spawnsPerWaveBeforeBonus = spawnsPerWaveBeforeBonus; + } + } ++ ++ // Leaves start - old FindSpawnPosition ++ private BlockPos getRavagerSpawnLocation(ServerLevel serverWorld, int proximity, int tries) { ++ int i = proximity == 0 ? 2 : 2 - proximity; ++ BlockPos.MutableBlockPos mutable = new BlockPos.MutableBlockPos(); ++ SpawnPlacementType spawnLocation = SpawnPlacements.getPlacementType(EntityType.RAVAGER); ++ ++ for (int j = 0; j < tries; j++) { ++ float f = serverWorld.random.nextFloat() * (float) (Math.PI * 2); ++ int k = this.center.getX() + Mth.floor(Mth.cos(f) * 32.0F * (float) i + serverWorld.random.nextInt(5)); ++ int l = this.center.getZ() + Mth.floor(Mth.sin(f) * 32.0F * (float) i + serverWorld.random.nextInt(5)); ++ int m = serverWorld.getHeight(Heightmap.Types.WORLD_SURFACE, k, l); ++ mutable.set(k, m, l); ++ ++ if (serverWorld.isVillage(mutable) && proximity < 2) { ++ continue; ++ } ++ if (serverWorld.hasChunksAt(mutable.getX() - 10, mutable.getZ() - 10, mutable.getX() + 10, mutable.getZ() + 10) ++ && serverWorld.isPositionEntityTicking(mutable) ++ && (spawnLocation.isSpawnPositionOk(serverWorld, mutable, EntityType.RAVAGER) ++ || serverWorld.getBlockState(mutable.below()).is(Blocks.SNOW) ++ && serverWorld.getBlockState(mutable).isAir()) ++ ) { ++ return mutable; ++ } ++ } ++ return null; ++ } ++ ++ private Optional preCalcRavagerSpawnLocation(ServerLevel serverWorld, int proximity) { ++ for (int i = 0; i < 3; i++) { ++ BlockPos blockPos = this.getRavagerSpawnLocation(serverWorld, proximity, 1); ++ if (blockPos != null) { ++ return Optional.of(blockPos); ++ } ++ } ++ return Optional.empty(); ++ } ++ // Leaves end - old FindSpawnPosition + } +diff --git a/net/minecraft/world/entity/raid/Raider.java b/net/minecraft/world/entity/raid/Raider.java +index 3e30989c99cd11c8c94d6fd194d2969e3a7efda6..b1f1bcf7f5ac002550077d5e1d4efd5c67d18063 100644 +--- a/net/minecraft/world/entity/raid/Raider.java ++++ b/net/minecraft/world/entity/raid/Raider.java +@@ -128,6 +128,43 @@ public abstract class Raider extends PatrollingMonster { + + currentRaid.removeFromRaid(serverLevel, this, false); + } ++ ++ // Leaves start - Revert raid changes ++ if (this.level() instanceof ServerLevel) { ++ if (fun.bm.lophine.config.modules.misc.OldFeatureConfig.oldRaidBehavior && !this.hasRaid()) { ++ ItemStack itemstack = this.getItemBySlot(EquipmentSlot.HEAD); ++ net.minecraft.world.entity.player.Player entityhuman = null; ++ if (entity instanceof net.minecraft.world.entity.player.Player player) { ++ entityhuman = player; ++ } else if (entity instanceof net.minecraft.world.entity.animal.wolf.Wolf wolf) { ++ LivingEntity entityliving = wolf.getOwner(); ++ if (wolf.isTame() && entityliving instanceof net.minecraft.world.entity.player.Player player) { ++ entityhuman = player; ++ } ++ } ++ ++ if (entityhuman != null && !itemstack.isEmpty() && this.isCaptain()) { ++ net.minecraft.world.effect.MobEffectInstance mobeffect = entityhuman.getEffect(net.minecraft.world.effect.MobEffects.BAD_OMEN); ++ int i = 1; ++ ++ if (mobeffect != null) { ++ i += mobeffect.getAmplifier(); ++ entityhuman.removeEffectNoUpdate(net.minecraft.world.effect.MobEffects.BAD_OMEN); ++ } else { ++ --i; ++ } ++ ++ i = net.minecraft.util.Mth.clamp(i, 0, 4); ++ net.minecraft.world.effect.MobEffectInstance mobeffect1 = new net.minecraft.world.effect.MobEffectInstance(net.minecraft.world.effect.MobEffects.BAD_OMEN, 120000, i, false, false, true); ++ ++ if (!serverLevel.getGameRules().getBoolean(net.minecraft.world.level.GameRules.RULE_DISABLE_RAIDS)) { ++ entityhuman.addEffect(mobeffect1, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.PATROL_CAPTAIN); // CraftBukkit ++ } ++ this.setPatrolLeader(false); ++ } ++ } ++ } ++ // Leaves end - Revert raid changes + } + + super.die(cause); diff --git a/lophine-server/minecraft-patches/features/0014-Purpur-Barrels-and-enderchests-6-rows.patch b/lophine-server/minecraft-patches/features/0015-Purpur-Barrels-and-enderchests-6-rows.patch similarity index 100% rename from lophine-server/minecraft-patches/features/0014-Purpur-Barrels-and-enderchests-6-rows.patch rename to lophine-server/minecraft-patches/features/0015-Purpur-Barrels-and-enderchests-6-rows.patch diff --git a/lophine-server/minecraft-patches/features/0015-Spawn-invulnerable-time.patch b/lophine-server/minecraft-patches/features/0016-Spawn-invulnerable-time.patch similarity index 100% rename from lophine-server/minecraft-patches/features/0015-Spawn-invulnerable-time.patch rename to lophine-server/minecraft-patches/features/0016-Spawn-invulnerable-time.patch diff --git a/lophine-server/minecraft-patches/features/0018-I18n-support.patch b/lophine-server/minecraft-patches/features/0018-I18n-support.patch index d04357d..070dac5 100644 --- a/lophine-server/minecraft-patches/features/0018-I18n-support.patch +++ b/lophine-server/minecraft-patches/features/0018-I18n-support.patch @@ -4,6 +4,32 @@ Date: Thu, 12 Jun 2025 04:44:24 +0800 Subject: [PATCH] I18n support +diff --git a/net/minecraft/locale/Language.java b/net/minecraft/locale/Language.java +index 7b9e2a1a208b46a69c16e6afd8b502259893574f..db46a31ea8a36562e548eb07f0c4cf9b9e8c3360 100644 +--- a/net/minecraft/locale/Language.java ++++ b/net/minecraft/locale/Language.java +@@ -65,7 +65,7 @@ public abstract class Language { + }; + } + +- private static void parseTranslations(BiConsumer output, String languagePath) { ++ public static void parseTranslations(BiConsumer output, String languagePath) { // Lophine - private -> public + try (InputStream resourceAsStream = Language.class.getResourceAsStream(languagePath)) { + loadFromJson(resourceAsStream, output); + } catch (JsonParseException | IOException var7) { +diff --git a/net/minecraft/server/Main.java b/net/minecraft/server/Main.java +index cf72cb8b418239b56c755486b03a321672e16bc3..b69c06607ca900beeebf4892a978442dad2db19b 100644 +--- a/net/minecraft/server/Main.java ++++ b/net/minecraft/server/Main.java +@@ -155,6 +155,8 @@ public class Main { + return; + } + ++ fun.bm.lophine.utils.ServerI18nUtil.preInit(); // Lophine - I18n support ++ + // Paper start - Detect headless JRE + String awtException = io.papermc.paper.util.ServerEnvironment.awtDependencyCheck(); + if (awtException != null) { diff --git a/net/minecraft/server/dedicated/DedicatedServer.java b/net/minecraft/server/dedicated/DedicatedServer.java index c6c6432c2efeb7b95c2894b188cd966321ae3186..1cd9af71ee66df5f277748c0a5a59a30bd57accc 100644 --- a/net/minecraft/server/dedicated/DedicatedServer.java diff --git a/lophine-server/minecraft-patches/features/0020-Compatibility-fix-for-Raid-Revert-and-Cross-Region-D.patch b/lophine-server/minecraft-patches/features/0020-Compatibility-fix-for-Raid-Revert-and-Cross-Region-D.patch new file mode 100644 index 0000000..dadbe04 --- /dev/null +++ b/lophine-server/minecraft-patches/features/0020-Compatibility-fix-for-Raid-Revert-and-Cross-Region-D.patch @@ -0,0 +1,54 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Helvetica Volubi +Date: Fri, 15 Aug 2025 02:29:30 +0800 +Subject: [PATCH] Compatibility fix for Raid Revert and Cross Region Damage + Trace + + +diff --git a/net/minecraft/world/entity/LivingEntity.java b/net/minecraft/world/entity/LivingEntity.java +index 3f4b1c54f9207ba1854a8c8da965cbaca5ea517d..a8348a073682eec31990e9ab963b11ad32cf8bf8 100644 +--- a/net/minecraft/world/entity/LivingEntity.java ++++ b/net/minecraft/world/entity/LivingEntity.java +@@ -1216,6 +1216,29 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin + } + } + ++ // Lophine Start - raid revert adapt Cross Region Damage trace ++ public boolean addEffect(MobEffectInstance effectInstance, EntityPotionEffectEvent.Cause cause, boolean fireEvent, boolean async) { ++ if (!async || ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(this)) { ++ return addEffect(effectInstance, this, cause, fireEvent); ++ } else if (fun.bm.lophine.config.modules.experiment.EntityDamageSourceTraceConfig.enabled) { ++ postToAddEffect(effectInstance, this, cause, fireEvent); ++ return true; ++ } ++ return false; ++ } ++ ++ private void postToAddEffect(MobEffectInstance effectInstance, @Nullable Entity entity, EntityPotionEffectEvent.Cause cause, boolean fireEvent) { ++ if (entity != null) ++ entity.getBukkitEntity().taskScheduler.schedule((Entity nmsEntity) -> { ++ try { ++ addEffect(effectInstance, nmsEntity, cause, fireEvent); ++ } catch (Throwable ex) { ++ LOGGER.error(ex.getMessage(), ex); ++ } ++ }, null, 1L ); ++ } ++ // Lophine End - raid revert adapt Cross Region Damage trace ++ + public boolean canBeAffected(MobEffectInstance effectInstance) { + if (this.getType().is(EntityTypeTags.IMMUNE_TO_INFESTED)) { + return !effectInstance.is(MobEffects.INFESTED); +diff --git a/net/minecraft/world/entity/raid/Raider.java b/net/minecraft/world/entity/raid/Raider.java +index b1f1bcf7f5ac002550077d5e1d4efd5c67d18063..6120f7b970ff05867524ee9f1433d46843d57483 100644 +--- a/net/minecraft/world/entity/raid/Raider.java ++++ b/net/minecraft/world/entity/raid/Raider.java +@@ -158,7 +158,7 @@ public abstract class Raider extends PatrollingMonster { + net.minecraft.world.effect.MobEffectInstance mobeffect1 = new net.minecraft.world.effect.MobEffectInstance(net.minecraft.world.effect.MobEffects.BAD_OMEN, 120000, i, false, false, true); + + if (!serverLevel.getGameRules().getBoolean(net.minecraft.world.level.GameRules.RULE_DISABLE_RAIDS)) { +- entityhuman.addEffect(mobeffect1, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.PATROL_CAPTAIN); // CraftBukkit ++ entityhuman.addEffect(mobeffect1, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.PATROL_CAPTAIN, true, fun.bm.lophine.config.modules.experiment.EntityDamageSourceTraceConfig.enabled); // Lophine - raid revert adapt Cross Region Damage trace + } + this.setPatrolLeader(false); + } diff --git a/lophine-server/src/main/java/fun/bm/lophine/config/modules/misc/OldFeatureConfig.java b/lophine-server/src/main/java/fun/bm/lophine/config/modules/misc/OldFeatureConfig.java index 6b1e8c7..a0bddbd 100644 --- a/lophine-server/src/main/java/fun/bm/lophine/config/modules/misc/OldFeatureConfig.java +++ b/lophine-server/src/main/java/fun/bm/lophine/config/modules/misc/OldFeatureConfig.java @@ -3,6 +3,7 @@ package fun.bm.lophine.config.modules.misc; import me.earthme.luminol.config.EnumConfigCategory; import me.earthme.luminol.config.IConfigModule; import me.earthme.luminol.config.flags.ConfigInfo; +import me.earthme.luminol.config.flags.TransformedConfig; public class OldFeatureConfig implements IConfigModule { @ConfigInfo(baseName = "spawn_invulnerable_time") @@ -14,6 +15,10 @@ public class OldFeatureConfig implements IConfigModule { @ConfigInfo(baseName = "old_explosion_damage_calculator") public static boolean oldExplosionDamageCalculator = false; + @TransformedConfig(name = "give_bad_omen_when_kill_raid_captain", category = {"misc", "revert_raid_changes"}, transformComments = false) + @ConfigInfo(baseName = "old_raid_behavior") + public static boolean oldRaidBehavior = false; + @Override public EnumConfigCategory getCategory() { return EnumConfigCategory.MISC; diff --git a/lophine-server/src/main/java/fun/bm/lophine/config/modules/misc/RaidChangesConfig.java b/lophine-server/src/main/java/fun/bm/lophine/config/modules/misc/RaidChangesConfig.java deleted file mode 100644 index e793d37..0000000 --- a/lophine-server/src/main/java/fun/bm/lophine/config/modules/misc/RaidChangesConfig.java +++ /dev/null @@ -1,54 +0,0 @@ -package fun.bm.lophine.config.modules.misc; - -import me.earthme.luminol.config.EnumConfigCategory; -import me.earthme.luminol.config.IConfigModule; -import me.earthme.luminol.config.flags.ConfigInfo; - -public class RaidChangesConfig implements IConfigModule { - @ConfigInfo(baseName = "allow_skip_cooldown", comments = - """ - Allow players with bad omen to skip a - 30-second cooldown and trigger attacks directly""") - public static boolean trigger = false; - - @ConfigInfo(baseName = "give_bad_omen_when_kill_raid_captain", comments = - """ - Enable players to obtain a bad omen - effect when killing the raid captain""") - public static boolean effect = false; - - @ConfigInfo(baseName = "bad_omen_infinite", comments = - """ - Enable bad omen effect infinite time - --- this config is not old version's function""") - public static boolean infinite = false; - - @ConfigInfo(baseName = "skip_height_check", comments = - """ - Disable y <= 96 check. - If you enabled use_old_position_find, this config - will useless and always behavior of enabled""") - public static boolean heightCheck = false; - - @ConfigInfo(baseName = "skip_self_raid_check", comments = - """ - Disable raid self check - --- this config is not old version's function""") - public static boolean selfCheck = false; - - @ConfigInfo(baseName = "use_old_position_find", comments = - """ - Revert Old raid's find spawn position logic - --- This revert MC-274911""") - public static boolean posRevert = false; - - @Override - public EnumConfigCategory getCategory() { - return EnumConfigCategory.MISC; - } - - @Override - public String getBaseName() { - return "revert_raid_changes"; - } -} \ No newline at end of file diff --git a/lophine-server/src/main/java/fun/bm/lophine/config/modules/misc/RedStoneConfig.java b/lophine-server/src/main/java/fun/bm/lophine/config/modules/misc/RedStoneConfig.java index f429d26..3a200fa 100644 --- a/lophine-server/src/main/java/fun/bm/lophine/config/modules/misc/RedStoneConfig.java +++ b/lophine-server/src/main/java/fun/bm/lophine/config/modules/misc/RedStoneConfig.java @@ -12,9 +12,6 @@ public class RedStoneConfig implements IConfigModule { Allows you to use the Shears to right-click to rotate the block.""") public static boolean shears = false; - @ConfigInfo(baseName = "vanilla_hopper") - public static boolean vanillaHopper = false; - @Override public EnumConfigCategory getCategory() { return EnumConfigCategory.MISC; diff --git a/lophine-server/src/main/java/fun/bm/lophine/config/modules/removed/RemovedConfig.java b/lophine-server/src/main/java/fun/bm/lophine/config/modules/removed/RemovedConfig.java index 3bdad8c..a668e6f 100644 --- a/lophine-server/src/main/java/fun/bm/lophine/config/modules/removed/RemovedConfig.java +++ b/lophine-server/src/main/java/fun/bm/lophine/config/modules/removed/RemovedConfig.java @@ -6,6 +6,12 @@ import me.earthme.luminol.config.flags.ConfigInfo; import me.earthme.luminol.config.flags.TransformedConfig; public class RemovedConfig implements IConfigModule { + @TransformedConfig(name = "allow_skip_cooldown", category = {"misc", "revert_raid_changes"}, transform = false) + @TransformedConfig(name = "bad_omen_infinite", category = {"misc", "revert_raid_changes"}, transform = false) + @TransformedConfig(name = "skip_height_check", category = {"misc", "revert_raid_changes"}, transform = false) + @TransformedConfig(name = "skip_self_raid_check", category = {"misc", "revert_raid_changes"}, transform = false) + @TransformedConfig(name = "use_old_position_find", category = {"misc", "revert_raid_changes"}, transform = false) + @TransformedConfig(name = "vanilla_hopper", category = {"misc", "redstone"}, transform = false) @TransformedConfig(name = "old_replaceable_by_mushrooms", category = {"misc", "old-feature"}, transform = false) @TransformedConfig(name = "old_nether_portal_collision", category = {"misc", "old-feature"}, transform = false) @TransformedConfig(name = "better_shulker_box", category = {"misc", "container_expansion"}, transform = false) diff --git a/lophine-server/src/main/java/fun/bm/lophine/utils/ServerI18nUtil.java b/lophine-server/src/main/java/fun/bm/lophine/utils/ServerI18nUtil.java index 079a45a..746968d 100644 --- a/lophine-server/src/main/java/fun/bm/lophine/utils/ServerI18nUtil.java +++ b/lophine-server/src/main/java/fun/bm/lophine/utils/ServerI18nUtil.java @@ -4,7 +4,10 @@ import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParser; import com.google.gson.JsonSyntaxException; +import com.mojang.logging.LogUtils; import fun.bm.lophine.config.modules.optimizations.LanguageConfig; +import me.earthme.luminol.config.ConfigManager; +import me.earthme.luminol.config.ConfigsInstance; import net.minecraft.DetectedVersion; import net.minecraft.locale.DeprecatedTranslationsInfo; import net.minecraft.locale.Language; @@ -12,8 +15,8 @@ import net.minecraft.network.chat.FormattedText; import net.minecraft.network.chat.Style; import net.minecraft.util.FormattedCharSequence; import net.minecraft.util.StringDecomposer; -import org.apache.commons.io.FileUtils; import org.jetbrains.annotations.NotNull; +import org.slf4j.Logger; import java.io.IOException; import java.io.InputStream; @@ -23,13 +26,13 @@ import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.nio.file.*; +import java.time.Duration; import java.util.HashMap; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.function.BiConsumer; -import java.util.logging.Logger; /* * authored by: Helvetica Volubi @@ -37,30 +40,62 @@ import java.util.logging.Logger; * Some of diff form Leaves */ public class ServerI18nUtil { - - private static final Logger logger = Logger.getLogger("LangLoader"); + private static final Logger logger = LogUtils.getClassLogger(); private static final String VERSION = DetectedVersion.BUILT_IN.name(); private static final String BASE_PATH = "cache/lophine/" + VERSION + "/"; + private static final String defaultLophineLangPath = "/assets/lophine/lang/en_us.json"; private static final String manifestUrl = "https://launchermeta.mojang.com/mc/game/version_manifest.json"; private static final String resourceBaseUrl = "https://resources.download.minecraft.net/"; - + // pre-load + private static CompletableFuture preloadTask; + // paths private static String langPath; private static String assetsPath; private static String versionPath; private static String manifestPath; private static String langJsonPath; + private static String lophineLangPath; public static void init() { if (Objects.equals(LanguageConfig.lang, "en_us")) { return; } langPath = BASE_PATH + "lang/" + LanguageConfig.lang + ".json"; + langJsonPath = "minecraft/lang/" + LanguageConfig.lang + ".json"; + lophineLangPath = "/assets/Lophine/lang/" + LanguageConfig.lang + ".json"; + logger.info("Starting load language: {}", LanguageConfig.lang); + preloadTask.thenAcceptAsync(v -> loadI18n(LanguageConfig.lang, 2)); + } + + public static void preInit() { assetsPath = BASE_PATH + "assets.json"; versionPath = BASE_PATH + VERSION + ".json"; manifestPath = BASE_PATH + "manifest.json"; - langJsonPath = "minecraft/lang/" + LanguageConfig.lang + ".json"; - logger.info("Starting load language: " + LanguageConfig.lang); - CompletableFuture.runAsync(() -> loadI18n(LanguageConfig.lang, 2)); + preloadTask = CompletableFuture.runAsync(() -> preLoadI18n(2)); + } + + private static void preLoadI18n(int retryTime) { + try { + if (!Files.exists(Path.of(assetsPath))) { + downloadAssets(true); + } + } catch (UnsupportedLanguageException e) { + logger.warn("Unsupported language: {}", LanguageConfig.lang); + // Fallback to English + final ConfigsInstance configsInstance = ConfigManager.configfiles.get("lophine"); + configsInstance.setConfig(new String[]{"optimizations", "lang"}, "en_us"); + configsInstance.reloadAsync(); + } catch (Exception e) { + if (e instanceof MalformedJsonException malformedJson) { + malformedJson.clean(); + } + logger.warn("Failed to download language list file: ", e); + if (retryTime > 0) { + preLoadI18n(retryTime - 1); + } else { + logger.error("Failed to download language list file for many times, skip pre-load language."); + } + } } private static void loadI18n(String lang, int retryTime) { @@ -69,15 +104,16 @@ public class ServerI18nUtil { downloadLang(true); } Language.inject(createLangInstance()); - logger.info("Successfully loaded language: " + lang); + logger.info("Successfully loaded language: {}", lang); } catch (Exception e) { - logger.warning("Failed to load language file for " + lang + "\n" + e); + if (e instanceof MalformedJsonException malformedJson) { + malformedJson.clean(); + } + logger.warn("Failed to load language file for {}", lang, e); if (retryTime > 0) { - cleanCache(); loadI18n(lang, retryTime - 1); } else { - logger.severe("Failed to load for many times, use default lang \"en_us\" instead"); - cleanCache(); + logger.error("Failed to load for many times, use default lang \"en_us\" instead"); } } } @@ -94,6 +130,10 @@ public class ServerI18nUtil { JsonObject langEntry = json.getAsJsonObject("objects").getAsJsonObject(langJsonPath); + if (langEntry == null) { + throw new UnsupportedLanguageException(); + } + String hash = langEntry.get("hash").getAsString(); if (hash == null || hash.length() < 2) { throw new IllegalArgumentException("Invalid hash value"); @@ -145,37 +185,31 @@ public class ServerI18nUtil { fetchAndSave(versionUrl, versionPath); } - private static String createHttpResponse(String path) throws IOException, InterruptedException { + private static byte[] fetch(String urlString) throws IOException, InterruptedException { try { HttpResponse response; try (HttpClient httpClient = HttpClient.newHttpClient()) { - HttpRequest request = HttpRequest.newBuilder() - .uri(URI.create(path)) + .uri(URI.create(urlString)) + .timeout(Duration.ofSeconds(10)) .build(); - response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); } int responseCode = response.statusCode(); if (responseCode != 200) { - logger.info("Unexpected response code: " + responseCode); - logger.info("Response body: " + response.body()); + logger.info("Unexpected response code: {}", responseCode); + logger.info("Response body: {}", response.body()); throw new UnsupportedEncodingException("Unexpected response code"); } else { - return response.body(); + return response.body().getBytes(); } } catch (Exception e) { - logger.warning("Error in getting info: " + e.getMessage()); + logger.warn("Error in getting info!"); throw e; } } - private static byte[] fetch(String urlString) throws IOException, InterruptedException { - String ret = createHttpResponse(urlString); - return ret.getBytes(); - } - private static void fetchAndSave(String url, String savePath) throws IOException, InterruptedException { byte[] data = fetch(url); Path outputPath = Path.of(savePath); @@ -183,23 +217,15 @@ public class ServerI18nUtil { Files.write(outputPath, data, StandardOpenOption.CREATE, StandardOpenOption.WRITE); } - private static void cleanCache() { - try { - FileUtils.deleteDirectory(Path.of(BASE_PATH).toFile()); - } catch (IOException e) { - logger.severe("Cache cleanup failed: " + e); - } - } - private static JsonObject loadJson(String path) { try { byte[] data = Files.readAllBytes(Paths.get(path)); return JsonParser.parseString(new String(data)).getAsJsonObject(); } catch (JsonSyntaxException e) { - logger.warning("Corrupt json file: " + e); - throw e; + logger.warn("Corrupt json file!"); + throw new MalformedJsonException(e, path); } catch (Exception e) { - logger.warning("Failed to load local JSON: " + e); + logger.warn("Failed to load local JSON!"); return null; } } @@ -208,6 +234,7 @@ public class ServerI18nUtil { DeprecatedTranslationsInfo deprecatedTranslationsInfo = DeprecatedTranslationsInfo.loadFromDefaultResource(); Map map = new HashMap<>(); parseTranslations(map::put); + loadLophineI18n(map::put); deprecatedTranslationsInfo.applyToMap(map); final Map map1 = Map.copyOf(map); return new Language() { @@ -237,16 +264,52 @@ public class ServerI18nUtil { }; } + private static void loadLophineI18n(BiConsumer bi) { + if (Language.class.getResource(lophineLangPath) != null) { + Language.parseTranslations(bi, lophineLangPath); + } else { + loadLophineI18nDefault(bi); + } + } + + public static void loadLophineI18nDefault(BiConsumer bi) { + if (Language.class.getResource(defaultLophineLangPath) != null) { + Language.parseTranslations(bi, defaultLophineLangPath); + } + } + private static void parseTranslations(BiConsumer output) throws IOException { Path filePath = Path.of(langPath); try (InputStream fileStream = Files.newInputStream(filePath)) { Language.loadFromJson(fileStream, output); - } catch (NoSuchFileException noSuchFileException) { - logger.warning("Couldn't find language file: " + langPath); - throw noSuchFileException; + } catch (NoSuchFileException e) { + logger.warn("Couldn't find language file: {}", langPath); + throw e; + } catch (JsonSyntaxException e) { + throw new MalformedJsonException(e, langPath); } catch (Exception e) { - logger.warning("Failed to load language from filesystem " + filePath + "\n" + e); + logger.warn("Failed to load language from filesystem {}", filePath); throw e; } } + + private static class UnsupportedLanguageException extends Exception { + } + + private static class MalformedJsonException extends RuntimeException { + private final Path path; + + protected MalformedJsonException(Throwable root, String path) { + super(root); + this.path = Path.of(path); + } + + public void clean() { + try { + Files.delete(path); + } catch (IOException e) { + logger.info("Failed to delete malformed JSON file: {}", path); + } + } + } } \ No newline at end of file diff --git a/lophine-server/src/main/java/org/leavesmc/leaves/util/ShearsWrenchUtil.java b/lophine-server/src/main/java/org/leavesmc/leaves/util/ShearsWrenchUtil.java new file mode 100644 index 0000000..dfa5059 --- /dev/null +++ b/lophine-server/src/main/java/org/leavesmc/leaves/util/ShearsWrenchUtil.java @@ -0,0 +1,141 @@ +/* + * This file is part of Leaves (https://github.com/LeavesMC/Leaves) + * + * Leaves is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Leaves is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Leaves. If not, see . + */ + +package org.leavesmc.leaves.util; + +import fun.bm.lophine.config.modules.misc.RedStoneConfig; +import net.minecraft.Util; +import net.minecraft.core.BlockPos; +import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.Items; +import net.minecraft.world.item.context.BlockPlaceContext; +import net.minecraft.world.item.context.UseOnContext; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.*; +import net.minecraft.world.level.block.piston.PistonBaseBlock; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.StateDefinition; +import net.minecraft.world.level.block.state.properties.Property; +import net.minecraft.world.level.block.state.properties.RailShape; + +import java.util.List; + +public class ShearsWrenchUtil { + + public static InteractionResult tryApplyRotate(UseOnContext context, BlockState blockState) { + Level level = context.getLevel(); + BlockPos clickedPos = context.getClickedPos(); + Block block = blockState.getBlock(); + if (!RedStoneConfig.shears || !(block instanceof ObserverBlock || block instanceof DispenserBlock || block instanceof PistonBaseBlock || block instanceof HopperBlock || block instanceof RepeaterBlock || block instanceof ComparatorBlock || block instanceof CrafterBlock || block instanceof LeverBlock || block instanceof CocoaBlock || block instanceof TrapDoorBlock || block instanceof FenceGateBlock || block instanceof LightningRodBlock || block instanceof CalibratedSculkSensorBlock || block instanceof BaseRailBlock)) { + return null; + } + StateDefinition blockstatelist = block.getStateDefinition(); + Property iblockstate; + if (block instanceof CrafterBlock) { + iblockstate = blockstatelist.getProperty("orientation"); + } else if (block instanceof BaseRailBlock) { + iblockstate = blockstatelist.getProperty("shape"); + } else { + iblockstate = blockstatelist.getProperty("facing"); + } + Player player = context.getPlayer(); + + if (iblockstate == null || player == null) { + return InteractionResult.FAIL; + } + + if (block instanceof BaseRailBlock) { + if (block instanceof RailBlock) { + if (blockState.getValue(RailBlock.SHAPE).isSlope()) { + return InteractionResult.FAIL; + } + } else { + if (getNameHelper(blockState, PoweredRailBlock.POWERED).equals("true")) { + return InteractionResult.FAIL; + } + if (blockState.getValue(PoweredRailBlock.SHAPE).isSlope()) { + return InteractionResult.FAIL; + } + } + } + + if (block instanceof PistonBaseBlock) { + if (getNameHelper(blockState, PistonBaseBlock.EXTENDED).equals("true")) { + return InteractionResult.FAIL; + } + } + + if (block instanceof RepeaterBlock || block instanceof ComparatorBlock) { + if (getNameHelper(blockState, ComparatorBlock.POWERED).equals("true")) { + return InteractionResult.FAIL; + } + if (block instanceof RepeaterBlock) { + if (getNameHelper(blockState, RepeaterBlock.LOCKED).equals("true")) { + return InteractionResult.FAIL; + } + } + } + + if (block instanceof CrafterBlock) { + if (getNameHelper(blockState, CrafterBlock.CRAFTING).equals("true")) { + return InteractionResult.FAIL; + } + } + + BlockState iblockdata1 = cycleState(blockState, iblockstate, player.isSecondaryUseActive()); + level.setBlock(clickedPos, iblockdata1, 18); + message(player, Component.translatable("item.minecraft.debug_stick.update", iblockstate.getName(), getNameHelper(iblockdata1, iblockstate))); + return InteractionResult.CONSUME; + } + + public static boolean shouldSkipPlace(BlockPlaceContext context) { + return RedStoneConfig.shears && + context.getPlayer() != null && + context.getPlayer().getItemInHand(InteractionHand.MAIN_HAND).is(Items.SHEARS); + } + + private static > BlockState cycleState(BlockState state, Property property, boolean inverse) { + List possibleValues = property.getPossibleValues(); + if (possibleValues.getFirst() instanceof RailShape) { + boolean isRailBlock = state.getBlock() instanceof RailBlock; + possibleValues = possibleValues.stream().filter(possibleValue -> { + RailShape shape = (RailShape) possibleValue; + if (isRailBlock) { + return !shape.isSlope(); + } + return !shape.isSlope() && shape != RailShape.NORTH_EAST && shape != RailShape.NORTH_WEST && shape != RailShape.SOUTH_EAST && shape != RailShape.SOUTH_WEST; + }).toList(); + } + return state.setValue(property, getRelative(possibleValues, state.getValue(property), inverse)); // CraftBukkit - decompile error + } + + private static T getRelative(Iterable elements, T current, boolean inverse) { + return inverse ? Util.findPreviousInIterable(elements, current) : Util.findNextInIterable(elements, current); + } + + private static void message(Player player, Component message) { + ((ServerPlayer) player).sendSystemMessage(message, true); + } + + private static > String getNameHelper(BlockState state, Property property) { + return property.getName(state.getValue(property)); + } +} \ No newline at end of file