Fabric API

Fabric API

152M Downloads

Fabric Networking API Considerations

inglettronald opened this issue · 6 comments

commented

Problem statement

It's understood in Minecraft's networking API, and also fabrics own networking API, that the relationship between identifiers and handlers is that mods should only register one handler per identifier.

In GlobalRecieverRegistry:

private final Map<Identifier, H> handlers = new HashMap<>();
// ... 
	public boolean registerGlobalReceiver(Identifier channelName, H handler) {
		Objects.requireNonNull(channelName, "Channel name cannot be null");
		Objects.requireNonNull(handler, "Channel handler cannot be null");

		if (NetworkingImpl.isReservedCommonChannel(channelName)) {
			throw new IllegalArgumentException(String.format("Cannot register handler for reserved channel with name \"%s\"", channelName));
		}

		assertPayloadType(channelName);

		Lock lock = this.lock.writeLock();
		lock.lock();

		try {
			final boolean replaced = this.handlers.putIfAbsent(channelName, handler) == null;

			if (replaced) {
				this.handleRegistration(channelName, handler);
			}

			return replaced;
		} finally {
			lock.unlock();
		}
	}

However, I have run into some issues regarding making implementations of Hypixel's Mod API.

At it's core, this is something that was introduced by Hypixel with the primary goal of providing a better option for parsing in-server locations than the current standard (using automated /locraw commands sent and managed by various mods). This, ideally, would be served by people including the jar in their folder provided by Hypixel, and devs declaring dependency on those mods. However, for a number of reasons people see fit to implement their own bespoke solutions for this.

  • Hypixel has maintained support for all the latest versions of Minecraft since it released, but not for all versions that the server supports. The server still maintains the same network protocol on those versions regarding the ModAPI.
  • Some devs have decided they want their own interfaces for these packets, and made their own implementation.

This brought up an issue. If there's some divide or disagreement on how different projects want to interface with/interpret these packets that Hypixel are offering, there is currently not a clean or accepted solution. The project I linked has their own mixins and wrappers to allow it to exist with Hypixel's official solution, but I think this solution could be made a lot cleaner and less invasive if Fabric's API had some functionality for this, since fabric has the reach to do this sort of thing.

The main point of this issue:

If a PR was offered to the Fabric API to provide an implementation to allow multiple mods to implement their own handling logic for a given packet, would that be within scope for this project? If so, how would we like this implementation to look?

commented

Thanks for the (super quick) response! I don't immediately have a solution in mind (wanted to judge the waters on whether or not the high level idea fit within this project first), but I'll be sure to set aside some time to see if I can come up with something that is working well and non-invasive.

commented

Hey this is a really intresting use case, that you are right in saying isnt supported by the current networking API.

I dont think this is going to be super easy, as its more than just allowing 2 handlers to be registered, we would also need to allow the packet to be copied and then decoded into 2 or more payloads all of this currently happens within Minecraft's networking code, none of it is setup in a way where you can have 1 packet to many payloads. See CustomPayload.createCodec as a starting point.

Im struggling to see how this could even be done without a vast amount of changes to Minecraft's networking code, something we dont really want. Have you got an alternative solution in mind?

commented

I dont have anything against it, other than it possibly being very complex/hard to maintain. I think (Im open to discuss this) all the mods should be in agreement that the packet can be shared, meaning the current API stays the same but there would be a seperate way to register a payload and handler that supports this. Doing that might also make things a bit easier as there could be 1 real payload (that just decodes to a byte[]) and then that calls the handlers registered by mods.

I think this is something that would be nice to have, however I do think its worth discussing if it does really make sense. The alternative would be for there to be a common library that all mods could JIJ, it seems like Hypixel already has an official Fabric mod for this here: https://github.com/HypixelDev/FabricModAPI What is stopping everyone from using this?

commented

As for the reasoning of why there is an alternative API, I can't speak for the authors of the alternative hypixel API directly, but it does have a different style of interacting with the packets (a single sealed parent class that should be pattern matched vs registering for specific classes directly). Nowadays, I would say that this should be abstracted over the official hypixel API jar, but at the time it wasn't very clear how much of that API was just going to be a reference implementation, and how frequently they would update the official mod api to newer versions.

Initially those two APIs were mutually exclusive (due to the fabric API not allowing for multiple registrations on the same channel), but I ended up writing (in collaboration with aaron) a (bit of a messy) solution to parsing a packet twice: We wrap the packet codec (similar to how fabric does), but instead of just replacing the original codec in the encode / decode methods, we directly modify the decode method and clone the byte buffer using ByteBuf#slice, in order to be decoded multiple times. We then package those combined packets up into a JoinedCustomPayload which is just a collection of multiple custom payloads and then have another mixin that dispatches each element of the JoinedCustomPayload once it reaches the packet handler.

I'm not sure if this exact infrastructure is fit for the fabric API (obviously the Fabric API would need things like arrays in the joined custom payload instead of just two fields, etc., but even beyond that), but it has lasted about a year without any required changes.

commented

Nowadays, I would say that this should be abstracted over the official hypixel API jar, but at the time it wasn't very clear how much of that API was just going to be a reference implementation, and how frequently they would update the official mod api to newer versions.

I'd also like to chime in here and reiterate that Hypixel's official mod implementation doesn't cover launching on some versions that they support, (i.e: 1.19 clients can still send/receive the packets, but they only started providing jars on 1.20.5+ fabric and 1.8.9 forge.

The counterpoint to this argument is that if there was significant demand for jars on these versions, they'd probably be developed by at least someone! However, what an API like this in mod loaders could do is allow servers to simply provide the netty codecs, and not have to maintain a parallel client-side jar.

commented

Hi folks,

I'd like to offer another, similar, use case where the current implementation causes significant headache: the BungeeCord & Velocity plugin message channel. A brief explanation: BungeeCord (and Velocity, and compatible proxies) offer a channel (bungeecord:main) that allows backend plugins/mods to:

  • change the server a player is connected to
  • query information about servers on the proxy, by forwarding requests via player connections
  • forward arbitrary information between servers via player connections

This functionality is pivotal to the creation of plugins (and now, mods) that work across a network of servers. Link to the specification: docs

At present, with the current design of the networking API, since you can only register one CustomPayload implementation per channel, the use of this channel is effectively limited to one mod per server. This therefore mandates either the use of mixins to hack around these limitations, or a common API mod to handle all plugin message requests, with a callback structure for listening mods to process plugin messages byte-by-byte. The first solution is not ideal in my opinion; the second I fear falls under XKDC 927 :-(.

Initially my suggestion would be to implement API for this specific specification in Fabric, but seeing as this issue also exists for similar use cases, perhaps a simple approach could be to simply expose API to allow (multiple) mods to register and send & listen for bytes directly on channels, by identifier (here's the Paper API for this, for instance, though I understand perhaps this is not considered ideal API for this project).