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