package carpet.script.utils; import carpet.fakes.MinecraftServerInterface; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.Util; import net.minecraft.core.BlockPos; import net.minecraft.core.Holder; import net.minecraft.core.MappedRegistry; import net.minecraft.core.Registry; import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.progress.ChunkProgressListener; import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.CustomSpawner; import net.minecraft.world.level.Level; import net.minecraft.world.level.biome.BiomeManager; import net.minecraft.world.level.biome.MultiNoiseBiomeSource; import net.minecraft.world.level.border.BorderChangeListener; import net.minecraft.world.level.chunk.ChunkGenerator; import net.minecraft.world.level.chunk.ChunkStatus; import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.level.chunk.storage.RegionFile; import net.minecraft.world.level.dimension.DimensionType; import net.minecraft.world.level.dimension.LevelStem; import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator; import net.minecraft.world.level.levelgen.NoiseGeneratorSettings; import net.minecraft.world.level.levelgen.WorldGenSettings; import net.minecraft.world.level.storage.DerivedLevelData; import net.minecraft.world.level.storage.ServerLevelData; import java.io.File; import java.io.IOException; import java.nio.file.Path; import java.util.List; import java.util.Map; import java.util.Random; import java.util.Set; public class WorldTools { public static boolean canHasChunk(ServerLevel world, ChunkPos chpos, Map regionCache, boolean deepcheck) { if (world.getChunk(chpos.x, chpos.z, ChunkStatus.STRUCTURE_STARTS, false) != null) return true; String currentRegionName = "r." + chpos.getRegionX() + "." + chpos.getRegionZ() + ".mca"; if (regionCache != null && regionCache.containsKey(currentRegionName)) { RegionFile region = regionCache.get(currentRegionName); if (region == null) return false; return region.hasChunk(chpos); } Path regionPath = new File(((MinecraftServerInterface )world.getServer()).getCMSession().getDimensionPath(world.dimension()).toFile(), "region").toPath(); Path regionFilePath = regionPath.resolve(currentRegionName); File regionFile = regionFilePath.toFile(); if (!regionFile.exists()) { if (regionCache != null) regionCache.put(currentRegionName, null); return false; } if (!deepcheck) return true; // not using cache in this case. try { RegionFile region = new RegionFile(regionFile.toPath(), regionPath, true); if (regionCache != null) regionCache.put(currentRegionName, region); return region.hasChunk(chpos); } catch (IOException ignored) { } return true; } public static boolean createWorld(MinecraftServer server, String worldKey, Long seed) { ResourceLocation worldId = new ResourceLocation(worldKey); ServerLevel overWorld = server.overworld(); Set> worldKeys = server.levelKeys(); for (ResourceKey worldRegistryKey : worldKeys) { if (worldRegistryKey.location().equals(worldId)) { // world with this id already exists return false; } } ServerLevelData serverWorldProperties = server.getWorldData().overworldData(); WorldGenSettings generatorOptions = server.getWorldData().worldGenSettings(); boolean bl = generatorOptions.isDebug(); long l = generatorOptions.seed(); long m = BiomeManager.obfuscateSeed(l); List list = List.of(); Registry simpleRegistry = generatorOptions.dimensions(); LevelStem dimensionOptions = simpleRegistry.get(LevelStem.OVERWORLD); ChunkGenerator chunkGenerator2; Holder dimensionType2; if (dimensionOptions == null) { dimensionType2 = server.registryAccess().registryOrThrow(Registry.DIMENSION_TYPE_REGISTRY).getOrCreateHolder(DimensionType.OVERWORLD_LOCATION);; chunkGenerator2 = WorldGenSettings.makeDefaultOverworld(server.registryAccess(), (new Random()).nextLong()); } else { dimensionType2 = dimensionOptions.typeHolder(); chunkGenerator2 = dimensionOptions.generator(); } ResourceKey customWorld = ResourceKey.create(Registry.DIMENSION_REGISTRY, worldId); //chunkGenerator2 = GeneratorOptions.createOverworldGenerator(server.getRegistryManager().get(Registry.BIOME_KEY), server.getRegistryManager().get(Registry.NOISE_SETTINGS_WORLDGEN), (seed==null)?l:seed); // from world/gen/GeneratorOptions //chunkGenerator2 = new NoiseChunkGenerator(MultiNoiseBiomeSource.createVanillaSource(server.getRegistryManager().get(Registry.BIOME_KEY), seed), seed, () -> { // return server.getRegistryManager().get(Registry.CHUNK_GENERATOR_SETTINGS_KEY).getOrThrow(ChunkGeneratorSettings.OVERWORLD); //}); chunkGenerator2 = new NoiseBasedChunkGenerator( server.registryAccess().registryOrThrow(Registry.NOISE_REGISTRY), server.registryAccess().registryOrThrow(Registry.CONFIGURED_STRUCTURE_FEATURE_REGISTRY), MultiNoiseBiomeSource.Preset.OVERWORLD.biomeSource(server.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY)), seed, Holder.direct(server.registryAccess().registryOrThrow(Registry.NOISE_GENERATOR_SETTINGS_REGISTRY).getOrThrow(NoiseGeneratorSettings.OVERWORLD)) ); ServerLevel serverWorld = new ServerLevel( server, Util.backgroundExecutor(), ((MinecraftServerInterface) server).getCMSession(), new DerivedLevelData(server.getWorldData(), serverWorldProperties), customWorld, dimensionType2, NOOP_LISTENER, chunkGenerator2, bl, (seed==null)?l:seed, list, false); overWorld.getWorldBorder().addListener(new BorderChangeListener.DelegateBorderChangeListener(serverWorld.getWorldBorder())); ((MinecraftServerInterface) server).getCMWorlds().put(customWorld, serverWorld); return true; } public static void forceChunkUpdate(BlockPos pos, ServerLevel world) { LevelChunk worldChunk = world.getChunkSource().getChunk(pos.getX()>>4, pos.getZ()>>4, false); if (worldChunk != null) { int vd = world.getServer().getPlayerList().getViewDistance() * 16; int vvd = vd * vd; List nearbyPlayers = world.getPlayers(p -> pos.distToCenterSqr(p.getX(), pos.getY(), p.getZ()) < vvd); if (!nearbyPlayers.isEmpty()) { ClientboundLevelChunkWithLightPacket packet = new ClientboundLevelChunkWithLightPacket(worldChunk, world.getLightEngine(), null, null, false); // false seems to update neighbours as well. ChunkPos chpos = new ChunkPos(pos); nearbyPlayers.forEach(p -> p.connection.send(packet)); } } } private static class NoopWorldGenerationProgressListener implements ChunkProgressListener { @Override public void updateSpawnPos(ChunkPos spawnPos) { } @Override public void onStatusChange(ChunkPos pos, ChunkStatus status) { } @Environment(EnvType.CLIENT) @Override public void start() { } @Override public void stop() { } } public static final ChunkProgressListener NOOP_LISTENER = new NoopWorldGenerationProgressListener(); }