Skip to content
Draft
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
137 changes: 134 additions & 3 deletions src/main/java/su/terrafirmagreg/core/common/TFGCommonEventHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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);
Expand Down Expand Up @@ -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<Biome> 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));
}
}
}
}
Original file line number Diff line number Diff line change
@@ -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<Boolean, Entity> 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) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The outer loop never iterates, because radius starts at maxRadius. Is that intentional? Can either remove the outer loop, or start close in and loop outwards if that's what you want

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<String, CustomSpawnCondition> CUSTOM_SPAWN_CONDITIONS = new HashMap<>();

public static HashMap<String, MutableComponent> 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<Level> dimension,
MutableComponent difficulty) {
}
}
Loading