1.19.2 chat support
This commit is contained in:
@ -3,16 +3,15 @@ package basic
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"github.com/Tnze/go-mc/chat"
|
||||
"github.com/Tnze/go-mc/registry"
|
||||
|
||||
"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 {
|
||||
RegistryCodec RegistryCodec
|
||||
RegistryCodec registry.NetworkCodec
|
||||
DimensionType string
|
||||
DimensionNames []string // Identifiers for all worlds on the server.
|
||||
DimensionName string // Name of the world being spawned into.
|
||||
@ -26,70 +25,6 @@ type WorldInfo struct {
|
||||
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 {
|
||||
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 ChatType struct {
|
||||
Chat chat.Decoration `nbt:"chat"`
|
||||
Narration chat.Decoration `nbt:"narration"`
|
||||
}
|
||||
|
||||
type RegistryCodec struct {
|
||||
// What is Below? (wiki.vg)
|
||||
ChatType Registry[ChatType] `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
|
||||
}
|
||||
|
||||
func (r *Registry[E]) FindByID(id int32) *E {
|
||||
if id >= 0 && id < int32(len(r.Value)) && r.Value[id].ID == id {
|
||||
return &r.Value[id].Element
|
||||
}
|
||||
for i := range r.Value {
|
||||
if r.Value[i].ID == id {
|
||||
return &r.Value[i].Element
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type PlayerInfo struct {
|
||||
EID int32 // The player's Entity ID (EID).
|
||||
Hardcore bool // Is hardcore
|
||||
|
179
bot/msg/chat.go
179
bot/msg/chat.go
@ -1,16 +1,16 @@
|
||||
package msg
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/Tnze/go-mc/chat/sign"
|
||||
|
||||
"github.com/Tnze/go-mc/bot"
|
||||
"github.com/Tnze/go-mc/bot/basic"
|
||||
"github.com/Tnze/go-mc/chat"
|
||||
"github.com/Tnze/go-mc/data/packetid"
|
||||
pk "github.com/Tnze/go-mc/net/packet"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type Manager struct {
|
||||
@ -24,149 +24,40 @@ func New(c *bot.Client, p *basic.Player, events EventsHandler) *Manager {
|
||||
}
|
||||
|
||||
func attachPlayerMsg(c *bot.Client, p *basic.Player, handler func(msg chat.Message) error) {
|
||||
c.Events.AddListener(bot.PacketHandler{
|
||||
Priority: 64, ID: packetid.ClientboundPlayerChat,
|
||||
F: func(packet pk.Packet) error {
|
||||
var message PlayerMessage
|
||||
if err := packet.Scan(&message); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var content chat.Message
|
||||
if message.content.formatted != nil {
|
||||
content = *message.content.formatted
|
||||
} else {
|
||||
content = chat.Text(message.content.plainMsg)
|
||||
}
|
||||
|
||||
ct := p.WorldInfo.RegistryCodec.ChatType.FindByID(message.chatType.ID)
|
||||
if ct == nil {
|
||||
return fmt.Errorf("chat type %d not found", message.chatType.ID)
|
||||
}
|
||||
|
||||
msg := (*chat.Type)(&message.chatType).Decorate(content, &ct.Chat)
|
||||
return handler(msg)
|
||||
c.Events.AddListener(
|
||||
bot.PacketHandler{
|
||||
Priority: 64, ID: packetid.ClientboundPlayerChatHeader,
|
||||
F: func(packet pk.Packet) error {
|
||||
fmt.Println(packetid.ClientboundPacketID(packet.ID))
|
||||
fmt.Println(hex.Dump(packet.Data))
|
||||
return nil
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
bot.PacketHandler{
|
||||
Priority: 64, ID: packetid.ClientboundPlayerChat,
|
||||
F: func(packet pk.Packet) error {
|
||||
var message sign.PlayerMessage
|
||||
var chatType chat.Type
|
||||
fmt.Println(packetid.ClientboundPacketID(packet.ID))
|
||||
fmt.Println(hex.Dump(packet.Data))
|
||||
if err := packet.Scan(&message, &chatType); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
type PlayerMessage struct {
|
||||
// SignedMessageHeader
|
||||
signature []byte
|
||||
sender uuid.UUID
|
||||
// MessageSignature
|
||||
msgSignature []byte
|
||||
// SignedMessageBody
|
||||
content msgContent
|
||||
timestamp time.Time
|
||||
salt int64
|
||||
prevMessages []prevMsg
|
||||
// Optional<Component>
|
||||
unsignedContent *chat.Message
|
||||
// FilterMask
|
||||
filterType int32
|
||||
filterSet pk.BitSet
|
||||
// ChatType
|
||||
chatType chatType
|
||||
}
|
||||
var content chat.Message
|
||||
if message.MessageBody.Message != nil {
|
||||
content = *message.MessageBody.Message
|
||||
} else {
|
||||
content = chat.Text(message.MessageBody.PlainMessage)
|
||||
}
|
||||
|
||||
func (p *PlayerMessage) String() string {
|
||||
return p.content.plainMsg
|
||||
}
|
||||
ct := p.WorldInfo.RegistryCodec.ChatType.FindByID(chatType.ID)
|
||||
if ct == nil {
|
||||
return fmt.Errorf("chat type %d not found", chatType.ID)
|
||||
}
|
||||
|
||||
func (p *PlayerMessage) ReadFrom(r io.Reader) (n int64, err error) {
|
||||
var hasMsgSign, hasUnsignedContent pk.Boolean
|
||||
var timestamp pk.Long
|
||||
var unsignedContent chat.Message
|
||||
n, err = pk.Tuple{
|
||||
&hasMsgSign,
|
||||
pk.Opt{
|
||||
Has: &hasMsgSign,
|
||||
Field: (*pk.ByteArray)(&p.signature),
|
||||
},
|
||||
(*pk.UUID)(&p.sender),
|
||||
(*pk.ByteArray)(&p.msgSignature),
|
||||
&p.content,
|
||||
×tamp,
|
||||
(*pk.Long)(&p.salt),
|
||||
pk.Array(&p.prevMessages),
|
||||
&hasUnsignedContent,
|
||||
pk.Opt{
|
||||
Has: &hasUnsignedContent,
|
||||
Field: &unsignedContent,
|
||||
},
|
||||
(*pk.VarInt)(&p.filterType),
|
||||
pk.Opt{
|
||||
Has: func() bool { return p.filterType == 2 },
|
||||
Field: &p.filterSet,
|
||||
},
|
||||
&p.chatType,
|
||||
}.ReadFrom(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
p.timestamp = time.UnixMilli(int64(timestamp))
|
||||
if hasUnsignedContent {
|
||||
p.unsignedContent = &unsignedContent
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
type msgContent struct {
|
||||
plainMsg string
|
||||
formatted *chat.Message
|
||||
}
|
||||
|
||||
func (m *msgContent) ReadFrom(r io.Reader) (n int64, err error) {
|
||||
var hasFormatted pk.Boolean
|
||||
n1, err := (*pk.String)(&m.plainMsg).ReadFrom(r)
|
||||
if err != nil {
|
||||
return n1, err
|
||||
}
|
||||
n2, err := hasFormatted.ReadFrom(r)
|
||||
if err != nil {
|
||||
return n1 + n2, err
|
||||
}
|
||||
if hasFormatted {
|
||||
m.formatted = new(chat.Message)
|
||||
n3, err := m.formatted.ReadFrom(r)
|
||||
return n1 + n2 + n3, err
|
||||
}
|
||||
return n1 + n2, err
|
||||
}
|
||||
|
||||
type prevMsg struct {
|
||||
sender uuid.UUID
|
||||
signature []byte
|
||||
}
|
||||
|
||||
func (p *prevMsg) ReadFrom(r io.Reader) (n int64, err error) {
|
||||
return pk.Tuple{
|
||||
(*pk.UUID)(&p.sender),
|
||||
(*pk.ByteArray)(&p.signature),
|
||||
}.ReadFrom(r)
|
||||
}
|
||||
|
||||
type chatType chat.Type
|
||||
|
||||
func (c *chatType) ReadFrom(r io.Reader) (n int64, err error) {
|
||||
var hasTargetName pk.Boolean
|
||||
n1, err := (*pk.VarInt)(&c.ID).ReadFrom(r)
|
||||
if err != nil {
|
||||
return n1, err
|
||||
}
|
||||
n2, err := c.SenderName.ReadFrom(r)
|
||||
if err != nil {
|
||||
return n1 + n2, err
|
||||
}
|
||||
n3, err := hasTargetName.ReadFrom(r)
|
||||
if err != nil {
|
||||
return n1 + n2 + n3, err
|
||||
}
|
||||
if hasTargetName {
|
||||
c.TargetName = new(chat.Message)
|
||||
n4, err := c.TargetName.ReadFrom(r)
|
||||
return n1 + n2 + n3 + n4, err
|
||||
}
|
||||
return n1 + n2 + n3, nil
|
||||
msg := chatType.Decorate(content, &ct.Chat)
|
||||
return handler(msg)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ func (w *World) onPlayerSpawn(pk.Packet) error {
|
||||
|
||||
func (w *World) handleLevelChunkWithLightPacket(packet pk.Packet) error {
|
||||
var pos level.ChunkPos
|
||||
currentDimType := w.p.WorldInfo.RegistryCodec.DimensionType.Find(w.p.DimensionType)
|
||||
_, currentDimType := w.p.WorldInfo.RegistryCodec.DimensionType.Find(w.p.DimensionType)
|
||||
if currentDimType == nil {
|
||||
return errors.New("dimension type " + w.p.DimensionType + " not found")
|
||||
}
|
||||
|
Reference in New Issue
Block a user