minerecon/response.go
2024-08-23 12:19:44 +02:00

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"`
}