Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 10 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Commoble/ac7d7b57c9cbbfcae310c4ab110c3cc0 to your computer and use it in GitHub Desktop.
Save Commoble/ac7d7b57c9cbbfcae310c4ab110c3cc0 to your computer and use it in GitHub Desktop.
Custom Dimensions and How You Go In Them in Minecraft Forge 1.14 and 1.15
/**
WHAT ARE WE WORKING WITH
--Dimension, DimensionType, ServerWorld
-- There exists a 1:1:1 relationship between each of these, e.g. there will be one DimensionType instance
and one Dimension instance for a given ServerWorld, and those DimensionType and Dimension instances will only be
used on that worldserver
--ServerWorld
-- The server-specific version of the World, which holds all the block and chunk data, entities, tile entities, etc.\
for a given dimension. You won't be making these yourself or extending the class, but you'll need to find and use the
ServerWorld for your Dimension to teleport the player there.
--Dimension
-- This one was called WorldProvider in 1.12 and earlier. It's mostly the same, and defines most of the properties
of your dimension. You will be extending this class.
--DimensionType
-- A DimensionType instance is essentially just a ResourceLocation with an optional PacketBuffer stapled to it.
DimensionType instances are used to generate and identify Dimension instances;
again, there's a one-to-one relationship here
-- You won't be extending this class; you use the DimensionManager to generate instances for you from that
ResourceLocation and an optional packetbuffer via DimensionManager.registerDimension
--ModDimension
-- Forge wrapper around the vanilla dimension framework. Responsible for generating the Dimension instance
for a given DimensionType. This is also able to pass extra data to the Dimension instance to tweak the
properties of your dimension as needed. You may have one ModDimension instance being responsible for
many dimensions. You will be extending this class.
THE FUNDAMENTAL DIMENSION PIPELINE
-- Extend these classes:
-- public class YourModDimension extends ModDimension
-- public class YourDimension extends Dimension
-- Register an instance of YourModDimension in the ModDimension registry event to make
an ObjectHolder-friendly instance (optional)
-- in the RegisterDimensionEvent FORGE event, call DimensionManager.registerDimension with your YourModDimension
instance and a ResourceLocation to get a DimensionType instance for that ResourceLocation
(you can optionally give it a PacketBuffer with extra data for your YourDimension as well
-- your ModDimension defines a factory that uses a DimensionType instance to generate an instance of YourDimension
(if you registered the DimensionType with a data packet, you have access to that data here and can tweak your
YourDimension instance as needed)
-- finally, your YourDimension instance is responsible for defining all the physical and visual
properties of your dimension. In particular, it defines the ChunkGenerator that generates chunks for
your dimension.
-- To summarize:
*DimensionManager uses ResourceLocation + ModDimension instance + PacketBuffer to generate DimensionType instance
*ModDimension instance uses DimensionType instance to generate Dimension instance
*Dimension instance defines the ChunkGenerator and other things that determine what your dimension's world has in it
WHAT ELSE DO WE WANT
-we probably also want at least one Biome, a ChunkGenerator, and maybe some Feature/Placements for the biome
HOW DO I REGISTER THESE THINGS
-Start with the ModDimension:
**/
// ModDimensions are a Forge wrapper around the vanilla dimension framework; if you intend to have a variety of dimensions
// with the same worldgen behaviour but different variations or pseudoseeds, you can use one ModDimension to dole out Dimensions
@EventBusSubscriber(modid = "yourmodid", bus = Bus.MOD)
public class ModDimensionRegistrar
{
@ObjectHolder("yourmodid:yourdimension")
public static final ModDimension YOUR_MOD_DIMENSION = null;
@SubscribeEvent
public static void onDimensionRegistryEvent(RegistryEvent.Register<ModDimension> event)
{
event.getRegistry().register(new YourModDimension().setRegistryName("yourmodid:yourdimension");
}
}
public class YourModDimension extends ModDimension
{
@Override
public BiFunction<World, DimensionType, ? extends Dimension> getFactory()
{
return YourDimension::new;
}
}
// You can see the ModDimension must implement a method that returns a Dimension based on a World and a DimensionType,
// so we'll need a Dimension as well
// Dimension is essentially the old WorldProvider from 1.12, with the caveat that they must be unique per world/level
// --(you can use one ModDimension for multiple very similar worlds/levels if you need that sort of thing)
public class YourDimension extends Dimension
{
public YourDimension(World worldIn, DimensionType typeIn)
{
super(worldIn, typeIn);
}
@Override
public ChunkGenerator<?> createChunkGenerator()
{
// your Chunk Generator tells your dimension what to put in each chunk
// it's important but also complicated
// check out the vanilla chunkgenerators to try to see how they work
}
// there's a bunch of things from Dimension you'll need to override;
// if you're familiar with 1.12 WorldProviders
// then they'll all look very similar, we'll ignore them here for brevity's sake
}
// we'll also need a DimensionType registered
// note that this event fires on the FORGE bus, not the MOD bus
@EventBusSubscriber(modid="yourmodid", bus=Bus.FORGE)
public class CommonForgeEventHandler
{
public static final ResourceLocation DIMENSION_TYPE_RL = new ResourceLocation("yourmodid", "yourdimension");
@SubscribeEvent
public static void onRegisterDimensionsEvent(RegisterDimensionsEvent event)
{
// the first argument here is a resource location for your dimension type
// the second argument here is the ModDimension that your DimensionType uses
// the third argument here is an optional PacketBuffer with extra data you want to pass
// to your DimensionType, which is in turn passed to your Dimension
// which allows you to define properties of your Dimension when you register this
// the fourth argument determines skylight for some reason
// we'll also need to add a check to prevent the dimension from being registered more than once
if (DimensionType.byName(DIMENSION_TYPE_RL) == null)
{
DimensionManager.registerDimension(DIMENSION_TYPE_RL, ModDimensionRegistrar.YOUR_MOD_DIMENSION, null, true);
}
// this returns a DimensionType for your ResourceLocation; alternatively you can also retrieve the dimensiontype with
// DimensionType.byName(ResourceLocation)
}
}
// the following block is no longer relevant as of forge build 1.14.4-28.1.35;
// the recommended forge build for minecraft 1.14 is still 28.1.0, so this will remain here for now
/**
IMPORTANT SIDE NOTE FOR FORGE BUILDS 28.1.0 through 28.1.34
Forge apparently doesn't properly sync dimensions to the client
https://github.com/MinecraftForge/MinecraftForge/issues/6147
Cryptic Mushroom's Midnight mod has a handshake packet that fixes this licensed under GPL 3
https://github.com/Cryptic-Mushroom/The-Midnight/blob/f33d483012e07107c238af96dd9940ccab555cc4/src/main/java/com/mushroom/midnight/common/registry/MidnightDimensions.java
**/
// HOW TO GET TO YOUR DIMENSION
public static void teleportPlayer(ServerPlayerEntity player, DimensionType destinationType, BlockPos destinationPos)
{
ServerWorld nextWorld = player.getServer().getWorld(destinationType);
nextWorld.getChunk(destinationPos); // make sure the chunk is loaded
player.teleport(nextWorld, destinationPos.getX(), destinationPos.getY(), destinationPos.getZ(), player.rotationYaw, player.rotationPitch);
}
// TODO
// putting stuff inside your dimension
@Draco18s
Copy link

L79: That won't work. When you save the world and reload it, that line will throw an error: duplicate key.

You have to check to see if your dim is already registered first:
https://github.com/Draco18s/ReasonableRealism/blob/1.14.4/src/main/java/com/draco18s/industry/ExpandedIndustry.java#L175

@williewillus
Copy link

williewillus commented Sep 21, 2019

Maybe just a note to define all the terminology up front at the top of the gist:

  • DimensionType: Unique handle to a world. There is one WorldServer for each DimensionType. Each DimensionType has a string ID like minecraft:overworld, that replaces the old int world ids. The name is a bad one since it's not a type, it's a unique handle.
  • Dimension: Implements a lot of logic for each dimension like the horizon height, sky color, etc. Used to be called WorldProvider. There's one per world just like DimensionType, but these have no name/ID and thus aren't uniquely identifiable.
  • ModDimension: Forge class, a template to create new Dimension. There need not be a 1:1 correspondence between this and worlds/DimensionTypes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment