Skip to content

Commit 2b2d970

Browse files
committed
Fix for #454 via a major rewrite of limbo return teleport logic.
1 parent a53ab1e commit 2b2d970

File tree

5 files changed

+143
-64
lines changed

5 files changed

+143
-64
lines changed

common/src/main/java/org/dimdev/dimdoors/DimensionalDoors.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ public static ServerLevel getWorld(ResourceKey<Level> world) {
105105

106106
private static Supplier<Path> CONFIG_ROOT = () -> dimDoorsMod.getFilePaths().get(0);
107107

108-
private static final ConfigHolder<ModConfig> CONFIG_MANAGER = AutoConfig.register(ModConfig.class, ModConfig.SubRootJanksonConfigSerializer::new);
108+
private static final ConfigHolder<ModConfig> CONFIG_MANAGER = AutoConfig.register(ModConfig.class, ModConfig.SubRootGsonConfigSerializer::new);
109109

110110
public static ModConfig getConfig() {
111111
return CONFIG_MANAGER.get();

common/src/main/java/org/dimdev/dimdoors/ModConfig.java

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package org.dimdev.dimdoors;
22

3+
import com.google.common.reflect.TypeToken;
4+
import com.google.gson.*;
35
import me.shedaniel.autoconfig.ConfigData;
46
import me.shedaniel.autoconfig.annotation.Config;
57
import me.shedaniel.autoconfig.serializer.ConfigSerializer;
@@ -8,6 +10,7 @@
810
import me.shedaniel.clothconfig2.gui.entries.SelectionListEntry;
911
import net.fabricmc.api.EnvType;
1012
import net.fabricmc.api.EnvironmentInterface;
13+
import net.minecraft.core.registries.Registries;
1114
import net.minecraft.resources.ResourceKey;
1215
import net.minecraft.resources.ResourceLocation;
1316
import net.minecraft.world.level.Level;
@@ -16,6 +19,7 @@
1619

1720
import java.io.BufferedWriter;
1821
import java.io.IOException;
22+
import java.lang.reflect.Type;
1923
import java.nio.file.Files;
2024
import java.nio.file.Path;
2125
import java.util.ArrayList;
@@ -174,16 +178,24 @@ public static class Monoliths {
174178
}
175179

176180
public static class Limbo {
177-
178181
@CollapsibleObject
179182
@RequiresRestart
180183
@Tooltip private WorldList worldsLeadingToLimbo = new WorldList();
181184
@Tooltip public boolean hardcoreLimbo = false;
182-
@Tooltip public int limboReturnDistance = 5000;
185+
186+
@Tooltip public int limboReturnDistanceMax = 200;
187+
@Tooltip public int limboReturnDistanceMin = 100;
188+
189+
@Tooltip public boolean decaySurroundings;
190+
191+
@Tooltip public boolean tryPlayerBedSpawn = false;
192+
@Tooltip public boolean defaultToWorldSpawn = true;
193+
194+
183195
@Tooltip public float limboBlocksCorruptingExitWorldAmount = 5;
184196
@Tooltip @Nullable public ResourceKey<Level> escapeTargetWorld = Level.OVERWORLD;
185-
@Tooltip public int escapeTargetWorldYSpawn = 64;
186-
@Tooltip public boolean escapeToWorldSpawn = false;
197+
198+
187199
public boolean shouldUseLimbo(ResourceKey<Level> level) {
188200
return worldsLeadingToLimbo.blacklist != worldsLeadingToLimbo.list.contains(level.location().toString());
189201
}
@@ -241,12 +253,12 @@ public static class Graphics {
241253
// }
242254
// }
243255

244-
public static class SubRootJanksonConfigSerializer<T extends ConfigData> implements ConfigSerializer<T> {
245-
private static final Jankson JANKSON = Jankson.builder().build();
256+
public static class SubRootGsonConfigSerializer<T extends ConfigData> implements ConfigSerializer<T> {
257+
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().registerTypeAdapter(new TypeToken<ResourceKey<Level>>() {}.getType(), new LevelKeyAdapter()).create();
246258
private final Config definition;
247259
private final Class<T> configClass;
248260

249-
public SubRootJanksonConfigSerializer(Config definition, Class<T> configClass) {
261+
public SubRootGsonConfigSerializer(Config definition, Class<T> configClass) {
250262
this.definition = definition;
251263
this.configClass = configClass;
252264
}
@@ -261,7 +273,7 @@ public void serialize(T config) throws SerializationException {
261273
try {
262274
Files.createDirectories(configPath.getParent());
263275
BufferedWriter writer = Files.newBufferedWriter(configPath);
264-
writer.write(JANKSON.toJson(config).toJson(true, true));
276+
writer.write(GSON.toJson(config));
265277
writer.close();
266278
} catch (IOException e) {
267279
throw new SerializationException(e);
@@ -272,8 +284,8 @@ public void serialize(T config) throws SerializationException {
272284
public T deserialize() throws SerializationException {
273285
Path configPath = getConfigPath();
274286
if (Files.exists(configPath)) {
275-
try {
276-
return JANKSON.fromJson(JANKSON.load(getConfigPath().toFile()), configClass);
287+
try (var reader = Files.newBufferedReader(getConfigPath())) {
288+
return GSON.fromJson(reader, configClass);
277289
} catch (Throwable e) {
278290
throw new SerializationException(e);
279291
}
@@ -287,4 +299,17 @@ public T createDefault() {
287299
return Utils.constructUnsafely(configClass);
288300
}
289301
}
290-
}
302+
303+
public static final class LevelKeyAdapter implements JsonSerializer<ResourceKey<Level>>, JsonDeserializer<ResourceKey<Level>> {
304+
305+
@Override
306+
public ResourceKey<Level> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
307+
return ResourceKey.create(Registries.DIMENSION, ResourceLocation.tryParse(json.getAsJsonPrimitive().getAsString()));
308+
}
309+
310+
@Override
311+
public JsonElement serialize(ResourceKey<Level> src, Type typeOfSrc, JsonSerializationContext context) {
312+
return new JsonPrimitive(src.location().toString());
313+
}
314+
}
315+
}

common/src/main/java/org/dimdev/dimdoors/api/util/Location.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22

33
import com.mojang.serialization.Codec;
44
import com.mojang.serialization.codecs.RecordCodecBuilder;
5+
import com.sk89q.worldedit.math.convolution.HeightMap;
56
import net.minecraft.core.BlockPos;
7+
import net.minecraft.core.Direction;
68
import net.minecraft.core.Holder;
79
import net.minecraft.core.registries.Registries;
810
import net.minecraft.nbt.CompoundTag;
@@ -15,6 +17,7 @@
1517
import net.minecraft.world.level.biome.Biome;
1618
import net.minecraft.world.level.block.entity.BlockEntity;
1719
import net.minecraft.world.level.block.state.BlockState;
20+
import net.minecraft.world.level.levelgen.Heightmap;
1821
import net.minecraft.world.level.material.FluidState;
1922
import org.dimdev.dimdoors.DimensionalDoors;
2023

@@ -30,6 +33,10 @@ public Location(ResourceKey<Level> world, BlockPos pos) {
3033
this.pos = pos;
3134
}
3235

36+
public Location(ResourceKey<Level> world, int x, int y, int z) {
37+
this(world, new BlockPos(x, y, z));
38+
}
39+
3340
public Location(ServerLevel world, int x, int y, int z) {
3441
this(world, new BlockPos(x, y, z));
3542
}
@@ -42,14 +49,26 @@ public int getX() {
4249
return this.pos.getX();
4350
}
4451

52+
public Location setX(int x) {
53+
return new Location(world, x, getY(), getZ());
54+
}
55+
4556
public int getY() {
4657
return this.pos.getY();
4758
}
4859

60+
public Location setY(int y) {
61+
return new Location(world, getX(), y, getZ());
62+
}
63+
4964
public int getZ() {
5065
return this.pos.getZ();
5166
}
5267

68+
public Location setZ(int z) {
69+
return new Location(world, getX(), getY(), z);
70+
}
71+
5372
public BlockState getBlockState() {
5473
return this.getWorld().getBlockState(this.pos);
5574
}
@@ -86,6 +105,10 @@ public ResourceKey<Level> getWorldId() {
86105
return this.world;
87106
}
88107

108+
public Location setWorldId(ResourceKey<Level> world) {
109+
return new Location(world, pos);
110+
}
111+
89112
public ServerLevel getWorld() {
90113
return DimensionalDoors.getServer().getLevel(this.world);
91114
}
@@ -104,4 +127,14 @@ public static Location fromNbt(CompoundTag nbt) {
104127
new BlockPos(pos[0], pos[1], pos[2])
105128
);
106129
}
130+
131+
public static BlockPos getHeightmapPosSafe(ServerLevel level, int x, int z) {
132+
var mutablePos = new BlockPos.MutableBlockPos(x, level.getMaxBuildHeight(), z);
133+
134+
while (mutablePos.getY() > level.getMinBuildHeight() && (!level.getBlockState(mutablePos).isSolid() && level.getFluidState(mutablePos).isEmpty())) mutablePos.move(Direction.DOWN);
135+
136+
return mutablePos.move(Direction.UP);
137+
}
138+
139+
107140
}

common/src/main/java/org/dimdev/dimdoors/rift/targets/EscapeTarget.java

Lines changed: 72 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,15 @@
33
import com.mojang.serialization.Codec;
44
import com.mojang.serialization.MapCodec;
55
import com.mojang.serialization.codecs.RecordCodecBuilder;
6+
import com.sk89q.worldedit.math.convolution.HeightMap;
67
import net.minecraft.core.BlockPos;
78
import net.minecraft.core.Rotations;
89
import net.minecraft.network.chat.Component;
910
import net.minecraft.resources.ResourceKey;
11+
import net.minecraft.server.level.ServerLevel;
1012
import net.minecraft.server.level.ServerPlayer;
1113
import net.minecraft.util.RandomSource;
14+
import net.minecraft.util.Tuple;
1215
import net.minecraft.world.entity.Entity;
1316
import net.minecraft.world.level.block.Block;
1417
import net.minecraft.world.level.block.Blocks;
@@ -24,6 +27,9 @@
2427
import org.dimdev.dimdoors.block.ModBlocks;
2528
import org.dimdev.dimdoors.block.UnravelUtil;
2629
import org.dimdev.dimdoors.world.ModDimensions;
30+
import org.dimdev.dimdoors.world.level.registry.DimensionalRegistry;
31+
import org.jetbrains.annotations.Nullable;
32+
import oshi.util.tuples.Pair;
2733

2834
import java.util.Random;
2935
import java.util.UUID;
@@ -54,43 +60,44 @@ public boolean receiveEntity(Entity entity, Vec3 relativePos, Rotations relative
5460
// chat(entity, Component.translatable("rifts.destinations.escape.cannot_escape_limbo")); TODO: Decide a proper alternate to spam
5561
return false;
5662
}
63+
64+
5765
if (entity.level().isClientSide)
5866
return false;
59-
UUID uuid = entity.getUUID();
60-
if (uuid != null) {
61-
//Location destLoc = DimensionalRegistry.getRiftRegistry().getOverworldRift(uuid);
62-
if (entity.level().getPlayerByUUID(uuid) == null) {
63-
LOGGER.log(Level.ERROR, "Tried to get player for escape target from uuid, but player does not exist, uh oh");
64-
return false;
65-
}
66-
Location destLoc;
67-
68-
if (((ServerPlayer) entity.level().getPlayerByUUID(uuid)).getRespawnPosition() != null && DimensionalDoors.getConfig().getLimboConfig().escapeTargetWorld == null && !DimensionalDoors.getConfig().getLimboConfig().escapeToWorldSpawn) {
69-
LOGGER.log(Level.INFO, "Sending player from limbo to their spawnpoint, good luck!");
70-
destLoc = new Location(((ServerPlayer) entity.level().getPlayerByUUID(uuid)).getRespawnDimension(), ((ServerPlayer) entity.level().getPlayerByUUID(uuid)).getRespawnPosition());
71-
} else if (DimensionalDoors.getConfig().getLimboConfig().escapeTargetWorld != null && !DimensionalDoors.getConfig().getLimboConfig().escapeToWorldSpawn) {
72-
targetWorldResourceKey = DimensionalDoors.getConfig().getLimboConfig().escapeTargetWorld;
73-
if (DimensionalDoors.getWorld(targetWorldResourceKey) != null) {
74-
LOGGER.log(Level.INFO, "Sending player from limbo to the exit dimension, good luck!");
75-
76-
var level = DimensionalDoors.getWorld(targetWorldResourceKey);
77-
destLoc = new Location(level, level.getHeightmapPos(Heightmap.Types.WORLD_SURFACE, entity.blockPosition()));
78-
} else {
79-
LOGGER.log(Level.INFO, "Target dimension defined in config does not exist. Use /forge dimensions for a list!");
80-
LOGGER.log(Level.INFO, "Sending player from limbo to worldspawn, good luck!");
67+
if (entity instanceof ServerPlayer player) { //TODO: Determine what other entity types should do when escaping.
68+
// Location destLoc = DimensionalRegistry.getRiftRegistry().get.getOverworldRift(uuid);
69+
70+
ServerLevel destLevel = null;
71+
BlockPos destPos = null;
8172

82-
var overworld = DimensionalDoors.getServer().overworld();
73+
if (DimensionalDoors.getConfig().getLimboConfig().tryPlayerBedSpawn) {
74+
var level = DimensionalDoors.getWorld(player.getRespawnDimension());
8375

84-
destLoc = new Location(overworld, overworld.getHeightmapPos(Heightmap.Types.WORLD_SURFACE, overworld.getSharedSpawnPos()));
76+
if(level != null) {
77+
destLevel = level;
78+
destPos = player.getRespawnPosition();
8579
}
86-
} else {
87-
LOGGER.log(Level.INFO, "sending player from limbo to worldspawn, good luck!");
80+
}
8881

89-
var overworld = DimensionalDoors.getServer().overworld();
9082

91-
destLoc = new Location(overworld, overworld.getHeightmapPos(Heightmap.Types.WORLD_SURFACE, overworld.getSharedSpawnPos()));
92-
}
83+
if(destLevel == null) {
84+
var targetWorld = DimensionalDoors.getConfig().getLimboConfig().escapeTargetWorld;
85+
destLevel = DimensionalDoors.getServer().overworld();
86+
87+
if(targetWorld != null) {
88+
var level = DimensionalDoors.getWorld(targetWorld);
9389

90+
if(level != null) {
91+
destLevel = level;
92+
}
93+
}
94+
95+
if(DimensionalDoors.getConfig().getLimboConfig().defaultToWorldSpawn) {
96+
destPos = destLevel.getSharedSpawnPos();
97+
} else {
98+
destPos = player.blockPosition();
99+
}
100+
}
94101

95102
/*
96103
if (destLoc != null && destLoc.getBlockEntity() instanceof RiftBlockEntity || this.canEscapeLimbo) {
@@ -108,26 +115,30 @@ public boolean receiveEntity(Entity entity, Vec3 relativePos, Rotations relative
108115
}
109116
*/
110117

111-
destLoc = randomizeLimboReturn(destLoc, DimensionalDoors.getConfig().getLimboConfig().limboReturnDistance); //todo add minimum radius
118+
var destLoc = randomizeLimboReturn(destLevel, destPos, DimensionalDoors.getConfig().getLimboConfig().limboReturnDistanceMin, DimensionalDoors.getConfig().getLimboConfig().limboReturnDistanceMax); //todo add minimum radius
112119

113120
if (destLoc != null && this.canEscapeLimbo) {
114121
Location location = destLoc; //VirtualLocation.fromLocation(new Location((ServerWorld) entity.world, destLoc.pos)).projectToWorld(false); //TODO Fix world projection.
115-
entity = TeleportUtil.teleport(entity, location.getWorld(), location.getBlockPos(), relativeAngle, relativeVelocity);
122+
123+
var level = location.getWorld();
124+
entity = TeleportUtil.teleport(entity, level, location.getBlockPos(), relativeAngle, relativeVelocity);
116125
entity.fallDistance = -500;
117-
location.getWorld().setBlockAndUpdate(location.getBlockPos(), Blocks.AIR.defaultBlockState());
118-
location.getWorld().setBlockAndUpdate(location.getBlockPos().offset(0, 1, 0), Blocks.AIR.defaultBlockState());
119-
120-
RandomSource random = RandomSource.create();
121-
BlockPos.withinManhattan(location.pos.offset(0, -3, 0), 3, 2, 3).forEach((pos1 -> {
122-
if (random.nextFloat() < (1 / ((float) location.pos.distSqr(pos1))) * DimensionalDoors.getConfig().getLimboConfig().limboBlocksCorruptingExitWorldAmount) {
123-
Block block = location.getWorld().getBlockState(pos1).getBlock();
124-
if (UnravelUtil.unravelBlocksMap.containsKey(block))
125-
location.getWorld().setBlockAndUpdate(pos1, UnravelUtil.unravelBlocksMap.get(block).defaultBlockState());
126-
else if (UnravelUtil.whitelistedBlocksForLimboRemoval.contains(block)) {
127-
location.getWorld().setBlockAndUpdate(pos1, ModBlocks.UNRAVELLED_FABRIC.get().defaultBlockState());
126+
level.setBlockAndUpdate(location.getBlockPos(), Blocks.AIR.defaultBlockState());
127+
level.setBlockAndUpdate(location.getBlockPos().offset(0, 1, 0), Blocks.AIR.defaultBlockState());
128+
129+
if(DimensionalDoors.getConfig().getLimboConfig().decaySurroundings) {
130+
RandomSource random = RandomSource.create();
131+
BlockPos.withinManhattan(location.pos.offset(0, -3, 0), 3, 2, 3).forEach((pos1 -> {
132+
if (random.nextFloat() < (1 / ((float) location.pos.distSqr(pos1))) * DimensionalDoors.getConfig().getLimboConfig().limboBlocksCorruptingExitWorldAmount) {
133+
Block block = level.getBlockState(pos1).getBlock();
134+
if (UnravelUtil.unravelBlocksMap.containsKey(block))
135+
level.setBlockAndUpdate(pos1, UnravelUtil.unravelBlocksMap.get(block).defaultBlockState());
136+
else if (UnravelUtil.whitelistedBlocksForLimboRemoval.contains(block)) {
137+
level.setBlockAndUpdate(pos1, ModBlocks.UNRAVELLED_FABRIC.get().defaultBlockState());
138+
}
128139
}
129-
}
130-
}));
140+
}));
141+
}
131142
} else {
132143
if (destLoc == null) {
133144
chat(entity, Component.translatable("rifts.destinations.escape.did_not_use_rift"));
@@ -156,16 +167,27 @@ public VirtualTarget copy() {
156167
return new EscapeTarget(canEscapeLimbo);
157168
}
158169

159-
public static Location randomizeLimboReturn(Location playerSpawn, int range){
160-
return new Location(playerSpawn.getWorld(), randomizeCoord(playerSpawn.getX(), range), playerSpawn.getY(), randomizeCoord(playerSpawn.getZ(),range));
170+
public static Location randomizeLimboReturn(ServerLevel level, BlockPos pos, int minRange, int maxRange) {
171+
if(level == null || pos == null) return null;
172+
173+
if(minRange == 0 && maxRange == 0) return new Location(level, pos);
174+
175+
return new Location(
176+
level,
177+
Location.getHeightmapPosSafe(level, randomizeCoord(pos.getX(), minRange, maxRange), randomizeCoord(pos.getZ(), minRange, maxRange))
178+
);
161179
}
162180

163-
public static int randomizeCoord(int coord, int range){
181+
public static int randomizeCoord(int coord, int minRange, int maxRange) {
164182
Random random = new Random();
165-
int offset = random.nextInt(range + 1); // Generate a random offset within the range
166-
boolean isPositive = random.nextBoolean(); // Randomly decide whether the offset should be positive or negative
167183

168-
// Apply the offset with the direction (positive or negative)
184+
if (minRange > maxRange) {
185+
throw new IllegalArgumentException("minRange cannot be greater than maxRange");
186+
}
187+
188+
int offset = minRange + random.nextInt((maxRange - minRange) + 1);
189+
boolean isPositive = random.nextBoolean();
190+
169191
return isPositive ? coord + offset : coord - offset;
170192
}
171193
}

common/src/main/java/org/dimdev/dimdoors/world/level/registry/DimensionalRegistry.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,8 +129,7 @@ public static DimensionalRegistry of(CompoundTag nbt) {
129129
})
130130
.toList();
131131

132-
CompletableFuture<Map<ResourceKey<Level>, PocketDirectory>> futurePocketRegistry =
133-
NbtLoaderUtil.joinAllFutures(pocketFutures, "PocketRegistry");
132+
CompletableFuture<Map<ResourceKey<Level>, PocketDirectory>> futurePocketRegistry = NbtLoaderUtil.joinAllFutures(pocketFutures, "PocketRegistry");
134133

135134
Map<ResourceKey<Level>, PocketDirectory> pocketRegistry = joinOrThrow(futurePocketRegistry, "PocketRegistry");
136135

0 commit comments

Comments
 (0)