Jump to content

Blocking structures, liquids, initial creature spawns for Skyblock generation mod (1.12.2)


Recommended Posts

Posted

Hello all. I'm trying to create a simple Skyblock (void + a single spawn island) mod that preserves biomes (only as visible through F3) along with their sizes and distributions, including those created by mods like Biomes O'Plenty as an optional mod compatibility. The goal is to support the unique spawning and growing logic through mods (Pixelmon being front of mind) and to encourage building the island out to these locations. A map won't suffice in that it will be the same every time and will have restrictive hard world limits. I want to preserve existing Nether/End/mod dimension gen logic and to only modify the Overworld, at least for now. I'm doing this first in 1.12.2, though I plan to write it eventually for 1.16.5 and potentially beyond. This mod will be for single player and multiplayer servers.

I've identified two ways of doing this, both somewhat brittle due to the nature of the mod. The first is to block many of the world gen events or substitute my own post generation event that wipes the generation and substitutes a floating skyblock island if it is the spawn area. The second is to rewrite the entire vanilla + mod biome gen logic in a similar manner to Realistic Terrain Generation, with the added complexity that I don't want to change the biome size or borders (I'm admittedly not sure how this works. If there's simple logic for this and biomes borders are locked at chunk boundaries instead of something more freeform, this might not be tough) I'm concerned however that this would be a lot of excess code and class bloat, and small additions would break it. As such, and given that logically what I am doing is simply taking existing biome gen but totally voiding it and writing in a small island, the first seems simplest and most intuitive if possible

Eventually the plan is to also add a command + simple object that can generate a new island in a random location, set the player's spawn point to that island, and teleport them there. Not immediately relevant or important, but if others see unique concerns with this, I'm all ears.

The way I'm accomplishing the void and rewrite is to write a class WorldGenHandler, registered during the FMLPreInitializationEvent handler in the main mod class in the following manner:

MinecraftForge.TERRAIN_GEN_BUS.register(WorldGenHandler.class);

and in the class subscribe to a series of chunk loading, decoration, replace biome blocks, and init map gen events. I'm aware it's likely messy and redundant or contains methods that effectively do nothing in conjunction with others, but right now I'm spitballing and seeing what sticks:

 

package com.jks.skygenmod.util;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import net.minecraft.init.Blocks;
import net.minecraft.world.chunk.Chunk;
import net.minecraft.world.chunk.storage.ExtendedBlockStorage;
import net.minecraft.world.gen.MapGenBase;
import net.minecraftforge.event.terraingen.BiomeEvent.GetVillageBlockID;
import net.minecraftforge.event.terraingen.ChunkGeneratorEvent.ReplaceBiomeBlocks;
import net.minecraftforge.event.terraingen.DecorateBiomeEvent;
import net.minecraftforge.event.terraingen.InitMapGenEvent;
import net.minecraftforge.event.terraingen.PopulateChunkEvent;
import net.minecraftforge.event.world.BlockEvent.CreateFluidSourceEvent;
import net.minecraftforge.event.world.BlockEvent.FluidPlaceBlockEvent;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
import net.minecraftforge.fml.common.eventhandler.Event.Result;
import net.minecraftforge.fml.common.eventhandler.EventPriority;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;

@EventBusSubscriber
public class WorldGenHandler {
	private static final HashSet<InitMapGenEvent.EventType> permittedMapGenEvents 
		= new HashSet<InitMapGenEvent.EventType>(
				Arrays.asList(
						InitMapGenEvent.EventType.END_CITY,
						InitMapGenEvent.EventType.NETHER_BRIDGE,
						InitMapGenEvent.EventType.NETHER_CAVE
						)
				);
	
	@SubscribeEvent(priority=EventPriority.LOWEST, receiveCanceled=true)
	public static void onPopulate(PopulateChunkEvent.Populate event)
	{
		Logger logger = LogManager.getLogger(Reference.MOD_ID);
		logger.info("<< JKS SKYGEN >> BLOCK POPULATE OF TYPE {}", event.getType().name());
	    
		if (!event.getWorld().isRemote)
		{
		    event.setResult(Result.DENY);
		}
	}
	
	@SubscribeEvent(priority=EventPriority.LOWEST, receiveCanceled=true)
	public static void onEvent(PopulateChunkEvent.Post event)
	{
		Logger logger = LogManager.getLogger(Reference.MOD_ID);
		logger.info("<< JKS SKYGEN >> REWRITING BLOCKS TO AIR");
	    
		if (!event.getWorld().isRemote)
		{
		    Chunk chunk = event.getWorld().getChunkFromChunkCoords(event.getChunkX(), event.getChunkZ());
	
		    for (ExtendedBlockStorage storage : chunk.getBlockStorageArray()) 
		    {
		        if (storage != null) 
		        {
		            for (int x = 0; x < 16; ++x) 
		            {
		                for (int y = 0; y < 16; ++y) 
		                {
		                    for (int z = 0; z < 16; ++z) 
		                    {
		                    	//logger.info("<< JKS SKYGEN >> REWRITING BLOCK AT X:{}, Y: {}, Z: {}", x, y, z);
		                    	storage.set(x, y, z, Blocks.AIR.getDefaultState());
		                    }
		                }
		            }
		        }
		    }  
		    chunk.setModified(true); // this is important as it marks it to be saved
		}
	}
	
	@SubscribeEvent
	public static void onDecorateBiome(DecorateBiomeEvent.Decorate event)
	{
		Logger logger = LogManager.getLogger(Reference.MOD_ID);
		logger.info("<< JKS >> onDecorateBiome: {}", event.getType().name());
		if (!event.getWorld().isRemote)
		{
			event.setResult(Result.DENY);
		}
	}
	
	@SubscribeEvent
	public static void onReplaceBiomeBlocks(ReplaceBiomeBlocks event)
	{
		Logger logger = LogManager.getLogger(Reference.MOD_ID);
		logger.info("<< JKS >> onReplaceBiomeBlocks");
		if (!event.getWorld().isRemote)
		{
			event.setResult(Result.DENY);
		}
	}
	
	@SubscribeEvent
	public static void onInitMapGen(InitMapGenEvent event)
	{
		Logger logger = LogManager.getLogger(Reference.MOD_ID);
		logger.info("<< JKS >> onInitMapGen: {}", event.getType().name());
		if (!permittedMapGenEvents.contains(event.getType())) {
			event.setResult(Result.DENY);
		}
		else {
			logger.info("<< ! JKS ! >> Permitted init event {}", event.getType().name());
		}
	}
}

This logic voids most of the world, but despite the onInitMapGen event, I still gee some structures generating, most notably mineshafts and ocean monuments, and I think at one point I might have seen a floating village garden, though I could be recalling an old test. I also see stray water and lava pillars. These seem logically inconsistent with the above behavior.

On a separate note, do I need to check isRemote on the onInitMapGen, or is it implicitly not remote? Unsure how to grab the world from the event. Also, is there a way to change some of this logic to only take effect when in the overworld? That onInitMapGen seems like it might make a check like that tricky.

All the best and thanks for taking the time to take a look at this. Let me know if more is needed.
jks

  • Curle locked this topic
Guest
This topic is now closed to further replies.

Announcements



×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.