diff --git a/cmd/rove-server/internal/routes.go b/cmd/rove-server/internal/routes.go index 06cf054..4fdf92c 100644 --- a/cmd/rove-server/internal/routes.go +++ b/cmd/rove-server/internal/routes.go @@ -9,6 +9,7 @@ import ( "github.com/mdiluz/rove/pkg/version" ) +// Status returns the status of the current server to a gRPC request func (s *Server) Status(context.Context, *rove.StatusRequest) (*rove.StatusResponse, error) { response := &rove.StatusResponse{ Ready: true, @@ -26,6 +27,7 @@ func (s *Server) Status(context.Context, *rove.StatusRequest) (*rove.StatusRespo return response, nil } +// Register registers a new account for a gRPC request func (s *Server) Register(ctx context.Context, req *rove.RegisterRequest) (*rove.RegisterResponse, error) { if len(req.Name) == 0 { return nil, fmt.Errorf("empty account name") @@ -44,6 +46,7 @@ func (s *Server) Register(ctx context.Context, req *rove.RegisterRequest) (*rove return &rove.RegisterResponse{}, nil } +// Rover returns rover information for a gRPC request func (s *Server) Rover(ctx context.Context, req *rove.RoverRequest) (*rove.RoverResponse, error) { response := &rove.RoverResponse{} if len(req.Account) == 0 { @@ -70,6 +73,7 @@ func (s *Server) Rover(ctx context.Context, req *rove.RoverRequest) (*rove.Rover return response, nil } +// Radar returns the radar information for a rover func (s *Server) Radar(ctx context.Context, req *rove.RadarRequest) (*rove.RadarResponse, error) { if len(req.Account) == 0 { return nil, fmt.Errorf("empty account name") @@ -95,6 +99,7 @@ func (s *Server) Radar(ctx context.Context, req *rove.RadarRequest) (*rove.Radar return response, nil } +// Commands issues commands to the world based on a gRPC request func (s *Server) Commands(ctx context.Context, req *rove.CommandsRequest) (*rove.CommandsResponse, error) { if len(req.Account) == 0 { return nil, fmt.Errorf("empty account") diff --git a/cmd/rove-server/internal/server.go b/cmd/rove-server/internal/server.go index 39f13d4..5f918d4 100644 --- a/cmd/rove-server/internal/server.go +++ b/cmd/rove-server/internal/server.go @@ -208,21 +208,22 @@ func (s *Server) LoadWorld() error { // SpawnRoverForAccount spawns the rover rover for an account func (s *Server) SpawnRoverForAccount(account string) (string, error) { - if inst, err := s.world.SpawnRover(); err != nil { + inst, err := s.world.SpawnRover() + if err != nil { return "", err - } else { - err := s.accountant.AssignData(account, "rover", inst) - if err != nil { - log.Printf("Failed to assign rover to account, %s", err) - - // Try and clear up the rover - if err := s.world.DestroyRover(inst); err != nil { - log.Printf("Failed to destroy rover after failed rover assign: %s", err) - } - - return "", err - } else { - return inst, nil - } } + + err = s.accountant.AssignData(account, "rover", inst) + if err != nil { + log.Printf("Failed to assign rover to account, %s", err) + + // Try and clear up the rover + if err := s.world.DestroyRover(inst); err != nil { + log.Printf("Failed to destroy rover after failed rover assign: %s", err) + } + + return "", err + } + + return inst, nil } diff --git a/cmd/rove-server/main.go b/cmd/rove-server/main.go index abd0a53..ef1d675 100644 --- a/cmd/rove-server/main.go +++ b/cmd/rove-server/main.go @@ -24,6 +24,7 @@ var data = os.Getenv("DATA_PATH") // The tick rate of the server in seconds var tick = os.Getenv("TICK_RATE") +// InnerMain is our main function so tests can run it func InnerMain() { // Ensure we've seeded rand rand.Seed(time.Now().UTC().UnixNano()) diff --git a/pkg/accounts/accounts.go b/pkg/accounts/accounts.go index 322b145..3e810fa 100644 --- a/pkg/accounts/accounts.go +++ b/pkg/accounts/accounts.go @@ -5,8 +5,6 @@ import ( "time" ) -const kAccountsFileName = "rove-accounts.json" - // Account represents a registered user type Account struct { // Name simply describes the account and must be unique @@ -51,7 +49,7 @@ func (a *Accountant) RegisterAccount(name string) (acc Account, err error) { return } -// AssignRover assigns data to an account +// AssignData assigns data to an account func (a *Accountant) AssignData(account string, key string, value string) error { // Find the account matching the ID @@ -65,12 +63,12 @@ func (a *Accountant) AssignData(account string, key string, value string) error return nil } -// GetRover gets the rover rover for the account +// GetValue gets the rover rover for the account func (a *Accountant) GetValue(account string, key string) (string, error) { // Find the account matching the ID - if this, ok := a.Accounts[account]; !ok { + this, ok := a.Accounts[account] + if !ok { return "", fmt.Errorf("no account found for id: %s", account) - } else { - return this.Data[key], nil } + return this.Data[key], nil } diff --git a/pkg/atlas/atlas.go b/pkg/atlas/atlas.go index 0c692d3..f7d1fe1 100644 --- a/pkg/atlas/atlas.go +++ b/pkg/atlas/atlas.go @@ -72,15 +72,15 @@ func (a *Atlas) SetTile(v vector.Vector, tile byte) { } local := a.worldSpaceToChunkLocal(v) - tileId := local.X + local.Y*a.ChunkSize + tileID := local.X + local.Y*a.ChunkSize // Sanity check - if tileId >= len(chunk.Tiles) || tileId < 0 { + if tileID >= len(chunk.Tiles) || tileID < 0 { log.Fatalf("Local tileID is not in valid chunk, somehow, this means something is very wrong") } // Set the chunk back - chunk.Tiles[tileId] = tile + chunk.Tiles[tileID] = tile a.Chunks[c] = chunk } @@ -94,14 +94,14 @@ func (a *Atlas) GetTile(v vector.Vector) byte { } local := a.worldSpaceToChunkLocal(v) - tileId := local.X + local.Y*a.ChunkSize + tileID := local.X + local.Y*a.ChunkSize // Sanity check - if tileId >= len(chunk.Tiles) || tileId < 0 { + if tileID >= len(chunk.Tiles) || tileID < 0 { log.Fatalf("Local tileID is not in valid chunk, somehow, this means something is very wrong") } - return chunk.Tiles[tileId] + return chunk.Tiles[tileID] } // worldSpaceToChunkLocal gets a chunk local coordinate for a tile diff --git a/pkg/bearing/bearing.go b/pkg/bearing/bearing.go index 6eec327..62053f2 100644 --- a/pkg/bearing/bearing.go +++ b/pkg/bearing/bearing.go @@ -11,13 +11,21 @@ import ( type Bearing int const ( + // North describes a 0,1 vector North Bearing = iota + // NorthEast describes a 1,1 vector NorthEast + // East describes a 1,0 vector East + // SouthEast describes a 1,-1 vector SouthEast + // South describes a 0,-1 vector South + // SouthWest describes a -1,-1 vector SouthWest + // West describes a -1,0 vector West + // NorthWest describes a -1,1 vector NorthWest ) diff --git a/pkg/game/command.go b/pkg/game/command.go index 2a5f875..cde071e 100644 --- a/pkg/game/command.go +++ b/pkg/game/command.go @@ -1,13 +1,13 @@ package game const ( - // Moves the rover in the chosen bearing + // CommandMove Moves the rover in the chosen bearing CommandMove = "move" - // Will attempt to stash the object at the current location + // CommandStash Will attempt to stash the object at the current location CommandStash = "stash" - // Will attempt to repair the rover with an inventory object + // CommandRepair Will attempt to repair the rover with an inventory object CommandRepair = "repair" ) diff --git a/pkg/game/world.go b/pkg/game/world.go index 6b04594..3e9128b 100644 --- a/pkg/game/world.go +++ b/pkg/game/world.go @@ -124,25 +124,26 @@ func (w *World) GetRover(rover string) (Rover, error) { w.worldMutex.RLock() defer w.worldMutex.RUnlock() - if i, ok := w.Rovers[rover]; ok { - return i, nil - } else { + i, ok := w.Rovers[rover] + if !ok { return Rover{}, fmt.Errorf("Failed to find rover with name: %s", rover) } + return i, nil } -// Removes an rover from the game +// DestroyRover Removes an rover from the game func (w *World) DestroyRover(rover string) error { w.worldMutex.Lock() defer w.worldMutex.Unlock() - if i, ok := w.Rovers[rover]; ok { - // Clear the tile - w.Atlas.SetTile(i.Pos, objects.Empty) - delete(w.Rovers, rover) - } else { + i, ok := w.Rovers[rover] + if !ok { return fmt.Errorf("no rover matching id") } + + // Clear the tile + w.Atlas.SetTile(i.Pos, objects.Empty) + delete(w.Rovers, rover) return nil } @@ -151,11 +152,11 @@ func (w *World) RoverPosition(rover string) (vector.Vector, error) { w.worldMutex.RLock() defer w.worldMutex.RUnlock() - if i, ok := w.Rovers[rover]; ok { - return i.Pos, nil - } else { + i, ok := w.Rovers[rover] + if !ok { return vector.Vector{}, fmt.Errorf("no rover matching id") } + return i.Pos, nil } // SetRoverPosition sets the position of the rover @@ -163,13 +164,14 @@ func (w *World) SetRoverPosition(rover string, pos vector.Vector) error { w.worldMutex.Lock() defer w.worldMutex.Unlock() - if i, ok := w.Rovers[rover]; ok { - i.Pos = pos - w.Rovers[rover] = i - return nil - } else { + i, ok := w.Rovers[rover] + if !ok { return fmt.Errorf("no rover matching id") } + + i.Pos = pos + w.Rovers[rover] = i + return nil } // RoverInventory returns the inventory of a requested rover @@ -177,11 +179,11 @@ func (w *World) RoverInventory(rover string) ([]byte, error) { w.worldMutex.RLock() defer w.worldMutex.RUnlock() - if i, ok := w.Rovers[rover]; ok { - return i.Inventory, nil - } else { + i, ok := w.Rovers[rover] + if !ok { return nil, fmt.Errorf("no rover matching id") } + return i.Inventory, nil } // WarpRover sets an rovers position @@ -189,55 +191,55 @@ func (w *World) WarpRover(rover string, pos vector.Vector) error { w.worldMutex.Lock() defer w.worldMutex.Unlock() - if i, ok := w.Rovers[rover]; ok { - // Nothing to do if these positions match - if i.Pos == pos { - return nil - } - - // Check the tile is not blocked - tile := w.Atlas.GetTile(pos) - if objects.IsBlocking(tile) { - return fmt.Errorf("can't warp rover to occupied tile, check before warping") - } - - i.Pos = pos - w.Rovers[rover] = i - return nil - } else { + i, ok := w.Rovers[rover] + if !ok { return fmt.Errorf("no rover matching id") } + // Nothing to do if these positions match + if i.Pos == pos { + return nil + } + + // Check the tile is not blocked + tile := w.Atlas.GetTile(pos) + if objects.IsBlocking(tile) { + return fmt.Errorf("can't warp rover to occupied tile, check before warping") + } + + i.Pos = pos + w.Rovers[rover] = i + return nil } -// SetPosition sets an rovers position +// MoveRover attempts to move a rover in a specific direction func (w *World) MoveRover(rover string, b bearing.Bearing) (vector.Vector, error) { w.worldMutex.Lock() defer w.worldMutex.Unlock() - if i, ok := w.Rovers[rover]; ok { - // Try the new move position - newPos := i.Pos.Added(b.Vector()) - - // Get the tile and verify it's empty - tile := w.Atlas.GetTile(newPos) - if !objects.IsBlocking(tile) { - // Perform the move - i.Pos = newPos - w.Rovers[rover] = i - } else { - // If it is a blocking tile, reduce the rover integrity - i.Integrity = i.Integrity - 1 - if i.Integrity == 0 { - // TODO: The rover needs to be left dormant with the player - } else { - w.Rovers[rover] = i - } - } - - return i.Pos, nil - } else { + i, ok := w.Rovers[rover] + if !ok { return vector.Vector{}, fmt.Errorf("no rover matching id") } + // Try the new move position + newPos := i.Pos.Added(b.Vector()) + + // Get the tile and verify it's empty + tile := w.Atlas.GetTile(newPos) + if !objects.IsBlocking(tile) { + // Perform the move + i.Pos = newPos + w.Rovers[rover] = i + } else { + // If it is a blocking tile, reduce the rover integrity + i.Integrity = i.Integrity - 1 + if i.Integrity == 0 { + // TODO: The rover needs to be left dormant with the player + } else { + w.Rovers[rover] = i + } + } + + return i.Pos, nil } // RoverStash will stash an item at the current rovers position @@ -245,20 +247,20 @@ func (w *World) RoverStash(rover string) (byte, error) { w.worldMutex.Lock() defer w.worldMutex.Unlock() - if r, ok := w.Rovers[rover]; ok { - tile := w.Atlas.GetTile(r.Pos) - if objects.IsStashable(tile) { - r.Inventory = append(r.Inventory, tile) - w.Rovers[rover] = r - w.Atlas.SetTile(r.Pos, objects.Empty) - return tile, nil - } - - } else { + r, ok := w.Rovers[rover] + if !ok { return objects.Empty, fmt.Errorf("no rover matching id") } - return objects.Empty, nil + tile := w.Atlas.GetTile(r.Pos) + if !objects.IsStashable(tile) { + return objects.Empty, nil + } + + r.Inventory = append(r.Inventory, tile) + w.Rovers[rover] = r + w.Atlas.SetTile(r.Pos, objects.Empty) + return tile, nil } // RadarFromRover can be used to query what a rover can currently see @@ -266,57 +268,58 @@ func (w *World) RadarFromRover(rover string) ([]byte, error) { w.worldMutex.RLock() defer w.worldMutex.RUnlock() - if r, ok := w.Rovers[rover]; ok { - // The radar should span in range direction on each axis, plus the row/column the rover is currently on - radarSpan := (r.Range * 2) + 1 - roverPos := r.Pos - - // Get the radar min and max values - radarMin := vector.Vector{ - X: roverPos.X - r.Range, - Y: roverPos.Y - r.Range, - } - radarMax := vector.Vector{ - X: roverPos.X + r.Range, - Y: roverPos.Y + r.Range, - } - - // Gather up all tiles within the range - var radar = make([]byte, radarSpan*radarSpan) - for j := radarMin.Y; j <= radarMax.Y; j++ { - for i := radarMin.X; i <= radarMax.X; i++ { - q := vector.Vector{X: i, Y: j} - - tile := w.Atlas.GetTile(q) - - // Get the position relative to the bottom left of the radar - relative := q.Added(radarMin.Negated()) - index := relative.X + relative.Y*radarSpan - radar[index] = tile - - } - } - - // Add all rovers to the radar - for _, r := range w.Rovers { - // If the rover is in range - dist := r.Pos.Added(roverPos.Negated()) - dist = dist.Abs() - - if dist.X <= r.Range && dist.Y <= r.Range { - relative := r.Pos.Added(radarMin.Negated()) - index := relative.X + relative.Y*radarSpan - radar[index] = objects.Rover - } - } - - // Add this rover - radar[len(radar)/2] = objects.Rover - - return radar, nil - } else { + r, ok := w.Rovers[rover] + if !ok { return nil, fmt.Errorf("no rover matching id") } + + // The radar should span in range direction on each axis, plus the row/column the rover is currently on + radarSpan := (r.Range * 2) + 1 + roverPos := r.Pos + + // Get the radar min and max values + radarMin := vector.Vector{ + X: roverPos.X - r.Range, + Y: roverPos.Y - r.Range, + } + radarMax := vector.Vector{ + X: roverPos.X + r.Range, + Y: roverPos.Y + r.Range, + } + + // Gather up all tiles within the range + var radar = make([]byte, radarSpan*radarSpan) + for j := radarMin.Y; j <= radarMax.Y; j++ { + for i := radarMin.X; i <= radarMax.X; i++ { + q := vector.Vector{X: i, Y: j} + + tile := w.Atlas.GetTile(q) + + // Get the position relative to the bottom left of the radar + relative := q.Added(radarMin.Negated()) + index := relative.X + relative.Y*radarSpan + radar[index] = tile + + } + } + + // Add all rovers to the radar + for _, r := range w.Rovers { + // If the rover is in range + dist := r.Pos.Added(roverPos.Negated()) + dist = dist.Abs() + + if dist.X <= r.Range && dist.Y <= r.Range { + relative := r.Pos.Added(radarMin.Negated()) + index := relative.X + relative.Y*radarSpan + radar[index] = objects.Rover + } + } + + // Add this rover + radar[len(radar)/2] = objects.Rover + + return radar, nil } // Enqueue will queue the commands given @@ -362,7 +365,7 @@ func (w *World) EnqueueAllIncoming() { w.Incoming = make(map[string]CommandStream) } -// Execute will execute any commands in the current command queue +// ExecuteCommandQueues will execute any commands in the current command queue func (w *World) ExecuteCommandQueues() { w.cmdMutex.Lock() defer w.cmdMutex.Unlock() @@ -408,15 +411,15 @@ func (w *World) ExecuteCommand(c *Command, rover string) (err error) { } case CommandRepair: - if r, err := w.GetRover(rover); err != nil { + r, err := w.GetRover(rover) + if err != nil { return err - } else { - // Consume an inventory item to repair - if len(r.Inventory) > 0 { - r.Inventory = r.Inventory[:len(r.Inventory)-1] - r.Integrity = r.Integrity + 1 - w.Rovers[rover] = r - } + } + // Consume an inventory item to repair + if len(r.Inventory) > 0 { + r.Inventory = r.Inventory[:len(r.Inventory)-1] + r.Integrity = r.Integrity + 1 + w.Rovers[rover] = r } default: return fmt.Errorf("unknown command: %s", c.Command) diff --git a/pkg/maths/maths.go b/pkg/maths/maths.go index 7e1d0fc..76c00a5 100644 --- a/pkg/maths/maths.go +++ b/pkg/maths/maths.go @@ -8,7 +8,7 @@ func Abs(x int) int { return x } -// pmod is a mositive modulo +// Pmod is a mositive modulo // golang's % is a "remainder" function si misbehaves for negative modulus inputs func Pmod(x, d int) int { if x == 0 || d == 0 { diff --git a/pkg/objects/objects.go b/pkg/objects/objects.go index 2f509c6..e553a1c 100644 --- a/pkg/objects/objects.go +++ b/pkg/objects/objects.go @@ -1,13 +1,20 @@ package objects const ( - Empty = byte(' ') - Rover = byte('R') + // Empty represents an non-existant object + Empty = byte(' ') + + // Rover represents a live rover + Rover = byte('R') + + // SmallRock is a small stashable rock SmallRock = byte('o') + + // LargeRock is a large blocking rock LargeRock = byte('O') ) -// Check if an object is a blocking object +// IsBlocking checks if an object is a blocking object func IsBlocking(object byte) bool { var blocking = [...]byte{ Rover, @@ -22,7 +29,7 @@ func IsBlocking(object byte) bool { return false } -// Check if an object is stashable +// IsStashable checks if an object is stashable func IsStashable(object byte) bool { var stashable = [...]byte{ SmallRock, diff --git a/pkg/persistence/persistence.go b/pkg/persistence/persistence.go index 8bb2e9d..fbe57b0 100644 --- a/pkg/persistence/persistence.go +++ b/pkg/persistence/persistence.go @@ -31,12 +31,13 @@ func jsonPath(name string) string { // Save will serialise the interface into a json file func Save(name string, data interface{}) error { p := jsonPath(name) - if b, err := json.MarshalIndent(data, "", " "); err != nil { + b, err := json.MarshalIndent(data, "", " ") + if err != nil { + return err + } + + if err := ioutil.WriteFile(p, b, os.ModePerm); err != nil { return err - } else { - if err := ioutil.WriteFile(p, b, os.ModePerm); err != nil { - return err - } } log.Printf("Saved %s\n", p) diff --git a/pkg/vector/vector.go b/pkg/vector/vector.go index 61efc97..b661d17 100644 --- a/pkg/vector/vector.go +++ b/pkg/vector/vector.go @@ -60,7 +60,7 @@ func Min(v1 Vector, v2 Vector) Vector { return Vector{maths.Min(v1.X, v2.X), maths.Min(v1.Y, v2.Y)} } -// Min returns the max values in both vectors +// Max returns the max values in both vectors func Max(v1 Vector, v2 Vector) Vector { return Vector{maths.Max(v1.X, v2.X), maths.Max(v1.Y, v2.Y)} } diff --git a/pkg/version/version.go b/pkg/version/version.go index b8b26bb..0daefb3 100644 --- a/pkg/version/version.go +++ b/pkg/version/version.go @@ -1,3 +1,4 @@ package version +// Version represents a version to be overrided with -ldflags var Version = "undefined"