527 lines
12 KiB
Go
527 lines
12 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"errors"
|
|
"github.com/google/uuid"
|
|
"math/rand"
|
|
"time"
|
|
)
|
|
|
|
func getHandshakePacket(data *bytes.Buffer) (version int32, address string, port uint16, nextState int32, err error) {
|
|
version, err = receiveVarint(data)
|
|
address, err = receiveString(data)
|
|
port = receiveUint16(data)
|
|
nextState, err = receiveVarint(data)
|
|
return
|
|
}
|
|
|
|
func encodeTextComponent(buffer *bytes.Buffer, component TextComponent) (err error) {
|
|
// First, serialize the component to JSON
|
|
jsonData, err := json.Marshal(component)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
addString(buffer, string(jsonData))
|
|
|
|
return
|
|
}
|
|
|
|
func getPingPacket(data *bytes.Buffer) (payload int64, err error) {
|
|
payload = receiveInt64(data)
|
|
return
|
|
}
|
|
|
|
func getLoginStart(data *bytes.Buffer) (userName string, playerUUID uuid.UUID, err error) {
|
|
userName, err = receiveString(data)
|
|
playerUUID, err = receiveUUID(data)
|
|
return
|
|
}
|
|
|
|
// Decodes encryption response data from the buffer.
|
|
func getEncryptionResponse(data *bytes.Buffer) (sharedSecret []byte, verifyToken []byte, err error) {
|
|
// Read sharedSecret length using VarInt
|
|
sharedSecretLen, err := receiveVarint(data)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
// Read sharedSecret bytes
|
|
sharedSecret = make([]byte, sharedSecretLen)
|
|
_, err = data.Read(sharedSecret)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
// Read verifyToken length using VarInt
|
|
verifyTokenLen, err := receiveVarint(data)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
// Read verifyToken bytes
|
|
verifyToken = make([]byte, verifyTokenLen)
|
|
_, err = data.Read(verifyToken)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
return sharedSecret, verifyToken, nil
|
|
}
|
|
|
|
func getClientInformation(data *bytes.Buffer) (locale string, viewDistance int8, chatMode int32, chatColors bool, skinParts uint8, isRightHanded bool, textFiltering bool, serverListing bool, err error) {
|
|
var offset int32
|
|
|
|
// Decode locale string
|
|
locale, err = receiveString(data)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
// Decode viewDistance
|
|
viewDistance = int8(data.Bytes()[offset])
|
|
offset++
|
|
|
|
// Decode chatMode
|
|
chatMode, err = receiveVarint(data)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
// Decode chatColors
|
|
chatColors, err = receiveBool(data)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
// Decode skinParts
|
|
skinParts = data.Bytes()[offset]
|
|
offset++
|
|
|
|
// Decode isRightHanded
|
|
mainHand, err := receiveVarint(data)
|
|
if err != nil {
|
|
return
|
|
}
|
|
isRightHanded = mainHand == 1
|
|
|
|
// Decode textFiltering
|
|
textFiltering, err = receiveBool(data)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
// Decode serverListing
|
|
serverListing, err = receiveBool(data)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func getPluginPacket(inData *bytes.Buffer) (key string, data *bytes.Buffer, err error) {
|
|
key, err = receiveString(inData)
|
|
data = inData
|
|
return
|
|
}
|
|
|
|
func (player *Player) getResourcePackPacket(data *bytes.Buffer) (err error) {
|
|
packUUID, err := receiveUUID(data)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
result, err := receiveVarint(data)
|
|
player.resourcePackResponse[packUUID] = result
|
|
return
|
|
}
|
|
|
|
func (player *Player) getDataPacksPacket(data *bytes.Buffer) (err error) {
|
|
packCount, err := receiveVarint(data)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for i := 0; i < int(packCount); i++ {
|
|
namespace, err := receiveString(data)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
id, err := receiveString(data)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
version, err := receiveString(data)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
player.knownPacks = append(player.knownPacks, DatapackInfo{
|
|
Namespace: namespace,
|
|
ID: id,
|
|
Version: version,
|
|
})
|
|
}
|
|
return
|
|
}
|
|
|
|
func (player *Player) sendPongPacket(payload int64) (err error) {
|
|
var buf bytes.Buffer
|
|
addInt64(&buf, payload)
|
|
err = player.sendPacket(0x01, &buf)
|
|
if err != nil {
|
|
return
|
|
}
|
|
return
|
|
}
|
|
|
|
func (player *Player) sendDisconnect(reason string) (err error) {
|
|
var buf bytes.Buffer
|
|
addString(&buf, reason)
|
|
switch player.state {
|
|
case 2: //login
|
|
err = player.sendPacket(0x00, &buf)
|
|
case 3: //configuration
|
|
err = player.sendPacket(0x02, &buf)
|
|
case 4: //play
|
|
err = player.sendPacket(0x1D, &buf)
|
|
|
|
}
|
|
return
|
|
}
|
|
func (player *Player) sendEncryptionRequest() error {
|
|
var buf bytes.Buffer
|
|
|
|
// Send an empty server ID
|
|
addVarint(&buf, int32(len(serverID))) // VarInt length of server ID (0)
|
|
if len(serverID) > 0 {
|
|
buf.Write(serverID)
|
|
}
|
|
|
|
// Continue with the rest of the packet (public key, token, etc.)
|
|
serverPubKeyBytes, err := ExportRSAPublicKey(serverPublicKey)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
verifyToken, err := generateRandomBytes(4)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
player.verifyToken = verifyToken
|
|
|
|
addVarint(&buf, int32(len(serverPubKeyBytes)))
|
|
buf.Write(serverPubKeyBytes)
|
|
|
|
addVarint(&buf, int32(len(player.verifyToken)))
|
|
buf.Write(player.verifyToken)
|
|
|
|
// Indicate that encryption is enabled
|
|
addBool(&buf, true)
|
|
|
|
// Send the packet with ID 0x01 (encryption request)
|
|
err = player.sendPacket(0x01, &buf)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (player *Player) sendLoginSuccess() error {
|
|
var buf bytes.Buffer
|
|
addUUID(&buf, player.uuid)
|
|
|
|
addString(&buf, player.Entity.Name)
|
|
|
|
addVarint(&buf, int32(len(player.profile.Properties)))
|
|
|
|
for _, property := range player.profile.Properties {
|
|
addString(&buf, property.Name)
|
|
addString(&buf, property.Value)
|
|
isSigned := len(property.Signature) > 0
|
|
addBool(&buf, isSigned)
|
|
if isSigned {
|
|
addString(&buf, property.Signature)
|
|
}
|
|
}
|
|
|
|
addBool(&buf, true)
|
|
|
|
return player.sendPacket(0x02, &buf)
|
|
}
|
|
|
|
func (player *Player) sendKeepAlive() error {
|
|
player.keepAlivePayload = rand.Int63()
|
|
player.keepAliveSentTimestamp = time.Now().Unix()
|
|
var buf bytes.Buffer
|
|
|
|
addInt64(&buf, player.keepAlivePayload)
|
|
switch player.state {
|
|
case 3:
|
|
return player.sendPacket(0x04, &buf)
|
|
case 4:
|
|
return player.sendPacket(0x26, &buf)
|
|
}
|
|
return errors.New("invalid keep alive state")
|
|
}
|
|
|
|
func (player *Player) sendKeepAlives() {
|
|
for {
|
|
err := player.sendKeepAlive()
|
|
if err != nil {
|
|
break
|
|
}
|
|
time.Sleep(18 * time.Second)
|
|
}
|
|
}
|
|
|
|
func (player *Player) sendPingPacket() error {
|
|
player.pingID = rand.Uint32()
|
|
var buf bytes.Buffer
|
|
addUint32(&buf, player.pingID)
|
|
player.pingSentTimestamp = time.Now().Unix()
|
|
switch player.state {
|
|
case 3:
|
|
return player.sendPacket(0x05, &buf)
|
|
case 4:
|
|
return player.sendPacket(0x36, &buf)
|
|
}
|
|
return errors.New("invalid ping state")
|
|
}
|
|
|
|
func (player *Player) getPongPacket(data *bytes.Buffer) (err error) {
|
|
pingID, err := receiveUint32(data)
|
|
if pingID == player.pingID {
|
|
player.pingReceivedTimestamp = time.Now().Unix()
|
|
}
|
|
return
|
|
}
|
|
|
|
func (player *Player) sendResetChat() error {
|
|
return player.sendPacket(0x06, &bytes.Buffer{})
|
|
}
|
|
|
|
func (player *Player) sendRegistryData() (err error) {
|
|
for _, registryData := range world.RegistryDataThings {
|
|
var buf bytes.Buffer
|
|
addString(&buf, registryData.RegistryID)
|
|
addVarint(&buf, int32(len(registryData.Entries)))
|
|
for _, entry := range registryData.Entries {
|
|
addString(&buf, entry.EntryID)
|
|
addBool(&buf, entry.HasNBT)
|
|
if entry.HasNBT {
|
|
buf.ReadFrom(entry.NBTData)
|
|
}
|
|
}
|
|
err = player.sendPacket(0x07, &buf)
|
|
if err != nil {
|
|
return
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (player *Player) removeResourcePack(packUUID uuid.UUID) (err error) {
|
|
var buf bytes.Buffer
|
|
|
|
hasUUID := packUUID != uuid.Nil
|
|
addBool(&buf, hasUUID)
|
|
|
|
if hasUUID {
|
|
addUUID(&buf, packUUID)
|
|
}
|
|
switch player.state {
|
|
case 3:
|
|
return player.sendPacket(0x08, &buf)
|
|
case 4:
|
|
return player.sendPacket(0x45, &buf)
|
|
}
|
|
return player.sendPacket(0x08, &buf)
|
|
}
|
|
|
|
func (player *Player) addResourcePack(packUUID uuid.UUID, url string, hash string, forced bool, message string) (err error) {
|
|
var buf bytes.Buffer
|
|
|
|
addUUID(&buf, packUUID)
|
|
addString(&buf, url)
|
|
addString(&buf, hash)
|
|
addBool(&buf, forced)
|
|
hasMessage := len(message) > 0
|
|
addBool(&buf, hasMessage)
|
|
if hasMessage {
|
|
addString(&buf, message)
|
|
}
|
|
|
|
switch player.state {
|
|
case 3:
|
|
return player.sendPacket(0x09, &buf)
|
|
case 4:
|
|
return player.sendPacket(0x46, &buf)
|
|
}
|
|
return errors.New("invalid resource pack state")
|
|
}
|
|
|
|
func (player *Player) sendTransfer(host string, port uint16) (err error) {
|
|
var buf bytes.Buffer
|
|
addString(&buf, host)
|
|
addVarint(&buf, int32(port))
|
|
switch player.state {
|
|
case 3:
|
|
return player.sendPacket(0x0B, &buf)
|
|
case 4:
|
|
return player.sendPacket(0x73, &buf)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (player *Player) sendFeatureFlags() error {
|
|
var buf bytes.Buffer
|
|
addVarint(&buf, int32(len(world.FeatureFlags)))
|
|
for _, featureFlag := range world.FeatureFlags {
|
|
addString(&buf, featureFlag)
|
|
}
|
|
return player.sendPacket(0x0C, &buf)
|
|
}
|
|
|
|
func (player *Player) sendUpdateTags() error {
|
|
var buf bytes.Buffer
|
|
addVarint(&buf, int32(len(world.UpdateTags)))
|
|
for _, updateTag := range world.UpdateTags {
|
|
addString(&buf, updateTag.TagRegistryIdentifier)
|
|
addVarint(&buf, int32(len(updateTag.Tags)))
|
|
for _, tag := range updateTag.Tags {
|
|
addString(&buf, tag.TagName)
|
|
addVarint(&buf, int32(len(tag.Entries)))
|
|
for _, entry := range tag.Entries {
|
|
addVarint(&buf, entry)
|
|
}
|
|
}
|
|
}
|
|
switch player.state {
|
|
case 3:
|
|
return player.sendPacket(0x0D, &buf)
|
|
case 4:
|
|
return player.sendPacket(0x78, &buf)
|
|
}
|
|
return errors.New("invalid update tags state")
|
|
}
|
|
|
|
func (player *Player) sendDataPacks() error {
|
|
var buf bytes.Buffer
|
|
addVarint(&buf, int32(len(world.DataPacks)))
|
|
for _, dataPack := range world.DataPacks {
|
|
addString(&buf, dataPack.Namespace)
|
|
addString(&buf, dataPack.ID)
|
|
addString(&buf, dataPack.Version)
|
|
}
|
|
return player.sendPacket(0x0E, &buf)
|
|
}
|
|
|
|
func (player *Player) sendReportDetails() (err error) {
|
|
var buf bytes.Buffer
|
|
addVarint(&buf, int32(len(world.ReportDetails)))
|
|
for key, value := range world.ReportDetails {
|
|
addString(&buf, key)
|
|
addString(&buf, value)
|
|
}
|
|
switch player.state {
|
|
case 3:
|
|
return player.sendPacket(0x0F, &buf)
|
|
case 4:
|
|
return player.sendPacket(0x7A, &buf)
|
|
}
|
|
return errors.New("invalid report details state")
|
|
|
|
}
|
|
|
|
func (player *Player) sendServerLinks() error {
|
|
var buf bytes.Buffer
|
|
addVarint(&buf, int32(len(world.ServerLinks)))
|
|
for _, serverLink := range world.ServerLinks {
|
|
if len(serverLink.LabelString) > 0 {
|
|
addBool(&buf, false)
|
|
addString(&buf, serverLink.LabelString)
|
|
} else {
|
|
addBool(&buf, true)
|
|
addVarint(&buf, serverLink.LabelInt)
|
|
}
|
|
addString(&buf, serverLink.URL)
|
|
}
|
|
|
|
switch player.state {
|
|
case 3:
|
|
return player.sendPacket(0x10, &buf)
|
|
case 4:
|
|
return player.sendPacket(0x7B, &buf)
|
|
}
|
|
return errors.New("invalid server links state")
|
|
}
|
|
|
|
func (player *Player) sendPluginMessage(channel string, data *bytes.Buffer) (err error) {
|
|
var buf bytes.Buffer
|
|
addString(&buf, channel)
|
|
_, err = buf.ReadFrom(data)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
switch player.state {
|
|
case 3:
|
|
return player.sendPacket(0x01, &buf)
|
|
case 4:
|
|
return player.sendPacket(0x19, &buf)
|
|
}
|
|
return errors.New("unknown player state")
|
|
}
|
|
|
|
func (player *Player) sendConfigurationFinish() error {
|
|
return player.sendPacket(0x03, &bytes.Buffer{})
|
|
}
|
|
|
|
func (player *Player) sendCompression() error {
|
|
var buf bytes.Buffer
|
|
|
|
addVarint(&buf, CompressionThreshold)
|
|
return player.sendPacket(0x03, &buf)
|
|
}
|
|
|
|
func (player *Player) sendStatusResponse(description string) (err error) {
|
|
if player.state == 1 {
|
|
var playerSamples []PlayerSample
|
|
for _, player := range world.Players {
|
|
if !player.serverListing || player.uuid == uuid.Nil {
|
|
continue
|
|
}
|
|
playerSamples = append(playerSamples, PlayerSample{
|
|
Name: player.Entity.Name,
|
|
ID: player.uuid.String(),
|
|
})
|
|
}
|
|
status := ServerStatus{
|
|
Version: Version{
|
|
Name: VERSION,
|
|
Protocol: ProtocolVersion,
|
|
},
|
|
Players: Players{
|
|
Max: maxPlayers,
|
|
Online: len(world.Players) - 1,
|
|
Sample: playerSamples,
|
|
},
|
|
Description: Description{description},
|
|
Favicon: icon,
|
|
EnforcesSecureChat: false,
|
|
}
|
|
jsonData, err := json.Marshal(status)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
var outBuffer bytes.Buffer
|
|
addString(&outBuffer, string(jsonData))
|
|
err = player.sendPacket(0x00, &outBuffer)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|