9
0
mirror of https://github.com/BX-Team/DivineMC.git synced 2025-12-19 14:59:25 +00:00
Files
DivineMC/divinemc-server/minecraft-patches/features/0048-Clump-experience-orbs.patch
2025-10-11 23:20:49 +03:00

215 lines
12 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
Date: Sat, 1 Feb 2025 16:47:09 +0300
Subject: [PATCH] Clump experience orbs
diff --git a/net/minecraft/world/entity/ExperienceOrb.java b/net/minecraft/world/entity/ExperienceOrb.java
index 850da365c388801f2083fd1471cc32ce86297de0..1c02152f1761476b19ac36e367d945c111854c0a 100644
--- a/net/minecraft/world/entity/ExperienceOrb.java
+++ b/net/minecraft/world/entity/ExperienceOrb.java
@@ -50,6 +50,10 @@ public class ExperienceOrb extends Entity {
@Nullable
public java.util.UUID triggerEntityId;
public org.bukkit.entity.ExperienceOrb.SpawnReason spawnReason = org.bukkit.entity.ExperienceOrb.SpawnReason.UNKNOWN;
+ // DivineMC start - Clump experience orbs
+ public java.util.Map<Integer, Integer> clumps$clumpedMap;
+ public Optional<EnchantedItemInUse> clumps$currentEntry;
+ // DivineMC end - Clump experience orbs
private void loadPaperNBT(ValueInput input) {
input.read("Paper.ExpData", net.minecraft.nbt.CompoundTag.CODEC).ifPresent(expData -> {
@@ -272,6 +276,28 @@ public class ExperienceOrb extends Entity {
}
private static boolean tryMergeToExisting(ServerLevel level, Vec3 pos, int amount) {
+ // DivineMC start - Clump experience orbs
+ if (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.clumpOrbs) {
+ AABB aABB = AABB.ofSize(pos, 1.0D, 1.0D, 1.0D);
+ int id = level.getRandom().nextInt(40);
+ List<ExperienceOrb> list = level.getEntities(EntityTypeTest.forClass(ExperienceOrb.class), aABB, (experienceOrbx) -> canMerge(experienceOrbx, id, amount));
+ if (!list.isEmpty()) {
+ ExperienceOrb experienceOrb = list.getFirst();
+ java.util.Map<Integer, Integer> clumpedMap = (experienceOrb).clumps$getClumpedMap();
+ (experienceOrb).clumps$setClumpedMap(java.util.stream.Stream.of(clumpedMap, java.util.Collections.singletonMap(amount, 1))
+ .flatMap(map -> map.entrySet().stream())
+ .collect(java.util.stream.Collectors.toMap(java.util.Map.Entry::getKey, java.util.Map.Entry::getValue, Integer::sum)));
+ (experienceOrb).count = (clumpedMap.values()
+ .stream()
+ .reduce(Integer::sum)
+ .orElse(1));
+ (experienceOrb).age = (0);
+ return true;
+ } else {
+ return false;
+ }
+ }
+ // DivineMC end - Clump experience orbs
// Paper - TODO some other event for this kind of merge
AABB aabb = AABB.ofSize(pos, 1.0, 1.0, 1.0);
int randomInt = level.getRandom().nextInt(io.papermc.paper.configuration.GlobalConfiguration.get().misc.xpOrbGroupsPerArea.or(ORB_GROUPS_PER_AREA)); // Paper - Configure how many orb groups per area
@@ -289,11 +315,11 @@ public class ExperienceOrb extends Entity {
}
private boolean canMerge(ExperienceOrb orb) {
- return orb != this && canMerge(orb, this.getId(), this.getValue());
+ return org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.clumpOrbs ? orb.isAlive() && !this.is(orb) : orb != this && ExperienceOrb.canMerge(orb, this.getId(), this.getValue()); // DivineMC - Clump experience orbs
}
private static boolean canMerge(ExperienceOrb orb, int amount, int other) {
- return !orb.isRemoved() && (orb.getId() - amount) % io.papermc.paper.configuration.GlobalConfiguration.get().misc.xpOrbGroupsPerArea.or(ORB_GROUPS_PER_AREA) == 0 && orb.getValue() == other; // Paper - Configure how many orbs will merge together
+ return org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.clumpOrbs ? orb.isAlive() : !orb.isRemoved() && (orb.getId() - amount) % io.papermc.paper.configuration.GlobalConfiguration.get().misc.xpOrbGroupsPerArea.or(ORB_GROUPS_PER_AREA) == 0 && orb.getValue() == other; // Paper - Configure how many orbs will merge together // Canvas - optimize orbs
}
private void merge(ExperienceOrb orb) {
@@ -302,6 +328,18 @@ public class ExperienceOrb extends Entity {
return;
}
// Paper end - call orb merge event
+ // DivineMC start - Clump experience orbs
+ if (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.clumpOrbs) {
+ java.util.Map<Integer, Integer> otherMap = (orb).clumps$getClumpedMap();
+ this.count = clumps$getClumpedMap().values().stream().reduce(Integer::sum).orElse(1);
+ this.age = Math.min(this.age, (orb).age);
+ clumps$setClumpedMap(java.util.stream.Stream.of(clumps$getClumpedMap(), otherMap)
+ .flatMap(map -> map.entrySet().stream())
+ .collect(java.util.stream.Collectors.toMap(java.util.Map.Entry::getKey, java.util.Map.Entry::getValue, Integer::sum)));
+ orb.discard();
+ return;
+ }
+ // DivineMC end - Clump experience orbs
this.count = this.count + orb.count;
this.age = Math.min(this.age, orb.age);
orb.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.MERGE); // CraftBukkit - add Bukkit remove cause
@@ -343,6 +381,13 @@ public class ExperienceOrb extends Entity {
output.putInt("Value", this.getValue()); // Paper - save as Integer
output.putInt("Count", this.count);
this.savePaperNBT(output); // Paper
+ // DivineMC start - Clump experience orbs
+ if (clumps$clumpedMap != null) {
+ net.minecraft.nbt.CompoundTag map = new net.minecraft.nbt.CompoundTag();
+ clumps$getClumpedMap().forEach((value, count) -> map.putInt(String.valueOf(value), count));
+ output.store("clumpedMap", net.minecraft.nbt.CompoundTag.CODEC, map);
+ }
+ // DivineMC end - Clump experience orbs
}
@Override
@@ -352,10 +397,52 @@ public class ExperienceOrb extends Entity {
this.setValue(input.getIntOr("Value", 0)); // Paper - load as Integer
this.count = input.read("Count", ExtraCodecs.POSITIVE_INT).orElse(1);
this.loadPaperNBT(input); // Paper
+ // DivineMC start - Clump experience orbs
+ java.util.Map<Integer, Integer> map = new java.util.HashMap<>();
+ input.read("clumpedMap", net.minecraft.nbt.CompoundTag.CODEC).ifPresentOrElse(clumpedMap -> {
+ for (String s : clumpedMap.keySet()) {
+ clumpedMap.getInt(s).ifPresent(value -> {
+ map.put(Integer.parseInt(s), value);
+ });
+ }
+ }, () -> map.put(getValue(), count));
+
+ clumps$setClumpedMap(map);
+ // DivineMC end - Clump experience orbs
}
@Override
public void playerTouch(Player entity) {
+ // DivineMC start - Clump experience orbs
+ if (entity instanceof ServerPlayer serverPlayer && org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.clumpOrbs && new com.destroystokyo.paper.event.player.PlayerPickupExperienceEvent(serverPlayer.getBukkitEntity(), (org.bukkit.entity.ExperienceOrb) this.getBukkitEntity()).callEvent()) {
+ entity.takeXpDelay = 0;
+ entity.take(this, 1);
+
+ if (this.getValue() != 0 || clumps$resolve()) {
+ java.util.concurrent.atomic.AtomicInteger toGive = new java.util.concurrent.atomic.AtomicInteger();
+ clumps$getClumpedMap().forEach((value, amount) -> {
+ int actualValue = value;
+ for (int i = 0; i < amount; i++) {
+ int leftOver = actualValue;
+ if (leftOver == actualValue) {
+ leftOver = this.repairPlayerItems((ServerPlayer) entity, actualValue);
+ }
+ if (leftOver > 0) {
+ toGive.addAndGet(leftOver);
+ }
+ }
+ });
+ if (toGive.get() > 0) {
+ entity.giveExperiencePoints(toGive.get());
+ }
+ }
+
+ this.count = 0;
+ this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.PICKUP);
+
+ return;
+ }
+ // Canvas end
if (entity instanceof ServerPlayer serverPlayer) {
if (entity.takeXpDelay == 0 && new com.destroystokyo.paper.event.player.PlayerPickupExperienceEvent(serverPlayer.getBukkitEntity(), (org.bukkit.entity.ExperienceOrb) this.getBukkitEntity()).callEvent()) { // Paper - PlayerPickupExperienceEvent
entity.takeXpDelay = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerXpCooldownEvent(entity, this.level().purpurConfig.playerExpPickupDelay, org.bukkit.event.player.PlayerExpCooldownChangeEvent.ChangeReason.PICKUP_ORB).getNewCooldown(); // CraftBukkit - entity.takeXpDelay = 2; // Purpur - Configurable player pickup exp delay
@@ -373,10 +460,60 @@ public class ExperienceOrb extends Entity {
}
}
+ // DivineMC start - Clump experience orbs
+ public Optional<EnchantedItemInUse> clumps$captureCurrentEntry(Optional<EnchantedItemInUse> entry) {
+ clumps$currentEntry = entry;
+ return entry;
+ }
+
+ public java.util.Map<Integer, Integer> clumps$getClumpedMap() {
+ if (clumps$clumpedMap == null) {
+ clumps$clumpedMap = new java.util.HashMap<>();
+ clumps$clumpedMap.put(this.getValue(), 1);
+ }
+
+ return clumps$clumpedMap;
+ }
+
+ public void clumps$setClumpedMap(java.util.Map<Integer, Integer> map) {
+ clumps$clumpedMap = map;
+ clumps$resolve();
+ }
+
+ public boolean clumps$resolve() {
+ this.setValue(clumps$getClumpedMap().entrySet()
+ .stream()
+ .map(entry -> entry.getKey() * entry.getValue())
+ .reduce(Integer::sum)
+ .orElse(1));
+
+ return this.getValue() > 0;
+ }
+ // DivineMC end - Clump experience orbs
+
private int repairPlayerItems(ServerPlayer player, int value) {
- Optional<EnchantedItemInUse> randomItemWith = level().purpurConfig.useBetterMending ? EnchantmentHelper.getMostDamagedItemWith(EnchantmentEffectComponents.REPAIR_WITH_XP, player) : EnchantmentHelper.getRandomItemWith( // Purpur - Add option to mend the most damaged equipment first
- EnchantmentEffectComponents.REPAIR_WITH_XP, player, ItemStack::isDamaged
- );
+ Optional<EnchantedItemInUse> randomItemWith = clumps$captureCurrentEntry(level().purpurConfig.useBetterMending ? EnchantmentHelper.getMostDamagedItemWith(EnchantmentEffectComponents.REPAIR_WITH_XP, player) : EnchantmentHelper.getRandomItemWith(EnchantmentEffectComponents.REPAIR_WITH_XP, player, ItemStack::isDamaged)); // Purpur - Add option to mend the most damaged equipment first // DivineMC - Clump experience orbs
+
+ // DivineMC start - Clump experience orbs
+ if (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.clumpOrbs) {
+ return clumps$currentEntry
+ .map(foundItem -> {
+ ItemStack itemstack = foundItem.itemStack();
+ int xpToRepair = EnchantmentHelper.modifyDurabilityToRepairFromXp(player.level(), itemstack, (int) (value * 1));
+ int toRepair = Math.min(xpToRepair, itemstack.getDamageValue());
+ itemstack.setDamageValue(itemstack.getDamageValue() - toRepair);
+ if (toRepair > 0) {
+ int used = value - toRepair * value / xpToRepair;
+ if (used > 0) {
+ return this.repairPlayerItems(player, used);
+ }
+ }
+ return 0;
+ })
+ .orElse(value);
+ }
+ // DivineMC end - Clump experience orbs
+
if (randomItemWith.isPresent()) {
ItemStack itemStack = randomItemWith.get().itemStack();
int i = EnchantmentHelper.modifyDurabilityToRepairFromXp(player.level(), itemStack, value);