minerecon/packetcreator.go
2024-08-22 19:33:17 +02:00

460 lines
11 KiB
Go

package main
import (
"bufio"
"bytes"
"compress/zlib"
"crypto/md5"
"encoding/binary"
"encoding/json"
"errors"
"fmt"
"io"
"math"
"strings"
"time"
)
// Decompress function using zlib
func decompress(data []byte) ([]byte, error) {
reader, err := zlib.NewReader(bytes.NewReader(data))
if err != nil {
return nil, err
}
defer func(reader io.ReadCloser) {
_ = reader.Close()
}(reader)
var decompressedData bytes.Buffer
_, err = io.Copy(&decompressedData, reader)
if err != nil {
return nil, err
}
return decompressedData.Bytes(), nil
}
// Compress function using zlib
func compress(data []byte) ([]byte, error) {
var compressedData bytes.Buffer
writer := zlib.NewWriter(&compressedData)
_, err := writer.Write(data)
if err != nil {
return nil, err
}
err = writer.Close()
if err != nil {
return nil, err
}
return compressedData.Bytes(), nil
}
func readPacket(reader *bufio.Reader, threshold int32) (packetID int32, packetData []byte, packetLength int, err error) {
packetLengthTemp, _, err := readVarint(reader)
if err != nil {
return
}
packetLength = packetLengthTemp
packetData = make([]byte, packetLength)
n, err := io.ReadFull(reader, packetData)
if err != nil {
return
}
if n != packetLength {
err = errors.New("packet read length mismatch")
return
}
var dataLength int
if threshold > 0 {
n, dataLengthTemp := receiveVarint(packetData)
packetData = packetData[n:]
dataLength = int(dataLengthTemp)
}
// If dataLength > 0, it means the packet is compressed
if dataLength > 0 {
packetData, err = decompress(packetData)
if err != nil {
return
}
}
n, packetID = receiveVarint(packetData)
packetLength -= n
packetData = packetData[n:]
return
}
func createPacket(packetID int32, packetData []byte, threshold int32, startedCompression bool) ([]byte, error) {
var dataBuffer []byte
addVarint(&dataBuffer, packetID)
dataBuffer = append(dataBuffer, packetData...)
var outBuffer []byte
length := int32(len(dataBuffer))
if startedCompression {
if threshold > 0 && length >= threshold {
// Compress the packet
compressedData, err := compress(dataBuffer)
if err != nil {
return nil, err
}
dataBuffer = compressedData
// Add the uncompressed length
addVarint(&outBuffer, int32(len(packetData)))
} else {
// Set Data Length to 0 if not compressed
addVarint(&outBuffer, 0)
}
}
addVarint(&outBuffer, int32(len(dataBuffer)))
// Append compressed or uncompressed data
outBuffer = append(outBuffer, dataBuffer...)
// Add the Packet Length
return outBuffer, nil
}
func addString(buffer *[]byte, str string) {
length := len(str)
addVarint(buffer, int32(length))
*buffer = append(*buffer, str...)
}
// Reads a VarInt from a bufio.Reader and returns the decoded value, the length read, and an error if any.
// Only negative values are transformed similar to Java's implementation.
func readVarint(r *bufio.Reader) (value int, length int, err error) {
var position uint
value = 0
for {
currentByte, err := r.ReadByte()
if err != nil {
return 0, length, err
}
value |= int(currentByte&0x7F) << position
length++
if (currentByte & 0x80) == 0 {
break
}
position += 7
if position >= 32 {
return 0, length, fmt.Errorf("VarInt is too big")
}
}
return value, length, nil
}
// Decodes a VarInt from the byte slice.
// This function handles the decoding in a manner similar to Java's VarInt,
// correctly managing positive and negative values.
func receiveVarint(b []byte) (currentOffset int, value int32) {
var shift uint
for {
byteValue := b[currentOffset]
currentOffset++
value |= int32(byteValue&0x7F) << shift
if (byteValue & 0x80) == 0 {
// Apply sign extension for negative values
break
}
shift += 7
if shift >= 32 {
panic("VarInt is too big")
}
}
return
}
func addVarint(buffer *[]byte, value int32) {
for {
// Extract the lower 7 bits of the value
temp := byte(value & 0x7F)
value >>= 7
// If the value is not zero or if there are more bytes to encode
if value != 0 && value != -1 {
temp |= 0x80 // Set the continuation bit
} else {
temp &= 0x7F // Clear the continuation bit if we're done
}
// Append the byte to the buffer
addByte(buffer, temp)
// Break the loop if no more continuation bit is set
if (temp & 0x80) == 0 {
break
}
}
}
func addVarlong(buffer *[]byte, value int64) {
for {
temp := byte(value & 0x7F) // Get the last 7 bits
value >>= 7
if value != 0 && value != -1 {
temp |= 0x80 // Set the continuation bit
}
addByte(buffer, temp)
if temp&0x80 == 0 {
break
}
}
}
// Decodes a VarLong from the byte slice.
func receiveVarlong(b []byte) (currentOffset int, value int64) {
var shift uint
for {
byteValue := b[currentOffset]
currentOffset++
value |= int64(byteValue&0x7F) << shift
if (byteValue & 0x80) == 0 {
// Sign extension for negative values
if shift < 32 {
value |= -1 << shift
}
break
}
shift += 7
if shift >= 32 {
panic("VarLong is too big")
}
}
return
}
func addByte(buffer *[]byte, byte byte) {
*buffer = append(*buffer, byte)
}
func receiveBool(buffer []byte) (currentOffset int, boolean bool) {
boolean = buffer[0] == 1
currentOffset = 1
return
}
func addBool(buffer *[]byte, bool bool) {
var boolByte byte
boolByte = 0x00
if bool {
boolByte = 0x01
}
*buffer = append(*buffer, boolByte)
}
func constructOfflinePlayerUUID(username string) (dataArray []byte) {
data := md5.Sum([]byte("OfflinePlayer:" + username))
dataArray = data[:]
// Set the version to 3 -> Name based md5 hash
dataArray[6] = (dataArray[6] & 0x0f) | 0x30
// IETF variant
dataArray[8] = (dataArray[8] & 0x3f) | 0x80
return dataArray
}
func createOfflineLoginPacket(username string, threshold int32) (outBuffer []byte, err error) {
addString(&outBuffer, username)
outBuffer = append(outBuffer, constructOfflinePlayerUUID(username)...)
outBuffer, err = createPacket(0, outBuffer, threshold, false)
return
}
func createClientBrandPacket(brand string, threshold int32, startedCompression bool) (outBuffer []byte, err error) {
addString(&outBuffer, "minecraft:brand")
addString(&outBuffer, brand)
outBuffer, err = createPacket(2, outBuffer, threshold, startedCompression)
return
}
func createConfirmLoginPacket(threshold int32, startedCompression bool) (packet []byte, err error) {
packet, err = createPacket(3, []byte{}, threshold, startedCompression)
return
}
func createStatusRequestPacket(threshold int32) (packet []byte, err error) {
packet, err = createPacket(0, []byte{}, threshold, false)
return
}
func addUint16(buffer *[]byte, value uint16) {
*buffer = binary.BigEndian.AppendUint16(*buffer, value)
}
func addInt64(buffer *[]byte, value int64) {
*buffer = binary.BigEndian.AppendUint64(*buffer, uint64(value))
}
func receiveFloat32(b []byte) (currentOffset int, value float32) {
value = math.Float32frombits(binary.BigEndian.Uint32(b))
currentOffset = binary.Size(value)
return
}
func receiveFloat64(b []byte) (currentOffset int, value float64) {
value = math.Float64frombits(binary.BigEndian.Uint64(b))
currentOffset = binary.Size(value)
return
}
func receiveInt32(b []byte) (currentOffset int, value int32) {
value = int32(binary.BigEndian.Uint32(b))
currentOffset = binary.Size(value)
return
}
func receiveInt64(b []byte) (currentOffset int, value int64) {
value = int64(binary.BigEndian.Uint64(b))
currentOffset = binary.Size(value)
return
}
func receiveByte(b []byte) (currentOffset int, value byte) {
value = b[0]
currentOffset = binary.Size(value)
return
}
func receivePosition(b []byte) (currentOffset int, position Position) {
encoded := binary.BigEndian.Uint64(b)
x, y, z := DecodePosition(encoded)
position = Position{X: x, Y: y, Z: z}
currentOffset = binary.Size(encoded)
return
}
func DecodePosition(encoded uint64) (x int32, y int16, z int32) {
// Extract x, y, z from encoded value
x = int32((encoded >> 38) & 0x3FFFFFF)
y = int16(encoded & 0xFFF)
z = int32((encoded >> 12) & 0x3FFFFFF)
// Adjust for sign
if x >= 1<<25 {
x -= 1 << 26
}
if y >= 1<<11 {
y -= 1 << 12
}
if z >= 1<<25 {
z -= 1 << 26
}
return
}
func createHandshakePacket(version int32, address string, port uint16, nextState int32, threshold int32) (outBuffer []byte, err error) {
addVarint(&outBuffer, version)
addString(&outBuffer, address)
addUint16(&outBuffer, port)
addVarint(&outBuffer, nextState)
outBuffer, err = createPacket(0, outBuffer, threshold, false)
return
}
func createTeleportConfirmPacket(teleportID int32, threshold int32, startedCompression bool) (outBuffer []byte, err error) {
addVarint(&outBuffer, teleportID)
outBuffer, err = createPacket(0, outBuffer, threshold, startedCompression)
return
}
func receiveString(buf []byte) (currentOffset int, strOut string) {
n, stringLen := receiveVarint(buf)
currentOffset = n
strOut = string(buf[currentOffset : currentOffset+int(stringLen)])
currentOffset += int(stringLen)
return
}
func receiveTextComponent(buf []byte) (currentOffset int, component TextComponent, err error) {
currentOffset, jsonString := receiveString(buf)
if strings.ContainsAny(jsonString, "") {
}
jsonDecoderText := json.NewDecoder(strings.NewReader(jsonString))
err = jsonDecoderText.Decode(&component)
if err != nil {
err = nil
component.Text = jsonString
component.CleanText = cleanMinecraftFormatting(jsonString)
}
return
}
func createEnabledDatapacksPacket(datapacks []DatapackInfo, threshold int32, startedCompression bool) (outBuffer []byte, err error) {
addVarint(&outBuffer, int32(len(datapacks)))
for _, datapack := range datapacks {
addString(&outBuffer, datapack.Namespace)
addString(&outBuffer, datapack.ID)
addString(&outBuffer, datapack.Version)
}
outBuffer, err = createPacket(0x07, outBuffer, threshold, startedCompression)
return
}
func createChatMessagePacket(message string, threshold int32, startedCompression bool) (outBuffer []byte, err error) {
addString(&outBuffer, message)
addInt64(&outBuffer, time.Now().Unix())
addInt64(&outBuffer, time.Now().Unix())
addBool(&outBuffer, false)
addVarint(&outBuffer, 0)
addByte(&outBuffer, 0)
addByte(&outBuffer, 0)
addByte(&outBuffer, 0)
outBuffer, err = createPacket(0x06, outBuffer, threshold, startedCompression)
return
}
func decodeEnabledDatapacks(buf []byte) (currentOffset int, outPacks []DatapackInfo) {
currentOffset, datapackCount := receiveVarint(buf)
for i := 0; i < int(datapackCount); i++ {
nextOffset, namespace := receiveString(buf[currentOffset:])
currentOffset += nextOffset
nextOffset, ID := receiveString(buf[currentOffset:])
currentOffset += nextOffset
nextOffset, version := receiveString(buf[currentOffset:])
currentOffset += nextOffset
enabledDatapack := DatapackInfo{
Namespace: namespace,
ID: ID,
Version: version,
}
outPacks = append(outPacks, enabledDatapack)
}
return
}
func createClientInformationPacket(locale string, renderDistance byte, chatMode int32, colors bool, skinParts byte, mainLeftHand bool, textFiltering bool, serverListing bool, threshold int32, startedCompression bool) (outBuffer []byte, err error) {
var mainHand int32
if mainLeftHand {
mainHand = 0
} else {
mainHand = 1
}
addString(&outBuffer, locale)
addByte(&outBuffer, renderDistance)
addVarint(&outBuffer, chatMode)
addBool(&outBuffer, colors)
addByte(&outBuffer, skinParts)
addVarint(&outBuffer, mainHand)
addBool(&outBuffer, textFiltering)
addBool(&outBuffer, serverListing)
outBuffer, err = createPacket(0, outBuffer, threshold, startedCompression)
return
}