Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,9 @@ Root command: `/resourceworld`
- `cooldown`: `/resourceworld tp` cooldown.
- `hideSeedHash`: Whether hide resource world seed hash from players, open this can partly prevent seed cracking but may
cause wrong biome sounds and sky colors.
- `allowHomeCommand`: Whether allow using `/resource home` command.
- `allowHomeCommand`: Whether allow using `/resource home` command.

### Developer Usage

If you want to customize the dimension random teleport locator, create a class implement `RandomTeleportEntrypoint`,
then annotate with `@RandomTeleportProvider`(forge) or write into entrypoint `random_teleport_locator`(fabric)
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
import java.util.function.Consumer;

public final class MixinCache {
@Nullable
public static MinecraftServer SERVER;
@Nullable
public static RegistryKey<World> CURRENT_TICKING_WORLD;
public static final List<Consumer<MinecraftServer>> WORLD_CHANGE_CALLBACKS = new CopyOnWriteArrayList<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ private static int teleport(CommandContext<ServerCommandSource> ctx) throws Comm
throw new SimpleCommandExceptionType(ServerI18n.translateToLiteral(source, "message.resource_world.unknown_resource_world")).create();
long delta = COOLDOWNS.getOrDefault(player, 0) + data.getSettings().getCooldown() * 1000L - System.currentTimeMillis();
if (delta > 0)
throw new SimpleCommandExceptionType(ServerI18n.translateToLiteral("message.resource_world.teleport_cooldown", String.valueOf(delta / 1000))).create();
throw new CommandException(ServerI18n.translateToLiteral(source, "message.resource_world.teleport_cooldown", String.valueOf(delta / 1000)));
source.sendMessage(ServerI18n.translateToLiteral(source, "message.resource_world.finding_position"));
BlockPos pos = PositionLocator.locate(world, data);
if (pos == null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ public static final class Settings {
private int cooldown;
private boolean hideSeedHash;
private boolean allowHomeCommand;
private boolean worldBorderInfoDirty;

public Settings() {
this(0, 0, 4096, Optional.empty(), 30, false, true);
Expand All @@ -107,6 +108,7 @@ public Settings(int centerX, int centerZ, int range, Optional<BlockPos> spawnPoi
this.cooldown = cooldown;
this.hideSeedHash = hideSeedHash;
this.allowHomeCommand = allowHomeCommand;
this.worldBorderInfoDirty = true;
}

public int getCenterX() {
Expand All @@ -115,6 +117,7 @@ public int getCenterX() {

public void setCenterX(int centerX) {
this.centerX = centerX;
this.worldBorderInfoDirty = true;
}

public int getCenterZ() {
Expand All @@ -123,6 +126,7 @@ public int getCenterZ() {

public void setCenterZ(int centerZ) {
this.centerZ = centerZ;
this.worldBorderInfoDirty = true;
}

public int getRange() {
Expand All @@ -131,6 +135,7 @@ public int getRange() {

public void setRange(int range) {
this.range = range;
this.worldBorderInfoDirty = true;
}

public Optional<BlockPos> getSpawnPoint() {
Expand Down Expand Up @@ -164,5 +169,11 @@ public boolean isAllowHomeCommand() {
public void setAllowHomeCommand(boolean allowHomeCommand) {
this.allowHomeCommand = allowHomeCommand;
}

public boolean isWorldBorderInfoDirty() {
boolean b = this.worldBorderInfoDirty;
this.worldBorderInfoDirty = false;
return b;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.iafenvoy.resourceworld.config;

import com.google.gson.JsonParser;
import com.iafenvoy.resourceworld.MixinCache;
import com.iafenvoy.resourceworld.ResourceWorld;
import com.iafenvoy.resourceworld.data.ResourceWorldHelper;
import com.mojang.brigadier.suggestion.Suggestions;
Expand Down Expand Up @@ -32,12 +33,9 @@ public class WorldConfig {
private static final WorldSavePath RESOURCE_WORLD_DATA = new WorldSavePath("resource_world.json");
private static final Random RANDOM = new Random();
private static final Map<String, ResourceWorldData> DATA = new HashMap<>();
@Nullable
private static MinecraftServer SERVER;

@ApiStatus.Internal
public static void bootstrap(MinecraftServer server) {
SERVER = server;
DATA.clear();
try {
DATA.putAll(CODEC.parse(JsonOps.INSTANCE, JsonParser.parseString(FileUtils.readFileToString(server.getSavePath(RESOURCE_WORLD_DATA).toFile(), StandardCharsets.UTF_8))).resultOrPartial(ResourceWorld.LOGGER::error).orElseThrow());
Expand All @@ -62,7 +60,6 @@ public static CompletableFuture<Suggestions> appendSuggestions(SuggestionsBuilde
@ApiStatus.Internal
public static void stop() {
saveConfig();
SERVER = null;
}

public static ResourceWorldData create(String id, Identifier target) {
Expand Down Expand Up @@ -106,9 +103,9 @@ private static long randomSeed() {
}

public static void saveConfig() {
if (SERVER == null) return;
if (MixinCache.SERVER == null) return;
try {
FileUtils.write(SERVER.getSavePath(RESOURCE_WORLD_DATA).toFile(), CODEC.encodeStart(JsonOps.INSTANCE, DATA).resultOrPartial(ResourceWorld.LOGGER::error).orElseThrow().toString(), StandardCharsets.UTF_8);
FileUtils.write(MixinCache.SERVER.getSavePath(RESOURCE_WORLD_DATA).toFile(), CODEC.encodeStart(JsonOps.INSTANCE, DATA).resultOrPartial(ResourceWorld.LOGGER::error).orElseThrow().toString(), StandardCharsets.UTF_8);
} catch (Exception ex) {
ResourceWorld.LOGGER.error("Failed to create config", ex);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.iafenvoy.resourceworld.data;

import java.util.List;

public abstract class EntryPointLoader {
public static EntryPointLoader INSTANCE;
private List<RandomTeleportEntrypoint> entries;

public List<RandomTeleportEntrypoint> getEntries() {
if (this.entries == null) this.entries = this.loadEntries();
return this.entries;
}

protected abstract List<RandomTeleportEntrypoint> loadEntries();
}
Original file line number Diff line number Diff line change
@@ -1,24 +1,33 @@
package com.iafenvoy.resourceworld.data;

import com.google.common.collect.ImmutableMap;
import com.iafenvoy.resourceworld.config.ResourceWorldData;
import net.minecraft.block.Blocks;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.Heightmap;
import net.minecraft.world.World;
import org.jetbrains.annotations.Nullable;

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.Random;
import java.util.function.BiFunction;

public class PositionLocator {
private static final Map<Identifier, BiFunction<World, ResourceWorldData, BlockPos>> LOCATOR = new HashMap<>();
private static final Map<Identifier, BiFunction<World, ResourceWorldData, BlockPos>> LOCATOR;

@Nullable
public static BlockPos locate(World world, ResourceWorldData data) {
return Optional.ofNullable(LOCATOR.getOrDefault(data.getTargetWorld(), (w, d) -> randomPos(w, d, world.getTopY()))).map(x -> x.apply(world, data)).filter(x -> world.getChunk(x) != null).map(pos -> world.getTopPosition(Heightmap.Type.MOTION_BLOCKING_NO_LEAVES, pos)).filter(x -> x.getY() > world.getBottomY()).orElse(null);
return LOCATOR.getOrDefault(data.getTargetWorld(), (w, d) -> toValidPos(randomPos(w, d, w.getTopY()), w)).apply(world, data);
}

@Nullable
public static BlockPos toValidPos(BlockPos pos, World world) {
if (pos == null || world.getChunk(pos) == null) return null;
while (pos.getY() > world.getBottomY() && (world.getBlockState(pos.down()).isAir() || !world.getBlockState(pos).isAir() || !world.getBlockState(pos.up()).isAir()))
pos = pos.down();
return pos.getY() > world.getBottomY() ? pos : null;
}

public static BlockPos randomPos(World world, ResourceWorldData data, int y) {
Expand All @@ -34,7 +43,15 @@ public static int randomInt(Random random, int min, int max) {
}

static {
LOCATOR.put(World.NETHER.getValue(), (world, data) -> randomPos(world, data, 125));
LOCATOR.put(World.END.getValue(), (world, data) -> world.getWorldBorder().contains(0, 0) ? BlockPos.ORIGIN.up(world.getTopY() - 1) : null);
ImmutableMap.Builder<Identifier, BiFunction<World, ResourceWorldData, BlockPos>> builder = ImmutableMap.builder();
builder.put(World.NETHER.getValue(), (world, data) -> {
BlockPos pos = toValidPos(randomPos(world, data, 100), world);
if (pos != null && world.getBlockState(pos.down()).isLiquid())
world.setBlockState(pos.down(), Blocks.NETHERRACK.getDefaultState());
return pos;
});
builder.put(World.END.getValue(), (world, data) -> toValidPos(world.getWorldBorder().contains(0, 0) ? BlockPos.ORIGIN.up(world.getTopY() - 1) : null, world));
EntryPointLoader.INSTANCE.getEntries().forEach(x -> x.register(builder));
LOCATOR = builder.buildKeepingLast();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.iafenvoy.resourceworld.data;

import com.google.common.collect.ImmutableMap;
import com.iafenvoy.resourceworld.config.ResourceWorldData;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;

import java.util.function.BiFunction;

public interface RandomTeleportEntrypoint {
void register(ImmutableMap.Builder<Identifier, BiFunction<World, ResourceWorldData, BlockPos>> builder);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.iafenvoy.resourceworld.data;

import java.lang.annotation.*;

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface RandomTeleportProvider {
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,10 @@
import net.minecraft.server.WorldGenerationProgressTracker;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.random.RandomSequencesState;
import net.minecraft.util.thread.ThreadExecutor;
import net.minecraft.world.SaveProperties;
import net.minecraft.world.World;
import net.minecraft.world.biome.source.BiomeAccess;
import net.minecraft.world.border.WorldBorder;
import net.minecraft.world.dimension.DimensionOptions;
import net.minecraft.world.gen.GeneratorOptions;
import net.minecraft.world.level.ServerWorldProperties;
Expand Down Expand Up @@ -60,7 +58,7 @@ protected MinecraftServerMixin(String name) {

@Inject(method = "<init>", at = @At("RETURN"))
private void onServerStart(CallbackInfo ci) {
WorldConfig.bootstrap((MinecraftServer) (Object) this);
WorldConfig.bootstrap(MixinCache.SERVER = (MinecraftServer) (Object) this);
}

@Inject(method = "createWorlds", at = @At("RETURN"))
Expand All @@ -71,6 +69,7 @@ private void createResourceWorlds(CallbackInfo ci) {
@Inject(method = "stop", at = @At("HEAD"))
private void onServerStop(CallbackInfo ci) {
WorldConfig.stop();
MixinCache.SERVER = null;
}

@SuppressWarnings("all")
Expand All @@ -85,10 +84,8 @@ private void onServerStop(CallbackInfo ci) {
long l = generatorOptions.getSeed();
long m = BiomeAccess.hashSeed(l);
ServerWorld serverWorld = this.worlds.get(World.OVERWORLD);
WorldBorder worldBorder = serverWorld.getWorldBorder();
RandomSequencesState randomSequencesState = serverWorld.getRandomSequences();
UnmodifiableLevelProperties unmodifiableLevelProperties = new UnmodifiableLevelProperties(this.saveProperties, serverWorldProperties);
ServerWorld serverWorld2 = new ServerWorld((MinecraftServer) (Object) this, this.workerExecutor, this.session, unmodifiableLevelProperties, key, registry.get(worldOption), WorldGenerationProgressTracker.create(16), bl, m, ImmutableList.of(), false, randomSequencesState);
ServerWorld serverWorld2 = new ServerWorld((MinecraftServer) (Object) this, this.workerExecutor, this.session, unmodifiableLevelProperties, key, registry.get(worldOption), new WorldGenerationProgressTracker(16), bl, m, ImmutableList.of(), false, null);
this.worlds.put(key, serverWorld2);
MixinCache.WORLD_CHANGE_CALLBACKS.forEach(x -> x.accept((MinecraftServer) (Object) this));
return true;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.iafenvoy.resourceworld.mixin;

import com.iafenvoy.resourceworld.config.ResourceWorldData;
import com.iafenvoy.resourceworld.config.WorldConfig;
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
import com.llamalad7.mixinextras.sugar.Local;
import net.minecraft.server.PlayerManager;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.world.border.WorldBorder;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;

@Mixin(PlayerManager.class)
public class PlayerManagerMixin {
@ModifyExpressionValue(method = "sendWorldInfo", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/world/ServerWorld;getWorldBorder()Lnet/minecraft/world/border/WorldBorder;"))
private WorldBorder handleBorderPacketTarget(WorldBorder original, @Local(argsOnly = true) ServerWorld world) {
//I can force redirect all, but I modify resource worlds only for better capability.
ResourceWorldData config = WorldConfig.get(world.getRegistryKey());
return config == null ? original : world.getWorldBorder();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.iafenvoy.resourceworld.ResourceWorld;
import com.iafenvoy.resourceworld.config.WorldConfig;
import com.iafenvoy.resourceworld.data.ResourceWorldHelper;
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
import net.minecraft.registry.DynamicRegistryManager;
import net.minecraft.registry.RegistryKey;
import net.minecraft.registry.entry.RegistryEntry;
Expand All @@ -28,6 +29,12 @@ protected ServerWorldMixin(MutableWorldProperties properties, RegistryKey<World>
super(properties, registryRef, registryManager, dimensionEntry, profiler, isClient, debugWorld, biomeAccess, maxChainedNeighborUpdates);
}

@ModifyExpressionValue(method = "<init>", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/gen/GeneratorOptions;getSeed()J"))
private long handleResourceWorldCreatingSeeds(long original) {
long seed = WorldConfig.getSeed(this.getRegistryKey());
return seed != 0 ? seed : original;
}

@Inject(method = "getSeed", at = @At("HEAD"), cancellable = true)
private void handleResourceWorldSeeds(CallbackInfoReturnable<Long> cir) {
long seed = WorldConfig.getSeed(this.getRegistryKey());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.iafenvoy.resourceworld.MixinCache;
import com.iafenvoy.resourceworld.config.ResourceWorldData;
import com.iafenvoy.resourceworld.config.WorldConfig;
import net.minecraft.network.packet.s2c.play.WorldBorderInitializeS2CPacket;
import net.minecraft.world.border.WorldBorder;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
Expand All @@ -24,7 +25,11 @@ private void forceSetBorder(CallbackInfo ci) {
ResourceWorldData data = WorldConfig.get(MixinCache.CURRENT_TICKING_WORLD);
if (data == null) return;
ResourceWorldData.Settings settings = data.getSettings();
this.setCenter(settings.getCenterX(), settings.getCenterZ());
this.setSize(settings.getRange() * 2);
if (settings.isWorldBorderInfoDirty()) {
this.setCenter(settings.getCenterX(), settings.getCenterZ());
this.setSize(settings.getRange() * 2);
if (MixinCache.SERVER != null)
MixinCache.SERVER.getPlayerManager().sendToDimension(new WorldBorderInitializeS2CPacket((WorldBorder) (Object) this), MixinCache.CURRENT_TICKING_WORLD);
}
}
}
2 changes: 2 additions & 0 deletions common/src/main/resources/resource_world.mixins.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
"DifficultyCommandMixin",
"GameRuleCommandMixin",
"MinecraftServerMixin",
"PlayerManagerMixin",
"PlayerRespawnS2CPacketMixin",
"CommonPlayerSpawnInfoMixin",
"ServerWorldMixin",
"WorldBorderMixin"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.iafenvoy.resourceworld.fabric;

import com.iafenvoy.resourceworld.data.EntryPointLoader;
import com.iafenvoy.resourceworld.data.RandomTeleportEntrypoint;
import net.fabricmc.loader.api.FabricLoader;

import java.util.List;

public class FabricEntryPointLoader extends EntryPointLoader {
public static void init() {
INSTANCE = new FabricEntryPointLoader();
}

@Override
protected List<RandomTeleportEntrypoint> loadEntries() {
return FabricLoader.getInstance().getEntrypoints("random_teleport_locator", RandomTeleportEntrypoint.class);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.iafenvoy.resourceworld.fabric;

import net.fabricmc.api.ModInitializer;

public final class ResourceWorldFabric implements ModInitializer {
@Override
public void onInitialize() {
FabricEntryPointLoader.init();
}
}
3 changes: 3 additions & 0 deletions fabric/src/main/resources/fabric.mod.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@
"icon": "logo.png",
"environment": "*",
"entrypoints": {
"main": [
"com.iafenvoy.resourceworld.fabric.ResourceWorldFabric"
]
},
"accessWidener": "resource_world.accesswidener",
"mixins": [
Expand Down
Loading
Loading