826 lines
24 KiB
Go
826 lines
24 KiB
Go
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"database/sql"
|
|
"encoding/binary"
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"github.com/google/uuid"
|
|
"log"
|
|
"net"
|
|
"os"
|
|
"reflect"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
func PingIP(ip net.IP, port uint16, host string) (response Response, errOut error) {
|
|
|
|
if host == "" {
|
|
host = ip.String()
|
|
}
|
|
|
|
addr := &net.TCPAddr{IP: ip, Port: int(port)}
|
|
|
|
conn, err := net.DialTCP("tcp", nil, addr)
|
|
if err != nil {
|
|
errOut = err
|
|
return
|
|
}
|
|
|
|
didRestartConnection := false
|
|
|
|
defer func(conn *net.TCPConn) {
|
|
_ = conn.Close()
|
|
}(conn)
|
|
|
|
defer func() {
|
|
if errOut == nil {
|
|
connectionBytes, err := os.ReadFile("secrets/.myconnectionstring")
|
|
if err != nil {
|
|
return
|
|
}
|
|
db, err := sql.Open("mysql", string(connectionBytes))
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
defer func(db *sql.DB) {
|
|
err := db.Close()
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
}(db)
|
|
err = saveResponse(db, response)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
return
|
|
}
|
|
}
|
|
}()
|
|
|
|
var state byte
|
|
state = 0
|
|
// 0 - handshaking
|
|
// 1 - status
|
|
// 2 - login
|
|
// 3 - configuration
|
|
// 4 - play(since this is a scanner, we don't need to actually join the game)
|
|
|
|
response.Encryption = false
|
|
response.CompressionThreshold = -2
|
|
response.PluginDataSent = map[string]string{}
|
|
response.ScanProgress = 0
|
|
response.ServerInfo = ServerInfo{
|
|
Hostname: host,
|
|
Port: port,
|
|
IP: ip.String(),
|
|
}
|
|
|
|
handshakePacketPing, err := createHandshakePacket(-1, host, port, 1, response.CompressionThreshold)
|
|
if err != nil {
|
|
errOut = err
|
|
return
|
|
}
|
|
_, err = conn.Write(handshakePacketPing)
|
|
if err != nil {
|
|
errOut = err
|
|
return
|
|
}
|
|
|
|
statusRequestPacket, err := createStatusRequestPacket(response.CompressionThreshold)
|
|
if err != nil {
|
|
errOut = err
|
|
return
|
|
}
|
|
_, err = conn.Write(statusRequestPacket)
|
|
if err != nil {
|
|
errOut = err
|
|
return
|
|
}
|
|
|
|
var username string
|
|
var PlayerUUID []byte
|
|
compressionStarted := false
|
|
reader := bufio.NewReader(conn)
|
|
for {
|
|
packetID, packetData, packetLength, err := readPacket(reader, response.CompressionThreshold)
|
|
if err != nil {
|
|
errOut = err
|
|
return
|
|
}
|
|
switch packetID {
|
|
case 0x00:
|
|
if state == 0 {
|
|
_, stringJson := receiveString(packetData)
|
|
jsonDecoder := json.NewDecoder(strings.NewReader(stringJson))
|
|
err = jsonDecoder.Decode(&response)
|
|
if err != nil {
|
|
errOut = err
|
|
println(stringJson)
|
|
return
|
|
}
|
|
response.ScanProgress = 1
|
|
if !didRestartConnection {
|
|
handshakePacketJoin, err := createHandshakePacket(response.Version.Protocol, host, port, 2, response.CompressionThreshold)
|
|
if err != nil {
|
|
errOut = err
|
|
return
|
|
}
|
|
err = conn.Close()
|
|
if err != nil {
|
|
errOut = err
|
|
return
|
|
}
|
|
|
|
conn, err = net.DialTCP("tcp", nil, addr)
|
|
if err != nil {
|
|
errOut = err
|
|
return
|
|
}
|
|
reader = bufio.NewReader(conn)
|
|
_, err = conn.Write(handshakePacketJoin)
|
|
if err != nil {
|
|
errOut = err
|
|
return
|
|
}
|
|
didRestartConnection = true
|
|
}
|
|
if len(response.Players.Sample) > 0 {
|
|
username = response.Players.Sample[0].Name
|
|
} else {
|
|
username = "YeahAkis_"
|
|
}
|
|
response.Username = username
|
|
PlayerUUID = constructOfflinePlayerUUID(username)
|
|
response.RawMessage = stringJson
|
|
state = 1
|
|
loginPacket, err := createOfflineLoginPacket(username, response.CompressionThreshold)
|
|
if err != nil {
|
|
errOut = err
|
|
return
|
|
}
|
|
_, err = conn.Write(loginPacket)
|
|
if err != nil {
|
|
errOut = err
|
|
return
|
|
}
|
|
state = 2
|
|
} else if state == 2 {
|
|
currentOffset, component, err := receiveTextComponent(packetData)
|
|
if err != nil {
|
|
errOut = err
|
|
return
|
|
}
|
|
if currentOffset != packetLength {
|
|
errOut = errors.New("packet length mismatch")
|
|
return
|
|
}
|
|
response.Message = component
|
|
return
|
|
}
|
|
break
|
|
|
|
case 0x01:
|
|
if state == 3 {
|
|
|
|
currentOffset, channelName := receiveString(packetData)
|
|
nextOffset, channelValue := receiveString(packetData[currentOffset:])
|
|
currentOffset += nextOffset
|
|
if currentOffset != packetLength {
|
|
errOut = errors.New("packet length mismatch")
|
|
return
|
|
}
|
|
response.PluginDataSent[channelName] = channelValue
|
|
} else if state == 2 {
|
|
response.Encryption = true
|
|
response.ScanProgress = 2
|
|
return
|
|
} else if state == 0 {
|
|
//ping response
|
|
}
|
|
break
|
|
|
|
case 0x02:
|
|
if state == 2 {
|
|
playerRecvUUID := packetData[:16]
|
|
currentOffset := 16
|
|
if !reflect.DeepEqual(PlayerUUID, playerRecvUUID) {
|
|
errOut = errors.New("player UUID mismatch")
|
|
return
|
|
}
|
|
addToOffset, receivedUsername := receiveString(packetData[16:])
|
|
currentOffset += addToOffset
|
|
if receivedUsername != username {
|
|
errOut = errors.New("username mismatch")
|
|
return
|
|
}
|
|
//TODO property array handling
|
|
confirmLoginPacket, err := createConfirmLoginPacket(response.CompressionThreshold, compressionStarted)
|
|
if err != nil {
|
|
errOut = err
|
|
return
|
|
}
|
|
_, err = conn.Write(confirmLoginPacket)
|
|
if err != nil {
|
|
errOut = err
|
|
return
|
|
}
|
|
clientBrandPacket, err := createClientBrandPacket("vanilla", response.CompressionThreshold, compressionStarted)
|
|
if err != nil {
|
|
errOut = err
|
|
return
|
|
}
|
|
_, err = conn.Write(clientBrandPacket)
|
|
if err != nil {
|
|
errOut = err
|
|
return
|
|
}
|
|
state = 3
|
|
} else if state == 3 {
|
|
currentOffset, component, err := receiveTextComponent(packetData)
|
|
if err != nil {
|
|
errOut = err
|
|
return
|
|
}
|
|
if currentOffset != packetLength {
|
|
errOut = errors.New("packet length mismatch")
|
|
return
|
|
}
|
|
response.Message = component
|
|
return
|
|
}
|
|
break
|
|
|
|
case 0x03:
|
|
if state == 2 {
|
|
currentOffset, compression := receiveVarint(packetData)
|
|
response.CompressionThreshold = compression
|
|
compressionStarted = true
|
|
if currentOffset != packetLength {
|
|
errOut = errors.New("packet length mismatch")
|
|
return
|
|
}
|
|
} else if state == 3 {
|
|
confirmPlaySwitchPacket, err := createConfirmLoginPacket(response.CompressionThreshold, compressionStarted)
|
|
if err != nil {
|
|
errOut = err
|
|
return
|
|
}
|
|
_, err = conn.Write(confirmPlaySwitchPacket)
|
|
state = 4
|
|
}
|
|
break
|
|
|
|
case 0x07:
|
|
if state == 3 {
|
|
currentOffset, registryID := receiveString(packetData)
|
|
newOffset, entryCount := receiveVarint(packetData[currentOffset:])
|
|
currentOffset += newOffset
|
|
var entries []RegistryEntry
|
|
for i := 0; i < int(entryCount); i++ {
|
|
newOffset, entryID := receiveString(packetData[currentOffset:])
|
|
currentOffset += newOffset
|
|
newOffset, hasNBT := receiveBool(packetData[currentOffset:])
|
|
currentOffset += newOffset
|
|
var nbtData []byte
|
|
if hasNBT {
|
|
nbtData = packetData[currentOffset:]
|
|
}
|
|
entries = append(entries, RegistryEntry{EntryID: entryID, HasNBT: hasNBT, NBTData: nbtData})
|
|
}
|
|
|
|
registryData := RegistryData{RegistryID: registryID, Entries: entries}
|
|
response.RegistryDatas = append(response.RegistryDatas, registryData)
|
|
}
|
|
break
|
|
|
|
case 0x0b:
|
|
if state == 4 {
|
|
currentOffset, difficulty := receiveByte(packetData)
|
|
newOffset, locked := receiveBool(packetData[currentOffset:])
|
|
currentOffset += newOffset
|
|
if currentOffset != packetLength {
|
|
errOut = errors.New("packet length mismatch")
|
|
return
|
|
}
|
|
response.ServerDifficulty = DifficultyObject{Difficulty: difficulty, Locked: locked}
|
|
}
|
|
|
|
case 0x0c:
|
|
if state == 3 {
|
|
currentOffset, flagCount := receiveVarint(packetData)
|
|
for i := 0; i < int(flagCount); i++ {
|
|
nextOffset, feature := receiveString(packetData[currentOffset:])
|
|
response.FeatureFlags = append(response.FeatureFlags, feature)
|
|
currentOffset += nextOffset
|
|
}
|
|
if currentOffset != packetLength {
|
|
errOut = errors.New("packet length mismatch")
|
|
}
|
|
clientInformationPacket, err := createClientInformationPacket("en_us", 32, 0, true, 0x7f, false, false, false, response.CompressionThreshold, compressionStarted)
|
|
if err != nil {
|
|
errOut = err
|
|
return
|
|
}
|
|
_, err = conn.Write(clientInformationPacket)
|
|
if err != nil {
|
|
errOut = err
|
|
return
|
|
}
|
|
}
|
|
break
|
|
case 0x0d:
|
|
if state == 3 {
|
|
currentOffset, entryCount := receiveVarint(packetData)
|
|
for i := 0; i < int(entryCount); i++ {
|
|
nextOffset, registryIdentifier := receiveString(packetData[currentOffset:])
|
|
currentOffset += nextOffset
|
|
nextOffset, lengthSubarray := receiveVarint(packetData[currentOffset:])
|
|
currentOffset += nextOffset
|
|
var tagArray []TagArray
|
|
for j := 0; j < int(lengthSubarray); j++ {
|
|
nextOffset, tagIdentifier := receiveString(packetData[currentOffset:])
|
|
currentOffset += nextOffset
|
|
nextOffset, subSubArrayLength := receiveVarint(packetData[currentOffset:])
|
|
currentOffset += nextOffset
|
|
var varInts []int32
|
|
for e := 0; e < int(subSubArrayLength); e++ {
|
|
nextOffset, varIntInArray := receiveVarint(packetData[currentOffset:])
|
|
currentOffset += nextOffset
|
|
varInts = append(varInts, varIntInArray)
|
|
}
|
|
tagPiece := TagArray{TagName: tagIdentifier, Entries: varInts}
|
|
tagArray = append(tagArray, tagPiece)
|
|
}
|
|
updateTag := UpdateTag{TagRegistryIdentifier: registryIdentifier, Tags: tagArray}
|
|
response.Tags = append(response.Tags, updateTag)
|
|
|
|
}
|
|
}
|
|
break
|
|
case 0x0e:
|
|
if state == 3 {
|
|
currentOffset, enabledDatapacks := decodeEnabledDatapacks(packetData)
|
|
if currentOffset != packetLength {
|
|
errOut = errors.New("packet length mismatch")
|
|
}
|
|
response.EnabledDatapacks = enabledDatapacks
|
|
datapackPacket, err := createEnabledDatapacksPacket(enabledDatapacks, response.CompressionThreshold, compressionStarted)
|
|
if err != nil {
|
|
errOut = err
|
|
return
|
|
}
|
|
_, err = conn.Write(datapackPacket)
|
|
if err != nil {
|
|
errOut = err
|
|
return
|
|
}
|
|
}
|
|
break
|
|
|
|
case 0x1D:
|
|
if state == 4 {
|
|
currentOffset, component, err := receiveTextComponent(packetData)
|
|
if err != nil {
|
|
errOut = err
|
|
return
|
|
}
|
|
if currentOffset != packetLength {
|
|
errOut = errors.New("packet length mismatch")
|
|
return
|
|
}
|
|
response.Message = component
|
|
return
|
|
}
|
|
break
|
|
|
|
case 0x2b:
|
|
if state == 4 {
|
|
currentOffset, entityID := receiveInt32(packetData)
|
|
newOffset, isHardCore := receiveBool(packetData[currentOffset:])
|
|
currentOffset += newOffset
|
|
newOffset, dimensionCount := receiveVarint(packetData[currentOffset:])
|
|
currentOffset += newOffset
|
|
var dimensions []string
|
|
for i := 0; i < int(dimensionCount); i++ {
|
|
newOffset, dimension := receiveString(packetData[currentOffset:])
|
|
currentOffset += newOffset
|
|
dimensions = append(dimensions, dimension)
|
|
}
|
|
newOffset, maxPlayers := receiveVarint(packetData[currentOffset:])
|
|
currentOffset += newOffset
|
|
newOffset, viewDistance := receiveVarint(packetData[currentOffset:])
|
|
currentOffset += newOffset
|
|
newOffset, simulationDistance := receiveVarint(packetData[currentOffset:])
|
|
currentOffset += newOffset
|
|
newOffset, reducedDebugInfo := receiveBool(packetData[currentOffset:])
|
|
currentOffset += newOffset
|
|
newOffset, enableRespawnScreen := receiveBool(packetData[currentOffset:])
|
|
currentOffset += newOffset
|
|
newOffset, doLimitedCrafting := receiveBool(packetData[currentOffset:])
|
|
currentOffset += newOffset
|
|
newOffset, dimensionType := receiveVarint(packetData[currentOffset:])
|
|
currentOffset += newOffset
|
|
newOffset, dimensionName := receiveString(packetData[currentOffset:])
|
|
currentOffset += newOffset
|
|
newOffset, hashedSeed := receiveInt64(packetData[currentOffset:])
|
|
currentOffset += newOffset
|
|
newOffset, gameMode := receiveByte(packetData[currentOffset:])
|
|
currentOffset += newOffset
|
|
newOffset, previousGameMode := receiveByte(packetData[currentOffset:])
|
|
currentOffset += newOffset
|
|
newOffset, isDebug := receiveBool(packetData[currentOffset:])
|
|
currentOffset += newOffset
|
|
newOffset, isFlat := receiveBool(packetData[currentOffset:])
|
|
currentOffset += newOffset
|
|
newOffset, hasDeathLocation := receiveBool(packetData[currentOffset:])
|
|
currentOffset += newOffset
|
|
var deathDimensionName string
|
|
var deathLocation Position
|
|
|
|
if hasDeathLocation {
|
|
newOffset, deathDimensionName = receiveString(packetData[currentOffset:])
|
|
currentOffset += newOffset
|
|
newOffset, deathLocation = receivePosition(packetData[currentOffset:])
|
|
currentOffset += newOffset
|
|
} else {
|
|
deathDimensionName = ""
|
|
deathLocation = Position{}
|
|
}
|
|
newOffset, portalCooldown := receiveVarint(packetData[currentOffset:])
|
|
currentOffset += newOffset
|
|
newOffset, enforcesSecureChat := receiveBool(packetData[currentOffset:])
|
|
currentOffset += newOffset
|
|
if currentOffset != packetLength {
|
|
errOut = errors.New("packet length mismatch")
|
|
return
|
|
}
|
|
response.PlayerLoginInfo = LoginInfo{
|
|
EntityID: entityID,
|
|
Hardcore: isHardCore,
|
|
Dimensions: dimensions,
|
|
MaxPlayers: maxPlayers,
|
|
ViewDistance: viewDistance,
|
|
SimulationDistance: simulationDistance,
|
|
ReducedDebugInfo: reducedDebugInfo,
|
|
EnableRespawnScreen: enableRespawnScreen,
|
|
DoLimitedCrafting: doLimitedCrafting,
|
|
DimensionType: dimensionType,
|
|
DimensionName: dimensionName,
|
|
HashedSeed: hashedSeed,
|
|
GameMode: gameMode,
|
|
PreviousGameMode: previousGameMode,
|
|
IsDebug: isDebug,
|
|
IsFlat: isFlat,
|
|
HasDeathLocation: hasDeathLocation,
|
|
DeathDimensionName: deathDimensionName,
|
|
DeathLocation: deathLocation,
|
|
PortalCooldown: portalCooldown,
|
|
EnforcesSecureChat: enforcesSecureChat,
|
|
}
|
|
}
|
|
break
|
|
|
|
case 0x38:
|
|
if state == 4 {
|
|
currentOffset, flags := receiveByte(packetData)
|
|
newOffset, flyingSpeed := receiveFloat32(packetData[currentOffset:])
|
|
currentOffset += newOffset
|
|
newOffset, fieldOfViewModifer := receiveFloat32(packetData[currentOffset:])
|
|
currentOffset += newOffset
|
|
if currentOffset != packetLength {
|
|
errOut = errors.New("packet length mismatch")
|
|
}
|
|
response.PlayerAbilities = PlayerAbilitiesObject{
|
|
Invulnerable: flags&0x01 != 0x00,
|
|
Flying: flags&0x02 != 0x00,
|
|
AllowFlying: flags&0x04 != 0x00,
|
|
CreativeMode: flags&0x08 != 0x00,
|
|
FlyingSpeed: flyingSpeed,
|
|
FieldOfViewModifier: fieldOfViewModifer,
|
|
}
|
|
}
|
|
break
|
|
|
|
case 0x53:
|
|
if state == 4 {
|
|
currentOffset, heldSlot := receiveByte(packetData)
|
|
if currentOffset != packetLength {
|
|
errOut = errors.New("packet length mismatch")
|
|
return
|
|
}
|
|
response.PlayerSlot = heldSlot
|
|
}
|
|
break
|
|
case 0x77:
|
|
if state == 4 {
|
|
//todo RECIPES
|
|
}
|
|
break
|
|
case 0x11:
|
|
//commands
|
|
if state == 4 {
|
|
|
|
}
|
|
|
|
break
|
|
|
|
case 0x41:
|
|
//recipe book
|
|
if state == 4 {
|
|
|
|
}
|
|
break
|
|
|
|
case 0x40:
|
|
//position update
|
|
if state == 4 {
|
|
currentOffset, x := receiveFloat64(packetData)
|
|
newOffset, y := receiveFloat64(packetData[currentOffset:])
|
|
currentOffset += newOffset
|
|
newOffset, z := receiveFloat64(packetData[currentOffset:])
|
|
currentOffset += newOffset
|
|
newOffset, yaw := receiveFloat32(packetData[currentOffset:])
|
|
currentOffset += newOffset
|
|
newOffset, pitch := receiveFloat32(packetData[currentOffset:])
|
|
currentOffset += newOffset
|
|
newOffset, flags := receiveByte(packetData[currentOffset:])
|
|
currentOffset += newOffset
|
|
newOffset, teleportId := receiveVarint(packetData[currentOffset:])
|
|
currentOffset += newOffset
|
|
if currentOffset != packetLength {
|
|
errOut = errors.New("packet length mismatch")
|
|
return
|
|
}
|
|
teleportConfirmPacket, err := createTeleportConfirmPacket(teleportId, response.CompressionThreshold, compressionStarted)
|
|
if err != nil {
|
|
errOut = err
|
|
return
|
|
}
|
|
_, err = conn.Write(teleportConfirmPacket)
|
|
if err != nil {
|
|
errOut = err
|
|
return
|
|
}
|
|
response.PlayerLocation = PlayerPosition{
|
|
X: x,
|
|
Y: y,
|
|
Z: z,
|
|
Yaw: yaw,
|
|
Pitch: pitch,
|
|
IsXRelative: flags&0x01 != 0x00,
|
|
IsYRelative: flags&0x02 != 0x00,
|
|
IsZRelative: flags&0x04 != 0x00,
|
|
IsYawRelative: flags&0x08 != 0x00,
|
|
IsPitchRelative: flags&0x10 != 0x00,
|
|
}
|
|
}
|
|
break
|
|
|
|
case 0x4b:
|
|
if state == 4 {
|
|
currentOffset, motd, errx := receiveTextComponent(packetData[2:])
|
|
currentOffset += 2
|
|
err = errx
|
|
if err != nil {
|
|
errOut = err
|
|
return
|
|
}
|
|
response.Description = motd
|
|
newOffset, hasIcon := receiveBool(packetData[currentOffset:])
|
|
currentOffset += newOffset
|
|
var iconSize int32
|
|
var iconData []byte
|
|
if hasIcon {
|
|
newOffset, iconSize = receiveVarint(packetData[currentOffset:])
|
|
currentOffset += newOffset
|
|
iconData = packetData[currentOffset : currentOffset+int(iconSize)]
|
|
response.Favicon.PngData = iconData
|
|
} else {
|
|
iconSize = 0
|
|
iconData = []byte{}
|
|
}
|
|
}
|
|
break
|
|
|
|
case 0x3e:
|
|
if state == 4 {
|
|
currentOffset, actions := receiveByte(packetData)
|
|
newOffset, numberOfPlayers := receiveVarint(packetData[currentOffset:])
|
|
currentOffset += newOffset
|
|
|
|
for i := 0; i < int(numberOfPlayers); i++ {
|
|
player := PlayerUpdate{}
|
|
newUUID, err := uuid.FromBytes(packetData[currentOffset : currentOffset+16])
|
|
if err != nil {
|
|
errOut = err
|
|
return
|
|
}
|
|
player.UUID = newUUID
|
|
currentOffset += 16
|
|
|
|
if actions&0x01 != 0x00 {
|
|
newOffset, player.Name = receiveString(packetData[currentOffset:])
|
|
currentOffset += newOffset
|
|
|
|
newOffset, numberOfProperties := receiveVarint(packetData[currentOffset:])
|
|
currentOffset += newOffset
|
|
|
|
for x := 0; x < int(numberOfProperties); x++ {
|
|
property := PlayerProperty{}
|
|
|
|
newOffset, property.Name = receiveString(packetData[currentOffset:])
|
|
currentOffset += newOffset
|
|
newOffset, property.Value = receiveString(packetData[currentOffset:])
|
|
currentOffset += newOffset
|
|
|
|
newOffset, propertySigned := receiveByte(packetData[currentOffset:])
|
|
currentOffset += newOffset
|
|
|
|
if propertySigned != 0 {
|
|
newOffset, property.Signature = receiveString(packetData[currentOffset:])
|
|
currentOffset += newOffset
|
|
}
|
|
|
|
player.Properties = append(player.Properties, property)
|
|
}
|
|
}
|
|
|
|
if actions&0x02 != 0x00 {
|
|
newOffset, hasSignatureData := receiveBool(packetData[currentOffset:])
|
|
currentOffset += newOffset
|
|
|
|
if hasSignatureData {
|
|
player.SignatureData = &PlayerSignatureData{}
|
|
|
|
player.SignatureData.ChatSessionID = packetData[currentOffset : currentOffset+16]
|
|
currentOffset += 16
|
|
|
|
newOffset, player.SignatureData.PublicKeyExpiryTime = receiveInt64(packetData[currentOffset:])
|
|
currentOffset += newOffset
|
|
|
|
newOffset, encodedPublicKeySize := receiveVarint(packetData[currentOffset:])
|
|
currentOffset += newOffset
|
|
player.SignatureData.EncodedPublicKey = packetData[currentOffset : currentOffset+int(encodedPublicKeySize)]
|
|
currentOffset += int(encodedPublicKeySize)
|
|
|
|
newOffset, publicKeySignatureSize := receiveVarint(packetData[currentOffset:])
|
|
currentOffset += newOffset
|
|
player.SignatureData.PublicKeySignature = packetData[currentOffset : currentOffset+int(publicKeySignatureSize)]
|
|
currentOffset += int(publicKeySignatureSize)
|
|
}
|
|
}
|
|
|
|
if actions&0x04 != 0x00 {
|
|
newOffset, player.GameMode = receiveVarint(packetData[currentOffset:])
|
|
currentOffset += newOffset
|
|
}
|
|
|
|
if actions&0x08 != 0x00 {
|
|
newOffset, player.Listed = receiveBool(packetData[currentOffset:])
|
|
currentOffset += newOffset
|
|
}
|
|
|
|
if actions&0x10 != 0x00 {
|
|
newOffset, player.Ping = receiveVarint(packetData[currentOffset:])
|
|
currentOffset += newOffset
|
|
}
|
|
|
|
if actions&0x20 != 0x00 {
|
|
newOffset, hasDisplayName := receiveBool(packetData[currentOffset:])
|
|
currentOffset += newOffset
|
|
|
|
if hasDisplayName {
|
|
newOffset, displayName, err := receiveTextComponent(packetData[currentOffset:])
|
|
if err != nil {
|
|
errOut = err
|
|
return
|
|
}
|
|
currentOffset += newOffset
|
|
player.DisplayName = &displayName
|
|
}
|
|
}
|
|
if !reflect.DeepEqual(PlayerUUID, player.UUID) {
|
|
response.PlayersInfo = append(response.PlayersInfo, player)
|
|
}
|
|
}
|
|
}
|
|
break
|
|
case 0x25:
|
|
if state == 4 {
|
|
currentOffset, x := receiveFloat64(packetData)
|
|
newOffset, z := receiveFloat64(packetData[currentOffset:])
|
|
currentOffset += newOffset
|
|
newOffset, oldDiameter := receiveFloat64(packetData[currentOffset:])
|
|
currentOffset += newOffset
|
|
newOffset, newDiameter := receiveFloat64(packetData[currentOffset:])
|
|
currentOffset += newOffset
|
|
newOffset, speed := receiveVarlong(packetData[currentOffset:])
|
|
currentOffset += newOffset
|
|
newOffset, portalTeleportBoundary := receiveVarint(packetData[currentOffset:])
|
|
currentOffset += newOffset
|
|
newOffset, warningBlocks := receiveVarint(packetData[currentOffset:])
|
|
currentOffset += newOffset
|
|
newOffset, warningTime := receiveVarint(packetData[currentOffset:])
|
|
currentOffset += newOffset
|
|
if currentOffset != packetLength {
|
|
errOut = errors.New("packet length mismatch")
|
|
return
|
|
}
|
|
|
|
response.WorldBorder = WorldBorderInfo{
|
|
X: x,
|
|
Z: z,
|
|
OldDiameter: oldDiameter,
|
|
NewDiameter: newDiameter,
|
|
Speed: speed,
|
|
PortalTeleportBoundary: portalTeleportBoundary,
|
|
WarningBlocks: warningBlocks,
|
|
WarningTime: warningTime,
|
|
}
|
|
|
|
}
|
|
break
|
|
case 0x64:
|
|
if state == 4 {
|
|
currentOffset, worldAge := receiveInt64(packetData)
|
|
newOffset, timeOfDay := receiveInt64(packetData[currentOffset:])
|
|
currentOffset += newOffset
|
|
if currentOffset != packetLength {
|
|
errOut = errors.New("packet length mismatch")
|
|
return
|
|
}
|
|
response.WorldAge = worldAge
|
|
response.TimeOfDay = timeOfDay
|
|
}
|
|
break
|
|
|
|
case 0x56:
|
|
if state == 4 {
|
|
currentOffset, location := receivePosition(packetData)
|
|
newOffset, angle := receiveFloat32(packetData[currentOffset:])
|
|
currentOffset += newOffset
|
|
if currentOffset != packetLength {
|
|
errOut = errors.New("packet length mismatch")
|
|
return
|
|
}
|
|
response.DefaultPositionSpawn = DefaultSpawnPosition{
|
|
Location: location,
|
|
Angle: angle,
|
|
}
|
|
}
|
|
break
|
|
|
|
case 0x22:
|
|
if state == 4 {
|
|
currentOffset, event := receiveByte(packetData)
|
|
nextOffset, value := receiveFloat32(packetData[currentOffset:])
|
|
currentOffset += nextOffset
|
|
if currentOffset != packetLength {
|
|
errOut = errors.New("packet length mismatch")
|
|
return
|
|
}
|
|
if event == 13 && value == 0 {
|
|
chatMessagePacket, err := createChatMessagePacket("Thanks for letting me scan this server", response.CompressionThreshold, compressionStarted)
|
|
if err != nil {
|
|
errOut = err
|
|
return
|
|
}
|
|
_, err = conn.Write(chatMessagePacket)
|
|
if err != nil {
|
|
errOut = err
|
|
return
|
|
}
|
|
response.ScanProgress = 3
|
|
return //we dont want chunks
|
|
}
|
|
}
|
|
|
|
case 0x1f:
|
|
if state == 4 {
|
|
//todo entityEvent
|
|
}
|
|
break
|
|
|
|
default:
|
|
packetIDBytes := make([]byte, 8)
|
|
binary.LittleEndian.PutUint64(packetIDBytes, uint64(packetID))
|
|
fmt.Printf("Unknown packet type %d (%s)\n", packetID, hex.EncodeToString(packetIDBytes))
|
|
}
|
|
}
|
|
}
|
|
|
|
func PingHostname(host string, port uint16) (Response, error) {
|
|
|
|
tcpServer, err := net.ResolveTCPAddr("tcp", host+":"+strconv.Itoa(int(port)))
|
|
|
|
if err != nil {
|
|
return Response{}, err
|
|
}
|
|
return PingIP(tcpServer.IP, port, host)
|
|
|
|
}
|