A minecraft networking library for creating custom protocols that run on the internal netty server.
Sick and tired of hyjacking client connections to send data to and from a proxy? Well I sure was. ProtoWeaver allows you to create a custom protocol that runs on the same port that the minecraft server uses.
This means a proxy such as velocity is able to communicate with the server regardless if players are on online or not, without running an extra socket server on a different port. Protoweaver is also not just
limited to proxies. Using the client module, any 3rd party java application can talk directly with ProtoWeaver!
Protoweaver is fast, secure, and easy to use. All protocols run under the same netty instance used by minecraft, are encypted with ssl, and are wrapped in a very straight forward api. Lets take a look:
- Zero user config required
- SSL encrytion
- Protocol authentication
- Packets as POJO's
- Compression (Gzip, Snappy, LZ)
- All major mod loaders
- API for registering raw protocols (http, ssh, etc)
- Custom serialization
- Custom SSL providers/certs
- Muti-protocol connections
- Tell me what you want to see!
Both tasks combine the classes and resources from common, client, and server into one JAR:
| Command (Windows) | Output in build/libs/ |
Bundled dependencies |
|---|---|---|
.\gradlew.bat :mergedShadowJar |
protoweaver-<version>-all.jar |
All runtime dependencies, including transitive dependencies, with relocated packages |
.\gradlew.bat :mergedJar |
protoweaver-<version>-slim.jar |
Only R, with its package relocated |
The slim JAR requires Fory, Netty, Bouncy Castle, and their runtime dependencies on the application's classpath. Dependency versions are defined in gradle.properties.
JitPack uses JDK 21 to run Gradle; the compiled library still targets Java 17. The build publishes both combined JARs under com.github.zzik2:ProtoWeaverLite:<tag>, with the slim and all classifiers.
repositories {
mavenCentral()
maven { url "https://jitpack.io" }
}
dependencies {
implementation "com.github.zzik2:ProtoWeaverLite:<tag>:all"
// Or use :slim when the application supplies the external dependencies.
}Choose one classifier. The shared POM does not declare external dependencies, so the slim variant requires the dependencies listed above to be provided separately.
Commit these build settings and use a new tag containing them (or its commit hash) as <tag>. Existing tags pointing to earlier commits do not include the JitPack configuration.
To verify the combined publication locally on Windows with JDK 21:
.\gradlew.bat clean build :publishToMavenLocal "-Pgroup=com.github.zzik2" "-Pversion=local-test"build also creates both combined JARs in build/libs/. The leading : in :publishToMavenLocal selects only the root combined publication, which JitPack collects from the local Maven repository.
In your build.gradle include
*** Note: Do not shade protoweaver! It will not work properly. ProtoWeaver is a mod/plugin that is installed along side yours!
repositories {
maven { url "https://maven.mrnavastar.me/releases" }
}
dependencies {
// 'common' can be replaced with any of: `client`, `fabric`, `forge`, `paper` or `proxy`.
implementation "me.mrnavastar.protoweaver:common:1.3.11"
}Protocols are very customizable with a lot of built in functionality. Here is an example protocol:
Protocol protocol = Protocol.create("my_mod_id", "cool_protocol")
.setCompression(CompressionType.GZIP)
.setServerHandler(MyCustomServerHandler.class)
.setClientHandler(MyCustomClientHandler.class)
.setMaxConnections(15)
.addPacket(String.class)
.build();
// Load the protocol before it can be used
ProtoWeaver.load(protocol);Sending packets is extremely simple. Any POJO that has been added to the protocol (See Creating a Protcol) is a valid packet that can be sent. Note that all POJO serialization is handled through kyro, a fast and fast serialization library.
// If you have a reference to a 'ProtoConnection', you can simply:
connection.send(myObject);
// If you have a reference to a 'ProtoClient', you can also:
client.send(myObject);All protocols need handlers in order to implement functionality. The client and server can either use the same handler, or both use different ones.
public class MyCustomServerHandler implements ProtoConnectionHandler {
// Note that all functions are optional to implement
@Override
public void onReady(ProtoConnection connection) {
System.out.println("awesome! looks like a new client has connected from: " + connection.getRemoteAddress());
}
@Override
public void onDisconnect(ProtoConnection connection) {
System.out.println("goodbye: " + connection.getRemoteAddress());
}
@Override
public void handlePacket(ProtoConnection connection, Object packet) {
System.out.println("wow! got: " + packet.toString() + " from: " + connection.getRemoteAddress());
}
}public class MyCustomClientHandler implements ProtoConnectionHandler {
// Note that all functions are optional to implement
@Override
public void onReady(ProtoConnection connection) {
System.out.println("awesome! connected to: " + connection.getRemoteAddress());
}
@Override
public void onDisconnect(ProtoConnection connection) {
System.out.println("goodbye: " + connection.getRemoteAddress());
}
@Override
public void handlePacket(ProtoConnection connection, Object packet) {
System.out.println("saying hi!");
connection.send("Hey server!");
}
}Sometimes it can be useful to send minecraft objects or other premade POJO's in your protocol. To allow this, you can register a custom serializer.
For example, here is a serializer for NBT tags:
public class NbtSerializer implements ProtoSerializer<CompoundTag> {
@Override
public void write(ByteArrayOutputStream buffer, CompoundTag value) {
try {
NbtIo.writeCompressed(value, buffer);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public CompoundTag read(ByteArrayInputStream buffer) {
try {
return NbtIo.readCompressed(buffer, NbtAccounter.unlimitedHeap());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}Then to register the serializer, simply do:
Protocol protocol = Protocol.create("my_mod_id", "cool_protocol")
.addPacket(CompoundTag.class, NbtSerializer.class)
.build();