Add cron tick of command queue

This commit is contained in:
Marc Di Luzio 2020-06-06 15:52:03 +01:00
parent 0a0a32cf58
commit 573bfbf9c7
7 changed files with 113 additions and 13 deletions

View file

@ -84,7 +84,12 @@ func HandleRegister(s *Server, vars map[string]string, b io.ReadCloser, w io.Wri
} else if acc, err := s.accountant.RegisterAccount(data.Name); err != nil {
response.Error = err.Error()
} else if err := s.SaveAll(); err != nil {
response.Error = fmt.Sprintf("Internal server error when saving accounts: %s", err)
} else {
// Save out the new accounts
response.Id = acc.Id.String()
response.Success = true
}

View file

@ -124,6 +124,9 @@ func TestHandleCommand(t *testing.T) {
attrib, err := s.world.RoverAttributes(inst)
assert.NoError(t, err, "Couldn't get rover attribs")
// Tick the command queues to progress the move command
s.world.ExecuteCommandQueues()
pos2, err := s.world.RoverPosition(inst)
assert.NoError(t, err, "Couldn't get rover position")
pos.Add(game.Vector{X: 0.0, Y: attrib.Speed * 1}) // Should have moved north by the speed and duration

View file

@ -15,6 +15,7 @@ import (
"github.com/mdiluz/rove/pkg/accounts"
"github.com/mdiluz/rove/pkg/game"
"github.com/mdiluz/rove/pkg/persistence"
"github.com/robfig/cron"
)
const (
@ -27,19 +28,25 @@ const (
// Server contains the relevant data to run a game server
type Server struct {
address string
// Internal state
accountant *accounts.Accountant
world *game.World
// HTTP server
listener net.Listener
server *http.Server
router *mux.Router
router *mux.Router
// Config settings
address string
persistence int
// sync point for sub-threads
sync sync.WaitGroup
// cron schedule for world ticks
schedule *cron.Cron
}
// ServerOption defines a server creation option
@ -69,6 +76,7 @@ func NewServer(opts ...ServerOption) *Server {
address: "",
persistence: EphemeralData,
router: router,
schedule: cron.New(),
}
// Apply all options
@ -93,10 +101,8 @@ func (s *Server) Initialise() (err error) {
s.sync.Add(1)
// Load the accounts if requested
if s.persistence == PersistentData {
if err := persistence.LoadAll("accounts", &s.accountant, "world", &s.world); err != nil {
return err
}
if err := s.LoadAll(); err != nil {
return err
}
// Set up the handlers
@ -122,6 +128,20 @@ func (s *Server) Addr() string {
func (s *Server) Run() {
defer s.sync.Done()
// Set up the schedule
s.schedule.AddFunc("0,30", func() {
// Ensure we don't quit during this function
s.sync.Add(1)
defer s.sync.Done()
// Run the command queues
s.world.ExecuteCommandQueues()
// Save out the new world state
s.SaveWorld()
})
s.schedule.Start()
// Serve the http requests
if err := s.server.Serve(s.listener); err != nil && err != http.ErrServerClosed {
log.Fatal(err)
@ -130,6 +150,9 @@ func (s *Server) Run() {
// Close closes up the server
func (s *Server) Close() error {
// Stop the cron
s.schedule.Stop()
// Try and shut down the http server
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
@ -137,11 +160,42 @@ func (s *Server) Close() error {
return err
}
// Wait until the server is shut down
// Wait until the server has shut down
s.sync.Wait()
// Save and return
return s.SaveAll()
}
// SaveWorld will save out the world file
func (s *Server) SaveWorld() error {
if s.persistence == PersistentData {
s.world.RLock()
defer s.world.RUnlock()
if err := persistence.SaveAll("world", s.world); err != nil {
return fmt.Errorf("failed to save out persistent data: %s", err)
}
}
return nil
}
// SaveAccounts will save out the accounts file
func (s *Server) SaveAccounts() error {
if s.persistence == PersistentData {
if err := persistence.SaveAll("accounts", s.accountant); err != nil {
return fmt.Errorf("failed to save out persistent data: %s", err)
}
}
return nil
}
// SaveAll will save out all server files
func (s *Server) SaveAll() error {
// Save the accounts if requested
if s.persistence == PersistentData {
s.world.RLock()
defer s.world.RUnlock()
if err := persistence.SaveAll("accounts", s.accountant, "world", s.world); err != nil {
return err
}
@ -149,6 +203,18 @@ func (s *Server) Close() error {
return nil
}
// LoadAll will load all persistent data
func (s *Server) LoadAll() error {
if s.persistence == PersistentData {
s.world.Lock()
defer s.world.Unlock()
if err := persistence.LoadAll("accounts", &s.accountant, "world", &s.world); err != nil {
return err
}
}
return nil
}
// wrapHandler wraps a request handler in http checks
func (s *Server) wrapHandler(method string, handler Handler) func(w http.ResponseWriter, r *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {