diff --git a/src/main/java/su/terrafirmagreg/core/common/TFGCommonEventHandler.java b/src/main/java/su/terrafirmagreg/core/common/TFGCommonEventHandler.java index b25ceb9cb..85f83f3f8 100644 --- a/src/main/java/su/terrafirmagreg/core/common/TFGCommonEventHandler.java +++ b/src/main/java/su/terrafirmagreg/core/common/TFGCommonEventHandler.java @@ -2,16 +2,33 @@ import static appeng.api.upgrades.Upgrades.add; +import java.util.Objects; + import com.gregtechceu.gtceu.api.GTCEuAPI; import com.gregtechceu.gtceu.api.data.chemical.material.event.MaterialRegistryEvent; import com.gregtechceu.gtceu.api.data.chemical.material.event.PostMaterialEvent; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.core.BlockPos; +import net.minecraft.core.GlobalPos; +import net.minecraft.nbt.CompoundTag; +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.util.RandomSource; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.ItemLike; +import net.minecraft.world.level.LevelAccessor; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.level.levelgen.XoroshiroRandomSource; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.event.AttachCapabilitiesEvent; import net.minecraftforge.event.entity.player.PlayerEvent; +import net.minecraftforge.event.level.LevelEvent; import net.minecraftforge.eventbus.api.IEventBus; import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; @@ -25,6 +42,8 @@ import su.terrafirmagreg.core.common.data.TFGItems; import su.terrafirmagreg.core.common.data.capabilities.LargeEggCapability; import su.terrafirmagreg.core.common.data.capabilities.LargeEggHandler; +import su.terrafirmagreg.core.common.data.utils.CustomSpawnHelper; +import su.terrafirmagreg.core.common.data.utils.CustomSpawnSaveHandler; import su.terrafirmagreg.core.compat.grappling_hook.GrapplehookCompat; import su.terrafirmagreg.core.compat.gtceu.materials.TFGMaterialHandler; import su.terrafirmagreg.core.compat.tfcambiental.TFCAmbientalCompat; @@ -42,6 +61,8 @@ public static void init() { otherBus.addGenericListener(ItemStack.class, TFGCommonEventHandler::attachItemCapabilities); otherBus.addListener(TFGCommonEventHandler::onPlayerLogin); + otherBus.addListener(TFGCommonEventHandler::onPlayerRespawn); + otherBus.addListener(TFGCommonEventHandler::onLevelLoad); bus.addListener(TFGConfig::onLoad); bus.addListener(TFGCommonEventHandler::onCommonSetup); @@ -86,14 +107,124 @@ private static void addUpgrades(ItemLike item) { add(TFGItems.WIRELESS_CARD.get(), item, 1, GuiText.WirelessTerminals.getTranslationKey()); } - /** - * Send the blaze burner liquid fuel map to send to the client and populate emi. - */ private static void onPlayerLogin(PlayerEvent.PlayerLoggedInEvent event) { if (event.getEntity() instanceof ServerPlayer player) { + //Send the blaze burner liquid fuel map to send to the client and populate emi. TFGNetworkHandler.INSTANCE.send( PacketDistributor.PLAYER.with(() -> player), new FuelSyncPacket(FuelSyncPacket.capturedJsonData)); + + //Checks if the player is in a custom dimension spawn, + // and puts them at that pos when they first join + GlobalPos spawnPos = CustomSpawnSaveHandler.getSpawnPos(Objects.requireNonNull(player.getServer()).overworld()); + + if (!spawnPos.dimension().equals(ServerLevel.OVERWORLD)) { + CompoundTag playerData = player.getPersistentData(); + CompoundTag tfgPlayerData; + + if (playerData.contains(TFGCore.MOD_ID, CompoundTag.TAG_COMPOUND)) { + tfgPlayerData = playerData.getCompound(TFGCore.MOD_ID); + } else { + tfgPlayerData = new CompoundTag(); + playerData.put(TFGCore.MOD_ID, tfgPlayerData); + } + + if (!tfgPlayerData.getBoolean("hasJoinedBefore")) { + tfgPlayerData.putBoolean("hasJoinedBefore", true); + playerData.put(TFGCore.MOD_ID, tfgPlayerData); + + CustomSpawnHelper.respawnTeleporter(player, player.getServer().getLevel(spawnPos.dimension()), spawnPos); + + } + + } + } + } + + private static void onPlayerRespawn(PlayerEvent.PlayerRespawnEvent event) { + if (event.getEntity() instanceof ServerPlayer player) { + MinecraftServer server = player.getServer(); + GlobalPos worldSpawn = CustomSpawnSaveHandler.getSpawnPos(Objects.requireNonNull(server).overworld()); + + if ((worldSpawn.dimension().equals(ServerLevel.OVERWORLD) || player.getRespawnPosition() != null)) + return; + CustomSpawnHelper.respawnTeleporter(player, server.getLevel(worldSpawn.dimension()), worldSpawn); + } + } + + private static void onLevelLoad(LevelEvent.Load event) { + LevelAccessor level = event.getLevel(); + if (level instanceof ClientLevel) + return; + + MinecraftServer server = Objects.requireNonNull(level.getServer()); + if (server.overworld() != level) { + GlobalPos spawnPos = CustomSpawnSaveHandler.getSpawnPos(server.overworld()); + + var targetLevel = server.getLevel(spawnPos.dimension()); + if (targetLevel == level) { + + RandomSource random = new XoroshiroRandomSource(targetLevel.getSeed()); + + BlockPos validSpawn = null; + + int count = 0; + while (Objects.isNull(validSpawn)) { + ChunkPos chunkPos = new ChunkPos(random.nextInt(32), random.nextInt(32)); + System.out.println("ChunkPos: " + chunkPos); + var testPos = chunkPos.getMiddleBlockPosition(128); + + int buildHeightLimit = Math.min(targetLevel.getMaxBuildHeight(), targetLevel.getMinBuildHeight() + targetLevel.getLogicalHeight()) - 1; + BlockPos.MutableBlockPos mutableTestPos = testPos.mutable(); + + System.out.println(targetLevel.getBlockState(mutableTestPos).getBlock().toString() + targetLevel.getBlockState(mutableTestPos.above()).getBlock()); + + ChunkAccess chunk = targetLevel.getChunk(mutableTestPos.immutable()); + + for (int testY = buildHeightLimit; testY > 0; testY--) { + mutableTestPos.setY(testY); + + /*System.out.println("\tBlocks"); + System.out.println("spawn point: " + mutableTestPos);*/ + var blockA = chunk.getBlockState(mutableTestPos.above()); + var blockB = chunk.getBlockState(mutableTestPos); + var blockC = chunk.getBlockState(mutableTestPos.below()); + + /*System.out.println(blockA); + System.out.print(blockA.isAir()); + System.out.println(blockB); + System.out.print(!blockB.isCollisionShapeFullBlock(targetLevel, mutableTestPos.immutable())); + System.out.println(blockC); + System.out.print(blockC.isCollisionShapeFullBlock(targetLevel, mutableTestPos.immutable())); + + */ + + if (blockA.isAir() && !blockB.isCollisionShapeFullBlock(targetLevel, mutableTestPos.immutable())) { + if (blockC.isCollisionShapeFullBlock(targetLevel, mutableTestPos.immutable())) { + ResourceKey biomeKey = targetLevel.getBiome(mutableTestPos).unwrapKey().orElse(null); + + //System.out.println(biomeKey); + if (Objects.nonNull(biomeKey) && biomeKey.location().equals(ResourceLocation.fromNamespaceAndPath(TFGCore.MOD_ID, "nether/lush_hollow"))) { + validSpawn = mutableTestPos.immutable(); + } + //If it is not in the right biome it is unlikely that going down more will be in the valid biome + break; + } + } + + } + + if (count >= 50) { + validSpawn = BlockPos.ZERO; + System.out.println("No Valid Spawn :("); + } + + count++; + } + + System.out.println("Found valid spawn point: " + validSpawn); + CustomSpawnSaveHandler.setSpawnPos(server.overworld(), GlobalPos.of(spawnPos.dimension(), validSpawn)); + } } } } diff --git a/src/main/java/su/terrafirmagreg/core/common/data/utils/CustomSpawnHelper.java b/src/main/java/su/terrafirmagreg/core/common/data/utils/CustomSpawnHelper.java new file mode 100644 index 000000000..3659b6679 --- /dev/null +++ b/src/main/java/su/terrafirmagreg/core/common/data/utils/CustomSpawnHelper.java @@ -0,0 +1,217 @@ +package su.terrafirmagreg.core.common.data.utils; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.function.Function; + +import net.dries007.tfc.TerraFirmaCraft; +import net.dries007.tfc.world.ChunkGeneratorExtension; +import net.dries007.tfc.world.biome.BiomeExtension; +import net.dries007.tfc.world.biome.BiomeSourceExtension; +import net.minecraft.core.BlockPos; +import net.minecraft.core.GlobalPos; +import net.minecraft.core.QuartPos; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.resources.ResourceKey; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.util.RandomSource; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.Level; +import net.minecraftforge.common.util.ITeleporter; + +import earth.terrarium.adastra.api.planets.Planet; + +import su.terrafirmagreg.core.config.TFGConfig; + +public class CustomSpawnHelper { + + public static final GlobalPos BENEATH_PLACEHOLDER = GlobalPos.of(ServerLevel.NETHER, BlockPos.ZERO); + public static final GlobalPos MARS_PLACEHOLDER = GlobalPos.of(Planet.MARS, BlockPos.ZERO); + + public static void respawnTeleporter(ServerPlayer player, ServerLevel targetLevel, GlobalPos worldSpawn) { + System.out.println("attempting to spawn player at: " + worldSpawn); + + player.changeDimension(targetLevel, new ITeleporter() { + + @Override + public Entity placeEntity(Entity entity, ServerLevel currentWorld, ServerLevel destWorld, float yaw, Function repositionEntity) { + BlockPos spawnPos = worldSpawn.pos(); + + entity.teleportTo(destWorld, spawnPos.getX(), spawnPos.getY(), spawnPos.getZ(), Set.of(), entity.getYRot(), entity.getXRot()); + + return entity; + } + + @Override + public boolean playTeleportSound(ServerPlayer player, ServerLevel sourceWorld, ServerLevel destWorld) { + return false; + } + }); + } + + public static CustomSpawnCondition getFromConfig() { + return CUSTOM_SPAWN_CONDITIONS.get(TFGConfig.COMMON.NEW_WORLD_SPAWN.get()); + } + + public static void resetConfigValue() { + TFGConfig.COMMON.NEW_WORLD_SPAWN.set(DEFAULT_SPAWN.id); + } + + public static boolean testWithinRanges(float temperature, float rainfall, CustomSpawnCondition condition) { + float[] tempRange = condition.temperatureRange; + float[] rainRange = condition.rainfallRange; + + //System.out.println(tempRange[0] + " <= " + temperature + " <= " + tempRange[1]); + //System.out.println(rainRange[0] + " <= " + rainfall + " <= " + rainRange[1]); + if (tempRange[0] <= temperature && temperature <= tempRange[1]) { + //System.out.println("Temp Match"); + if (rainRange[0] <= rainfall && rainfall <= rainRange[1]) { + //System.out.println("Rain Match"); + return true; + } + } + return false; + } + + //Adapted from TFC code, but with more config + public static BlockPos findSpawnBiome(int spawnCenterX, int spawnCenterZ, int spawnRadius, RandomSource random, ChunkGeneratorExtension extension) { + int step = Math.max(1, spawnRadius / 256); + int centerX = QuartPos.fromBlock(spawnCenterX); + int centerZ = QuartPos.fromBlock(spawnCenterZ); + int maxRadius = QuartPos.fromBlock(spawnRadius); + BlockPos found = null; + int count = 0; + + for (int radius = maxRadius; radius <= maxRadius; radius += step) { + for (int dx = -radius; dx <= radius; dx += step) { + for (int dz = -radius; dz <= radius; dz += step) { + int quartX = centerX + dz; + int quartZ = centerZ + dx; + BiomeExtension biome = ((BiomeSourceExtension) extension.self().getBiomeSource()).getBiomeExtensionNoRiver(quartX, quartZ); + if (biome.isSpawnable()) { + if (found == null || random.nextInt(count + 1) == 0) { + found = new BlockPos(QuartPos.toBlock(quartX), 0, QuartPos.toBlock(quartZ)); + } + + ++count; + } + } + } + } + + if (found == null) { + TerraFirmaCraft.LOGGER.warn("Unable to find spawn biome!"); + return new BlockPos(spawnCenterX, 0, spawnCenterZ); + } else { + return found; + } + } + + public static HashMap CUSTOM_SPAWN_CONDITIONS = new HashMap<>(); + + public static HashMap SPAWN_DIFFICULTIES = new HashMap<>(Map.of( + "easy", Component.translatable("tfg.gui.spawn_difficulty.easy"), + "normal", Component.translatable("tfg.gui.spawn_difficulty.normal"), + "hard", Component.translatable("tfg.gui.spawn_difficulty.hard"), + "extreme", Component.translatable("tfg.gui.spawn_difficulty.extreme"))); + + public static final CustomSpawnCondition DESERT_SPAWN = new CustomSpawnCondition( + "desert", + -10000, + 10000, + 1, + new float[] { 20f, 30f }, + new float[] { 0f, 90f }, + Level.OVERWORLD, + SPAWN_DIFFICULTIES.get("hard")); + + public static final CustomSpawnCondition POLAR_SPAWN = new CustomSpawnCondition( + "polar", + -2000, + -10000, + 1, + new float[] { -20f, -10f }, + new float[] { 100f, 250f }, + Level.OVERWORLD, + SPAWN_DIFFICULTIES.get("hard")); + + public static final CustomSpawnCondition TEMPERATE_SPAWN = new CustomSpawnCondition( + "temperate", + 2500, + 1500, + 1, + new float[] { 5f, 15f }, + new float[] { 250f, 350f }, + Level.OVERWORLD, + SPAWN_DIFFICULTIES.get("normal")); + + public static final CustomSpawnCondition TROPICAL_SPAWN = new CustomSpawnCondition( + "tropical", + 10000, + 10000, + 1, + new float[] { 20f, 30f }, + new float[] { 350f, 500f }, + Level.OVERWORLD, + SPAWN_DIFFICULTIES.get("easy")); + + public static final CustomSpawnCondition BENEATH_SPAWN = new CustomSpawnCondition( + "beneath", + 0, + 0, + 1, + new float[] { -20f, 20f }, + new float[] { 0f, 400f }, + Level.NETHER, + SPAWN_DIFFICULTIES.get("extreme")); + + public static final CustomSpawnCondition DEFAULT_SPAWN = new CustomSpawnCondition( + "default", + 0, + 0, + 1, + new float[] { -20f, 20f }, + new float[] { 0f, 400f }, + Level.OVERWORLD, + SPAWN_DIFFICULTIES.get("normal")); + + /** + * Registers a new CustomSpawnCondition in the CUSTOM_SPAWN_CONDITIONS map. + * @param condition The CustomSpawnCondition to register. + */ + private static void initNewType(CustomSpawnCondition condition) { + CUSTOM_SPAWN_CONDITIONS.put(condition.id, condition); + } + + static { + initNewType(DEFAULT_SPAWN); + initNewType(TEMPERATE_SPAWN); + initNewType(TROPICAL_SPAWN); + initNewType(POLAR_SPAWN); + initNewType(DESERT_SPAWN); + initNewType(BENEATH_SPAWN); + } + + /// Holds spawn conditions for a particular custom world spawn + /// @param id string used for mapping + /// @param spawnCenterX int block pos estimate on the X axis + /// @param spawnCenterZ int block pos estimate on the Z axis + /// @param spawnRadiusMultiplier int multiplier on default radius + /// @param temperatureRange inclusive range to check for the temperature + /// @param rainfallRange inclusive range to check for the rainfall + /// @param dimension level that this spawn occurs in + /// @param difficulty translatable component that displays the difficulty + public record CustomSpawnCondition( + String id, + int spawnCenterX, + int spawnCenterZ, + int spawnRadiusMultiplier, + float[] temperatureRange, + float[] rainfallRange, + ResourceKey dimension, + MutableComponent difficulty) { + } +} diff --git a/src/main/java/su/terrafirmagreg/core/common/data/utils/CustomSpawnSaveHandler.java b/src/main/java/su/terrafirmagreg/core/common/data/utils/CustomSpawnSaveHandler.java new file mode 100644 index 000000000..041bc617a --- /dev/null +++ b/src/main/java/su/terrafirmagreg/core/common/data/utils/CustomSpawnSaveHandler.java @@ -0,0 +1,47 @@ +package su.terrafirmagreg.core.common.data.utils; + +import com.teamresourceful.resourcefullib.common.utils.SaveHandler; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.GlobalPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.NbtOps; +import net.minecraft.server.level.ServerLevel; + +/// Manages a single GlobalPos that represents the servers world spawn +public class CustomSpawnSaveHandler extends SaveHandler { + + private GlobalPos data = GlobalPos.of(ServerLevel.OVERWORLD, BlockPos.ZERO); + + @Override + public void loadData(CompoundTag compoundTag) { + GlobalPos.CODEC.parse(NbtOps.INSTANCE, compoundTag).result().ifPresent(globalPos -> data = globalPos); + } + + @Override + public void saveData(CompoundTag compoundTag) { + GlobalPos.CODEC.encodeStart(NbtOps.INSTANCE, data).result().ifPresent(encodedPos -> { + compoundTag.put("spawn", encodedPos); + }); + } + + public static GlobalPos getSpawnPos(ServerLevel level) { + return read(level).data; + } + + public static void setSpawnPos(ServerLevel level, GlobalPos globalPos) { + CustomSpawnSaveHandler handler = read(level); + handler.data = globalPos; + } + + public static CustomSpawnSaveHandler read(ServerLevel level) { + return (CustomSpawnSaveHandler) read(level.getDataStorage(), CustomSpawnSaveHandler::new, "tfg_spawn_position_data"); + } + + //This probably doesn't need to always be true, I just don't know enough about it + @Override + public boolean isDirty() { + return true; + } + +} diff --git a/src/main/java/su/terrafirmagreg/core/config/CommonConfig.java b/src/main/java/su/terrafirmagreg/core/config/CommonConfig.java index 24f6a8a41..71f5fb276 100644 --- a/src/main/java/su/terrafirmagreg/core/config/CommonConfig.java +++ b/src/main/java/su/terrafirmagreg/core/config/CommonConfig.java @@ -9,6 +9,7 @@ public final class CommonConfig { public final ForgeConfigSpec.BooleanValue ENABLE_TFC_AMBIENTAL_COMPAT; public final ForgeConfigSpec.BooleanValue ENABLE_CREATE_COMPAT; + public final ForgeConfigSpec.ConfigValue NEW_WORLD_SPAWN; CommonConfig(ForgeConfigSpec.Builder builder) { builder.push("general"); @@ -16,5 +17,9 @@ public final class CommonConfig { ENABLE_TFC_AMBIENTAL_COMPAT = builder.comment("Should be tfc ambiental compat enabled?") .define("tfcAmbientalCompat", true); builder.pop(); + builder.push("worldSpawn"); + NEW_WORLD_SPAWN = builder.comment("ID of the custom spawn conditions for the next generated world").define("id", "default"); + builder.pop(); + } } diff --git a/src/main/java/su/terrafirmagreg/core/mixins/client/minecraft/CreateWorldScreenMixin.java b/src/main/java/su/terrafirmagreg/core/mixins/client/minecraft/CreateWorldScreenMixin.java index 247feb3a9..8a647e5ab 100644 --- a/src/main/java/su/terrafirmagreg/core/mixins/client/minecraft/CreateWorldScreenMixin.java +++ b/src/main/java/su/terrafirmagreg/core/mixins/client/minecraft/CreateWorldScreenMixin.java @@ -17,6 +17,7 @@ public abstract class CreateWorldScreenMixin { /** * Убираем из списка все типы мира кроме TFC. + * Remove all world types besides TFC. */ @Redirect(method = "", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/components/CycleButton$Builder;withValues(Lnet/minecraft/client/gui/components/CycleButton$ValueListSupplier;)Lnet/minecraft/client/gui/components/CycleButton$Builder;")) private CycleButton.Builder tfg$init$builder$withValues( @@ -31,4 +32,5 @@ public abstract class CreateWorldScreenMixin { return instance.withValues(CycleButton.ValueListSupplier.create(allowedWorldPresets)); } + } diff --git a/src/main/java/su/terrafirmagreg/core/mixins/client/minecraft/GameTabMixin.java b/src/main/java/su/terrafirmagreg/core/mixins/client/minecraft/GameTabMixin.java new file mode 100644 index 000000000..bf8b51263 --- /dev/null +++ b/src/main/java/su/terrafirmagreg/core/mixins/client/minecraft/GameTabMixin.java @@ -0,0 +1,38 @@ +package su.terrafirmagreg.core.mixins.client.minecraft; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import com.llamalad7.mixinextras.sugar.Local; + +import net.minecraft.client.gui.components.CycleButton; +import net.minecraft.client.gui.components.Tooltip; +import net.minecraft.client.gui.layouts.GridLayout; +import net.minecraft.client.gui.screens.worldselection.CreateWorldScreen; +import net.minecraft.network.chat.Component; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; + +import su.terrafirmagreg.core.common.data.utils.CustomSpawnHelper; +import su.terrafirmagreg.core.config.TFGConfig; + +@Mixin(targets = "net.minecraft.client.gui.screens.worldselection.CreateWorldScreen$GameTab") +@OnlyIn(Dist.CLIENT) +public abstract class GameTabMixin { + + @Inject(method = "", at = @At("TAIL")) + private void tfg$addCustomSpawn(CreateWorldScreen this$0, CallbackInfo ci, @Local(ordinal = 0) GridLayout.RowHelper gridlayout$rowhelper) { + + CycleButton spawnCycleButton = gridlayout$rowhelper + .addChild(CycleButton.builder(s -> (Component.translatable("tfg.gui.spawn_condition." + s.id()).append(" ").append(s.difficulty()))) + .withValues(CustomSpawnHelper.CUSTOM_SPAWN_CONDITIONS.values()).create(0, 0, 210, 20, Component.translatable("tfg.gui.spawn_condition.title"), (button, condition) -> { + TFGConfig.COMMON.NEW_WORLD_SPAWN.set(condition.id()); + button.setTooltip(Tooltip.create(Component.translatable("tfg.gui.spawn_condition.tooltip." + condition.id()))); + })); + spawnCycleButton.setValue(CustomSpawnHelper.CUSTOM_SPAWN_CONDITIONS.get("default")); + spawnCycleButton.setTooltip(Tooltip.create(Component.translatable("tfg.gui.spawn_condition.tooltip.default"))); + } + +} diff --git a/src/main/java/su/terrafirmagreg/core/mixins/common/tfc/ForgeEventHandlerMixin.java b/src/main/java/su/terrafirmagreg/core/mixins/common/tfc/ForgeEventHandlerMixin.java index c6c48d2ec..eb76e339f 100644 --- a/src/main/java/su/terrafirmagreg/core/mixins/common/tfc/ForgeEventHandlerMixin.java +++ b/src/main/java/su/terrafirmagreg/core/mixins/common/tfc/ForgeEventHandlerMixin.java @@ -1,6 +1,10 @@ package su.terrafirmagreg.core.mixins.common.tfc; +import static net.dries007.tfc.TerraFirmaCraft.LOGGER; + +import org.spongepowered.asm.mixin.Debug; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @@ -9,14 +13,32 @@ import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import net.dries007.tfc.ForgeEventHandler; +import net.dries007.tfc.world.ChunkGeneratorExtension; +import net.dries007.tfc.world.region.Region; +import net.dries007.tfc.world.region.RegionGenerator; +import net.dries007.tfc.world.region.Units; import net.minecraft.core.BlockPos; +import net.minecraft.core.GlobalPos; +import net.minecraft.server.level.PlayerRespawnLogic; import net.minecraft.server.level.ServerLevel; +import net.minecraft.util.RandomSource; +import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.chunk.ChunkGenerator; +import net.minecraft.world.level.levelgen.XoroshiroRandomSource; +import net.minecraft.world.level.storage.ServerLevelData; import net.minecraftforge.event.level.BlockEvent; +import net.minecraftforge.event.level.LevelEvent; + +import earth.terrarium.adastra.api.planets.Planet; + +import su.terrafirmagreg.core.common.data.utils.CustomSpawnHelper; +import su.terrafirmagreg.core.common.data.utils.CustomSpawnSaveHandler; -@Mixin(value = ForgeEventHandler.class) +@Mixin(value = ForgeEventHandler.class, remap = false) +@Debug(export = true) public class ForgeEventHandlerMixin { // Forcibly disable nether portals because there's some funky mod conflict going on with @@ -44,4 +66,94 @@ public class ForgeEventHandlerMixin { // Otherwise do as usual return original.call(level, pos, newState); } + + /** + * @author + * @reason It was becoming too complicated to do as a normal mixin. + */ + @Overwrite() + public static void onCreateWorldSpawn(LevelEvent.CreateSpawnPosition event) { + if (event.getLevel() instanceof ServerLevel level && level.getChunkSource().getGenerator() instanceof ChunkGeneratorExtension extension) { + final ChunkGenerator generator = extension.self(); + final ServerLevelData levelData = event.getSettings(); + + ChunkPos chunkPos = null; + RandomSource random = new XoroshiroRandomSource(level.getSeed()); + + RegionGenerator regionGen = new RegionGenerator(extension.settings(), random); + + var condition = CustomSpawnHelper.getFromConfig(); + + boolean climateMatch = false; + int seedTicker = 0; + + while (!climateMatch) { + + chunkPos = new ChunkPos( + CustomSpawnHelper.findSpawnBiome(condition.spawnCenterX(), condition.spawnCenterZ(), extension.settings().spawnDistance() * condition.spawnRadiusMultiplier(), + random, extension)); + Region.Point regionPoint = regionGen.getOrCreateRegionPoint(Units.blockToGrid(chunkPos.getMinBlockX()), Units.blockToGrid(chunkPos.getMinBlockZ())); + + System.out.println("Testing chunkPos " + chunkPos.getWorldPosition()); + System.out.println(regionPoint.temperature); + System.out.println(regionPoint.rainfall); + if (CustomSpawnHelper.testWithinRanges(regionPoint.temperature, regionPoint.rainfall, condition)) { + climateMatch = true; + } else { + ++seedTicker; + random = new XoroshiroRandomSource(level.getSeed() + seedTicker); + } + } + + levelData.setSpawn(chunkPos.getWorldPosition().offset(8, generator.getSpawnHeight(level), 8), 0.0F); + + boolean foundExactSpawn = false; + int x = 0, z = 0; + int xStep = 0; + int zStep = -1; + + GlobalPos globalSpawnPos = null; + + for (int tries = 0; tries < 1024; ++tries) { + if (x > -16 && x <= 16 && z > -16 && z <= 16) { + final BlockPos spawnPos = PlayerRespawnLogic.getSpawnPosInChunk(level, new ChunkPos(chunkPos.x + x, chunkPos.z + z)); + if (spawnPos != null) { + globalSpawnPos = GlobalPos.of(ServerLevel.OVERWORLD, spawnPos); + levelData.setSpawn(spawnPos, 0); + foundExactSpawn = true; + break; + } + } + + if ((x == z) || (x < 0 && x == -z) || (x > 0 && x == 1 - z)) { + final int swap = xStep; + xStep = -zStep; + zStep = swap; + } + + x += xStep; + z += zStep; + } + + if (!foundExactSpawn) { + LOGGER.warn("Unable to find a suitable spawn location!"); + } + + if (level.getServer().getWorldData().worldGenOptions().generateBonusChest()) { + LOGGER.warn("No bonus chest for you, you cheaty cheater!"); + } + + if (condition.dimension() == ServerLevel.OVERWORLD) { + CustomSpawnSaveHandler.setSpawnPos(level, globalSpawnPos); + } else if (condition.dimension() == ServerLevel.NETHER) { + CustomSpawnSaveHandler.setSpawnPos(level, CustomSpawnHelper.BENEATH_PLACEHOLDER); + } else if (condition.dimension() == Planet.MARS) { + CustomSpawnSaveHandler.setSpawnPos(level, CustomSpawnHelper.MARS_PLACEHOLDER); + } + + CustomSpawnHelper.resetConfigValue(); + event.setCanceled(true); + } + + } } diff --git a/src/main/resources/tfg.mixins.json b/src/main/resources/tfg.mixins.json index f4bfdee04..37ab999b4 100644 --- a/src/main/resources/tfg.mixins.json +++ b/src/main/resources/tfg.mixins.json @@ -205,6 +205,7 @@ "client.ftb.QuestScreenMixin", "client.gtceu.ISurfaceRockRendererAccessor", "client.minecraft.CreateWorldScreenMixin", + "client.minecraft.GameTabMixin", "client.tfc.ClimateRenderCacheMixin", "client.tfc.DoubleIngotPileBlockModelMixin", "client.tfc.FarmlandBlockMixin",