Add very basic Java MC server impl

This commit is contained in:
2024-04-09 01:47:28 -04:00
parent dd2ce094d2
commit e606a36145
9 changed files with 333 additions and 0 deletions

View File

@ -0,0 +1,89 @@
/*
* MIT License
*
* Copyright (c) 2024 Braydon (Rainnny).
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package cc.fascinated.common.packet;
import lombok.NonNull;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* Represents a packet in the
* Minecraft Java protocol.
*
* @author Braydon
* @see <a href="https://wiki.vg/Protocol">Protocol Docs</a>
*/
public abstract class MinecraftJavaPacket {
/**
* Process this packet.
*
* @param inputStream the input stream to read from
* @param outputStream the output stream to write to
* @throws IOException if an I/O error occurs
*/
public abstract void process(@NonNull DataInputStream inputStream, @NonNull DataOutputStream outputStream) throws IOException;
/**
* Write a variable integer to the output stream.
*
* @param outputStream the output stream to write to
* @param paramInt the integer to write
* @throws IOException if an I/O error occurs
*/
protected final void writeVarInt(DataOutputStream outputStream, int paramInt) throws IOException {
while (true) {
if ((paramInt & 0xFFFFFF80) == 0) {
outputStream.writeByte(paramInt);
return;
}
outputStream.writeByte(paramInt & 0x7F | 0x80);
paramInt >>>= 7;
}
}
/**
* Read a variable integer from the input stream.
*
* @param inputStream the input stream to read from
* @return the integer that was read
* @throws IOException if an I/O error occurs
*/
protected final int readVarInt(@NonNull DataInputStream inputStream) throws IOException {
int i = 0;
int j = 0;
while (true) {
int k = inputStream.readByte();
i |= (k & 0x7F) << j++ * 7;
if (j > 5) {
throw new RuntimeException("VarInt too big");
}
if ((k & 0x80) != 128) {
break;
}
}
return i;
}
}

View File

@ -0,0 +1,64 @@
package cc.fascinated.common.packet.impl.java;
import cc.fascinated.common.packet.MinecraftJavaPacket;
import lombok.AllArgsConstructor;
import lombok.NonNull;
import lombok.ToString;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* This packet is sent by the client to the server to set
* the hostname, port, and protocol version of the client.
*
* @author Braydon
* @see <a href="https://wiki.vg/Protocol#Handshake">Protocol Docs</a>
*/
@AllArgsConstructor @ToString
public final class JavaPacketHandshakingInSetProtocol extends MinecraftJavaPacket {
private static final byte ID = 0x00; // The ID of the packet
private static final int STATUS_HANDSHAKE = 1; // The status handshake ID
/**
* The hostname of the server.
*/
@NonNull private final String hostname;
/**
* The port of the server.
*/
private final int port;
/**
* The protocol version of the server.
*/
private final int protocolVersion;
/**
* Process this packet.
*
* @param inputStream the input stream to read from
* @param outputStream the output stream to write to
* @throws IOException if an I/O error occurs
*/
@Override
public void process(@NonNull DataInputStream inputStream, @NonNull DataOutputStream outputStream) throws IOException {
try (ByteArrayOutputStream handshakeBytes = new ByteArrayOutputStream();
DataOutputStream handshake = new DataOutputStream(handshakeBytes)
) {
handshake.writeByte(ID); // Write the ID of the packet
writeVarInt(handshake, protocolVersion); // Write the protocol version
writeVarInt(handshake, hostname.length()); // Write the length of the hostname
handshake.writeBytes(hostname); // Write the hostname
handshake.writeShort(port); // Write the port
writeVarInt(handshake, STATUS_HANDSHAKE); // Write the status handshake ID
// Write the handshake bytes to the output stream
writeVarInt(outputStream, handshakeBytes.size());
outputStream.write(handshakeBytes.toByteArray());
}
}
}

View File

@ -0,0 +1,62 @@
package cc.fascinated.common.packet.impl.java;
import cc.fascinated.common.packet.MinecraftJavaPacket;
import lombok.Getter;
import lombok.NonNull;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* This packet is sent by the client to the server to request the
* status of the server. The server will respond with a json object
* containing the server's status.
*
* @author Braydon
* @see <a href="https://wiki.vg/Protocol#Status_Request">Protocol Docs</a>
*/
@Getter
public final class JavaPacketStatusInStart extends MinecraftJavaPacket {
private static final byte ID = 0x00; // The ID of the packet
/**
* The response json from the server, null if none.
*/
private String response;
/**
* Process this packet.
*
* @param inputStream the input stream to read from
* @param outputStream the output stream to write to
* @throws IOException if an I/O error occurs
*/
@Override
public void process(@NonNull DataInputStream inputStream, @NonNull DataOutputStream outputStream) throws IOException {
// Send the status request
outputStream.writeByte(0x01); // Size of packet
outputStream.writeByte(ID);
// Read the status response
readVarInt(inputStream); // Size of the response
int id = readVarInt(inputStream);
if (id == -1) { // The stream was prematurely ended
throw new IOException("Server prematurely ended stream.");
} else if (id != ID) { // Invalid packet ID
throw new IOException("Server returned invalid packet ID.");
}
int length = readVarInt(inputStream); // Length of the response
if (length == -1) { // The stream was prematurely ended
throw new IOException("Server prematurely ended stream.");
} else if (length == 0) {
throw new IOException("Server returned unexpected value.");
}
// Get the json response
byte[] data = new byte[length];
inputStream.readFully(data);
response = new String(data);
}
}