Add pk.Option API
This commit is contained in:
32
bot/mcbot.go
32
bot/mcbot.go
@ -79,22 +79,20 @@ func (c *Client) join(ctx context.Context, d *mcnet.Dialer, addr string) error {
|
||||
}
|
||||
// Login Start
|
||||
c.KeyPair, err = user.GetOrFetchKeyPair(c.Auth.AsTk)
|
||||
HasSignature := err == nil
|
||||
KeyPair := pk.Option[keyPair]{
|
||||
Has: err == nil,
|
||||
Val: keyPair(c.KeyPair),
|
||||
}
|
||||
c.UUID, err = uuid.Parse(c.Auth.UUID)
|
||||
HasPlayerUUID := err == nil
|
||||
PlayerUUID := pk.Option[pk.UUID]{
|
||||
Has: err == nil,
|
||||
Val: pk.UUID(c.UUID),
|
||||
}
|
||||
err = c.Conn.WritePacket(pk.Marshal(
|
||||
packetid.LoginStart,
|
||||
pk.String(c.Auth.Name),
|
||||
pk.Boolean(HasSignature),
|
||||
pk.Opt{
|
||||
Has: HasSignature,
|
||||
Field: keyPair(c.KeyPair),
|
||||
},
|
||||
pk.Boolean(HasPlayerUUID),
|
||||
pk.Opt{
|
||||
Has: HasPlayerUUID,
|
||||
Field: pk.UUID(c.UUID),
|
||||
},
|
||||
KeyPair,
|
||||
PlayerUUID,
|
||||
))
|
||||
if err != nil {
|
||||
return LoginErr{"login start", err}
|
||||
@ -148,9 +146,10 @@ func (c *Client) join(ctx context.Context, d *mcnet.Dialer, addr string) error {
|
||||
return LoginErr{"Login Plugin", err}
|
||||
}
|
||||
|
||||
handler, ok := c.LoginPlugin[string(channel)]
|
||||
if ok {
|
||||
data, err = handler(data)
|
||||
var PluginMessageData pk.Option[pk.PluginMessageData]
|
||||
if handler, ok := c.LoginPlugin[string(channel)]; ok {
|
||||
PluginMessageData.Has = true
|
||||
PluginMessageData.Val, err = handler(data)
|
||||
if err != nil {
|
||||
return LoginErr{"Login Plugin", err}
|
||||
}
|
||||
@ -158,8 +157,7 @@ func (c *Client) join(ctx context.Context, d *mcnet.Dialer, addr string) error {
|
||||
|
||||
if err := c.Conn.WritePacket(pk.Marshal(
|
||||
packetid.LoginPluginResponse,
|
||||
msgid, pk.Boolean(ok),
|
||||
pk.Opt{Has: ok, Field: data},
|
||||
msgid, PluginMessageData,
|
||||
)); err != nil {
|
||||
return LoginErr{"login Plugin", err}
|
||||
}
|
||||
|
@ -11,6 +11,7 @@
|
||||
package chat
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
@ -113,6 +114,7 @@ func (m Message) MarshalJSON() ([]byte, error) {
|
||||
|
||||
// UnmarshalJSON decode json to Message
|
||||
func (m *Message) UnmarshalJSON(raw []byte) (err error) {
|
||||
raw = bytes.TrimSpace(raw)
|
||||
if len(raw) == 0 {
|
||||
return io.EOF
|
||||
}
|
||||
|
@ -7,6 +7,12 @@ import (
|
||||
"reflect"
|
||||
)
|
||||
|
||||
var (
|
||||
_ Field = (*Option[Field])(nil)
|
||||
_ Field = (*Ary[VarInt])(nil)
|
||||
_ Field = Tuple(nil)
|
||||
)
|
||||
|
||||
// Ary is used to send or receive the packet field like "Array of X"
|
||||
// which has a count must be known from the context.
|
||||
//
|
||||
@ -19,16 +25,16 @@ import (
|
||||
// So it's allowed to directly set an integer type Len, but not a pointer.
|
||||
//
|
||||
// Note that Ary DO read or write the Len. You aren't need to do so by your self.
|
||||
type Ary[T VarInt | VarLong | Byte | UnsignedByte | Short | UnsignedShort | Int | Long] struct {
|
||||
type Ary[LEN VarInt | VarLong | Byte | UnsignedByte | Short | UnsignedShort | Int | Long] struct {
|
||||
Ary interface{} // Slice or Pointer of Slice of FieldEncoder, FieldDecoder or both (Field)
|
||||
}
|
||||
|
||||
func (a Ary[T]) WriteTo(w io.Writer) (n int64, err error) {
|
||||
func (a Ary[LEN]) WriteTo(w io.Writer) (n int64, err error) {
|
||||
array := reflect.ValueOf(a.Ary)
|
||||
for array.Kind() == reflect.Ptr {
|
||||
array = array.Elem()
|
||||
}
|
||||
Len := T(array.Len())
|
||||
Len := LEN(array.Len())
|
||||
if nn, err := any(&Len).(FieldEncoder).WriteTo(w); err != nil {
|
||||
return n, err
|
||||
} else {
|
||||
@ -45,8 +51,8 @@ func (a Ary[T]) WriteTo(w io.Writer) (n int64, err error) {
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (a Ary[T]) ReadFrom(r io.Reader) (n int64, err error) {
|
||||
var Len T
|
||||
func (a Ary[LEN]) ReadFrom(r io.Reader) (n int64, err error) {
|
||||
var Len LEN
|
||||
if nn, err := any(&Len).(FieldDecoder).ReadFrom(r); err != nil {
|
||||
return nn, err
|
||||
} else {
|
||||
@ -133,7 +139,33 @@ func (o Opt) ReadFrom(r io.Reader) (int64, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
type Tuple []interface{} // FieldEncoder, FieldDecoder or both (Field)
|
||||
type Option[T any] struct {
|
||||
Has Boolean
|
||||
Val T
|
||||
}
|
||||
|
||||
func (o Option[T]) WriteTo(w io.Writer) (n int64, err error) {
|
||||
n1, err := o.Has.WriteTo(w)
|
||||
if err != nil || !o.Has {
|
||||
return n1, err
|
||||
}
|
||||
n2, err := any(&o.Val).(FieldEncoder).WriteTo(w)
|
||||
return n1 + n2, err
|
||||
}
|
||||
|
||||
func (o *Option[T]) ReadFrom(r io.Reader) (n int64, err error) {
|
||||
n1, err := o.Has.ReadFrom(r)
|
||||
if err != nil || !o.Has {
|
||||
return n1, err
|
||||
}
|
||||
// This lose performance, but current Golang doesn't provide any better solution.
|
||||
// I hope Go support we declare type constraint like `Option[*T Field]` in the future
|
||||
// and then we can prevent using any dynamic dispatch.
|
||||
n2, err := any(&o.Val).(FieldDecoder).ReadFrom(r)
|
||||
return n1 + n2, err
|
||||
}
|
||||
|
||||
type Tuple []any // FieldEncoder, FieldDecoder or both (Field)
|
||||
|
||||
// WriteTo write Tuple to io.Writer, panic when any of filed don't implement FieldEncoder
|
||||
func (t Tuple) WriteTo(w io.Writer) (n int64, err error) {
|
||||
|
@ -138,7 +138,6 @@ func ExampleOpt_ReadFrom() {
|
||||
// WILL NOT BE READ, WILL NOT BE COVERED
|
||||
}
|
||||
|
||||
func ExampleOpt_ReadFrom_func() {
|
||||
// As an example, we define this packet as this:
|
||||
// +------+-----------------+----------------------------------+
|
||||
// | Name | Type | Notes |
|
||||
@ -148,6 +147,7 @@ func ExampleOpt_ReadFrom_func() {
|
||||
// | User | Optional String | The player's name. |
|
||||
// +------+-----------------+----------------------------------+
|
||||
// So we need a function to decide if the User field is present.
|
||||
func ExampleOpt_ReadFrom_func() {
|
||||
var flag pk.Byte
|
||||
var data pk.String
|
||||
p := pk.Packet{Data: []byte{
|
||||
@ -188,3 +188,38 @@ func ExampleTuple_ReadFrom() {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// As an example, we define this packet as this:
|
||||
// +------+-----------------+-----------------------------------+
|
||||
// | Name | Type | Notes |
|
||||
// +------+-----------------+-----------------------------------+
|
||||
// | Has | Boolean | True if the following is present. |
|
||||
// +------+-----------------+-----------------------------------+
|
||||
// | User | Optional String | The player's name. |
|
||||
// +------+-----------------+-----------------------------------+
|
||||
// So we need a function to decide if the User field is present.
|
||||
func ExampleOption_ReadFrom_func() {
|
||||
p1 := pk.Packet{Data: []byte{
|
||||
0x01, // pk.Boolean(true)
|
||||
4, 'T', 'n', 'z', 'e', // pk.String("Tnze")
|
||||
}}
|
||||
p2 := pk.Packet{Data: []byte{
|
||||
0x00, // pk.Boolean(false)
|
||||
// empty
|
||||
}}
|
||||
|
||||
var User1, User2 pk.Option[pk.String]
|
||||
if err := p1.Scan(&User1); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := p2.Scan(&User2); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Println(User1.Has, User1.Val)
|
||||
fmt.Println(User2.Has, User2.Val)
|
||||
|
||||
// Output:
|
||||
// true Tnze
|
||||
// false
|
||||
}
|
||||
|
@ -118,7 +118,6 @@ func (k *KeepAlive) tickPlayer(c KeepAliveClient) {
|
||||
elem, ok := k.listIndex[c]
|
||||
if !ok {
|
||||
panic(errors.New("keepalive: fail to tick player: client not found"))
|
||||
return
|
||||
}
|
||||
if elem.Prev() == nil {
|
||||
if !k.waitTimer.Stop() {
|
||||
@ -156,7 +155,6 @@ func keepAliveSetTimer(l *list.List, timer *time.Timer, interval time.Duration)
|
||||
}
|
||||
}
|
||||
timer.Reset(interval)
|
||||
return
|
||||
}
|
||||
|
||||
type keepAliveItem struct {
|
||||
|
@ -64,21 +64,13 @@ func (d *MojangLoginHandler) AcceptLogin(conn *net.Conn, protocol int32) (name s
|
||||
}
|
||||
|
||||
var (
|
||||
hasPubKey pk.Boolean
|
||||
pubKey auth.PublicKey
|
||||
hasUUID pk.Boolean
|
||||
profileUUID pk.UUID // ignored
|
||||
pubKey pk.Option[auth.PublicKey]
|
||||
profileUUID pk.Option[pk.UUID] // ignored
|
||||
)
|
||||
err = p.Scan(
|
||||
(*pk.String)(&name), // decode username as pk.String
|
||||
&hasPubKey, pk.Opt{
|
||||
Has: &hasPubKey,
|
||||
Field: &pubKey,
|
||||
},
|
||||
&hasUUID, pk.Opt{
|
||||
Has: &hasUUID,
|
||||
Field: &profileUUID,
|
||||
},
|
||||
&pubKey,
|
||||
&profileUUID,
|
||||
)
|
||||
if err != nil {
|
||||
return
|
||||
@ -86,12 +78,12 @@ func (d *MojangLoginHandler) AcceptLogin(conn *net.Conn, protocol int32) (name s
|
||||
|
||||
// auth
|
||||
if d.OnlineMode {
|
||||
if hasPubKey {
|
||||
if !pubKey.Verify() {
|
||||
if pubKey.Has {
|
||||
if !pubKey.Val.Verify() {
|
||||
err = LoginFailErr{reason: chat.TranslateMsg("multiplayer.disconnect.invalid_public_key_signature")}
|
||||
return
|
||||
}
|
||||
profilePubKey = &pubKey
|
||||
profilePubKey = &pubKey.Val
|
||||
} else if d.EnforceSecureProfile {
|
||||
err = LoginFailErr{reason: chat.TranslateMsg("multiplayer.disconnect.missing_public_key")}
|
||||
return
|
||||
@ -99,7 +91,7 @@ func (d *MojangLoginHandler) AcceptLogin(conn *net.Conn, protocol int32) (name s
|
||||
|
||||
var resp *auth.Resp
|
||||
// Auth, Encrypt
|
||||
resp, err = auth.Encrypt(conn, name, pubKey.PubKey)
|
||||
resp, err = auth.Encrypt(conn, name, pubKey.Val.PubKey)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
Reference in New Issue
Block a user