Merge branch 'master' of https://github.com/Tnze/go-mc
This commit is contained in:
2
.github/workflows/go.yml
vendored
2
.github/workflows/go.yml
vendored
@ -10,7 +10,7 @@ jobs:
|
|||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v1
|
uses: actions/setup-go@v1
|
||||||
with:
|
with:
|
||||||
go-version: 1.18
|
go-version: 1.19
|
||||||
id: go
|
id: go
|
||||||
|
|
||||||
- name: Check out code into the Go module directory
|
- name: Check out code into the Go module directory
|
||||||
|
53
bot/mcbot.go
53
bot/mcbot.go
@ -6,10 +6,7 @@ package bot
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/base64"
|
|
||||||
"encoding/pem"
|
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
|
||||||
"net"
|
"net"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
@ -79,22 +76,20 @@ 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)
|
||||||
HasSignature := err == nil
|
KeyPair := pk.OptionEncoder[user.KeyPairResp]{
|
||||||
|
Has: err == nil,
|
||||||
|
Val: c.KeyPair,
|
||||||
|
}
|
||||||
c.UUID, err = uuid.Parse(c.Auth.UUID)
|
c.UUID, err = uuid.Parse(c.Auth.UUID)
|
||||||
HasPlayerUUID := err == nil
|
PlayerUUID := pk.Option[pk.UUID, *pk.UUID]{
|
||||||
|
Has: err == nil,
|
||||||
|
Val: pk.UUID(c.UUID),
|
||||||
|
}
|
||||||
err = c.Conn.WritePacket(pk.Marshal(
|
err = c.Conn.WritePacket(pk.Marshal(
|
||||||
packetid.LoginStart,
|
packetid.LoginStart,
|
||||||
pk.String(c.Auth.Name),
|
pk.String(c.Auth.Name),
|
||||||
pk.Boolean(HasSignature),
|
KeyPair,
|
||||||
pk.Opt{
|
PlayerUUID,
|
||||||
Has: HasSignature,
|
|
||||||
Field: keyPair(c.KeyPair),
|
|
||||||
},
|
|
||||||
pk.Boolean(HasPlayerUUID),
|
|
||||||
pk.Opt{
|
|
||||||
Has: HasPlayerUUID,
|
|
||||||
Field: pk.UUID(c.UUID),
|
|
||||||
},
|
|
||||||
))
|
))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return LoginErr{"login start", err}
|
return LoginErr{"login start", err}
|
||||||
@ -148,9 +143,10 @@ func (c *Client) join(ctx context.Context, d *mcnet.Dialer, addr string) error {
|
|||||||
return LoginErr{"Login Plugin", err}
|
return LoginErr{"Login Plugin", err}
|
||||||
}
|
}
|
||||||
|
|
||||||
handler, ok := c.LoginPlugin[string(channel)]
|
var PluginMessageData pk.Option[pk.PluginMessageData, *pk.PluginMessageData]
|
||||||
if ok {
|
if handler, ok := c.LoginPlugin[string(channel)]; ok {
|
||||||
data, err = handler(data)
|
PluginMessageData.Has = true
|
||||||
|
PluginMessageData.Val, err = handler(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return LoginErr{"Login Plugin", err}
|
return LoginErr{"Login Plugin", err}
|
||||||
}
|
}
|
||||||
@ -158,8 +154,7 @@ func (c *Client) join(ctx context.Context, d *mcnet.Dialer, addr string) error {
|
|||||||
|
|
||||||
if err := c.Conn.WritePacket(pk.Marshal(
|
if err := c.Conn.WritePacket(pk.Marshal(
|
||||||
packetid.LoginPluginResponse,
|
packetid.LoginPluginResponse,
|
||||||
msgid, pk.Boolean(ok),
|
msgid, PluginMessageData,
|
||||||
pk.Opt{Has: ok, Field: data},
|
|
||||||
)); err != nil {
|
)); err != nil {
|
||||||
return LoginErr{"login Plugin", err}
|
return LoginErr{"login Plugin", err}
|
||||||
}
|
}
|
||||||
@ -167,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
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
package chat
|
package chat
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -113,6 +114,7 @@ func (m Message) MarshalJSON() ([]byte, error) {
|
|||||||
|
|
||||||
// UnmarshalJSON decode json to Message
|
// UnmarshalJSON decode json to Message
|
||||||
func (m *Message) UnmarshalJSON(raw []byte) (err error) {
|
func (m *Message) UnmarshalJSON(raw []byte) (err error) {
|
||||||
|
raw = bytes.TrimSpace(raw)
|
||||||
if len(raw) == 0 {
|
if len(raw) == 0 {
|
||||||
return io.EOF
|
return io.EOF
|
||||||
}
|
}
|
||||||
|
2
go.mod
2
go.mod
@ -1,6 +1,6 @@
|
|||||||
module github.com/Tnze/go-mc
|
module github.com/Tnze/go-mc
|
||||||
|
|
||||||
go 1.18
|
go 1.19
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/google/uuid v1.1.1
|
github.com/google/uuid v1.1.1
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,6 +7,12 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ Field = (*Option[VarInt, *VarInt])(nil)
|
||||||
|
_ Field = (*Ary[VarInt])(nil)
|
||||||
|
_ Field = Tuple(nil)
|
||||||
|
)
|
||||||
|
|
||||||
// Ary is used to send or receive the packet field like "Array of X"
|
// Ary is used to send or receive the packet field like "Array of X"
|
||||||
// which has a count must be known from the context.
|
// 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.
|
// 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.
|
// 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)
|
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)
|
array := reflect.ValueOf(a.Ary)
|
||||||
for array.Kind() == reflect.Ptr {
|
for array.Kind() == reflect.Ptr {
|
||||||
array = array.Elem()
|
array = array.Elem()
|
||||||
}
|
}
|
||||||
Len := T(array.Len())
|
Len := LEN(array.Len())
|
||||||
if nn, err := any(&Len).(FieldEncoder).WriteTo(w); err != nil {
|
if nn, err := any(&Len).(FieldEncoder).WriteTo(w); err != nil {
|
||||||
return n, err
|
return n, err
|
||||||
} else {
|
} else {
|
||||||
@ -45,8 +51,8 @@ func (a Ary[T]) WriteTo(w io.Writer) (n int64, err error) {
|
|||||||
return n, nil
|
return n, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a Ary[T]) ReadFrom(r io.Reader) (n int64, err error) {
|
func (a Ary[LEN]) ReadFrom(r io.Reader) (n int64, err error) {
|
||||||
var Len T
|
var Len LEN
|
||||||
if nn, err := any(&Len).(FieldDecoder).ReadFrom(r); err != nil {
|
if nn, err := any(&Len).(FieldDecoder).ReadFrom(r); err != nil {
|
||||||
return nn, err
|
return nn, err
|
||||||
} else {
|
} else {
|
||||||
@ -80,6 +86,12 @@ func Array(ary any) Field {
|
|||||||
return Ary[VarInt]{Ary: ary}
|
return Ary[VarInt]{Ary: ary}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Opt is a optional [Field] which sending/receiving or not is depending on its Has field.
|
||||||
|
// When calling `WriteTo()` or `ReadFrom()`, if Has is true, the Field's `WriteTo` or `ReadFrom()` is called.
|
||||||
|
// Otherwise, it does nothing and return 0 and nil.
|
||||||
|
//
|
||||||
|
// The different between [Opt] and [Option] is that [Opt] does NOT read or write the Has field for you.
|
||||||
|
// Which should be cared.
|
||||||
type Opt struct {
|
type Opt struct {
|
||||||
Has any // Pointer of bool, or `func() bool`
|
Has any // Pointer of bool, or `func() bool`
|
||||||
Field any // FieldEncoder, FieldDecoder, `func() FieldEncoder`, `func() FieldDecoder` or `func() Field`
|
Field any // FieldEncoder, FieldDecoder, `func() FieldEncoder`, `func() FieldDecoder` or `func() Field`
|
||||||
@ -133,7 +145,99 @@ func (o Opt) ReadFrom(r io.Reader) (int64, error) {
|
|||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type Tuple []interface{} // FieldEncoder, FieldDecoder or both (Field)
|
type fieldPointer[T any] interface {
|
||||||
|
*T
|
||||||
|
FieldDecoder
|
||||||
|
}
|
||||||
|
|
||||||
|
// Option is a helper type for encoding/decoding these kind of packet:
|
||||||
|
//
|
||||||
|
// +-----------+------------+----------------------------------------- +
|
||||||
|
// | Name | Type | Notes |
|
||||||
|
// +-----------+------------+------------------------------------------+
|
||||||
|
// | Has Value | Boolean | Whether or not the Value should be sent. |
|
||||||
|
// +-----------+------------+------------------------------------------+
|
||||||
|
// | Value | Optional T | Only exist when Has Value is true. |
|
||||||
|
// +-----------+------------+------------------------------------------+
|
||||||
|
//
|
||||||
|
// # Usage
|
||||||
|
//
|
||||||
|
// `Option[T]` implements [FieldEncoder] and `*Option[T]` implements [FieldDecoder].
|
||||||
|
// That is, you can call `WriteTo()` and `ReadFrom()` methods on it.
|
||||||
|
//
|
||||||
|
// var optStr Option[String]
|
||||||
|
// n, err := optStr.ReadFrom(r)
|
||||||
|
// if err != nil {
|
||||||
|
// // ...
|
||||||
|
// }
|
||||||
|
// if optStr.Has {
|
||||||
|
// fmt.Println(optStr.Val)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// # Notes
|
||||||
|
//
|
||||||
|
// 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
|
||||||
|
Val T
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o Option[T, P]) 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
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Option[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
|
||||||
|
}
|
||||||
|
|
||||||
|
// OptionDecoder is basiclly same with [Option], but support [FieldDecoder] only.
|
||||||
|
// This allowed wrapping a [FieldDecoder] type (which isn't a [FieldEncoder]) to an Option.
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// OptionEncoder is basiclly same with [Option], but support [FieldEncoder] only.
|
||||||
|
// This allowed wrapping a [FieldEncoder] type (which isn't a [FieldDecoder]) to an Option.
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
type Tuple []any // FieldEncoder, FieldDecoder or both (Field)
|
||||||
|
|
||||||
// WriteTo write Tuple to io.Writer, panic when any of filed don't implement FieldEncoder
|
// 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) {
|
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
|
// WILL NOT BE READ, WILL NOT BE COVERED
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExampleOpt_ReadFrom_func() {
|
|
||||||
// As an example, we define this packet as this:
|
// As an example, we define this packet as this:
|
||||||
// +------+-----------------+----------------------------------+
|
// +------+-----------------+----------------------------------+
|
||||||
// | Name | Type | Notes |
|
// | Name | Type | Notes |
|
||||||
@ -148,6 +147,7 @@ func ExampleOpt_ReadFrom_func() {
|
|||||||
// | User | Optional String | The player's name. |
|
// | User | Optional String | The player's name. |
|
||||||
// +------+-----------------+----------------------------------+
|
// +------+-----------------+----------------------------------+
|
||||||
// So we need a function to decide if the User field is present.
|
// So we need a function to decide if the User field is present.
|
||||||
|
func ExampleOpt_ReadFrom_func() {
|
||||||
var flag pk.Byte
|
var flag pk.Byte
|
||||||
var data pk.String
|
var data pk.String
|
||||||
p := pk.Packet{Data: []byte{
|
p := pk.Packet{Data: []byte{
|
||||||
@ -188,3 +188,38 @@ func ExampleTuple_ReadFrom() {
|
|||||||
panic(err)
|
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, *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
|
||||||
|
}
|
||||||
|
@ -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
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"container/list"
|
"container/list"
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
|
||||||
pk "github.com/Tnze/go-mc/net/packet"
|
pk "github.com/Tnze/go-mc/net/packet"
|
||||||
)
|
)
|
||||||
@ -28,21 +29,27 @@ func (s WritePacketError) Unwrap() error {
|
|||||||
return s.Err
|
return s.Err
|
||||||
}
|
}
|
||||||
|
|
||||||
type PacketQueue struct {
|
type PacketQueue interface {
|
||||||
queue *list.List
|
Push(packet pk.Packet)
|
||||||
closed bool
|
Pull() (packet pk.Packet, ok bool)
|
||||||
cond sync.Cond
|
Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPacketQueue() (p *PacketQueue) {
|
func NewPacketQueue() (p PacketQueue) {
|
||||||
p = &PacketQueue{
|
p = &LinkedListPacketQueue{
|
||||||
queue: list.New(),
|
queue: list.New(),
|
||||||
cond: sync.Cond{L: new(sync.Mutex)},
|
cond: sync.Cond{L: new(sync.Mutex)},
|
||||||
}
|
}
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *PacketQueue) Push(packet pk.Packet) {
|
type LinkedListPacketQueue struct {
|
||||||
|
queue *list.List
|
||||||
|
closed bool
|
||||||
|
cond sync.Cond
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *LinkedListPacketQueue) Push(packet pk.Packet) {
|
||||||
p.cond.L.Lock()
|
p.cond.L.Lock()
|
||||||
if !p.closed {
|
if !p.closed {
|
||||||
p.queue.PushBack(packet)
|
p.queue.PushBack(packet)
|
||||||
@ -51,7 +58,7 @@ func (p *PacketQueue) Push(packet pk.Packet) {
|
|||||||
p.cond.L.Unlock()
|
p.cond.L.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *PacketQueue) Pull() (packet pk.Packet, ok bool) {
|
func (p *LinkedListPacketQueue) Pull() (packet pk.Packet, ok bool) {
|
||||||
p.cond.L.Lock()
|
p.cond.L.Lock()
|
||||||
defer p.cond.L.Unlock()
|
defer p.cond.L.Unlock()
|
||||||
for p.queue.Front() == nil && !p.closed {
|
for p.queue.Front() == nil && !p.closed {
|
||||||
@ -65,9 +72,36 @@ func (p *PacketQueue) Pull() (packet pk.Packet, ok bool) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *PacketQueue) Close() {
|
func (p *LinkedListPacketQueue) Close() {
|
||||||
p.cond.L.Lock()
|
p.cond.L.Lock()
|
||||||
p.closed = true
|
p.closed = true
|
||||||
p.cond.Broadcast()
|
p.cond.Broadcast()
|
||||||
p.cond.L.Unlock()
|
p.cond.L.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ChannelPacketQueue struct {
|
||||||
|
c chan pk.Packet
|
||||||
|
closed atomic.Bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c ChannelPacketQueue) Push(packet pk.Packet) {
|
||||||
|
if c.closed.Load() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case c.c <- packet:
|
||||||
|
default:
|
||||||
|
c.closed.Store(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c ChannelPacketQueue) Pull() (packet pk.Packet, ok bool) {
|
||||||
|
if !c.closed.Load() {
|
||||||
|
packet, ok = <-c.c
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c ChannelPacketQueue) Close() {
|
||||||
|
c.closed.Store(true)
|
||||||
|
}
|
||||||
|
@ -118,7 +118,6 @@ func (k *KeepAlive) tickPlayer(c KeepAliveClient) {
|
|||||||
elem, ok := k.listIndex[c]
|
elem, ok := k.listIndex[c]
|
||||||
if !ok {
|
if !ok {
|
||||||
panic(errors.New("keepalive: fail to tick player: client not found"))
|
panic(errors.New("keepalive: fail to tick player: client not found"))
|
||||||
return
|
|
||||||
}
|
}
|
||||||
if elem.Prev() == nil {
|
if elem.Prev() == nil {
|
||||||
if !k.waitTimer.Stop() {
|
if !k.waitTimer.Stop() {
|
||||||
@ -156,7 +155,6 @@ func keepAliveSetTimer(l *list.List, timer *time.Timer, interval time.Duration)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
timer.Reset(interval)
|
timer.Reset(interval)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type keepAliveItem struct {
|
type keepAliveItem struct {
|
||||||
|
@ -64,21 +64,13 @@ func (d *MojangLoginHandler) AcceptLogin(conn *net.Conn, protocol int32) (name s
|
|||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
hasPubKey pk.Boolean
|
pubKey pk.Option[auth.PublicKey, *auth.PublicKey]
|
||||||
pubKey auth.PublicKey
|
profileUUID pk.Option[pk.UUID, *pk.UUID] // ignored
|
||||||
hasUUID pk.Boolean
|
|
||||||
profileUUID 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
|
||||||
&hasPubKey, pk.Opt{
|
&pubKey,
|
||||||
Has: &hasPubKey,
|
&profileUUID,
|
||||||
Field: &pubKey,
|
|
||||||
},
|
|
||||||
&hasUUID, pk.Opt{
|
|
||||||
Has: &hasUUID,
|
|
||||||
Field: &profileUUID,
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
@ -86,12 +78,12 @@ func (d *MojangLoginHandler) AcceptLogin(conn *net.Conn, protocol int32) (name s
|
|||||||
|
|
||||||
// auth
|
// auth
|
||||||
if d.OnlineMode {
|
if d.OnlineMode {
|
||||||
if hasPubKey {
|
if pubKey.Has {
|
||||||
if !pubKey.Verify() {
|
if !pubKey.Val.Verify() {
|
||||||
err = LoginFailErr{reason: chat.TranslateMsg("multiplayer.disconnect.invalid_public_key_signature")}
|
err = LoginFailErr{reason: chat.TranslateMsg("multiplayer.disconnect.invalid_public_key_signature")}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
profilePubKey = &pubKey
|
profilePubKey = &pubKey.Val
|
||||||
} else if d.EnforceSecureProfile {
|
} else if d.EnforceSecureProfile {
|
||||||
err = LoginFailErr{reason: chat.TranslateMsg("multiplayer.disconnect.missing_public_key")}
|
err = LoginFailErr{reason: chat.TranslateMsg("multiplayer.disconnect.missing_public_key")}
|
||||||
return
|
return
|
||||||
@ -99,7 +91,7 @@ func (d *MojangLoginHandler) AcceptLogin(conn *net.Conn, protocol int32) (name s
|
|||||||
|
|
||||||
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.Val.PubKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user