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



  • Recently Browsing

    • No registered users viewing this page.
  • Posts

    • When you name a method like that, with no return value, it is a constructor. The constructor must have the same name as the class it constructs, in this case, ModItems. I would strongly advise reading up on some basic Java tutorials, because you will definitely be running into a lot more issues as you go along without the basics. *I should also add that the Forge documentation is a reference, not a tutorial. Even following tutorials, you should know Java basics, otherwise the smallest of mistakes will trip you up as you copy someone elses code.
    • so, I'm starting modding and I'm following the official documantation for forge: https://docs.minecraftforge.net, but in the registries part it is not working as it is in the docs:   public class ModItems { private static final DeferredRegister<Item> ITEMS = DeferredRegister.create(ForgeRegistries.ITEMS, DarkStarvation.MOD_ID); public static final RegistryObject<Item> TEST_ITEM = ITEMS.register("test_item", () -> new Item(new Item.Properties())); public DarkStarvation(FMLJavaModLoadingContext context) { ITEMS.register(context.getModEventBus()); } } in 'public DarkStarvation(...' the DarkStarvation has this error: Invalid method declaration; return type required and the getModEventBus(): Cannot resolve method 'getModEventBus' in 'FMLJavaModLoadingContext' please help, I asked gpt but it is saying that I'm using an old method, but I'm following the latest version of Forge Docs???
    • I merged your second post with the original , there is no need to post a new thread asking for an answer. If someone sees your post and can help, they will reply. If you are seeking a quicker response, you could try asking in the Minecraft Forge diacord.
    • Create a new instance and start with cobblemon - if this works, add the rest of your mods in groups   Maybe another mod is conflicting - like Sodium/Iris or Radical Cobblemon Trainers
    • https://forums.minecraftforge.net/topic/157393-1201-forge-rocket-flame-particle-trail-moves-up-and-crashes-into-the-rocket-during-flight/#comment-584134
  • Topics

×
×
  • Create New...

Important Information

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