pk.Option improvement

This commit is contained in:
Tnze
2022-12-06 11:07:19 +08:00
parent 55bf5eddbb
commit ea76e5a713
7 changed files with 77 additions and 40 deletions

View File

@ -6,10 +6,7 @@ package bot
import ( import (
"context" "context"
"encoding/base64"
"encoding/pem"
"errors" "errors"
"io"
"net" "net"
"strconv" "strconv"
@ -79,12 +76,12 @@ func (c *Client) join(ctx context.Context, d *mcnet.Dialer, addr string) error {
} }
// Login Start // Login Start
c.KeyPair, err = user.GetOrFetchKeyPair(c.Auth.AsTk) c.KeyPair, err = user.GetOrFetchKeyPair(c.Auth.AsTk)
KeyPair := pk.Option[keyPair]{ KeyPair := pk.OptionEncoder[user.KeyPairResp]{
Has: err == nil, Has: err == nil,
Val: keyPair(c.KeyPair), Val: c.KeyPair,
} }
c.UUID, err = uuid.Parse(c.Auth.UUID) c.UUID, err = uuid.Parse(c.Auth.UUID)
PlayerUUID := pk.Option[pk.UUID]{ PlayerUUID := pk.Option[pk.UUID, *pk.UUID]{
Has: err == nil, Has: err == nil,
Val: pk.UUID(c.UUID), Val: pk.UUID(c.UUID),
} }
@ -146,7 +143,7 @@ func (c *Client) join(ctx context.Context, d *mcnet.Dialer, addr string) error {
return LoginErr{"Login Plugin", err} return LoginErr{"Login Plugin", err}
} }
var PluginMessageData pk.Option[pk.PluginMessageData] var PluginMessageData pk.Option[pk.PluginMessageData, *pk.PluginMessageData]
if handler, ok := c.LoginPlugin[string(channel)]; ok { if handler, ok := c.LoginPlugin[string(channel)]; ok {
PluginMessageData.Has = true PluginMessageData.Has = true
PluginMessageData.Val, err = handler(data) PluginMessageData.Val, err = handler(data)
@ -165,24 +162,6 @@ func (c *Client) join(ctx context.Context, d *mcnet.Dialer, addr string) error {
} }
} }
type keyPair user.KeyPairResp
func (k keyPair) WriteTo(w io.Writer) (int64, error) {
block, _ := pem.Decode([]byte(k.KeyPair.PublicKey))
if block == nil {
return 0, errors.New("pem decode error: no data is found")
}
signature, err := base64.StdEncoding.DecodeString(k.PublicKeySignature)
if err != nil {
return 0, err
}
return pk.Tuple{
pk.Long(k.ExpiresAt.UnixMilli()),
pk.ByteArray(block.Bytes),
pk.ByteArray(signature),
}.WriteTo(w)
}
type LoginErr struct { type LoginErr struct {
Stage string Stage string
Err error Err error

View File

@ -479,8 +479,8 @@ func (u *UUID) ReadFrom(r io.Reader) (n int64, err error) {
return int64(nn), err return int64(nn), err
} }
func (p *PluginMessageData) WriteTo(w io.Writer) (n int64, err error) { func (p PluginMessageData) WriteTo(w io.Writer) (n int64, err error) {
nn, err := w.Write(*p) nn, err := w.Write(p)
return int64(nn), err return int64(nn), err
} }

View File

@ -8,7 +8,7 @@ import (
) )
var ( var (
_ Field = (*Option[Field])(nil) _ Field = (*Option[VarInt, *VarInt])(nil)
_ Field = (*Ary[VarInt])(nil) _ Field = (*Ary[VarInt])(nil)
_ Field = Tuple(nil) _ Field = Tuple(nil)
) )
@ -139,29 +139,65 @@ func (o Opt) ReadFrom(r io.Reader) (int64, error) {
return 0, nil return 0, nil
} }
type Option[T any] struct { type fieldPointer[T any] interface {
*T
FieldDecoder
}
// Currently we have to repeat T in the type arguments.
//
// var opt Option[String, *String]
//
// Constraint type will inference makes it less awkward in the future.
// See: https://github.com/golang/go/issues/54469
type Option[T FieldEncoder, P fieldPointer[T]] struct {
Has Boolean Has Boolean
Val T Val T
} }
func (o Option[T]) WriteTo(w io.Writer) (n int64, err error) { func (o Option[T, P]) WriteTo(w io.Writer) (n int64, err error) {
n1, err := o.Has.WriteTo(w) n1, err := o.Has.WriteTo(w)
if err != nil || !o.Has { if err != nil || !o.Has {
return n1, err return n1, err
} }
n2, err := any(&o.Val).(FieldEncoder).WriteTo(w) n2, err := o.Val.WriteTo(w)
return n1 + n2, err return n1 + n2, err
} }
func (o *Option[T]) ReadFrom(r io.Reader) (n int64, err error) { func (o *Option[T, P]) ReadFrom(r io.Reader) (n int64, err error) {
n1, err := o.Has.ReadFrom(r) n1, err := o.Has.ReadFrom(r)
if err != nil || !o.Has { if err != nil || !o.Has {
return n1, err return n1, err
} }
// This lose performance, but current Golang doesn't provide any better solution. n2, err := P(&o.Val).ReadFrom(r)
// I hope Go support we declare type constraint like `Option[*T Field]` in the future return n1 + n2, err
// and then we can prevent using any dynamic dispatch. }
n2, err := any(&o.Val).(FieldDecoder).ReadFrom(r)
type OptionDecoder[T any, P fieldPointer[T]] struct {
Has Boolean
Val T
}
func (o *OptionDecoder[T, P]) ReadFrom(r io.Reader) (n int64, err error) {
n1, err := o.Has.ReadFrom(r)
if err != nil || !o.Has {
return n1, err
}
n2, err := P(&o.Val).ReadFrom(r)
return n1 + n2, err
}
type OptionEncoder[T FieldEncoder] struct {
Has Boolean
Val T
}
func (o OptionEncoder[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 := o.Val.WriteTo(w)
return n1 + n2, err return n1 + n2, err
} }

View File

@ -208,7 +208,7 @@ func ExampleOption_ReadFrom_func() {
// empty // empty
}} }}
var User1, User2 pk.Option[pk.String] var User1, User2 pk.Option[pk.String, *pk.String]
if err := p1.Scan(&User1); err != nil { if err := p1.Scan(&User1); err != nil {
panic(err) panic(err)
} }

View File

@ -17,7 +17,7 @@ type PublicKey struct {
Signature []byte Signature []byte
} }
func (p *PublicKey) WriteTo(w io.Writer) (n int64, err error) { func (p PublicKey) WriteTo(w io.Writer) (n int64, err error) {
pubKeyEncoded, err := x509.MarshalPKIXPublicKey(p.PubKey) pubKeyEncoded, err := x509.MarshalPKIXPublicKey(p.PubKey)
if err != nil { if err != nil {
return 0, err return 0, err

View File

@ -64,8 +64,8 @@ func (d *MojangLoginHandler) AcceptLogin(conn *net.Conn, protocol int32) (name s
} }
var ( var (
pubKey pk.Option[auth.PublicKey] pubKey pk.Option[auth.PublicKey, *auth.PublicKey]
profileUUID pk.Option[pk.UUID] // ignored profileUUID pk.Option[pk.UUID, *pk.UUID] // ignored
) )
err = p.Scan( err = p.Scan(
(*pk.String)(&name), // decode username as pk.String (*pk.String)(&name), // decode username as pk.String

View File

@ -1,10 +1,16 @@
package user package user
import ( import (
"encoding/base64"
"encoding/json" "encoding/json"
"encoding/pem"
"errors"
"fmt" "fmt"
"io"
"net/http" "net/http"
"time" "time"
pk "github.com/Tnze/go-mc/net/packet"
) )
var ServicesURL = "https://api.minecraftservices.com" var ServicesURL = "https://api.minecraftservices.com"
@ -21,6 +27,22 @@ type KeyPairResp struct {
RefreshedAfter time.Time `json:"refreshedAfter"` RefreshedAfter time.Time `json:"refreshedAfter"`
} }
func (k KeyPairResp) WriteTo(w io.Writer) (int64, error) {
block, _ := pem.Decode([]byte(k.KeyPair.PublicKey))
if block == nil {
return 0, errors.New("pem decode error: no data is found")
}
signature, err := base64.StdEncoding.DecodeString(k.PublicKeySignature)
if err != nil {
return 0, err
}
return pk.Tuple{
pk.Long(k.ExpiresAt.UnixMilli()),
pk.ByteArray(block.Bytes),
pk.ByteArray(signature),
}.WriteTo(w)
}
func GetOrFetchKeyPair(accessToken string) (KeyPairResp, error) { func GetOrFetchKeyPair(accessToken string) (KeyPairResp, error) {
return fetchKeyPair(accessToken) // TODO: cache return fetchKeyPair(accessToken) // TODO: cache
} }