功能已基本完成,现投入docker进行测试

This commit is contained in:
MahnoKropotkinvich 2024-12-23 21:36:53 +08:00
parent 519c945eff
commit 084f3644ff
7 changed files with 209 additions and 27 deletions

71
conn.go Normal file
View File

@ -0,0 +1,71 @@
package main
import (
"io"
"net"
"sync"
)
func handle(client net.Conn) {
defer client.Close()
queryChan := make(QueryChan)
ChanChan <- queryChan
proceed := func() {
server, err := net.Dial("tcp", "127.0.0.1:25565")
defer server.Close()
if err != nil {
GetLogger().Errorf("Failed to connect to MC server: %v", err)
return
}
var wg sync.WaitGroup
wg.Add(2)
go func() {
defer wg.Done()
if _, err := io.Copy(server, client); err != nil {
GetLogger().Errorf("Error forwarding client to server: %v", err)
}
}()
// 从服务器到客户端
go func() {
defer wg.Done()
if _, err := io.Copy(client, server); err != nil {
GetLogger().Errorf("Error forwarding server to client: %v", err)
}
}()
wg.Wait()
GetLogger().Infof("Connection from %s closed", client.RemoteAddr())
}
switch state {
case RUNNING:
CntChan <- INCREASE
defer func() { CntChan <- DECREASE }()
proceed()
case STOPPED, WAITING:
// client.Write([]byte("Server not ready!"))
GetLogger().Warnf("Connection queued, server currently at %s state", stateToStr[state])
CntChan <- INCREASE
defer func() { CntChan <- DECREASE }()
<-RunningChan
proceed()
default:
client.Write([]byte("Server not ready!"))
GetLogger().Warnf("Connection refused, server currently at %s state", stateToStr[state])
}
}
func Listen() {
listener, _ := net.Listen("tcp", "0.0.0.0:"+config.Port)
defer listener.Close()
GetLogger().Infof("Listening on %s", config.Port)
for {
conn, err := listener.Accept()
if err != nil {
GetLogger().Errorf("ection error: %v", err)
continue
}
GetLogger().Infof("New connection from %s", conn.RemoteAddr())
go handle(conn)
}
}

View File

@ -0,0 +1,41 @@
package main
import (
"os"
"os/exec"
"strings"
"syscall"
)
var proc *exec.Cmd
var DaemonChanRX = make(chan struct{})
var DaemonChanTX = make(chan struct{})
func Stopped() {
<-DaemonChanTX
DaemonChanRX <- struct{}{}
go Booting()
}
func Booting() {
GetLogger().Info("Starting MC Server")
arg := strings.Fields(config.StartCommand)
proc = exec.Command(arg[0], arg[1:]...)
proc.Stdout = os.Stdout
proc.Stderr = os.Stderr
proc.Start()
DaemonChanRX <- struct{}{}
GetLogger().Info("enter RUNNING state")
go Running()
}
func Running() {
<-DaemonChanTX
go Stopping()
DaemonChanRX <- struct{}{}
}
func Stopping() {
proc.Process.Signal(syscall.SIGINT)
proc.Wait()
go Stopped()
DaemonChanRX <- struct{}{}
}

2
ds.go
View File

@ -19,7 +19,7 @@ func (s *Stack[T]) Pop() T {
if l == 0 {
panic("Stack: pop")
}
ret := s.items[l]
ret := s.items[l-1]
s.items = s.items[:l-1]
return ret
}

111
fsm.go
View File

@ -73,7 +73,34 @@ func handleRunningToWaiting() {
}
// TODO: work with daemon
func handleWaitingToStopping() {}
func handleWaitingToStopping() {
go stoppingThread()
state = STOPPING
}
func stoppingThread() {
DaemonChanTX <- struct{}{}
<-DaemonChanRX
handleStoppingToStopped()
}
func handleStoppingToStopped() {
state = STOPPED
}
func bootingThread() {
DaemonChanTX <- struct{}{}
<-DaemonChanRX
handleBootingToRunning()
}
var RunningChan = make(chan struct{})
func handleBootingToRunning() {
state = RUNNING
RunningChan <- struct{}{}
}
func handleStoppedToBooting() {
go bootingThread()
state = BOOTING
}
func waitingThread() {
defer close(waitChan)
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(config.Timeout)*time.Minute)
@ -96,64 +123,98 @@ func handleState() { //goroutine only, handles both write and read
SIZE
)
selectCases := make([]reflect.SelectCase, SIZE)
selectCases[CHANCHAN] = reflect.SelectCase{
Dir: reflect.SelectRecv, // readonly
Chan: reflect.ValueOf(ChanChan),
}
selectCases[EVENTCHAN] = reflect.SelectCase{
Dir: reflect.SelectRecv, // readonly
Chan: reflect.ValueOf(eventChan),
}
selectCases := make([]reflect.SelectCase, 1)
//selectCases[CHANCHAN] = reflect.SelectCase{
// Dir: reflect.SelectRecv, // readonly
// Chan: reflect.ValueOf(ChanChan),
//}
//selectCases[EVENTCHAN] = reflect.SelectCase{
// Dir: reflect.SelectRecv, // readonly
// Chan: reflect.ValueOf(eventChan),
//}
//selectCases[CMDCHAN] = reflect.SelectCase{
// Dir: reflect.SelectRecv,
// Chan: reflect.ValueOf(cmdChan),
//}
packagedChan := make(QueryChan)
selectCases[0] = reflect.SelectCase{
Dir: reflect.SelectRecv,
Chan: reflect.ValueOf(packagedChan),
}
addChan := func(Chan QueryChan) {
logger.Debug("handleState():adding new chanchan")
logger.Debug("addChan(): adding new chan from chanchan")
nelem := reflect.SelectCase{
Dir: reflect.SelectDefault,
Dir: reflect.SelectRecv,
Chan: reflect.ValueOf(Chan),
}
logger.Debug("addChan(): done")
if unused.IsEmpty() {
selectCases = append(selectCases, nelem)
} else {
selectCases[unused.Pop()] = nelem
}
}
const SIGNAL = 114514
queryThread := func() {
prevId := -1
for {
id, recv, ok := reflect.Select(selectCases)
logger.Debugf("queryThread(): recv message from No.%d", id)
if prevId != -1 {
logger.Debug("queryThread(): clearing previous")
selectCases[prevId].Dir = reflect.SelectRecv
selectCases[prevId].Send = reflect.Value{}
prevId = -1
}
if !ok {
unused.Push(id)
continue
}
if id != 0 {
packagedChan <- MCState(recv.Int())
state, _ := <-packagedChan
selectCases[id].Dir = reflect.SelectSend
selectCases[id].Send = reflect.ValueOf(state)
prevId = id
}
}
}
go queryThread()
logger.Debug("handleState(): ready")
for {
id, recv, ok := reflect.Select(selectCases)
if !ok {
unused.Push(id)
continue
}
switch id {
case CHANCHAN:
nchan := *(*QueryChan)(recv.UnsafePointer())
select {
case nchan, _ := <-ChanChan:
addChan(nchan)
case EVENTCHAN:
packagedChan <- SIGNAL
//selectCases[id].Dir = reflect.SelectSend
//selectCases[id].Send = reflect.ValueOf(make(QueryChan))
//prevId = id
case event, _ := <-eventChan:
// wait until transformation finishes
event := globalConnEvent(recv.Int())
switch event {
case INCOMING:
switch state {
case STOPPED:
handleStoppedToBooting()
// case STOPPING: // it shouldn't be there, too, should be handled with conn.go
// case BOOTING: //shouldn't be there too
case WAITING:
handleWaitingToRunning()
default:
panic("handleState():written wrong!")
panic("handleState():written wrong!" + stateToStr[state])
}
case EMPTY:
switch state {
case RUNNING:
handleRunningToWaiting()
case BOOTING:
default:
panic("handleState():written wrong!")
panic("handleState():written wrong!" + stateToStr[state])
}
}
default:
case <-packagedChan:
packagedChan <- state
}
}

View File

@ -10,5 +10,7 @@ func main() {
if err := LoadConfig("config.yml"); err != nil {
os.Exit(1)
}
InitState()
go Stopped()
Listen()
}

4
restart.sh Normal file
View File

@ -0,0 +1,4 @@
CGO_ENABLED=0 go build -o bin/minimcd
cd bin
./minimcd
cd ..

View File

@ -15,4 +15,7 @@ const (
BOOTING
WAITING
STOPPING
SIZE
)
var stateToStr = [SIZE]string{"STOPPED", "RUNNING", "BOOTING", "WAITING", "STOPPING"}