Compare commits
2 Commits
simplified
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| cecc1ea127 | |||
| e33d213789 |
8
.gitignore
vendored
8
.gitignore
vendored
@ -1,6 +1,8 @@
|
|||||||
bin/
|
bin/
|
||||||
go.sum
|
go.sum
|
||||||
.vscode/
|
.vscode/
|
||||||
test/
|
test-res/
|
||||||
test-bak/
|
test-res-bak/
|
||||||
rebuild-test.sh
|
rebuild-test.sh
|
||||||
|
*_test.go
|
||||||
|
test/
|
||||||
31
config.go
31
config.go
@ -1,8 +1,9 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"gopkg.in/yaml.v2"
|
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"gopkg.in/yaml.v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
@ -10,10 +11,20 @@ type Config struct {
|
|||||||
StartCommand string `yaml:"start_command"`
|
StartCommand string `yaml:"start_command"`
|
||||||
Port string `yaml:"port"`
|
Port string `yaml:"port"`
|
||||||
ConnectTimeout int `yaml:"connect_timeout"` //seconds
|
ConnectTimeout int `yaml:"connect_timeout"` //seconds
|
||||||
|
MCPort string `yaml:"mc_port"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var defaultConfig = Config{
|
||||||
|
MCPort: "25565",
|
||||||
|
ConnectTimeout: 10,
|
||||||
|
Port: "4567",
|
||||||
|
Timeout: 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
// this is a global constant since it's shared
|
// this is a global constant since it's shared
|
||||||
var config Config
|
var config = Config{}
|
||||||
|
|
||||||
|
const DEFAULT_VALUE_FMT_STR = "%s not found, default to %v"
|
||||||
|
|
||||||
func LoadConfig(filePath string) error {
|
func LoadConfig(filePath string) error {
|
||||||
data, err := os.ReadFile(filePath)
|
data, err := os.ReadFile(filePath)
|
||||||
@ -26,5 +37,21 @@ func LoadConfig(filePath string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
GetLogger().Info("Configuration loaded successfully")
|
GetLogger().Info("Configuration loaded successfully")
|
||||||
|
if config.MCPort == "" {
|
||||||
|
GetLogger().Warnf(DEFAULT_VALUE_FMT_STR, "MCPort", defaultConfig.MCPort)
|
||||||
|
config.MCPort = defaultConfig.MCPort
|
||||||
|
}
|
||||||
|
if config.ConnectTimeout == 0 {
|
||||||
|
GetLogger().Warnf(DEFAULT_VALUE_FMT_STR, "ConnectTimeout", defaultConfig.ConnectTimeout)
|
||||||
|
config.ConnectTimeout = defaultConfig.ConnectTimeout
|
||||||
|
}
|
||||||
|
if config.Port == "" {
|
||||||
|
GetLogger().Warnf(DEFAULT_VALUE_FMT_STR, "Port", defaultConfig.Port)
|
||||||
|
config.Port = defaultConfig.Port
|
||||||
|
}
|
||||||
|
if config.Timeout == 0 {
|
||||||
|
GetLogger().Warnf(DEFAULT_VALUE_FMT_STR, "Timeout", defaultConfig.Timeout)
|
||||||
|
config.Timeout = defaultConfig.Timeout
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
48
conn.go
48
conn.go
@ -2,6 +2,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
||||||
|
"minimcd/protocol"
|
||||||
"net"
|
"net"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
@ -31,21 +32,53 @@ func bridge() {
|
|||||||
for {
|
for {
|
||||||
msg, _ := <-ConnSignalChan
|
msg, _ := <-ConnSignalChan
|
||||||
clientSignalChan.TX <- msg
|
clientSignalChan.TX <- msg
|
||||||
|
clientSignalChan = NewDynamicMultiChan[MCState](false, 2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ENABLE_LOGIN_FILTER = true
|
||||||
|
|
||||||
func handleConn(clientOriginal net.Conn) {
|
func handleConn(clientOriginal net.Conn) {
|
||||||
client := timeoutConn{clientOriginal}
|
client := timeoutConn{clientOriginal}
|
||||||
defer clientOriginal.Close()
|
defer clientOriginal.Close()
|
||||||
|
const DENIED_FMT_STR = "%s refused, it's not login connection"
|
||||||
|
check := func() protocol.Packet {
|
||||||
|
l := protocol.StreamedFromVarNoException[int](clientOriginal)
|
||||||
|
if l == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
pid := protocol.StreamedFromVarNoException[int](clientOriginal)
|
||||||
|
if pid != 0x0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
ver, addr, port, nxtst := protocol.StreamedFromHandshakePacketDataNoException(clientOriginal)
|
||||||
|
if nxtst != 2 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return protocol.ToHandshakePacket(ver, addr, port, nxtst)
|
||||||
|
}
|
||||||
|
var firstPacket protocol.Packet
|
||||||
|
if ENABLE_LOGIN_FILTER {
|
||||||
|
if firstPacket = check(); firstPacket == nil {
|
||||||
|
GetLogger().Infof(DENIED_FMT_STR, clientOriginal.RemoteAddr())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
queryChan := make(QueryChan)
|
queryChan := make(QueryChan)
|
||||||
defer close(queryChan)
|
defer close(queryChan)
|
||||||
QueryChanChan <- queryChan
|
QueryChanChan <- queryChan
|
||||||
|
queryChan <- STOPPED
|
||||||
|
st, _ := <-queryChan
|
||||||
proceed := func() {
|
proceed := func() {
|
||||||
server, err := net.Dial("tcp", "127.0.0.1:25565")
|
server, err := net.Dial("tcp", "127.0.0.1:"+config.MCPort)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
GetLogger().Errorf("Failed to connect to MC server: %v", err)
|
GetLogger().Errorf("Failed to connect to MC server: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
|
if ENABLE_LOGIN_FILTER {
|
||||||
|
server.Write(firstPacket)
|
||||||
|
}
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
wg.Add(2)
|
wg.Add(2)
|
||||||
go func() {
|
go func() {
|
||||||
@ -66,21 +99,21 @@ func handleConn(clientOriginal net.Conn) {
|
|||||||
wg.Wait()
|
wg.Wait()
|
||||||
GetLogger().Infof("Connection from %s closed", clientOriginal.RemoteAddr())
|
GetLogger().Infof("Connection from %s closed", clientOriginal.RemoteAddr())
|
||||||
}
|
}
|
||||||
CntChan <- INCREASE
|
|
||||||
defer func() { CntChan <- DECREASE }()
|
|
||||||
queryChan <- STOPPED
|
|
||||||
st, _ := <-queryChan
|
|
||||||
switch st {
|
switch st {
|
||||||
case RUNNING:
|
case RUNNING:
|
||||||
|
CntChan <- INCREASE
|
||||||
|
defer func() { CntChan <- DECREASE }()
|
||||||
proceed()
|
proceed()
|
||||||
case STOPPED, WAITING:
|
case STOPPED, WAITING:
|
||||||
// client.Write([]byte("Server not ready!"))
|
// client.Write([]byte("Server not ready!"))
|
||||||
GetLogger().Infof("Connection queued, server currently at %s state", stateToStr[state])
|
GetLogger().Infof("Connection queued, server currently at %s state", stateToStr[state])
|
||||||
|
CntChan <- INCREASE
|
||||||
|
defer func() { CntChan <- DECREASE }()
|
||||||
curChan := make(chan MCState)
|
curChan := make(chan MCState)
|
||||||
defer close(curChan)
|
defer close(curChan)
|
||||||
clientSignalChan.Add(curChan)
|
clientSignalChan.Add(curChan)
|
||||||
state, _ := <-curChan
|
st, _ := <-curChan
|
||||||
if state == RUNNING {
|
if st == RUNNING {
|
||||||
proceed()
|
proceed()
|
||||||
} else {
|
} else {
|
||||||
client.Write([]byte("You are too late, server dying!"))
|
client.Write([]byte("You are too late, server dying!"))
|
||||||
@ -104,6 +137,7 @@ func Listen() {
|
|||||||
}
|
}
|
||||||
//conn.SetReadDeadline(time.Now().Add(time.Duration(config.ConnectTimeout) * time.Second))
|
//conn.SetReadDeadline(time.Now().Add(time.Duration(config.ConnectTimeout) * time.Second))
|
||||||
GetLogger().Infof("New connection from %s", conn.RemoteAddr())
|
GetLogger().Infof("New connection from %s", conn.RemoteAddr())
|
||||||
|
conn.SetDeadline(time.Now().Add(time.Duration(config.ConnectTimeout) * time.Second))
|
||||||
go handleConn(conn)
|
go handleConn(conn)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,11 +20,11 @@ func Booting() {
|
|||||||
proc = exec.Command(arg[0], arg[1:]...)
|
proc = exec.Command(arg[0], arg[1:]...)
|
||||||
proc.Stdout = os.Stdout
|
proc.Stdout = os.Stdout
|
||||||
proc.Stderr = os.Stderr
|
proc.Stderr = os.Stderr
|
||||||
|
proc.Stdin = os.Stdin
|
||||||
proc.Start()
|
proc.Start()
|
||||||
DaemonChanRX <- struct{}{}
|
DaemonChanRX <- struct{}{}
|
||||||
GetLogger().Info("enter RUNNING state")
|
GetLogger().Info("enter RUNNING state")
|
||||||
go Running()
|
go Running()
|
||||||
|
|
||||||
}
|
}
|
||||||
func Running() {
|
func Running() {
|
||||||
<-DaemonChanTX
|
<-DaemonChanTX
|
||||||
|
|||||||
115
ds.go
115
ds.go
@ -3,6 +3,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
|
inf "github.com/Code-Hex/go-infinity-channel"
|
||||||
"golang.org/x/exp/constraints"
|
"golang.org/x/exp/constraints"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -103,7 +104,7 @@ type DynamicMultiChan[T constraints.Ordered] struct {
|
|||||||
TX chan T
|
TX chan T
|
||||||
RX chan T
|
RX chan T
|
||||||
reply bool
|
reply bool
|
||||||
modify chan modify_t[T]
|
modify *inf.Channel[modify_t[T]]
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDynamicMultiChan[T constraints.Ordered](reply bool, m int) *DynamicMultiChan[T] {
|
func NewDynamicMultiChan[T constraints.Ordered](reply bool, m int) *DynamicMultiChan[T] {
|
||||||
@ -112,10 +113,10 @@ func NewDynamicMultiChan[T constraints.Ordered](reply bool, m int) *DynamicMulti
|
|||||||
RX: make(chan T),
|
RX: make(chan T),
|
||||||
|
|
||||||
reply: reply,
|
reply: reply,
|
||||||
modify: make(chan modify_t[T]),
|
modify: inf.NewChannel[modify_t[T]](),
|
||||||
}
|
}
|
||||||
mode := m
|
mode := m
|
||||||
reload := make(chan struct{})
|
reload := make([]chan struct{}, 0)
|
||||||
used := make(map[int]bool)
|
used := make(map[int]bool)
|
||||||
unused := make(map[int]bool)
|
unused := make(map[int]bool)
|
||||||
// delta := -1
|
// delta := -1
|
||||||
@ -123,14 +124,16 @@ func NewDynamicMultiChan[T constraints.Ordered](reply bool, m int) *DynamicMulti
|
|||||||
selectCases := make([]reflect.SelectCase, 1)
|
selectCases := make([]reflect.SelectCase, 1)
|
||||||
viewCnt := 0
|
viewCnt := 0
|
||||||
addQue := NewQueue[int]()
|
addQue := NewQueue[int]()
|
||||||
|
|
||||||
|
del := make(map[int]bool)
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
modification, _ := <-ret.modify
|
modification, _ := <-ret.modify.Out()
|
||||||
op := modification.op
|
op := modification.op
|
||||||
id := modification.id
|
id := modification.id
|
||||||
ch := modification.ch
|
ch := modification.ch
|
||||||
for range viewCnt {
|
for _, v := range reload {
|
||||||
reload <- struct{}{}
|
v <- struct{}{}
|
||||||
}
|
}
|
||||||
switch op {
|
switch op {
|
||||||
case ADD:
|
case ADD:
|
||||||
@ -158,6 +161,7 @@ func NewDynamicMultiChan[T constraints.Ordered](reply bool, m int) *DynamicMulti
|
|||||||
}
|
}
|
||||||
case DELETE:
|
case DELETE:
|
||||||
delete(used, id)
|
delete(used, id)
|
||||||
|
delete(del, id+1)
|
||||||
unused[id] = true
|
unused[id] = true
|
||||||
list[id] = nil
|
list[id] = nil
|
||||||
selectCases[id+1] = reflect.SelectCase{
|
selectCases[id+1] = reflect.SelectCase{
|
||||||
@ -167,64 +171,71 @@ func NewDynamicMultiChan[T constraints.Ordered](reply bool, m int) *DynamicMulti
|
|||||||
}
|
}
|
||||||
//deleted <- struct{}{} // <-reloadTX //extra communication
|
//deleted <- struct{}{} // <-reloadTX //extra communication
|
||||||
}
|
}
|
||||||
for range viewCnt {
|
for _, v := range reload {
|
||||||
reload <- struct{}{}
|
v <- struct{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
go func() {
|
{
|
||||||
|
chanId := viewCnt
|
||||||
viewCnt++
|
viewCnt++
|
||||||
|
reload = append(reload, make(chan struct{}))
|
||||||
selectCases[0] = reflect.SelectCase{
|
selectCases[0] = reflect.SelectCase{
|
||||||
Dir: reflect.SelectRecv,
|
Dir: reflect.SelectRecv,
|
||||||
Chan: reflect.ValueOf(reload),
|
Chan: reflect.ValueOf(reload[chanId]),
|
||||||
}
|
}
|
||||||
prevId := -1
|
prevId := -1
|
||||||
for {
|
|
||||||
id, recv, ok := reflect.Select(selectCases)
|
|
||||||
if prevId != -1 {
|
|
||||||
selectCases[prevId].Dir = reflect.SelectRecv
|
|
||||||
selectCases[prevId].Send = reflect.Value{}
|
|
||||||
prevId = -1
|
|
||||||
}
|
|
||||||
if !ok {
|
|
||||||
// ret.used.Push(id)
|
|
||||||
//
|
|
||||||
// delete(used, id)
|
|
||||||
ret.modify <- modify_t[T]{DELETE, id - 1, nil}
|
|
||||||
<-reload
|
|
||||||
// reloadRX <- struct{}{} //I'm currently not dealing with other chan
|
|
||||||
<-reload //waiting for you finished
|
|
||||||
// reloadRX <- struct{}{} //I've read all deltas, you can release them
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if mode == 1 && id != 0 {
|
|
||||||
ret.RX <- To[T](recv) // possible for below to send to Chan?
|
|
||||||
if ret.reply {
|
|
||||||
msg, _ := <-ret.TX
|
|
||||||
selectCases[id].Dir = reflect.SelectSend
|
|
||||||
selectCases[id].Send = reflect.ValueOf(msg)
|
|
||||||
prevId = id
|
|
||||||
}
|
|
||||||
} else if id == 0 {
|
|
||||||
// reloadRX <- struct{}{} //I'm currently not dealing with other chan
|
|
||||||
<-reload //waiting for you finished
|
|
||||||
// reloadRX <- struct{}{} //I've read all deltas, you can release them
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
if mode == 2 {
|
|
||||||
go func() {
|
go func() {
|
||||||
viewCnt++
|
for {
|
||||||
msgList := make([]T, 0)
|
id, recv, ok := reflect.Select(selectCases)
|
||||||
|
if prevId != -1 {
|
||||||
|
selectCases[prevId].Dir = reflect.SelectRecv
|
||||||
|
selectCases[prevId].Send = reflect.Value{}
|
||||||
|
prevId = -1
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
// ret.used.Push(id)
|
||||||
|
//
|
||||||
|
// delete(used, id)
|
||||||
|
if _, Ok := del[id]; !Ok {
|
||||||
|
del[id] = true
|
||||||
|
ret.modify.In() <- modify_t[T]{DELETE, id - 1, nil}
|
||||||
|
<-reload[chanId]
|
||||||
|
<-reload[chanId]
|
||||||
|
}
|
||||||
|
// reloadRX <- struct{}{} //I'm currently not dealing with other chan
|
||||||
|
//waiting for you finished
|
||||||
|
// reloadRX <- struct{}{} //I've read all deltas, you can release them
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if mode == 1 && id != 0 {
|
||||||
|
ret.RX <- To[T](recv) // possible for below to send to Chan?
|
||||||
|
if ret.reply {
|
||||||
|
msg, _ := <-ret.TX
|
||||||
|
selectCases[id].Dir = reflect.SelectSend
|
||||||
|
selectCases[id].Send = reflect.ValueOf(msg)
|
||||||
|
prevId = id
|
||||||
|
}
|
||||||
|
} else if id == 0 {
|
||||||
|
// reloadRX <- struct{}{} //I'm currently not dealing with other chan
|
||||||
|
<-reload[chanId] //waiting for you finished
|
||||||
|
// reloadRX <- struct{}{} //I've read all deltas, you can release them
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
if mode == 2 {
|
||||||
|
chanId := viewCnt
|
||||||
|
viewCnt++
|
||||||
|
reload = append(reload, make(chan struct{}))
|
||||||
|
msgList := make([]T, 0)
|
||||||
|
go func() {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-reload:
|
case <-reload[chanId]:
|
||||||
// reloadRX <- struct{}{} //I'm currently not dealing with other chan
|
// reloadRX <- struct{}{} //I'm currently not dealing with other chan
|
||||||
<-reload //waiting for you finished
|
<-reload[chanId] //waiting for you finished
|
||||||
if addQue.Length() == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
for !addQue.IsEmpty() {
|
for !addQue.IsEmpty() {
|
||||||
delta := addQue.Pop()
|
delta := addQue.Pop()
|
||||||
for _, x := range msgList {
|
for _, x := range msgList {
|
||||||
@ -249,7 +260,7 @@ func (self DynamicMultiChan[T]) IsReply() bool {
|
|||||||
return self.reply
|
return self.reply
|
||||||
}
|
}
|
||||||
func (self *DynamicMultiChan[T]) Add(ch chan T) {
|
func (self *DynamicMultiChan[T]) Add(ch chan T) {
|
||||||
self.modify <- modify_t[T]{ADD, -1, ch}
|
self.modify.In() <- modify_t[T]{ADD, -1, ch}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: we need to wait for 2 msg and send 4 msg in chan
|
// TODO: we need to wait for 2 msg and send 4 msg in chan
|
||||||
|
|||||||
14
fsm.go
14
fsm.go
@ -12,6 +12,7 @@ const (
|
|||||||
INCOMING globalConnEvent = iota
|
INCOMING globalConnEvent = iota
|
||||||
EMPTY
|
EMPTY
|
||||||
)
|
)
|
||||||
|
const SERVER_STATE_INFO_FMTSTR = "Server is currently at %s state"
|
||||||
|
|
||||||
// luckily I use int for them all
|
// luckily I use int for them all
|
||||||
var (
|
var (
|
||||||
@ -54,15 +55,16 @@ var waitChan chan struct{}
|
|||||||
|
|
||||||
// no we don't need cmdChan, we just make it work immediately
|
// no we don't need cmdChan, we just make it work immediately
|
||||||
func handleWaitingToRunning() {
|
func handleWaitingToRunning() {
|
||||||
GetLogger().Infof("Server is currently at %s state", stateToStr[RUNNING])
|
GetLogger().Infof(SERVER_STATE_INFO_FMTSTR, stateToStr[RUNNING])
|
||||||
waitChan <- struct{}{}
|
waitChan <- struct{}{}
|
||||||
_, ok := <-waitChan
|
_, ok := <-waitChan
|
||||||
if ok {
|
if ok {
|
||||||
state = RUNNING //happens immediately
|
state = RUNNING //happens immediately
|
||||||
|
ConnSignalChan <- RUNNING
|
||||||
} //else it's too late
|
} //else it's too late
|
||||||
}
|
}
|
||||||
func handleRunningToWaiting() {
|
func handleRunningToWaiting() {
|
||||||
GetLogger().Infof("Server is currently at %s state", stateToStr[WAITING])
|
GetLogger().Infof(SERVER_STATE_INFO_FMTSTR, stateToStr[WAITING])
|
||||||
waitChan = make(chan struct{})
|
waitChan = make(chan struct{})
|
||||||
go waitingThread()
|
go waitingThread()
|
||||||
state = WAITING
|
state = WAITING
|
||||||
@ -70,7 +72,7 @@ func handleRunningToWaiting() {
|
|||||||
|
|
||||||
// TODO: work with daemon
|
// TODO: work with daemon
|
||||||
func handleWaitingToStopping() {
|
func handleWaitingToStopping() {
|
||||||
GetLogger().Infof("Server is currently at %s state", stateToStr[STOPPING])
|
GetLogger().Infof(SERVER_STATE_INFO_FMTSTR, stateToStr[STOPPING])
|
||||||
go stoppingThread()
|
go stoppingThread()
|
||||||
state = STOPPING
|
state = STOPPING
|
||||||
}
|
}
|
||||||
@ -82,7 +84,7 @@ func stoppingThread() {
|
|||||||
handleStoppingToStopped()
|
handleStoppingToStopped()
|
||||||
}
|
}
|
||||||
func handleStoppingToStopped() {
|
func handleStoppingToStopped() {
|
||||||
GetLogger().Infof("Server is currently at %s state", stateToStr[STOPPED])
|
GetLogger().Infof(SERVER_STATE_INFO_FMTSTR, stateToStr[STOPPED])
|
||||||
state = STOPPED
|
state = STOPPED
|
||||||
}
|
}
|
||||||
func bootingThread() {
|
func bootingThread() {
|
||||||
@ -95,12 +97,12 @@ func bootingThread() {
|
|||||||
var runningChan chan struct{}
|
var runningChan chan struct{}
|
||||||
|
|
||||||
func handleBootingToRunning() {
|
func handleBootingToRunning() {
|
||||||
GetLogger().Infof("Server is currently at %s state", stateToStr[RUNNING])
|
GetLogger().Infof(SERVER_STATE_INFO_FMTSTR, stateToStr[RUNNING])
|
||||||
state = RUNNING
|
state = RUNNING
|
||||||
ConnSignalChan <- RUNNING
|
ConnSignalChan <- RUNNING
|
||||||
}
|
}
|
||||||
func handleStoppedToBooting() {
|
func handleStoppedToBooting() {
|
||||||
GetLogger().Infof("Server is currently at %s state", stateToStr[BOOTING])
|
GetLogger().Infof(SERVER_STATE_INFO_FMTSTR, stateToStr[BOOTING])
|
||||||
state = BOOTING
|
state = BOOTING
|
||||||
bootingThread()
|
bootingThread()
|
||||||
}
|
}
|
||||||
|
|||||||
1
go.mod
1
go.mod
@ -9,6 +9,7 @@ require (
|
|||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/Code-Hex/go-infinity-channel v1.0.0
|
||||||
github.com/alecthomas/repr v0.4.0 // indirect
|
github.com/alecthomas/repr v0.4.0 // indirect
|
||||||
github.com/hexops/gotextdiff v1.0.3 // indirect
|
github.com/hexops/gotextdiff v1.0.3 // indirect
|
||||||
golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67
|
golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67
|
||||||
|
|||||||
10
logger.go
10
logger.go
@ -1,21 +1,29 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
var logger *logrus.Logger
|
var logger *logrus.Logger
|
||||||
|
|
||||||
func InitLogger() {
|
func InitLogger() {
|
||||||
logger = logrus.New()
|
logger = logrus.New()
|
||||||
|
disableLogColor := os.Getenv("DISABLE_LOG_COLOR")
|
||||||
|
logColor := true
|
||||||
|
if disableLogColor == "true" {
|
||||||
|
logColor = false
|
||||||
|
}
|
||||||
logger.SetFormatter(&logrus.TextFormatter{
|
logger.SetFormatter(&logrus.TextFormatter{
|
||||||
FullTimestamp: true,
|
FullTimestamp: true,
|
||||||
|
ForceColors: logColor,
|
||||||
})
|
})
|
||||||
logLevel := os.Getenv("LOG_LEVEL")
|
logLevel := os.Getenv("LOG_LEVEL")
|
||||||
switch logLevel {
|
switch logLevel {
|
||||||
case "debug":
|
case "debug":
|
||||||
logger.SetLevel(logrus.DebugLevel)
|
logger.SetLevel(logrus.DebugLevel)
|
||||||
|
logger.SetReportCaller(true)
|
||||||
case "info":
|
case "info":
|
||||||
logger.SetLevel(logrus.InfoLevel)
|
logger.SetLevel(logrus.InfoLevel)
|
||||||
case "warn", "warning":
|
case "warn", "warning":
|
||||||
|
|||||||
250
protocol/protocol.go
Normal file
250
protocol/protocol.go
Normal file
@ -0,0 +1,250 @@
|
|||||||
|
package protocol
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"encoding/json"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"golang.org/x/exp/constraints"
|
||||||
|
)
|
||||||
|
|
||||||
|
type intType interface {
|
||||||
|
int | int32 | int64
|
||||||
|
}
|
||||||
|
type Var[T intType] []byte
|
||||||
|
type String []byte
|
||||||
|
|
||||||
|
const SEGMENT_BITS = 0x7F
|
||||||
|
const CONTINUE_BIT = 0x80
|
||||||
|
|
||||||
|
func ToVar[T intType](value T) Var[T] {
|
||||||
|
ret := make(Var[T], 0)
|
||||||
|
for {
|
||||||
|
if (value & ^SEGMENT_BITS) == 0 {
|
||||||
|
ret = append(ret, byte(value))
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
ret = append(ret, byte((value&SEGMENT_BITS)|CONTINUE_BIT))
|
||||||
|
value = T(uint(value) >> 7)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func FromVar[T intType](x Var[T]) T {
|
||||||
|
value, position := T(0), 0
|
||||||
|
for i := 0; ; i++ {
|
||||||
|
cur := x[i]
|
||||||
|
value |= T((cur & SEGMENT_BITS) << byte(position))
|
||||||
|
if (cur & CONTINUE_BIT) == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
position += 7
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
func StreamedFromVarNoException[T intType](reader io.Reader) T {
|
||||||
|
value, position := T(0), 0
|
||||||
|
buf := make(Var[T], 1)
|
||||||
|
for i := 0; ; i++ {
|
||||||
|
reader.Read(buf)
|
||||||
|
cur := buf[0]
|
||||||
|
value |= (T(cur) & SEGMENT_BITS) << position
|
||||||
|
if (cur & CONTINUE_BIT) == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
position += 7
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
func ToString(str string) String {
|
||||||
|
ret := make(String, 0)
|
||||||
|
ret = append(ret, ToVar[int](len(str))...)
|
||||||
|
ret = append(ret, []byte(str)...)
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func FromString(x String) string {
|
||||||
|
l := FromVar(Var[int](x))
|
||||||
|
ll := Length(Var[int](x))
|
||||||
|
return string(x[ll : ll+l])
|
||||||
|
}
|
||||||
|
func StreamedFromStringNoException(reader io.Reader) string {
|
||||||
|
l := StreamedFromVarNoException[int](reader)
|
||||||
|
buf := make([]byte, l)
|
||||||
|
reader.Read(buf)
|
||||||
|
return string(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
type PrefixedArray[T intType | byte] []byte
|
||||||
|
|
||||||
|
func ToPrefixedArray[T intType | byte](arr []T) PrefixedArray[T] {
|
||||||
|
ret := make(PrefixedArray[T], 0)
|
||||||
|
ret = append(ret, ToVar[int](len(arr))...)
|
||||||
|
switch any(*new(T)).(type) {
|
||||||
|
case byte:
|
||||||
|
for _, v := range arr {
|
||||||
|
ret = append(ret, byte(v))
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
panic("unimplemented!")
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
func StreamedFromPrefixedArrayNoException(reader io.Reader) []byte { //I don't know how to write parser for other types, left for further change
|
||||||
|
l := StreamedFromVarNoException[int](reader)
|
||||||
|
ret := make([]byte, l)
|
||||||
|
reader.Read(ret)
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
type VarLengthTypes interface {
|
||||||
|
Var[int] | Var[int32] | Var[int64] | String
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: rewrite as method
|
||||||
|
func Length[T VarLengthTypes](x T) int {
|
||||||
|
switch any(x).(type) {
|
||||||
|
case Var[int], Var[int32], Var[int64]:
|
||||||
|
i := 0
|
||||||
|
for ; ; i++ {
|
||||||
|
cur := x[i]
|
||||||
|
if (cur & CONTINUE_BIT) == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return i + 1
|
||||||
|
case String:
|
||||||
|
return Length[Var[int]](Var[int](x)) + len(FromString(String(x)))
|
||||||
|
default:
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Boolean [4]byte
|
||||||
|
|
||||||
|
func ToBoolean(x bool) (ret Boolean) {
|
||||||
|
if x {
|
||||||
|
ret[3] = 1
|
||||||
|
} else {
|
||||||
|
ret[3] = 0
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func StreamedFromBooleanNoException(reader io.Reader) bool {
|
||||||
|
buf := Boolean{}
|
||||||
|
reader.Read(buf[:])
|
||||||
|
if buf[3] == 1 {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type BigEndian[T constraints.Integer] []byte
|
||||||
|
|
||||||
|
func ToBigEndian[T constraints.Integer](x T) BigEndian[T] {
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
binary.Write(buf, binary.BigEndian, x)
|
||||||
|
return buf.Bytes()
|
||||||
|
}
|
||||||
|
func FromBigEndian[T constraints.Integer](x BigEndian[T]) (ret T) {
|
||||||
|
binary.Read(bytes.NewReader(x), binary.BigEndian, &ret)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func StreamedFromBigEndianNoException[T constraints.Integer](reader io.Reader) (ret T) {
|
||||||
|
buf := make(BigEndian[T], binary.Size(ret))
|
||||||
|
reader.Read(buf)
|
||||||
|
binary.Read(bytes.NewReader(buf), binary.BigEndian, &ret)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type UUID [16]byte // I don't think that I should parse the UUID sent from server, which means the endianess will be kept the same
|
||||||
|
|
||||||
|
type Data []byte
|
||||||
|
|
||||||
|
func ToHandshakePacketData(ver int, addr string, port uint16, nxtst int) Data {
|
||||||
|
ret := make(Data, 0)
|
||||||
|
ret = append(ret, ToVar(ver)...)
|
||||||
|
ret = append(ret, ToString(addr)...)
|
||||||
|
ret = append(ret, ToBigEndian(port)...)
|
||||||
|
ret = append(ret, ToVar(nxtst)...)
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
func FromHandshakePacketData(x Data) (ver int, addr string, port uint16, nxtst int) {
|
||||||
|
ver = FromVar(Var[int](x))
|
||||||
|
x = x[Length(Var[int](x)):]
|
||||||
|
addr = FromString(String(x))
|
||||||
|
x = x[Length(String(x)):]
|
||||||
|
port = FromBigEndian(BigEndian[uint16](x[:2]))
|
||||||
|
x = x[2:]
|
||||||
|
nxtst = FromVar(Var[int](x))
|
||||||
|
return ver, addr, port, nxtst
|
||||||
|
}
|
||||||
|
func StreamedFromHandshakePacketDataNoException(reader io.Reader) (ver int, addr string, port uint16, nxtst int) {
|
||||||
|
ver = StreamedFromVarNoException[int](reader)
|
||||||
|
//x = x[Length(Var[int](x)):]
|
||||||
|
addr = StreamedFromStringNoException(reader)
|
||||||
|
// x = x[Length(String(x)):]
|
||||||
|
port = StreamedFromBigEndianNoException[uint16](reader)
|
||||||
|
// x = x[2:]
|
||||||
|
nxtst = StreamedFromVarNoException[int](reader)
|
||||||
|
return ver, addr, port, nxtst
|
||||||
|
}
|
||||||
|
func ToLoginStartPacketData(name string, uuid UUID) Data {
|
||||||
|
ret := Data(ToString(name))
|
||||||
|
ret = append(ret, uuid[:]...)
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
func StreamedFromLoginStartPacketDataNoException(reader io.Reader) (name string, uuid UUID) {
|
||||||
|
name = StreamedFromStringNoException(reader)
|
||||||
|
reader.Read(uuid[:])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func ToEncryptionRequestPacketData(sid string, pubkey []byte, token []byte, auth bool) Data {
|
||||||
|
ret := make(Data, 0)
|
||||||
|
ret = append(ret, ToString(sid)...)
|
||||||
|
ret = append(ret, ToPrefixedArray(pubkey)...)
|
||||||
|
ret = append(ret, ToPrefixedArray(token)...)
|
||||||
|
authByte := ToBoolean(auth)
|
||||||
|
ret = append(ret, authByte[:]...)
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
func StreamedFromEncryptionRequestPacketData(reader io.Reader) (sid string, pubkey []byte, token []byte, auth bool) {
|
||||||
|
sid = StreamedFromStringNoException(reader)
|
||||||
|
pubkey = StreamedFromPrefixedArrayNoException(reader)
|
||||||
|
token = StreamedFromPrefixedArrayNoException(reader)
|
||||||
|
auth = StreamedFromBooleanNoException(reader)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type disconnectJsonFields struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
Text string `json:"text"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func ToDisconnectPacketData(text string) Data {
|
||||||
|
ret, _ := json.Marshal(disconnectJsonFields{"text", text})
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
type Packet []byte
|
||||||
|
|
||||||
|
func ToPacket(p int, data Data) Packet {
|
||||||
|
ret := make(Packet, 0)
|
||||||
|
pid := ToVar[int](p)
|
||||||
|
ret = append(ret, ToVar[int](Length(pid)+len(data))...)
|
||||||
|
ret = append(ret, pid...)
|
||||||
|
ret = append(ret, data...)
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
func StreamedFromPacketNoException(reader io.Reader) (int, Data) {
|
||||||
|
l := StreamedFromVarNoException[int](reader)
|
||||||
|
// ll := Length(Var[int](x))
|
||||||
|
//pid := FromVar(Var[int](x[ll:]))
|
||||||
|
pid := StreamedFromVarNoException[int](reader)
|
||||||
|
ret := make(Data, l-Length(ToVar(pid)))
|
||||||
|
reader.Read(ret)
|
||||||
|
return pid, ret
|
||||||
|
}
|
||||||
|
func ToHandshakePacket(ver int, addr string, port uint16, nxtst int) Packet {
|
||||||
|
return ToPacket(0x00, ToHandshakePacketData(ver, addr, port, nxtst))
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user