From 1ed1c60de09a267b97940fb4cf417e555a868397 Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Sat, 27 Jun 2020 00:32:27 +0100 Subject: [PATCH] Simplify - remove RoverAttributes and rover UUIDs --- .vscode/launch.json | 4 +- cmd/rove-server/internal/routes.go | 44 +++------- cmd/rove-server/internal/server.go | 14 ++- pkg/game/rover.go | 18 +--- pkg/game/world.go | 134 +++++++++++++---------------- pkg/game/world_test.go | 13 +-- 6 files changed, 89 insertions(+), 138 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index d2109b5..539f842 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -11,7 +11,9 @@ "mode": "auto", "program": "${workspaceFolder}/cmd/rove-server/main.go", "cwd": "${workspaceFolder}", - "env": {}, + "env": { + "WORDS_FILE": "${workspaceFolder}/data/words_alpha.txt", + }, "args": [], }, { diff --git a/cmd/rove-server/internal/routes.go b/cmd/rove-server/internal/routes.go index 3515506..2b18a7e 100644 --- a/cmd/rove-server/internal/routes.go +++ b/cmd/rove-server/internal/routes.go @@ -4,7 +4,6 @@ import ( "context" "fmt" - "github.com/google/uuid" "github.com/mdiluz/rove/pkg/accounts" "github.com/mdiluz/rove/pkg/game" "github.com/mdiluz/rove/pkg/rove" @@ -37,12 +36,11 @@ func (s *Server) Register(ctx context.Context, req *rove.RegisterRequest) (*rove if _, err := s.accountant.Register(ctx, &accounts.RegisterInfo{Name: req.Name}, grpc.WaitForReady(true)); err != nil { return nil, err - } else if _, _, err := s.SpawnRoverForAccount(req.Name); err != nil { + } else if _, err := s.SpawnRoverForAccount(req.Name); err != nil { return nil, fmt.Errorf("failed to spawn rover for account: %s", err) } else if err := s.SaveWorld(); err != nil { return nil, fmt.Errorf("internal server error when saving world: %s", err) - } return &rove.RegisterResponse{}, nil @@ -56,27 +54,18 @@ func (s *Server) Rover(ctx context.Context, req *rove.RoverRequest) (*rove.Rover } else if resp, err := s.accountant.GetValue(ctx, &accounts.DataKey{Account: req.Account, Key: "rover"}); err != nil { return nil, fmt.Errorf("gRPC failed to contact accountant: %s", err) - } else if id, err := uuid.Parse(resp.Value); err != nil { - return nil, fmt.Errorf("account had invalid rover ID: %s", resp.Value) - - } else if attrib, err := s.world.RoverAttributes(id); err != nil { - return nil, fmt.Errorf("error getting rover attributes: %s", err) - - } else if pos, err := s.world.RoverPosition(id); err != nil { - return nil, fmt.Errorf("error getting rover attributes: %s", err) - - } else if inv, err := s.world.RoverInventory(id); err != nil { - return nil, fmt.Errorf("error getting rover attributes: %s", err) + } else if rover, err := s.world.GetRover(resp.Value); err != nil { + return nil, fmt.Errorf("error getting rover: %s", err) } else { response = &rove.RoverResponse{ - Name: attrib.Name, + Name: rover.Name, Position: &rove.Vector{ - X: int32(pos.X), - Y: int32(pos.Y), + X: int32(rover.Pos.X), + Y: int32(rover.Pos.Y), }, - Range: int32(attrib.Range), - Inventory: inv, + Range: int32(rover.Range), + Inventory: rover.Inventory, } } return response, nil @@ -92,20 +81,16 @@ func (s *Server) Radar(ctx context.Context, req *rove.RadarRequest) (*rove.Radar resp, err := s.accountant.GetValue(ctx, &accounts.DataKey{Account: req.Account, Key: "rover"}) if err != nil { return nil, fmt.Errorf("gRPC failed to contact accountant: %s", err) - } - if id, err := uuid.Parse(resp.Value); err != nil { - return nil, fmt.Errorf("account had invalid rover ID: %s", resp.Value) - - } else if attrib, err := s.world.RoverAttributes(id); err != nil { + } else if rover, err := s.world.GetRover(resp.Value); err != nil { return nil, fmt.Errorf("error getting rover attributes: %s", err) - } else if radar, err := s.world.RadarFromRover(id); err != nil { + } else if radar, err := s.world.RadarFromRover(resp.Value); err != nil { return nil, fmt.Errorf("error getting radar from rover: %s", err) } else { response.Tiles = radar - response.Range = int32(attrib.Range) + response.Range = int32(rover.Range) } return response, nil @@ -121,11 +106,6 @@ func (s *Server) Commands(ctx context.Context, req *rove.CommandsRequest) (*rove return nil, err } - id, err := uuid.Parse(resp.Value) - if err != nil { - return nil, fmt.Errorf("account had invalid rover ID: %s", resp.Value) - } - var cmds []game.Command for _, c := range req.Commands { cmds = append(cmds, game.Command{ @@ -133,7 +113,7 @@ func (s *Server) Commands(ctx context.Context, req *rove.CommandsRequest) (*rove Command: c.Command}) } - if err := s.world.Enqueue(id, cmds...); err != nil { + if err := s.world.Enqueue(resp.Value, cmds...); err != nil { return nil, err } diff --git a/cmd/rove-server/internal/server.go b/cmd/rove-server/internal/server.go index 449d9e2..0345936 100644 --- a/cmd/rove-server/internal/server.go +++ b/cmd/rove-server/internal/server.go @@ -8,7 +8,6 @@ import ( "os" "sync" - "github.com/google/uuid" "github.com/mdiluz/rove/pkg/accounts" "github.com/mdiluz/rove/pkg/game" "github.com/mdiluz/rove/pkg/persistence" @@ -239,15 +238,12 @@ type BadRequestError struct { } // SpawnRoverForAccount spawns the rover rover for an account -func (s *Server) SpawnRoverForAccount(account string) (game.RoverAttributes, uuid.UUID, error) { +func (s *Server) SpawnRoverForAccount(account string) (string, error) { if inst, err := s.world.SpawnRover(); err != nil { - return game.RoverAttributes{}, uuid.UUID{}, err - - } else if attribs, err := s.world.RoverAttributes(inst); err != nil { - return game.RoverAttributes{}, uuid.UUID{}, fmt.Errorf("no attributes found for created rover: %s", err) + return "", err } else { - keyval := accounts.DataKeyValue{Account: account, Key: "rover", Value: inst.String()} + keyval := accounts.DataKeyValue{Account: account, Key: "rover", Value: inst} _, err := s.accountant.AssignValue(context.Background(), &keyval) if err != nil { log.Printf("Failed to assign rover to account, %s", err) @@ -257,9 +253,9 @@ func (s *Server) SpawnRoverForAccount(account string) (game.RoverAttributes, uui log.Printf("Failed to destroy rover after failed rover assign: %s", err) } - return game.RoverAttributes{}, uuid.UUID{}, err + return "", err } else { - return attribs, inst, nil + return inst, nil } } } diff --git a/pkg/game/rover.go b/pkg/game/rover.go index 9c50ab8..3bec8c8 100644 --- a/pkg/game/rover.go +++ b/pkg/game/rover.go @@ -1,29 +1,19 @@ package game import ( - "github.com/google/uuid" "github.com/mdiluz/rove/pkg/vector" ) -// RoverAttributes contains attributes of a rover -type RoverAttributes struct { - // Name of this rover - Name string `json:"name"` - - // Range represents the distance the unit's radar can see - Range int `json:"range"` -} - // Rover describes a single rover in the world type Rover struct { - // Id is a unique ID for this rover - Id uuid.UUID `json:"id"` + // Unique name of this rover + Name string `json:"name"` // Pos represents where this rover is in the world Pos vector.Vector `json:"pos"` - // Attributes represents the physical attributes of the rover - Attributes RoverAttributes `json:"attributes"` + // Range represents the distance the unit's radar can see + Range int `json:"range"` // Inventory represents any items the rover is carrying Inventory []byte `json:"inventory"` diff --git a/pkg/game/world.go b/pkg/game/world.go index 406c66f..b123962 100644 --- a/pkg/game/world.go +++ b/pkg/game/world.go @@ -20,7 +20,7 @@ import ( // World describes a self contained universe and everything in it type World struct { // Rovers is a id->data map of all the rovers in the game - Rovers map[uuid.UUID]Rover `json:"rovers"` + Rovers map[string]Rover `json:"rovers"` // Atlas represends the world map of chunks and tiles Atlas atlas.Atlas `json:"atlas"` @@ -29,10 +29,10 @@ type World struct { worldMutex sync.RWMutex // Commands is the set of currently executing command streams per rover - CommandQueue map[uuid.UUID]CommandStream `json:"commands"` + CommandQueue map[string]CommandStream `json:"commands"` // Incoming represents the set of commands to add to the queue at the end of the current tick - Incoming map[uuid.UUID]CommandStream `json:"incoming"` + Incoming map[string]CommandStream `json:"incoming"` // Mutex to lock around command operations cmdMutex sync.RWMutex @@ -62,9 +62,9 @@ func NewWorld(size, chunkSize int) *World { } return &World{ - Rovers: make(map[uuid.UUID]Rover), - CommandQueue: make(map[uuid.UUID]CommandStream), - Incoming: make(map[uuid.UUID]CommandStream), + Rovers: make(map[string]Rover), + CommandQueue: make(map[string]CommandStream), + Incoming: make(map[string]CommandStream), Atlas: atlas.NewAtlas(size, chunkSize), words: lines, } @@ -83,22 +83,26 @@ func (w *World) SpawnWorld(fillWorld bool) error { } // SpawnRover adds an rover to the game -func (w *World) SpawnRover() (uuid.UUID, error) { +func (w *World) SpawnRover() (string, error) { w.worldMutex.Lock() defer w.worldMutex.Unlock() // Initialise the rover rover := Rover{ - Id: uuid.New(), - Attributes: RoverAttributes{ - Range: 5.0, - Name: "rover", - }, + Range: 4.0, + Name: uuid.New().String(), } // Assign a random name if we have words if len(w.words) > 0 { - rover.Attributes.Name = fmt.Sprintf("%s-%s", w.words[rand.Intn(len(w.words))], w.words[rand.Intn(len(w.words))]) + for { + // Loop until we find a unique name + name := fmt.Sprintf("%s-%s", w.words[rand.Intn(len(w.words))], w.words[rand.Intn(len(w.words))]) + if _, ok := w.Rovers[name]; !ok { + rover.Name = name + break + } + } } // Spawn in a random place near the origin @@ -110,7 +114,7 @@ func (w *World) SpawnRover() (uuid.UUID, error) { // Seach until we error (run out of world) for { if tile, err := w.Atlas.GetTile(rover.Pos); err != nil { - return uuid.Nil, err + return "", err } else { if !objects.IsBlocking(tile) { break @@ -124,22 +128,34 @@ func (w *World) SpawnRover() (uuid.UUID, error) { log.Printf("Spawned rover at %+v\n", rover.Pos) // Append the rover to the list - w.Rovers[rover.Id] = rover + w.Rovers[rover.Name] = rover - return rover.Id, nil + return rover.Name, nil +} + +// GetRover gets a specific rover by name +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 { + return Rover{}, fmt.Errorf("Failed to find rover with name: %s", rover) + } } // Removes an rover from the game -func (w *World) DestroyRover(id uuid.UUID) error { +func (w *World) DestroyRover(rover string) error { w.worldMutex.Lock() defer w.worldMutex.Unlock() - if i, ok := w.Rovers[id]; ok { + if i, ok := w.Rovers[rover]; ok { // Clear the tile if err := w.Atlas.SetTile(i.Pos, objects.Empty); err != nil { return fmt.Errorf("coudln't clear old rover tile: %s", err) } - delete(w.Rovers, id) + delete(w.Rovers, rover) } else { return fmt.Errorf("no rover matching id") } @@ -147,11 +163,11 @@ func (w *World) DestroyRover(id uuid.UUID) error { } // RoverPosition returns the position of the rover -func (w *World) RoverPosition(id uuid.UUID) (vector.Vector, error) { +func (w *World) RoverPosition(rover string) (vector.Vector, error) { w.worldMutex.RLock() defer w.worldMutex.RUnlock() - if i, ok := w.Rovers[id]; ok { + if i, ok := w.Rovers[rover]; ok { return i.Pos, nil } else { return vector.Vector{}, fmt.Errorf("no rover matching id") @@ -159,39 +175,13 @@ func (w *World) RoverPosition(id uuid.UUID) (vector.Vector, error) { } // SetRoverPosition sets the position of the rover -func (w *World) SetRoverPosition(id uuid.UUID, pos vector.Vector) error { +func (w *World) SetRoverPosition(rover string, pos vector.Vector) error { w.worldMutex.Lock() defer w.worldMutex.Unlock() - if i, ok := w.Rovers[id]; ok { + if i, ok := w.Rovers[rover]; ok { i.Pos = pos - w.Rovers[id] = i - return nil - } else { - return fmt.Errorf("no rover matching id") - } -} - -// RoverAttributes returns the attributes of a requested rover -func (w *World) RoverAttributes(id uuid.UUID) (RoverAttributes, error) { - w.worldMutex.RLock() - defer w.worldMutex.RUnlock() - - if i, ok := w.Rovers[id]; ok { - return i.Attributes, nil - } else { - return RoverAttributes{}, fmt.Errorf("no rover matching id") - } -} - -// SetRoverAttributes sets the attributes of a requested rover -func (w *World) SetRoverAttributes(id uuid.UUID, attributes RoverAttributes) error { - w.worldMutex.Lock() - defer w.worldMutex.Unlock() - - if i, ok := w.Rovers[id]; ok { - i.Attributes = attributes - w.Rovers[id] = i + w.Rovers[rover] = i return nil } else { return fmt.Errorf("no rover matching id") @@ -199,11 +189,11 @@ func (w *World) SetRoverAttributes(id uuid.UUID, attributes RoverAttributes) err } // RoverInventory returns the inventory of a requested rover -func (w *World) RoverInventory(id uuid.UUID) ([]byte, error) { +func (w *World) RoverInventory(rover string) ([]byte, error) { w.worldMutex.RLock() defer w.worldMutex.RUnlock() - if i, ok := w.Rovers[id]; ok { + if i, ok := w.Rovers[rover]; ok { return i.Inventory, nil } else { return nil, fmt.Errorf("no rover matching id") @@ -211,11 +201,11 @@ func (w *World) RoverInventory(id uuid.UUID) ([]byte, error) { } // WarpRover sets an rovers position -func (w *World) WarpRover(id uuid.UUID, pos vector.Vector) error { +func (w *World) WarpRover(rover string, pos vector.Vector) error { w.worldMutex.Lock() defer w.worldMutex.Unlock() - if i, ok := w.Rovers[id]; ok { + if i, ok := w.Rovers[rover]; ok { // Nothing to do if these positions match if i.Pos == pos { return nil @@ -229,7 +219,7 @@ func (w *World) WarpRover(id uuid.UUID, pos vector.Vector) error { } i.Pos = pos - w.Rovers[id] = i + w.Rovers[rover] = i return nil } else { return fmt.Errorf("no rover matching id") @@ -237,11 +227,11 @@ func (w *World) WarpRover(id uuid.UUID, pos vector.Vector) error { } // SetPosition sets an rovers position -func (w *World) MoveRover(id uuid.UUID, b bearing.Bearing) (vector.Vector, error) { +func (w *World) MoveRover(rover string, b bearing.Bearing) (vector.Vector, error) { w.worldMutex.Lock() defer w.worldMutex.Unlock() - if i, ok := w.Rovers[id]; ok { + if i, ok := w.Rovers[rover]; ok { // Try the new move position newPos := i.Pos.Added(b.Vector()) @@ -251,7 +241,7 @@ func (w *World) MoveRover(id uuid.UUID, b bearing.Bearing) (vector.Vector, error } else if !objects.IsBlocking(tile) { // Perform the move i.Pos = newPos - w.Rovers[id] = i + w.Rovers[rover] = i } return i.Pos, nil @@ -261,17 +251,17 @@ func (w *World) MoveRover(id uuid.UUID, b bearing.Bearing) (vector.Vector, error } // RoverStash will stash an item at the current rovers position -func (w *World) RoverStash(id uuid.UUID) (byte, error) { +func (w *World) RoverStash(rover string) (byte, error) { w.worldMutex.Lock() defer w.worldMutex.Unlock() - if r, ok := w.Rovers[id]; ok { + if r, ok := w.Rovers[rover]; ok { if tile, err := w.Atlas.GetTile(r.Pos); err != nil { return objects.Empty, err } else { if objects.IsStashable(tile) { r.Inventory = append(r.Inventory, tile) - w.Rovers[id] = r + w.Rovers[rover] = r if err := w.Atlas.SetTile(r.Pos, objects.Empty); err != nil { return objects.Empty, err } else { @@ -288,23 +278,23 @@ func (w *World) RoverStash(id uuid.UUID) (byte, error) { } // RadarFromRover can be used to query what a rover can currently see -func (w *World) RadarFromRover(id uuid.UUID) ([]byte, error) { +func (w *World) RadarFromRover(rover string) ([]byte, error) { w.worldMutex.RLock() defer w.worldMutex.RUnlock() - if r, ok := w.Rovers[id]; ok { + 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.Attributes.Range * 2) + 1 + radarSpan := (r.Range * 2) + 1 roverPos := r.Pos // Get the radar min and max values radarMin := vector.Vector{ - X: roverPos.X - r.Attributes.Range, - Y: roverPos.Y - r.Attributes.Range, + X: roverPos.X - r.Range, + Y: roverPos.Y - r.Range, } radarMax := vector.Vector{ - X: roverPos.X + r.Attributes.Range, - Y: roverPos.Y + r.Attributes.Range, + X: roverPos.X + r.Range, + Y: roverPos.Y + r.Range, } // Make sure we only query within the actual world @@ -342,7 +332,7 @@ func (w *World) RadarFromRover(id uuid.UUID) ([]byte, error) { dist := r.Pos.Added(roverPos.Negated()) dist = dist.Abs() - if dist.X <= r.Attributes.Range && dist.Y <= r.Attributes.Range { + 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 @@ -359,7 +349,7 @@ func (w *World) RadarFromRover(id uuid.UUID) ([]byte, error) { } // Enqueue will queue the commands given -func (w *World) Enqueue(rover uuid.UUID, commands ...Command) error { +func (w *World) Enqueue(rover string, commands ...Command) error { // First validate the commands for _, c := range commands { @@ -397,7 +387,7 @@ func (w *World) EnqueueAllIncoming() { commands = append(commands, incoming...) w.CommandQueue[id] = commands } - w.Incoming = make(map[uuid.UUID]CommandStream) + w.Incoming = make(map[string]CommandStream) } // Execute will execute any commands in the current command queue @@ -429,7 +419,7 @@ func (w *World) ExecuteCommandQueues() { } // ExecuteCommand will execute a single command -func (w *World) ExecuteCommand(c *Command, rover uuid.UUID) (err error) { +func (w *World) ExecuteCommand(c *Command, rover string) (err error) { log.Printf("Executing command: %+v\n", *c) switch c.Command { diff --git a/pkg/game/world_test.go b/pkg/game/world_test.go index 27d2943..49cfdac 100644 --- a/pkg/game/world_test.go +++ b/pkg/game/world_test.go @@ -32,14 +32,14 @@ func TestWorld_CreateRover(t *testing.T) { } } -func TestWorld_RoverAttributes(t *testing.T) { +func TestWorld_GetRover(t *testing.T) { world := NewWorld(2, 4) a, err := world.SpawnRover() assert.NoError(t, err) - attribs, err := world.RoverAttributes(a) + rover, err := world.GetRover(a) assert.NoError(t, err, "Failed to get rover attribs") - assert.NotZero(t, attribs.Range, "Rover should not be spawned blind") + assert.NotZero(t, rover.Range, "Rover should not be spawned blind") } func TestWorld_DestroyRover(t *testing.T) { @@ -97,13 +97,6 @@ func TestWorld_RadarFromRover(t *testing.T) { b, err := world.SpawnRover() assert.NoError(t, err) - // Set the rover range to a predictable value - attrib, err := world.RoverAttributes(a) - assert.NoError(t, err, "Failed to get rover attribs") - attrib.Range = 4 // Set the range to 4 so we can predict the radar fully - err = world.SetRoverAttributes(a, attrib) - assert.NoError(t, err, "Failed to set rover attribs") - // Warp the rovers into position bpos := vector.Vector{X: -3, Y: -3} assert.NoError(t, world.WarpRover(b, bpos), "Failed to warp rover")