From 084f3644ff2ebd942e979f4b607b7f9924e60b51 Mon Sep 17 00:00:00 2001 From: MahnoKropotkinvich Date: Mon, 23 Dec 2024 21:36:53 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8A=9F=E8=83=BD=E5=B7=B2=E5=9F=BA=E6=9C=AC?= =?UTF-8?q?=E5=AE=8C=E6=88=90=EF=BC=8C=E7=8E=B0=E6=8A=95=E5=85=A5docker?= =?UTF-8?q?=E8=BF=9B=E8=A1=8C=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- conn.go | 71 ++++++++++++++++++++++++++++++++++ daemon.go | 41 ++++++++++++++++++++ ds.go | 2 +- fsm.go | 111 +++++++++++++++++++++++++++++++++++++++++------------ main.go | 4 +- restart.sh | 4 ++ state.go | 3 ++ 7 files changed, 209 insertions(+), 27 deletions(-) create mode 100644 conn.go create mode 100644 restart.sh diff --git a/conn.go b/conn.go new file mode 100644 index 0000000..6921d62 --- /dev/null +++ b/conn.go @@ -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) + } +} diff --git a/daemon.go b/daemon.go index e69de29..51a9fff 100644 --- a/daemon.go +++ b/daemon.go @@ -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{}{} +} diff --git a/ds.go b/ds.go index bfcaf16..1c643a2 100644 --- a/ds.go +++ b/ds.go @@ -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 } diff --git a/fsm.go b/fsm.go index 96ba396..0e9ac7b 100644 --- a/fsm.go +++ b/fsm.go @@ -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 } } diff --git a/main.go b/main.go index 7fabdb8..b1b198c 100644 --- a/main.go +++ b/main.go @@ -10,5 +10,7 @@ func main() { if err := LoadConfig("config.yml"); err != nil { os.Exit(1) } - + InitState() + go Stopped() + Listen() } diff --git a/restart.sh b/restart.sh new file mode 100644 index 0000000..0d0e197 --- /dev/null +++ b/restart.sh @@ -0,0 +1,4 @@ +CGO_ENABLED=0 go build -o bin/minimcd +cd bin +./minimcd +cd .. diff --git a/state.go b/state.go index dfe1774..5ceaff3 100644 --- a/state.go +++ b/state.go @@ -15,4 +15,7 @@ const ( BOOTING WAITING STOPPING + SIZE ) + +var stateToStr = [SIZE]string{"STOPPED", "RUNNING", "BOOTING", "WAITING", "STOPPING"}