Skip to content

Commit

Permalink
Add simple packet handler
Browse files Browse the repository at this point in the history
  • Loading branch information
rtm516 committed Aug 20, 2024
1 parent 3c19bde commit 864f3b4
Show file tree
Hide file tree
Showing 4 changed files with 363 additions and 65 deletions.
Original file line number Diff line number Diff line change
@@ -1,25 +1,19 @@
package com.rtm516.mcxboxbroadcast.core.webrtc;

import com.rtm516.mcxboxbroadcast.core.webrtc.bedrock.RedirectPacketHandler;
import com.rtm516.mcxboxbroadcast.core.webrtc.encryption.BedrockEncryptionEncoder;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import javax.crypto.SecretKey;
import org.bouncycastle.util.encoders.Hex;
import org.cloudburstmc.protocol.bedrock.codec.BedrockCodec;
import org.cloudburstmc.protocol.bedrock.codec.BedrockCodecHelper;
import org.cloudburstmc.protocol.bedrock.data.DisconnectFailReason;
import org.cloudburstmc.protocol.bedrock.data.PacketCompressionAlgorithm;
import org.cloudburstmc.protocol.bedrock.netty.BedrockPacketWrapper;
import org.cloudburstmc.protocol.bedrock.netty.codec.packet.BedrockPacketCodec;
import org.cloudburstmc.protocol.bedrock.netty.codec.packet.BedrockPacketCodec_v3;
import org.cloudburstmc.protocol.bedrock.packet.BedrockPacket;
import org.cloudburstmc.protocol.bedrock.packet.DisconnectPacket;
import org.cloudburstmc.protocol.bedrock.packet.LoginPacket;
import org.cloudburstmc.protocol.bedrock.packet.NetworkSettingsPacket;
import org.cloudburstmc.protocol.bedrock.packet.RequestNetworkSettingsPacket;
import org.cloudburstmc.protocol.bedrock.packet.ResourcePackClientResponsePacket;
import org.cloudburstmc.protocol.bedrock.packet.ResourcePackStackPacket;
import org.cloudburstmc.protocol.bedrock.packet.TransferPacket;
import org.cloudburstmc.protocol.bedrock.util.EncryptionUtils;
import org.cloudburstmc.protocol.common.util.VarInts;
import pe.pi.sctp4j.sctp.SCTPByteStreamListener;
Expand All @@ -30,6 +24,7 @@ public class MinecraftDataHandler implements SCTPByteStreamListener {
private final SCTPStream sctpStream;
private final BedrockCodec codec;
private final BedrockCodecHelper helper;
private final RedirectPacketHandler redirectPacketHandler;

private BedrockEncryptionEncoder encryptionEncoder;

Expand All @@ -40,12 +35,14 @@ public MinecraftDataHandler(SCTPStream sctpStream, BedrockCodec codec) {
this.sctpStream = sctpStream;
this.codec = codec;
this.helper = codec.createHelper();

this.redirectPacketHandler = new RedirectPacketHandler(this);
}

@Override
public void onMessage(SCTPStream sctpStream, byte[] bytes) {
try {
System.out.println("binary message (" + sctpStream.getLabel() + "): " + Hex.toHexString(bytes));
// System.out.println("binary message (" + sctpStream.getLabel() + "): " + Hex.toHexString(bytes));
if (bytes.length == 0) {
throw new IllegalStateException("Expected at least 2 bytes");
}
Expand All @@ -55,6 +52,12 @@ public void onMessage(SCTPStream sctpStream, byte[] bytes) {

byte remainingSegments = buf.readByte();
if (concat == null) {
if (remainingSegments > 0) {
// TODO Make sure this is correct, and implement on the sending side
// This seems to be included when there are multiple segments
// Seems to always be 0xFF
buf.readByte();
}
expectedLength = VarInts.readUnsignedInt(buf);
}

Expand All @@ -73,62 +76,22 @@ public void onMessage(SCTPStream sctpStream, byte[] bytes) {
concat = null;
}

// if (buf.readableBytes() != expectedLength) {
// System.out.println("expected " + expectedLength + " bytes but got " + buf.readableBytes());
// var disconnect = new DisconnectPacket();
// disconnect.setReason(DisconnectFailReason.BAD_PACKET);
// disconnect.setKickMessage("");
// sendPacket(disconnect, sctpStream);
// return;
// }
if (buf.readableBytes() != expectedLength) {
System.out.println("expected " + expectedLength + " bytes but got " + buf.readableBytes());
var disconnect = new DisconnectPacket();
disconnect.setReason(DisconnectFailReason.BAD_PACKET);
disconnect.setKickMessage("");
sendPacket(disconnect);
return;
}

var packet = readPacket(buf);

if (!(packet instanceof LoginPacket)) {
System.out.println(packet);
System.out.println("C -> S: " + packet);
}

if (packet instanceof RequestNetworkSettingsPacket) {
var networkSettings = new NetworkSettingsPacket();
networkSettings.setCompressionAlgorithm(PacketCompressionAlgorithm.ZLIB);
networkSettings.setCompressionThreshold(0);
sendPacket(networkSettings);
} else if (packet instanceof LoginPacket login) {
// Utils.validateAndEncryptConnection(this, login.getChain(), login.getExtra());

var disconnect = new DisconnectPacket();
disconnect.setReason(DisconnectFailReason.ZOMBIE_CONNECTION);
disconnect.setKickMessage("hhhhehehe");
sendPacket(disconnect);

// var status = new PlayStatusPacket();
// status.setStatus(PlayStatusPacket.Status.LOGIN_SUCCESS);
// sendPacket(status);
//
// var info = new ResourcePacksInfoPacket();
// sendPacket(info);
} else if (packet instanceof ResourcePackClientResponsePacket status) {
switch (status.getStatus()) {
case COMPLETED -> {
var transfer = new TransferPacket();
transfer.setAddress("test.geysermc.org");
transfer.setPort(19132);
sendPacket(transfer);
}
case HAVE_ALL_PACKS -> {
var stack = new ResourcePackStackPacket();
stack.setExperimentsPreviouslyToggled(false);
stack.setForcedToAccept(false);
stack.setGameVersion("*");
sendPacket(stack);
}
default -> {
var disconnect = new DisconnectPacket();
disconnect.setKickMessage("disconnectionScreen.resourcePack");
sendPacket(disconnect);
}
}
}
packet.handle(redirectPacketHandler);
} catch (Exception e) {
e.printStackTrace();
}
Expand All @@ -145,10 +108,11 @@ public void close(SCTPStream sctpStream) {
}

public void sendPacket(BedrockPacket packet) {
System.out.println("S -> C: " + packet);
try {
ByteBuf dataBuf = Unpooled.buffer(128);
int packetId = codec.getPacketDefinition(packet.getClass()).getId();
System.out.println("packet id: " + packetId);
// System.out.println("packet id: " + packetId);
packetCodec.encodeHeader(
dataBuf,
BedrockPacketWrapper.create(packetId, 0, 0, null, null)
Expand All @@ -169,7 +133,7 @@ public void sendPacket(BedrockPacket packet) {

byte[] send = new byte[sendBuf.readableBytes()];
sendBuf.readBytes(send);
System.out.println("sending: " + Hex.toHexString(send));
// System.out.println("sending: " + Hex.toHexString(send));
sctpStream.send(send);
}
} catch (Exception e) {
Expand All @@ -180,7 +144,7 @@ public void sendPacket(BedrockPacket packet) {
private BedrockPacket readPacket(ByteBuf buf) {
BedrockPacketWrapper wrapper = BedrockPacketWrapper.create();
packetCodec.decodeHeader(buf, wrapper);
System.out.println("sender/target: " + wrapper.getSenderSubClientId() + " " + wrapper.getTargetSubClientId());
// System.out.println("sender/target: " + wrapper.getSenderSubClientId() + " " + wrapper.getTargetSubClientId());
var packet = codec.tryDecode(helper, buf.slice(), wrapper.getPacketId());
// release it
wrapper.getHandle().recycle(wrapper);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ private void handleConnectRequest(BigInteger from, String sessionId, String mess
var json = Constants.GSON.toJson(new WsToMessage(
1, from, "CONNECTRESPONSE " + sessionId + " " + answer
));
System.out.println(json);
// System.out.println(json);
send(json);

int i = 0;
Expand All @@ -207,17 +207,17 @@ private void handleConnectRequest(BigInteger from, String sessionId, String mess
1, from, "CANDIDATEADD " + sessionId + " " + candidate.toString() + " generation 0 ufrag " + agent.getLocalUfrag() + " network-id " + i + " network-cost 0"
));
i++;
System.out.println(jsonAdd);
// System.out.println(jsonAdd);
send(jsonAdd);
}

agent.addStateChangeListener(evt -> {
System.out.println("state change! " + evt);
// System.out.println("state change! " + evt);
if ("IceProcessingState".equals(evt.getPropertyName()) && IceProcessingState.COMPLETED.equals(evt.getNewValue())) {
transport.init(component);
try {
var dtlsTransport = new DTLSClientProtocol().connect(client, transport);
Log.setLevel(Log.DEBUG);
// Log.setLevel(Log.DEBUG);

// Log the remote public IP
component.getRemoteCandidates().forEach(remoteCandidate -> {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package com.rtm516.mcxboxbroadcast.core.webrtc.bedrock;

import org.cloudburstmc.nbt.NBTOutputStream;
import org.cloudburstmc.nbt.NbtList;
import org.cloudburstmc.nbt.NbtMap;
import org.cloudburstmc.nbt.NbtMapBuilder;
import org.cloudburstmc.nbt.NbtUtils;

import java.io.ByteArrayOutputStream;
import java.io.IOException;

/**
* This class is mostly copied from Geyser
*/
public class PaletteUtils {
public static final NbtMap BIOMES_PALETTE;
public static final byte[] EMPTY_LEVEL_CHUNK_DATA;

private static final NbtMap EMPTY_TAG = NbtMap.EMPTY;

static {
/* Load biomes */
// Build a fake plains biome entry
NbtMapBuilder plainsBuilder = NbtMap.builder();
plainsBuilder.putFloat("blue_spores", 0f);
plainsBuilder.putFloat("white_ash", 0f);
plainsBuilder.putFloat("ash", 0f);
plainsBuilder.putFloat("temperature", 0f);
plainsBuilder.putFloat("red_spores", 0f);
plainsBuilder.putFloat("downfall", 0f);

plainsBuilder.put("minecraft:overworld_generation_rules", NbtMap.EMPTY);
plainsBuilder.put("minecraft:climate", NbtMap.EMPTY);
plainsBuilder.put("tags", NbtList.EMPTY);

// Add the fake plains to the map
NbtMapBuilder biomesBuilder = NbtMap.builder();
biomesBuilder.put("plains", plainsBuilder.build());

// Build the biomes palette
BIOMES_PALETTE = biomesBuilder.build();

/* Create empty chunk data */
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
outputStream.write(new byte[258]); // Biomes + Border Size + Extra Data Size

try (NBTOutputStream nbtOutputStream = NbtUtils.createNetworkWriter(outputStream)) {
nbtOutputStream.writeTag(EMPTY_TAG);
}

EMPTY_LEVEL_CHUNK_DATA = outputStream.toByteArray();
} catch (IOException e) {
throw new AssertionError("Unable to generate empty level chunk data");
}
}
}
Loading

0 comments on commit 864f3b4

Please sign in to comment.