266 lines
9.8 KiB
Go
266 lines
9.8 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"github.com/google/uuid"
|
|
"io"
|
|
"regexp"
|
|
"strings"
|
|
)
|
|
|
|
type ServerInfo struct {
|
|
Hostname string `json:"hostname,omitempty"`
|
|
Port uint16 `json:"port,omitempty"`
|
|
IP string `json:"ip,omitempty"`
|
|
}
|
|
|
|
type PlayerPosition struct {
|
|
X float64 `json:"x,omitempty"`
|
|
Y float64 `json:"y,omitempty"`
|
|
Z float64 `json:"z,omitempty"`
|
|
Yaw float32 `json:"yaw,omitempty"`
|
|
Pitch float32 `json:"pitch,omitempty"`
|
|
IsXRelative bool `json:"isXRelative,omitempty"`
|
|
IsYRelative bool `json:"isYRelative,omitempty"`
|
|
IsZRelative bool `json:"isZRelative,omitempty"`
|
|
IsYawRelative bool `json:"isYawRelative,omitempty"`
|
|
IsPitchRelative bool `json:"isPitchRelative,omitempty"`
|
|
}
|
|
|
|
type Position struct {
|
|
X int32 `json:"x,omitempty"`
|
|
Y int16 `json:"y,omitempty"`
|
|
Z int32 `json:"z,omitempty"`
|
|
}
|
|
|
|
type DefaultSpawnPosition struct {
|
|
Location Position `json:"location,omitempty"`
|
|
Angle float32 `json:"angle,omitempty"`
|
|
}
|
|
|
|
type PlayerAbilitiesObject struct {
|
|
Invulnerable bool `json:"invulnerable,omitempty"`
|
|
Flying bool `json:"flying,omitempty"`
|
|
AllowFlying bool `json:"allowFlying,omitempty"`
|
|
CreativeMode bool `json:"creativeMode,omitempty"`
|
|
FlyingSpeed float32 `json:"flyingSpeed,omitempty"`
|
|
FieldOfViewModifier float32 `json:"fieldOfViewModifier,omitempty"`
|
|
}
|
|
|
|
type PlayerProperty struct {
|
|
Name string `json:"name,omitempty"`
|
|
Value string `json:"value,omitempty"`
|
|
Signature string `json:"signature,omitempty"`
|
|
}
|
|
|
|
type PlayerSignatureData struct {
|
|
ChatSessionID []byte `json:"chatSessionID,omitempty"`
|
|
PublicKeyExpiryTime int64 `json:"publicKeyExpiryTime,omitempty"`
|
|
EncodedPublicKey []byte `json:"encodedPublicKey,omitempty"`
|
|
PublicKeySignature []byte `json:"publicKeySignature,omitempty"`
|
|
}
|
|
|
|
type AddedPlayer struct {
|
|
UUID uuid.UUID
|
|
PlayerId int
|
|
}
|
|
|
|
type PlayerUpdate struct {
|
|
UUID uuid.UUID `json:"uuid,omitempty"`
|
|
Name string `json:"name,omitempty"`
|
|
Properties []PlayerProperty `json:"properties,omitempty"`
|
|
SignatureData *PlayerSignatureData `json:"signatureData,omitempty"`
|
|
GameMode int32 `json:"gameMode,omitempty"`
|
|
Listed bool `json:"listed,omitempty"`
|
|
Ping int32 `json:"ping,omitempty"`
|
|
DisplayName *TextComponent `json:"displayName,omitempty"`
|
|
}
|
|
|
|
type LoginInfo struct {
|
|
EntityID int32 `json:"entityID,omitempty"`
|
|
Hardcore bool `json:"hardcore,omitempty"`
|
|
Dimensions []string `json:"dimensions,omitempty"`
|
|
MaxPlayers int32 `json:"maxPlayers,omitempty"`
|
|
ViewDistance int32 `json:"viewDistance,omitempty"`
|
|
SimulationDistance int32 `json:"simulationDistance,omitempty"`
|
|
ReducedDebugInfo bool `json:"reducedDebugInfo,omitempty"`
|
|
EnableRespawnScreen bool `json:"enableRespawnScreen,omitempty"`
|
|
DoLimitedCrafting bool `json:"doLimitedCrafting,omitempty"`
|
|
DimensionType int32 `json:"dimensionType,omitempty"`
|
|
DimensionName string `json:"dimensionName,omitempty"`
|
|
HashedSeed int64 `json:"hashedSeed,omitempty"`
|
|
GameMode byte `json:"gameMode,omitempty"`
|
|
PreviousGameMode byte `json:"previousGameMode,omitempty"`
|
|
IsDebug bool `json:"isDebug,omitempty"`
|
|
IsFlat bool `json:"isFlat,omitempty"`
|
|
HasDeathLocation bool `json:"hasDeathLocation,omitempty"`
|
|
DeathDimensionName string `json:"deathDimensionName,omitempty"`
|
|
DeathLocation Position `json:"deathLocation,omitempty"`
|
|
PortalCooldown int32 `json:"portalCooldown,omitempty"`
|
|
EnforcesSecureChat bool `json:"enforcesSecureChat,omitempty"`
|
|
}
|
|
|
|
type DifficultyObject struct {
|
|
Difficulty byte `json:"difficulty,omitempty"`
|
|
Locked bool `json:"locked,omitempty"`
|
|
}
|
|
|
|
type TagArray struct {
|
|
TagName string `json:"tagName,omitempty"`
|
|
Entries []int32 `json:"entries,omitempty"`
|
|
}
|
|
|
|
type UpdateTag struct {
|
|
TagRegistryIdentifier string `json:"tagRegistryIdentifier,omitempty"`
|
|
Tags []TagArray `json:"tags,omitempty"`
|
|
}
|
|
|
|
type RegistryEntry struct {
|
|
EntryID string `json:"entryID,omitempty"`
|
|
HasNBT bool `json:"hasNBT,omitempty"`
|
|
//TODO implement the nbt data
|
|
NBTData []byte `json:"nbtData,omitempty"`
|
|
}
|
|
|
|
type RegistryData struct {
|
|
RegistryID string `json:"registryID,omitempty"`
|
|
Entries []RegistryEntry `json:"entries,omitempty"`
|
|
}
|
|
|
|
type Favicon struct {
|
|
PngData []byte `json:"pngData,omitempty"`
|
|
}
|
|
|
|
type DatapackInfo struct {
|
|
Namespace string `json:"namespace,omitempty"`
|
|
ID string `json:"id,omitempty"`
|
|
Version string `json:"version,omitempty"`
|
|
}
|
|
|
|
type TextPiece struct {
|
|
Text string `json:"text,omitempty"`
|
|
Color string `json:"color,omitempty"`
|
|
CleanText string `json:"cleantext,omitempty"`
|
|
}
|
|
|
|
type TextComponent struct {
|
|
Text string `json:"text,omitempty"`
|
|
Extra []TextPiece `json:"extra,omitempty"`
|
|
CleanText string `json:"cleantext,omitempty"`
|
|
}
|
|
|
|
type WorldBorderInfo struct {
|
|
X float64 `json:"x,omitempty"`
|
|
Z float64 `json:"z,omitempty"`
|
|
OldDiameter float64 `json:"oldDiameter,omitempty"`
|
|
NewDiameter float64 `json:"newDiameter,omitempty"`
|
|
Speed int64 `json:"speed,omitempty"`
|
|
PortalTeleportBoundary int32 `json:"portalTeleportBoundry,omitempty"`
|
|
WarningBlocks int32 `json:"warningBlocks,omitempty"`
|
|
WarningTime int32 `json:"warningTime,omitempty"`
|
|
}
|
|
|
|
// Custom unmarshaler for the TextComponent type
|
|
func (d *TextComponent) UnmarshalJSON(data []byte) error {
|
|
// Try to unmarshal as a simple string
|
|
var text string
|
|
if err := json.Unmarshal(data, &text); err == nil {
|
|
d.Text = text
|
|
d.CleanText = cleanMinecraftFormatting(text)
|
|
return nil
|
|
}
|
|
|
|
// If that fails, try to unmarshal as an object with a "text" field and optional "extra" field
|
|
var temp struct {
|
|
Text string `json:"text,omitempty"`
|
|
Extra []TextPiece `json:"extra,omitempty"`
|
|
}
|
|
if err := json.Unmarshal(data, &temp); err == nil {
|
|
d.Text = temp.Text
|
|
d.Extra = temp.Extra
|
|
|
|
// Clean the text and combine it with cleaned children texts
|
|
var sb strings.Builder
|
|
sb.Write([]byte(temp.Text))
|
|
for _, extra := range temp.Extra {
|
|
sb.Write([]byte(extra.Text))
|
|
}
|
|
d.CleanText = cleanMinecraftFormatting(sb.String())
|
|
|
|
return nil
|
|
}
|
|
|
|
// Return an error if neither unmarshaling succeeds
|
|
return fmt.Errorf("could not unmarshal TextComponent: data is neither a string nor an object with 'text' and 'extra' fields")
|
|
}
|
|
|
|
// Function to clean Minecraft formatting from a text
|
|
func cleanMinecraftFormatting(text string) string {
|
|
// Regex to match Minecraft formatting codes like § and &
|
|
regex := regexp.MustCompile(`(?i)§[0-9A-FK-OR]`)
|
|
return regex.ReplaceAllString(text, "")
|
|
}
|
|
|
|
// Custom unmarshaler for the TextComponent type
|
|
func (f *Favicon) UnmarshalJSON(data []byte) error {
|
|
// Try to unmarshal as a simple string
|
|
var text string
|
|
if err := json.Unmarshal(data, &text); err == nil {
|
|
if strings.HasPrefix(text, "data:image/png;base64,") {
|
|
trimmed := strings.TrimPrefix(text, "data:image/png;base64,")
|
|
pngDecoder := base64.NewDecoder(base64.StdEncoding, strings.NewReader(trimmed))
|
|
pngData, err := io.ReadAll(pngDecoder)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
f.PngData = pngData
|
|
return nil
|
|
} else {
|
|
return fmt.Errorf("wrong prefix for favicon")
|
|
}
|
|
}
|
|
return errors.New("could not unmarshal Favicon")
|
|
}
|
|
|
|
type Response struct {
|
|
RawMessage string `json:"rawMessage,omitempty"`
|
|
Version struct {
|
|
Name string `json:"name"`
|
|
Protocol int32 `json:"protocol"`
|
|
} `json:"version"`
|
|
Players struct {
|
|
Max int32 `json:"max"`
|
|
Online int32 `json:"online"`
|
|
Sample []Player `json:"sample"`
|
|
} `json:"players"`
|
|
Description TextComponent `json:"description"` // Handles both string and object with "text" and "extra" fields
|
|
Favicon Favicon `json:"favicon"`
|
|
EnforcesSecureChat bool `json:"enforcesSecureChat"`
|
|
PreventsChatReports bool `json:"preventsChatReports"`
|
|
CompressionThreshold int32 `json:"compressionThreshold,omitempty"`
|
|
IsOfflineMode bool `json:"IsOfflineMode,omitempty"`
|
|
PluginDataSent map[string]string `json:"PluginDataSent,omitempty"`
|
|
FeatureFlags []string `json:"featureFlags,omitempty"`
|
|
EnabledDatapacks []DatapackInfo `json:"EnabledDatapacks,omitempty"`
|
|
Encryption bool `json:"Encryption,omitempty"`
|
|
Message TextComponent `json:"Message,omitempty"`
|
|
RegistryDatas []RegistryData `json:"RegistryDatas,omitempty"`
|
|
Tags []UpdateTag `json:"Tags,omitempty"`
|
|
Username string `json:"Username,omitempty"`
|
|
PlayerLoginInfo LoginInfo `json:"PlayerLoginInfo,omitempty"`
|
|
ServerDifficulty DifficultyObject `json:"ServerDifficulty,omitempty"`
|
|
PlayerAbilities PlayerAbilitiesObject `json:"PlayerAbilities,omitempty"`
|
|
PlayerSlot byte `json:"PlayerSlot,omitempty"`
|
|
PlayerLocation PlayerPosition `json:"PlayerLocation,omitempty"`
|
|
DefaultPositionSpawn DefaultSpawnPosition `json:"DefaultPositionSpawn,omitempty"`
|
|
TimeOfDay int64 `json:"TimeOfDay,omitempty"`
|
|
WorldAge int64 `json:"worldAge,omitempty"`
|
|
WorldBorder WorldBorderInfo `json:"worldBorder,omitempty"`
|
|
PlayersInfo []PlayerUpdate `json:"playersinfo,omitempty"`
|
|
ServerInfo ServerInfo `json:"ServerInfo,omitempty"`
|
|
ScanProgress byte `json:"ScanProgress,omitempty"`
|
|
}
|