Merge branch 'chat'
This commit is contained in:
@ -3,16 +3,14 @@ package basic
|
|||||||
import (
|
import (
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"github.com/Tnze/go-mc/chat"
|
|
||||||
|
|
||||||
"github.com/Tnze/go-mc/data/packetid"
|
"github.com/Tnze/go-mc/data/packetid"
|
||||||
"github.com/Tnze/go-mc/nbt"
|
|
||||||
pk "github.com/Tnze/go-mc/net/packet"
|
pk "github.com/Tnze/go-mc/net/packet"
|
||||||
|
"github.com/Tnze/go-mc/registry"
|
||||||
)
|
)
|
||||||
|
|
||||||
// WorldInfo content player info in server.
|
// WorldInfo content player info in server.
|
||||||
type WorldInfo struct {
|
type WorldInfo struct {
|
||||||
RegistryCodec RegistryCodec
|
RegistryCodec registry.NetworkCodec
|
||||||
DimensionType string
|
DimensionType string
|
||||||
DimensionNames []string // Identifiers for all worlds on the server.
|
DimensionNames []string // Identifiers for all worlds on the server.
|
||||||
DimensionName string // Name of the world being spawned into.
|
DimensionName string // Name of the world being spawned into.
|
||||||
@ -26,70 +24,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.
|
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 {
|
type PlayerInfo struct {
|
||||||
EID int32 // The player's Entity ID (EID).
|
EID int32 // The player's Entity ID (EID).
|
||||||
Hardcore bool // Is hardcore
|
Hardcore bool // Is hardcore
|
||||||
|
181
bot/msg/chat.go
181
bot/msg/chat.go
@ -1,16 +1,15 @@
|
|||||||
package msg
|
package msg
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/Tnze/go-mc/bot"
|
"github.com/Tnze/go-mc/bot"
|
||||||
"github.com/Tnze/go-mc/bot/basic"
|
"github.com/Tnze/go-mc/bot/basic"
|
||||||
"github.com/Tnze/go-mc/chat"
|
"github.com/Tnze/go-mc/chat"
|
||||||
|
"github.com/Tnze/go-mc/chat/sign"
|
||||||
"github.com/Tnze/go-mc/data/packetid"
|
"github.com/Tnze/go-mc/data/packetid"
|
||||||
pk "github.com/Tnze/go-mc/net/packet"
|
pk "github.com/Tnze/go-mc/net/packet"
|
||||||
"github.com/google/uuid"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Manager struct {
|
type Manager struct {
|
||||||
@ -24,149 +23,43 @@ 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) {
|
func attachPlayerMsg(c *bot.Client, p *basic.Player, handler func(msg chat.Message) error) {
|
||||||
c.Events.AddListener(bot.PacketHandler{
|
c.Events.AddListener(
|
||||||
Priority: 64, ID: packetid.ClientboundPlayerChat,
|
bot.PacketHandler{
|
||||||
F: func(packet pk.Packet) error {
|
Priority: 64, ID: packetid.ClientboundPlayerChatHeader,
|
||||||
var message PlayerMessage
|
F: func(packet pk.Packet) error {
|
||||||
if err := packet.Scan(&message); err != nil {
|
fmt.Println(packetid.ClientboundPacketID(packet.ID))
|
||||||
return err
|
fmt.Println(hex.Dump(packet.Data))
|
||||||
}
|
return nil
|
||||||
|
},
|
||||||
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)
|
|
||||||
},
|
},
|
||||||
})
|
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 {
|
var content chat.Message
|
||||||
// SignedMessageHeader
|
if message.MessageBody.DecoratedMsg != nil {
|
||||||
signature []byte
|
data, _ := message.MessageBody.DecoratedMsg.MarshalJSON()
|
||||||
sender uuid.UUID
|
if err := content.UnmarshalJSON(data); err != nil {
|
||||||
// MessageSignature
|
return err
|
||||||
msgSignature []byte
|
}
|
||||||
// SignedMessageBody
|
} else {
|
||||||
content msgContent
|
content = chat.Text(message.MessageBody.PlainMsg)
|
||||||
timestamp time.Time
|
}
|
||||||
salt int64
|
|
||||||
prevMessages []prevMsg
|
|
||||||
// Optional<Component>
|
|
||||||
unsignedContent *chat.Message
|
|
||||||
// FilterMask
|
|
||||||
filterType int32
|
|
||||||
filterSet pk.BitSet
|
|
||||||
// ChatType
|
|
||||||
chatType chatType
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *PlayerMessage) String() string {
|
ct := p.WorldInfo.RegistryCodec.ChatType.FindByID(chatType.ID)
|
||||||
return p.content.plainMsg
|
if ct == nil {
|
||||||
}
|
return fmt.Errorf("chat type %d not found", chatType.ID)
|
||||||
|
}
|
||||||
|
|
||||||
func (p *PlayerMessage) ReadFrom(r io.Reader) (n int64, err error) {
|
msg := chatType.Decorate(content, &ct.Chat)
|
||||||
var hasMsgSign, hasUnsignedContent pk.Boolean
|
return handler(msg)
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,7 @@ func (w *World) onPlayerSpawn(pk.Packet) error {
|
|||||||
|
|
||||||
func (w *World) handleLevelChunkWithLightPacket(packet pk.Packet) error {
|
func (w *World) handleLevelChunkWithLightPacket(packet pk.Packet) error {
|
||||||
var pos level.ChunkPos
|
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 {
|
if currentDimType == nil {
|
||||||
return errors.New("dimension type " + w.p.DimensionType + " not found")
|
return errors.New("dimension type " + w.p.DimensionType + " not found")
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,24 @@
|
|||||||
package chat
|
package chat
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
|
||||||
|
pk "github.com/Tnze/go-mc/net/packet"
|
||||||
|
)
|
||||||
|
|
||||||
type Decoration struct {
|
type Decoration struct {
|
||||||
TranslationKey string `nbt:"translation_key"`
|
TranslationKey string `nbt:"translation_key"`
|
||||||
Parameters []string `nbt:"parameters"`
|
Parameters []string `nbt:"parameters"`
|
||||||
Style struct {
|
Style struct {
|
||||||
Bold bool `nbt:"bold"`
|
Bold bool `nbt:"bold,omitempty"`
|
||||||
Italic bool `nbt:"italic"`
|
Italic bool `nbt:"italic,omitempty"`
|
||||||
UnderLined bool `nbt:"underlined"`
|
UnderLined bool `nbt:"underlined,omitempty"`
|
||||||
StrikeThrough bool `nbt:"strikethrough"`
|
StrikeThrough bool `nbt:"strikethrough,omitempty"`
|
||||||
Obfuscated bool `nbt:"obfuscated"`
|
Obfuscated bool `nbt:"obfuscated,omitempty"`
|
||||||
Color string `nbt:"color"`
|
Color string `nbt:"color,omitempty"`
|
||||||
Insertion string `nbt:"insertion"`
|
Insertion string `nbt:"insertion,omitempty"`
|
||||||
Font string `nbt:"font"`
|
Font string `nbt:"font,omitempty"`
|
||||||
} `nbt:"style"`
|
} `nbt:"style,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Type struct {
|
type Type struct {
|
||||||
@ -49,3 +55,46 @@ func (t *Type) Decorate(content Message, d *Decoration) (msg Message) {
|
|||||||
Insertion: d.Style.Insertion,
|
Insertion: d.Style.Insertion,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *Type) ReadFrom(r io.Reader) (n int64, err error) {
|
||||||
|
var hasTargetName pk.Boolean
|
||||||
|
n1, err := (*pk.VarInt)(&t.ID).ReadFrom(r)
|
||||||
|
if err != nil {
|
||||||
|
return n1, err
|
||||||
|
}
|
||||||
|
n2, err := t.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 {
|
||||||
|
t.TargetName = new(Message)
|
||||||
|
n4, err := t.TargetName.ReadFrom(r)
|
||||||
|
return n1 + n2 + n3 + n4, err
|
||||||
|
}
|
||||||
|
return n1 + n2 + n3, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Type) WriteTo(w io.Writer) (n int64, err error) {
|
||||||
|
hasTargetName := pk.Boolean(t.TargetName != nil)
|
||||||
|
n1, err := (*pk.VarInt)(&t.ID).WriteTo(w)
|
||||||
|
if err != nil {
|
||||||
|
return n1, err
|
||||||
|
}
|
||||||
|
n2, err := t.SenderName.WriteTo(w)
|
||||||
|
if err != nil {
|
||||||
|
return n1 + n2, err
|
||||||
|
}
|
||||||
|
n3, err := hasTargetName.WriteTo(w)
|
||||||
|
if err != nil {
|
||||||
|
return n1 + n2 + n3, err
|
||||||
|
}
|
||||||
|
if hasTargetName {
|
||||||
|
n4, err := t.TargetName.WriteTo(w)
|
||||||
|
return n1 + n2 + n3 + n4, err
|
||||||
|
}
|
||||||
|
return n1 + n2 + n3, nil
|
||||||
|
}
|
||||||
|
@ -132,13 +132,13 @@ func (m *Message) UnmarshalJSON(raw []byte) (err error) {
|
|||||||
|
|
||||||
// ReadFrom decode Message in a ChatMsg packet
|
// ReadFrom decode Message in a ChatMsg packet
|
||||||
func (m *Message) ReadFrom(r io.Reader) (int64, error) {
|
func (m *Message) ReadFrom(r io.Reader) (int64, error) {
|
||||||
var Len pk.VarInt
|
var code pk.String
|
||||||
if n, err := Len.ReadFrom(r); err != nil {
|
n, err := code.ReadFrom(r)
|
||||||
|
if err != nil {
|
||||||
return n, err
|
return n, err
|
||||||
}
|
}
|
||||||
lr := &io.LimitedReader{R: r, N: int64(Len)}
|
err = json.Unmarshal([]byte(code), m)
|
||||||
err := json.NewDecoder(lr).Decode(m)
|
return n, err
|
||||||
return int64(Len) - lr.N, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteTo encode Message into a ChatMsg packet
|
// WriteTo encode Message into a ChatMsg packet
|
||||||
|
219
chat/sign/sign.go
Normal file
219
chat/sign/sign.go
Normal file
@ -0,0 +1,219 @@
|
|||||||
|
package sign
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/binary"
|
||||||
|
"encoding/json"
|
||||||
|
"io"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/Tnze/go-mc/chat"
|
||||||
|
pk "github.com/Tnze/go-mc/net/packet"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MessageHeader struct {
|
||||||
|
PrevSignature []byte
|
||||||
|
Sender uuid.UUID
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MessageHeader) WriteTo(w io.Writer) (n int64, err error) {
|
||||||
|
hasSignature := pk.Boolean(len(m.PrevSignature) > 0)
|
||||||
|
n, err = hasSignature.WriteTo(w)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if hasSignature {
|
||||||
|
n2, err := pk.ByteArray(m.PrevSignature).WriteTo(w)
|
||||||
|
n += n2
|
||||||
|
if err != nil {
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
n3, err := pk.UUID(m.Sender).WriteTo(w)
|
||||||
|
return n + n3, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MessageHeader) ReadFrom(r io.Reader) (n int64, err error) {
|
||||||
|
var hasSignature pk.Boolean
|
||||||
|
n, err = hasSignature.ReadFrom(r)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if hasSignature {
|
||||||
|
n2, err := (*pk.ByteArray)(&m.PrevSignature).ReadFrom(r)
|
||||||
|
n += n2
|
||||||
|
if err != nil {
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
n3, err := (*pk.UUID)(&m.Sender).ReadFrom(r)
|
||||||
|
return n + n3, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MessageHeader) Hash(bodyHash []byte) []byte {
|
||||||
|
hash := sha256.New()
|
||||||
|
if m.PrevSignature != nil {
|
||||||
|
hash.Write(m.PrevSignature)
|
||||||
|
}
|
||||||
|
hash.Write(m.Sender[:])
|
||||||
|
hash.Write(bodyHash)
|
||||||
|
return hash.Sum(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
type MessageBody struct {
|
||||||
|
PlainMsg string
|
||||||
|
DecoratedMsg json.RawMessage
|
||||||
|
Timestamp time.Time
|
||||||
|
Salt int64
|
||||||
|
History []HistoryMessage
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MessageBody) WriteTo(w io.Writer) (n int64, err error) {
|
||||||
|
return pk.Tuple{
|
||||||
|
pk.String(m.PlainMsg),
|
||||||
|
pk.Boolean(m.DecoratedMsg != nil),
|
||||||
|
pk.Opt{
|
||||||
|
Has: m.DecoratedMsg != nil,
|
||||||
|
Field: pk.ByteArray(m.DecoratedMsg),
|
||||||
|
},
|
||||||
|
pk.Long(m.Timestamp.UnixMilli()),
|
||||||
|
pk.Long(m.Salt),
|
||||||
|
pk.Array(&m.History),
|
||||||
|
}.WriteTo(w)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MessageBody) ReadFrom(r io.Reader) (n int64, err error) {
|
||||||
|
var hasContent pk.Boolean
|
||||||
|
var timestamp pk.Long
|
||||||
|
n, err = pk.Tuple{
|
||||||
|
(*pk.String)(&m.PlainMsg),
|
||||||
|
&hasContent,
|
||||||
|
pk.Opt{
|
||||||
|
Has: &hasContent,
|
||||||
|
Field: (*pk.ByteArray)(&m.DecoratedMsg),
|
||||||
|
},
|
||||||
|
×tamp,
|
||||||
|
(*pk.Long)(&m.Salt),
|
||||||
|
pk.Array(&m.History),
|
||||||
|
}.ReadFrom(r)
|
||||||
|
m.Timestamp = time.UnixMilli(int64(timestamp))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MessageBody) Hash() []byte {
|
||||||
|
hash := sha256.New()
|
||||||
|
_ = binary.Write(hash, binary.BigEndian, m.Salt)
|
||||||
|
_ = binary.Write(hash, binary.BigEndian, m.Timestamp.Unix())
|
||||||
|
hash.Write([]byte(m.PlainMsg))
|
||||||
|
hash.Write([]byte{70})
|
||||||
|
if m.DecoratedMsg != nil {
|
||||||
|
decorated, _ := m.DecoratedMsg.MarshalJSON()
|
||||||
|
hash.Write(decorated)
|
||||||
|
}
|
||||||
|
for _, v := range m.History {
|
||||||
|
hash.Write([]byte{70})
|
||||||
|
hash.Write(v.Sender[:])
|
||||||
|
hash.Write(v.Signature)
|
||||||
|
}
|
||||||
|
return hash.Sum(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
type FilterMask struct {
|
||||||
|
Type byte
|
||||||
|
Mask pk.BitSet
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FilterMask) WriteTo(w io.Writer) (n int64, err error) {
|
||||||
|
n, err = pk.VarInt(f.Type).WriteTo(w)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if f.Type == 2 {
|
||||||
|
var n1 int64
|
||||||
|
n1, err = f.Mask.WriteTo(w)
|
||||||
|
n += n1
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FilterMask) ReadFrom(r io.Reader) (n int64, err error) {
|
||||||
|
var Type pk.VarInt
|
||||||
|
if n, err = Type.ReadFrom(r); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
f.Type = byte(Type)
|
||||||
|
if f.Type == 2 {
|
||||||
|
var n1 int64
|
||||||
|
n1, err = f.Mask.ReadFrom(r)
|
||||||
|
n += n1
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type PlayerMessage struct {
|
||||||
|
MessageHeader
|
||||||
|
MessageSignature []byte
|
||||||
|
MessageBody
|
||||||
|
UnsignedContent *chat.Message
|
||||||
|
FilterMask
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *PlayerMessage) ReadFrom(r io.Reader) (n int64, err error) {
|
||||||
|
var hasUnsignedContent pk.Boolean
|
||||||
|
return pk.Tuple{
|
||||||
|
&msg.MessageHeader,
|
||||||
|
(*pk.ByteArray)(&msg.MessageSignature),
|
||||||
|
&msg.MessageBody,
|
||||||
|
&hasUnsignedContent,
|
||||||
|
pk.Opt{
|
||||||
|
Has: &hasUnsignedContent,
|
||||||
|
Field: func() pk.FieldDecoder {
|
||||||
|
msg.UnsignedContent = new(chat.Message)
|
||||||
|
return msg.UnsignedContent
|
||||||
|
},
|
||||||
|
},
|
||||||
|
&msg.FilterMask,
|
||||||
|
}.ReadFrom(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *PlayerMessage) WriteTo(w io.Writer) (n int64, err error) {
|
||||||
|
return pk.Tuple{
|
||||||
|
&msg.MessageHeader,
|
||||||
|
pk.ByteArray(msg.MessageSignature),
|
||||||
|
&msg.MessageBody,
|
||||||
|
pk.Boolean(msg.UnsignedContent != nil),
|
||||||
|
pk.Opt{
|
||||||
|
Has: msg.UnsignedContent != nil,
|
||||||
|
Field: msg.UnsignedContent,
|
||||||
|
},
|
||||||
|
&msg.FilterMask,
|
||||||
|
}.WriteTo(w)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *PlayerMessage) Hash() []byte {
|
||||||
|
return msg.MessageHeader.Hash(msg.MessageBody.Hash())
|
||||||
|
}
|
||||||
|
|
||||||
|
type HistoryMessage struct {
|
||||||
|
Sender uuid.UUID
|
||||||
|
Signature []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *HistoryMessage) ReadFrom(r io.Reader) (n int64, err error) {
|
||||||
|
n, err = (*pk.UUID)(&p.Sender).ReadFrom(r)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
n2, err := (*pk.ByteArray)(&p.Signature).ReadFrom(r)
|
||||||
|
return n + n2, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p HistoryMessage) WriteTo(w io.Writer) (n int64, err error) {
|
||||||
|
n, err = pk.UUID(p.Sender).WriteTo(w)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
n2, err := pk.ByteArray(p.Signature).WriteTo(w)
|
||||||
|
return n + n2, err
|
||||||
|
}
|
55
chat/sign/sign_test.go
Normal file
55
chat/sign/sign_test.go
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
package sign
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto"
|
||||||
|
"crypto/rsa"
|
||||||
|
"encoding/base64"
|
||||||
|
"math/big"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMessageBody_Hash(t *testing.T) {
|
||||||
|
msg := PlayerMessage{
|
||||||
|
MessageHeader: MessageHeader{
|
||||||
|
PrevSignature: nil,
|
||||||
|
Sender: uuid.MustParse("58f6356e-b30c-4811-8bfc-d72a9ee99e73"),
|
||||||
|
},
|
||||||
|
MessageSignature: fromBase64("L+gR1hrubxVdhPAe6J+nQCH4U+jsUvJDJE+dzsL8DJwpHfUwT4dgSwm7mI/u8rVxrjPVial1DU0sPZodaGVqcSqApyK38bJThWpmojYtieT63hcsgAZzhGNG0GUrykEzHIMPvAnO0bEBmOqPKWjp/kDxhUgF1kKgmQmiOb1fTazi9dVxGuepVkFXknhp7aZBvQ4oQ94bRY5lTMoWyvNcs3CUpVdeFuWwIVnRAIn+hQQ5rDBvWTgKpFTOuxcCOf6hbtPOO2HZ7TT0rsM1D1LV0R9oHUqlEe4nB0E/vT3GdcplSQTSWc7dDmwTjB+wFeGxrNjFP3FEt3he6a+8Q1svlw=="),
|
||||||
|
MessageBody: MessageBody{
|
||||||
|
PlainMsg: "123",
|
||||||
|
DecoratedMsg: nil,
|
||||||
|
Timestamp: time.Unix(1669990398, 750000000),
|
||||||
|
Salt: -5503869105027681791,
|
||||||
|
History: nil,
|
||||||
|
},
|
||||||
|
UnsignedContent: nil,
|
||||||
|
FilterMask: FilterMask{},
|
||||||
|
}
|
||||||
|
hash := msg.MessageBody.Hash()
|
||||||
|
want := []byte{
|
||||||
|
0x40, 0x2f, 0x63, 0xf1, 0x41, 0x64, 0x83, 0xea,
|
||||||
|
0x64, 0xbc, 0xe1, 0xab, 0x4f, 0xa1, 0xdd, 0xcf,
|
||||||
|
0x31, 0x6b, 0xdf, 0x9, 0xd3, 0xe3, 0x0, 0xed,
|
||||||
|
0xd9, 0x9d, 0x61, 0xb2, 0xfe, 0xe1, 0x23, 0x38,
|
||||||
|
}
|
||||||
|
if !bytes.Equal(hash, want) {
|
||||||
|
t.Errorf("hash not match: want %v, got: %v", want, hash)
|
||||||
|
}
|
||||||
|
N := new(big.Int)
|
||||||
|
N.SetString("19764335557512872060688171398766153113124870942516110890430525316014258628424563821100465499350547856280308462415364939681918846480558609475927282823778386281239015421014618416397562105532153533222767841904849714784545929150813290491806771374240538006775842793512508846836865954304676946257326405255232806869270545112507181469911882584371001804731679283550070980294409246775152428394531383836676761395396452034288295355452943368136472112108083541314359681019016586668426966809565492641015821429276348346645505643438759550398087660394335140584223095644803819139358171070866744685891950091018380254474327892983538632197", 10)
|
||||||
|
if err := rsa.VerifyPKCS1v15(&rsa.PublicKey{N: N, E: 65537}, crypto.SHA256, msg.Hash(), msg.MessageSignature); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func fromBase64(s string) []byte {
|
||||||
|
b, err := base64.StdEncoding.DecodeString(s)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
@ -145,6 +145,7 @@ const (
|
|||||||
ClientboundUpdateMobEffect
|
ClientboundUpdateMobEffect
|
||||||
ClientboundUpdateRecipes
|
ClientboundUpdateRecipes
|
||||||
ClientboundUpdateTags
|
ClientboundUpdateTags
|
||||||
|
ClientboundPacketIDGuard
|
||||||
)
|
)
|
||||||
|
|
||||||
// Game Serverbound
|
// Game Serverbound
|
||||||
@ -200,4 +201,5 @@ const (
|
|||||||
ServerboundTeleportToEntity
|
ServerboundTeleportToEntity
|
||||||
ServerboundUseItemOn
|
ServerboundUseItemOn
|
||||||
ServerboundUseItem
|
ServerboundUseItem
|
||||||
|
ServerboundPacketIDGuard
|
||||||
)
|
)
|
||||||
|
@ -22,7 +22,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
address = flag.String("address", "127.0.0.1", "The server address")
|
address = flag.String("address", "127.0.0.1:25565", "The server address")
|
||||||
name = flag.String("name", "Daze", "The player's name")
|
name = flag.String("name", "Daze", "The player's name")
|
||||||
playerID = flag.String("uuid", "", "The player's UUID")
|
playerID = flag.String("uuid", "", "The player's UUID")
|
||||||
accessToken = flag.String("token", "", "AccessToken")
|
accessToken = flag.String("token", "", "AccessToken")
|
||||||
|
@ -83,7 +83,7 @@ func (s *scanner) eof() int {
|
|||||||
return scanEnd
|
return scanEnd
|
||||||
}
|
}
|
||||||
if s.errContext == "" {
|
if s.errContext == "" {
|
||||||
s.errContext = "unexpected end of JSON input"
|
s.errContext = "unexpected end of SNBT input"
|
||||||
}
|
}
|
||||||
return scanError
|
return scanError
|
||||||
}
|
}
|
||||||
|
@ -28,10 +28,10 @@ func Marshal[ID ~int32 | int](id ID, fields ...FieldEncoder) (pk Packet) {
|
|||||||
// Scan decode the packet and fill data into fields
|
// Scan decode the packet and fill data into fields
|
||||||
func (p Packet) Scan(fields ...FieldDecoder) error {
|
func (p Packet) Scan(fields ...FieldDecoder) error {
|
||||||
r := bytes.NewReader(p.Data)
|
r := bytes.NewReader(p.Data)
|
||||||
for _, v := range fields {
|
for i, v := range fields {
|
||||||
_, err := v.ReadFrom(r)
|
_, err := v.ReadFrom(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("scanning packet field[%d] error: %w", i, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -95,13 +95,13 @@ func (b Boolean) WriteTo(w io.Writer) (int64, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (b *Boolean) ReadFrom(r io.Reader) (n int64, err error) {
|
func (b *Boolean) ReadFrom(r io.Reader) (n int64, err error) {
|
||||||
v, err := readByte(r)
|
n, v, err := readByte(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 1, err
|
return n, err
|
||||||
}
|
}
|
||||||
|
|
||||||
*b = v != 0
|
*b = v != 0
|
||||||
return 1, nil
|
return n, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s String) WriteTo(w io.Writer) (int64, error) {
|
func (s String) WriteTo(w io.Writer) (int64, error) {
|
||||||
@ -134,13 +134,14 @@ func (s *String) ReadFrom(r io.Reader) (n int64, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// readByte read one byte from io.Reader
|
// readByte read one byte from io.Reader
|
||||||
func readByte(r io.Reader) (byte, error) {
|
func readByte(r io.Reader) (int64, byte, error) {
|
||||||
if r, ok := r.(io.ByteReader); ok {
|
if r, ok := r.(io.ByteReader); ok {
|
||||||
return r.ReadByte()
|
v, err := r.ReadByte()
|
||||||
|
return 1, v, err
|
||||||
}
|
}
|
||||||
var v [1]byte
|
var v [1]byte
|
||||||
_, err := io.ReadFull(r, v[:])
|
n, err := r.Read(v[:])
|
||||||
return v[0], err
|
return int64(n), v[0], err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b Byte) WriteTo(w io.Writer) (n int64, err error) {
|
func (b Byte) WriteTo(w io.Writer) (n int64, err error) {
|
||||||
@ -149,12 +150,12 @@ func (b Byte) WriteTo(w io.Writer) (n int64, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (b *Byte) ReadFrom(r io.Reader) (n int64, err error) {
|
func (b *Byte) ReadFrom(r io.Reader) (n int64, err error) {
|
||||||
v, err := readByte(r)
|
n, v, err := readByte(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return n, err
|
||||||
}
|
}
|
||||||
*b = Byte(v)
|
*b = Byte(v)
|
||||||
return 1, nil
|
return n, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u UnsignedByte) WriteTo(w io.Writer) (n int64, err error) {
|
func (u UnsignedByte) WriteTo(w io.Writer) (n int64, err error) {
|
||||||
@ -163,12 +164,12 @@ func (u UnsignedByte) WriteTo(w io.Writer) (n int64, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (u *UnsignedByte) ReadFrom(r io.Reader) (n int64, err error) {
|
func (u *UnsignedByte) ReadFrom(r io.Reader) (n int64, err error) {
|
||||||
v, err := readByte(r)
|
n, v, err := readByte(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return n, err
|
||||||
}
|
}
|
||||||
*u = UnsignedByte(v)
|
*u = UnsignedByte(v)
|
||||||
return 1, nil
|
return n, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s Short) WriteTo(w io.Writer) (int64, error) {
|
func (s Short) WriteTo(w io.Writer) (int64, error) {
|
||||||
@ -267,17 +268,19 @@ func (v VarInt) WriteTo(w io.Writer) (n int64, err error) {
|
|||||||
|
|
||||||
func (v *VarInt) ReadFrom(r io.Reader) (n int64, err error) {
|
func (v *VarInt) ReadFrom(r io.Reader) (n int64, err error) {
|
||||||
var V uint32
|
var V uint32
|
||||||
for sec := byte(0x80); sec&0x80 != 0; n++ {
|
var num, n2 int64
|
||||||
if n > MaxVarIntLen {
|
for sec := byte(0x80); sec&0x80 != 0; num++ {
|
||||||
|
if num > MaxVarIntLen {
|
||||||
return n, errors.New("VarInt is too big")
|
return n, errors.New("VarInt is too big")
|
||||||
}
|
}
|
||||||
|
|
||||||
sec, err = readByte(r)
|
n2, sec, err = readByte(r)
|
||||||
|
n += n2
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return n, err
|
return n, err
|
||||||
}
|
}
|
||||||
|
|
||||||
V |= uint32(sec&0x7F) << uint32(7*n)
|
V |= uint32(sec&0x7F) << uint32(7*num)
|
||||||
}
|
}
|
||||||
|
|
||||||
*v = VarInt(V)
|
*v = VarInt(V)
|
||||||
@ -304,16 +307,18 @@ func (v VarLong) WriteTo(w io.Writer) (n int64, err error) {
|
|||||||
|
|
||||||
func (v *VarLong) ReadFrom(r io.Reader) (n int64, err error) {
|
func (v *VarLong) ReadFrom(r io.Reader) (n int64, err error) {
|
||||||
var V uint64
|
var V uint64
|
||||||
for sec := byte(0x80); sec&0x80 != 0; n++ {
|
var num, n2 int64
|
||||||
if n >= MaxVarLongLen {
|
for sec := byte(0x80); sec&0x80 != 0; num++ {
|
||||||
|
if num >= MaxVarLongLen {
|
||||||
return n, errors.New("VarLong is too big")
|
return n, errors.New("VarLong is too big")
|
||||||
}
|
}
|
||||||
sec, err = readByte(r)
|
n2, sec, err = readByte(r)
|
||||||
|
n += n2
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
V |= uint64(sec&0x7F) << uint64(7*n)
|
V |= uint64(sec&0x7F) << uint64(7*num)
|
||||||
}
|
}
|
||||||
|
|
||||||
*v = VarLong(V)
|
*v = VarLong(V)
|
||||||
@ -455,11 +460,13 @@ func (b *ByteArray) ReadFrom(r io.Reader) (n int64, err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return n1, err
|
return n1, err
|
||||||
}
|
}
|
||||||
buf := bytes.NewBuffer(*b)
|
if cap(*b) < int(Len) {
|
||||||
buf.Reset()
|
*b = make(ByteArray, Len)
|
||||||
n2, err := io.CopyN(buf, r, int64(Len))
|
} else {
|
||||||
*b = buf.Bytes()
|
*b = (*b)[:Len]
|
||||||
return n1 + n2, err
|
}
|
||||||
|
n2, err := io.ReadFull(r, *b)
|
||||||
|
return n1 + int64(n2), err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u UUID) WriteTo(w io.Writer) (n int64, err error) {
|
func (u UUID) WriteTo(w io.Writer) (n int64, err error) {
|
||||||
|
@ -81,8 +81,8 @@ func Array(ary any) Field {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Opt struct {
|
type Opt struct {
|
||||||
Has interface{} // Pointer of bool, or `func() bool`
|
Has any // Pointer of bool, or `func() bool`
|
||||||
Field interface{} // FieldEncoder, FieldDecoder or both (Field)
|
Field any // FieldEncoder, FieldDecoder, `func() FieldEncoder`, `func() FieldDecoder` or `func() Field`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o Opt) has() bool {
|
func (o Opt) has() bool {
|
||||||
@ -103,14 +103,32 @@ func (o Opt) has() bool {
|
|||||||
|
|
||||||
func (o Opt) WriteTo(w io.Writer) (int64, error) {
|
func (o Opt) WriteTo(w io.Writer) (int64, error) {
|
||||||
if o.has() {
|
if o.has() {
|
||||||
return o.Field.(FieldEncoder).WriteTo(w)
|
switch field := o.Field.(type) {
|
||||||
|
case FieldEncoder:
|
||||||
|
return field.WriteTo(w)
|
||||||
|
case func() FieldEncoder:
|
||||||
|
return field().WriteTo(w)
|
||||||
|
case func() Field:
|
||||||
|
return field().WriteTo(w)
|
||||||
|
default:
|
||||||
|
panic("unsupported Field type: " + reflect.TypeOf(o.Field).String())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o Opt) ReadFrom(r io.Reader) (int64, error) {
|
func (o Opt) ReadFrom(r io.Reader) (int64, error) {
|
||||||
if o.has() {
|
if o.has() {
|
||||||
return o.Field.(FieldDecoder).ReadFrom(r)
|
switch field := o.Field.(type) {
|
||||||
|
case FieldDecoder:
|
||||||
|
return field.ReadFrom(r)
|
||||||
|
case func() FieldDecoder:
|
||||||
|
return field().ReadFrom(r)
|
||||||
|
case func() Field:
|
||||||
|
return field().ReadFrom(r)
|
||||||
|
default:
|
||||||
|
panic("unsupported Field type: " + reflect.TypeOf(o.Field).String())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
8
registry/chattype.go
Normal file
8
registry/chattype.go
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
package registry
|
||||||
|
|
||||||
|
import "github.com/Tnze/go-mc/chat"
|
||||||
|
|
||||||
|
type ChatType struct {
|
||||||
|
Chat chat.Decoration `nbt:"chat"`
|
||||||
|
Narration chat.Decoration `nbt:"narration"`
|
||||||
|
}
|
9
registry/codec.go
Normal file
9
registry/codec.go
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
package registry
|
||||||
|
|
||||||
|
import "github.com/Tnze/go-mc/nbt"
|
||||||
|
|
||||||
|
type NetworkCodec struct {
|
||||||
|
ChatType Registry[ChatType] `nbt:"minecraft:chat_type"`
|
||||||
|
DimensionType Registry[Dimension] `nbt:"minecraft:dimension_type"`
|
||||||
|
WorldGenBiome Registry[nbt.RawMessage] `nbt:"minecraft:worldgen/biome"`
|
||||||
|
}
|
25
registry/dimension.go
Normal file
25
registry/dimension.go
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package registry
|
||||||
|
|
||||||
|
import "github.com/Tnze/go-mc/nbt"
|
||||||
|
|
||||||
|
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"`
|
||||||
|
}
|
31
registry/registry.go
Normal file
31
registry/registry.go
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
package registry
|
||||||
|
|
||||||
|
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) (int32, *E) {
|
||||||
|
for i := range r.Value {
|
||||||
|
if r.Value[i].Name == name {
|
||||||
|
return int32(i), &r.Value[i].Element
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1, 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
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
package auth
|
package auth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto"
|
||||||
"crypto/rsa"
|
"crypto/rsa"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"errors"
|
"errors"
|
||||||
@ -67,3 +68,7 @@ func (p *PublicKey) Verify() bool {
|
|||||||
}
|
}
|
||||||
return VerifySignature(encoded, p.Signature)
|
return VerifySignature(encoded, p.Signature)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *PublicKey) VerifyMessage(hash, signature []byte) error {
|
||||||
|
return rsa.VerifyPKCS1v15(p.PubKey, crypto.SHA256, hash, signature)
|
||||||
|
}
|
||||||
|
@ -84,19 +84,19 @@ func (d *MojangLoginHandler) AcceptLogin(conn *net.Conn, protocol int32) (name s
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if hasPubKey {
|
|
||||||
if !pubKey.Verify() {
|
|
||||||
err = LoginFailErr{reason: chat.TranslateMsg("multiplayer.disconnect.invalid_public_key_signature")}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
profilePubKey = &pubKey
|
|
||||||
} else if d.EnforceSecureProfile {
|
|
||||||
err = LoginFailErr{reason: chat.TranslateMsg("multiplayer.disconnect.missing_public_key")}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// auth
|
// auth
|
||||||
if d.OnlineMode {
|
if d.OnlineMode {
|
||||||
|
if hasPubKey {
|
||||||
|
if !pubKey.Verify() {
|
||||||
|
err = LoginFailErr{reason: chat.TranslateMsg("multiplayer.disconnect.invalid_public_key_signature")}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
profilePubKey = &pubKey
|
||||||
|
} else if d.EnforceSecureProfile {
|
||||||
|
err = LoginFailErr{reason: chat.TranslateMsg("multiplayer.disconnect.missing_public_key")}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
var resp *auth.Resp
|
var resp *auth.Resp
|
||||||
// Auth, Encrypt
|
// Auth, Encrypt
|
||||||
resp, err = auth.Encrypt(conn, name, pubKey.PubKey)
|
resp, err = auth.Encrypt(conn, name, pubKey.PubKey)
|
||||||
|
Reference in New Issue
Block a user