783 lines
23 KiB
Go
783 lines
23 KiB
Go
|
package main
|
||
|
|
||
|
import (
|
||
|
"bufio"
|
||
|
"encoding/binary"
|
||
|
"encoding/hex"
|
||
|
"encoding/json"
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
"net"
|
||
|
"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)
|
||
|
|
||
|
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{}
|
||
|
|
||
|
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 !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
|
||
|
if err != nil {
|
||
|
errOut = err
|
||
|
println(stringJson)
|
||
|
return
|
||
|
}
|
||
|
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
|
||
|
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{}
|
||
|
player.UUID = packetData[currentOffset : currentOffset+16]
|
||
|
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
|
||
|
}
|
||
|
}
|
||
|
|
||
|
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,
|
||
|
PortalTeleportBoundry: 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
|
||
|
}
|
||
|
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)
|
||
|
|
||
|
}
|