Fabric API

Fabric API

106M Downloads

Custom Dimensions

modmuss50 opened this issue ยท 6 comments

commented

Custom Dimensions

After messing around with dims for the last day or so I have found a few places that might be a good candidate to be handled by fabric.

TL;DR at bottom of this issue

Creation of the SecondaryServerWorld

In MinecraftServer.createWorlds, SecondaryServerWorld's are created for the end and the neather. As well as adding them to the MinecraftServer.worlds map. This would be an easy thing to patch. An API would need to be created to allow modders to register their DimensionType's to be created

Creating a new DimensionType

Currently DimensionType has a protected constructor, I think the best solution to this is to create a builder (similar to the item groups) This builder would take an identifier and handle some things the modder shouldn't need to touch such as the saveDir

One possible idea would be to remove the need to speicfy a unique dim id, this needs to be looked into some more as the dim id's would need to be synced from the server to the client on connection. The id's seem to be used in very few places so I think this could be feasible without too much work.

ChunkGeneratorType

ChunkGeneratorType is used by the Dimension to create generate chunks, easy enough. ChunkGeneratorType.register could do with an AT, this is the least of the issues however.

ChunkGeneratorFactory is package private, this makes creating ChunkGeneratorType's a pain, to work around this I moved this code into the net.minecraft.world.gen.chunk package, however this is bad on a few levels, and will not work outside of dev. This class should be AT'ed to be public.

Entity Teleportation and Portal Creation (Very WIP)

One area that I think would befit from an API in fabric would be allowing a way to bypass the code located in class_1946. class_1946 is currently hard coded to handle the nether portal, however it gets called when traveling to custom dimensions.

To slove this issue I came up with the following interface: (Very rough API and bad java docs)

public interface PlayerPlacementHandler {

	/**
	 *
	 * This is used to create a portal if needed, as well as set the player location in the new dim
	 *
	 * when you return true no other placement handlers will be called when the player is moving
	 *
	 * @param entity the entity that is traveling between 2 dims
	 * @param previousWorld the world that the entity is traveling from
	 * @param newWorld the world that the entity is traveling true
	 * @return if the placement handler handled this movement
	 */
	boolean placeInPortal(Entity entity, ServerWorld previousWorld, ServerWorld newWorld);

	//TODO move out of this?
	default void setEntityLocation(Entity entity, BlockPos pos){
		if (entity instanceof ServerPlayerEntity) {
			((ServerPlayerEntity)entity).networkHandler.method_14363(pos.getX(), pos.getY(), pos.getZ(), 0, 0);
			((ServerPlayerEntity)entity).networkHandler.method_14372();
		} else {
			entity.setPositionAndAngles(pos.getX(), pos.getY(), pos.getZ(), 0, 0);
		}
	}

}

The placeInPortal method is called whenever an entity changes dimension (including vanilla), this idea is to allow mods to handle teleporting to and from their own dims, as well enabling mods to replace the functionality of telporting to vanilla dims if required.

The setEntityLocation is added as a helper method to set the player's location correctly, this was based of the code found in class_1946

Bellow is the example mixin targeting PlayerManager to implement PlayerPlacementHandler

@Mixin(PlayerManager.class)
public class MixinPlayerManager {

	@Inject(method = "method_14558", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/Profiler;begin(Ljava/lang/String;)V", shift = At.Shift.BEFORE), cancellable = true)
	public void method_14558(Entity entity, DimensionType dimensionType, ServerWorld previousWorld, ServerWorld newWorld, CallbackInfo info) {
		for (PlayerPlacementHandler playerPlacementHandler : DimAPI.playerPlacementHandlerList) {
			if (playerPlacementHandler.placeInPortal(entity, previousWorld, newWorld)) {

				newWorld.spawnEntity(entity);
				newWorld.method_8553(entity, false);
				entity.setWorld(newWorld);

				info.cancel();
				break;
			}
		}

	}

}

Issues with this idea:

If a mod does not handle the teleportation it will pass it onto class_1946 and the game will most likely crash. Do we need an API that holds the hand of the modder a bit more? (For example requiring them to handle all teleport to their dim?)

TL;DR

  • Patch MinecraftServer.createWorlds to handle the creation of SecondaryServerWorld

  • Create a builder around DimensionType, and possibly handle auto dim id assigning?

  • Add some AT's to ChunkGeneratorType and ChunkGeneratorFactory to make them easily accessible

  • Create an API in fabric to allow modder's to handle entity location and portal creation. Possibly find a way to stop it crashing if not handled correctly?

Conclusion

I think there are a few places where Fabric can help with the creation of modded dims, however im not sure if all of these places should be handled by fabric, or handled by the mods (Mainly thinking about the places I suggest AT'ing when possible)

One of the things that I think needs more thinking over is the placement / teleportation code as right now it is very easy to get it wrong and crash the game.

Finally, all feedback will be greatly appreciated, thanks for spending your time reading this.

commented

The current things you have outlined all seem appropriate. I have a few additional requests I would like to add on, however they may be out of scope for this discussion. If that's the case then it's fine to discuss these later.

If we are patching this part of the game, would it be appropriate to add a new event that is fired when the dimension of an entity is changed, and potentially allow the teleportation to be canceled? I make use of a similar feature in my mod DimensionStages to allow dimension access to be restricted with a custom progression system. While my mod is mainly a modpack tool, I could easily see other dimension mods preventing access to their dimensions until a player has unlocked an advancement, or only making it accessible at certain times of the day in game.

Many existing dimension mods make use of various hooks to customize the attributes and atmosphere. Typically these hooks provide a way the world to delegate some of it's methods to the dimension. For example, a dimension may want to provide it's own sky renderer, or have different colors for fog and clouds. Would Fabric be able to add these hooks into the game?

commented

If there is no way to patch class_1946 to prevent the crashing, then the API should require modders to handle teleportation to their dims.

commented

You could fix class_1946 to not crash, but the mechanics of it are largely useless as it's designed for spawning either a Nether or End Portal at the spawn point which is far from helpful for a custom dimension. If anything the most useful method for teleporting is TeleportCommand#method_13766, which besides from being private handles teleporting any entity statically with various handling for setting facings if you so desired.

You can quite easily avoid forcing needing to implement any custom dimension teleporting at all so that the only way to enter and leave is to use commands, people are then free to add whatever portal/teleporting mechanic they want (or nothing at all if it isn't designed for the player to ever go to). Blindly teleporting to modded dims through Entity#changeDimension or PlayerManager#method_14558 would leave the responsible code blindly open to crashes, but this will always be true unless there is a single central place responsible for handling cross-dimensional entity teleportation (which would have to be used instead) or every result of ServerWorld#method_14173 producing a helpful teleporter for the world (which right now would require replacing field_13956 for additional worlds). Said place could also house a teleportation event if one was wanted.

As for dimension customisation itself, it would logically depend on allowing an extension of ServerWorld to be used in place of just using SecondaryServerWorld. Sure you could inject into the appropriate methods and check dimension but at that point every world implementation is suffering from having no better options.

commented

Can we just override setPositionAndAngles for ServerPlayerEntity to auto send to network handler? That would prevent duplicate code in cross dimensional placement handler.

commented

This has been done for ages.

commented

People keep failing at getting dimension apis merged lol