Skip to content

Temp PR to leave comments on#321

Draft
Mqrius wants to merge 8 commits intodevfrom
feature/customSpawnSelection
Draft

Temp PR to leave comments on#321
Mqrius wants to merge 8 commits intodevfrom
feature/customSpawnSelection

Conversation

@Mqrius
Copy link
Member

@Mqrius Mqrius commented Feb 25, 2026

Don't merge.


@Override
public void loadData(CompoundTag compoundTag) {
GlobalPos.CODEC.parse(NbtOps.INSTANCE, compoundTag).result().ifPresent(globalPos -> data = globalPos);
Copy link
Member Author

@Mqrius Mqrius Feb 25, 2026

Choose a reason for hiding this comment

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

saveData saves to compoundTag "spawn", but loadData tries to load from root rather than the compoundTag. This silently fails, and the code will end up doing the whole finding valid spawn chunks as if it's a first boot. This usually works but by accident and it can fail in interesting ways.

Try this

  @Override
  public void loadData(CompoundTag compoundTag) {
      GlobalPos.CODEC.parse(NbtOps.INSTANCE, compoundTag.get("spawn")).result().ifPresent(globalPos -> data = globalPos);
  }


public static void setSpawnPos(ServerLevel level, GlobalPos globalPos) {
CustomSpawnSaveHandler handler = read(level);
handler.data = globalPos;
Copy link
Member Author

Choose a reason for hiding this comment

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

This is where handler.setDirty() goes; you set a field dirty any time you change it from its saved value. That only happens in this method

//This probably doesn't need to always be true, I just don't know enough about it
@Override
public boolean isDirty() {
return true;
Copy link
Member Author

Choose a reason for hiding this comment

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

See above, don't need this bit, just mark dirty whenever you change the data from what is stored


/**
* @author
* @reason It was becoming too complicated to do as a normal mixin.
Copy link
Member Author

@Mqrius Mqrius Feb 25, 2026

Choose a reason for hiding this comment

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

If you want this more targeted rather than an overwrite, it would look something like this:

    @Redirect(method = "onCreateWorldSpawn", at = @At(value = "INVOKE", target = "Lnet/dries007/tfc/world/ChunkGeneratorExtension;findSpawnBiome(Lnet/minecraft/util/RandomSource;)Lnet/minecraft/core/BlockPos;", remap = true))
    private static BlockPos tfg$findSpawnBiome(ChunkGeneratorExtension extension, RandomSource random, @Local(name = "level") ServerLevel level) {
        RegionGenerator regionGen = new RegionGenerator(extension.settings(), random);

        var condition = CustomSpawnHelper.getFromConfig();

        boolean climateMatch = false;
        int seedTicker = 0;

        BlockPos pos = null;
        ChunkPos chunkPos;
        while (!climateMatch) {

            pos = CustomSpawnHelper.findSpawnBiome(condition.spawnCenterX(), condition.spawnCenterZ(), extension.settings().spawnDistance() * condition.spawnRadiusMultiplier(), random, extension);
            chunkPos = new ChunkPos(pos);
            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);
            }
        }

        return pos;
    }

    @Inject(method = "onCreateWorldSpawn", at = @At(value = "INVOKE", target = "Lnet/minecraftforge/event/level/LevelEvent$CreateSpawnPosition;setCanceled(Z)V"))
    private static void tfg$saveSpawnPos(LevelEvent.CreateSpawnPosition event, CallbackInfo ci, @Local(name = "level") ServerLevel level, @Local(name = "levelData") ServerLevelData leveldata) {
        var condition = CustomSpawnHelper.getFromConfig();

        BlockPos spawnPos = new BlockPos(leveldata.getXSpawn(), leveldata.getYSpawn(), leveldata.getZSpawn());
        GlobalPos globalSpawnPos = GlobalPos.of(ServerLevel.OVERWORLD, spawnPos);

        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();
    }

Specifically injecting on the spawn biome search, and at the end to do the save. It's more obvious what has changed this way though the overwrite is fine too

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

}

if (condition.dimension() == ServerLevel.OVERWORLD) {
CustomSpawnSaveHandler.setSpawnPos(level, globalSpawnPos);
Copy link
Member Author

Choose a reason for hiding this comment

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

If globalSpawnPos is null then this will cause a NPE when you getSpawnPos.

TFC doesn't have this issue because they do levelData.setSpawn(chunkPos.getWorldPosition().offset(8, generator.getSpawnHeight(level), 8), 0.0F); earlier as a rough first pass. Could do a similar fallback like that for globalSpawnPos by initiating it to that instead of null.

@Mqrius Mqrius requested a review from BlueBoat29 February 25, 2026 14:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants