Introduction
ViaVersion is a general-purpose protocol translation library for Minecraft, not just a Paper plugin. Its architecture is independent of specific server software or mod loaders, allowing it to be implemented in virtually any Minecraft-related application.
By default, the base ViaVersion JAR supports Paper and Velocity. Other environments—such as Fabric, NeoForge/Forge, or Sponge—use separate, platform-specific loader projects.
The bridge between ViaVersion and a server software or mod loader is called a platform. This guide explains how to implement such a platform from scratch.
Getting Started
Including ViaVersion
Any ViaVersion platform must comply with the GNU General Public License v3, as the viaversion-common artifact is required.
Refer to the Basic API usage documentation for the Maven coordinates. Ensure that you depend on viaversion-common, as it provides access to the required internal APIs.
Implicit Required Dependencies
ViaVersion depends on several libraries that must be present at runtime:
- Netty (
io.netty:netty-all:4.0.20.Finalor newer) Used for packet handling and Netty pipeline injection - Gson (
com.google.code.gson:gson:2.x) Used for JSON serialization and deserialization - Guava (
com.google.guava:guava:17.0or newer) Provides utility classes used throughout the project
These dependencies are typically already provided by Minecraft servers and proxies.
Add-ons
ViaVersion allows newer clients to join older server versions. The opposite direction, as well as more specialized versions (such as April Fools snapshots), requires additional add-ons:
- ViaBackwards — Allows older clients to join newer server versions (requires ViaVersion)
- ViaRewind — Allows 1.8.x and 1.7.x clients to join 1.9+ servers (requires ViaBackwards)
- ViaLegacy — Allows clients to join servers running versions ≤ 1.7.10 (requires ViaVersion)
- ViaAprilFools — Allows clients to join selected Minecraft snapshot versions (requires ViaBackwards)
- ViaBedrock — Allows clients to join Bedrock Edition servers (requires the latest ViaVersion snapshot version)
Creating a Platform Implementation
Platform Base Class
Create a class named ProjectNamePlatform.
It is recommended to place all ViaVersion-related classes in a dedicated package, for example:
com.yourproject.viaversioncom.yourproject.protocoltranslator
Overrides of default classes should be prefixed with your project name:
public class ProjectNameInjector implements ViaInjector {}
Single-connection platforms
Extend UserConnectionViaVersionPlatform if your platform:
- Does not have a custom player abstraction
- Handles one connection per Netty channel
Multi-connection platforms
If your platform supports multiple connections (for example, a proxy or backend server) and has its own player object, implement:
ViaPlatform<ProjectPlayer>
This allows API methods in Via.getAPI() to directly accept your player object.
Caution
This guide focuses on
UserConnectionViaVersionPlatform. For multi-connection platforms, additional methods must be implemented. See existing implementations here and here.
Some official implementations predate the current architecture and may not fully follow this guide.
Example Platform Implementation
public final class TestPlatform extends UserConnectionViaVersionPlatform {
public TestPlatform() {
// Folder for configuration files
super(new File("ViaVersion"));
}
@Override
public Logger createLogger(final String name) {
// Required for ViaBackwards and other add-ons to have their own logger
return Logger.getGlobal();
}
@Override
public boolean isProxy() {
// true:
// - Proxy platforms (Velocity, BungeeCord)
// - Client-side platforms (Fabric, Forge)
//
// false:
// - Server-side platforms (Spigot, Paper)
return true;
}
@Override
public String getPlatformName() {
return "Test"; // Project name
}
@Override
public String getPlatformVersion() {
return "test"; // Project version
}
}
Version Provider
Create a class that implements ViaPlatformLoader to register platform-specific providers:
public final class TestPlatformLoader implements ViaPlatformLoader {
@Override
public void load() {
Via.getManager().getProviders().use(VersionProvider.class, new BaseVersionProvider() {
@Override
public ProtocolVersion getClosestServerProtocol(UserConnection connection) {
// Change the logic here to select the target server version
return ProtocolVersion.v1_8;
}
});
}
@Override
public void unload() {
}
}
Providers allow the common ViaVersion code to interact with platform-specific features or retrieve data that can only be accessed through platform APIs.
The only mandatory provider is VersionProvider, which determines the target protocol version for each connection.
Initializing ViaVersion
The initAndLoad Call
Once your platform and (optional) platform loader are implemented, initialize ViaVersion:
ViaManagerImpl.initAndLoad(
new TestPlatform(),
new NoopInjector(),
new ViaCommandHandler(false /* if true, a command for toggling check-for-updates in ViaVersion's config will be registered */),
new TestPlatformLoader()
);
This bootstraps all internal ViaVersion systems.
You can verify successful initialization with:
Via.isLoaded();
Netty Modifications
ViaVersion must be injected into the Netty pipeline of each player connection in order to intercept and modify packets. In the example above, a NoopInjector was passed to initAndLoad, which performs no injection. This injector is intended for platforms that either have direct API access to modify the pipeline or use alternative injection mechanisms (for example, Mixins or ASM).
If your platform targets a server or proxy where reflection-based injection is required, review the ViaInjector interface and the Bukkit or Velocity implementations for reference.
Decoder/Encoder vs. Codec Pipeline
ViaVersion supports two approaches for Netty pipeline injection:
- Separate decoder and encoder handlers (
ViaDecodeHandlerandViaEncodeHandler) - A single codec handler for both directions (
ViaCodecHandler)
The appropriate approach depends on the software you are integrating with. If the target software uses a split pipeline (such as Spigot or Paper), you should also use the split approach.
When using the codec approach, extend NoopInjector with a custom implementation:
public final class TestInjector extends NoopInjector {
@Override
public String getEncoderName() {
return ViaCodecHandler.NAME;
}
@Override
public String getDecoderName() {
return ViaCodecHandler.NAME;
}
}
Both methods must return the name of the pipeline handler. By default, ViaVersion uses ViaEncodeHandler.NAME and ViaDecodeHandler.NAME.
Injecting into the Pipeline
// clientside = true: proxy software (frontend connection, client <-> proxy) or client mods
// clientside = false: proxy software (backend connection, proxy <-> server) or server software
final UserConnection connection = ViaChannelInitializer.createUserConnection(channel, true /* clientside */);
final ViaInjector injector = Via.getManager().getInjector();
channel.pipeline().addBefore("packet_decoder", injector.getDecoderName(), new ViaDecodeHandler(connection));
channel.pipeline().addBefore("packet_encoder", injector.getEncoderName(), new ViaEncodeHandler(connection));
//channel.pipeline().addBefore("packet_codec", injector.getEncoderName(), new ViaCodecHandler(connection));
If you are using ViaLegacy, consult its README for required Netty pipeline changes.
Caution
Depending on your integration point, you may need to ensure that the pipeline maintains the correct handler order. Vanilla Minecraft clients and servers modify the pipeline when compression handlers are added. You must ensure that the Via* handlers remain in the correct positions. See existing implementations here and here.
Moving Forward
This guide covers only the minimum requirements to get ViaVersion running on a new platform.
Additional features—such as command registration and configuration management—may require further implementation to function fully.
UserConnectionViaVersionPlatform (or ViaPlatform<T> with only the mandatory overrides) omits several optional methods that must be implemented to achieve full functionality.
For example, the Server & Player Details Protocol requires overriding the sendCustomPayload and sendCustomPayloadToClient methods in your platform class.
Most internal components include JavaDoc to aid understanding. We also recommend reviewing existing implementations within our GitHub organization or seeking help on our Discord server.