Skip to content

Commit

Permalink
[ci skip] feat: Rewrite Nether Portal find logic(WIP)
Browse files Browse the repository at this point in the history
  • Loading branch information
MC-XiaoHei committed Aug 17, 2024
1 parent 7e3c7b9 commit f89aaec
Showing 1 changed file with 195 additions and 39 deletions.
234 changes: 195 additions & 39 deletions patches/server/0058-Rewrite-Nether-Portal-find-logic.patch
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,18 @@ Subject: [PATCH] Rewrite Nether Portal find logic


diff --git a/src/main/java/io/papermc/paper/util/PoiAccess.java b/src/main/java/io/papermc/paper/util/PoiAccess.java
index 69be1761b3b5ba7b496c1c10a4db897e6212d671..b65ea7f1ffb16b750dc4a33334b4dc71d8452d4c 100644
index 69be1761b3b5ba7b496c1c10a4db897e6212d671..8a2000e2e27cd539f728238bf96e88894cd4d230 100644
--- a/src/main/java/io/papermc/paper/util/PoiAccess.java
+++ b/src/main/java/io/papermc/paper/util/PoiAccess.java
@@ -181,7 +181,7 @@ public final class PoiAccess {

// only includes x/z axis
// finds the closest poi data by distance. if multiple match the same distance, then they all are returned.
- public static void findClosestPoiDataRecords(final PoiManager poiStorage,
+ public static void findClosestPoiDataRecords(final net.minecraft.world.level.portal.PortalForcer.IPoiManager poiStorage, // Lumina - rewrite nether portal find logic
final Predicate<Holder<PoiType>> villagePlaceType,
// position predicate must not modify chunk POI
final Predicate<BlockPos> positionPredicate,
@@ -195,7 +195,7 @@ public final class PoiAccess {
findClosestPoiDataRecords(poiStorage, villagePlaceType, predicate, sourcePosition, range, maxDistanceSquared, occupancy, load, ret);
}
Expand All @@ -30,15 +39,29 @@ index 69be1761b3b5ba7b496c1c10a4db897e6212d671..b65ea7f1ffb16b750dc4a33334b4dc71
final int upperZ = Mth.floor(sourcePosition.getZ() + range) >> 4;

final int centerX = sourcePosition.getX() >> 4;
@@ -273,7 +273,7 @@ public final class PoiAccess {
@@ -273,13 +273,19 @@ public final class PoiAccess {
}
}

- final Optional<PoiSection> poiSectionOptional = load ? poiStorage.getOrLoad(key) : poiStorage.get(key);
+ final Optional<PoiSection> poiSectionOptional = load ? poiStorage._getOrLoad(key) : poiStorage._get(key); // Lumina - rewrite nether portal find logic
+ // Lumina start - rewrite nether portal find logic
+ final Optional<? extends net.minecraft.world.level.portal.PortalForcer.IPoiSection> poiSectionOptional = load ? poiStorage._getOrLoad(key) : poiStorage._get(key);

if (poiSectionOptional == null || !poiSectionOptional.isPresent()) {
- if (poiSectionOptional == null || !poiSectionOptional.isPresent()) {
+ if(poiSectionOptional == null) {
+ throw new net.minecraft.world.level.portal.PortalForcer.UncachedSectionException();
+ }
+
+ if (poiSectionOptional.isEmpty()) {
continue;
}

- final PoiSection poiSection = poiSectionOptional.get();
+ final net.minecraft.world.level.portal.PortalForcer.IPoiSection poiSection = poiSectionOptional.get();
+ // Lumina end - rewrite nether portal find logic

final Map<Holder<PoiType>, Set<PoiRecord>> sectionData = poiSection.getData();
if (sectionData.isEmpty()) {
diff --git a/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiManager.java b/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiManager.java
index 16e2e3edc62f9774518b8e783f8eb1ca35d413c8..615b167f461ab9f3346311fd0d06264b7b381a73 100644
--- a/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiManager.java
Expand Down Expand Up @@ -84,27 +107,43 @@ index 16e2e3edc62f9774518b8e783f8eb1ca35d413c8..615b167f461ab9f3346311fd0d06264b
private final Long2ByteMap levels = new Long2ByteOpenHashMap();

diff --git a/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiSection.java b/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiSection.java
index 4ee7d75c56d9f9ff3607276857dde84410ba3f2a..8731cebf72febd84822284914bbf76075e6b4b0b 100644
index 4ee7d75c56d9f9ff3607276857dde84410ba3f2a..76408fe55a8c7d27e7c4f609c059db5da8bfb5f8 100644
--- a/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiSection.java
+++ b/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiSection.java
@@ -78,6 +78,7 @@ public class PoiSection {
private boolean add(PoiRecord poi) {
BlockPos blockPos = poi.getPos();
Holder<PoiType> holder = poi.getPoiType();
+ if (holder.is(PoiTypes.NETHER_PORTAL)) System.out.println("Adding nether portal at " + blockPos); // Lumina - rewrite nether portal logic
short s = SectionPos.sectionRelativePos(blockPos);
PoiRecord poiRecord = this.records.get(s);
if (poiRecord != null) {
@@ -95,6 +96,7 @@ public class PoiSection {

public void remove(BlockPos pos) {
PoiRecord poiRecord = this.records.remove(SectionPos.sectionRelativePos(pos));
+ if (poiRecord.getPoiType().is(PoiTypes.NETHER_PORTAL)) System.out.println("Adding nether portal at " + pos); // Lumina - rewrite nether portal logic
if (poiRecord == null) {
LOGGER.error("POI data mismatch: never registered at {}", pos);
} else {
@@ -23,10 +23,10 @@ import net.minecraft.core.SectionPos;
import net.minecraft.util.VisibleForDebug;
import org.slf4j.Logger;

-public class PoiSection {
+public class PoiSection implements net.minecraft.world.level.portal.PortalForcer.IPoiSection { // Lumina - rewrite nether portal find logic
private static final Logger LOGGER = LogUtils.getLogger();
private final Short2ObjectMap<PoiRecord> records = new Short2ObjectOpenHashMap<>();
- private final Map<Holder<PoiType>, Set<PoiRecord>> byType = Maps.newHashMap(); public final Map<Holder<PoiType>, Set<PoiRecord>> getData() { return this.byType; } // Paper - public accessor
+ private final Map<Holder<PoiType>, Set<PoiRecord>> byType = Maps.newHashMap(); @Override public final Map<Holder<PoiType>, Set<PoiRecord>> getData() { return this.byType; } // Paper - public accessor // Lumina - rewrite nether portal find logic
private final Runnable setDirty;
private boolean isValid;
public final Optional<PoiSection> noAllocateOptional = Optional.of(this); // Paper - rewrite chunk system
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
index 7fb23c02dc91ed3af27eb4420ce920ab22bdb359..3e050da7117788c7aef8838e5f251f72730f2649 100644
--- a/src/main/java/net/minecraft/world/level/Level.java
+++ b/src/main/java/net/minecraft/world/level/Level.java
@@ -1021,6 +1021,14 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
public boolean setBlock(BlockPos pos, BlockState state, int flags, int maxUpdateDepth) {
io.papermc.paper.util.TickThread.ensureTickThread((ServerLevel)this, pos, "Updating block asynchronously"); // Folia - region threading
io.papermc.paper.threadedregions.RegionizedWorldData worldData = this.getCurrentWorldData(); // Folia - region threading
+ // Lumina start - rewrite nether portal find logic
+ BlockState oldType = getBlockState(pos);
+ if(oldType.is(Blocks.NETHER_PORTAL)) {
+ ((ServerLevel)this).getPortalForcer().cacheManager.getOrCreateIfAbsent().remove(pos);
+ } else if (state.is(Blocks.NETHER_PORTAL)) {
+ ((ServerLevel)this).getPortalForcer().cacheManager.getOrCreateIfAbsent().add(pos);
+ }
+ // Lumina end - rewrite nether portal find logic
// CraftBukkit start - tree generation
if (worldData.captureTreeGeneration) { // Folia - region threading
// Paper start - Protect Bedrock and End Portal/Frames from being destroyed
diff --git a/src/main/java/net/minecraft/world/level/portal/PortalForcer.java b/src/main/java/net/minecraft/world/level/portal/PortalForcer.java
index e251935b89799046e82228f49ea7a7737078892b..44fac6e555685887d7389ef0ae3b33e65f314447 100644
index e251935b89799046e82228f49ea7a7737078892b..09565f18cae25c857e5b5e90f6e6ce5d7ff67113 100644
--- a/src/main/java/net/minecraft/world/level/portal/PortalForcer.java
+++ b/src/main/java/net/minecraft/world/level/portal/PortalForcer.java
@@ -11,6 +11,7 @@ import net.minecraft.server.level.TicketType;
Expand All @@ -115,23 +154,61 @@ index e251935b89799046e82228f49ea7a7737078892b..44fac6e555685887d7389ef0ae3b33e6
import net.minecraft.world.entity.ai.village.poi.PoiTypes;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.block.Blocks;
@@ -39,6 +40,7 @@ public class PortalForcer {
@@ -19,6 +20,13 @@ import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.border.WorldBorder;
import net.minecraft.world.level.levelgen.Heightmap;
+// Lumina start - rewrite nether portal find logic
+import net.minecraft.core.Holder;
+import net.minecraft.world.entity.ai.village.poi.*;
+import org.jetbrains.annotations.Nullable;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+// Lumina end - rewrite nether portal find logic

public class PortalForcer {

@@ -39,6 +47,7 @@ public class PortalForcer {

public PortalForcer(ServerLevel world) {
this.level = world;
+ cacheManager = new CacheManager(this.level); // Lumina - rewrite nether portal find logic
}

public Optional<BlockUtil.FoundRectangle> findPortalAround(BlockPos pos, boolean destIsNether, WorldBorder worldBorder) {
@@ -46,7 +48,50 @@ public class PortalForcer {
@@ -46,32 +55,129 @@ public class PortalForcer {
return this.findPortalAround(pos, worldBorder, destIsNether ? level.paperConfig().environment.portalCreateRadius : level.paperConfig().environment.portalSearchRadius); // Search Radius // Paper - Configurable portal search radius
}

- public Optional<BlockUtil.FoundRectangle> findPortalAround(BlockPos blockposition, WorldBorder worldborder, int i) {
- PoiManager villageplace = this.level.getPoiManager();
- // int i = flag ? 16 : 128;
- // CraftBukkit end
+ // Lumina start - rewrite nether portal find logic
+ private static class CacheManager implements IPoiManager {
+ private final java.util.concurrent.ConcurrentHashMap<Long, PoiSection> portalCache = new java.util.concurrent.ConcurrentHashMap<>();
+ public static class CacheManager implements IPoiManager {
+ private final java.util.concurrent.ConcurrentHashMap<Long, IPoiSection> portalCache = new java.util.concurrent.ConcurrentHashMap<>();
+ private final PoiManager poiManager;
+

- // Paper start - optimise portals
- Optional<PoiRecord> optional;
- java.util.List<PoiRecord> records = new java.util.ArrayList<>();
- io.papermc.paper.util.PoiAccess.findClosestPoiDataRecords(
- villageplace,
- type -> type.is(PoiTypes.NETHER_PORTAL),
- (BlockPos pos) -> {
- net.minecraft.world.level.chunk.ChunkAccess lowest = this.level.getChunk(pos.getX() >> 4, pos.getZ() >> 4, net.minecraft.world.level.chunk.status.ChunkStatus.EMPTY);
- if (!lowest.getStatus().isOrAfter(net.minecraft.world.level.chunk.status.ChunkStatus.FULL)
- && (lowest.getBelowZeroRetrogen() == null || !lowest.getBelowZeroRetrogen().targetStatus().isOrAfter(net.minecraft.world.level.chunk.status.ChunkStatus.SPAWN))) {
- // why would we generate the chunk?
- return false;
- }
- if (!worldborder.isWithinBounds(pos) || (this.level.getTypeKey() == net.minecraft.world.level.dimension.LevelStem.NETHER && this.level.paperConfig().environment.netherCeilingVoidDamageHeight.test(v -> pos.getY() >= v))) { // Paper - Configurable nether ceiling damage
- return false;
- }
- return lowest.getBlockState(pos).hasProperty(BlockStateProperties.HORIZONTAL_AXIS);
- },
- blockposition, i, Double.MAX_VALUE, PoiManager.Occupancy.ANY, true, records
- );
+ public CacheManager(ServerLevel level) {
+ this.poiManager = level.getPoiManager();
+ }
Expand All @@ -147,30 +224,109 @@ index e251935b89799046e82228f49ea7a7737078892b..44fac6e555685887d7389ef0ae3b33e6
+ }
+
+ @Override
+ public Optional<PoiSection> _getOrLoad(long pos) {
+ return Optional.ofNullable(portalCache.get(pos));
+ public java.util.Optional<IPoiSection> _getOrLoad(long pos) {
+ IPoiSection res = portalCache.get(pos);
+ return res == null ? null : java.util.Optional.ofNullable(portalCache.get(pos));
+ }
+
+ @Override
+ public Optional<PoiSection> _get(long pos) {
+ public java.util.Optional<PoiSection> _get(long pos) {
+ return this.poiManager._get(pos);
+ }
+
+ public void update(BlockPos pos) {
+ public IPoiSection getOrCreateIfAbsent(long pos) {
+ IPoiSection res = portalCache.get(pos);
+ if (res == null) {
+ res = new NetherPortalBlockConcurrentPoiSection();
+ portalCache.put(pos, res);
+ }
+ return res;
+ }
+ }
+
+ public static class NetherPortalBlockConcurrentPoiSection implements IPoiSection {
+ private final Map<Holder<PoiType>, Set<PoiRecord>> data = new java.util.concurrent.ConcurrentHashMap<>();
+ private final Holder<PoiType> netherPortalType;

+ public NetherPortalBlockConcurrentPoiSection() {
+ this.netherPortalType = PoiTypes.forState(NetherPortalBlock.stateById(0)).get();
+ }
+
+ @Override
+ public Map<Holder<PoiType>, Set<PoiRecord>> getData() {
+ return data;
+ }
+
+ @Override
+ public void add(BlockPos pos) {
+ PoiRecord record = new PoiRecord(pos, netherPortalType, () -> {});
+ Set<PoiRecord> set = data.computeIfAbsent(netherPortalType, (key) -> ConcurrentHashMap.newKeySet());
+ set.add(record);
+ data.put(netherPortalType, set);
+ }
+
+ @Override
+ public void remove(BlockPos pos) {
+ PoiRecord record = new PoiRecord(pos, netherPortalType, () -> {});
+ Set<PoiRecord> set = data.get(netherPortalType);
+ if (set != null && set.contains(record)) {
+ set.remove(record);
+ data.put(netherPortalType, set);
+ }
+ }
+ }
+
+ private CacheManager cacheManager;
+ public final CacheManager cacheManager;
+
+ public interface IPoiManager {
+ int getMinSection();
+ int getMaxSection();
+ Optional<PoiSection> _getOrLoad(long pos);
+ Optional<PoiSection> _get(long pos);
+ @Nullable
+ Optional<? extends IPoiSection> _getOrLoad(long pos);
+ Optional<? extends IPoiSection> _get(long pos);
+ }
+
public Optional<BlockUtil.FoundRectangle> findPortalAround(BlockPos blockposition, WorldBorder worldborder, int i) {
+ // Lumina end - rewrite nether portal find logic
PoiManager villageplace = this.level.getPoiManager();
// int i = flag ? 16 : 128;
// CraftBukkit end
+ public interface IPoiSection {
+ Map<Holder<PoiType>, Set<PoiRecord>> getData();
+ default void add(BlockPos pos) {}
+ default void remove(BlockPos pos) {}
+ }
+
+ public static class UncachedSectionException extends RuntimeException { }
+
+ public Optional<BlockUtil.FoundRectangle> findPortalAround(BlockPos blockposition, WorldBorder worldborder, int i) {
+ java.util.List<PoiRecord> records = new java.util.ArrayList<>();
+ Optional<PoiRecord> optional;
+ try {
+ io.papermc.paper.util.PoiAccess.findClosestPoiDataRecords(
+ cacheManager,
+ type -> type.is(PoiTypes.NETHER_PORTAL),
+ (BlockPos ignored) -> true,
+ blockposition, i, Double.MAX_VALUE, PoiManager.Occupancy.ANY, true, records);
+ } catch (UncachedSectionException ignored) {
+ PoiManager villageplace = this.level.getPoiManager();
+ // int i = flag ? 16 : 128;
+ // CraftBukkit end
+
+ // Paper start - optimise portals
+ io.papermc.paper.util.PoiAccess.findClosestPoiDataRecords(
+ villageplace,
+ type -> type.is(PoiTypes.NETHER_PORTAL),
+ (BlockPos pos) -> {
+ net.minecraft.world.level.chunk.ChunkAccess lowest = this.level.getChunk(pos.getX() >> 4, pos.getZ() >> 4, net.minecraft.world.level.chunk.status.ChunkStatus.EMPTY);
+ if (!lowest.getStatus().isOrAfter(net.minecraft.world.level.chunk.status.ChunkStatus.FULL)
+ && (lowest.getBelowZeroRetrogen() == null || !lowest.getBelowZeroRetrogen().targetStatus().isOrAfter(net.minecraft.world.level.chunk.status.ChunkStatus.SPAWN))) {
+ // why would we generate the chunk?
+ return false;
+ }
+ if (!worldborder.isWithinBounds(pos) || (this.level.getTypeKey() == net.minecraft.world.level.dimension.LevelStem.NETHER && this.level.paperConfig().environment.netherCeilingVoidDamageHeight.test(v -> pos.getY() >= v))) { // Paper - Configurable nether ceiling damage
+ return false;
+ }
+ return lowest.getBlockState(pos).hasProperty(BlockStateProperties.HORIZONTAL_AXIS);
+ },
+ blockposition, i, Double.MAX_VALUE, PoiManager.Occupancy.ANY, true, records
+ );
+ } // Lumina end - rewrite nether portal find logic
// this gets us most of the way there, but we bias towards lower y values.
PoiRecord lowestYRecord = null;
for (PoiRecord record : records) {

0 comments on commit f89aaec

Please sign in to comment.