yess
This commit is contained in:
@@ -9,6 +9,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/admin-else/strom/crypto"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -105,3 +106,10 @@ func (a *Account) JoinServer(serverId string) (err error) {
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewOfflineAccount(name string) *Account {
|
||||||
|
return &Account{
|
||||||
|
Name: name,
|
||||||
|
Uuid: crypto.FromOfflinePlayer(name),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,37 +0,0 @@
|
|||||||
package bot
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/rsa"
|
|
||||||
"crypto/x509"
|
|
||||||
"encoding/pem"
|
|
||||||
|
|
||||||
"github.com/admin-else/strom/api"
|
|
||||||
"github.com/google/uuid"
|
|
||||||
)
|
|
||||||
|
|
||||||
type SecureChatSession struct {
|
|
||||||
Account *api.Account
|
|
||||||
PlayerKeys api.PlayerKeys
|
|
||||||
Private *rsa.PrivateKey
|
|
||||||
SessionId uuid.UUID
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewSecureChat(a *api.Account) (s *SecureChatSession, err error) {
|
|
||||||
s = &SecureChatSession{Account: a}
|
|
||||||
s.PlayerKeys, err = a.FetchKeys()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
block, _ := pem.Decode([]byte(s.PlayerKeys.KeyPair.PrivateKey))
|
|
||||||
|
|
||||||
k, err := x509.ParsePKCS8PrivateKey(block.Bytes)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
s.Private = k.(*rsa.PrivateKey)
|
|
||||||
|
|
||||||
s.SessionId, err = uuid.NewRandom()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
package bot
|
|
||||||
|
|
||||||
import "net"
|
|
||||||
|
|
||||||
func ServeClient(cNet net.Conn, factory func(c *Conn) (h HandlerInst, err error)) (err error) {
|
|
||||||
c := Servee(cNet)
|
|
||||||
h, err := factory(c)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = c.Start(h)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func StartServer(listenAddr string, factory func(c *Conn) (h HandlerInst, err error)) (err error) {
|
|
||||||
l, err := net.Listen("tcp", listenAddr)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var cNet net.Conn
|
|
||||||
for {
|
|
||||||
cNet, err = l.Accept()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
go ServeClient(cNet, factory)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"github.com/admin-else/strom/proto"
|
||||||
|
"github.com/admin-else/strom/proto_base"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Connect(addr string) (ret *proto.Conn, err error) {
|
||||||
|
ret = &proto.Conn{}
|
||||||
|
ret.Version = "1.21.8"
|
||||||
|
ret.State = proto_base.Handshaking
|
||||||
|
ret.CompressionThreshold = -1
|
||||||
|
ret.Actor = proto_base.Client
|
||||||
|
ret.Conn, err = net.Dial("tcp", addr)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ret.R = ret.Conn
|
||||||
|
ret.W = ret.Conn
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -4,13 +4,14 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/admin-else/strom/bot"
|
"github.com/admin-else/strom/event"
|
||||||
|
"github.com/admin-else/strom/proto"
|
||||||
"github.com/admin-else/strom/proto_base"
|
"github.com/admin-else/strom/proto_base"
|
||||||
"github.com/admin-else/strom/proto_generated/v1_21_8"
|
"github.com/admin-else/strom/proto_generated/v1_21_8"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ConfigIgnorer struct {
|
type ConfigIgnorer struct {
|
||||||
*bot.Conn
|
*proto.Conn
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ConfigIgnorer) Default(event any) (err error) {
|
func (c *ConfigIgnorer) Default(event any) (err error) {
|
||||||
@@ -28,7 +29,7 @@ func (c *ConfigIgnorer) OnFinish(_ v1_21_8.ConfigurationToClientPacketFinishConf
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.State = proto_base.Play
|
c.State = proto_base.Play
|
||||||
err = bot.HandlerDone
|
err = event.HandlerDone
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,7 +43,7 @@ func (c *ConfigIgnorer) OnKeepAlive(packet v1_21_8.ConfigurationToClientPacketKe
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func IgnoreConfig(c *bot.Conn) (err error) {
|
func IgnoreConfig(c *proto.Conn) (err error) {
|
||||||
err = c.Start(&ConfigIgnorer{c})
|
err = c.Start(&ConfigIgnorer{c})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = errors.Join(err, errors.New("failed to ignore config"))
|
err = errors.Join(err, errors.New("failed to ignore config"))
|
||||||
@@ -12,8 +12,10 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/admin-else/strom/api"
|
"github.com/admin-else/strom/api"
|
||||||
"github.com/admin-else/strom/bot"
|
"github.com/admin-else/strom/client"
|
||||||
"github.com/admin-else/strom/crypto"
|
"github.com/admin-else/strom/crypto"
|
||||||
|
"github.com/admin-else/strom/event"
|
||||||
|
"github.com/admin-else/strom/proto"
|
||||||
"github.com/admin-else/strom/proto_base"
|
"github.com/admin-else/strom/proto_base"
|
||||||
|
|
||||||
"github.com/admin-else/strom/data"
|
"github.com/admin-else/strom/data"
|
||||||
@@ -21,8 +23,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type LoginClient struct {
|
type LoginClient struct {
|
||||||
*bot.Conn
|
*proto.Conn
|
||||||
Account api.Account
|
Account *api.Account
|
||||||
|
|
||||||
GivenAccount v1_21_8.LoginToClientPacketSuccess
|
GivenAccount v1_21_8.LoginToClientPacketSuccess
|
||||||
}
|
}
|
||||||
@@ -32,11 +34,11 @@ func (s *LoginClient) Default(event any) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *LoginClient) OnCycle(_ bot.OnLoopCycle) (err error) {
|
func (s *LoginClient) OnCycle(_ event.OnLoopCycle) (err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *LoginClient) OnStart(_ bot.OnStart) (err error) {
|
func (s *LoginClient) OnStart(_ event.OnStart) (err error) {
|
||||||
host, portStr, err := net.SplitHostPort(s.RemoteAddr().String())
|
host, portStr, err := net.SplitHostPort(s.RemoteAddr().String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
@@ -131,11 +133,26 @@ func (s *LoginClient) OnSuccess(success v1_21_8.LoginToClientPacketSuccess) (err
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
s.State = proto_base.Configuration
|
s.State = proto_base.Configuration
|
||||||
err = bot.HandlerDone
|
err = event.HandlerDone
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func Login(c *bot.Conn, account api.Account) (err error) {
|
func Login(c *proto.Conn, account *api.Account) (err error) {
|
||||||
|
err = c.Start(&LoginClient{
|
||||||
|
Conn: c,
|
||||||
|
Account: account,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
err = errors.Join(err, errors.New("failed to login"))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func ConnectAndLogin(connectTo string, account *api.Account) (c *proto.Conn, err error) {
|
||||||
|
c, err = client.Connect(connectTo)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
err = c.Start(&LoginClient{
|
err = c.Start(&LoginClient{
|
||||||
Conn: c,
|
Conn: c,
|
||||||
Account: account,
|
Account: account,
|
||||||
@@ -5,7 +5,8 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/admin-else/strom/bot"
|
"github.com/admin-else/strom/event"
|
||||||
|
"github.com/admin-else/strom/proto"
|
||||||
"github.com/admin-else/strom/proto_base"
|
"github.com/admin-else/strom/proto_base"
|
||||||
|
|
||||||
"github.com/admin-else/strom/data"
|
"github.com/admin-else/strom/data"
|
||||||
@@ -13,7 +14,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type StatusClient struct {
|
type StatusClient struct {
|
||||||
*bot.Conn
|
*proto.Conn
|
||||||
Status string
|
Status string
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -22,7 +23,7 @@ func (s *StatusClient) Default(event any) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StatusClient) OnStart(_ bot.OnStart) (err error) {
|
func (s *StatusClient) OnStart(_ event.OnStart) (err error) {
|
||||||
parts := strings.Split(s.RemoteAddr().String(), ":")
|
parts := strings.Split(s.RemoteAddr().String(), ":")
|
||||||
versionData, err := data.LookUpProtocolVersionByName(s.Version)
|
versionData, err := data.LookUpProtocolVersionByName(s.Version)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -48,6 +49,6 @@ func (s *StatusClient) OnStart(_ bot.OnStart) (err error) {
|
|||||||
|
|
||||||
func (s *StatusClient) OnStatus(p v1_21_8.StatusToClientPacketServerInfo) (err error) {
|
func (s *StatusClient) OnStatus(p v1_21_8.StatusToClientPacketServerInfo) (err error) {
|
||||||
s.Status = p.Response
|
s.Status = p.Response
|
||||||
err = bot.HandlerDone
|
err = event.HandlerDone
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package crypto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/md5"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
// UUIDv3 Variation of google.uuid.NewHash
|
||||||
|
// We need this because we want a 14-byte namespace and Google forces 16
|
||||||
|
func UUIDv3(space []byte, data []byte) uuid.UUID {
|
||||||
|
h := md5.New()
|
||||||
|
h.Reset()
|
||||||
|
h.Write(space) //nolint:errcheck
|
||||||
|
h.Write(data) //nolint:errcheck
|
||||||
|
s := h.Sum(nil)
|
||||||
|
var u uuid.UUID
|
||||||
|
copy(u[:], s)
|
||||||
|
u[6] = (u[6] & 0x0f) | uint8((3&0xf)<<4) // Version 3
|
||||||
|
u[8] = (u[8] & 0x3f) | 0x80 // RFC 4122 variant
|
||||||
|
return u
|
||||||
|
}
|
||||||
|
|
||||||
|
func FromOfflinePlayer(displayName string) uuid.UUID {
|
||||||
|
return UUIDv3([]byte("OfflinePlayer:"), []byte(displayName))
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package error_utils
|
||||||
|
|
||||||
|
func Unwrap[T any](v T, err error) T {
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package bot
|
package event
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
@@ -32,7 +32,7 @@ func FindHandlers(inst HandlerInst) (handlers map[reflect.Type]reflect.Value) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func FireEvent(inst HandlerInst, event any, handlers map[reflect.Type]reflect.Value) (err error) {
|
func Fire(inst HandlerInst, event any, handlers map[reflect.Type]reflect.Value) (err error) {
|
||||||
if handler, ok := handlers[reflect.TypeOf(event)]; ok {
|
if handler, ok := handlers[reflect.TypeOf(event)]; ok {
|
||||||
errV := handler.Call([]reflect.Value{reflect.ValueOf(event)})[0]
|
errV := handler.Call([]reflect.Value{reflect.ValueOf(event)})[0]
|
||||||
if !errV.IsNil() {
|
if !errV.IsNil() {
|
||||||
@@ -43,25 +43,3 @@ func FireEvent(inst HandlerInst, event any, handlers map[reflect.Type]reflect.Va
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Conn) Start(inst HandlerInst) (err error) {
|
|
||||||
_ = *c // exit early on nil connection
|
|
||||||
handlers := FindHandlers(inst)
|
|
||||||
err = FireEvent(inst, OnStart{}, handlers)
|
|
||||||
for err == nil {
|
|
||||||
var event any
|
|
||||||
event, err = c.Receive()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = FireEvent(inst, event, handlers)
|
|
||||||
if err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
err = FireEvent(inst, OnLoopCycle{}, handlers)
|
|
||||||
}
|
|
||||||
if errors.Is(err, HandlerDone) {
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package bot
|
package proto
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
@@ -7,56 +7,29 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
|
"github.com/admin-else/strom/event"
|
||||||
"github.com/admin-else/strom/proto_base"
|
"github.com/admin-else/strom/proto_base"
|
||||||
"github.com/admin-else/strom/proto_generated"
|
"github.com/admin-else/strom/proto_generated"
|
||||||
)
|
)
|
||||||
|
|
||||||
var BadPacketTypeError = errors.New("bad packet type")
|
var BadPacketTypeError = errors.New("bad packet type")
|
||||||
|
|
||||||
// UnCodablePacket represents a packet that could not be decoded due to querser not supporting all packets.
|
// UnCodablePacket represents a packet that could not be decoded due to proto_generated not supporting all packets.
|
||||||
type UnCodablePacket struct {
|
type UnCodablePacket struct {
|
||||||
Err error
|
Err error
|
||||||
Partial any
|
Partial any
|
||||||
Data []byte
|
Data []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
type Actor int
|
|
||||||
|
|
||||||
const (
|
|
||||||
Server Actor = iota
|
|
||||||
Client
|
|
||||||
)
|
|
||||||
|
|
||||||
func (a Actor) SendDirection() proto_base.Direction {
|
|
||||||
switch a {
|
|
||||||
case Server:
|
|
||||||
return proto_base.ToClient
|
|
||||||
case Client:
|
|
||||||
return proto_base.ToServer
|
|
||||||
default:
|
|
||||||
panic("invalid actor")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a Actor) ReceiveDirection() proto_base.Direction {
|
|
||||||
switch a {
|
|
||||||
case Server:
|
|
||||||
return proto_base.ToServer
|
|
||||||
case Client:
|
|
||||||
return proto_base.ToClient
|
|
||||||
default:
|
|
||||||
panic("invalid actor")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type Conn struct {
|
type Conn struct {
|
||||||
net.Conn
|
net.Conn
|
||||||
R io.Reader
|
R io.Reader
|
||||||
W io.Writer
|
W io.Writer
|
||||||
CompressionThreshold int32
|
CompressionThreshold int32
|
||||||
State proto_base.State
|
State proto_base.State
|
||||||
Actor Actor
|
Actor proto_base.Actor
|
||||||
Version string
|
Version string
|
||||||
|
DontDecodePacketsWithoutHandlers bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Conn) Send(packet any) (err error) {
|
func (c *Conn) Send(packet any) (err error) {
|
||||||
@@ -131,6 +104,9 @@ func (c *Conn) Receive() (packet any, err error) {
|
|||||||
}
|
}
|
||||||
if packetLen == 0 {
|
if packetLen == 0 {
|
||||||
packetBytes, err = io.ReadAll(rawPacketBuffer)
|
packetBytes, err = io.ReadAll(rawPacketBuffer)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
var zReader io.ReadCloser
|
var zReader io.ReadCloser
|
||||||
zReader, err = zlib.NewReader(rawPacketBuffer)
|
zReader, err = zlib.NewReader(rawPacketBuffer)
|
||||||
@@ -155,29 +131,24 @@ func (c *Conn) Receive() (packet any, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func Connect(addr string) (ret *Conn, err error) {
|
func (c *Conn) Start(inst event.HandlerInst) (err error) {
|
||||||
ret = &Conn{}
|
_ = *c // exit early on nil connection
|
||||||
ret.Version = "1.21.8"
|
handlers := event.FindHandlers(inst)
|
||||||
ret.State = proto_base.Handshaking
|
err = event.Fire(inst, event.OnStart{}, handlers)
|
||||||
ret.CompressionThreshold = -1
|
for err == nil {
|
||||||
ret.Actor = Client
|
var packet any
|
||||||
ret.Conn, err = net.Dial("tcp", addr)
|
packet, err = c.Receive()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ret.R = ret.Conn
|
err = event.Fire(inst, packet, handlers)
|
||||||
ret.W = ret.Conn
|
if err != nil {
|
||||||
return
|
break
|
||||||
|
}
|
||||||
|
err = event.Fire(inst, event.OnLoopCycle{}, handlers)
|
||||||
|
}
|
||||||
|
if errors.Is(err, event.HandlerDone) {
|
||||||
|
err = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func Servee(c net.Conn) (ret *Conn) {
|
|
||||||
ret = &Conn{}
|
|
||||||
ret.Version = "1.21.8"
|
|
||||||
ret.State = proto_base.Handshaking
|
|
||||||
ret.CompressionThreshold = -1
|
|
||||||
ret.Actor = Server
|
|
||||||
ret.Conn = c
|
|
||||||
ret.R = c
|
|
||||||
ret.W = c
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package proto_base_test
|
package proto_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
@@ -23,6 +23,35 @@ const (
|
|||||||
Play
|
Play
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type Actor int
|
||||||
|
|
||||||
|
const (
|
||||||
|
Server Actor = iota
|
||||||
|
Client
|
||||||
|
)
|
||||||
|
|
||||||
|
func (a Actor) SendDirection() Direction {
|
||||||
|
switch a {
|
||||||
|
case Server:
|
||||||
|
return ToClient
|
||||||
|
case Client:
|
||||||
|
return ToServer
|
||||||
|
default:
|
||||||
|
panic("invalid actor")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a Actor) ReceiveDirection() Direction {
|
||||||
|
switch a {
|
||||||
|
case Server:
|
||||||
|
return ToServer
|
||||||
|
case Client:
|
||||||
|
return ToClient
|
||||||
|
default:
|
||||||
|
panic("invalid actor")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type PacketIdentifier struct {
|
type PacketIdentifier struct {
|
||||||
Direction Direction
|
Direction Direction
|
||||||
State State
|
State State
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
package proto_generated
|
package proto_generated
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io"
|
||||||
|
|
||||||
"github.com/admin-else/strom/proto_base"
|
"github.com/admin-else/strom/proto_base"
|
||||||
"github.com/admin-else/strom/proto_generated/v1_21_8"
|
"github.com/admin-else/strom/proto_generated/v1_21_8"
|
||||||
"io"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func PacketIdentifierToType(v string, d proto_base.Direction, s proto_base.State, i string) (t any) {
|
func PacketIdentifierToType(v string, d proto_base.Direction, s proto_base.State, i string) (t any) {
|
||||||
|
|||||||
@@ -321,7 +321,7 @@ func generateVersion(v string) (err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
f, err := os.Create("proto_generated/v" + vUnderscore + "/proto.go")
|
f, err := os.Create("proto_generated/v" + vUnderscore + "/proto_base.go")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,67 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"github.com/admin-else/strom/event"
|
||||||
|
"github.com/admin-else/strom/proto"
|
||||||
|
"github.com/admin-else/strom/proto_base"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Servee(c net.Conn) (ret *proto.Conn) {
|
||||||
|
ret = &proto.Conn{}
|
||||||
|
ret.Version = "1.21.8"
|
||||||
|
ret.State = proto_base.Handshaking
|
||||||
|
ret.CompressionThreshold = -1
|
||||||
|
ret.Actor = proto_base.Server
|
||||||
|
ret.Conn = c
|
||||||
|
ret.R = c
|
||||||
|
ret.W = c
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func ServeClient(cNet net.Conn, factory func(c *proto.Conn) (h event.HandlerInst, err error)) (err error) {
|
||||||
|
c := Servee(cNet)
|
||||||
|
h, err := factory(c)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = c.Start(h)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func StartServerWithFactory(listenAddr string, factory func(c *proto.Conn) (h event.HandlerInst, err error)) (err error) {
|
||||||
|
l, err := net.Listen("tcp", listenAddr)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var cNet net.Conn
|
||||||
|
for {
|
||||||
|
cNet, err = l.Accept()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
go ServeClient(cNet, factory)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func StartServerWithOnConn(listenAddr string, onConn func(c *proto.Conn) (err error)) (err error) {
|
||||||
|
l, err := net.Listen("tcp", listenAddr)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var cNet net.Conn
|
||||||
|
for {
|
||||||
|
cNet, err = l.Accept()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
connErr := onConn(Servee(cNet))
|
||||||
|
if connErr != nil {
|
||||||
|
log.Println(connErr)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
package server_modules
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/admin-else/strom/api"
|
||||||
|
"github.com/admin-else/strom/event"
|
||||||
|
"github.com/admin-else/strom/proto"
|
||||||
|
"github.com/admin-else/strom/proto_base"
|
||||||
|
"github.com/admin-else/strom/proto_generated/v1_21_8"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
var UnexpectedStatusRequest = UnexpectedNextStateError{proto_base.Status}
|
||||||
|
|
||||||
|
type NameAndUUID struct {
|
||||||
|
Name string
|
||||||
|
UUID uuid.UUID
|
||||||
|
}
|
||||||
|
|
||||||
|
type UnexpectedNextStateError struct {
|
||||||
|
NextState proto_base.State
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u UnexpectedNextStateError) Error() string {
|
||||||
|
return fmt.Sprintf("unexpected next state: %v", u.NextState)
|
||||||
|
}
|
||||||
|
|
||||||
|
type LoginServer struct {
|
||||||
|
*proto.Conn
|
||||||
|
//OnlineMode bool TODO: implement this
|
||||||
|
Requested NameAndUUID
|
||||||
|
Given *NameAndUUID
|
||||||
|
CompressionThreshold int32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LoginServer) OnHandshake(packet v1_21_8.HandshakingToServerPacketSetProtocol) (err error) {
|
||||||
|
l.State = proto_base.State(packet.NextState)
|
||||||
|
if packet.NextState != proto_base.VarInt(proto_base.Login) {
|
||||||
|
err = UnexpectedNextStateError{proto_base.State(packet.NextState)}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LoginServer) SetCompressionThreshold(threshold int32) (err error) {
|
||||||
|
err = l.Send(v1_21_8.LoginToClientPacketCompress{Threshold: proto_base.VarInt(threshold)})
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
l.CompressionThreshold = threshold
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LoginServer) OnLoginStart(packet v1_21_8.LoginToServerPacketLoginStart) (err error) {
|
||||||
|
l.Requested = NameAndUUID{packet.Username, packet.PlayerUUID}
|
||||||
|
//err = l.SetCompressionThreshold(l.CompressionThreshold)
|
||||||
|
//if err != nil {
|
||||||
|
// return
|
||||||
|
//}
|
||||||
|
if l.Given == nil {
|
||||||
|
l.Given = &l.Requested
|
||||||
|
}
|
||||||
|
err = l.Send(v1_21_8.LoginToClientPacketSuccess{
|
||||||
|
Uuid: l.Given.UUID,
|
||||||
|
Username: l.Given.Name,
|
||||||
|
Properties: nil,
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LoginServer) OnLoginAcknowledged(_ v1_21_8.LoginToServerPacketLoginAcknowledged) (err error) {
|
||||||
|
l.State = proto_base.Configuration
|
||||||
|
err = event.HandlerDone
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LoginServer) Default(event any) (err error) {
|
||||||
|
err = fmt.Errorf("unexpected event during login: %#v", event)
|
||||||
|
fmt.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LoginServer) OnStart(_ event.OnStart) (err error) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LoginServer) OnCycle(_ event.OnLoopCycle) (err error) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func ServeLogin(c *proto.Conn) (ret *LoginServer, err error) {
|
||||||
|
ret = &LoginServer{Conn: c}
|
||||||
|
ret.CompressionThreshold = 256
|
||||||
|
err = ret.Start(ret)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func ServeLoginWithOtherAccount(c *proto.Conn, a *api.Account) (ret *LoginServer, err error) {
|
||||||
|
ret = &LoginServer{Conn: c, Given: &NameAndUUID{
|
||||||
|
Name: a.Name,
|
||||||
|
UUID: a.Uuid,
|
||||||
|
}}
|
||||||
|
ret.CompressionThreshold = 256
|
||||||
|
err = ret.Start(ret)
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
package server_modules
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/admin-else/strom/proto"
|
||||||
|
"github.com/admin-else/strom/proto_generated/v1_21_8"
|
||||||
|
)
|
||||||
|
|
||||||
|
// StatusResponse here is the structure I used: https://minecraft.wiki/w/Java_Edition_protocol/Server_List_Ping#Status_Response
|
||||||
|
type StatusResponse struct {
|
||||||
|
Version struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Protocol int `json:"protocol"`
|
||||||
|
} `json:"version"`
|
||||||
|
Players struct {
|
||||||
|
Max int `json:"max"`
|
||||||
|
Online int `json:"online"`
|
||||||
|
Sample []struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
ID string `json:"id"`
|
||||||
|
} `json:"sample"`
|
||||||
|
} `json:"players"`
|
||||||
|
Description struct {
|
||||||
|
Text string `json:"text"`
|
||||||
|
} `json:"description"`
|
||||||
|
Favicon string `json:"favicon"`
|
||||||
|
EnforcesSecureChat bool `json:"enforcesSecureChat"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type StatusServer struct {
|
||||||
|
*proto.Conn
|
||||||
|
Status string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *StatusServer) Default(_ any) (err error) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *StatusServer) OnStatusRequest(_ v1_21_8.StatusToServerPacketPingStart) (err error) {
|
||||||
|
return p.Send(v1_21_8.StatusToClientPacketServerInfo{Response: p.Status})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *StatusServer) OnStatusPing(packet v1_21_8.StatusToServerPacketPing) (err error) {
|
||||||
|
return p.Send(v1_21_8.StatusToClientPacketPing(packet))
|
||||||
|
}
|
||||||
|
|
||||||
|
func ServeStatus(c *proto.Conn, s StatusResponse) (err error) {
|
||||||
|
status, err := json.Marshal(s)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
server := &StatusServer{
|
||||||
|
Conn: c,
|
||||||
|
Status: string(status),
|
||||||
|
}
|
||||||
|
err = server.Start(server)
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
package text
|
||||||
|
|
||||||
|
// https://minecraft.wiki/w/Text_component_format
|
||||||
Reference in New Issue
Block a user