diff --git a/Makefile b/Makefile index 3aedd83..b73faf0 100644 --- a/Makefile +++ b/Makefile @@ -18,9 +18,12 @@ gen: protoc --proto_path proto --go_out=plugins=grpc,paths=source_relative:proto/ proto/roveapi/roveapi.proto test: - @echo Run unit and integration tests - docker-compose -f docker-compose-test.yml up --build --exit-code-from=rove-tests --abort-on-container-exit rove-tests - docker-compose -f docker-compose-test.yml down + @echo Unit tests + go test -v ./... + + @echo Integration tests + docker-compose up --build --exit-code-from=rove-tests --abort-on-container-exit rove-tests + docker-compose down go tool cover -html=/tmp/coverage-data/c.out -o /tmp/coverage.html @echo Done, coverage data can be found in /tmp/coverage.html diff --git a/README.md b/README.md index aad1a6f..2aff518 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,9 @@ Rove ==== +![Tests](https://github.com/mdiluz/rove/workflows/Tests/badge.svg) ![Docker](https://github.com/mdiluz/rove/workflows/Docker/badge.svg) [![rove](https://snapcraft.io//rove/badge.svg)](https://snapcraft.io/rove) -![Rove](data/icon.svg) +![Rove](https://github.com/mdiluz/rove/blob/master/data/icon.svg) Rove is an asynchronous nomadic game about exploring as part of a loose community. -This repository contains the source code for the `rove-server` deployment and the `rove` command line client. See [mdiluz.github.io/rove](https://mdiluz.github.io/rove/) for game details, and [roveapi.proto](proto/roveapi/roveapi.proto) for the current server-client API. +This repository contains the source code for the `rove-server` deployment and the `rove` command line client. See [mdiluz.github.io/rove](https://mdiluz.github.io/rove/) for game details, and [roveapi.proto](https://github.com/mdiluz/rove/blob/master/proto/roveapi/roveapi.proto) for the current server-client API. diff --git a/pkg/accounts/accounts.go b/cmd/rove-server/internal/accounts.go similarity index 91% rename from pkg/accounts/accounts.go rename to cmd/rove-server/internal/accounts.go index eb8637d..99362ad 100644 --- a/pkg/accounts/accounts.go +++ b/cmd/rove-server/internal/accounts.go @@ -1,4 +1,4 @@ -package accounts +package internal // Accountant decribes something that stores accounts and account values type Accountant interface { @@ -21,8 +21,8 @@ type Accountant interface { // Account represents a registered user type Account struct { // Name simply describes the account and must be unique - Name string + Name string `json:"name"` // Data represents internal account data - Data map[string]string + Data map[string]string `json:"data"` } diff --git a/pkg/accounts/accounts_test.go b/cmd/rove-server/internal/accounts_test.go similarity index 98% rename from pkg/accounts/accounts_test.go rename to cmd/rove-server/internal/accounts_test.go index bd2416f..9e7891f 100644 --- a/pkg/accounts/accounts_test.go +++ b/cmd/rove-server/internal/accounts_test.go @@ -1,4 +1,4 @@ -package accounts +package internal import ( "testing" diff --git a/cmd/rove-server/internal/routes.go b/cmd/rove-server/internal/routes.go index 69dd820..0fb41d8 100644 --- a/cmd/rove-server/internal/routes.go +++ b/cmd/rove-server/internal/routes.go @@ -5,6 +5,7 @@ import ( "fmt" "log" + "github.com/mdiluz/rove/pkg/rove" "github.com/mdiluz/rove/pkg/version" "github.com/mdiluz/rove/proto/roveapi" ) @@ -34,7 +35,7 @@ func (s *Server) Register(ctx context.Context, req *roveapi.RegisterRequest) (*r return nil, fmt.Errorf("empty account name") } - if acc, err := s.world.Accountant.RegisterAccount(req.Name); err != nil { + if acc, err := s.accountant.RegisterAccount(req.Name); err != nil { return nil, err } else if _, err := s.SpawnRoverForAccount(req.Name); err != nil { @@ -57,13 +58,13 @@ func (s *Server) Register(ctx context.Context, req *roveapi.RegisterRequest) (*r func (s *Server) Status(ctx context.Context, req *roveapi.StatusRequest) (response *roveapi.StatusResponse, err error) { log.Printf("Handling status request: %s\n", req.Account.Name) - if valid, err := s.world.Accountant.VerifySecret(req.Account.Name, req.Account.Secret); err != nil { + if valid, err := s.accountant.VerifySecret(req.Account.Name, req.Account.Secret); err != nil { return nil, err } else if !valid { return nil, fmt.Errorf("Secret incorrect for account %s", req.Account.Name) - } else if resp, err := s.world.Accountant.GetValue(req.Account.Name, "rover"); err != nil { + } else if resp, err := s.accountant.GetValue(req.Account.Name, "rover"); err != nil { return nil, err } else if rover, err := s.world.GetRover(resp); err != nil { @@ -75,7 +76,40 @@ func (s *Server) Status(ctx context.Context, req *roveapi.StatusRequest) (respon inv = append(inv, byte(i.Type)) } - queued := s.world.RoverCommands(resp) + i, q := s.world.RoverCommands(resp) + var incoming, queued []*roveapi.Command + for _, i := range i { + c := &roveapi.Command{ + Command: i.Command, + } + switch i.Command { + case roveapi.CommandType_move: + c.Data = &roveapi.Command_Bearing{ + Bearing: i.Bearing, + } + case roveapi.CommandType_broadcast: + c.Data = &roveapi.Command_Message{ + Message: i.Message, + } + } + incoming = append(incoming, c) + } + for _, q := range q { + c := &roveapi.Command{ + Command: q.Command, + } + switch q.Command { + case roveapi.CommandType_move: + c.Data = &roveapi.Command_Bearing{ + Bearing: q.Bearing, + } + case roveapi.CommandType_broadcast: + c.Data = &roveapi.Command_Message{ + Message: q.Message, + } + } + queued = append(queued, c) + } var logs []*roveapi.Log for _, log := range rover.Logs { logs = append(logs, &roveapi.Log{ @@ -85,29 +119,21 @@ func (s *Server) Status(ctx context.Context, req *roveapi.StatusRequest) (respon } response = &roveapi.StatusResponse{ - Readings: &roveapi.RoverReadings{ - Position: &roveapi.Vector{ - X: int32(rover.Pos.X), - Y: int32(rover.Pos.Y), - }, - Logs: logs, - Wind: s.world.Wind, - }, - Spec: &roveapi.RoverSpecifications{ - Name: rover.Name, - Range: int32(rover.Range), - Capacity: int32(rover.Capacity), - MaximumIntegrity: int32(rover.MaximumIntegrity), - MaximumCharge: int32(rover.MaximumCharge), - }, - Status: &roveapi.RoverStatus{ - Bearing: rover.Bearing, - Inventory: inv, - Integrity: int32(rover.Integrity), - Charge: int32(rover.Charge), - QueuedCommands: queued, - SailPosition: rover.SailPosition, + Name: rover.Name, + Position: &roveapi.Vector{ + X: int32(rover.Pos.X), + Y: int32(rover.Pos.Y), }, + Range: int32(rover.Range), + Inventory: inv, + Capacity: int32(rover.Capacity), + Integrity: int32(rover.Integrity), + MaximumIntegrity: int32(rover.MaximumIntegrity), + Charge: int32(rover.Charge), + MaximumCharge: int32(rover.MaximumCharge), + IncomingCommands: incoming, + QueuedCommands: queued, + Logs: logs, } } return response, nil @@ -117,7 +143,7 @@ func (s *Server) Status(ctx context.Context, req *roveapi.StatusRequest) (respon func (s *Server) Radar(ctx context.Context, req *roveapi.RadarRequest) (*roveapi.RadarResponse, error) { log.Printf("Handling radar request: %s\n", req.Account.Name) - if valid, err := s.world.Accountant.VerifySecret(req.Account.Name, req.Account.Secret); err != nil { + if valid, err := s.accountant.VerifySecret(req.Account.Name, req.Account.Secret); err != nil { return nil, err } else if !valid { @@ -126,7 +152,7 @@ func (s *Server) Radar(ctx context.Context, req *roveapi.RadarRequest) (*roveapi response := &roveapi.RadarResponse{} - resp, err := s.world.Accountant.GetValue(req.Account.Name, "rover") + resp, err := s.accountant.GetValue(req.Account.Name, "rover") if err != nil { return nil, err @@ -149,19 +175,33 @@ func (s *Server) Radar(ctx context.Context, req *roveapi.RadarRequest) (*roveapi func (s *Server) Command(ctx context.Context, req *roveapi.CommandRequest) (*roveapi.CommandResponse, error) { log.Printf("Handling command request: %s and %+v\n", req.Account.Name, req.Commands) - if valid, err := s.world.Accountant.VerifySecret(req.Account.Name, req.Account.Secret); err != nil { + if valid, err := s.accountant.VerifySecret(req.Account.Name, req.Account.Secret); err != nil { return nil, err } else if !valid { return nil, fmt.Errorf("Secret incorrect for account %s", req.Account.Name) } - resp, err := s.world.Accountant.GetValue(req.Account.Name, "rover") + resp, err := s.accountant.GetValue(req.Account.Name, "rover") if err != nil { return nil, err } - if err := s.world.Enqueue(resp, req.Commands...); err != nil { + var cmds []rove.Command + for _, c := range req.Commands { + n := rove.Command{ + Command: c.Command, + } + switch c.Command { + case roveapi.CommandType_move: + n.Bearing = c.GetBearing() + case roveapi.CommandType_broadcast: + n.Message = c.GetMessage() + } + cmds = append(cmds, n) + } + + if err := s.world.Enqueue(resp, cmds...); err != nil { return nil, err } diff --git a/cmd/rove-server/internal/server.go b/cmd/rove-server/internal/server.go index e2b7e16..102c30d 100644 --- a/cmd/rove-server/internal/server.go +++ b/cmd/rove-server/internal/server.go @@ -4,8 +4,6 @@ import ( "fmt" "log" "net" - "os" - "path" "sync" "github.com/mdiluz/rove/pkg/persistence" @@ -13,12 +11,8 @@ import ( "github.com/mdiluz/rove/proto/roveapi" "github.com/robfig/cron" "google.golang.org/grpc" - "google.golang.org/grpc/credentials" - "google.golang.org/grpc/reflection" ) -var cert = os.Getenv("CERT_NAME") - const ( // PersistentData will allow the server to load and save it's state PersistentData = iota @@ -33,6 +27,9 @@ type Server struct { // Internal state world *rove.World + // Accountant + accountant Accountant + // gRPC server netListener net.Listener grpcServ *grpc.Server @@ -83,6 +80,7 @@ func NewServer(opts ...ServerOption) *Server { persistence: EphemeralData, schedule: cron.New(), world: rove.NewWorld(32), + accountant: NewSimpleAccountant(), } // Apply all options @@ -109,22 +107,8 @@ func (s *Server) Initialise(fillWorld bool) (err error) { if err != nil { log.Fatalf("failed to listen: %v", err) } - - // Load TLS - var opts []grpc.ServerOption - if len(os.Getenv("NO_TLS")) == 0 { - pem := path.Join("/etc/letsencrypt/live/", cert, "fullchain.pem") - key := path.Join("/etc/letsencrypt/live/", cert, "privkey.pem") - creds, err := credentials.NewServerTLSFromFile(pem, key) - if err != nil { - log.Fatalf("failed to setup TLS: %v", err) - } - opts = append(opts, grpc.Creds(creds)) - } - - s.grpcServ = grpc.NewServer(opts...) + s.grpcServ = grpc.NewServer() roveapi.RegisterRoveServer(s.grpcServ, s) - reflection.Register(s.grpcServ) return nil } @@ -147,8 +131,8 @@ func (s *Server) Run() { log.Println("Executing server tick") - // Tick the world - s.world.Tick() + // Run the command queues + s.world.ExecuteCommandQueues() // Save out the new world state if err := s.SaveWorld(); err != nil { @@ -204,7 +188,7 @@ 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 { + if err := persistence.SaveAll("world", s.world, "accounts", s.accountant); err != nil { return fmt.Errorf("failed to save out persistent data: %s", err) } } @@ -216,7 +200,7 @@ func (s *Server) LoadWorld() error { if s.persistence == PersistentData { s.world.Lock() defer s.world.Unlock() - if err := persistence.LoadAll("world", &s.world); err != nil { + if err := persistence.LoadAll("world", &s.world, "accounts", &s.accountant); err != nil { return err } } @@ -225,10 +209,22 @@ func (s *Server) LoadWorld() error { // SpawnRoverForAccount spawns the rover rover for an account func (s *Server) SpawnRoverForAccount(account string) (string, error) { - inst, err := s.world.SpawnRover(account) + inst, err := s.world.SpawnRover() if err != nil { return "", err } + 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/internal/server_test.go b/cmd/rove-server/internal/server_test.go index 40d4b80..36db679 100644 --- a/cmd/rove-server/internal/server_test.go +++ b/cmd/rove-server/internal/server_test.go @@ -1,7 +1,6 @@ package internal import ( - "os" "testing" ) @@ -31,7 +30,6 @@ func TestNewServer_OptionPersistentData(t *testing.T) { } func TestServer_Run(t *testing.T) { - os.Setenv("NO_TLS", "1") server := NewServer() if server == nil { t.Error("Failed to create server") @@ -47,7 +45,6 @@ func TestServer_Run(t *testing.T) { } func TestServer_RunPersistentData(t *testing.T) { - os.Setenv("NO_TLS", "1") server := NewServer(OptionPersistentData()) if server == nil { t.Error("Failed to create server") diff --git a/pkg/accounts/simpleAccountant.go b/cmd/rove-server/internal/simpleAccountant.go similarity index 97% rename from pkg/accounts/simpleAccountant.go rename to cmd/rove-server/internal/simpleAccountant.go index 9d6c43f..3168f30 100644 --- a/pkg/accounts/simpleAccountant.go +++ b/cmd/rove-server/internal/simpleAccountant.go @@ -1,4 +1,4 @@ -package accounts +package internal import ( "fmt" @@ -9,7 +9,7 @@ import ( // SimpleAccountant manages a set of accounts type SimpleAccountant struct { - Accounts map[string]Account + Accounts map[string]Account `json:"accounts"` } // NewSimpleAccountant creates a new accountant diff --git a/cmd/rove/internal/glyph.go b/cmd/rove/internal/glyph.go index 4a774e7..1a26f5d 100644 --- a/cmd/rove/internal/glyph.go +++ b/cmd/rove/internal/glyph.go @@ -22,12 +22,6 @@ const ( // GlyphRoverLive represents a live rover GlyphRoverLive = Glyph('R') - // GlyphRoverDormant represents a dormant rover - GlyphRoverDormant = Glyph('r') - - // GlyphRoverParts represents spare rover parts - GlyphRoverParts = Glyph('*') - // GlyphRockSmall is a small stashable rock GlyphRockSmall = Glyph('o') @@ -57,12 +51,8 @@ func ObjectGlyph(o roveapi.Object) Glyph { return GlyphRoverLive case roveapi.Object_RockSmall: return GlyphRockSmall - case roveapi.Object_RoverDormant: - return GlyphRoverDormant case roveapi.Object_RockLarge: return GlyphRockLarge - case roveapi.Object_RoverParts: - return GlyphRoverParts } log.Fatalf("Unknown object type: %c", o) diff --git a/cmd/rove/main.go b/cmd/rove/main.go index f1e993d..bd98d52 100644 --- a/cmd/rove/main.go +++ b/cmd/rove/main.go @@ -1,7 +1,6 @@ package main import ( - "crypto/tls" "encoding/json" "fmt" "io/ioutil" @@ -9,7 +8,6 @@ import ( "os" "path" "path/filepath" - "strconv" "time" "github.com/mdiluz/rove/cmd/rove/internal" @@ -17,7 +15,6 @@ import ( "github.com/mdiluz/rove/proto/roveapi" "golang.org/x/net/context" "google.golang.org/grpc" - "google.golang.org/grpc/credentials" ) var home = os.Getenv("HOME") @@ -25,44 +22,38 @@ var defaultDataPath = path.Join(home, ".local/share/") // Command usage func printUsage() { - fmt.Fprintln(os.Stderr, "Usage: rove ARG [OPT...]") - fmt.Fprintf(os.Stderr, "\n") - fmt.Fprintln(os.Stderr, "Arguments:") - fmt.Fprintln(os.Stderr, "\tversion outputs version") - fmt.Fprintln(os.Stderr, "\thelp outputs this usage text") - fmt.Fprintln(os.Stderr, "\tconfig [HOST] outputs the local config, optionally sets host") - fmt.Fprintln(os.Stderr, "\tserver-status prints the server status") - fmt.Fprintln(os.Stderr, "\tregister NAME registers an account and spawns a rover") - fmt.Fprintln(os.Stderr, "\tradar prints radar data in ASCII form") - fmt.Fprintln(os.Stderr, "\tstatus gets rover status") - fmt.Fprintln(os.Stderr, "\tcommand CMD [VAL...] [REPEAT] sets the command queue, accepts multiple in sequence") - fmt.Fprintf(os.Stderr, "\n") - fmt.Fprintln(os.Stderr, "Rover commands:") - fmt.Fprintln(os.Stderr, "\ttoggle toggles the current sail mode") - fmt.Fprintln(os.Stderr, "\tstash stores the object at the rover location in the inventory") - fmt.Fprintln(os.Stderr, "\trepair repairs the rover using inventory item") - fmt.Fprintln(os.Stderr, "\tbroadcast MSG broadcast a simple ASCII triplet to nearby rovers") - fmt.Fprintln(os.Stderr, "\tsalvage salvages a dormant rover for parts") - fmt.Fprintln(os.Stderr, "\ttransfer transfer's control into a dormant rover") - fmt.Fprintln(os.Stderr, "\tupgrade SPEC spends rover parts to upgrade one rover spec (capacity, range, integrity, charge") - fmt.Fprintln(os.Stderr, "\twait waits before performing the next command") - fmt.Fprintf(os.Stderr, "\n") - fmt.Fprintln(os.Stderr, "Environment") - fmt.Fprintln(os.Stderr, "\tROVE_USER_DATA path to user data, defaults to "+defaultDataPath) + fmt.Fprintf(os.Stderr, "Usage: rove COMMAND [ARGS...]\n") + fmt.Fprintln(os.Stderr, "\nCommands") + fmt.Fprintln(os.Stderr, "\tserver-status prints the server status") + fmt.Fprintln(os.Stderr, "\tregister NAME registers an account and stores it (use with -name)") + fmt.Fprintln(os.Stderr, "\tcommand COMMAND [VAL...] issue commands to rover, accepts multiple, see below") + fmt.Fprintln(os.Stderr, "\tradar gathers radar data for the current rover") + fmt.Fprintln(os.Stderr, "\tstatus gets status info for current rover") + fmt.Fprintln(os.Stderr, "\tconfig [HOST] outputs the local config info, optionally sets host") + fmt.Fprintln(os.Stderr, "\thelp outputs this usage information") + fmt.Fprintln(os.Stderr, "\tversion outputs version info") + fmt.Fprintln(os.Stderr, "\nRover commands:") + fmt.Fprintln(os.Stderr, "\tmove BEARING moves the rover in the chosen direction") + fmt.Fprintln(os.Stderr, "\tstash stores the object at the rover location in the inventory") + fmt.Fprintln(os.Stderr, "\trepair uses an inventory object to repair the rover") + fmt.Fprintln(os.Stderr, "\trecharge wait a tick to recharge the rover") + fmt.Fprintln(os.Stderr, "\tbroadcast MSG broadcast a simple ASCII triplet to nearby rovers") + fmt.Fprintln(os.Stderr, "\nEnvironment") + fmt.Fprintln(os.Stderr, "\tROVE_USER_DATA path to user data, defaults to "+defaultDataPath) } const gRPCport = 9090 // Account stores data for an account type Account struct { - Name string - Secret string + Name string `json:"name"` + Secret string `json:"secret"` } // Config is used to store internal data type Config struct { - Host string - Account Account + Host string `json:"host,omitempty"` + Account Account `json:"account,omitempty"` } // ConfigPath returns the configuration path @@ -137,20 +128,12 @@ func BearingFromString(s string) roveapi.Bearing { switch s { case "N": return roveapi.Bearing_North - case "NE": - return roveapi.Bearing_NorthEast case "E": return roveapi.Bearing_East - case "SE": - return roveapi.Bearing_SouthEast case "S": return roveapi.Bearing_South - case "SW": - return roveapi.Bearing_SouthWest case "W": return roveapi.Bearing_West - case "NW": - return roveapi.Bearing_NorthWest } return roveapi.Bearing_BearingUnknown } @@ -188,15 +171,8 @@ func InnerMain(command string, args ...string) error { return fmt.Errorf("no host set in %s, set one with '%s config {HOST}'", ConfigPath(), os.Args[0]) } - var opts []grpc.DialOption - if len(os.Getenv("NO_TLS")) == 0 { - opts = append(opts, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{}))) - } else { - opts = append(opts, grpc.WithInsecure()) - } - // Set up the server - clientConn, err := grpc.Dial(fmt.Sprintf("%s:%d", config.Host, gRPCport), opts...) + clientConn, err := grpc.Dial(fmt.Sprintf("%s:%d", config.Host, gRPCport), grpc.WithInsecure()) if err != nil { return err } @@ -248,22 +224,22 @@ func InnerMain(command string, args ...string) error { // Iterate through each command var commands []*roveapi.Command for i := 0; i < len(args); i++ { - - var cmd *roveapi.Command switch args[i] { - case "turn": + case "move": i++ if len(args) == i { - return fmt.Errorf("turn command must be passed a compass bearing") + return fmt.Errorf("move command must be passed bearing") } - b := BearingFromString(args[i]) - if b == roveapi.Bearing_BearingUnknown { - return fmt.Errorf("turn command must be given a valid bearing %s", args[i]) - } - cmd = &roveapi.Command{ - Command: roveapi.CommandType_turn, - Bearing: b, + var b roveapi.Bearing + if b = BearingFromString(args[i]); b == roveapi.Bearing_BearingUnknown { + return fmt.Errorf("unrecognised bearing: %s", args[i]) } + commands = append(commands, + &roveapi.Command{ + Command: roveapi.CommandType_move, + Data: &roveapi.Command_Bearing{Bearing: b}, + }, + ) case "broadcast": i++ if len(args) == i { @@ -271,51 +247,20 @@ func InnerMain(command string, args ...string) error { } else if len(args[i]) > 3 { return fmt.Errorf("broadcast command must be given ASCII triplet of 3 or less: %s", args[i]) } - cmd = &roveapi.Command{ - Command: roveapi.CommandType_broadcast, - Data: []byte(args[i]), - } - case "upgrade": - i++ - if len(args) == i { - return fmt.Errorf("upgrade command must be passed a spec to upgrade") - } - var u roveapi.RoverUpgrade - switch args[i] { - case "capacity": - u = roveapi.RoverUpgrade_Capacity - case "range": - u = roveapi.RoverUpgrade_Range - case "integrity": - u = roveapi.RoverUpgrade_MaximumIntegrity - case "charge": - u = roveapi.RoverUpgrade_MaximumCharge - default: - return fmt.Errorf("upgrade command must be passed a known upgrade spec") - } - cmd = &roveapi.Command{ - Command: roveapi.CommandType_upgrade, - Upgrade: u, - } + commands = append(commands, + &roveapi.Command{ + Command: roveapi.CommandType_broadcast, + Data: &roveapi.Command_Message{Message: []byte(args[i])}, + }, + ) default: // By default just use the command literally - cmd = &roveapi.Command{ - Command: roveapi.CommandType(roveapi.CommandType_value[args[i]]), - } + commands = append(commands, + &roveapi.Command{ + Command: roveapi.CommandType(roveapi.CommandType_value[args[i]]), + }, + ) } - - // Try and convert the next command to a number - number := 0 - if len(args) > i+1 { - num, err := strconv.Atoi(args[i+1]) - if err == nil { - number = num - i++ - } - } - cmd.Repeat = int32(number) - - commands = append(commands, cmd) } _, err := client.Command(ctx, &roveapi.CommandRequest{ @@ -403,15 +348,6 @@ func InnerMain(command string, args ...string) error { func main() { // Bail without any args if len(os.Args) == 1 { - fmt.Fprintf(os.Stderr, "\n") - fmt.Fprintln(os.Stderr, "m mm mmm m m mmm") - fmt.Fprintln(os.Stderr, "#\" \" #\" \"# \"m m\" #\" #") - fmt.Fprintln(os.Stderr, "# # # #m# #\"\"\"\"") - fmt.Fprintln(os.Stderr, "# \"#m#\" # \"#mm\"") - fmt.Fprintf(os.Stderr, "\n") - fmt.Fprintln(os.Stderr, "Rove is an asychronous nomadic game about exploring a planet as part of a loose community.") - fmt.Fprintln(os.Stderr, "Visit https://mdiluz.github.io/rove/ for more information.") - fmt.Fprintf(os.Stderr, "\n") printUsage() os.Exit(1) } diff --git a/cmd/rove/main_test.go b/cmd/rove/main_test.go index 250988a..dd588e1 100644 --- a/cmd/rove/main_test.go +++ b/cmd/rove/main_test.go @@ -13,7 +13,6 @@ import ( ) func Test_InnerMain(t *testing.T) { - os.Setenv("NO_TLS", "1") // Use temporary local user data tmp, err := ioutil.TempDir(os.TempDir(), "rove-") @@ -51,17 +50,12 @@ func Test_InnerMain(t *testing.T) { assert.Error(t, InnerMain("command")) // Give it commands - assert.NoError(t, InnerMain("command", "toggle")) + assert.NoError(t, InnerMain("command", "move", "N")) assert.NoError(t, InnerMain("command", "stash")) assert.NoError(t, InnerMain("command", "repair")) - assert.NoError(t, InnerMain("command", "upgrade", "capacity")) assert.NoError(t, InnerMain("command", "broadcast", "abc")) - assert.NoError(t, InnerMain("command", "wait", "10")) - assert.NoError(t, InnerMain("command", "wait", "1", "turn", "NW", "toggle", "broadcast", "zyx")) // Give it malformed commands - assert.Error(t, InnerMain("command", "unknown")) + assert.Error(t, InnerMain("command", "move", "stash")) assert.Error(t, InnerMain("command", "broadcast")) - assert.Error(t, InnerMain("command", "upgrade")) - assert.Error(t, InnerMain("command", "1")) } diff --git a/docker-compose-test.yml b/docker-compose-test.yml deleted file mode 100644 index aec8cee..0000000 --- a/docker-compose-test.yml +++ /dev/null @@ -1,32 +0,0 @@ -version: '3' - -services: - rove-test-server: - build: - context: . - dockerfile: Dockerfile - image: rove:latest - ports: - - "9090:9090" - environment: - - PORT=9090 - - DATA_PATH=/tmp/ - - WORDS_FILE=data/words_alpha.txt - - TICK_RATE=10 - - NO_TLS=1 - command: [ "./rove-server"] - - rove-tests: - depends_on: [ rove-test-server ] - build: - context: . - dockerfile: Dockerfile - image: rove:latest - environment: - - ROVE_GRPC=rove-test-server - command: [ "./script/wait-for-it.sh", "rove-test-server:9090", "--", "go", "test", "-v", "./...", "--tags=integration", "-cover", "-coverprofile=/mnt/coverage-data/c.out", "-count", "1" ] - volumes: - - /tmp/coverage-data:/mnt/coverage-data:rw - - - diff --git a/docker-compose.yml b/docker-compose.yml index d5606f7..71013b5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -15,11 +15,22 @@ services: - PORT=9090 - DATA_PATH=/mnt/rove-server - WORDS_FILE=data/words_alpha.txt - - TICK_RATE=3 - - CERT_NAME=${CERT_NAME} + - TICK_RATE=5 volumes: - persistent-data:/mnt/rove-server:rw - - /etc/letsencrypt/:/etc/letsencrypt/ command: [ "./rove-server"] + rove-tests: + depends_on: [ rove-server ] + build: + context: . + dockerfile: Dockerfile + image: rove:latest + environment: + - ROVE_GRPC=rove-server + command: [ "./script/wait-for-it.sh", "rove-server:9090", "--", "go", "test", "-v", "./...", "--tags=integration", "-cover", "-coverprofile=/mnt/coverage-data/c.out", "-count", "1" ] + volumes: + - /tmp/coverage-data:/mnt/coverage-data:rw + + diff --git a/pkg/rove/atlas.go b/pkg/atlas/atlas.go similarity index 97% rename from pkg/rove/atlas.go rename to pkg/atlas/atlas.go index ab09dba..05012aa 100644 --- a/pkg/rove/atlas.go +++ b/pkg/atlas/atlas.go @@ -1,4 +1,4 @@ -package rove +package atlas import ( "github.com/mdiluz/rove/pkg/maths" diff --git a/pkg/rove/atlas_test.go b/pkg/atlas/atlas_test.go similarity index 99% rename from pkg/rove/atlas_test.go rename to pkg/atlas/atlas_test.go index 56e5d8d..3d715f1 100644 --- a/pkg/rove/atlas_test.go +++ b/pkg/atlas/atlas_test.go @@ -1,4 +1,4 @@ -package rove +package atlas import ( "testing" diff --git a/pkg/rove/chunkAtlas.go b/pkg/atlas/chunkAtlas.go similarity index 74% rename from pkg/rove/chunkAtlas.go rename to pkg/atlas/chunkAtlas.go index 1903c7b..3f19b07 100644 --- a/pkg/rove/chunkAtlas.go +++ b/pkg/atlas/chunkAtlas.go @@ -1,54 +1,62 @@ -package rove +package atlas import ( "log" + "math/rand" "github.com/mdiluz/rove/pkg/maths" "github.com/mdiluz/rove/proto/roveapi" + "github.com/ojrac/opensimplex-go" ) // chunk represents a fixed square grid of tiles type chunk struct { // Tiles represents the tiles within the chunk - Tiles []byte + Tiles []byte `json:"tiles"` // Objects represents the objects within the chunk // only one possible object per tile for now - Objects map[int]Object + Objects map[int]Object `json:"objects"` } // chunkBasedAtlas represents a grid of Chunks type chunkBasedAtlas struct { // Chunks represents all chunks in the world // This is intentionally not a 2D array so it can be expanded in all directions - Chunks []chunk + Chunks []chunk `json:"chunks"` // LowerBound is the origin of the bottom left corner of the current chunks in world space (current chunks cover >= this value) - LowerBound maths.Vector + LowerBound maths.Vector `json:"lowerBound"` // UpperBound is the top left corner of the current chunks (curent chunks cover < this value) - UpperBound maths.Vector + UpperBound maths.Vector `json:"upperBound"` // ChunkSize is the x/y dimensions of each square chunk - ChunkSize int + ChunkSize int `json:"chunksize"` - // worldGen is the internal world generator - worldGen WorldGen + // terrainNoise describes the noise function for the terrain + terrainNoise opensimplex.Noise + + // terrainNoise describes the noise function for the terrain + objectNoise opensimplex.Noise } const ( - noiseSeed = 1024 + noiseSeed = 1024 + terrainNoiseScale = 6 + objectNoiseScale = 3 ) // NewChunkAtlas creates a new empty atlas func NewChunkAtlas(chunkSize int) Atlas { // Start up with one chunk a := chunkBasedAtlas{ - ChunkSize: chunkSize, - Chunks: make([]chunk, 1), - LowerBound: maths.Vector{X: 0, Y: 0}, - UpperBound: maths.Vector{X: chunkSize, Y: chunkSize}, - worldGen: NewNoiseWorldGen(noiseSeed), + ChunkSize: chunkSize, + Chunks: make([]chunk, 1), + LowerBound: maths.Vector{X: 0, Y: 0}, + UpperBound: maths.Vector{X: chunkSize, Y: chunkSize}, + terrainNoise: opensimplex.New(noiseSeed), + objectNoise: opensimplex.New(noiseSeed), } // Initialise the first chunk a.populate(0) @@ -97,16 +105,41 @@ func (a *chunkBasedAtlas) populate(chunk int) { origin := a.chunkOriginInWorldSpace(chunk) for i := 0; i < a.ChunkSize; i++ { for j := 0; j < a.ChunkSize; j++ { - loc := maths.Vector{X: origin.X + i, Y: origin.Y + j} - // Set the tile - c.Tiles[j*a.ChunkSize+i] = byte(a.worldGen.GetTile(loc)) - - // Set the object - obj := a.worldGen.GetObject(loc) - if obj.Type != roveapi.Object_ObjectUnknown { - c.Objects[j*a.ChunkSize+i] = obj + // Get the terrain noise value for this location + t := a.terrainNoise.Eval2(float64(origin.X+i)/terrainNoiseScale, float64(origin.Y+j)/terrainNoiseScale) + var tile roveapi.Tile + switch { + case t > 0.5: + tile = roveapi.Tile_Gravel + case t > 0.05: + tile = roveapi.Tile_Sand + default: + tile = roveapi.Tile_Rock } + c.Tiles[j*a.ChunkSize+i] = byte(tile) + + // Get the object noise value for this location + o := a.objectNoise.Eval2(float64(origin.X+i)/objectNoiseScale, float64(origin.Y+j)/objectNoiseScale) + var obj = roveapi.Object_ObjectUnknown + switch { + case o > 0.6: + obj = roveapi.Object_RockLarge + case o > 0.5: + obj = roveapi.Object_RockSmall + } + if obj != roveapi.Object_ObjectUnknown { + c.Objects[j*a.ChunkSize+i] = Object{Type: roveapi.Object(obj)} + } + } + } + + // Set up any objects + for i := 0; i < len(c.Tiles); i++ { + if rand.Intn(16) == 0 { + c.Objects[i] = Object{Type: roveapi.Object_RockLarge} + } else if rand.Intn(32) == 0 { + c.Objects[i] = Object{Type: roveapi.Object_RockSmall} } } @@ -203,11 +236,12 @@ func (a *chunkBasedAtlas) worldSpaceToChunkWithGrow(v maths.Vector) int { // Create the new empty atlas newAtlas := chunkBasedAtlas{ - ChunkSize: a.ChunkSize, - LowerBound: lower, - UpperBound: upper, - Chunks: make([]chunk, size.X*size.Y), - worldGen: a.worldGen, + ChunkSize: a.ChunkSize, + LowerBound: lower, + UpperBound: upper, + Chunks: make([]chunk, size.X*size.Y), + terrainNoise: a.terrainNoise, + objectNoise: a.objectNoise, } // Log that we're resizing diff --git a/pkg/rove/objects.go b/pkg/atlas/objects.go similarity index 82% rename from pkg/rove/objects.go rename to pkg/atlas/objects.go index df10453..e1b6730 100644 --- a/pkg/rove/objects.go +++ b/pkg/atlas/objects.go @@ -1,4 +1,4 @@ -package rove +package atlas import ( "github.com/mdiluz/rove/proto/roveapi" @@ -7,10 +7,7 @@ import ( // Object represents an object in the world type Object struct { // The type of the object - Type roveapi.Object - - // Data is an internal type used for certain types of object - Data []byte + Type roveapi.Object `json:"type"` } // IsBlocking checks if an object is a blocking object @@ -32,7 +29,6 @@ func (o *Object) IsBlocking() bool { func (o *Object) IsStashable() bool { var stashable = [...]roveapi.Object{ roveapi.Object_RockSmall, - roveapi.Object_RoverParts, } for _, t := range stashable { diff --git a/pkg/maths/vector.go b/pkg/maths/vector.go index a74ff16..8a63708 100644 --- a/pkg/maths/vector.go +++ b/pkg/maths/vector.go @@ -8,8 +8,8 @@ import ( // Vector desribes a 3D vector type Vector struct { - X int - Y int + X int `json:"x"` + Y int `json:"y"` } // Add adds one vector to another @@ -89,31 +89,13 @@ func BearingToVector(b roveapi.Bearing) Vector { switch b { case roveapi.Bearing_North: return Vector{Y: 1} - case roveapi.Bearing_NorthEast: - return Vector{X: 1, Y: 1} case roveapi.Bearing_East: return Vector{X: 1} - case roveapi.Bearing_SouthEast: - return Vector{X: 1, Y: -1} case roveapi.Bearing_South: return Vector{Y: -1} - case roveapi.Bearing_SouthWest: - return Vector{X: -1, Y: -1} case roveapi.Bearing_West: return Vector{X: -1} - case roveapi.Bearing_NorthWest: - return Vector{X: -1, Y: 1} } return Vector{} } - -// Dot returns the dot product of two vectors -func Dot(a Vector, b Vector) int { - return a.X*b.X + a.Y*b.Y -} - -// AngleCos returns the cosine of the angle between two vectors -func AngleCos(a Vector, b Vector) float64 { - return float64(Dot(a, b)) / a.Length() * b.Length() -} diff --git a/pkg/rove/command.go b/pkg/rove/command.go new file mode 100644 index 0000000..e64d646 --- /dev/null +++ b/pkg/rove/command.go @@ -0,0 +1,17 @@ +package rove + +import "github.com/mdiluz/rove/proto/roveapi" + +// Command represends a single command to execute +type Command struct { + Command roveapi.CommandType `json:"command"` + + // Used in the move command + Bearing roveapi.Bearing `json:"bearing,omitempty"` + + // Used in the broadcast command + Message []byte `json:"message,omitempty"` +} + +// CommandStream is a list of commands to execute in order +type CommandStream []Command diff --git a/pkg/rove/command_test.go b/pkg/rove/command_test.go index bf1eed6..337782f 100644 --- a/pkg/rove/command_test.go +++ b/pkg/rove/command_test.go @@ -1,7 +1,6 @@ package rove import ( - "encoding/json" "testing" "github.com/mdiluz/rove/pkg/maths" @@ -9,297 +8,63 @@ import ( "github.com/stretchr/testify/assert" ) -func TestCommand_Invalid(t *testing.T) { - w := NewWorld(8) - name, err := w.SpawnRover("") +func TestCommand_Move(t *testing.T) { + world := NewWorld(8) + a, err := world.SpawnRover() assert.NoError(t, err) - - err = w.Enqueue(name, &roveapi.Command{Command: roveapi.CommandType_none}) - assert.Error(t, err) -} - -func TestCommand_Toggle(t *testing.T) { - w := NewWorld(8) - a, err := w.SpawnRover("") - assert.NoError(t, err) - - r, err := w.GetRover(a) - assert.NoError(t, err) - assert.Equal(t, roveapi.SailPosition_SolarCharging, r.SailPosition) - - err = w.Enqueue(a, &roveapi.Command{Command: roveapi.CommandType_toggle}) - assert.NoError(t, err) - w.Tick() - - r, err = w.GetRover(a) - assert.NoError(t, err) - assert.Equal(t, roveapi.SailPosition_CatchingWind, r.SailPosition) - - err = w.Enqueue(a, &roveapi.Command{Command: roveapi.CommandType_toggle}) - assert.NoError(t, err) - w.Tick() - - r, err = w.GetRover(a) - assert.NoError(t, err) - assert.Equal(t, roveapi.SailPosition_SolarCharging, r.SailPosition) -} - -func TestCommand_Turn(t *testing.T) { - w := NewWorld(8) - a, err := w.SpawnRover("") - assert.NoError(t, err) - - err = w.Enqueue(a, &roveapi.Command{Command: roveapi.CommandType_turn, Bearing: roveapi.Bearing_NorthWest}) - assert.NoError(t, err) - w.Tick() - - r, err := w.GetRover(a) - assert.NoError(t, err) - assert.Equal(t, roveapi.Bearing_NorthWest, r.Bearing) -} - -func TestCommand_Stash(t *testing.T) { - w := NewWorld(8) - name, err := w.SpawnRover("") - assert.NoError(t, err) - - info, err := w.GetRover(name) - assert.NoError(t, err) - assert.Empty(t, info.Inventory) - - // Drop a pickup below us - w.Atlas.SetObject(info.Pos, Object{Type: roveapi.Object_RockSmall}) - - // Try and stash it - err = w.Enqueue(name, &roveapi.Command{Command: roveapi.CommandType_stash}) - assert.NoError(t, err) - w.Tick() - - // Check we now have it in the inventory - info, err = w.GetRover(name) - assert.NoError(t, err) - assert.Equal(t, 1, len(info.Inventory)) - assert.Equal(t, Object{Type: roveapi.Object_RockSmall}, info.Inventory[0]) - - // Check it's no longer on the atlas - _, obj := w.Atlas.QueryPosition(info.Pos) - assert.Equal(t, Object{Type: roveapi.Object_ObjectUnknown}, obj) -} - -func TestCommand_Repair(t *testing.T) { - w := NewWorld(8) - name, err := w.SpawnRover("") - assert.NoError(t, err) - - info, err := w.GetRover(name) - assert.NoError(t, err) - assert.Equal(t, info.MaximumIntegrity, info.Integrity) - - // Put a blocking rock to the north - w.Atlas.SetObject(info.Pos.Added(maths.Vector{X: 0, Y: 1}), Object{Type: roveapi.Object_RockLarge}) - - // Try and move and make sure we're blocked - newpos, err := w.TryMoveRover(name, roveapi.Bearing_North) - assert.NoError(t, err) - assert.Equal(t, info.Pos, newpos) - - // Check we're damaged - info, err = w.GetRover(name) - assert.NoError(t, err) - assert.Equal(t, info.MaximumIntegrity-1, info.Integrity) - - // Stash a repair object - w.Atlas.SetObject(info.Pos, Object{Type: roveapi.Object_RoverParts}) - obj, err := w.RoverStash(name) - assert.NoError(t, err) - assert.Equal(t, roveapi.Object_RoverParts, obj) - - // Enqueue the repair and tick - err = w.Enqueue(name, &roveapi.Command{Command: roveapi.CommandType_repair}) - assert.NoError(t, err) - w.Tick() - - // Check we're repaired - info, err = w.GetRover(name) - assert.NoError(t, err) - assert.Equal(t, info.MaximumIntegrity, info.Integrity) - assert.Equal(t, 0, len(info.Inventory)) -} - -func TestCommand_Broadcast(t *testing.T) { - w := NewWorld(8) - name, err := w.SpawnRover("") - assert.NoError(t, err) - - // Enqueue the broadcast and tick - err = w.Enqueue(name, &roveapi.Command{Command: roveapi.CommandType_broadcast, Data: []byte("ABC")}) - assert.NoError(t, err) - w.Tick() - - info, err := w.GetRover(name) - assert.NoError(t, err) - assert.Contains(t, info.Logs[len(info.Logs)-1].Text, "ABC") -} - -func TestCommand_Salvage(t *testing.T) { - w := NewWorld(8) - name, err := w.SpawnRover("") - assert.NoError(t, err) - - info, err := w.GetRover(name) - assert.NoError(t, err) - - w.Atlas.SetObject(info.Pos, Object{Type: roveapi.Object_RoverDormant}) - - // Enqueue the broadcast and tick - err = w.Enqueue(name, &roveapi.Command{Command: roveapi.CommandType_salvage}) - assert.NoError(t, err) - w.Tick() - - // Check we now have some rover parts - info, err = w.GetRover(name) - assert.NoError(t, err) - assert.NotEmpty(t, info.Inventory) - for _, i := range info.Inventory { - assert.Equal(t, roveapi.Object_RoverParts, i.Type) + pos := maths.Vector{ + X: 1.0, + Y: 2.0, } - // Check the dormant rover is gone - _, obj := w.Atlas.QueryPosition(info.Pos) - assert.Equal(t, roveapi.Object_ObjectUnknown, obj.Type) + err = world.WarpRover(a, pos) + assert.NoError(t, err, "Failed to set position for rover") + + // Try the move command + moveCommand := Command{Command: roveapi.CommandType_move, Bearing: roveapi.Bearing_North} + assert.NoError(t, world.Enqueue(a, moveCommand), "Failed to execute move command") + + // Tick the world + world.EnqueueAllIncoming() + world.ExecuteCommandQueues() + + newPos, err := world.RoverPosition(a) + assert.NoError(t, err, "Failed to set position for rover") + pos.Add(maths.Vector{X: 0.0, Y: 1}) + assert.Equal(t, pos, newPos, "Failed to correctly set position for rover") } -func TestCommand_Transfer(t *testing.T) { - w := NewWorld(8) - acc, err := w.Accountant.RegisterAccount("tmp") +func TestCommand_Recharge(t *testing.T) { + world := NewWorld(8) + a, err := world.SpawnRover() assert.NoError(t, err) - nameA, err := w.SpawnRover(acc.Name) - assert.NoError(t, err) - - infoA, err := w.GetRover(nameA) - assert.NoError(t, err) - - // Drop a dormant rover on the current position - infoB := DefaultRover() - infoB.Name = "abc" - infoB.Pos = infoA.Pos - data, err := json.Marshal(infoB) - assert.NoError(t, err) - w.Atlas.SetObject(infoA.Pos, Object{Type: roveapi.Object_RoverDormant, Data: data}) - - // Enqueue a transfer as well as a dud command - err = w.Enqueue(nameA, - &roveapi.Command{Command: roveapi.CommandType_transfer}, - &roveapi.Command{Command: roveapi.CommandType_broadcast, Data: []byte("xyz")}) - assert.NoError(t, err) - w.Tick() - - // Ensure both command queues are empty - assert.Empty(t, w.CommandQueue[nameA]) - assert.Empty(t, w.CommandQueue[infoB.Name]) - - // Verify the account now controls the new rover - accountRover, err := w.Accountant.GetValue(acc.Name, "rover") - assert.NoError(t, err) - assert.Equal(t, infoB.Name, accountRover) - - // Verify the position now has a dormant rover - _, obj := w.Atlas.QueryPosition(infoA.Pos) - assert.Equal(t, roveapi.Object_RoverDormant, obj.Type) - - // Verify the stored data matches - var stored Rover - err = json.Unmarshal(obj.Data, &stored) - assert.NoError(t, err) - assert.Equal(t, infoA.Name, stored.Name) - - // Verify the new rover data matches what we put in - infoB2, err := w.GetRover(infoB.Name) - assert.NoError(t, err) - assert.Equal(t, infoB.Name, infoB2.Name) - -} - -func TestCommand_Wait(t *testing.T) { - w := NewWorld(8) - a, err := w.SpawnRover("") - assert.NoError(t, err) - - r, err := w.GetRover(a) - assert.NoError(t, err) - assert.Equal(t, roveapi.SailPosition_SolarCharging, r.SailPosition) - - err = w.Enqueue(a, &roveapi.Command{Command: roveapi.CommandType_wait, Repeat: 4}, &roveapi.Command{Command: roveapi.CommandType_toggle}) - assert.NoError(t, err) - - // Tick 5 times during the wait (1 normal execute + 4) - for i := 0; i < 5; i++ { - w.Tick() - - r, err = w.GetRover(a) - assert.NoError(t, err) - assert.Equal(t, roveapi.SailPosition_SolarCharging, r.SailPosition) + pos := maths.Vector{ + X: 1.0, + Y: 2.0, } - // One last tick to do the toggle - w.Tick() + err = world.WarpRover(a, pos) + assert.NoError(t, err, "Failed to set position for rover") + + // Move to use up some charge + moveCommand := Command{Command: roveapi.CommandType_move, Bearing: roveapi.Bearing_North} + assert.NoError(t, world.Enqueue(a, moveCommand), "Failed to queue move command") + + // Tick the world + world.EnqueueAllIncoming() + world.ExecuteCommandQueues() + + rover, _ := world.GetRover(a) + assert.Equal(t, rover.MaximumCharge-1, rover.Charge) + + chargeCommand := Command{Command: roveapi.CommandType_recharge} + assert.NoError(t, world.Enqueue(a, chargeCommand), "Failed to queue recharge command") + + // Tick the world + world.EnqueueAllIncoming() + world.ExecuteCommandQueues() + + rover, _ = world.GetRover(a) + assert.Equal(t, rover.MaximumCharge, rover.Charge) - r, err = w.GetRover(a) - assert.NoError(t, err) - assert.Equal(t, roveapi.SailPosition_CatchingWind, r.SailPosition) -} - -func TestCommand_Upgrade(t *testing.T) { - w := NewWorld(8) - name, err := w.SpawnRover("") - assert.NoError(t, err) - rover, ok := w.Rovers[name] - assert.True(t, ok) - - // Try an invalid upgrade - err = w.Enqueue(name, &roveapi.Command{Command: roveapi.CommandType_upgrade}) - assert.Error(t, err) - - // Try a valid command but without the parts - err = w.Enqueue(name, &roveapi.Command{Command: roveapi.CommandType_upgrade, Upgrade: roveapi.RoverUpgrade_Capacity}) - assert.NoError(t, err) - - // Ensure nothing changed and we logged the attempt - pre := rover.Capacity - w.Tick() - assert.Equal(t, pre, rover.Capacity) - assert.Contains(t, rover.Logs[len(rover.Logs)-1].Text, "tried") - - // One non-part item - rover.Inventory = []Object{ - { - Type: roveapi.Object_RoverParts, - }, - { - Type: roveapi.Object_RoverParts, - }, - { - Type: roveapi.Object_RockSmall, - }, - { - Type: roveapi.Object_RoverParts, - }, - { - Type: roveapi.Object_RoverParts, - }, - { - Type: roveapi.Object_RoverParts, - }, - } - - // Try a valid command again - err = w.Enqueue(name, &roveapi.Command{Command: roveapi.CommandType_upgrade, Upgrade: roveapi.RoverUpgrade_Capacity}) - assert.NoError(t, err) - - // Check that the capacity increases on the tick and all the parts are used - pre = rover.Capacity - w.Tick() - assert.Equal(t, pre+1, rover.Capacity) - assert.Equal(t, 1, len(rover.Inventory)) - assert.Equal(t, roveapi.Object_RockSmall, rover.Inventory[0].Type) } diff --git a/pkg/rove/rover.go b/pkg/rove/rover.go index bebde50..089b44a 100644 --- a/pkg/rove/rover.go +++ b/pkg/rove/rover.go @@ -1,89 +1,54 @@ package rove import ( - "bufio" "fmt" "log" - "math/rand" - "os" "time" - "github.com/google/uuid" + "github.com/mdiluz/rove/pkg/atlas" "github.com/mdiluz/rove/pkg/maths" - "github.com/mdiluz/rove/proto/roveapi" -) - -const ( - maxLogEntries = 16 ) // RoverLogEntry describes a single log entry for the rover type RoverLogEntry struct { // Time is the timestamp of the entry - Time time.Time + Time time.Time `json:"time"` // Text contains the information in this log entry - Text string + Text string `json:"text"` } // Rover describes a single rover in the world type Rover struct { // Unique name of this rover - Name string + Name string `json:"name"` // Pos represents where this rover is in the world - Pos maths.Vector - - // Bearing is the current direction the rover is facing - Bearing roveapi.Bearing + Pos maths.Vector `json:"pos"` // Range represents the distance the unit's radar can see - Range int + Range int `json:"range"` // Inventory represents any items the rover is carrying - Inventory []Object + Inventory []atlas.Object `json:"inventory"` // Capacity is the maximum number of inventory items - Capacity int + Capacity int `json:"capacity"` // Integrity represents current rover health - Integrity int + Integrity int `json:"integrity"` // MaximumIntegrity is the full integrity of the rover - MaximumIntegrity int + MaximumIntegrity int `json:"maximum-integrity"` // Charge is the amount of energy the rover has - Charge int + Charge int `json:"charge"` // MaximumCharge is the maximum charge able to be stored - MaximumCharge int - - // SailPosition is the current position of the sails - SailPosition roveapi.SailPosition - - // Current number of ticks in this move, used for sailing speeds - MoveTicks int + MaximumCharge int `json:"maximum-Charge"` // Logs Stores log of information - Logs []RoverLogEntry - - // The account that owns this rover - Owner string -} - -// DefaultRover returns a default rover object with default settings -func DefaultRover() *Rover { - return &Rover{ - Range: 10, - Integrity: 10, - MaximumIntegrity: 10, - Capacity: 10, - Charge: 10, - MaximumCharge: 10, - Bearing: roveapi.Bearing_North, - SailPosition: roveapi.SailPosition_SolarCharging, - Name: GenerateRoverName(), - } + Logs []RoverLogEntry `json:"logs"` } // AddLogEntryf adds an entry to the rovers log @@ -96,42 +61,4 @@ func (r *Rover) AddLogEntryf(format string, args ...interface{}) { Text: text, }, ) - - // Limit the number of logs - if len(r.Logs) > maxLogEntries { - r.Logs = r.Logs[len(r.Logs)-maxLogEntries:] - } -} - -var wordsFile = os.Getenv("WORDS_FILE") -var roverWords []string - -// GenerateRoverName generates a new rover name -func GenerateRoverName() string { - - // Try and load the rover words file - if len(roverWords) == 0 { - // Try and load the words file - if file, err := os.Open(wordsFile); err != nil { - log.Printf("Couldn't read words file [%s], running without words: %s\n", wordsFile, err) - } else { - defer file.Close() - scanner := bufio.NewScanner(file) - for scanner.Scan() { - roverWords = append(roverWords, scanner.Text()) - } - if scanner.Err() != nil { - log.Printf("Failure during word file scan: %s\n", scanner.Err()) - } - } - } - - // Assign a random name if we have words - if len(roverWords) > 0 { - // Loop until we find a unique name - return fmt.Sprintf("%s-%s", roverWords[rand.Intn(len(roverWords))], roverWords[rand.Intn(len(roverWords))]) - } - - // Default to a unique string - return uuid.New().String() } diff --git a/pkg/rove/world.go b/pkg/rove/world.go index d509d8d..7c313ba 100644 --- a/pkg/rove/world.go +++ b/pkg/rove/world.go @@ -1,81 +1,104 @@ package rove import ( - "encoding/json" + "bufio" "fmt" "log" "math/rand" + "os" "sync" - "github.com/mdiluz/rove/pkg/accounts" + "github.com/google/uuid" + "github.com/mdiluz/rove/pkg/atlas" "github.com/mdiluz/rove/pkg/maths" "github.com/mdiluz/rove/proto/roveapi" ) -const ( - // ticksPerNormalMove defines the number of ticks it should take for a "normal" speed move - ticksPerNormalMove = 4 - - // upgradeCost is the cost in rover parts needed to upgrade a rover specification - upgradeCost = 5 -) - -// CommandStream is a list of commands to execute in order -type CommandStream []*roveapi.Command - // World describes a self contained universe and everything in it type World struct { - // TicksPerDay is the amount of ticks in a single day - TicksPerDay int + TicksPerDay int `json:"ticks-per-day"` // Current number of ticks from the start - CurrentTicks int + CurrentTicks int `json:"current-ticks"` // Rovers is a id->data map of all the rovers in the game - Rovers map[string]*Rover + Rovers map[string]Rover `json:"rovers"` // Atlas represends the world map of chunks and tiles - Atlas Atlas - - // Wind is the current wind direction - Wind roveapi.Bearing + Atlas atlas.Atlas `json:"atlas"` // Commands is the set of currently executing command streams per rover - CommandQueue map[string]CommandStream - - // Accountant - Accountant accounts.Accountant + CommandQueue map[string]CommandStream `json:"commands"` + // Incoming represents the set of commands to add to the queue at the end of the current tick + CommandIncoming map[string]CommandStream `json:"incoming"` // Mutex to lock around all world operations worldMutex sync.RWMutex // Mutex to lock around command operations cmdMutex sync.RWMutex + // Set of possible words to use for names + words []string } +var wordsFile = os.Getenv("WORDS_FILE") + // NewWorld creates a new world object func NewWorld(chunkSize int) *World { + + // Try and load the words file + var lines []string + if file, err := os.Open(wordsFile); err != nil { + log.Printf("Couldn't read words file [%s], running without words: %s\n", wordsFile, err) + } else { + defer file.Close() + scanner := bufio.NewScanner(file) + for scanner.Scan() { + lines = append(lines, scanner.Text()) + } + if scanner.Err() != nil { + log.Printf("Failure during word file scan: %s\n", scanner.Err()) + } + } + return &World{ - Rovers: make(map[string]*Rover), - CommandQueue: make(map[string]CommandStream), - Atlas: NewChunkAtlas(chunkSize), - TicksPerDay: 24, - CurrentTicks: 0, - Accountant: accounts.NewSimpleAccountant(), - Wind: roveapi.Bearing_North, + Rovers: make(map[string]Rover), + CommandQueue: make(map[string]CommandStream), + CommandIncoming: make(map[string]CommandStream), + Atlas: atlas.NewChunkAtlas(chunkSize), + words: lines, + TicksPerDay: 24, + CurrentTicks: 0, } } -// SpawnRover adds an rover to the game (without lock) -func (w *World) SpawnRover(account string) (string, error) { +// SpawnRover adds an rover to the game +func (w *World) SpawnRover() (string, error) { w.worldMutex.Lock() defer w.worldMutex.Unlock() // Initialise the rover - rover := DefaultRover() + rover := Rover{ + Range: 4, + Integrity: 10, + MaximumIntegrity: 10, + Capacity: 10, + Charge: 10, + MaximumCharge: 10, + Name: uuid.New().String(), + } - // Assign the owner - rover.Owner = account + // Assign a random name if we have words + if len(w.words) > 0 { + 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 rover.Pos = maths.Vector{ @@ -101,13 +124,7 @@ func (w *World) SpawnRover(account string) (string, error) { // Append the rover to the list w.Rovers[rover.Name] = rover - var err error - // Only assign if we've been given an account - if len(account) > 0 { - err = w.Accountant.AssignData(account, "rover", rover.Name) - } - - return rover.Name, err + return rover.Name, nil } // GetRover gets a specific rover by name @@ -119,7 +136,7 @@ func (w *World) GetRover(rover string) (Rover, error) { if !ok { return Rover{}, fmt.Errorf("Failed to find rover with name: %s", rover) } - return *i, nil + return i, nil } // RoverRecharge charges up a rover @@ -142,6 +159,7 @@ func (w *World) RoverRecharge(rover string) (int, error) { i.Charge++ i.AddLogEntryf("recharged to %d", i.Charge) } + w.Rovers[rover] = i return i.Charge, nil } @@ -176,6 +194,7 @@ func (w *World) RoverBroadcast(rover string, message []byte) (err error) { } i.AddLogEntryf("broadcasted %s", string(message)) + w.Rovers[rover] = i return } @@ -184,26 +203,12 @@ func (w *World) DestroyRover(rover string) error { w.worldMutex.Lock() defer w.worldMutex.Unlock() - r, ok := w.Rovers[rover] + _, ok := w.Rovers[rover] if !ok { return fmt.Errorf("no rover matching id") } - // Remove this rover from tracked rovers delete(w.Rovers, rover) - - r.Owner = "" - r.AddLogEntryf("rover destroyed") - - // Marshal the rover data - data, err := json.Marshal(r) - if err != nil { - return err - } - - // Place the dormant rover down - w.Atlas.SetObject(r.Pos, Object{Type: roveapi.Object_RoverDormant, Data: data}) - return nil } @@ -230,11 +235,12 @@ func (w *World) SetRoverPosition(rover string, pos maths.Vector) error { } i.Pos = pos + w.Rovers[rover] = i return nil } // RoverInventory returns the inventory of a requested rover -func (w *World) RoverInventory(rover string) ([]Object, error) { +func (w *World) RoverInventory(rover string) ([]atlas.Object, error) { w.worldMutex.RLock() defer w.worldMutex.RUnlock() @@ -266,11 +272,12 @@ func (w *World) WarpRover(rover string, pos maths.Vector) error { } i.Pos = pos + w.Rovers[rover] = i return nil } -// TryMoveRover attempts to move a rover in a specific direction -func (w *World) TryMoveRover(rover string, b roveapi.Bearing) (maths.Vector, error) { +// MoveRover attempts to move a rover in a specific direction +func (w *World) MoveRover(rover string, b roveapi.Bearing) (maths.Vector, error) { w.worldMutex.Lock() defer w.worldMutex.Unlock() @@ -279,6 +286,12 @@ func (w *World) TryMoveRover(rover string, b roveapi.Bearing) (maths.Vector, err return maths.Vector{}, fmt.Errorf("no rover matching id") } + // Ensure the rover has energy + if i.Charge <= 0 { + return i.Pos, nil + } + i.Charge-- + // Try the new move position newPos := i.Pos.Added(maths.BearingToVector(b)) @@ -288,11 +301,17 @@ func (w *World) TryMoveRover(rover string, b roveapi.Bearing) (maths.Vector, err i.AddLogEntryf("moved %s to %+v", b.String(), newPos) // Perform the move i.Pos = newPos + w.Rovers[rover] = i } else { // If it is a blocking tile, reduce the rover integrity i.AddLogEntryf("tried to move %s to %+v", b.String(), newPos) i.Integrity = i.Integrity - 1 i.AddLogEntryf("had a collision, new integrity %d", i.Integrity) + if i.Integrity == 0 { + // TODO: The rover needs to be left dormant with the player + } else { + w.Rovers[rover] = i + } } return i.Pos, nil @@ -310,13 +329,11 @@ func (w *World) RoverStash(rover string) (roveapi.Object, error) { // Can't pick up when full if len(r.Inventory) >= r.Capacity { - r.AddLogEntryf("tried to stash object but inventory was full") return roveapi.Object_ObjectUnknown, nil } // Ensure the rover has energy if r.Charge <= 0 { - r.AddLogEntryf("tried to stash object but had no charge") return roveapi.Object_ObjectUnknown, nil } r.Charge-- @@ -328,237 +345,11 @@ func (w *World) RoverStash(rover string) (roveapi.Object, error) { r.AddLogEntryf("stashed %c", obj.Type) r.Inventory = append(r.Inventory, obj) - w.Atlas.SetObject(r.Pos, Object{Type: roveapi.Object_ObjectUnknown}) + w.Rovers[rover] = r + w.Atlas.SetObject(r.Pos, atlas.Object{Type: roveapi.Object_ObjectUnknown}) return obj.Type, nil } -// RoverSalvage will salvage a rover for parts -func (w *World) RoverSalvage(rover string) (roveapi.Object, error) { - w.worldMutex.Lock() - defer w.worldMutex.Unlock() - - r, ok := w.Rovers[rover] - if !ok { - return roveapi.Object_ObjectUnknown, fmt.Errorf("no rover matching id") - } - - // Can't pick up when full - if len(r.Inventory) >= r.Capacity { - r.AddLogEntryf("tried to salvage dormant rover but inventory was full") - return roveapi.Object_ObjectUnknown, nil - } - - // Ensure the rover has energy - if r.Charge <= 0 { - r.AddLogEntryf("tried to salvage dormant rover but had no charge") - return roveapi.Object_ObjectUnknown, nil - } - r.Charge-- - - _, obj := w.Atlas.QueryPosition(r.Pos) - if obj.Type != roveapi.Object_RoverDormant { - r.AddLogEntryf("tried to salvage dormant rover but found no rover to salvage") - return roveapi.Object_ObjectUnknown, nil - } - - r.AddLogEntryf("salvaged dormant rover") - for i := 0; i < 5; i++ { - if len(r.Inventory) == r.Capacity { - break - } - r.Inventory = append(r.Inventory, Object{Type: roveapi.Object_RoverParts}) - } - w.Atlas.SetObject(r.Pos, Object{Type: roveapi.Object_ObjectUnknown}) - return obj.Type, nil -} - -// RoverTransfer will transfer rover control to dormant rover -func (w *World) RoverTransfer(rover string) (string, error) { - w.worldMutex.Lock() - defer w.worldMutex.Unlock() - - oldRover, ok := w.Rovers[rover] - if !ok { - return "", fmt.Errorf("no rover matching id") - } - - _, obj := w.Atlas.QueryPosition(oldRover.Pos) - if obj.Type != roveapi.Object_RoverDormant { - oldRover.AddLogEntryf("tried to transfer to dormant rover but found no rover") - return "", nil - } - - // Unmarshal the dormant rover - var newRover Rover - err := json.Unmarshal(obj.Data, &newRover) - if err != nil { - return "", err - } - - // Add logs - oldRover.AddLogEntryf("transferring to dormant rover %s", newRover.Name) - newRover.AddLogEntryf("transferred from rover %s", oldRover.Name) - - // Transfer the ownership - err = w.Accountant.AssignData(oldRover.Owner, "rover", newRover.Name) - if err != nil { - return "", err - } - newRover.Owner = oldRover.Owner - oldRover.Owner = "" - - // Place the old rover in the world - oldRoverData, err := json.Marshal(oldRover) - if err != nil { - return "", err - } - w.Atlas.SetObject(oldRover.Pos, Object{Type: roveapi.Object_RoverDormant, Data: oldRoverData}) - - // Swap the rovers in the tracking - w.Rovers[newRover.Name] = &newRover - delete(w.Rovers, oldRover.Name) - - // Clear the command queues for both rovers - delete(w.CommandQueue, oldRover.Name) - delete(w.CommandQueue, newRover.Name) - - return newRover.Name, nil -} - -// RoverToggle will toggle the sail position -func (w *World) RoverToggle(rover string) (roveapi.SailPosition, error) { - w.worldMutex.Lock() - defer w.worldMutex.Unlock() - - r, ok := w.Rovers[rover] - if !ok { - return roveapi.SailPosition_UnknownSailPosition, fmt.Errorf("no rover matching id") - } - - // Swap the sail position - switch r.SailPosition { - case roveapi.SailPosition_CatchingWind: - r.SailPosition = roveapi.SailPosition_SolarCharging - case roveapi.SailPosition_SolarCharging: - r.SailPosition = roveapi.SailPosition_CatchingWind - } - - // Reset the movement ticks - r.MoveTicks = 0 - - return r.SailPosition, nil -} - -// RoverUpgrade will try to upgrade the rover -func (w *World) RoverUpgrade(rover string, upgrade roveapi.RoverUpgrade) (int, error) { - w.worldMutex.Lock() - defer w.worldMutex.Unlock() - - r, ok := w.Rovers[rover] - if !ok { - return 0, fmt.Errorf("no rover matching id") - } - - cost := upgradeCost - num := 0 - for i := range r.Inventory { - if r.Inventory[i].Type == roveapi.Object_RoverParts { - num++ - } - } - - if num < cost { - r.AddLogEntryf("tried to upgrade but lacked rover parts") - return 0, nil - } - - // Apply the upgrade - var ret int - switch upgrade { - case roveapi.RoverUpgrade_Capacity: - r.Capacity++ - ret = r.Capacity - case roveapi.RoverUpgrade_Range: - r.Range++ - ret = r.Range - case roveapi.RoverUpgrade_MaximumCharge: - r.MaximumCharge++ - ret = r.MaximumCharge - case roveapi.RoverUpgrade_MaximumIntegrity: - r.MaximumIntegrity++ - ret = r.MaximumIntegrity - default: - return 0, fmt.Errorf("unknown upgrade: %s", upgrade) - } - - // Remove the cost in rover parts - var n []Object - for _, o := range r.Inventory { - if o.Type == roveapi.Object_RoverParts && cost > 0 { - cost-- - } else { - n = append(n, o) - } - } - // Assign back the inventory - r.Inventory = n - - r.AddLogEntryf("upgraded %s to %d", upgrade, ret) - - return ret, nil -} - -// RoverTurn will turn the rover -func (w *World) RoverTurn(rover string, bearing roveapi.Bearing) (roveapi.Bearing, error) { - w.worldMutex.Lock() - defer w.worldMutex.Unlock() - - r, ok := w.Rovers[rover] - if !ok { - return roveapi.Bearing_BearingUnknown, fmt.Errorf("no rover matching id") - } - - // Set the new bearing - r.Bearing = bearing - // Reset the movement ticks - r.MoveTicks = 0 - - return r.Bearing, nil -} - -// RoverRepair will turn the rover -func (w *World) RoverRepair(rover string) (int, error) { - w.worldMutex.Lock() - defer w.worldMutex.Unlock() - - r, ok := w.Rovers[rover] - if !ok { - return 0, fmt.Errorf("no rover matching id") - } - - // Can't repair past max - if r.Integrity >= r.MaximumIntegrity { - return r.Integrity, nil - } - - // Find rover parts in inventory - for i, o := range r.Inventory { - if o.Type == roveapi.Object_RoverParts { - - // Copy-erase from slice - r.Inventory[i] = r.Inventory[len(r.Inventory)-1] - r.Inventory = r.Inventory[:len(r.Inventory)-1] - - // Repair - r.Integrity = r.Integrity + 1 - r.AddLogEntryf("repaired self to %d", r.Integrity) - break - } - } - - return r.Integrity, nil -} - // RadarFromRover can be used to query what a rover can currently see func (w *World) RadarFromRover(rover string) (radar []roveapi.Tile, objs []roveapi.Object, err error) { w.worldMutex.RLock() @@ -618,7 +409,10 @@ func (w *World) RadarFromRover(rover string) (radar []roveapi.Tile, objs []rovea } // RoverCommands returns current commands for the given rover -func (w *World) RoverCommands(rover string) (queued CommandStream) { +func (w *World) RoverCommands(rover string) (incoming []Command, queued []Command) { + if c, ok := w.CommandIncoming[rover]; ok { + incoming = c + } if c, ok := w.CommandQueue[rover]; ok { queued = c } @@ -626,34 +420,27 @@ func (w *World) RoverCommands(rover string) (queued CommandStream) { } // Enqueue will queue the commands given -func (w *World) Enqueue(rover string, commands ...*roveapi.Command) error { +func (w *World) Enqueue(rover string, commands ...Command) error { // First validate the commands for _, c := range commands { switch c.Command { - case roveapi.CommandType_broadcast: - if len(c.GetData()) > 3 { - return fmt.Errorf("too many characters in message (limit 3): %d", len(c.GetData())) + case roveapi.CommandType_move: + if c.Bearing == roveapi.Bearing_BearingUnknown { + return fmt.Errorf("bearing must be valid") } - for _, b := range c.GetData() { + case roveapi.CommandType_broadcast: + if len(c.Message) > 3 { + return fmt.Errorf("too many characters in message (limit 3): %d", len(c.Message)) + } + for _, b := range c.Message { if b < 37 || b > 126 { return fmt.Errorf("invalid message character: %c", b) } } - case roveapi.CommandType_turn: - if c.GetBearing() == roveapi.Bearing_BearingUnknown { - return fmt.Errorf("turn command given unknown bearing") - } - case roveapi.CommandType_upgrade: - if c.GetUpgrade() == roveapi.RoverUpgrade_RoverUpgradeUnknown { - return fmt.Errorf("upgrade command given unknown upgrade") - } - case roveapi.CommandType_wait: - case roveapi.CommandType_toggle: case roveapi.CommandType_stash: case roveapi.CommandType_repair: - case roveapi.CommandType_salvage: - case roveapi.CommandType_transfer: + case roveapi.CommandType_recharge: // Nothing to verify default: return fmt.Errorf("unknown command: %s", c.Command) @@ -664,31 +451,39 @@ func (w *World) Enqueue(rover string, commands ...*roveapi.Command) error { w.cmdMutex.Lock() defer w.cmdMutex.Unlock() - w.CommandQueue[rover] = commands + // Override the incoming command set + w.CommandIncoming[rover] = commands return nil } -// Tick will execute any commands in the current command queue and tick the world -func (w *World) Tick() { +// EnqueueAllIncoming will enqueue the incoming commands +func (w *World) EnqueueAllIncoming() { + // Add any incoming commands from this tick and clear that queue + for id, incoming := range w.CommandIncoming { + commands := w.CommandQueue[id] + commands = append(commands, incoming...) + w.CommandQueue[id] = commands + } + w.CommandIncoming = make(map[string]CommandStream) +} + +// ExecuteCommandQueues will execute any commands in the current command queue +func (w *World) ExecuteCommandQueues() { w.cmdMutex.Lock() defer w.cmdMutex.Unlock() // Iterate through all the current commands for rover, cmds := range w.CommandQueue { if len(cmds) != 0 { + // Extract the first command in the queue + c := cmds[0] + w.CommandQueue[rover] = cmds[1:] // Execute the command - if done, err := w.ExecuteCommand(cmds[0], rover); err != nil { + if err := w.ExecuteCommand(&c, rover); err != nil { log.Println(err) // TODO: Report this error somehow - - } else if done { - // Extract the first command in the queue - // Only if the command queue still has entries (the command may have modified this queue) - if _, ok := w.CommandQueue[rover]; ok { - w.CommandQueue[rover] = cmds[1:] - } } } else { @@ -697,115 +492,54 @@ func (w *World) Tick() { } } - // Move all the rovers based on current wind and sails - for n, r := range w.Rovers { - // Skip if we're not catching the wind - if r.SailPosition != roveapi.SailPosition_CatchingWind { - continue - } - - // Increment the current move ticks - r.MoveTicks++ - - // Get the difference between the two bearings - // Normalise, we don't care about clockwise/anticlockwise - diff := maths.Abs(int(w.Wind - r.Bearing)) - if diff > 4 { - diff = 8 - diff - } - - // Calculate the travel "ticks" - var ticksToMove int - switch diff { - case 0: - // Going with the wind, travel at base speed of once every 4 ticks - ticksToMove = ticksPerNormalMove - case 1: - // At a slight angle, we can go a little faster - ticksToMove = ticksPerNormalMove / 2 - case 2: - // Perpendicular to wind, max speed - ticksToMove = 1 - case 3: - // Heading at 45 degrees into the wind, back to min speed - ticksToMove = ticksPerNormalMove - case 4: - // Heading durectly into the wind, no movement at all - default: - log.Fatalf("bearing difference of %d should be impossible", diff) - } - - // If we've incremented over the current move ticks on the rover, we can try and make the move - if ticksToMove != 0 && r.MoveTicks >= ticksToMove { - _, err := w.TryMoveRover(n, r.Bearing) - if err != nil { - log.Println(err) - // TODO: Report this error somehow - } - - // Reset the move ticks - r.MoveTicks = 0 - } - } - - // Check all rover integrities - for _, r := range w.Rovers { - if r.Integrity <= 0 { - // The rover has died destroy it - err := w.DestroyRover(r.Name) - if err != nil { - log.Println(err) - // TODO: Report this error somehow - } - - // Spawn a new one for this account - _, err = w.SpawnRover(r.Owner) - if err != nil { - log.Println(err) - // TODO: Report this error somehow - } - } - } + // Add any incoming commands from this tick and clear that queue + w.EnqueueAllIncoming() // Increment the current tick count w.CurrentTicks++ - - // Change the wind every day - if (w.CurrentTicks % w.TicksPerDay) == 0 { - w.Wind = roveapi.Bearing((rand.Int() % 8) + 1) // Random cardinal bearing - } } // ExecuteCommand will execute a single command -func (w *World) ExecuteCommand(c *roveapi.Command, rover string) (done bool, err error) { - log.Printf("Executing command: %+v for %s\n", c.Command, rover) +func (w *World) ExecuteCommand(c *Command, rover string) (err error) { + log.Printf("Executing command: %+v for %s\n", *c, rover) switch c.Command { - case roveapi.CommandType_toggle: - _, err = w.RoverToggle(rover) + case roveapi.CommandType_move: + if _, err := w.MoveRover(rover, c.Bearing); err != nil { + return err + } + case roveapi.CommandType_stash: - _, err = w.RoverStash(rover) + if _, err := w.RoverStash(rover); err != nil { + return err + } + case roveapi.CommandType_repair: - _, err = w.RoverRepair(rover) + r, err := w.GetRover(rover) + if err != nil { + return err + } + // Consume an inventory item to repair if possible + if len(r.Inventory) > 0 && r.Integrity < r.MaximumIntegrity { + r.Inventory = r.Inventory[:len(r.Inventory)-1] + r.Integrity = r.Integrity + 1 + r.AddLogEntryf("repaired self to %d", r.Integrity) + w.Rovers[rover] = r + } + case roveapi.CommandType_recharge: + _, err := w.RoverRecharge(rover) + if err != nil { + return err + } case roveapi.CommandType_broadcast: - err = w.RoverBroadcast(rover, c.GetData()) - case roveapi.CommandType_turn: - _, err = w.RoverTurn(rover, c.GetBearing()) - case roveapi.CommandType_salvage: - _, err = w.RoverSalvage(rover) - case roveapi.CommandType_transfer: - _, err = w.RoverTransfer(rover) - case roveapi.CommandType_upgrade: - _, err = w.RoverUpgrade(rover, c.GetUpgrade()) - case roveapi.CommandType_wait: - // Nothing to do + if err := w.RoverBroadcast(rover, c.Message); err != nil { + return err + } default: - return true, fmt.Errorf("unknown command: %s", c.Command) + return fmt.Errorf("unknown command: %s", c.Command) } - // Decrement the repeat number - c.Repeat-- - return c.Repeat < 0, err + return } // Daytime returns if it's currently daytime diff --git a/pkg/rove/world_test.go b/pkg/rove/world_test.go index f085d89..7cf2483 100644 --- a/pkg/rove/world_test.go +++ b/pkg/rove/world_test.go @@ -3,6 +3,7 @@ package rove import ( "testing" + "github.com/mdiluz/rove/pkg/atlas" "github.com/mdiluz/rove/pkg/maths" "github.com/mdiluz/rove/proto/roveapi" "github.com/stretchr/testify/assert" @@ -18,9 +19,9 @@ func TestNewWorld(t *testing.T) { func TestWorld_CreateRover(t *testing.T) { world := NewWorld(8) - a, err := world.SpawnRover("") + a, err := world.SpawnRover() assert.NoError(t, err) - b, err := world.SpawnRover("") + b, err := world.SpawnRover() assert.NoError(t, err) // Basic duplicate check @@ -33,7 +34,7 @@ func TestWorld_CreateRover(t *testing.T) { func TestWorld_GetRover(t *testing.T) { world := NewWorld(4) - a, err := world.SpawnRover("") + a, err := world.SpawnRover() assert.NoError(t, err) rover, err := world.GetRover(a) @@ -44,9 +45,9 @@ func TestWorld_GetRover(t *testing.T) { func TestWorld_DestroyRover(t *testing.T) { world := NewWorld(1) - a, err := world.SpawnRover("") + a, err := world.SpawnRover() assert.NoError(t, err) - b, err := world.SpawnRover("") + b, err := world.SpawnRover() assert.NoError(t, err) err = world.DestroyRover(a) @@ -62,7 +63,7 @@ func TestWorld_DestroyRover(t *testing.T) { func TestWorld_GetSetMovePosition(t *testing.T) { world := NewWorld(4) - a, err := world.SpawnRover("") + a, err := world.SpawnRover() assert.NoError(t, err) pos := maths.Vector{ @@ -78,47 +79,49 @@ func TestWorld_GetSetMovePosition(t *testing.T) { assert.Equal(t, pos, newPos, "Failed to correctly set position for rover") b := roveapi.Bearing_North - newPos, err = world.TryMoveRover(a, b) + newPos, err = world.MoveRover(a, b) assert.NoError(t, err, "Failed to set position for rover") pos.Add(maths.Vector{X: 0, Y: 1}) assert.Equal(t, pos, newPos, "Failed to correctly move position for rover") rover, err := world.GetRover(a) assert.NoError(t, err, "Failed to get rover information") + assert.Equal(t, rover.MaximumCharge-1, rover.Charge, "Rover should have lost charge for moving") assert.Contains(t, rover.Logs[len(rover.Logs)-1].Text, "moved", "Rover logs should contain the move") // Place a tile in front of the rover - world.Atlas.SetObject(maths.Vector{X: 0, Y: 2}, Object{Type: roveapi.Object_RockLarge}) - newPos, err = world.TryMoveRover(a, b) + world.Atlas.SetObject(maths.Vector{X: 0, Y: 2}, atlas.Object{Type: roveapi.Object_RockLarge}) + newPos, err = world.MoveRover(a, b) assert.NoError(t, err, "Failed to move rover") assert.Equal(t, pos, newPos, "Failed to correctly not move position for rover into wall") + + rover, err = world.GetRover(a) + assert.NoError(t, err, "Failed to get rover information") + assert.Equal(t, rover.MaximumCharge-2, rover.Charge, "Rover should have lost charge for move attempt") } func TestWorld_RadarFromRover(t *testing.T) { // Create world that should have visible walls on the radar world := NewWorld(2) - a, err := world.SpawnRover("") + a, err := world.SpawnRover() assert.NoError(t, err) - b, err := world.SpawnRover("") + b, err := world.SpawnRover() assert.NoError(t, err) // Warp the rovers into position bpos := maths.Vector{X: -3, Y: -3} - world.Atlas.SetObject(bpos, Object{Type: roveapi.Object_ObjectUnknown}) assert.NoError(t, world.WarpRover(b, bpos), "Failed to warp rover") - world.Atlas.SetObject(maths.Vector{X: 0, Y: 0}, Object{Type: roveapi.Object_ObjectUnknown}) assert.NoError(t, world.WarpRover(a, maths.Vector{X: 0, Y: 0}), "Failed to warp rover") - r, err := world.GetRover(a) - assert.NoError(t, err) - radar, objs, err := world.RadarFromRover(a) assert.NoError(t, err, "Failed to get radar from rover") - fullRange := r.Range + r.Range + 1 + fullRange := 4 + 4 + 1 assert.Equal(t, fullRange*fullRange, len(radar), "Radar returned wrong length") assert.Equal(t, fullRange*fullRange, len(objs), "Radar returned wrong length") - // TODO: Verify the other rover is on the radar + // Test the expected values + assert.Equal(t, roveapi.Object_RoverLive, objs[1+fullRange]) + assert.Equal(t, roveapi.Object_RoverLive, objs[4+4*fullRange]) // Check the radar results are stable radar1, objs1, err := world.RadarFromRover(a) @@ -129,11 +132,82 @@ func TestWorld_RadarFromRover(t *testing.T) { assert.Equal(t, objs1, objs2) } -func TestWorld_RoverDamage(t *testing.T) { +func TestWorld_RoverStash(t *testing.T) { world := NewWorld(2) - acc, err := world.Accountant.RegisterAccount("tmp") - assert.NoError(t, err) - a, err := world.SpawnRover(acc.Name) + a, err := world.SpawnRover() + assert.NoError(t, err) + + pos := maths.Vector{ + X: 0.0, + Y: 0.0, + } + + world.Atlas.SetObject(pos, atlas.Object{Type: roveapi.Object_ObjectUnknown}) + err = world.WarpRover(a, pos) + assert.NoError(t, err, "Failed to set position for rover") + + rover, err := world.GetRover(a) + assert.NoError(t, err, "Failed to get rover") + + for i := 0; i < rover.Capacity; i++ { + // Place an object + world.Atlas.SetObject(pos, atlas.Object{Type: roveapi.Object_RockSmall}) + + // Pick it up + o, err := world.RoverStash(a) + assert.NoError(t, err, "Failed to stash") + assert.Equal(t, roveapi.Object_RockSmall, o, "Failed to get correct object") + + // Check it's gone + _, obj := world.Atlas.QueryPosition(pos) + assert.Equal(t, roveapi.Object_ObjectUnknown, obj.Type, "Stash failed to remove object from atlas") + + // Check we have it + inv, err := world.RoverInventory(a) + assert.NoError(t, err, "Failed to get inventory") + assert.Equal(t, i+1, len(inv)) + assert.Equal(t, atlas.Object{Type: roveapi.Object_RockSmall}, inv[i]) + + // Check that this did reduce the charge + info, err := world.GetRover(a) + assert.NoError(t, err, "Failed to get rover") + assert.Equal(t, info.MaximumCharge-(i+1), info.Charge, "Rover lost charge for stash") + assert.Contains(t, info.Logs[len(info.Logs)-1].Text, "stashed", "Rover logs should contain the move") + } + + // Recharge the rover + for i := 0; i < rover.MaximumCharge; i++ { + _, err = world.RoverRecharge(a) + assert.NoError(t, err) + + } + + // Place an object + world.Atlas.SetObject(pos, atlas.Object{Type: roveapi.Object_RockSmall}) + + // Try to pick it up + o, err := world.RoverStash(a) + assert.NoError(t, err, "Failed to stash") + assert.Equal(t, roveapi.Object_ObjectUnknown, o, "Failed to get correct object") + + // Check it's still there + _, obj := world.Atlas.QueryPosition(pos) + assert.Equal(t, roveapi.Object_RockSmall, obj.Type, "Stash failed to remove object from atlas") + + // Check we don't have it + inv, err := world.RoverInventory(a) + assert.NoError(t, err, "Failed to get inventory") + assert.Equal(t, rover.Capacity, len(inv)) + + // Check that this didn't reduce the charge + info, err := world.GetRover(a) + assert.NoError(t, err, "Failed to get rover") + assert.Equal(t, info.MaximumCharge, info.Charge, "Rover lost charge for non-stash") +} + +func TestWorld_RoverDamage(t *testing.T) { + world := NewWorld(2) + a, err := world.SpawnRover() assert.NoError(t, err) pos := maths.Vector{ @@ -141,16 +215,15 @@ func TestWorld_RoverDamage(t *testing.T) { Y: 0.0, } - world.Atlas.SetObject(pos, Object{Type: roveapi.Object_ObjectUnknown}) err = world.WarpRover(a, pos) assert.NoError(t, err, "Failed to set position for rover") info, err := world.GetRover(a) assert.NoError(t, err, "couldn't get rover info") - world.Atlas.SetObject(maths.Vector{X: 0.0, Y: 1.0}, Object{Type: roveapi.Object_RockLarge}) + world.Atlas.SetObject(maths.Vector{X: 0.0, Y: 1.0}, atlas.Object{Type: roveapi.Object_RockLarge}) - vec, err := world.TryMoveRover(a, roveapi.Bearing_North) + vec, err := world.MoveRover(a, roveapi.Bearing_North) assert.NoError(t, err, "Failed to move rover") assert.Equal(t, pos, vec, "Rover managed to move into large rock") @@ -158,29 +231,102 @@ func TestWorld_RoverDamage(t *testing.T) { assert.NoError(t, err, "couldn't get rover info") assert.Equal(t, info.Integrity-1, newinfo.Integrity, "rover should have lost integrity") assert.Contains(t, newinfo.Logs[len(newinfo.Logs)-1].Text, "collision", "Rover logs should contain the collision") +} - // Keep moving to damage the rover - for i := 0; i < info.Integrity-1; i++ { - vec, err := world.TryMoveRover(a, roveapi.Bearing_North) - assert.NoError(t, err, "Failed to move rover") - assert.Equal(t, pos, vec, "Rover managed to move into large rock") +func TestWorld_RoverRepair(t *testing.T) { + world := NewWorld(2) + a, err := world.SpawnRover() + assert.NoError(t, err) + + pos := maths.Vector{ + X: 0.0, + Y: 0.0, } - // Tick the world to check for rover deaths - world.Tick() + world.Atlas.SetObject(pos, atlas.Object{Type: roveapi.Object_ObjectUnknown}) - // Rover should have been destroyed now - _, err = world.GetRover(a) - assert.Error(t, err) + err = world.WarpRover(a, pos) + assert.NoError(t, err, "Failed to set position for rover") + + originalInfo, err := world.GetRover(a) + assert.NoError(t, err, "couldn't get rover info") + + // Pick up something to repair with + world.Atlas.SetObject(pos, atlas.Object{Type: roveapi.Object_RockSmall}) + o, err := world.RoverStash(a) + assert.NoError(t, err, "Failed to stash") + assert.Equal(t, roveapi.Object_RockSmall, o, "Failed to get correct object") + + world.Atlas.SetObject(maths.Vector{X: 0.0, Y: 1.0}, atlas.Object{Type: roveapi.Object_RockLarge}) + + // Try and bump into the rock + vec, err := world.MoveRover(a, roveapi.Bearing_North) + assert.NoError(t, err, "Failed to move rover") + assert.Equal(t, pos, vec, "Rover managed to move into large rock") + + newinfo, err := world.GetRover(a) + assert.NoError(t, err, "couldn't get rover info") + assert.Equal(t, originalInfo.Integrity-1, newinfo.Integrity, "rover should have lost integrity") + + err = world.ExecuteCommand(&Command{Command: roveapi.CommandType_repair}, a) + assert.NoError(t, err, "Failed to repair rover") + + newinfo, err = world.GetRover(a) + assert.NoError(t, err, "couldn't get rover info") + assert.Equal(t, originalInfo.Integrity, newinfo.Integrity, "rover should have gained integrity") + assert.Contains(t, newinfo.Logs[len(newinfo.Logs)-1].Text, "repair", "Rover logs should contain the repair") + + // Check again that it can't repair past the max + world.Atlas.SetObject(pos, atlas.Object{Type: roveapi.Object_RockSmall}) + o, err = world.RoverStash(a) + assert.NoError(t, err, "Failed to stash") + assert.Equal(t, roveapi.Object_RockSmall, o, "Failed to get correct object") + + err = world.ExecuteCommand(&Command{Command: roveapi.CommandType_repair}, a) + assert.NoError(t, err, "Failed to repair rover") + + newinfo, err = world.GetRover(a) + assert.NoError(t, err, "couldn't get rover info") + assert.Equal(t, originalInfo.Integrity, newinfo.Integrity, "rover should have kept the same integrity") +} + +func TestWorld_Charge(t *testing.T) { + world := NewWorld(4) + a, err := world.SpawnRover() + assert.NoError(t, err) + + // Get the rover information + rover, err := world.GetRover(a) + assert.NoError(t, err, "Failed to get rover information") + assert.Equal(t, rover.MaximumCharge, rover.Charge, "Rover should start with maximum charge") + + // Use up all the charge + for i := 0; i < rover.MaximumCharge; i++ { + // Get the initial position + initialPos, err := world.RoverPosition(a) + assert.NoError(t, err, "Failed to get position for rover") + + // Ensure the path ahead is empty + world.Atlas.SetTile(initialPos.Added(maths.BearingToVector(roveapi.Bearing_North)), roveapi.Tile_Rock) + world.Atlas.SetObject(initialPos.Added(maths.BearingToVector(roveapi.Bearing_North)), atlas.Object{Type: roveapi.Object_ObjectUnknown}) + + // Try and move north (along unblocked path) + newPos, err := world.MoveRover(a, roveapi.Bearing_North) + assert.NoError(t, err, "Failed to set position for rover") + assert.Equal(t, initialPos.Added(maths.BearingToVector(roveapi.Bearing_North)), newPos, "Failed to correctly move position for rover") + + // Ensure rover lost charge + rover, err := world.GetRover(a) + assert.NoError(t, err, "Failed to get rover information") + assert.Equal(t, rover.MaximumCharge-(i+1), rover.Charge, "Rover should have lost charge") + } - _, obj := world.Atlas.QueryPosition(info.Pos) - assert.Equal(t, roveapi.Object_RoverDormant, obj.Type) } func TestWorld_Daytime(t *testing.T) { world := NewWorld(1) - a, err := world.SpawnRover("") + a, err := world.SpawnRover() assert.NoError(t, err) // Remove rover charge @@ -196,7 +342,7 @@ func TestWorld_Daytime(t *testing.T) { // Loop for half the day for i := 0; i < world.TicksPerDay/2; i++ { assert.True(t, world.Daytime()) - world.Tick() + world.ExecuteCommandQueues() } // Remove rover charge again @@ -212,22 +358,20 @@ func TestWorld_Daytime(t *testing.T) { // Loop for half the day for i := 0; i < world.TicksPerDay/2; i++ { assert.False(t, world.Daytime()) - world.Tick() + world.ExecuteCommandQueues() } } func TestWorld_Broadcast(t *testing.T) { world := NewWorld(8) - a, err := world.SpawnRover("") + a, err := world.SpawnRover() assert.NoError(t, err) - b, err := world.SpawnRover("") + b, err := world.SpawnRover() assert.NoError(t, err) // Warp rovers near to eachother - world.Atlas.SetObject(maths.Vector{X: 0, Y: 0}, Object{Type: roveapi.Object_ObjectUnknown}) - world.Atlas.SetObject(maths.Vector{X: 1, Y: 0}, Object{Type: roveapi.Object_ObjectUnknown}) assert.NoError(t, world.WarpRover(a, maths.Vector{X: 0, Y: 0})) assert.NoError(t, world.WarpRover(b, maths.Vector{X: 1, Y: 0})) @@ -246,7 +390,7 @@ func TestWorld_Broadcast(t *testing.T) { assert.Contains(t, rb.Logs[len(rb.Logs)-1].Text, "ABC", "Rover A should have logged it's broadcast") // Warp B outside of the range of A - world.Atlas.SetObject(maths.Vector{X: ra.Range, Y: 0}, Object{Type: roveapi.Object_ObjectUnknown}) + world.Atlas.SetObject(maths.Vector{X: ra.Range, Y: 0}, atlas.Object{Type: roveapi.Object_ObjectUnknown}) assert.NoError(t, world.WarpRover(b, maths.Vector{X: ra.Range, Y: 0})) // Broadcast from a again @@ -263,7 +407,7 @@ func TestWorld_Broadcast(t *testing.T) { assert.Contains(t, rb.Logs[len(rb.Logs)-1].Text, "XYZ", "Rover A should have logged it's broadcast") // Warp B outside of the range of A - world.Atlas.SetObject(maths.Vector{X: ra.Range + 1, Y: 0}, Object{Type: roveapi.Object_ObjectUnknown}) + world.Atlas.SetObject(maths.Vector{X: ra.Range + 1, Y: 0}, atlas.Object{Type: roveapi.Object_ObjectUnknown}) assert.NoError(t, world.WarpRover(b, maths.Vector{X: ra.Range + 1, Y: 0})) // Broadcast from a again @@ -279,81 +423,3 @@ func TestWorld_Broadcast(t *testing.T) { assert.NoError(t, err) assert.Contains(t, rb.Logs[len(rb.Logs)-1].Text, "HJK", "Rover A should have logged it's broadcast") } - -func TestWorld_Sailing(t *testing.T) { - world := NewWorld(8) - world.Tick() // One initial tick to set the wind direction the first time - world.Wind = roveapi.Bearing_North // Set the wind direction to north - - name, err := world.SpawnRover("") - assert.NoError(t, err) - - // Warp the rover to 0,0 after clearing it - world.Atlas.SetObject(maths.Vector{X: 0, Y: 0}, Object{Type: roveapi.Object_ObjectUnknown}) - assert.NoError(t, world.WarpRover(name, maths.Vector{X: 0, Y: 0})) - - s, err := world.RoverToggle(name) - assert.NoError(t, err) - assert.Equal(t, roveapi.SailPosition_CatchingWind, s) - - b, err := world.RoverTurn(name, roveapi.Bearing_North) - assert.NoError(t, err) - assert.Equal(t, roveapi.Bearing_North, b) - - // Clear the space to the north - world.Atlas.SetObject(maths.Vector{X: 0, Y: 1}, Object{Type: roveapi.Object_ObjectUnknown}) - - // Tick the world and check we've moved not moved - world.Tick() - info, err := world.GetRover(name) - assert.NoError(t, err) - assert.Equal(t, maths.Vector{Y: 0}, info.Pos) - - // Loop a few more times - for i := 0; i < ticksPerNormalMove-2; i++ { - world.Tick() - info, err := world.GetRover(name) - assert.NoError(t, err) - assert.Equal(t, maths.Vector{Y: 0}, info.Pos) - } - - // Now check we've moved (after the TicksPerNormalMove number of ticks) - world.Tick() - info, err = world.GetRover(name) - assert.NoError(t, err) - assert.Equal(t, maths.Vector{Y: 1}, info.Pos) - - // Reset the world ticks back to stop any wind changes etc. - world.CurrentTicks = 1 - - // Face the rover south, into the wind - b, err = world.RoverTurn(name, roveapi.Bearing_South) - assert.NoError(t, err) - assert.Equal(t, roveapi.Bearing_South, b) - - // Tick a bunch, we should never move - for i := 0; i < ticksPerNormalMove*2; i++ { - world.Tick() - info, err := world.GetRover(name) - assert.NoError(t, err) - assert.Equal(t, maths.Vector{Y: 1}, info.Pos) - } - - // Reset the world ticks back to stop any wind changes etc. - world.CurrentTicks = 1 - world.Wind = roveapi.Bearing_SouthEast // Set up a south easternly wind - - // Turn the rover perpendicular - b, err = world.RoverTurn(name, roveapi.Bearing_NorthEast) - assert.NoError(t, err) - assert.Equal(t, roveapi.Bearing_NorthEast, b) - - // Clear a space - world.Atlas.SetObject(maths.Vector{X: 1, Y: 2}, Object{Type: roveapi.Object_ObjectUnknown}) - - // Now check we've moved immediately - world.Tick() - info, err = world.GetRover(name) - assert.NoError(t, err) - assert.Equal(t, maths.Vector{X: 1, Y: 2}, info.Pos) -} diff --git a/pkg/rove/worldgen.go b/pkg/rove/worldgen.go deleted file mode 100644 index 6cb425f..0000000 --- a/pkg/rove/worldgen.go +++ /dev/null @@ -1,70 +0,0 @@ -package rove - -import ( - "github.com/mdiluz/rove/pkg/maths" - "github.com/mdiluz/rove/proto/roveapi" - "github.com/ojrac/opensimplex-go" -) - -// WorldGen describes a world gen algorythm -type WorldGen interface { - // GetTile generates a tile for a location - GetTile(v maths.Vector) roveapi.Tile - - // GetObject generates an object for a location - GetObject(v maths.Vector) Object -} - -// NoiseWorldGen returns a noise based world generator -type NoiseWorldGen struct { - // noise describes the noise function - noise opensimplex.Noise -} - -// NewNoiseWorldGen creates a new noise based world generator -func NewNoiseWorldGen(seed int64) WorldGen { - return &NoiseWorldGen{ - noise: opensimplex.New(seed), - } -} - -const ( - terrainNoiseScale = 15 - rockNoiseScale = 3 - partsNoiseScale = 2 -) - -// GetTile returns the chosen tile at a location -func (g *NoiseWorldGen) GetTile(v maths.Vector) roveapi.Tile { - t := g.noise.Eval2(float64(v.X)/terrainNoiseScale, float64(v.Y)/terrainNoiseScale) - switch { - case t > 0.5: - return roveapi.Tile_Gravel - case t > 0.05: - return roveapi.Tile_Sand - default: - return roveapi.Tile_Rock - } -} - -// GetObject returns the chosen object at a location -func (g *NoiseWorldGen) GetObject(v maths.Vector) (obj Object) { - r := g.noise.Eval2(float64(v.X)/rockNoiseScale, float64(v.Y)/rockNoiseScale) - switch { - // Prioritise rocks - case r > 0.6: - obj.Type = roveapi.Object_RockLarge - case r > 0.5: - obj.Type = roveapi.Object_RockSmall - - default: - // Otherwise, try some rover parts - p := g.noise.Eval2(float64(v.X)/partsNoiseScale, float64(v.Y)/partsNoiseScale) - switch { - case p > 0.7: - obj.Type = roveapi.Object_RoverParts - } - } - - return obj -} diff --git a/proto/roveapi/roveapi.pb.go b/proto/roveapi/roveapi.pb.go index 81a6cb5..e35c863 100644 --- a/proto/roveapi/roveapi.pb.go +++ b/proto/roveapi/roveapi.pb.go @@ -39,51 +39,35 @@ type CommandType int32 const ( CommandType_none CommandType = 0 - // Waits before performing the next command - CommandType_wait CommandType = 1 - // Toggles the sails, either catching the wind, or charging from the sun - CommandType_toggle CommandType = 2 - // Turns the rover in the specified bearing (requires bearing) - CommandType_turn CommandType = 3 + // Move the rover in a direction, requires bearing + CommandType_move CommandType = 1 // Stashes item at current location in rover inventory - CommandType_stash CommandType = 4 + CommandType_stash CommandType = 2 // Repairs the rover using an inventory object - CommandType_repair CommandType = 5 - // Broadcasts a message to nearby rovers (requires data) - CommandType_broadcast CommandType = 6 - // Salvages a neighboring dormant rover for parts - CommandType_salvage CommandType = 7 - // Transfers remote control into dormant rover - CommandType_transfer CommandType = 8 - // Upgrades a chosen rover specification using 5 rover parts - CommandType_upgrade CommandType = 9 + CommandType_repair CommandType = 3 + // Waits a tick to add more charge to the rover + CommandType_recharge CommandType = 4 + // Broadcasts a message to nearby rovers + CommandType_broadcast CommandType = 5 ) // Enum value maps for CommandType. var ( CommandType_name = map[int32]string{ 0: "none", - 1: "wait", - 2: "toggle", - 3: "turn", - 4: "stash", - 5: "repair", - 6: "broadcast", - 7: "salvage", - 8: "transfer", - 9: "upgrade", + 1: "move", + 2: "stash", + 3: "repair", + 4: "recharge", + 5: "broadcast", } CommandType_value = map[string]int32{ "none": 0, - "wait": 1, - "toggle": 2, - "turn": 3, - "stash": 4, - "repair": 5, - "broadcast": 6, - "salvage": 7, - "transfer": 8, - "upgrade": 9, + "move": 1, + "stash": 2, + "repair": 3, + "recharge": 4, + "broadcast": 5, } ) @@ -114,20 +98,15 @@ func (CommandType) EnumDescriptor() ([]byte, []int) { return file_roveapi_roveapi_proto_rawDescGZIP(), []int{0} } -// Bearing represents a compass direction type Bearing int32 const ( // BearingUnknown an unknown invalid bearing Bearing_BearingUnknown Bearing = 0 Bearing_North Bearing = 1 - Bearing_NorthEast Bearing = 2 - Bearing_East Bearing = 3 - Bearing_SouthEast Bearing = 4 - Bearing_South Bearing = 5 - Bearing_SouthWest Bearing = 6 - Bearing_West Bearing = 7 - Bearing_NorthWest Bearing = 8 + Bearing_East Bearing = 2 + Bearing_South Bearing = 3 + Bearing_West Bearing = 4 ) // Enum value maps for Bearing. @@ -135,24 +114,16 @@ var ( Bearing_name = map[int32]string{ 0: "BearingUnknown", 1: "North", - 2: "NorthEast", - 3: "East", - 4: "SouthEast", - 5: "South", - 6: "SouthWest", - 7: "West", - 8: "NorthWest", + 2: "East", + 3: "South", + 4: "West", } Bearing_value = map[string]int32{ "BearingUnknown": 0, "North": 1, - "NorthEast": 2, - "East": 3, - "SouthEast": 4, - "South": 5, - "SouthWest": 6, - "West": 7, - "NorthWest": 8, + "East": 2, + "South": 3, + "West": 4, } ) @@ -183,62 +154,6 @@ func (Bearing) EnumDescriptor() ([]byte, []int) { return file_roveapi_roveapi_proto_rawDescGZIP(), []int{1} } -// Describes the type of upgrade -type RoverUpgrade int32 - -const ( - RoverUpgrade_RoverUpgradeUnknown RoverUpgrade = 0 - RoverUpgrade_Range RoverUpgrade = 1 - RoverUpgrade_Capacity RoverUpgrade = 2 - RoverUpgrade_MaximumIntegrity RoverUpgrade = 3 - RoverUpgrade_MaximumCharge RoverUpgrade = 4 -) - -// Enum value maps for RoverUpgrade. -var ( - RoverUpgrade_name = map[int32]string{ - 0: "RoverUpgradeUnknown", - 1: "Range", - 2: "Capacity", - 3: "MaximumIntegrity", - 4: "MaximumCharge", - } - RoverUpgrade_value = map[string]int32{ - "RoverUpgradeUnknown": 0, - "Range": 1, - "Capacity": 2, - "MaximumIntegrity": 3, - "MaximumCharge": 4, - } -) - -func (x RoverUpgrade) Enum() *RoverUpgrade { - p := new(RoverUpgrade) - *p = x - return p -} - -func (x RoverUpgrade) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (RoverUpgrade) Descriptor() protoreflect.EnumDescriptor { - return file_roveapi_roveapi_proto_enumTypes[2].Descriptor() -} - -func (RoverUpgrade) Type() protoreflect.EnumType { - return &file_roveapi_roveapi_proto_enumTypes[2] -} - -func (x RoverUpgrade) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use RoverUpgrade.Descriptor instead. -func (RoverUpgrade) EnumDescriptor() ([]byte, []int) { - return file_roveapi_roveapi_proto_rawDescGZIP(), []int{2} -} - // Types of objects type Object int32 @@ -247,15 +162,10 @@ const ( Object_ObjectUnknown Object = 0 // RoverLive represents a live rover Object_RoverLive Object = 1 - // RoverDormant describes a dormant rover - Object_RoverDormant Object = 2 // RockSmall is a small stashable rock - Object_RockSmall Object = 3 + Object_RockSmall Object = 2 // RockLarge is a large blocking rock - Object_RockLarge Object = 4 - // RoverParts is one unit of rover parts, used for repairing and fixing the - // rover - Object_RoverParts Object = 5 + Object_RockLarge Object = 3 ) // Enum value maps for Object. @@ -263,18 +173,14 @@ var ( Object_name = map[int32]string{ 0: "ObjectUnknown", 1: "RoverLive", - 2: "RoverDormant", - 3: "RockSmall", - 4: "RockLarge", - 5: "RoverParts", + 2: "RockSmall", + 3: "RockLarge", } Object_value = map[string]int32{ "ObjectUnknown": 0, "RoverLive": 1, - "RoverDormant": 2, - "RockSmall": 3, - "RockLarge": 4, - "RoverParts": 5, + "RockSmall": 2, + "RockLarge": 3, } ) @@ -289,11 +195,11 @@ func (x Object) String() string { } func (Object) Descriptor() protoreflect.EnumDescriptor { - return file_roveapi_roveapi_proto_enumTypes[3].Descriptor() + return file_roveapi_roveapi_proto_enumTypes[2].Descriptor() } func (Object) Type() protoreflect.EnumType { - return &file_roveapi_roveapi_proto_enumTypes[3] + return &file_roveapi_roveapi_proto_enumTypes[2] } func (x Object) Number() protoreflect.EnumNumber { @@ -302,7 +208,7 @@ func (x Object) Number() protoreflect.EnumNumber { // Deprecated: Use Object.Descriptor instead. func (Object) EnumDescriptor() ([]byte, []int) { - return file_roveapi_roveapi_proto_rawDescGZIP(), []int{3} + return file_roveapi_roveapi_proto_rawDescGZIP(), []int{2} } type Tile int32 @@ -345,11 +251,11 @@ func (x Tile) String() string { } func (Tile) Descriptor() protoreflect.EnumDescriptor { - return file_roveapi_roveapi_proto_enumTypes[4].Descriptor() + return file_roveapi_roveapi_proto_enumTypes[3].Descriptor() } func (Tile) Type() protoreflect.EnumType { - return &file_roveapi_roveapi_proto_enumTypes[4] + return &file_roveapi_roveapi_proto_enumTypes[3] } func (x Tile) Number() protoreflect.EnumNumber { @@ -358,59 +264,7 @@ func (x Tile) Number() protoreflect.EnumNumber { // Deprecated: Use Tile.Descriptor instead. func (Tile) EnumDescriptor() ([]byte, []int) { - return file_roveapi_roveapi_proto_rawDescGZIP(), []int{4} -} - -// SailPosition represents the position of the sola sail -type SailPosition int32 - -const ( - SailPosition_UnknownSailPosition SailPosition = 0 - // CatchingWind means the sail is catching the wind and moving the rover - SailPosition_CatchingWind SailPosition = 1 - // SolarCharging means the sail is facing the sun and charging - SailPosition_SolarCharging SailPosition = 2 -) - -// Enum value maps for SailPosition. -var ( - SailPosition_name = map[int32]string{ - 0: "UnknownSailPosition", - 1: "CatchingWind", - 2: "SolarCharging", - } - SailPosition_value = map[string]int32{ - "UnknownSailPosition": 0, - "CatchingWind": 1, - "SolarCharging": 2, - } -) - -func (x SailPosition) Enum() *SailPosition { - p := new(SailPosition) - *p = x - return p -} - -func (x SailPosition) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (SailPosition) Descriptor() protoreflect.EnumDescriptor { - return file_roveapi_roveapi_proto_enumTypes[5].Descriptor() -} - -func (SailPosition) Type() protoreflect.EnumType { - return &file_roveapi_roveapi_proto_enumTypes[5] -} - -func (x SailPosition) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use SailPosition.Descriptor instead. -func (SailPosition) EnumDescriptor() ([]byte, []int) { - return file_roveapi_roveapi_proto_rawDescGZIP(), []int{5} + return file_roveapi_roveapi_proto_rawDescGZIP(), []int{3} } // ServerStatusRequest is an empty placeholder @@ -701,15 +555,10 @@ type Command struct { // The command type Command CommandType `protobuf:"varint,1,opt,name=command,proto3,enum=roveapi.CommandType" json:"command,omitempty"` - // The number of times to repeat the command after the first - Repeat int32 `protobuf:"varint,2,opt,name=repeat,proto3" json:"repeat,omitempty"` - // broadcast - a simple message, must be composed of up to 3 printable ASCII - // glyphs (32-126) - Data []byte `protobuf:"bytes,3,opt,name=data,proto3" json:"data,omitempty"` - // move - the bearing for the rover to turn to - Bearing Bearing `protobuf:"varint,4,opt,name=bearing,proto3,enum=roveapi.Bearing" json:"bearing,omitempty"` - // upgrade - the upgrade to apply to the rover - Upgrade RoverUpgrade `protobuf:"varint,5,opt,name=upgrade,proto3,enum=roveapi.RoverUpgrade" json:"upgrade,omitempty"` + // Types that are assignable to Data: + // *Command_Bearing + // *Command_Message + Data isCommand_Data `protobuf_oneof:"data"` } func (x *Command) Reset() { @@ -751,34 +600,48 @@ func (x *Command) GetCommand() CommandType { return CommandType_none } -func (x *Command) GetRepeat() int32 { - if x != nil { - return x.Repeat - } - return 0 -} - -func (x *Command) GetData() []byte { - if x != nil { - return x.Data +func (m *Command) GetData() isCommand_Data { + if m != nil { + return m.Data } return nil } func (x *Command) GetBearing() Bearing { - if x != nil { + if x, ok := x.GetData().(*Command_Bearing); ok { return x.Bearing } return Bearing_BearingUnknown } -func (x *Command) GetUpgrade() RoverUpgrade { - if x != nil { - return x.Upgrade +func (x *Command) GetMessage() []byte { + if x, ok := x.GetData().(*Command_Message); ok { + return x.Message } - return RoverUpgrade_RoverUpgradeUnknown + return nil } +type isCommand_Data interface { + isCommand_Data() +} + +type Command_Bearing struct { + // A bearing + // Used with MOVE + Bearing Bearing `protobuf:"varint,2,opt,name=bearing,proto3,enum=roveapi.Bearing,oneof"` +} + +type Command_Message struct { + // A simple message, must be composed of printable ASCII glyphs (32-126) + // maximum of three characters + // Used with BROADCAST + Message []byte `protobuf:"bytes,3,opt,name=message,proto3,oneof"` +} + +func (*Command_Bearing) isCommand_Data() {} + +func (*Command_Message) isCommand_Data() {} + // CommandRequest describes a set of commands to be requested for the rover type CommandRequest struct { state protoimpl.MessageState @@ -1156,267 +1019,42 @@ func (x *Vector) GetY() int32 { return 0 } -type RoverSpecifications struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // The name of the rover - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - // The range of this rover's radar and broadcasting - Range int32 `protobuf:"varint,2,opt,name=range,proto3" json:"range,omitempty"` - // The capacity of the inventory - Capacity int32 `protobuf:"varint,3,opt,name=capacity,proto3" json:"capacity,omitempty"` - // The maximum health of the rover - MaximumIntegrity int32 `protobuf:"varint,4,opt,name=maximumIntegrity,proto3" json:"maximumIntegrity,omitempty"` - // The max energy the rover can store - MaximumCharge int32 `protobuf:"varint,5,opt,name=maximumCharge,proto3" json:"maximumCharge,omitempty"` -} - -func (x *RoverSpecifications) Reset() { - *x = RoverSpecifications{} - if protoimpl.UnsafeEnabled { - mi := &file_roveapi_roveapi_proto_msgTypes[13] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *RoverSpecifications) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*RoverSpecifications) ProtoMessage() {} - -func (x *RoverSpecifications) ProtoReflect() protoreflect.Message { - mi := &file_roveapi_roveapi_proto_msgTypes[13] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use RoverSpecifications.ProtoReflect.Descriptor instead. -func (*RoverSpecifications) Descriptor() ([]byte, []int) { - return file_roveapi_roveapi_proto_rawDescGZIP(), []int{13} -} - -func (x *RoverSpecifications) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *RoverSpecifications) GetRange() int32 { - if x != nil { - return x.Range - } - return 0 -} - -func (x *RoverSpecifications) GetCapacity() int32 { - if x != nil { - return x.Capacity - } - return 0 -} - -func (x *RoverSpecifications) GetMaximumIntegrity() int32 { - if x != nil { - return x.MaximumIntegrity - } - return 0 -} - -func (x *RoverSpecifications) GetMaximumCharge() int32 { - if x != nil { - return x.MaximumCharge - } - return 0 -} - -type RoverStatus struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // The current direction of the rover - Bearing Bearing `protobuf:"varint,1,opt,name=bearing,proto3,enum=roveapi.Bearing" json:"bearing,omitempty"` - // The current position of the sails - SailPosition SailPosition `protobuf:"varint,2,opt,name=sailPosition,proto3,enum=roveapi.SailPosition" json:"sailPosition,omitempty"` - // The items in the rover inventory - Inventory []byte `protobuf:"bytes,3,opt,name=inventory,proto3" json:"inventory,omitempty"` - // The current health of the rover - Integrity int32 `protobuf:"varint,4,opt,name=integrity,proto3" json:"integrity,omitempty"` - // The energy stored in the rover - Charge int32 `protobuf:"varint,5,opt,name=charge,proto3" json:"charge,omitempty"` - // The set of currently queued commands - QueuedCommands []*Command `protobuf:"bytes,6,rep,name=queuedCommands,proto3" json:"queuedCommands,omitempty"` -} - -func (x *RoverStatus) Reset() { - *x = RoverStatus{} - if protoimpl.UnsafeEnabled { - mi := &file_roveapi_roveapi_proto_msgTypes[14] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *RoverStatus) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*RoverStatus) ProtoMessage() {} - -func (x *RoverStatus) ProtoReflect() protoreflect.Message { - mi := &file_roveapi_roveapi_proto_msgTypes[14] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use RoverStatus.ProtoReflect.Descriptor instead. -func (*RoverStatus) Descriptor() ([]byte, []int) { - return file_roveapi_roveapi_proto_rawDescGZIP(), []int{14} -} - -func (x *RoverStatus) GetBearing() Bearing { - if x != nil { - return x.Bearing - } - return Bearing_BearingUnknown -} - -func (x *RoverStatus) GetSailPosition() SailPosition { - if x != nil { - return x.SailPosition - } - return SailPosition_UnknownSailPosition -} - -func (x *RoverStatus) GetInventory() []byte { - if x != nil { - return x.Inventory - } - return nil -} - -func (x *RoverStatus) GetIntegrity() int32 { - if x != nil { - return x.Integrity - } - return 0 -} - -func (x *RoverStatus) GetCharge() int32 { - if x != nil { - return x.Charge - } - return 0 -} - -func (x *RoverStatus) GetQueuedCommands() []*Command { - if x != nil { - return x.QueuedCommands - } - return nil -} - -type RoverReadings struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // Position of the rover in world coordinates - Position *Vector `protobuf:"bytes,1,opt,name=position,proto3" json:"position,omitempty"` - // The current wind direction - Wind Bearing `protobuf:"varint,2,opt,name=wind,proto3,enum=roveapi.Bearing" json:"wind,omitempty"` - // The most recent logs - Logs []*Log `protobuf:"bytes,3,rep,name=logs,proto3" json:"logs,omitempty"` -} - -func (x *RoverReadings) Reset() { - *x = RoverReadings{} - if protoimpl.UnsafeEnabled { - mi := &file_roveapi_roveapi_proto_msgTypes[15] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *RoverReadings) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*RoverReadings) ProtoMessage() {} - -func (x *RoverReadings) ProtoReflect() protoreflect.Message { - mi := &file_roveapi_roveapi_proto_msgTypes[15] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use RoverReadings.ProtoReflect.Descriptor instead. -func (*RoverReadings) Descriptor() ([]byte, []int) { - return file_roveapi_roveapi_proto_rawDescGZIP(), []int{15} -} - -func (x *RoverReadings) GetPosition() *Vector { - if x != nil { - return x.Position - } - return nil -} - -func (x *RoverReadings) GetWind() Bearing { - if x != nil { - return x.Wind - } - return Bearing_BearingUnknown -} - -func (x *RoverReadings) GetLogs() []*Log { - if x != nil { - return x.Logs - } - return nil -} - // StatusResponse is the response given to a status request type StatusResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // The static rover information - Spec *RoverSpecifications `protobuf:"bytes,1,opt,name=spec,proto3" json:"spec,omitempty"` - // Current rover status - Status *RoverStatus `protobuf:"bytes,2,opt,name=status,proto3" json:"status,omitempty"` - // Current rover readings - Readings *RoverReadings `protobuf:"bytes,3,opt,name=readings,proto3" json:"readings,omitempty"` + // The name of the rover + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + // Position of the rover in world coordinates + Position *Vector `protobuf:"bytes,2,opt,name=position,proto3" json:"position,omitempty"` + // The range of this rover's radar and broadcasting + Range int32 `protobuf:"varint,3,opt,name=range,proto3" json:"range,omitempty"` + // The items in the rover inventory + Inventory []byte `protobuf:"bytes,4,opt,name=inventory,proto3" json:"inventory,omitempty"` + // The capacity of the inventory + Capacity int32 `protobuf:"varint,5,opt,name=capacity,proto3" json:"capacity,omitempty"` + // The current health of the rover + Integrity int32 `protobuf:"varint,6,opt,name=integrity,proto3" json:"integrity,omitempty"` + // The maximum health of the rover + MaximumIntegrity int32 `protobuf:"varint,7,opt,name=maximumIntegrity,proto3" json:"maximumIntegrity,omitempty"` + // The energy stored in the rover + Charge int32 `protobuf:"varint,8,opt,name=charge,proto3" json:"charge,omitempty"` + // The max energy the rover can store + MaximumCharge int32 `protobuf:"varint,9,opt,name=maximumCharge,proto3" json:"maximumCharge,omitempty"` + // The set of currently incoming commands for this tick + IncomingCommands []*Command `protobuf:"bytes,10,rep,name=incomingCommands,proto3" json:"incomingCommands,omitempty"` + // The set of currently queued commands + QueuedCommands []*Command `protobuf:"bytes,11,rep,name=queuedCommands,proto3" json:"queuedCommands,omitempty"` + // The most recent logs + Logs []*Log `protobuf:"bytes,12,rep,name=logs,proto3" json:"logs,omitempty"` } func (x *StatusResponse) Reset() { *x = StatusResponse{} if protoimpl.UnsafeEnabled { - mi := &file_roveapi_roveapi_proto_msgTypes[16] + mi := &file_roveapi_roveapi_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1429,7 +1067,7 @@ func (x *StatusResponse) String() string { func (*StatusResponse) ProtoMessage() {} func (x *StatusResponse) ProtoReflect() protoreflect.Message { - mi := &file_roveapi_roveapi_proto_msgTypes[16] + mi := &file_roveapi_roveapi_proto_msgTypes[13] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1442,26 +1080,89 @@ func (x *StatusResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use StatusResponse.ProtoReflect.Descriptor instead. func (*StatusResponse) Descriptor() ([]byte, []int) { - return file_roveapi_roveapi_proto_rawDescGZIP(), []int{16} + return file_roveapi_roveapi_proto_rawDescGZIP(), []int{13} } -func (x *StatusResponse) GetSpec() *RoverSpecifications { +func (x *StatusResponse) GetName() string { if x != nil { - return x.Spec + return x.Name + } + return "" +} + +func (x *StatusResponse) GetPosition() *Vector { + if x != nil { + return x.Position } return nil } -func (x *StatusResponse) GetStatus() *RoverStatus { +func (x *StatusResponse) GetRange() int32 { if x != nil { - return x.Status + return x.Range + } + return 0 +} + +func (x *StatusResponse) GetInventory() []byte { + if x != nil { + return x.Inventory } return nil } -func (x *StatusResponse) GetReadings() *RoverReadings { +func (x *StatusResponse) GetCapacity() int32 { if x != nil { - return x.Readings + return x.Capacity + } + return 0 +} + +func (x *StatusResponse) GetIntegrity() int32 { + if x != nil { + return x.Integrity + } + return 0 +} + +func (x *StatusResponse) GetMaximumIntegrity() int32 { + if x != nil { + return x.MaximumIntegrity + } + return 0 +} + +func (x *StatusResponse) GetCharge() int32 { + if x != nil { + return x.Charge + } + return 0 +} + +func (x *StatusResponse) GetMaximumCharge() int32 { + if x != nil { + return x.MaximumCharge + } + return 0 +} + +func (x *StatusResponse) GetIncomingCommands() []*Command { + if x != nil { + return x.IncomingCommands + } + return nil +} + +func (x *StatusResponse) GetQueuedCommands() []*Command { + if x != nil { + return x.QueuedCommands + } + return nil +} + +func (x *StatusResponse) GetLogs() []*Log { + if x != nil { + return x.Logs } return nil } @@ -1492,157 +1193,115 @@ var file_roveapi_roveapi_proto_rawDesc = []byte{ 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x07, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, - 0x52, 0x07, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0xc2, 0x01, 0x0a, 0x07, 0x43, 0x6f, + 0x52, 0x07, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x8b, 0x01, 0x0a, 0x07, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x2e, 0x0a, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x14, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x54, 0x79, 0x70, 0x65, 0x52, 0x07, 0x63, 0x6f, - 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x12, 0x12, 0x0a, - 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, - 0x61, 0x12, 0x2a, 0x0a, 0x07, 0x62, 0x65, 0x61, 0x72, 0x69, 0x6e, 0x67, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x0e, 0x32, 0x10, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x42, 0x65, 0x61, - 0x72, 0x69, 0x6e, 0x67, 0x52, 0x07, 0x62, 0x65, 0x61, 0x72, 0x69, 0x6e, 0x67, 0x12, 0x2f, 0x0a, - 0x07, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, - 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x52, 0x6f, 0x76, 0x65, 0x72, 0x55, 0x70, - 0x67, 0x72, 0x61, 0x64, 0x65, 0x52, 0x07, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x22, 0x6a, - 0x0a, 0x0e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x2a, 0x0a, 0x07, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x10, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x41, 0x63, 0x63, 0x6f, - 0x75, 0x6e, 0x74, 0x52, 0x07, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2c, 0x0a, 0x08, - 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, - 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, - 0x52, 0x08, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x22, 0x11, 0x0a, 0x0f, 0x43, 0x6f, - 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3a, 0x0a, - 0x0c, 0x52, 0x61, 0x64, 0x61, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2a, 0x0a, - 0x07, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, - 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, - 0x52, 0x07, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x75, 0x0a, 0x0d, 0x52, 0x61, 0x64, - 0x61, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x61, - 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x72, 0x61, 0x6e, 0x67, 0x65, - 0x12, 0x23, 0x0a, 0x05, 0x74, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0e, 0x32, - 0x0d, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x54, 0x69, 0x6c, 0x65, 0x52, 0x05, - 0x74, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x29, 0x0a, 0x07, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, - 0x18, 0x03, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x0f, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, - 0x2e, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x07, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, - 0x22, 0x3b, 0x0a, 0x0d, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x2a, 0x0a, 0x07, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x41, 0x63, 0x63, - 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x07, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x2d, 0x0a, - 0x03, 0x4c, 0x6f, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, 0x78, 0x74, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x65, 0x78, 0x74, 0x22, 0x24, 0x0a, 0x06, - 0x56, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x0c, 0x0a, 0x01, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x05, 0x52, 0x01, 0x78, 0x12, 0x0c, 0x0a, 0x01, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, - 0x01, 0x79, 0x22, 0xad, 0x01, 0x0a, 0x13, 0x52, 0x6f, 0x76, 0x65, 0x72, 0x53, 0x70, 0x65, 0x63, - 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, - 0x0a, 0x05, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x72, - 0x61, 0x6e, 0x67, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, - 0x12, 0x2a, 0x0a, 0x10, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x49, 0x6e, 0x74, 0x65, 0x67, - 0x72, 0x69, 0x74, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x10, 0x6d, 0x61, 0x78, 0x69, - 0x6d, 0x75, 0x6d, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x69, 0x74, 0x79, 0x12, 0x24, 0x0a, 0x0d, - 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x43, 0x68, 0x61, 0x72, 0x67, 0x65, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x05, 0x52, 0x0d, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x43, 0x68, 0x61, 0x72, - 0x67, 0x65, 0x22, 0x82, 0x02, 0x0a, 0x0b, 0x52, 0x6f, 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x12, 0x2a, 0x0a, 0x07, 0x62, 0x65, 0x61, 0x72, 0x69, 0x6e, 0x67, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0e, 0x32, 0x10, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x42, 0x65, - 0x61, 0x72, 0x69, 0x6e, 0x67, 0x52, 0x07, 0x62, 0x65, 0x61, 0x72, 0x69, 0x6e, 0x67, 0x12, 0x39, - 0x0a, 0x0c, 0x73, 0x61, 0x69, 0x6c, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x53, - 0x61, 0x69, 0x6c, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0c, 0x73, 0x61, 0x69, - 0x6c, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x69, 0x6e, 0x76, - 0x65, 0x6e, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x69, 0x6e, - 0x76, 0x65, 0x6e, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x1c, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x65, 0x67, - 0x72, 0x69, 0x74, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x69, 0x6e, 0x74, 0x65, - 0x67, 0x72, 0x69, 0x74, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x63, 0x68, 0x61, 0x72, 0x67, 0x65, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x63, 0x68, 0x61, 0x72, 0x67, 0x65, 0x12, 0x38, 0x0a, - 0x0e, 0x71, 0x75, 0x65, 0x75, 0x65, 0x64, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x18, - 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, - 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x52, 0x0e, 0x71, 0x75, 0x65, 0x75, 0x65, 0x64, 0x43, - 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x22, 0x84, 0x01, 0x0a, 0x0d, 0x52, 0x6f, 0x76, 0x65, - 0x72, 0x52, 0x65, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x2b, 0x0a, 0x08, 0x70, 0x6f, 0x73, - 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x72, 0x6f, - 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x56, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x08, 0x70, 0x6f, - 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x24, 0x0a, 0x04, 0x77, 0x69, 0x6e, 0x64, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0e, 0x32, 0x10, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x42, - 0x65, 0x61, 0x72, 0x69, 0x6e, 0x67, 0x52, 0x04, 0x77, 0x69, 0x6e, 0x64, 0x12, 0x20, 0x0a, 0x04, - 0x6c, 0x6f, 0x67, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x72, 0x6f, 0x76, - 0x65, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x6f, 0x67, 0x52, 0x04, 0x6c, 0x6f, 0x67, 0x73, 0x22, 0xa4, - 0x01, 0x0a, 0x0e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x30, 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1c, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x52, 0x6f, 0x76, 0x65, 0x72, 0x53, - 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x04, 0x73, - 0x70, 0x65, 0x63, 0x12, 0x2c, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x52, 0x6f, - 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x12, 0x32, 0x0a, 0x08, 0x72, 0x65, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x52, 0x6f, - 0x76, 0x65, 0x72, 0x52, 0x65, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x08, 0x72, 0x65, 0x61, - 0x64, 0x69, 0x6e, 0x67, 0x73, 0x2a, 0x85, 0x01, 0x0a, 0x0b, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, - 0x64, 0x54, 0x79, 0x70, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x6e, 0x6f, 0x6e, 0x65, 0x10, 0x00, 0x12, - 0x08, 0x0a, 0x04, 0x77, 0x61, 0x69, 0x74, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x74, 0x6f, 0x67, - 0x67, 0x6c, 0x65, 0x10, 0x02, 0x12, 0x08, 0x0a, 0x04, 0x74, 0x75, 0x72, 0x6e, 0x10, 0x03, 0x12, - 0x09, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x73, 0x68, 0x10, 0x04, 0x12, 0x0a, 0x0a, 0x06, 0x72, 0x65, - 0x70, 0x61, 0x69, 0x72, 0x10, 0x05, 0x12, 0x0d, 0x0a, 0x09, 0x62, 0x72, 0x6f, 0x61, 0x64, 0x63, - 0x61, 0x73, 0x74, 0x10, 0x06, 0x12, 0x0b, 0x0a, 0x07, 0x73, 0x61, 0x6c, 0x76, 0x61, 0x67, 0x65, - 0x10, 0x07, 0x12, 0x0c, 0x0a, 0x08, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x10, 0x08, - 0x12, 0x0b, 0x0a, 0x07, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x10, 0x09, 0x2a, 0x83, 0x01, - 0x0a, 0x07, 0x42, 0x65, 0x61, 0x72, 0x69, 0x6e, 0x67, 0x12, 0x12, 0x0a, 0x0e, 0x42, 0x65, 0x61, - 0x72, 0x69, 0x6e, 0x67, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x10, 0x00, 0x12, 0x09, 0x0a, - 0x05, 0x4e, 0x6f, 0x72, 0x74, 0x68, 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x6f, 0x72, 0x74, - 0x68, 0x45, 0x61, 0x73, 0x74, 0x10, 0x02, 0x12, 0x08, 0x0a, 0x04, 0x45, 0x61, 0x73, 0x74, 0x10, - 0x03, 0x12, 0x0d, 0x0a, 0x09, 0x53, 0x6f, 0x75, 0x74, 0x68, 0x45, 0x61, 0x73, 0x74, 0x10, 0x04, - 0x12, 0x09, 0x0a, 0x05, 0x53, 0x6f, 0x75, 0x74, 0x68, 0x10, 0x05, 0x12, 0x0d, 0x0a, 0x09, 0x53, - 0x6f, 0x75, 0x74, 0x68, 0x57, 0x65, 0x73, 0x74, 0x10, 0x06, 0x12, 0x08, 0x0a, 0x04, 0x57, 0x65, - 0x73, 0x74, 0x10, 0x07, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x6f, 0x72, 0x74, 0x68, 0x57, 0x65, 0x73, - 0x74, 0x10, 0x08, 0x2a, 0x69, 0x0a, 0x0c, 0x52, 0x6f, 0x76, 0x65, 0x72, 0x55, 0x70, 0x67, 0x72, - 0x61, 0x64, 0x65, 0x12, 0x17, 0x0a, 0x13, 0x52, 0x6f, 0x76, 0x65, 0x72, 0x55, 0x70, 0x67, 0x72, - 0x61, 0x64, 0x65, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, - 0x52, 0x61, 0x6e, 0x67, 0x65, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x43, 0x61, 0x70, 0x61, 0x63, - 0x69, 0x74, 0x79, 0x10, 0x02, 0x12, 0x14, 0x0a, 0x10, 0x4d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, - 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x69, 0x74, 0x79, 0x10, 0x03, 0x12, 0x11, 0x0a, 0x0d, 0x4d, - 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x43, 0x68, 0x61, 0x72, 0x67, 0x65, 0x10, 0x04, 0x2a, 0x6a, - 0x0a, 0x06, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x11, 0x0a, 0x0d, 0x4f, 0x62, 0x6a, 0x65, - 0x63, 0x74, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x52, - 0x6f, 0x76, 0x65, 0x72, 0x4c, 0x69, 0x76, 0x65, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x52, 0x6f, - 0x76, 0x65, 0x72, 0x44, 0x6f, 0x72, 0x6d, 0x61, 0x6e, 0x74, 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09, - 0x52, 0x6f, 0x63, 0x6b, 0x53, 0x6d, 0x61, 0x6c, 0x6c, 0x10, 0x03, 0x12, 0x0d, 0x0a, 0x09, 0x52, - 0x6f, 0x63, 0x6b, 0x4c, 0x61, 0x72, 0x67, 0x65, 0x10, 0x04, 0x12, 0x0e, 0x0a, 0x0a, 0x52, 0x6f, - 0x76, 0x65, 0x72, 0x50, 0x61, 0x72, 0x74, 0x73, 0x10, 0x05, 0x2a, 0x37, 0x0a, 0x04, 0x54, 0x69, - 0x6c, 0x65, 0x12, 0x0f, 0x0a, 0x0b, 0x54, 0x69, 0x6c, 0x65, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, - 0x6e, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x52, 0x6f, 0x63, 0x6b, 0x10, 0x01, 0x12, 0x0a, 0x0a, - 0x06, 0x47, 0x72, 0x61, 0x76, 0x65, 0x6c, 0x10, 0x02, 0x12, 0x08, 0x0a, 0x04, 0x53, 0x61, 0x6e, - 0x64, 0x10, 0x03, 0x2a, 0x4c, 0x0a, 0x0c, 0x53, 0x61, 0x69, 0x6c, 0x50, 0x6f, 0x73, 0x69, 0x74, - 0x69, 0x6f, 0x6e, 0x12, 0x17, 0x0a, 0x13, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x53, 0x61, - 0x69, 0x6c, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x10, 0x00, 0x12, 0x10, 0x0a, 0x0c, - 0x43, 0x61, 0x74, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x57, 0x69, 0x6e, 0x64, 0x10, 0x01, 0x12, 0x11, - 0x0a, 0x0d, 0x53, 0x6f, 0x6c, 0x61, 0x72, 0x43, 0x68, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x67, 0x10, - 0x02, 0x32, 0xcf, 0x02, 0x0a, 0x04, 0x52, 0x6f, 0x76, 0x65, 0x12, 0x4d, 0x0a, 0x0c, 0x53, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1c, 0x2e, 0x72, 0x6f, 0x76, - 0x65, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, - 0x70, 0x69, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x41, 0x0a, 0x08, 0x52, 0x65, 0x67, - 0x69, 0x73, 0x74, 0x65, 0x72, 0x12, 0x18, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, - 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x19, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, - 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3e, 0x0a, 0x07, - 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x17, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, - 0x69, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x18, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, - 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x38, 0x0a, 0x05, - 0x52, 0x61, 0x64, 0x61, 0x72, 0x12, 0x15, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, - 0x52, 0x61, 0x64, 0x61, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x72, - 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x52, 0x61, 0x64, 0x61, 0x72, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3b, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x12, 0x16, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, - 0x70, 0x69, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x00, 0x42, 0x26, 0x5a, 0x24, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, - 0x6d, 0x2f, 0x6d, 0x64, 0x69, 0x6c, 0x75, 0x7a, 0x2f, 0x72, 0x6f, 0x76, 0x65, 0x2f, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x2f, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x62, 0x06, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x33, + 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x2c, 0x0a, 0x07, 0x62, 0x65, 0x61, 0x72, 0x69, 0x6e, 0x67, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x10, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, + 0x2e, 0x42, 0x65, 0x61, 0x72, 0x69, 0x6e, 0x67, 0x48, 0x00, 0x52, 0x07, 0x62, 0x65, 0x61, 0x72, + 0x69, 0x6e, 0x67, 0x12, 0x1a, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x42, + 0x06, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x6a, 0x0a, 0x0e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, + 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2a, 0x0a, 0x07, 0x61, 0x63, 0x63, + 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x72, 0x6f, 0x76, + 0x65, 0x61, 0x70, 0x69, 0x2e, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x07, 0x61, 0x63, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2c, 0x0a, 0x08, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, + 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, + 0x69, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x52, 0x08, 0x63, 0x6f, 0x6d, 0x6d, 0x61, + 0x6e, 0x64, 0x73, 0x22, 0x11, 0x0a, 0x0f, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3a, 0x0a, 0x0c, 0x52, 0x61, 0x64, 0x61, 0x72, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2a, 0x0a, 0x07, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, + 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, + 0x69, 0x2e, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x07, 0x61, 0x63, 0x63, 0x6f, 0x75, + 0x6e, 0x74, 0x22, 0x75, 0x0a, 0x0d, 0x52, 0x61, 0x64, 0x61, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x05, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x23, 0x0a, 0x05, 0x74, 0x69, 0x6c, + 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x0d, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, + 0x70, 0x69, 0x2e, 0x54, 0x69, 0x6c, 0x65, 0x52, 0x05, 0x74, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x29, + 0x0a, 0x07, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0e, 0x32, + 0x0f, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, + 0x52, 0x07, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x22, 0x3b, 0x0a, 0x0d, 0x53, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2a, 0x0a, 0x07, 0x61, 0x63, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x72, 0x6f, + 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x07, 0x61, + 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x2d, 0x0a, 0x03, 0x4c, 0x6f, 0x67, 0x12, 0x12, 0x0a, + 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x69, 0x6d, + 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, 0x78, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x74, 0x65, 0x78, 0x74, 0x22, 0x24, 0x0a, 0x06, 0x56, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, + 0x0c, 0x0a, 0x01, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x01, 0x78, 0x12, 0x0c, 0x0a, + 0x01, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x01, 0x79, 0x22, 0xc3, 0x03, 0x0a, 0x0e, + 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, + 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x12, 0x2b, 0x0a, 0x08, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x56, + 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x08, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x14, 0x0a, 0x05, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, + 0x72, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x69, 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x6f, + 0x72, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x69, 0x6e, 0x76, 0x65, 0x6e, 0x74, + 0x6f, 0x72, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x12, + 0x1c, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x69, 0x74, 0x79, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x09, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x69, 0x74, 0x79, 0x12, 0x2a, 0x0a, + 0x10, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x69, 0x74, + 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x52, 0x10, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, + 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x69, 0x74, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x63, 0x68, 0x61, + 0x72, 0x67, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x63, 0x68, 0x61, 0x72, 0x67, + 0x65, 0x12, 0x24, 0x0a, 0x0d, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x43, 0x68, 0x61, 0x72, + 0x67, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, + 0x6d, 0x43, 0x68, 0x61, 0x72, 0x67, 0x65, 0x12, 0x3c, 0x0a, 0x10, 0x69, 0x6e, 0x63, 0x6f, 0x6d, + 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x10, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, + 0x61, 0x6e, 0x64, 0x52, 0x10, 0x69, 0x6e, 0x63, 0x6f, 0x6d, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6d, + 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x12, 0x38, 0x0a, 0x0e, 0x71, 0x75, 0x65, 0x75, 0x65, 0x64, 0x43, + 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, + 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x52, + 0x0e, 0x71, 0x75, 0x65, 0x75, 0x65, 0x64, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x12, + 0x20, 0x0a, 0x04, 0x6c, 0x6f, 0x67, 0x73, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, + 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x6f, 0x67, 0x52, 0x04, 0x6c, 0x6f, 0x67, + 0x73, 0x2a, 0x55, 0x0a, 0x0b, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x54, 0x79, 0x70, 0x65, + 0x12, 0x08, 0x0a, 0x04, 0x6e, 0x6f, 0x6e, 0x65, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x6d, 0x6f, + 0x76, 0x65, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x73, 0x68, 0x10, 0x02, 0x12, + 0x0a, 0x0a, 0x06, 0x72, 0x65, 0x70, 0x61, 0x69, 0x72, 0x10, 0x03, 0x12, 0x0c, 0x0a, 0x08, 0x72, + 0x65, 0x63, 0x68, 0x61, 0x72, 0x67, 0x65, 0x10, 0x04, 0x12, 0x0d, 0x0a, 0x09, 0x62, 0x72, 0x6f, + 0x61, 0x64, 0x63, 0x61, 0x73, 0x74, 0x10, 0x05, 0x2a, 0x47, 0x0a, 0x07, 0x42, 0x65, 0x61, 0x72, + 0x69, 0x6e, 0x67, 0x12, 0x12, 0x0a, 0x0e, 0x42, 0x65, 0x61, 0x72, 0x69, 0x6e, 0x67, 0x55, 0x6e, + 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x4e, 0x6f, 0x72, 0x74, 0x68, + 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x45, 0x61, 0x73, 0x74, 0x10, 0x02, 0x12, 0x09, 0x0a, 0x05, + 0x53, 0x6f, 0x75, 0x74, 0x68, 0x10, 0x03, 0x12, 0x08, 0x0a, 0x04, 0x57, 0x65, 0x73, 0x74, 0x10, + 0x04, 0x2a, 0x48, 0x0a, 0x06, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x11, 0x0a, 0x0d, 0x4f, + 0x62, 0x6a, 0x65, 0x63, 0x74, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x10, 0x00, 0x12, 0x0d, + 0x0a, 0x09, 0x52, 0x6f, 0x76, 0x65, 0x72, 0x4c, 0x69, 0x76, 0x65, 0x10, 0x01, 0x12, 0x0d, 0x0a, + 0x09, 0x52, 0x6f, 0x63, 0x6b, 0x53, 0x6d, 0x61, 0x6c, 0x6c, 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09, + 0x52, 0x6f, 0x63, 0x6b, 0x4c, 0x61, 0x72, 0x67, 0x65, 0x10, 0x03, 0x2a, 0x37, 0x0a, 0x04, 0x54, + 0x69, 0x6c, 0x65, 0x12, 0x0f, 0x0a, 0x0b, 0x54, 0x69, 0x6c, 0x65, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, + 0x77, 0x6e, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x52, 0x6f, 0x63, 0x6b, 0x10, 0x01, 0x12, 0x0a, + 0x0a, 0x06, 0x47, 0x72, 0x61, 0x76, 0x65, 0x6c, 0x10, 0x02, 0x12, 0x08, 0x0a, 0x04, 0x53, 0x61, + 0x6e, 0x64, 0x10, 0x03, 0x32, 0xcf, 0x02, 0x0a, 0x04, 0x52, 0x6f, 0x76, 0x65, 0x12, 0x4d, 0x0a, + 0x0c, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1c, 0x2e, + 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x72, 0x6f, + 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x41, 0x0a, 0x08, + 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x12, 0x18, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, + 0x70, 0x69, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x52, 0x65, 0x67, + 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, + 0x3e, 0x0a, 0x07, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x17, 0x2e, 0x72, 0x6f, 0x76, + 0x65, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x6f, + 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, + 0x38, 0x0a, 0x05, 0x52, 0x61, 0x64, 0x61, 0x72, 0x12, 0x15, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, + 0x70, 0x69, 0x2e, 0x52, 0x61, 0x64, 0x61, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x16, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x52, 0x61, 0x64, 0x61, 0x72, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3b, 0x0a, 0x06, 0x53, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x12, 0x16, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x72, 0x6f, + 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x26, 0x5a, 0x24, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6d, 0x64, 0x69, 0x6c, 0x75, 0x7a, 0x2f, 0x72, 0x6f, 0x76, 0x65, + 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x62, 0x06, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1657,68 +1316,57 @@ func file_roveapi_roveapi_proto_rawDescGZIP() []byte { return file_roveapi_roveapi_proto_rawDescData } -var file_roveapi_roveapi_proto_enumTypes = make([]protoimpl.EnumInfo, 6) -var file_roveapi_roveapi_proto_msgTypes = make([]protoimpl.MessageInfo, 17) +var file_roveapi_roveapi_proto_enumTypes = make([]protoimpl.EnumInfo, 4) +var file_roveapi_roveapi_proto_msgTypes = make([]protoimpl.MessageInfo, 14) var file_roveapi_roveapi_proto_goTypes = []interface{}{ (CommandType)(0), // 0: roveapi.CommandType (Bearing)(0), // 1: roveapi.Bearing - (RoverUpgrade)(0), // 2: roveapi.RoverUpgrade - (Object)(0), // 3: roveapi.Object - (Tile)(0), // 4: roveapi.Tile - (SailPosition)(0), // 5: roveapi.SailPosition - (*ServerStatusRequest)(nil), // 6: roveapi.ServerStatusRequest - (*ServerStatusResponse)(nil), // 7: roveapi.ServerStatusResponse - (*RegisterRequest)(nil), // 8: roveapi.RegisterRequest - (*Account)(nil), // 9: roveapi.Account - (*RegisterResponse)(nil), // 10: roveapi.RegisterResponse - (*Command)(nil), // 11: roveapi.Command - (*CommandRequest)(nil), // 12: roveapi.CommandRequest - (*CommandResponse)(nil), // 13: roveapi.CommandResponse - (*RadarRequest)(nil), // 14: roveapi.RadarRequest - (*RadarResponse)(nil), // 15: roveapi.RadarResponse - (*StatusRequest)(nil), // 16: roveapi.StatusRequest - (*Log)(nil), // 17: roveapi.Log - (*Vector)(nil), // 18: roveapi.Vector - (*RoverSpecifications)(nil), // 19: roveapi.RoverSpecifications - (*RoverStatus)(nil), // 20: roveapi.RoverStatus - (*RoverReadings)(nil), // 21: roveapi.RoverReadings - (*StatusResponse)(nil), // 22: roveapi.StatusResponse + (Object)(0), // 2: roveapi.Object + (Tile)(0), // 3: roveapi.Tile + (*ServerStatusRequest)(nil), // 4: roveapi.ServerStatusRequest + (*ServerStatusResponse)(nil), // 5: roveapi.ServerStatusResponse + (*RegisterRequest)(nil), // 6: roveapi.RegisterRequest + (*Account)(nil), // 7: roveapi.Account + (*RegisterResponse)(nil), // 8: roveapi.RegisterResponse + (*Command)(nil), // 9: roveapi.Command + (*CommandRequest)(nil), // 10: roveapi.CommandRequest + (*CommandResponse)(nil), // 11: roveapi.CommandResponse + (*RadarRequest)(nil), // 12: roveapi.RadarRequest + (*RadarResponse)(nil), // 13: roveapi.RadarResponse + (*StatusRequest)(nil), // 14: roveapi.StatusRequest + (*Log)(nil), // 15: roveapi.Log + (*Vector)(nil), // 16: roveapi.Vector + (*StatusResponse)(nil), // 17: roveapi.StatusResponse } var file_roveapi_roveapi_proto_depIdxs = []int32{ - 9, // 0: roveapi.RegisterResponse.account:type_name -> roveapi.Account + 7, // 0: roveapi.RegisterResponse.account:type_name -> roveapi.Account 0, // 1: roveapi.Command.command:type_name -> roveapi.CommandType 1, // 2: roveapi.Command.bearing:type_name -> roveapi.Bearing - 2, // 3: roveapi.Command.upgrade:type_name -> roveapi.RoverUpgrade - 9, // 4: roveapi.CommandRequest.account:type_name -> roveapi.Account - 11, // 5: roveapi.CommandRequest.commands:type_name -> roveapi.Command - 9, // 6: roveapi.RadarRequest.account:type_name -> roveapi.Account - 4, // 7: roveapi.RadarResponse.tiles:type_name -> roveapi.Tile - 3, // 8: roveapi.RadarResponse.objects:type_name -> roveapi.Object - 9, // 9: roveapi.StatusRequest.account:type_name -> roveapi.Account - 1, // 10: roveapi.RoverStatus.bearing:type_name -> roveapi.Bearing - 5, // 11: roveapi.RoverStatus.sailPosition:type_name -> roveapi.SailPosition - 11, // 12: roveapi.RoverStatus.queuedCommands:type_name -> roveapi.Command - 18, // 13: roveapi.RoverReadings.position:type_name -> roveapi.Vector - 1, // 14: roveapi.RoverReadings.wind:type_name -> roveapi.Bearing - 17, // 15: roveapi.RoverReadings.logs:type_name -> roveapi.Log - 19, // 16: roveapi.StatusResponse.spec:type_name -> roveapi.RoverSpecifications - 20, // 17: roveapi.StatusResponse.status:type_name -> roveapi.RoverStatus - 21, // 18: roveapi.StatusResponse.readings:type_name -> roveapi.RoverReadings - 6, // 19: roveapi.Rove.ServerStatus:input_type -> roveapi.ServerStatusRequest - 8, // 20: roveapi.Rove.Register:input_type -> roveapi.RegisterRequest - 12, // 21: roveapi.Rove.Command:input_type -> roveapi.CommandRequest - 14, // 22: roveapi.Rove.Radar:input_type -> roveapi.RadarRequest - 16, // 23: roveapi.Rove.Status:input_type -> roveapi.StatusRequest - 7, // 24: roveapi.Rove.ServerStatus:output_type -> roveapi.ServerStatusResponse - 10, // 25: roveapi.Rove.Register:output_type -> roveapi.RegisterResponse - 13, // 26: roveapi.Rove.Command:output_type -> roveapi.CommandResponse - 15, // 27: roveapi.Rove.Radar:output_type -> roveapi.RadarResponse - 22, // 28: roveapi.Rove.Status:output_type -> roveapi.StatusResponse - 24, // [24:29] is the sub-list for method output_type - 19, // [19:24] is the sub-list for method input_type - 19, // [19:19] is the sub-list for extension type_name - 19, // [19:19] is the sub-list for extension extendee - 0, // [0:19] is the sub-list for field type_name + 7, // 3: roveapi.CommandRequest.account:type_name -> roveapi.Account + 9, // 4: roveapi.CommandRequest.commands:type_name -> roveapi.Command + 7, // 5: roveapi.RadarRequest.account:type_name -> roveapi.Account + 3, // 6: roveapi.RadarResponse.tiles:type_name -> roveapi.Tile + 2, // 7: roveapi.RadarResponse.objects:type_name -> roveapi.Object + 7, // 8: roveapi.StatusRequest.account:type_name -> roveapi.Account + 16, // 9: roveapi.StatusResponse.position:type_name -> roveapi.Vector + 9, // 10: roveapi.StatusResponse.incomingCommands:type_name -> roveapi.Command + 9, // 11: roveapi.StatusResponse.queuedCommands:type_name -> roveapi.Command + 15, // 12: roveapi.StatusResponse.logs:type_name -> roveapi.Log + 4, // 13: roveapi.Rove.ServerStatus:input_type -> roveapi.ServerStatusRequest + 6, // 14: roveapi.Rove.Register:input_type -> roveapi.RegisterRequest + 10, // 15: roveapi.Rove.Command:input_type -> roveapi.CommandRequest + 12, // 16: roveapi.Rove.Radar:input_type -> roveapi.RadarRequest + 14, // 17: roveapi.Rove.Status:input_type -> roveapi.StatusRequest + 5, // 18: roveapi.Rove.ServerStatus:output_type -> roveapi.ServerStatusResponse + 8, // 19: roveapi.Rove.Register:output_type -> roveapi.RegisterResponse + 11, // 20: roveapi.Rove.Command:output_type -> roveapi.CommandResponse + 13, // 21: roveapi.Rove.Radar:output_type -> roveapi.RadarResponse + 17, // 22: roveapi.Rove.Status:output_type -> roveapi.StatusResponse + 18, // [18:23] is the sub-list for method output_type + 13, // [13:18] is the sub-list for method input_type + 13, // [13:13] is the sub-list for extension type_name + 13, // [13:13] is the sub-list for extension extendee + 0, // [0:13] is the sub-list for field type_name } func init() { file_roveapi_roveapi_proto_init() } @@ -1884,42 +1532,6 @@ func file_roveapi_roveapi_proto_init() { } } file_roveapi_roveapi_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RoverSpecifications); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_roveapi_roveapi_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RoverStatus); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_roveapi_roveapi_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RoverReadings); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_roveapi_roveapi_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*StatusResponse); i { case 0: return &v.state @@ -1932,13 +1544,17 @@ func file_roveapi_roveapi_proto_init() { } } } + file_roveapi_roveapi_proto_msgTypes[5].OneofWrappers = []interface{}{ + (*Command_Bearing)(nil), + (*Command_Message)(nil), + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_roveapi_roveapi_proto_rawDesc, - NumEnums: 6, - NumMessages: 17, + NumEnums: 4, + NumMessages: 14, NumExtensions: 0, NumServices: 1, }, diff --git a/proto/roveapi/roveapi.proto b/proto/roveapi/roveapi.proto index 72a1fc2..b43087b 100644 --- a/proto/roveapi/roveapi.proto +++ b/proto/roveapi/roveapi.proto @@ -89,47 +89,25 @@ message RegisterResponse { // CommandType defines the type of a command to give to the rover enum CommandType { none = 0; - // Waits before performing the next command - wait = 1; - // Toggles the sails, either catching the wind, or charging from the sun - toggle = 2; - // Turns the rover in the specified bearing (requires bearing) - turn = 3; + // Move the rover in a direction, requires bearing + move = 1; // Stashes item at current location in rover inventory - stash = 4; + stash = 2; // Repairs the rover using an inventory object - repair = 5; - // Broadcasts a message to nearby rovers (requires data) - broadcast = 6; - // Salvages a neighboring dormant rover for parts - salvage = 7; - // Transfers remote control into dormant rover - transfer = 8; - // Upgrades a chosen rover specification using 5 rover parts - upgrade = 9; + repair = 3; + // Waits a tick to add more charge to the rover + recharge = 4; + // Broadcasts a message to nearby rovers + broadcast = 5; } -// Bearing represents a compass direction enum Bearing { // BearingUnknown an unknown invalid bearing BearingUnknown = 0; North = 1; - NorthEast = 2; - East = 3; - SouthEast = 4; - South = 5; - SouthWest = 6; - West = 7; - NorthWest = 8; -} - -// Describes the type of upgrade -enum RoverUpgrade { - RoverUpgradeUnknown = 0; - Range = 1; - Capacity = 2; - MaximumIntegrity = 3; - MaximumCharge = 4; + East = 2; + South = 3; + West = 4; } // Command is a single command for a rover @@ -137,18 +115,16 @@ message Command { // The command type CommandType command = 1; - // The number of times to repeat the command after the first - int32 repeat = 2; + oneof data { + // A bearing + // Used with MOVE + Bearing bearing = 2; - // broadcast - a simple message, must be composed of up to 3 printable ASCII - // glyphs (32-126) - bytes data = 3; - - // move - the bearing for the rover to turn to - Bearing bearing = 4; - - // upgrade - the upgrade to apply to the rover - RoverUpgrade upgrade = 5; + // A simple message, must be composed of printable ASCII glyphs (32-126) + // maximum of three characters + // Used with BROADCAST + bytes message = 3; + } } // CommandRequest describes a set of commands to be requested for the rover @@ -175,18 +151,11 @@ enum Object { // RoverLive represents a live rover RoverLive = 1; - // RoverDormant describes a dormant rover - RoverDormant = 2; - // RockSmall is a small stashable rock - RockSmall = 3; + RockSmall = 2; // RockLarge is a large blocking rock - RockLarge = 4; - - // RoverParts is one unit of rover parts, used for repairing and fixing the - // rover - RoverParts = 5; + RockLarge = 3; } enum Tile { @@ -247,76 +216,41 @@ message Vector { int32 y = 2; } -// SailPosition represents the position of the sola sail -enum SailPosition { - UnknownSailPosition = 0; - - // CatchingWind means the sail is catching the wind and moving the rover - CatchingWind = 1; - - // SolarCharging means the sail is facing the sun and charging - SolarCharging = 2; -} - -message RoverSpecifications { - +// StatusResponse is the response given to a status request +message StatusResponse { // The name of the rover string name = 1; + // Position of the rover in world coordinates + Vector position = 2; + // The range of this rover's radar and broadcasting - int32 range = 2; - - // The capacity of the inventory - int32 capacity = 3; - - // The maximum health of the rover - int32 maximumIntegrity = 4; - - // The max energy the rover can store - int32 maximumCharge = 5; -} - -message RoverStatus { - - // The current direction of the rover - Bearing bearing = 1; - - // The current position of the sails - SailPosition sailPosition = 2; + int32 range = 3; // The items in the rover inventory - bytes inventory = 3; + bytes inventory = 4; + + // The capacity of the inventory + int32 capacity = 5; // The current health of the rover - int32 integrity = 4; + int32 integrity = 6; + + // The maximum health of the rover + int32 maximumIntegrity = 7; // The energy stored in the rover - int32 charge = 5; + int32 charge = 8; + + // The max energy the rover can store + int32 maximumCharge = 9; + + // The set of currently incoming commands for this tick + repeated Command incomingCommands = 10; // The set of currently queued commands - repeated Command queuedCommands = 6; -} - -message RoverReadings { - // Position of the rover in world coordinates - Vector position = 1; - - // The current wind direction - Bearing wind = 2; + repeated Command queuedCommands = 11; // The most recent logs - repeated Log logs = 3; -} - -// StatusResponse is the response given to a status request -message StatusResponse { - - // The static rover information - RoverSpecifications spec = 1; - - // Current rover status - RoverStatus status = 2; - - // Current rover readings - RoverReadings readings = 3; + repeated Log logs = 12; } \ No newline at end of file