Easy to use low-level networking library for Minecraft.
To use NetMinecraft with Gradle/Maven you can get it from Lenni0451's Maven or Jitpack. You can also find instructions how to implement it into your build script there.
If you just want the latest jar file you can download it from GitHub Actions or Lenni0451's Jenkins.
To create a client you can create a new NetClient
instance. The first parameter is a supplier which returns a ChannelHandler
which will be used to handle the packets.
To connect to a server with the client you can use the connect
method. (To resolve SRV records you can use the MinecraftServerAddress
class)
To create a server you can create a new NetServer
instance. The first parameter is a supplier which returns a ChannelHandler
which will be used to handle the incoming connections.
To bind the server to a port you can use the bind
method.
NetMinecraft allows you to either handle packet reading fully yourself or use the built-in packet reading system.
To read packets yourself you don't have to do anything special. NetMinecraft will decompress and decrypt the packets for you and you can read them from the ByteBuf
in the channel handlers.
The built-in packet reading system requires the packets
submodule to be included into your project.
It will allow you to read packets from all version of Minecraft, but only from the Handshake, Status and Login state.
To use the built-in packet reading system you have to overwrite the ChannelInitializer
of the NetClient
or NetServer
instance.
Here is an example for a client:
public class ClientChannelInitializer extends MinecraftChannelInitializer {
public ClientChannelInitializer(final Supplier<ChannelHandler> handlerSupplier) {
super(handlerSupplier);
}
@Override
protected void initChannel(Channel channel) {
super.initChannel(channel);
channel.attr(MCPipeline.PACKET_REGISTRY_ATTRIBUTE_KEY).set(new DefaultPacketRegistry(true, protocolVersion));
}
}
From this point on you will receive Packet
s instead of ByteBuf
s in your channel handlers.
You need to set the connection state of the PacketRegistry
to the correct state yourself.
NetClient client = new NetClient(new Supplier<ChannelHandler>() {
@Override
public ChannelHandler get() {
return new SimpleChannelInboundHandler<ByteBuf>() {
long start;
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
super.handlerAdded(ctx);
start = System.currentTimeMillis();
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
super.channelActive(ctx);
System.out.println("connected");
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
super.channelInactive(ctx);
System.out.println("disconnected");
}
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf) throws Exception {
System.out.println("got response in " + (System.currentTimeMillis() - start) + " ms");
channelHandlerContext.close();
}
};
}
});
ByteBuf handshake = Unpooled.buffer();
PacketTypes.writeVarInt(handshake, 0); // packet id
PacketTypes.writeVarInt(handshake, 47); // protocol version
PacketTypes.writeString(handshake, "localhost"); // server address
handshake.writeShort(25565); // server port
PacketTypes.writeVarInt(handshake, 1); // next state
ByteBuf request = Unpooled.buffer();
PacketTypes.writeVarInt(request, 0); // packet id
client.connect(MinecraftServerAddress.ofResolved("localhost")).syncUninterruptibly(); // blocking connect
client.getChannel().writeAndFlush(handshake);
client.getChannel().writeAndFlush(request);
client.getChannel().closeFuture().syncUninterruptibly();
NetServer server = new NetServer(new Supplier<ChannelHandler>() {
@Override
public ChannelHandler get() {
return new SimpleChannelInboundHandler<ByteBuf>() {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
super.channelActive(ctx);
System.out.println("New connection from " + ctx.channel().remoteAddress());
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
super.channelInactive(ctx);
System.out.println(ctx.channel().remoteAddress() + " closed connection");
}
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf) throws Exception {
// Read packets here
}
};
}
});
server.bind(new InetSocketAddress("0.0.0.0", 25565), true);
final NetServer server = new NetServer(() -> new SimpleChannelInboundHandler<ByteBuf>() {
private Channel otherChannel;
private final NetClient client = new NetClient(new Supplier<ChannelHandler>() {
@Override
public ChannelHandler get() {
return new SimpleChannelInboundHandler<ByteBuf>() {
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf) {
otherChannel.writeAndFlush(byteBuf.retain());
}
};
}
}, channelHandlerSupplier -> new ChannelInitializer<Channel>() {
@Override
protected void initChannel(Channel channel) {
channel.pipeline().addLast(channelHandlerSupplier.get());
}
});
@Override
public void channelActive(ChannelHandlerContext ctx) {
this.otherChannel = ctx.channel();
this.client.connect(MinecraftServerAddress.ofResolved("localhost"));
}
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf) {
this.client.getChannel().writeAndFlush(byteBuf.retain());
}
@Override
public void channelInactive(ChannelHandlerContext ctx) {
this.client.getChannel().close().syncUninterruptibly();
}
}, channelHandlerSupplier -> new ChannelInitializer<Channel>() {
@Override
protected void initChannel(Channel channel) {
channel.pipeline().addLast(channelHandlerSupplier.get());
}
});
server.bind(new InetSocketAddress("0.0.0.0", 25565));