diff --git a/bot/basic/info.go b/bot/basic/info.go index 390cff9..81fc4ac 100644 --- a/bot/basic/info.go +++ b/bot/basic/info.go @@ -4,15 +4,16 @@ import ( "unsafe" "github.com/Tnze/go-mc/data/packetid" + "github.com/Tnze/go-mc/nbt" pk "github.com/Tnze/go-mc/net/packet" ) // WorldInfo content player info in server. type WorldInfo struct { DimensionCodec DimensionCodec - Dimension Dimension - WorldNames []string // Identifiers for all worlds on the server. - WorldName string // Name of the world being spawned into. + DimensionType string + DimensionNames []string // Identifiers for all worlds on the server. + DimensionName string // Name of the world being spawned into. HashedSeed int64 // First 8 bytes of the SHA-256 hash of the world's seed. Used client side for biome noise MaxPlayers int32 // Was once used by the client to draw the player list, but now is ignored. ViewDistance int32 // Render distance (2-32). @@ -22,112 +23,52 @@ type WorldInfo struct { IsDebug bool // True if the world is a debug mode world; debug mode worlds cannot be modified and have predefined blocks. IsFlat bool // True if the world is a superflat world; flat worlds have different void fog and a horizon at y=0 instead of y=63. } + type Dimension struct { - Name string `nbt:"name"` - Id int `nbt:"id"` - Element struct { - PiglinSafe byte `nbt:"piglin_safe"` - Natural byte `nbt:"natural"` - AmbientLight float64 `nbt:"ambient_light"` - MonsterSpawnBlockLightLimit int `nbt:"monster_spawn_block_light_limit"` - Infiniburn string `nbt:"infiniburn"` - RespawnAnchorWorks byte `nbt:"respawn_anchor_works"` - HasSkylight byte `nbt:"has_skylight"` - BedWorks byte `nbt:"bed_works"` - Effects string `nbt:"effects"` - HasRaids byte `nbt:"has_raids"` - Shrunk byte `nbt:"shrunk"` - LogicalHeight int `nbt:"logical_height"` - CoordinateScale float64 `nbt:"coordinate_scale"` - MinY int `nbt:"min_y"` - MonsterSpawnLightLevel int `nbt:"monster_spawn_light_level"` - Ultrawarm byte `nbt:"ultrawarm"` - HasCeiling byte `nbt:"has_ceiling"` - Height int `nbt:"height"` - FixedTime int64 `nbt:"fixed_time,omitempty"` - } `nbt:"element"` + FixedTime int64 `nbt:"fixed_time,omitempty"` + HasSkylight bool `nbt:"has_skylight"` + HasCeiling bool `nbt:"has_ceiling"` + Ultrawarm bool `nbt:"ultrawarm"` + Natural bool `nbt:"natural"` + CoordinateScale float64 `nbt:"coordinate_scale"` + BedWorks bool `nbt:"bed_works"` + RespawnAnchorWorks byte `nbt:"respawn_anchor_works"` + MinY int32 `nbt:"min_y"` + Height int32 `nbt:"height"` + LogicalHeight int32 `nbt:"logical_height"` + InfiniteBurn string `nbt:"infiniburn"` + Effects string `nbt:"effects"` + AmbientLight float64 `nbt:"ambient_light"` + + PiglinSafe byte `nbt:"piglin_safe"` + HasRaids byte `nbt:"has_raids"` + MonsterSpawnLightLevel nbt.RawMessage `nbt:"monster_spawn_light_level"` // Tag_Int or {type:"minecraft:uniform", value:{min_inclusive: Tag_Int, max_inclusive: Tag_Int}} + MonsterSpawnBlockLightLimit int32 `nbt:"monster_spawn_block_light_limit"` } + type DimensionCodec struct { // What is Below? (wiki.vg) - ChatType struct { - Type string `nbt:"type"` - Value []struct { - Name string `nbt:"name"` - Id int `nbt:"id"` - Element struct { - Chat struct { - Decoration struct { - TranslationKey interface{} `nbt:"translation_key"` - Style struct { - Color interface{} `nbt:"color,omitempty"` - Italic interface{} `nbt:"italic,omitempty"` - } `nbt:"style"` - Parameters []interface{} `nbt:"parameters"` - } `nbt:"decoration,omitempty"` - } `nbt:"chat,omitempty"` - Narration struct { - Priority interface{} `nbt:"priority"` - Decoration struct { - TranslationKey interface{} `nbt:"translation_key"` - Style struct { - } `nbt:"style"` - Parameters []interface{} `nbt:"parameters"` - } `nbt:"decoration,omitempty"` - } `nbt:"narration,omitempty"` - Overlay struct { - } `nbt:"overlay,omitempty"` - } `nbt:"element"` - } `nbt:"value"` - } `nbt:"minecraft:chat_type"` - DimensionType struct { - Type string `nbt:"type"` - Value []Dimension `nbt:"value"` - } `nbt:"minecraft:dimension_type"` - WorldGenBiome struct { - Type string `nbt:"type"` - Value []struct { - Name string `nbt:"name"` - Id int `nbt:"id"` - Element struct { - Precipitation interface{} `nbt:"precipitation"` - Effects struct { - SkyColor int `nbt:"sky_color"` - WaterFogColor int `nbt:"water_fog_color"` - FogColor int `nbt:"fog_color"` - WaterColor int `nbt:"water_color"` - MoodSound struct { - TickDelay int `nbt:"tick_delay"` - Offset interface{} `nbt:"offset"` - Sound string `nbt:"sound"` - BlockSearchExtent int `nbt:"block_search_extent"` - } `nbt:"mood_sound"` - GrassColorModifier interface{} `nbt:"grass_color_modifier,omitempty"` - Music struct { - ReplaceCurrentMusic interface{} `nbt:"replace_current_music"` - MaxDelay int `nbt:"max_delay"` - Sound string `nbt:"sound"` - MinDelay int `nbt:"min_delay"` - } `nbt:"music,omitempty"` - FoliageColor int `nbt:"foliage_color,omitempty"` - GrassColor int `nbt:"grass_color,omitempty"` - AmbientSound string `nbt:"ambient_sound,omitempty"` - AdditionsSound struct { - Sound string `nbt:"sound"` - TickChance interface{} `nbt:"tick_chance"` - } `nbt:"additions_sound,omitempty"` - Particle struct { - Probability interface{} `nbt:"probability"` - Options struct { - Type string `nbt:"type"` - } `nbt:"options"` - } `nbt:"particle,omitempty"` - } `nbt:"effects"` - Temperature interface{} `nbt:"temperature"` - Downfall interface{} `nbt:"downfall"` - TemperatureModifier interface{} `nbt:"temperature_modifier,omitempty"` - } `nbt:"element"` - } `nbt:"value"` - } `nbt:"minecraft:worldgen/biome"` + ChatType Registry[nbt.RawMessage] `nbt:"minecraft:chat_type"` + DimensionType Registry[Dimension] `nbt:"minecraft:dimension_type"` + WorldGenBiome Registry[nbt.RawMessage] `nbt:"minecraft:worldgen/biome"` +} + +type Registry[E any] struct { + Type string `nbt:"type"` + Value []struct { + Name string `nbt:"name"` + ID int32 `nbt:"id"` + Element E `nbt:"element"` + } `nbt:"value"` +} + +func (r *Registry[E]) Find(name string) *E { + for i := range r.Value { + if r.Value[i].Name == name { + return &r.Value[i].Element + } + } + return nil } type PlayerInfo struct { @@ -143,17 +84,15 @@ type ServInfo struct { } func (p *Player) handleLoginPacket(packet pk.Packet) error { - var WorldNames = make([]pk.Identifier, 0) - var currentDimension pk.Identifier err := packet.Scan( (*pk.Int)(&p.EID), (*pk.Boolean)(&p.Hardcore), (*pk.UnsignedByte)(&p.Gamemode), (*pk.Byte)(&p.PrevGamemode), - pk.Array(&WorldNames), + pk.Array((*[]pk.Identifier)(unsafe.Pointer(&p.DimensionNames))), pk.NBT(&p.WorldInfo.DimensionCodec), - ¤tDimension, - (*pk.Identifier)(&p.WorldName), + (*pk.Identifier)(&p.WorldInfo.DimensionType), + (*pk.Identifier)(&p.DimensionName), (*pk.Long)(&p.HashedSeed), (*pk.VarInt)(&p.MaxPlayers), (*pk.VarInt)(&p.ViewDistance), @@ -166,18 +105,6 @@ func (p *Player) handleLoginPacket(packet pk.Packet) error { if err != nil { return Error{err} } - // This line should work "like" the following code but without copy things - // p.WorldNames = make([]string, len(WorldNames)) - // for i, v := range WorldNames { - // p.WorldNames[i] = string(v) - // } - p.WorldNames = *(*[]string)(unsafe.Pointer(&WorldNames)) - for _, d := range p.DimensionCodec.DimensionType.Value { - if d.Name == string(currentDimension) { - p.Dimension = d - } - } - err = p.c.Conn.WritePacket(pk.Marshal( //PluginMessage packet packetid.ServerboundCustomPayload, pk.Identifier("minecraft:brand"), @@ -205,10 +132,9 @@ func (p *Player) handleLoginPacket(packet pk.Packet) error { } func (p *Player) handleRespawnPacket(packet pk.Packet) error { var copyMeta bool - var currentDimension pk.Identifier err := packet.Scan( - ¤tDimension, - (*pk.Identifier)(&p.WorldName), + (*pk.String)(&p.DimensionType), + (*pk.Identifier)(&p.DimensionName), (*pk.Long)(&p.HashedSeed), (*pk.UnsignedByte)(&p.Gamemode), (*pk.Byte)(&p.PrevGamemode), @@ -219,10 +145,5 @@ func (p *Player) handleRespawnPacket(packet pk.Packet) error { if err != nil { return Error{err} } - for _, d := range p.DimensionCodec.DimensionType.Value { - if d.Name == string(currentDimension) { - p.Dimension = d - } - } return nil } diff --git a/bot/world/chunks.go b/bot/world/chunks.go index fc5f1aa..688a08d 100644 --- a/bot/world/chunks.go +++ b/bot/world/chunks.go @@ -39,8 +39,8 @@ func (w *World) onPlayerSpawn(pk.Packet) error { func (w *World) handleLevelChunkWithLightPacket(packet pk.Packet) error { var pos level.ChunkPos - - chunk := level.EmptyChunk(int(w.p.WorldInfo.Dimension.Element.Height / 16)) + currentDimType := w.p.WorldInfo.DimensionCodec.DimensionType.Find(w.p.DimensionType) + chunk := level.EmptyChunk(int(currentDimType.Height) / 16) if err := packet.Scan(&pos, chunk); err != nil { return err } diff --git a/nbt/decode.go b/nbt/decode.go index 297619c..c17f7b0 100644 --- a/nbt/decode.go +++ b/nbt/decode.go @@ -39,10 +39,6 @@ func (d *Decoder) Decode(v interface{}) (string, error) { return tagName, fmt.Errorf("nbt: %w", err) } - if c := d.checkCompressed(tagType); c != "" { - return tagName, fmt.Errorf("nbt: unknown Tag, maybe compressed by %s, uncompress it first", c) - } - // We decode val not val.Elem because the Unmarshaler interface // test must be applied at the top level of the value. err = d.unmarshal(val, tagType) @@ -78,7 +74,7 @@ func (d *Decoder) unmarshal(val reflect.Value, tagType byte) error { switch tagType { default: - return fmt.Errorf("unknown Tag 0x%02x", tagType) + return fmt.Errorf("unknown Tag %#02x", tagType) case TagEnd: return ErrEND @@ -505,7 +501,7 @@ func (d *Decoder) rawRead(tagType byte) error { var buf [8]byte switch tagType { default: - return fmt.Errorf("unknown to read 0x%02x", tagType) + return fmt.Errorf("unknown to read %#02x", tagType) case TagByte: _, err := d.readByte() return err @@ -590,7 +586,12 @@ func (d *Decoder) readTag() (tagType byte, tagName string, err error) { return } - if tagType != TagEnd { //Read Tag + switch tagType { + case 0x1f, 0x78: + c := d.checkCompressed(tagType) + err = fmt.Errorf("nbt: unknown Tag %#02x, which seems like %s header and you should uncompress it first", tagType, c) + case TagEnd: + default: //Read Tag tagName, err = d.readString() } return diff --git a/nbt/encode.go b/nbt/encode.go index dfd097c..b2ae079 100644 --- a/nbt/encode.go +++ b/nbt/encode.go @@ -200,6 +200,9 @@ func (e *Encoder) writeValue(val reflect.Value, tagType byte) error { if tagProps.OmitEmpty && isEmptyValue(v) { continue } + if tagProps.Type == TagNone { + return fmt.Errorf("encode %q error: unsupport type %v", tagProps.Name, v.Type()) + } if err := e.marshal(val.Field(i), tagProps.Type, tagProps.Name); err != nil { return err @@ -216,7 +219,7 @@ func (e *Encoder) writeValue(val reflect.Value, tagType byte) error { } tagType, tagValue := getTagType(r.Value()) if tagType == TagNone { - return errors.New("unsupported value " + tagValue.String()) + return fmt.Errorf("encoding %q error: unsupport type %v", tagName, tagValue.Type()) } if err := e.marshal(tagValue, tagType, tagName); err != nil { diff --git a/net/packet/packet_test.go b/net/packet/packet_test.go index e3de8ff..e231895 100644 --- a/net/packet/packet_test.go +++ b/net/packet/packet_test.go @@ -109,7 +109,7 @@ func ExamplePacket_Scan_joinGame() { Hardcore pk.Boolean Gamemode pk.UnsignedByte PreGamemode pk.Byte - WorldNames = make([]pk.Identifier, 0) // This cannot replace with "var WorldNames []pk.Identifier" because "nil" has no type information + WorldNames = make([]pk.Identifier, 0) // This cannot replace with "var DimensionNames []pk.Identifier" because "nil" has no type information DimensionCodec struct { DimensionType interface{} `nbt:"minecraft:dimension_type"` WorldgenBiome interface{} `nbt:"minecraft:worldgen/biome"` diff --git a/server/gameplay.go b/server/gameplay.go index 8c5c713..ce45cd0 100644 --- a/server/gameplay.go +++ b/server/gameplay.go @@ -26,7 +26,7 @@ type Game struct { components []Component } -func (g *Game) AcceptPlayer(name string, id uuid.UUID, protocol int32, conn *net.Conn) { +func (g *Game) AcceptPlayer(name string, id uuid.UUID, profilePubKey *rsa.PublicKey, protocol int32, conn *net.Conn) { conn.Close() }