Compare commits

...

67 commits

Author SHA1 Message Date
f29b189a42 Update README.md
Some checks failed
Docker / build (push) Has been cancelled
Tests / Build and Test (push) Has been cancelled
2024-09-27 10:43:28 +01:00
Marc Di Luzio
fe8029a4b3
Merge pull request #43 from mdiluz/gameplay-improvements
Assorted gameplay improvements from play testing
2020-08-02 12:52:29 +01:00
35b25dde98 Add the upgrade command to the cmdline client 2020-08-02 12:49:15 +01:00
804f82dd20 Fix up commandline interface for repeat commands to go after the command 2020-08-02 12:43:44 +01:00
4e4af1a1be Add a test for the upgrade command 2020-08-02 12:15:49 +01:00
b114b68ff7 Add upgrade command code 2020-08-02 12:03:12 +01:00
1200b0a2a2 Add "upgrade" command to use the rover parts 2020-08-02 11:39:28 +01:00
6a44633d40 Add rover parts to the cmdline pretty printer 2020-08-02 11:28:14 +01:00
636f0ed773 Spawn rover parts a little more frequently 2020-08-01 11:26:10 +01:00
018c122861 Stop spawning dormant rovers in the world 2020-08-01 11:24:53 +01:00
e66b899e2a Increase the base rover range to 10 2020-08-01 11:15:34 +01:00
70f041ae5d Spawn rover parts in the world 2020-08-01 11:09:15 +01:00
d7bda3f607
Merge pull request #42 from mdiluz/tls
Add TLS to server-client communications
2020-07-26 23:57:35 +01:00
94767f06d3 Fix to disable TLS in tests 2020-07-26 23:53:29 +01:00
500e0f9557 Skip the tls verify on the client side for now 2020-07-26 23:46:42 +01:00
4f2a7edeb1 Skip local tests, removing duplicate test runs 2020-07-26 23:42:23 +01:00
cf1dff2814 Make sure the client verifies the TLS 2020-07-26 23:41:52 +01:00
71a0ef9920 Use the fullchain.pem not the cert.pem as explained by letsencrypt 2020-07-26 23:36:34 +01:00
9b03ffb7f1 Add skip verify on the client for now 2020-07-26 23:30:09 +01:00
4821a90143 Pass the cert name into the docker deployment 2020-07-26 23:29:58 +01:00
ac3844fe7a Mount letsencrypt in the docker to read the certs 2020-07-26 23:26:36 +01:00
70d92c2d5e Add TLS to gRPC 2020-07-26 23:10:39 +01:00
bb50fae00b
Merge pull request #41 from mdiluz/add-reflection
Add gRPC reflection to the server
2020-07-26 22:53:59 +01:00
a321e5d72f Add gRPC reflection to the server 2020-07-26 22:48:48 +01:00
49ffa18f23
Merge pull request #40 from mdiluz/add-wait-command
Add wait command
2020-07-26 18:19:30 +01:00
e542999b91 Move test deployment out to it's own file 2020-07-26 18:11:01 +01:00
74e1cd4564 Convert number to repeat to avoid confusion 2020-07-26 18:02:06 +01:00
c0d4a809c9 Update command line client to allow specifying command number 2020-07-26 17:31:09 +01:00
1514603517 Allow number to be used in all commands 2020-07-26 17:19:04 +01:00
9dcbbee1a2
Merge pull request #39 from mdiluz/fix-help-text
Big update to help text and add a simple description
2020-07-26 17:12:22 +01:00
bcf71f0bf9 Add a "wait" command with a number 2020-07-26 17:09:47 +01:00
cec61a9db7 Big update to help text and add a simple description 2020-07-26 16:57:43 +01:00
47e2e13c49
Merge pull request #38 from mdiluz/world-gen-improve
World gen improve
2020-07-25 23:48:59 +01:00
6891ec8439 Adjust the terrain scale to be much larger 2020-07-25 23:39:32 +01:00
113090fbcb Fix bug where we were still placing psuedo-random objects down 2020-07-25 23:39:13 +01:00
a235f6a5f5
Merge pull request #37 from mdiluz/destroy-rovers
Destroy rovers
2020-07-25 23:24:46 +01:00
cd97220a11 Perform rover destruction during the main server tick 2020-07-25 23:18:21 +01:00
f9b3ce3edb Destroy the rover when it has 0 integrity 2020-07-25 23:13:05 +01:00
abf67f7f37
Merge pull request #35 from mdiluz/fix-starting-wind
Add the starting wind as north and ensure it's only updated the next day
2020-07-24 23:27:07 +01:00
5d4fd801c1 Add the starting wind as north and ensure it's only updated the next day 2020-07-24 23:22:46 +01:00
fa05e7f253
Merge pull request #34 from mdiluz/implement-salvage
Add rover salvage mechanics
2020-07-24 23:08:27 +01:00
1f5aa765f4 Merge remote-tracking branch 'origin/master' into implement-salvage 2020-07-24 23:05:02 +01:00
7be0f83c5e Fix golanglint missing error check 2020-07-24 22:58:59 +01:00
a0e04b7e3a Placed dormant world rovers randomly have better base stats 2020-07-24 22:56:35 +01:00
a93ce97b0b Only assign rovers to accounts if given an account 2020-07-24 22:54:06 +01:00
57621d169a Implement a test for transfer and fix bugs 2020-07-24 22:50:47 +01:00
fdfcc88540 Move the account registration into the world 2020-07-24 22:50:35 +01:00
6f2d67bd7c Tag rovers by the controlling account 2020-07-24 22:22:32 +01:00
e840b3e47b Move accountant into world 2020-07-24 20:06:06 +01:00
1e4d642038 Add rover transfer command and implementation
Need to swap the accounts
2020-07-24 20:01:35 +01:00
5b2ea533f4 Remove incorrect proto comment 2020-07-24 19:46:32 +01:00
edd3e5a6cb Fix test by removing object before warping rover 2020-07-24 19:42:34 +01:00
b0ff3eb6ea Remove redundant tests (covered in command_tests) 2020-07-24 19:39:33 +01:00
be36f0631b Add logs for failed stashes 2020-07-24 19:39:25 +01:00
c321f88d96 Add code for salvage command 2020-07-24 19:39:14 +01:00
7cccb4394f Fix the help text comment 2020-07-24 19:28:44 +01:00
524487ce14 Stop the dormant rover from being a blocking object 2020-07-24 19:27:54 +01:00
2f1ccdfdb9 Make repair require rover parts 2020-07-24 19:08:39 +01:00
ce6e10afbb Add salvage command to main.go man page 2020-07-24 19:08:03 +01:00
2c1bb80779 Add salvage command
Slight refactor to re-use command variables

	Also fixes the cmdline client turn command
2020-07-23 20:57:36 +01:00
a5ac809387
Merge pull request #33 from mdiluz/dev
General dev
2020-07-23 18:50:31 +01:00
41cd93e986 Add command test for no command as error 2020-07-23 18:41:12 +01:00
8cc3b9155e Implement broadcast command test 2020-07-23 18:40:32 +01:00
8a8a27ab47 Add a test for the repair command 2020-07-23 18:37:54 +01:00
3bfc91b8f6 Add command test for stashing 2020-07-23 16:58:17 +01:00
8279a08a37 Limit the log entries to a max number 2020-07-23 16:47:39 +01:00
46d904acc6 Rename and comment ticksPerNormalMove 2020-07-23 16:44:15 +01:00
22 changed files with 1140 additions and 573 deletions

View file

@ -18,12 +18,9 @@ gen:
protoc --proto_path proto --go_out=plugins=grpc,paths=source_relative:proto/ proto/roveapi/roveapi.proto protoc --proto_path proto --go_out=plugins=grpc,paths=source_relative:proto/ proto/roveapi/roveapi.proto
test: test:
@echo Unit tests @echo Run unit and integration tests
go test -v ./... 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 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 go tool cover -html=/tmp/coverage-data/c.out -o /tmp/coverage.html
@echo Done, coverage data can be found in /tmp/coverage.html @echo Done, coverage data can be found in /tmp/coverage.html

View file

@ -1,9 +1,8 @@
Rove 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](https://github.com/mdiluz/rove/blob/master/data/icon.svg) ![Rove](data/icon.svg)
Rove is an asynchronous nomadic game about exploring as part of a loose community. 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](https://github.com/mdiluz/rove/blob/master/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](proto/roveapi/roveapi.proto) for the current server-client API.

View file

@ -34,7 +34,7 @@ func (s *Server) Register(ctx context.Context, req *roveapi.RegisterRequest) (*r
return nil, fmt.Errorf("empty account name") return nil, fmt.Errorf("empty account name")
} }
if acc, err := s.accountant.RegisterAccount(req.Name); err != nil { if acc, err := s.world.Accountant.RegisterAccount(req.Name); err != nil {
return nil, err return nil, err
} else if _, err := s.SpawnRoverForAccount(req.Name); err != nil { } else if _, err := s.SpawnRoverForAccount(req.Name); err != nil {
@ -57,13 +57,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) { 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) log.Printf("Handling status request: %s\n", req.Account.Name)
if valid, err := s.accountant.VerifySecret(req.Account.Name, req.Account.Secret); err != nil { if valid, err := s.world.Accountant.VerifySecret(req.Account.Name, req.Account.Secret); err != nil {
return nil, err return nil, err
} else if !valid { } else if !valid {
return nil, fmt.Errorf("Secret incorrect for account %s", req.Account.Name) return nil, fmt.Errorf("Secret incorrect for account %s", req.Account.Name)
} else if resp, err := s.accountant.GetValue(req.Account.Name, "rover"); err != nil { } else if resp, err := s.world.Accountant.GetValue(req.Account.Name, "rover"); err != nil {
return nil, err return nil, err
} else if rover, err := s.world.GetRover(resp); err != nil { } else if rover, err := s.world.GetRover(resp); err != nil {
@ -117,7 +117,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) { func (s *Server) Radar(ctx context.Context, req *roveapi.RadarRequest) (*roveapi.RadarResponse, error) {
log.Printf("Handling radar request: %s\n", req.Account.Name) log.Printf("Handling radar request: %s\n", req.Account.Name)
if valid, err := s.accountant.VerifySecret(req.Account.Name, req.Account.Secret); err != nil { if valid, err := s.world.Accountant.VerifySecret(req.Account.Name, req.Account.Secret); err != nil {
return nil, err return nil, err
} else if !valid { } else if !valid {
@ -126,7 +126,7 @@ func (s *Server) Radar(ctx context.Context, req *roveapi.RadarRequest) (*roveapi
response := &roveapi.RadarResponse{} response := &roveapi.RadarResponse{}
resp, err := s.accountant.GetValue(req.Account.Name, "rover") resp, err := s.world.Accountant.GetValue(req.Account.Name, "rover")
if err != nil { if err != nil {
return nil, err return nil, err
@ -149,14 +149,14 @@ func (s *Server) Radar(ctx context.Context, req *roveapi.RadarRequest) (*roveapi
func (s *Server) Command(ctx context.Context, req *roveapi.CommandRequest) (*roveapi.CommandResponse, error) { 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) log.Printf("Handling command request: %s and %+v\n", req.Account.Name, req.Commands)
if valid, err := s.accountant.VerifySecret(req.Account.Name, req.Account.Secret); err != nil { if valid, err := s.world.Accountant.VerifySecret(req.Account.Name, req.Account.Secret); err != nil {
return nil, err return nil, err
} else if !valid { } else if !valid {
return nil, fmt.Errorf("Secret incorrect for account %s", req.Account.Name) return nil, fmt.Errorf("Secret incorrect for account %s", req.Account.Name)
} }
resp, err := s.accountant.GetValue(req.Account.Name, "rover") resp, err := s.world.Accountant.GetValue(req.Account.Name, "rover")
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -4,6 +4,8 @@ import (
"fmt" "fmt"
"log" "log"
"net" "net"
"os"
"path"
"sync" "sync"
"github.com/mdiluz/rove/pkg/persistence" "github.com/mdiluz/rove/pkg/persistence"
@ -11,8 +13,12 @@ import (
"github.com/mdiluz/rove/proto/roveapi" "github.com/mdiluz/rove/proto/roveapi"
"github.com/robfig/cron" "github.com/robfig/cron"
"google.golang.org/grpc" "google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/reflection"
) )
var cert = os.Getenv("CERT_NAME")
const ( const (
// PersistentData will allow the server to load and save it's state // PersistentData will allow the server to load and save it's state
PersistentData = iota PersistentData = iota
@ -27,9 +33,6 @@ type Server struct {
// Internal state // Internal state
world *rove.World world *rove.World
// Accountant
accountant Accountant
// gRPC server // gRPC server
netListener net.Listener netListener net.Listener
grpcServ *grpc.Server grpcServ *grpc.Server
@ -80,7 +83,6 @@ func NewServer(opts ...ServerOption) *Server {
persistence: EphemeralData, persistence: EphemeralData,
schedule: cron.New(), schedule: cron.New(),
world: rove.NewWorld(32), world: rove.NewWorld(32),
accountant: NewSimpleAccountant(),
} }
// Apply all options // Apply all options
@ -107,8 +109,22 @@ func (s *Server) Initialise(fillWorld bool) (err error) {
if err != nil { if err != nil {
log.Fatalf("failed to listen: %v", err) log.Fatalf("failed to listen: %v", err)
} }
s.grpcServ = grpc.NewServer()
// 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...)
roveapi.RegisterRoveServer(s.grpcServ, s) roveapi.RegisterRoveServer(s.grpcServ, s)
reflection.Register(s.grpcServ)
return nil return nil
} }
@ -188,7 +204,7 @@ func (s *Server) SaveWorld() error {
if s.persistence == PersistentData { if s.persistence == PersistentData {
s.world.RLock() s.world.RLock()
defer s.world.RUnlock() defer s.world.RUnlock()
if err := persistence.SaveAll("world", s.world, "accounts", s.accountant); err != nil { if err := persistence.SaveAll("world", s.world); err != nil {
return fmt.Errorf("failed to save out persistent data: %s", err) return fmt.Errorf("failed to save out persistent data: %s", err)
} }
} }
@ -200,7 +216,7 @@ func (s *Server) LoadWorld() error {
if s.persistence == PersistentData { if s.persistence == PersistentData {
s.world.Lock() s.world.Lock()
defer s.world.Unlock() defer s.world.Unlock()
if err := persistence.LoadAll("world", &s.world, "accounts", &s.accountant); err != nil { if err := persistence.LoadAll("world", &s.world); err != nil {
return err return err
} }
} }
@ -209,22 +225,10 @@ func (s *Server) LoadWorld() error {
// SpawnRoverForAccount spawns the rover rover for an account // SpawnRoverForAccount spawns the rover rover for an account
func (s *Server) SpawnRoverForAccount(account string) (string, error) { func (s *Server) SpawnRoverForAccount(account string) (string, error) {
inst, err := s.world.SpawnRover() inst, err := s.world.SpawnRover(account)
if err != nil { if err != nil {
return "", err 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 return inst, nil
} }

View file

@ -1,6 +1,7 @@
package internal package internal
import ( import (
"os"
"testing" "testing"
) )
@ -30,6 +31,7 @@ func TestNewServer_OptionPersistentData(t *testing.T) {
} }
func TestServer_Run(t *testing.T) { func TestServer_Run(t *testing.T) {
os.Setenv("NO_TLS", "1")
server := NewServer() server := NewServer()
if server == nil { if server == nil {
t.Error("Failed to create server") t.Error("Failed to create server")
@ -45,6 +47,7 @@ func TestServer_Run(t *testing.T) {
} }
func TestServer_RunPersistentData(t *testing.T) { func TestServer_RunPersistentData(t *testing.T) {
os.Setenv("NO_TLS", "1")
server := NewServer(OptionPersistentData()) server := NewServer(OptionPersistentData())
if server == nil { if server == nil {
t.Error("Failed to create server") t.Error("Failed to create server")

View file

@ -25,6 +25,9 @@ const (
// GlyphRoverDormant represents a dormant rover // GlyphRoverDormant represents a dormant rover
GlyphRoverDormant = Glyph('r') GlyphRoverDormant = Glyph('r')
// GlyphRoverParts represents spare rover parts
GlyphRoverParts = Glyph('*')
// GlyphRockSmall is a small stashable rock // GlyphRockSmall is a small stashable rock
GlyphRockSmall = Glyph('o') GlyphRockSmall = Glyph('o')
@ -58,6 +61,8 @@ func ObjectGlyph(o roveapi.Object) Glyph {
return GlyphRoverDormant return GlyphRoverDormant
case roveapi.Object_RockLarge: case roveapi.Object_RockLarge:
return GlyphRockLarge return GlyphRockLarge
case roveapi.Object_RoverParts:
return GlyphRoverParts
} }
log.Fatalf("Unknown object type: %c", o) log.Fatalf("Unknown object type: %c", o)

View file

@ -1,6 +1,7 @@
package main package main
import ( import (
"crypto/tls"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
@ -8,6 +9,7 @@ import (
"os" "os"
"path" "path"
"path/filepath" "path/filepath"
"strconv"
"time" "time"
"github.com/mdiluz/rove/cmd/rove/internal" "github.com/mdiluz/rove/cmd/rove/internal"
@ -15,6 +17,7 @@ import (
"github.com/mdiluz/rove/proto/roveapi" "github.com/mdiluz/rove/proto/roveapi"
"golang.org/x/net/context" "golang.org/x/net/context"
"google.golang.org/grpc" "google.golang.org/grpc"
"google.golang.org/grpc/credentials"
) )
var home = os.Getenv("HOME") var home = os.Getenv("HOME")
@ -22,23 +25,30 @@ var defaultDataPath = path.Join(home, ".local/share/")
// Command usage // Command usage
func printUsage() { func printUsage() {
fmt.Fprintf(os.Stderr, "Usage: rove COMMAND [ARGS...]\n") fmt.Fprintln(os.Stderr, "Usage: rove ARG [OPT...]")
fmt.Fprintln(os.Stderr, "\nCommands") fmt.Fprintf(os.Stderr, "\n")
fmt.Fprintln(os.Stderr, "\tserver-status prints the server status") fmt.Fprintln(os.Stderr, "Arguments:")
fmt.Fprintln(os.Stderr, "\tregister NAME registers an account and stores it (use with -name)") fmt.Fprintln(os.Stderr, "\tversion outputs version")
fmt.Fprintln(os.Stderr, "\tcommand COMMAND [VAL...] issue commands to rover, accepts multiple, see below") fmt.Fprintln(os.Stderr, "\thelp outputs this usage text")
fmt.Fprintln(os.Stderr, "\tradar gathers radar data for the current rover") fmt.Fprintln(os.Stderr, "\tconfig [HOST] outputs the local config, optionally sets host")
fmt.Fprintln(os.Stderr, "\tstatus gets status info for current rover") fmt.Fprintln(os.Stderr, "\tserver-status prints the server status")
fmt.Fprintln(os.Stderr, "\tconfig [HOST] outputs the local config info, optionally sets host") fmt.Fprintln(os.Stderr, "\tregister NAME registers an account and spawns a rover")
fmt.Fprintln(os.Stderr, "\thelp outputs this usage information") fmt.Fprintln(os.Stderr, "\tradar prints radar data in ASCII form")
fmt.Fprintln(os.Stderr, "\tversion outputs version info") fmt.Fprintln(os.Stderr, "\tstatus gets rover status")
fmt.Fprintln(os.Stderr, "\nRover commands:") fmt.Fprintln(os.Stderr, "\tcommand CMD [VAL...] [REPEAT] sets the command queue, accepts multiple in sequence")
fmt.Fprintln(os.Stderr, "\ttoggle toggles the sails, either catching the wind, or charging from the sun") fmt.Fprintf(os.Stderr, "\n")
fmt.Fprintln(os.Stderr, "\tstash stores the object at the rover location in the inventory") fmt.Fprintln(os.Stderr, "Rover commands:")
fmt.Fprintln(os.Stderr, "\trepair uses an inventory object to repair the rover") fmt.Fprintln(os.Stderr, "\ttoggle toggles the current sail mode")
fmt.Fprintln(os.Stderr, "\tbroadcast MSG broadcast a simple ASCII triplet to nearby rovers") fmt.Fprintln(os.Stderr, "\tstash stores the object at the rover location in the inventory")
fmt.Fprintln(os.Stderr, "\nEnvironment") fmt.Fprintln(os.Stderr, "\trepair repairs the rover using inventory item")
fmt.Fprintln(os.Stderr, "\tROVE_USER_DATA path to user data, defaults to "+defaultDataPath) 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)
} }
const gRPCport = 9090 const gRPCport = 9090
@ -178,8 +188,15 @@ 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]) 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 // Set up the server
clientConn, err := grpc.Dial(fmt.Sprintf("%s:%d", config.Host, gRPCport), grpc.WithInsecure()) clientConn, err := grpc.Dial(fmt.Sprintf("%s:%d", config.Host, gRPCport), opts...)
if err != nil { if err != nil {
return err return err
} }
@ -231,6 +248,8 @@ func InnerMain(command string, args ...string) error {
// Iterate through each command // Iterate through each command
var commands []*roveapi.Command var commands []*roveapi.Command
for i := 0; i < len(args); i++ { for i := 0; i < len(args); i++ {
var cmd *roveapi.Command
switch args[i] { switch args[i] {
case "turn": case "turn":
i++ i++
@ -241,12 +260,10 @@ func InnerMain(command string, args ...string) error {
if b == roveapi.Bearing_BearingUnknown { if b == roveapi.Bearing_BearingUnknown {
return fmt.Errorf("turn command must be given a valid bearing %s", args[i]) return fmt.Errorf("turn command must be given a valid bearing %s", args[i])
} }
commands = append(commands, cmd = &roveapi.Command{
&roveapi.Command{ Command: roveapi.CommandType_turn,
Command: roveapi.CommandType_broadcast, Bearing: b,
Broadcast: []byte(args[i]), }
},
)
case "broadcast": case "broadcast":
i++ i++
if len(args) == i { if len(args) == i {
@ -254,20 +271,51 @@ func InnerMain(command string, args ...string) error {
} else if len(args[i]) > 3 { } else if len(args[i]) > 3 {
return fmt.Errorf("broadcast command must be given ASCII triplet of 3 or less: %s", args[i]) return fmt.Errorf("broadcast command must be given ASCII triplet of 3 or less: %s", args[i])
} }
commands = append(commands, cmd = &roveapi.Command{
&roveapi.Command{ Command: roveapi.CommandType_broadcast,
Command: roveapi.CommandType_broadcast, Data: []byte(args[i]),
Broadcast: []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,
}
default: default:
// By default just use the command literally // By default just use the command literally
commands = append(commands, cmd = &roveapi.Command{
&roveapi.Command{ Command: roveapi.CommandType(roveapi.CommandType_value[args[i]]),
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{ _, err := client.Command(ctx, &roveapi.CommandRequest{
@ -355,6 +403,15 @@ func InnerMain(command string, args ...string) error {
func main() { func main() {
// Bail without any args // Bail without any args
if len(os.Args) == 1 { 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() printUsage()
os.Exit(1) os.Exit(1)
} }

View file

@ -13,6 +13,7 @@ import (
) )
func Test_InnerMain(t *testing.T) { func Test_InnerMain(t *testing.T) {
os.Setenv("NO_TLS", "1")
// Use temporary local user data // Use temporary local user data
tmp, err := ioutil.TempDir(os.TempDir(), "rove-") tmp, err := ioutil.TempDir(os.TempDir(), "rove-")
@ -53,9 +54,14 @@ func Test_InnerMain(t *testing.T) {
assert.NoError(t, InnerMain("command", "toggle")) assert.NoError(t, InnerMain("command", "toggle"))
assert.NoError(t, InnerMain("command", "stash")) assert.NoError(t, InnerMain("command", "stash"))
assert.NoError(t, InnerMain("command", "repair")) 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", "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 // Give it malformed commands
assert.Error(t, InnerMain("command", "unknown")) assert.Error(t, InnerMain("command", "unknown"))
assert.Error(t, InnerMain("command", "broadcast")) assert.Error(t, InnerMain("command", "broadcast"))
assert.Error(t, InnerMain("command", "upgrade"))
assert.Error(t, InnerMain("command", "1"))
} }

32
docker-compose-test.yml Normal file
View file

@ -0,0 +1,32 @@
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

View file

@ -16,21 +16,10 @@ services:
- DATA_PATH=/mnt/rove-server - DATA_PATH=/mnt/rove-server
- WORDS_FILE=data/words_alpha.txt - WORDS_FILE=data/words_alpha.txt
- TICK_RATE=3 - TICK_RATE=3
- CERT_NAME=${CERT_NAME}
volumes: volumes:
- persistent-data:/mnt/rove-server:rw - persistent-data:/mnt/rove-server:rw
- /etc/letsencrypt/:/etc/letsencrypt/
command: [ "./rove-server"] 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

View file

@ -1,4 +1,4 @@
package internal package accounts
// Accountant decribes something that stores accounts and account values // Accountant decribes something that stores accounts and account values
type Accountant interface { type Accountant interface {

View file

@ -1,4 +1,4 @@
package internal package accounts
import ( import (
"testing" "testing"

View file

@ -1,4 +1,4 @@
package internal package accounts
import ( import (
"fmt" "fmt"

View file

@ -2,7 +2,6 @@ package rove
import ( import (
"log" "log"
"math/rand"
"github.com/mdiluz/rove/pkg/maths" "github.com/mdiluz/rove/pkg/maths"
"github.com/mdiluz/rove/proto/roveapi" "github.com/mdiluz/rove/proto/roveapi"
@ -111,15 +110,6 @@ func (a *chunkBasedAtlas) populate(chunk int) {
} }
} }
// 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}
}
}
a.Chunks[chunk] = c a.Chunks[chunk] = c
} }

View file

@ -1,15 +1,26 @@
package rove package rove
import ( import (
"encoding/json"
"testing" "testing"
"github.com/mdiluz/rove/pkg/maths"
"github.com/mdiluz/rove/proto/roveapi" "github.com/mdiluz/rove/proto/roveapi"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
func TestCommand_Invalid(t *testing.T) {
w := NewWorld(8)
name, err := w.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) { func TestCommand_Toggle(t *testing.T) {
w := NewWorld(8) w := NewWorld(8)
a, err := w.SpawnRover() a, err := w.SpawnRover("")
assert.NoError(t, err) assert.NoError(t, err)
r, err := w.GetRover(a) r, err := w.GetRover(a)
@ -35,10 +46,10 @@ func TestCommand_Toggle(t *testing.T) {
func TestCommand_Turn(t *testing.T) { func TestCommand_Turn(t *testing.T) {
w := NewWorld(8) w := NewWorld(8)
a, err := w.SpawnRover() a, err := w.SpawnRover("")
assert.NoError(t, err) assert.NoError(t, err)
err = w.Enqueue(a, &roveapi.Command{Command: roveapi.CommandType_turn, Turn: roveapi.Bearing_NorthWest}) err = w.Enqueue(a, &roveapi.Command{Command: roveapi.CommandType_turn, Bearing: roveapi.Bearing_NorthWest})
assert.NoError(t, err) assert.NoError(t, err)
w.Tick() w.Tick()
@ -48,17 +59,247 @@ func TestCommand_Turn(t *testing.T) {
} }
func TestCommand_Stash(t *testing.T) { func TestCommand_Stash(t *testing.T) {
// TODO: Test the stash command 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) { func TestCommand_Repair(t *testing.T) {
// TODO: Test the repair command 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) { func TestCommand_Broadcast(t *testing.T) {
// TODO: Test the stash command 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_Invalid(t *testing.T) { func TestCommand_Salvage(t *testing.T) {
// TODO: Test an invalid command 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)
}
// Check the dormant rover is gone
_, obj := w.Atlas.QueryPosition(info.Pos)
assert.Equal(t, roveapi.Object_ObjectUnknown, obj.Type)
}
func TestCommand_Transfer(t *testing.T) {
w := NewWorld(8)
acc, err := w.Accountant.RegisterAccount("tmp")
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)
}
// One last tick to do the toggle
w.Tick()
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)
} }

View file

@ -17,7 +17,6 @@ type Object struct {
func (o *Object) IsBlocking() bool { func (o *Object) IsBlocking() bool {
var blocking = [...]roveapi.Object{ var blocking = [...]roveapi.Object{
roveapi.Object_RoverLive, roveapi.Object_RoverLive,
roveapi.Object_RoverDormant,
roveapi.Object_RockLarge, roveapi.Object_RockLarge,
} }
@ -33,6 +32,7 @@ func (o *Object) IsBlocking() bool {
func (o *Object) IsStashable() bool { func (o *Object) IsStashable() bool {
var stashable = [...]roveapi.Object{ var stashable = [...]roveapi.Object{
roveapi.Object_RockSmall, roveapi.Object_RockSmall,
roveapi.Object_RoverParts,
} }
for _, t := range stashable { for _, t := range stashable {

View file

@ -13,6 +13,10 @@ import (
"github.com/mdiluz/rove/proto/roveapi" "github.com/mdiluz/rove/proto/roveapi"
) )
const (
maxLogEntries = 16
)
// RoverLogEntry describes a single log entry for the rover // RoverLogEntry describes a single log entry for the rover
type RoverLogEntry struct { type RoverLogEntry struct {
// Time is the timestamp of the entry // Time is the timestamp of the entry
@ -62,12 +66,15 @@ type Rover struct {
// Logs Stores log of information // Logs Stores log of information
Logs []RoverLogEntry Logs []RoverLogEntry
// The account that owns this rover
Owner string
} }
// DefaultRover returns a default rover object with default settings // DefaultRover returns a default rover object with default settings
func DefaultRover() *Rover { func DefaultRover() *Rover {
return &Rover{ return &Rover{
Range: 4, Range: 10,
Integrity: 10, Integrity: 10,
MaximumIntegrity: 10, MaximumIntegrity: 10,
Capacity: 10, Capacity: 10,
@ -89,6 +96,11 @@ func (r *Rover) AddLogEntryf(format string, args ...interface{}) {
Text: text, 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 wordsFile = os.Getenv("WORDS_FILE")

View file

@ -1,17 +1,23 @@
package rove package rove
import ( import (
"encoding/json"
"fmt" "fmt"
"log" "log"
"math/rand" "math/rand"
"sync" "sync"
"github.com/mdiluz/rove/pkg/accounts"
"github.com/mdiluz/rove/pkg/maths" "github.com/mdiluz/rove/pkg/maths"
"github.com/mdiluz/rove/proto/roveapi" "github.com/mdiluz/rove/proto/roveapi"
) )
const ( const (
TicksPerNormalMove = 4 // 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 // CommandStream is a list of commands to execute in order
@ -38,6 +44,9 @@ type World struct {
// Commands is the set of currently executing command streams per rover // Commands is the set of currently executing command streams per rover
CommandQueue map[string]CommandStream CommandQueue map[string]CommandStream
// Accountant
Accountant accounts.Accountant
// Mutex to lock around all world operations // Mutex to lock around all world operations
worldMutex sync.RWMutex worldMutex sync.RWMutex
// Mutex to lock around command operations // Mutex to lock around command operations
@ -52,17 +61,22 @@ func NewWorld(chunkSize int) *World {
Atlas: NewChunkAtlas(chunkSize), Atlas: NewChunkAtlas(chunkSize),
TicksPerDay: 24, TicksPerDay: 24,
CurrentTicks: 0, CurrentTicks: 0,
Accountant: accounts.NewSimpleAccountant(),
Wind: roveapi.Bearing_North,
} }
} }
// SpawnRover adds an rover to the game // SpawnRover adds an rover to the game (without lock)
func (w *World) SpawnRover() (string, error) { func (w *World) SpawnRover(account string) (string, error) {
w.worldMutex.Lock() w.worldMutex.Lock()
defer w.worldMutex.Unlock() defer w.worldMutex.Unlock()
// Initialise the rover // Initialise the rover
rover := DefaultRover() rover := DefaultRover()
// Assign the owner
rover.Owner = account
// Spawn in a random place near the origin // Spawn in a random place near the origin
rover.Pos = maths.Vector{ rover.Pos = maths.Vector{
X: 10 - rand.Intn(20), X: 10 - rand.Intn(20),
@ -87,7 +101,13 @@ func (w *World) SpawnRover() (string, error) {
// Append the rover to the list // Append the rover to the list
w.Rovers[rover.Name] = rover w.Rovers[rover.Name] = rover
return rover.Name, nil 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
} }
// GetRover gets a specific rover by name // GetRover gets a specific rover by name
@ -164,12 +184,26 @@ func (w *World) DestroyRover(rover string) error {
w.worldMutex.Lock() w.worldMutex.Lock()
defer w.worldMutex.Unlock() defer w.worldMutex.Unlock()
_, ok := w.Rovers[rover] r, ok := w.Rovers[rover]
if !ok { if !ok {
return fmt.Errorf("no rover matching id") return fmt.Errorf("no rover matching id")
} }
// Remove this rover from tracked rovers
delete(w.Rovers, rover) 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 return nil
} }
@ -259,9 +293,6 @@ func (w *World) TryMoveRover(rover string, b roveapi.Bearing) (maths.Vector, err
i.AddLogEntryf("tried to move %s to %+v", b.String(), newPos) i.AddLogEntryf("tried to move %s to %+v", b.String(), newPos)
i.Integrity = i.Integrity - 1 i.Integrity = i.Integrity - 1
i.AddLogEntryf("had a collision, new integrity %d", i.Integrity) i.AddLogEntryf("had a collision, new integrity %d", i.Integrity)
// TODO: The rover needs to be left dormant with the player
//if i.Integrity == 0 {
//}
} }
return i.Pos, nil return i.Pos, nil
@ -279,11 +310,13 @@ func (w *World) RoverStash(rover string) (roveapi.Object, error) {
// Can't pick up when full // Can't pick up when full
if len(r.Inventory) >= r.Capacity { if len(r.Inventory) >= r.Capacity {
r.AddLogEntryf("tried to stash object but inventory was full")
return roveapi.Object_ObjectUnknown, nil return roveapi.Object_ObjectUnknown, nil
} }
// Ensure the rover has energy // Ensure the rover has energy
if r.Charge <= 0 { if r.Charge <= 0 {
r.AddLogEntryf("tried to stash object but had no charge")
return roveapi.Object_ObjectUnknown, nil return roveapi.Object_ObjectUnknown, nil
} }
r.Charge-- r.Charge--
@ -299,6 +332,99 @@ func (w *World) RoverStash(rover string) (roveapi.Object, error) {
return obj.Type, nil 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 // RoverToggle will toggle the sail position
func (w *World) RoverToggle(rover string) (roveapi.SailPosition, error) { func (w *World) RoverToggle(rover string) (roveapi.SailPosition, error) {
w.worldMutex.Lock() w.worldMutex.Lock()
@ -323,6 +449,65 @@ func (w *World) RoverToggle(rover string) (roveapi.SailPosition, error) {
return r.SailPosition, nil 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 // RoverTurn will turn the rover
func (w *World) RoverTurn(rover string, bearing roveapi.Bearing) (roveapi.Bearing, error) { func (w *World) RoverTurn(rover string, bearing roveapi.Bearing) (roveapi.Bearing, error) {
w.worldMutex.Lock() w.worldMutex.Lock()
@ -351,11 +536,24 @@ func (w *World) RoverRepair(rover string) (int, error) {
return 0, fmt.Errorf("no rover matching id") return 0, fmt.Errorf("no rover matching id")
} }
// Consume an inventory item to repair if possible // Can't repair past max
if len(r.Inventory) > 0 && r.Integrity < r.MaximumIntegrity { if r.Integrity >= r.MaximumIntegrity {
r.Inventory = r.Inventory[:len(r.Inventory)-1] return r.Integrity, nil
r.Integrity = r.Integrity + 1 }
r.AddLogEntryf("repaired self to %d", r.Integrity)
// 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 return r.Integrity, nil
@ -434,21 +632,28 @@ func (w *World) Enqueue(rover string, commands ...*roveapi.Command) error {
for _, c := range commands { for _, c := range commands {
switch c.Command { switch c.Command {
case roveapi.CommandType_broadcast: case roveapi.CommandType_broadcast:
if len(c.GetBroadcast()) > 3 { if len(c.GetData()) > 3 {
return fmt.Errorf("too many characters in message (limit 3): %d", len(c.GetBroadcast())) return fmt.Errorf("too many characters in message (limit 3): %d", len(c.GetData()))
} }
for _, b := range c.GetBroadcast() { for _, b := range c.GetData() {
if b < 37 || b > 126 { if b < 37 || b > 126 {
return fmt.Errorf("invalid message character: %c", b) return fmt.Errorf("invalid message character: %c", b)
} }
} }
case roveapi.CommandType_turn: case roveapi.CommandType_turn:
if c.GetTurn() == roveapi.Bearing_BearingUnknown { if c.GetBearing() == roveapi.Bearing_BearingUnknown {
return fmt.Errorf("turn command given unknown bearing") 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_toggle:
case roveapi.CommandType_stash: case roveapi.CommandType_stash:
case roveapi.CommandType_repair: case roveapi.CommandType_repair:
case roveapi.CommandType_salvage:
case roveapi.CommandType_transfer:
// Nothing to verify // Nothing to verify
default: default:
return fmt.Errorf("unknown command: %s", c.Command) return fmt.Errorf("unknown command: %s", c.Command)
@ -474,13 +679,17 @@ func (w *World) Tick() {
if len(cmds) != 0 { if len(cmds) != 0 {
// Execute the command // Execute the command
if err := w.ExecuteCommand(cmds[0], rover); err != nil { if done, err := w.ExecuteCommand(cmds[0], rover); err != nil {
log.Println(err) log.Println(err)
// TODO: Report this error somehow // TODO: Report this error somehow
}
// Extract the first command in the queue } else if done {
w.CommandQueue[rover] = cmds[1:] // 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 { } else {
// Clean out the empty entry // Clean out the empty entry
@ -488,11 +697,6 @@ func (w *World) Tick() {
} }
} }
// Change the wind every day
if (w.CurrentTicks % w.TicksPerDay) == 0 {
w.Wind = roveapi.Bearing((rand.Int() % 8) + 1) // Random cardinal bearing
}
// Move all the rovers based on current wind and sails // Move all the rovers based on current wind and sails
for n, r := range w.Rovers { for n, r := range w.Rovers {
// Skip if we're not catching the wind // Skip if we're not catching the wind
@ -515,16 +719,16 @@ func (w *World) Tick() {
switch diff { switch diff {
case 0: case 0:
// Going with the wind, travel at base speed of once every 4 ticks // Going with the wind, travel at base speed of once every 4 ticks
ticksToMove = TicksPerNormalMove ticksToMove = ticksPerNormalMove
case 1: case 1:
// At a slight angle, we can go a little faster // At a slight angle, we can go a little faster
ticksToMove = TicksPerNormalMove / 2 ticksToMove = ticksPerNormalMove / 2
case 2: case 2:
// Perpendicular to wind, max speed // Perpendicular to wind, max speed
ticksToMove = 1 ticksToMove = 1
case 3: case 3:
// Heading at 45 degrees into the wind, back to min speed // Heading at 45 degrees into the wind, back to min speed
ticksToMove = TicksPerNormalMove ticksToMove = ticksPerNormalMove
case 4: case 4:
// Heading durectly into the wind, no movement at all // Heading durectly into the wind, no movement at all
default: default:
@ -544,44 +748,64 @@ func (w *World) Tick() {
} }
} }
// 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
}
}
}
// Increment the current tick count // Increment the current tick count
w.CurrentTicks++ 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 // ExecuteCommand will execute a single command
func (w *World) ExecuteCommand(c *roveapi.Command, rover string) (err error) { func (w *World) ExecuteCommand(c *roveapi.Command, rover string) (done bool, err error) {
log.Printf("Executing command: %+v for %s\n", c.Command, rover) log.Printf("Executing command: %+v for %s\n", c.Command, rover)
switch c.Command { switch c.Command {
case roveapi.CommandType_toggle: case roveapi.CommandType_toggle:
if _, err := w.RoverToggle(rover); err != nil { _, err = w.RoverToggle(rover)
return err
}
case roveapi.CommandType_stash: case roveapi.CommandType_stash:
if _, err := w.RoverStash(rover); err != nil { _, err = w.RoverStash(rover)
return err
}
case roveapi.CommandType_repair: case roveapi.CommandType_repair:
if _, err := w.RoverRepair(rover); err != nil { _, err = w.RoverRepair(rover)
return err
}
case roveapi.CommandType_broadcast: case roveapi.CommandType_broadcast:
if err := w.RoverBroadcast(rover, c.GetBroadcast()); err != nil { err = w.RoverBroadcast(rover, c.GetData())
return err
}
case roveapi.CommandType_turn: case roveapi.CommandType_turn:
if _, err := w.RoverTurn(rover, c.GetTurn()); err != nil { _, err = w.RoverTurn(rover, c.GetBearing())
return err 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
default: default:
return fmt.Errorf("unknown command: %s", c.Command) return true, fmt.Errorf("unknown command: %s", c.Command)
} }
return // Decrement the repeat number
c.Repeat--
return c.Repeat < 0, err
} }
// Daytime returns if it's currently daytime // Daytime returns if it's currently daytime

View file

@ -18,9 +18,9 @@ func TestNewWorld(t *testing.T) {
func TestWorld_CreateRover(t *testing.T) { func TestWorld_CreateRover(t *testing.T) {
world := NewWorld(8) world := NewWorld(8)
a, err := world.SpawnRover() a, err := world.SpawnRover("")
assert.NoError(t, err) assert.NoError(t, err)
b, err := world.SpawnRover() b, err := world.SpawnRover("")
assert.NoError(t, err) assert.NoError(t, err)
// Basic duplicate check // Basic duplicate check
@ -33,7 +33,7 @@ func TestWorld_CreateRover(t *testing.T) {
func TestWorld_GetRover(t *testing.T) { func TestWorld_GetRover(t *testing.T) {
world := NewWorld(4) world := NewWorld(4)
a, err := world.SpawnRover() a, err := world.SpawnRover("")
assert.NoError(t, err) assert.NoError(t, err)
rover, err := world.GetRover(a) rover, err := world.GetRover(a)
@ -44,9 +44,9 @@ func TestWorld_GetRover(t *testing.T) {
func TestWorld_DestroyRover(t *testing.T) { func TestWorld_DestroyRover(t *testing.T) {
world := NewWorld(1) world := NewWorld(1)
a, err := world.SpawnRover() a, err := world.SpawnRover("")
assert.NoError(t, err) assert.NoError(t, err)
b, err := world.SpawnRover() b, err := world.SpawnRover("")
assert.NoError(t, err) assert.NoError(t, err)
err = world.DestroyRover(a) err = world.DestroyRover(a)
@ -62,7 +62,7 @@ func TestWorld_DestroyRover(t *testing.T) {
func TestWorld_GetSetMovePosition(t *testing.T) { func TestWorld_GetSetMovePosition(t *testing.T) {
world := NewWorld(4) world := NewWorld(4)
a, err := world.SpawnRover() a, err := world.SpawnRover("")
assert.NoError(t, err) assert.NoError(t, err)
pos := maths.Vector{ pos := maths.Vector{
@ -97,9 +97,9 @@ func TestWorld_GetSetMovePosition(t *testing.T) {
func TestWorld_RadarFromRover(t *testing.T) { func TestWorld_RadarFromRover(t *testing.T) {
// Create world that should have visible walls on the radar // Create world that should have visible walls on the radar
world := NewWorld(2) world := NewWorld(2)
a, err := world.SpawnRover() a, err := world.SpawnRover("")
assert.NoError(t, err) assert.NoError(t, err)
b, err := world.SpawnRover() b, err := world.SpawnRover("")
assert.NoError(t, err) assert.NoError(t, err)
// Warp the rovers into position // Warp the rovers into position
@ -109,15 +109,16 @@ func TestWorld_RadarFromRover(t *testing.T) {
world.Atlas.SetObject(maths.Vector{X: 0, Y: 0}, Object{Type: roveapi.Object_ObjectUnknown}) 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") 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) radar, objs, err := world.RadarFromRover(a)
assert.NoError(t, err, "Failed to get radar from rover") assert.NoError(t, err, "Failed to get radar from rover")
fullRange := 4 + 4 + 1 fullRange := r.Range + r.Range + 1
assert.Equal(t, fullRange*fullRange, len(radar), "Radar returned wrong length") assert.Equal(t, fullRange*fullRange, len(radar), "Radar returned wrong length")
assert.Equal(t, fullRange*fullRange, len(objs), "Radar returned wrong length") assert.Equal(t, fullRange*fullRange, len(objs), "Radar returned wrong length")
// Test the expected values // TODO: Verify the other rover is on the radar
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 // Check the radar results are stable
radar1, objs1, err := world.RadarFromRover(a) radar1, objs1, err := world.RadarFromRover(a)
@ -128,9 +129,11 @@ func TestWorld_RadarFromRover(t *testing.T) {
assert.Equal(t, objs1, objs2) assert.Equal(t, objs1, objs2)
} }
func TestWorld_RoverStash(t *testing.T) { func TestWorld_RoverDamage(t *testing.T) {
world := NewWorld(2) world := NewWorld(2)
a, err := world.SpawnRover() acc, err := world.Accountant.RegisterAccount("tmp")
assert.NoError(t, err)
a, err := world.SpawnRover(acc.Name)
assert.NoError(t, err) assert.NoError(t, err)
pos := maths.Vector{ pos := maths.Vector{
@ -142,78 +145,6 @@ func TestWorld_RoverStash(t *testing.T) {
err = world.WarpRover(a, pos) err = world.WarpRover(a, pos)
assert.NoError(t, err, "Failed to set position for rover") 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, 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, 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, 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{
X: 0.0,
Y: 0.0,
}
err = world.WarpRover(a, pos)
assert.NoError(t, err, "Failed to set position for rover")
info, err := world.GetRover(a) info, err := world.GetRover(a)
assert.NoError(t, err, "couldn't get rover info") assert.NoError(t, err, "couldn't get rover info")
@ -227,69 +158,29 @@ func TestWorld_RoverDamage(t *testing.T) {
assert.NoError(t, err, "couldn't get rover info") assert.NoError(t, err, "couldn't get rover info")
assert.Equal(t, info.Integrity-1, newinfo.Integrity, "rover should have lost integrity") 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") assert.Contains(t, newinfo.Logs[len(newinfo.Logs)-1].Text, "collision", "Rover logs should contain the collision")
}
func TestWorld_RoverRepair(t *testing.T) { // Keep moving to damage the rover
world := NewWorld(2) for i := 0; i < info.Integrity-1; i++ {
a, err := world.SpawnRover() vec, err := world.TryMoveRover(a, roveapi.Bearing_North)
assert.NoError(t, err) assert.NoError(t, err, "Failed to move rover")
assert.Equal(t, pos, vec, "Rover managed to move into large rock")
pos := maths.Vector{
X: 0.0,
Y: 0.0,
} }
world.Atlas.SetObject(pos, Object{Type: roveapi.Object_ObjectUnknown}) // Tick the world to check for rover deaths
world.Tick()
err = world.WarpRover(a, pos) // Rover should have been destroyed now
assert.NoError(t, err, "Failed to set position for rover") _, err = world.GetRover(a)
assert.Error(t, err)
originalInfo, err := world.GetRover(a) _, obj := world.Atlas.QueryPosition(info.Pos)
assert.NoError(t, err, "couldn't get rover info") assert.Equal(t, roveapi.Object_RoverDormant, obj.Type)
// Pick up something to repair with
world.Atlas.SetObject(pos, 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}, Object{Type: roveapi.Object_RockLarge})
// Try and bump into the rock
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")
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(&roveapi.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, 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(&roveapi.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_Daytime(t *testing.T) { func TestWorld_Daytime(t *testing.T) {
world := NewWorld(1) world := NewWorld(1)
a, err := world.SpawnRover() a, err := world.SpawnRover("")
assert.NoError(t, err) assert.NoError(t, err)
// Remove rover charge // Remove rover charge
@ -328,10 +219,10 @@ func TestWorld_Daytime(t *testing.T) {
func TestWorld_Broadcast(t *testing.T) { func TestWorld_Broadcast(t *testing.T) {
world := NewWorld(8) world := NewWorld(8)
a, err := world.SpawnRover() a, err := world.SpawnRover("")
assert.NoError(t, err) assert.NoError(t, err)
b, err := world.SpawnRover() b, err := world.SpawnRover("")
assert.NoError(t, err) assert.NoError(t, err)
// Warp rovers near to eachother // Warp rovers near to eachother
@ -394,7 +285,7 @@ func TestWorld_Sailing(t *testing.T) {
world.Tick() // One initial tick to set the wind direction the first time world.Tick() // One initial tick to set the wind direction the first time
world.Wind = roveapi.Bearing_North // Set the wind direction to north world.Wind = roveapi.Bearing_North // Set the wind direction to north
name, err := world.SpawnRover() name, err := world.SpawnRover("")
assert.NoError(t, err) assert.NoError(t, err)
// Warp the rover to 0,0 after clearing it // Warp the rover to 0,0 after clearing it
@ -419,7 +310,7 @@ func TestWorld_Sailing(t *testing.T) {
assert.Equal(t, maths.Vector{Y: 0}, info.Pos) assert.Equal(t, maths.Vector{Y: 0}, info.Pos)
// Loop a few more times // Loop a few more times
for i := 0; i < TicksPerNormalMove-2; i++ { for i := 0; i < ticksPerNormalMove-2; i++ {
world.Tick() world.Tick()
info, err := world.GetRover(name) info, err := world.GetRover(name)
assert.NoError(t, err) assert.NoError(t, err)
@ -441,7 +332,7 @@ func TestWorld_Sailing(t *testing.T) {
assert.Equal(t, roveapi.Bearing_South, b) assert.Equal(t, roveapi.Bearing_South, b)
// Tick a bunch, we should never move // Tick a bunch, we should never move
for i := 0; i < TicksPerNormalMove*2; i++ { for i := 0; i < ticksPerNormalMove*2; i++ {
world.Tick() world.Tick()
info, err := world.GetRover(name) info, err := world.GetRover(name)
assert.NoError(t, err) assert.NoError(t, err)

View file

@ -1,9 +1,6 @@
package rove package rove
import ( import (
"encoding/json"
"log"
"github.com/mdiluz/rove/pkg/maths" "github.com/mdiluz/rove/pkg/maths"
"github.com/mdiluz/rove/proto/roveapi" "github.com/mdiluz/rove/proto/roveapi"
"github.com/ojrac/opensimplex-go" "github.com/ojrac/opensimplex-go"
@ -32,8 +29,9 @@ func NewNoiseWorldGen(seed int64) WorldGen {
} }
const ( const (
terrainNoiseScale = 6 terrainNoiseScale = 15
rockNoiseScale = 3 rockNoiseScale = 3
partsNoiseScale = 2
) )
// GetTile returns the chosen tile at a location // GetTile returns the chosen tile at a location
@ -51,43 +49,22 @@ func (g *NoiseWorldGen) GetTile(v maths.Vector) roveapi.Tile {
// GetObject returns the chosen object at a location // GetObject returns the chosen object at a location
func (g *NoiseWorldGen) GetObject(v maths.Vector) (obj Object) { func (g *NoiseWorldGen) GetObject(v maths.Vector) (obj Object) {
o := g.noise.Eval2(float64(v.X)/rockNoiseScale, float64(v.Y)/rockNoiseScale) r := g.noise.Eval2(float64(v.X)/rockNoiseScale, float64(v.Y)/rockNoiseScale)
switch { switch {
case o > 0.6: // Prioritise rocks
case r > 0.6:
obj.Type = roveapi.Object_RockLarge obj.Type = roveapi.Object_RockLarge
case o > 0.5: case r > 0.5:
obj.Type = roveapi.Object_RockSmall obj.Type = roveapi.Object_RockSmall
}
// Very rarely spawn a dormant rover default:
if obj.Type == roveapi.Object_ObjectUnknown { // Otherwise, try some rover parts
// TODO: Make this better, ideally with noise p := g.noise.Eval2(float64(v.X)/partsNoiseScale, float64(v.Y)/partsNoiseScale)
if v.X%25 == 0 && v.Y%25 == 0 && v.X != 0 && v.Y != 0 { switch {
obj.Type = roveapi.Object_RoverDormant case p > 0.7:
obj.Type = roveapi.Object_RoverParts
} }
} }
// Post process any spawned objects
switch obj.Type {
case roveapi.Object_RoverDormant:
// Create the rover
r := DefaultRover()
// Set the rover variables
r.Pos = v
// For now, mark the log as corrupted
r.AddLogEntryf("log corrupted")
// Marshal the rover data into the object data
b, err := json.Marshal(r)
if err != nil {
log.Fatalf("couldn't marshal rover, should never fail: %s", err)
}
// Store the bytes
obj.Data = b
}
return obj return obj
} }

View file

@ -39,35 +39,51 @@ type CommandType int32
const ( const (
CommandType_none CommandType = 0 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 // Toggles the sails, either catching the wind, or charging from the sun
CommandType_toggle CommandType = 1 CommandType_toggle CommandType = 2
// Turns the rover in the specified bearing, requires data // Turns the rover in the specified bearing (requires bearing)
CommandType_turn CommandType = 2 CommandType_turn CommandType = 3
// Stashes item at current location in rover inventory // Stashes item at current location in rover inventory
CommandType_stash CommandType = 3 CommandType_stash CommandType = 4
// Repairs the rover using an inventory object // Repairs the rover using an inventory object
CommandType_repair CommandType = 4 CommandType_repair CommandType = 5
// Broadcasts a message to nearby rovers, requires data // Broadcasts a message to nearby rovers (requires data)
CommandType_broadcast CommandType = 5 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
) )
// Enum value maps for CommandType. // Enum value maps for CommandType.
var ( var (
CommandType_name = map[int32]string{ CommandType_name = map[int32]string{
0: "none", 0: "none",
1: "toggle", 1: "wait",
2: "turn", 2: "toggle",
3: "stash", 3: "turn",
4: "repair", 4: "stash",
5: "broadcast", 5: "repair",
6: "broadcast",
7: "salvage",
8: "transfer",
9: "upgrade",
} }
CommandType_value = map[string]int32{ CommandType_value = map[string]int32{
"none": 0, "none": 0,
"toggle": 1, "wait": 1,
"turn": 2, "toggle": 2,
"stash": 3, "turn": 3,
"repair": 4, "stash": 4,
"broadcast": 5, "repair": 5,
"broadcast": 6,
"salvage": 7,
"transfer": 8,
"upgrade": 9,
} }
) )
@ -167,6 +183,62 @@ func (Bearing) EnumDescriptor() ([]byte, []int) {
return file_roveapi_roveapi_proto_rawDescGZIP(), []int{1} 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 // Types of objects
type Object int32 type Object int32
@ -181,6 +253,9 @@ const (
Object_RockSmall Object = 3 Object_RockSmall Object = 3
// RockLarge is a large blocking rock // RockLarge is a large blocking rock
Object_RockLarge Object = 4 Object_RockLarge Object = 4
// RoverParts is one unit of rover parts, used for repairing and fixing the
// rover
Object_RoverParts Object = 5
) )
// Enum value maps for Object. // Enum value maps for Object.
@ -191,6 +266,7 @@ var (
2: "RoverDormant", 2: "RoverDormant",
3: "RockSmall", 3: "RockSmall",
4: "RockLarge", 4: "RockLarge",
5: "RoverParts",
} }
Object_value = map[string]int32{ Object_value = map[string]int32{
"ObjectUnknown": 0, "ObjectUnknown": 0,
@ -198,6 +274,7 @@ var (
"RoverDormant": 2, "RoverDormant": 2,
"RockSmall": 3, "RockSmall": 3,
"RockLarge": 4, "RockLarge": 4,
"RoverParts": 5,
} }
) )
@ -212,11 +289,11 @@ func (x Object) String() string {
} }
func (Object) Descriptor() protoreflect.EnumDescriptor { func (Object) Descriptor() protoreflect.EnumDescriptor {
return file_roveapi_roveapi_proto_enumTypes[2].Descriptor() return file_roveapi_roveapi_proto_enumTypes[3].Descriptor()
} }
func (Object) Type() protoreflect.EnumType { func (Object) Type() protoreflect.EnumType {
return &file_roveapi_roveapi_proto_enumTypes[2] return &file_roveapi_roveapi_proto_enumTypes[3]
} }
func (x Object) Number() protoreflect.EnumNumber { func (x Object) Number() protoreflect.EnumNumber {
@ -225,7 +302,7 @@ func (x Object) Number() protoreflect.EnumNumber {
// Deprecated: Use Object.Descriptor instead. // Deprecated: Use Object.Descriptor instead.
func (Object) EnumDescriptor() ([]byte, []int) { func (Object) EnumDescriptor() ([]byte, []int) {
return file_roveapi_roveapi_proto_rawDescGZIP(), []int{2} return file_roveapi_roveapi_proto_rawDescGZIP(), []int{3}
} }
type Tile int32 type Tile int32
@ -268,11 +345,11 @@ func (x Tile) String() string {
} }
func (Tile) Descriptor() protoreflect.EnumDescriptor { func (Tile) Descriptor() protoreflect.EnumDescriptor {
return file_roveapi_roveapi_proto_enumTypes[3].Descriptor() return file_roveapi_roveapi_proto_enumTypes[4].Descriptor()
} }
func (Tile) Type() protoreflect.EnumType { func (Tile) Type() protoreflect.EnumType {
return &file_roveapi_roveapi_proto_enumTypes[3] return &file_roveapi_roveapi_proto_enumTypes[4]
} }
func (x Tile) Number() protoreflect.EnumNumber { func (x Tile) Number() protoreflect.EnumNumber {
@ -281,7 +358,7 @@ func (x Tile) Number() protoreflect.EnumNumber {
// Deprecated: Use Tile.Descriptor instead. // Deprecated: Use Tile.Descriptor instead.
func (Tile) EnumDescriptor() ([]byte, []int) { func (Tile) EnumDescriptor() ([]byte, []int) {
return file_roveapi_roveapi_proto_rawDescGZIP(), []int{3} return file_roveapi_roveapi_proto_rawDescGZIP(), []int{4}
} }
// SailPosition represents the position of the sola sail // SailPosition represents the position of the sola sail
@ -320,11 +397,11 @@ func (x SailPosition) String() string {
} }
func (SailPosition) Descriptor() protoreflect.EnumDescriptor { func (SailPosition) Descriptor() protoreflect.EnumDescriptor {
return file_roveapi_roveapi_proto_enumTypes[4].Descriptor() return file_roveapi_roveapi_proto_enumTypes[5].Descriptor()
} }
func (SailPosition) Type() protoreflect.EnumType { func (SailPosition) Type() protoreflect.EnumType {
return &file_roveapi_roveapi_proto_enumTypes[4] return &file_roveapi_roveapi_proto_enumTypes[5]
} }
func (x SailPosition) Number() protoreflect.EnumNumber { func (x SailPosition) Number() protoreflect.EnumNumber {
@ -333,7 +410,7 @@ func (x SailPosition) Number() protoreflect.EnumNumber {
// Deprecated: Use SailPosition.Descriptor instead. // Deprecated: Use SailPosition.Descriptor instead.
func (SailPosition) EnumDescriptor() ([]byte, []int) { func (SailPosition) EnumDescriptor() ([]byte, []int) {
return file_roveapi_roveapi_proto_rawDescGZIP(), []int{4} return file_roveapi_roveapi_proto_rawDescGZIP(), []int{5}
} }
// ServerStatusRequest is an empty placeholder // ServerStatusRequest is an empty placeholder
@ -624,11 +701,15 @@ type Command struct {
// The command type // The command type
Command CommandType `protobuf:"varint,1,opt,name=command,proto3,enum=roveapi.CommandType" json:"command,omitempty"` Command CommandType `protobuf:"varint,1,opt,name=command,proto3,enum=roveapi.CommandType" json:"command,omitempty"`
// A simple message, must be composed of printable ASCII glyphs (32-126) // The number of times to repeat the command after the first
// maximum of three characters Repeat int32 `protobuf:"varint,2,opt,name=repeat,proto3" json:"repeat,omitempty"`
Broadcast []byte `protobuf:"bytes,2,opt,name=broadcast,proto3" json:"broadcast,omitempty"` // broadcast - a simple message, must be composed of up to 3 printable ASCII
// The bearing for the rover to turn to // glyphs (32-126)
Turn Bearing `protobuf:"varint,3,opt,name=turn,proto3,enum=roveapi.Bearing" json:"turn,omitempty"` 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"`
} }
func (x *Command) Reset() { func (x *Command) Reset() {
@ -670,20 +751,34 @@ func (x *Command) GetCommand() CommandType {
return CommandType_none return CommandType_none
} }
func (x *Command) GetBroadcast() []byte { func (x *Command) GetRepeat() int32 {
if x != nil { if x != nil {
return x.Broadcast return x.Repeat
}
return 0
}
func (x *Command) GetData() []byte {
if x != nil {
return x.Data
} }
return nil return nil
} }
func (x *Command) GetTurn() Bearing { func (x *Command) GetBearing() Bearing {
if x != nil { if x != nil {
return x.Turn return x.Bearing
} }
return Bearing_BearingUnknown return Bearing_BearingUnknown
} }
func (x *Command) GetUpgrade() RoverUpgrade {
if x != nil {
return x.Upgrade
}
return RoverUpgrade_RoverUpgradeUnknown
}
// CommandRequest describes a set of commands to be requested for the rover // CommandRequest describes a set of commands to be requested for the rover
type CommandRequest struct { type CommandRequest struct {
state protoimpl.MessageState state protoimpl.MessageState
@ -1397,141 +1492,157 @@ var file_roveapi_roveapi_proto_rawDesc = []byte{
0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, 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, 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, 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, 0x7d, 0x0a, 0x07, 0x43, 0x6f, 0x6d, 0x52, 0x07, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0xc2, 0x01, 0x0a, 0x07, 0x43, 0x6f,
0x6d, 0x61, 0x6e, 0x64, 0x12, 0x2e, 0x0a, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x18, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x2e, 0x0a, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64,
0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x14, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x14, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69,
0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x54, 0x79, 0x70, 0x65, 0x52, 0x07, 0x63, 0x6f, 0x6d, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x54, 0x79, 0x70, 0x65, 0x52, 0x07, 0x63, 0x6f,
0x6d, 0x61, 0x6e, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x62, 0x72, 0x6f, 0x61, 0x64, 0x63, 0x61, 0x73, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x18,
0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x62, 0x72, 0x6f, 0x61, 0x64, 0x63, 0x61, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x12, 0x12, 0x0a,
0x73, 0x74, 0x12, 0x24, 0x0a, 0x04, 0x74, 0x75, 0x72, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74,
0x32, 0x10, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x42, 0x65, 0x61, 0x72, 0x69, 0x61, 0x12, 0x2a, 0x0a, 0x07, 0x62, 0x65, 0x61, 0x72, 0x69, 0x6e, 0x67, 0x18, 0x04, 0x20, 0x01,
0x6e, 0x67, 0x52, 0x04, 0x74, 0x75, 0x72, 0x6e, 0x22, 0x6a, 0x0a, 0x0e, 0x43, 0x6f, 0x6d, 0x6d, 0x28, 0x0e, 0x32, 0x10, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x42, 0x65, 0x61,
0x61, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2a, 0x0a, 0x07, 0x61, 0x63, 0x72, 0x69, 0x6e, 0x67, 0x52, 0x07, 0x62, 0x65, 0x61, 0x72, 0x69, 0x6e, 0x67, 0x12, 0x2f, 0x0a,
0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x72, 0x6f, 0x07, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15,
0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x07, 0x61, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x52, 0x6f, 0x76, 0x65, 0x72, 0x55, 0x70,
0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2c, 0x0a, 0x08, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x67, 0x72, 0x61, 0x64, 0x65, 0x52, 0x07, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x22, 0x6a,
0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x0a, 0x0e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x70, 0x69, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x52, 0x08, 0x63, 0x6f, 0x6d, 0x6d, 0x12, 0x2a, 0x0a, 0x07, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28,
0x61, 0x6e, 0x64, 0x73, 0x22, 0x11, 0x0a, 0x0f, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x52, 0x0b, 0x32, 0x10, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x41, 0x63, 0x63, 0x6f,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3a, 0x0a, 0x0c, 0x52, 0x61, 0x64, 0x61, 0x72, 0x75, 0x6e, 0x74, 0x52, 0x07, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2c, 0x0a, 0x08,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2a, 0x0a, 0x07, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10,
0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64,
0x70, 0x69, 0x2e, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x07, 0x61, 0x63, 0x63, 0x6f, 0x52, 0x08, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x22, 0x11, 0x0a, 0x0f, 0x43, 0x6f,
0x75, 0x6e, 0x74, 0x22, 0x75, 0x0a, 0x0d, 0x52, 0x61, 0x64, 0x61, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3a, 0x0a,
0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x0c, 0x52, 0x61, 0x64, 0x61, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2a, 0x0a,
0x01, 0x28, 0x05, 0x52, 0x05, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x23, 0x0a, 0x05, 0x74, 0x69, 0x07, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10,
0x6c, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x0d, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74,
0x61, 0x70, 0x69, 0x2e, 0x54, 0x69, 0x6c, 0x65, 0x52, 0x05, 0x74, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x52, 0x07, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x75, 0x0a, 0x0d, 0x52, 0x61, 0x64,
0x29, 0x0a, 0x07, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0e, 0x61, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x61,
0x32, 0x0f, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x72, 0x61, 0x6e, 0x67, 0x65,
0x74, 0x52, 0x07, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x22, 0x3b, 0x0a, 0x0d, 0x53, 0x74, 0x12, 0x23, 0x0a, 0x05, 0x74, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0e, 0x32,
0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2a, 0x0a, 0x07, 0x61, 0x0d, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x54, 0x69, 0x6c, 0x65, 0x52, 0x05,
0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x72, 0x74, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x29, 0x0a, 0x07, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73,
0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x07, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x0f, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69,
0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x2d, 0x0a, 0x03, 0x4c, 0x6f, 0x67, 0x12, 0x12, 0x2e, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x07, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73,
0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x69, 0x22, 0x3b, 0x0a, 0x0d, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, 0x78, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x74, 0x12, 0x2a, 0x0a, 0x07, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01,
0x52, 0x04, 0x74, 0x65, 0x78, 0x74, 0x22, 0x24, 0x0a, 0x06, 0x56, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x41, 0x63, 0x63,
0x12, 0x0c, 0x0a, 0x01, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x01, 0x78, 0x12, 0x0c, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x07, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x2d, 0x0a,
0x0a, 0x01, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x01, 0x79, 0x22, 0xad, 0x01, 0x0a, 0x03, 0x4c, 0x6f, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01,
0x13, 0x52, 0x6f, 0x76, 0x65, 0x72, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x28, 0x09, 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, 0x78, 0x74,
0x69, 0x6f, 0x6e, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x65, 0x78, 0x74, 0x22, 0x24, 0x0a, 0x06,
0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x61, 0x6e, 0x67, 0x56, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x0c, 0x0a, 0x01, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28,
0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x1a, 0x05, 0x52, 0x01, 0x78, 0x12, 0x0c, 0x0a, 0x01, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52,
0x0a, 0x08, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x01, 0x79, 0x22, 0xad, 0x01, 0x0a, 0x13, 0x52, 0x6f, 0x76, 0x65, 0x72, 0x53, 0x70, 0x65, 0x63,
0x52, 0x08, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x12, 0x2a, 0x0a, 0x10, 0x6d, 0x61, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61,
0x78, 0x69, 0x6d, 0x75, 0x6d, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x69, 0x74, 0x79, 0x18, 0x04, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14,
0x20, 0x01, 0x28, 0x05, 0x52, 0x10, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x49, 0x6e, 0x74, 0x0a, 0x05, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x72,
0x65, 0x67, 0x72, 0x69, 0x74, 0x79, 0x12, 0x24, 0x0a, 0x0d, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79,
0x6d, 0x43, 0x68, 0x61, 0x72, 0x67, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x6d, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79,
0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x43, 0x68, 0x61, 0x72, 0x67, 0x65, 0x22, 0x82, 0x02, 0x0a, 0x12, 0x2a, 0x0a, 0x10, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x49, 0x6e, 0x74, 0x65, 0x67,
0x0b, 0x52, 0x6f, 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x2a, 0x0a, 0x07, 0x72, 0x69, 0x74, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x10, 0x6d, 0x61, 0x78, 0x69,
0x62, 0x65, 0x61, 0x72, 0x69, 0x6e, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x10, 0x2e, 0x6d, 0x75, 0x6d, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x69, 0x74, 0x79, 0x12, 0x24, 0x0a, 0x0d,
0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x42, 0x65, 0x61, 0x72, 0x69, 0x6e, 0x67, 0x52, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x43, 0x68, 0x61, 0x72, 0x67, 0x65, 0x18, 0x05, 0x20,
0x07, 0x62, 0x65, 0x61, 0x72, 0x69, 0x6e, 0x67, 0x12, 0x39, 0x0a, 0x0c, 0x73, 0x61, 0x69, 0x6c, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x43, 0x68, 0x61, 0x72,
0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x67, 0x65, 0x22, 0x82, 0x02, 0x0a, 0x0b, 0x52, 0x6f, 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74,
0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x61, 0x69, 0x6c, 0x50, 0x6f, 0x73, 0x75, 0x73, 0x12, 0x2a, 0x0a, 0x07, 0x62, 0x65, 0x61, 0x72, 0x69, 0x6e, 0x67, 0x18, 0x01, 0x20,
0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0c, 0x73, 0x61, 0x69, 0x6c, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x01, 0x28, 0x0e, 0x32, 0x10, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x42, 0x65,
0x69, 0x6f, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x69, 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x6f, 0x72, 0x79, 0x61, 0x72, 0x69, 0x6e, 0x67, 0x52, 0x07, 0x62, 0x65, 0x61, 0x72, 0x69, 0x6e, 0x67, 0x12, 0x39,
0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x69, 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x6f, 0x72, 0x0a, 0x0c, 0x73, 0x61, 0x69, 0x6c, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02,
0x79, 0x12, 0x1c, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x69, 0x74, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x53,
0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x69, 0x74, 0x79, 0x12, 0x61, 0x69, 0x6c, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0c, 0x73, 0x61, 0x69,
0x16, 0x0a, 0x06, 0x63, 0x68, 0x61, 0x72, 0x67, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x6c, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x69, 0x6e, 0x76,
0x06, 0x63, 0x68, 0x61, 0x72, 0x67, 0x65, 0x12, 0x38, 0x0a, 0x0e, 0x71, 0x75, 0x65, 0x75, 0x65, 0x65, 0x6e, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x69, 0x6e,
0x64, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x76, 0x65, 0x6e, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x1c, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x65, 0x67,
0x10, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x72, 0x69, 0x74, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x69, 0x6e, 0x74, 0x65,
0x64, 0x52, 0x0e, 0x71, 0x75, 0x65, 0x75, 0x65, 0x64, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x67, 0x72, 0x69, 0x74, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x63, 0x68, 0x61, 0x72, 0x67, 0x65, 0x18,
0x73, 0x22, 0x84, 0x01, 0x0a, 0x0d, 0x52, 0x6f, 0x76, 0x65, 0x72, 0x52, 0x65, 0x61, 0x64, 0x69, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x63, 0x68, 0x61, 0x72, 0x67, 0x65, 0x12, 0x38, 0x0a,
0x6e, 0x67, 0x73, 0x12, 0x2b, 0x0a, 0x08, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0e, 0x71, 0x75, 0x65, 0x75, 0x65, 0x64, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x18,
0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 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, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x52, 0x0e, 0x71, 0x75, 0x65, 0x75, 0x65, 0x64, 0x43,
0x12, 0x24, 0x0a, 0x04, 0x77, 0x69, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x10, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x22, 0x84, 0x01, 0x0a, 0x0d, 0x52, 0x6f, 0x76, 0x65,
0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x42, 0x65, 0x61, 0x72, 0x69, 0x6e, 0x67, 0x72, 0x52, 0x65, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x2b, 0x0a, 0x08, 0x70, 0x6f, 0x73,
0x52, 0x04, 0x77, 0x69, 0x6e, 0x64, 0x12, 0x20, 0x0a, 0x04, 0x6c, 0x6f, 0x67, 0x73, 0x18, 0x03, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x72, 0x6f,
0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x56, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x08, 0x70, 0x6f,
0x6f, 0x67, 0x52, 0x04, 0x6c, 0x6f, 0x67, 0x73, 0x22, 0xa4, 0x01, 0x0a, 0x0e, 0x53, 0x74, 0x61, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x24, 0x0a, 0x04, 0x77, 0x69, 0x6e, 0x64, 0x18, 0x02,
0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x30, 0x0a, 0x04, 0x73, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x10, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x42,
0x70, 0x65, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x65, 0x61, 0x72, 0x69, 0x6e, 0x67, 0x52, 0x04, 0x77, 0x69, 0x6e, 0x64, 0x12, 0x20, 0x0a, 0x04,
0x61, 0x70, 0x69, 0x2e, 0x52, 0x6f, 0x76, 0x65, 0x72, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x6c, 0x6f, 0x67, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x72, 0x6f, 0x76,
0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x12, 0x2c, 0x0a, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x6f, 0x67, 0x52, 0x04, 0x6c, 0x6f, 0x67, 0x73, 0x22, 0xa4,
0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x01, 0x0a, 0x0e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x52, 0x6f, 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, 0x65, 0x12, 0x30, 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32,
0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x32, 0x0a, 0x08, 0x72, 0x1c, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x52, 0x6f, 0x76, 0x65, 0x72, 0x53,
0x65, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x04, 0x73,
0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x52, 0x6f, 0x76, 0x65, 0x72, 0x52, 0x65, 0x61, 0x70, 0x65, 0x63, 0x12, 0x2c, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20,
0x64, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x08, 0x72, 0x65, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x73, 0x2a, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x52, 0x6f,
0x53, 0x0a, 0x0b, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x54, 0x79, 0x70, 0x65, 0x12, 0x08, 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75,
0x0a, 0x04, 0x6e, 0x6f, 0x6e, 0x65, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x74, 0x6f, 0x67, 0x67, 0x73, 0x12, 0x32, 0x0a, 0x08, 0x72, 0x65, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x03, 0x20,
0x6c, 0x65, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x74, 0x75, 0x72, 0x6e, 0x10, 0x02, 0x12, 0x09, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x52, 0x6f,
0x0a, 0x05, 0x73, 0x74, 0x61, 0x73, 0x68, 0x10, 0x03, 0x12, 0x0a, 0x0a, 0x06, 0x72, 0x65, 0x70, 0x76, 0x65, 0x72, 0x52, 0x65, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x08, 0x72, 0x65, 0x61,
0x61, 0x69, 0x72, 0x10, 0x04, 0x12, 0x0d, 0x0a, 0x09, 0x62, 0x72, 0x6f, 0x61, 0x64, 0x63, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x73, 0x2a, 0x85, 0x01, 0x0a, 0x0b, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e,
0x73, 0x74, 0x10, 0x05, 0x2a, 0x83, 0x01, 0x0a, 0x07, 0x42, 0x65, 0x61, 0x72, 0x69, 0x6e, 0x67, 0x64, 0x54, 0x79, 0x70, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x6e, 0x6f, 0x6e, 0x65, 0x10, 0x00, 0x12,
0x12, 0x12, 0x0a, 0x0e, 0x42, 0x65, 0x61, 0x72, 0x69, 0x6e, 0x67, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x08, 0x0a, 0x04, 0x77, 0x61, 0x69, 0x74, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x74, 0x6f, 0x67,
0x77, 0x6e, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x4e, 0x6f, 0x72, 0x74, 0x68, 0x10, 0x01, 0x12, 0x67, 0x6c, 0x65, 0x10, 0x02, 0x12, 0x08, 0x0a, 0x04, 0x74, 0x75, 0x72, 0x6e, 0x10, 0x03, 0x12,
0x0d, 0x0a, 0x09, 0x4e, 0x6f, 0x72, 0x74, 0x68, 0x45, 0x61, 0x73, 0x74, 0x10, 0x02, 0x12, 0x08, 0x09, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x73, 0x68, 0x10, 0x04, 0x12, 0x0a, 0x0a, 0x06, 0x72, 0x65,
0x0a, 0x04, 0x45, 0x61, 0x73, 0x74, 0x10, 0x03, 0x12, 0x0d, 0x0a, 0x09, 0x53, 0x6f, 0x75, 0x74, 0x70, 0x61, 0x69, 0x72, 0x10, 0x05, 0x12, 0x0d, 0x0a, 0x09, 0x62, 0x72, 0x6f, 0x61, 0x64, 0x63,
0x68, 0x45, 0x61, 0x73, 0x74, 0x10, 0x04, 0x12, 0x09, 0x0a, 0x05, 0x53, 0x6f, 0x75, 0x74, 0x68, 0x61, 0x73, 0x74, 0x10, 0x06, 0x12, 0x0b, 0x0a, 0x07, 0x73, 0x61, 0x6c, 0x76, 0x61, 0x67, 0x65,
0x10, 0x05, 0x12, 0x0d, 0x0a, 0x09, 0x53, 0x6f, 0x75, 0x74, 0x68, 0x57, 0x65, 0x73, 0x74, 0x10, 0x10, 0x07, 0x12, 0x0c, 0x0a, 0x08, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x10, 0x08,
0x06, 0x12, 0x08, 0x0a, 0x04, 0x57, 0x65, 0x73, 0x74, 0x10, 0x07, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x12, 0x0b, 0x0a, 0x07, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x10, 0x09, 0x2a, 0x83, 0x01,
0x6f, 0x72, 0x74, 0x68, 0x57, 0x65, 0x73, 0x74, 0x10, 0x08, 0x2a, 0x5a, 0x0a, 0x06, 0x4f, 0x62, 0x0a, 0x07, 0x42, 0x65, 0x61, 0x72, 0x69, 0x6e, 0x67, 0x12, 0x12, 0x0a, 0x0e, 0x42, 0x65, 0x61,
0x6a, 0x65, 0x63, 0x74, 0x12, 0x11, 0x0a, 0x0d, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x55, 0x6e, 0x72, 0x69, 0x6e, 0x67, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x10, 0x00, 0x12, 0x09, 0x0a,
0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x52, 0x6f, 0x76, 0x65, 0x72, 0x05, 0x4e, 0x6f, 0x72, 0x74, 0x68, 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x6f, 0x72, 0x74,
0x4c, 0x69, 0x76, 0x65, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x52, 0x6f, 0x76, 0x65, 0x72, 0x44, 0x68, 0x45, 0x61, 0x73, 0x74, 0x10, 0x02, 0x12, 0x08, 0x0a, 0x04, 0x45, 0x61, 0x73, 0x74, 0x10,
0x6f, 0x72, 0x6d, 0x61, 0x6e, 0x74, 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09, 0x52, 0x6f, 0x63, 0x6b, 0x03, 0x12, 0x0d, 0x0a, 0x09, 0x53, 0x6f, 0x75, 0x74, 0x68, 0x45, 0x61, 0x73, 0x74, 0x10, 0x04,
0x53, 0x6d, 0x61, 0x6c, 0x6c, 0x10, 0x03, 0x12, 0x0d, 0x0a, 0x09, 0x52, 0x6f, 0x63, 0x6b, 0x4c, 0x12, 0x09, 0x0a, 0x05, 0x53, 0x6f, 0x75, 0x74, 0x68, 0x10, 0x05, 0x12, 0x0d, 0x0a, 0x09, 0x53,
0x61, 0x72, 0x67, 0x65, 0x10, 0x04, 0x2a, 0x37, 0x0a, 0x04, 0x54, 0x69, 0x6c, 0x65, 0x12, 0x0f, 0x6f, 0x75, 0x74, 0x68, 0x57, 0x65, 0x73, 0x74, 0x10, 0x06, 0x12, 0x08, 0x0a, 0x04, 0x57, 0x65,
0x0a, 0x0b, 0x54, 0x69, 0x6c, 0x65, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x10, 0x00, 0x12, 0x73, 0x74, 0x10, 0x07, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x6f, 0x72, 0x74, 0x68, 0x57, 0x65, 0x73,
0x08, 0x0a, 0x04, 0x52, 0x6f, 0x63, 0x6b, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x47, 0x72, 0x61, 0x74, 0x10, 0x08, 0x2a, 0x69, 0x0a, 0x0c, 0x52, 0x6f, 0x76, 0x65, 0x72, 0x55, 0x70, 0x67, 0x72,
0x76, 0x65, 0x6c, 0x10, 0x02, 0x12, 0x08, 0x0a, 0x04, 0x53, 0x61, 0x6e, 0x64, 0x10, 0x03, 0x2a, 0x61, 0x64, 0x65, 0x12, 0x17, 0x0a, 0x13, 0x52, 0x6f, 0x76, 0x65, 0x72, 0x55, 0x70, 0x67, 0x72,
0x4c, 0x0a, 0x0c, 0x53, 0x61, 0x69, 0x6c, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x61, 0x64, 0x65, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05,
0x17, 0x0a, 0x13, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x53, 0x61, 0x69, 0x6c, 0x50, 0x6f, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x43, 0x61, 0x70, 0x61, 0x63,
0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x10, 0x00, 0x12, 0x10, 0x0a, 0x0c, 0x43, 0x61, 0x74, 0x63, 0x69, 0x74, 0x79, 0x10, 0x02, 0x12, 0x14, 0x0a, 0x10, 0x4d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d,
0x68, 0x69, 0x6e, 0x67, 0x57, 0x69, 0x6e, 0x64, 0x10, 0x01, 0x12, 0x11, 0x0a, 0x0d, 0x53, 0x6f, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x69, 0x74, 0x79, 0x10, 0x03, 0x12, 0x11, 0x0a, 0x0d, 0x4d,
0x6c, 0x61, 0x72, 0x43, 0x68, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x67, 0x10, 0x02, 0x32, 0xcf, 0x02, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x43, 0x68, 0x61, 0x72, 0x67, 0x65, 0x10, 0x04, 0x2a, 0x6a,
0x0a, 0x04, 0x52, 0x6f, 0x76, 0x65, 0x12, 0x4d, 0x0a, 0x0c, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x0a, 0x06, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x11, 0x0a, 0x0d, 0x4f, 0x62, 0x6a, 0x65,
0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1c, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x63, 0x74, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x52,
0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x6f, 0x76, 0x65, 0x72, 0x4c, 0x69, 0x76, 0x65, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x52, 0x6f,
0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x76, 0x65, 0x72, 0x44, 0x6f, 0x72, 0x6d, 0x61, 0x6e, 0x74, 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09,
0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x52, 0x6f, 0x63, 0x6b, 0x53, 0x6d, 0x61, 0x6c, 0x6c, 0x10, 0x03, 0x12, 0x0d, 0x0a, 0x09, 0x52,
0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x41, 0x0a, 0x08, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x6f, 0x63, 0x6b, 0x4c, 0x61, 0x72, 0x67, 0x65, 0x10, 0x04, 0x12, 0x0e, 0x0a, 0x0a, 0x52, 0x6f,
0x72, 0x12, 0x18, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x76, 0x65, 0x72, 0x50, 0x61, 0x72, 0x74, 0x73, 0x10, 0x05, 0x2a, 0x37, 0x0a, 0x04, 0x54, 0x69,
0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x72, 0x6f, 0x6c, 0x65, 0x12, 0x0f, 0x0a, 0x0b, 0x54, 0x69, 0x6c, 0x65, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77,
0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x6e, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x52, 0x6f, 0x63, 0x6b, 0x10, 0x01, 0x12, 0x0a, 0x0a,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3e, 0x0a, 0x07, 0x43, 0x6f, 0x6d, 0x6d, 0x06, 0x47, 0x72, 0x61, 0x76, 0x65, 0x6c, 0x10, 0x02, 0x12, 0x08, 0x0a, 0x04, 0x53, 0x61, 0x6e,
0x61, 0x6e, 0x64, 0x12, 0x17, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x6f, 0x64, 0x10, 0x03, 0x2a, 0x4c, 0x0a, 0x0c, 0x53, 0x61, 0x69, 0x6c, 0x50, 0x6f, 0x73, 0x69, 0x74,
0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x72, 0x69, 0x6f, 0x6e, 0x12, 0x17, 0x0a, 0x13, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x53, 0x61,
0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x52, 0x65, 0x69, 0x6c, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x10, 0x00, 0x12, 0x10, 0x0a, 0x0c,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x38, 0x0a, 0x05, 0x52, 0x61, 0x64, 0x61, 0x43, 0x61, 0x74, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x57, 0x69, 0x6e, 0x64, 0x10, 0x01, 0x12, 0x11,
0x72, 0x12, 0x15, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x52, 0x61, 0x64, 0x61, 0x0a, 0x0d, 0x53, 0x6f, 0x6c, 0x61, 0x72, 0x43, 0x68, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x67, 0x10,
0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x02, 0x32, 0xcf, 0x02, 0x0a, 0x04, 0x52, 0x6f, 0x76, 0x65, 0x12, 0x4d, 0x0a, 0x0c, 0x53, 0x65,
0x70, 0x69, 0x2e, 0x52, 0x61, 0x64, 0x61, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1c, 0x2e, 0x72, 0x6f, 0x76,
0x22, 0x00, 0x12, 0x3b, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x16, 0x2e, 0x72, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75,
0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61,
0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x70, 0x69, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52,
0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x41, 0x0a, 0x08, 0x52, 0x65, 0x67,
0x26, 0x5a, 0x24, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6d, 0x64, 0x69, 0x73, 0x74, 0x65, 0x72, 0x12, 0x18, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e,
0x69, 0x6c, 0x75, 0x7a, 0x2f, 0x72, 0x6f, 0x76, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 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 ( var (
@ -1546,66 +1657,68 @@ func file_roveapi_roveapi_proto_rawDescGZIP() []byte {
return file_roveapi_roveapi_proto_rawDescData return file_roveapi_roveapi_proto_rawDescData
} }
var file_roveapi_roveapi_proto_enumTypes = make([]protoimpl.EnumInfo, 5) 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_msgTypes = make([]protoimpl.MessageInfo, 17)
var file_roveapi_roveapi_proto_goTypes = []interface{}{ var file_roveapi_roveapi_proto_goTypes = []interface{}{
(CommandType)(0), // 0: roveapi.CommandType (CommandType)(0), // 0: roveapi.CommandType
(Bearing)(0), // 1: roveapi.Bearing (Bearing)(0), // 1: roveapi.Bearing
(Object)(0), // 2: roveapi.Object (RoverUpgrade)(0), // 2: roveapi.RoverUpgrade
(Tile)(0), // 3: roveapi.Tile (Object)(0), // 3: roveapi.Object
(SailPosition)(0), // 4: roveapi.SailPosition (Tile)(0), // 4: roveapi.Tile
(*ServerStatusRequest)(nil), // 5: roveapi.ServerStatusRequest (SailPosition)(0), // 5: roveapi.SailPosition
(*ServerStatusResponse)(nil), // 6: roveapi.ServerStatusResponse (*ServerStatusRequest)(nil), // 6: roveapi.ServerStatusRequest
(*RegisterRequest)(nil), // 7: roveapi.RegisterRequest (*ServerStatusResponse)(nil), // 7: roveapi.ServerStatusResponse
(*Account)(nil), // 8: roveapi.Account (*RegisterRequest)(nil), // 8: roveapi.RegisterRequest
(*RegisterResponse)(nil), // 9: roveapi.RegisterResponse (*Account)(nil), // 9: roveapi.Account
(*Command)(nil), // 10: roveapi.Command (*RegisterResponse)(nil), // 10: roveapi.RegisterResponse
(*CommandRequest)(nil), // 11: roveapi.CommandRequest (*Command)(nil), // 11: roveapi.Command
(*CommandResponse)(nil), // 12: roveapi.CommandResponse (*CommandRequest)(nil), // 12: roveapi.CommandRequest
(*RadarRequest)(nil), // 13: roveapi.RadarRequest (*CommandResponse)(nil), // 13: roveapi.CommandResponse
(*RadarResponse)(nil), // 14: roveapi.RadarResponse (*RadarRequest)(nil), // 14: roveapi.RadarRequest
(*StatusRequest)(nil), // 15: roveapi.StatusRequest (*RadarResponse)(nil), // 15: roveapi.RadarResponse
(*Log)(nil), // 16: roveapi.Log (*StatusRequest)(nil), // 16: roveapi.StatusRequest
(*Vector)(nil), // 17: roveapi.Vector (*Log)(nil), // 17: roveapi.Log
(*RoverSpecifications)(nil), // 18: roveapi.RoverSpecifications (*Vector)(nil), // 18: roveapi.Vector
(*RoverStatus)(nil), // 19: roveapi.RoverStatus (*RoverSpecifications)(nil), // 19: roveapi.RoverSpecifications
(*RoverReadings)(nil), // 20: roveapi.RoverReadings (*RoverStatus)(nil), // 20: roveapi.RoverStatus
(*StatusResponse)(nil), // 21: roveapi.StatusResponse (*RoverReadings)(nil), // 21: roveapi.RoverReadings
(*StatusResponse)(nil), // 22: roveapi.StatusResponse
} }
var file_roveapi_roveapi_proto_depIdxs = []int32{ var file_roveapi_roveapi_proto_depIdxs = []int32{
8, // 0: roveapi.RegisterResponse.account:type_name -> roveapi.Account 9, // 0: roveapi.RegisterResponse.account:type_name -> roveapi.Account
0, // 1: roveapi.Command.command:type_name -> roveapi.CommandType 0, // 1: roveapi.Command.command:type_name -> roveapi.CommandType
1, // 2: roveapi.Command.turn:type_name -> roveapi.Bearing 1, // 2: roveapi.Command.bearing:type_name -> roveapi.Bearing
8, // 3: roveapi.CommandRequest.account:type_name -> roveapi.Account 2, // 3: roveapi.Command.upgrade:type_name -> roveapi.RoverUpgrade
10, // 4: roveapi.CommandRequest.commands:type_name -> roveapi.Command 9, // 4: roveapi.CommandRequest.account:type_name -> roveapi.Account
8, // 5: roveapi.RadarRequest.account:type_name -> roveapi.Account 11, // 5: roveapi.CommandRequest.commands:type_name -> roveapi.Command
3, // 6: roveapi.RadarResponse.tiles:type_name -> roveapi.Tile 9, // 6: roveapi.RadarRequest.account:type_name -> roveapi.Account
2, // 7: roveapi.RadarResponse.objects:type_name -> roveapi.Object 4, // 7: roveapi.RadarResponse.tiles:type_name -> roveapi.Tile
8, // 8: roveapi.StatusRequest.account:type_name -> roveapi.Account 3, // 8: roveapi.RadarResponse.objects:type_name -> roveapi.Object
1, // 9: roveapi.RoverStatus.bearing:type_name -> roveapi.Bearing 9, // 9: roveapi.StatusRequest.account:type_name -> roveapi.Account
4, // 10: roveapi.RoverStatus.sailPosition:type_name -> roveapi.SailPosition 1, // 10: roveapi.RoverStatus.bearing:type_name -> roveapi.Bearing
10, // 11: roveapi.RoverStatus.queuedCommands:type_name -> roveapi.Command 5, // 11: roveapi.RoverStatus.sailPosition:type_name -> roveapi.SailPosition
17, // 12: roveapi.RoverReadings.position:type_name -> roveapi.Vector 11, // 12: roveapi.RoverStatus.queuedCommands:type_name -> roveapi.Command
1, // 13: roveapi.RoverReadings.wind:type_name -> roveapi.Bearing 18, // 13: roveapi.RoverReadings.position:type_name -> roveapi.Vector
16, // 14: roveapi.RoverReadings.logs:type_name -> roveapi.Log 1, // 14: roveapi.RoverReadings.wind:type_name -> roveapi.Bearing
18, // 15: roveapi.StatusResponse.spec:type_name -> roveapi.RoverSpecifications 17, // 15: roveapi.RoverReadings.logs:type_name -> roveapi.Log
19, // 16: roveapi.StatusResponse.status:type_name -> roveapi.RoverStatus 19, // 16: roveapi.StatusResponse.spec:type_name -> roveapi.RoverSpecifications
20, // 17: roveapi.StatusResponse.readings:type_name -> roveapi.RoverReadings 20, // 17: roveapi.StatusResponse.status:type_name -> roveapi.RoverStatus
5, // 18: roveapi.Rove.ServerStatus:input_type -> roveapi.ServerStatusRequest 21, // 18: roveapi.StatusResponse.readings:type_name -> roveapi.RoverReadings
7, // 19: roveapi.Rove.Register:input_type -> roveapi.RegisterRequest 6, // 19: roveapi.Rove.ServerStatus:input_type -> roveapi.ServerStatusRequest
11, // 20: roveapi.Rove.Command:input_type -> roveapi.CommandRequest 8, // 20: roveapi.Rove.Register:input_type -> roveapi.RegisterRequest
13, // 21: roveapi.Rove.Radar:input_type -> roveapi.RadarRequest 12, // 21: roveapi.Rove.Command:input_type -> roveapi.CommandRequest
15, // 22: roveapi.Rove.Status:input_type -> roveapi.StatusRequest 14, // 22: roveapi.Rove.Radar:input_type -> roveapi.RadarRequest
6, // 23: roveapi.Rove.ServerStatus:output_type -> roveapi.ServerStatusResponse 16, // 23: roveapi.Rove.Status:input_type -> roveapi.StatusRequest
9, // 24: roveapi.Rove.Register:output_type -> roveapi.RegisterResponse 7, // 24: roveapi.Rove.ServerStatus:output_type -> roveapi.ServerStatusResponse
12, // 25: roveapi.Rove.Command:output_type -> roveapi.CommandResponse 10, // 25: roveapi.Rove.Register:output_type -> roveapi.RegisterResponse
14, // 26: roveapi.Rove.Radar:output_type -> roveapi.RadarResponse 13, // 26: roveapi.Rove.Command:output_type -> roveapi.CommandResponse
21, // 27: roveapi.Rove.Status:output_type -> roveapi.StatusResponse 15, // 27: roveapi.Rove.Radar:output_type -> roveapi.RadarResponse
23, // [23:28] is the sub-list for method output_type 22, // 28: roveapi.Rove.Status:output_type -> roveapi.StatusResponse
18, // [18:23] is the sub-list for method input_type 24, // [24:29] is the sub-list for method output_type
18, // [18:18] is the sub-list for extension type_name 19, // [19:24] is the sub-list for method input_type
18, // [18:18] is the sub-list for extension extendee 19, // [19:19] is the sub-list for extension type_name
0, // [0:18] is the sub-list for field type_name 19, // [19:19] is the sub-list for extension extendee
0, // [0:19] is the sub-list for field type_name
} }
func init() { file_roveapi_roveapi_proto_init() } func init() { file_roveapi_roveapi_proto_init() }
@ -1824,7 +1937,7 @@ func file_roveapi_roveapi_proto_init() {
File: protoimpl.DescBuilder{ File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(), GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_roveapi_roveapi_proto_rawDesc, RawDescriptor: file_roveapi_roveapi_proto_rawDesc,
NumEnums: 5, NumEnums: 6,
NumMessages: 17, NumMessages: 17,
NumExtensions: 0, NumExtensions: 0,
NumServices: 1, NumServices: 1,

View file

@ -89,16 +89,24 @@ message RegisterResponse {
// CommandType defines the type of a command to give to the rover // CommandType defines the type of a command to give to the rover
enum CommandType { enum CommandType {
none = 0; none = 0;
// Waits before performing the next command
wait = 1;
// Toggles the sails, either catching the wind, or charging from the sun // Toggles the sails, either catching the wind, or charging from the sun
toggle = 1; toggle = 2;
// Turns the rover in the specified bearing, requires data // Turns the rover in the specified bearing (requires bearing)
turn = 2; turn = 3;
// Stashes item at current location in rover inventory // Stashes item at current location in rover inventory
stash = 3; stash = 4;
// Repairs the rover using an inventory object // Repairs the rover using an inventory object
repair = 4; repair = 5;
// Broadcasts a message to nearby rovers, requires data // Broadcasts a message to nearby rovers (requires data)
broadcast = 5; 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;
} }
// Bearing represents a compass direction // Bearing represents a compass direction
@ -115,17 +123,32 @@ enum Bearing {
NorthWest = 8; NorthWest = 8;
} }
// Describes the type of upgrade
enum RoverUpgrade {
RoverUpgradeUnknown = 0;
Range = 1;
Capacity = 2;
MaximumIntegrity = 3;
MaximumCharge = 4;
}
// Command is a single command for a rover // Command is a single command for a rover
message Command { message Command {
// The command type // The command type
CommandType command = 1; CommandType command = 1;
// A simple message, must be composed of printable ASCII glyphs (32-126) // The number of times to repeat the command after the first
// maximum of three characters int32 repeat = 2;
bytes broadcast = 2;
// The bearing for the rover to turn to // broadcast - a simple message, must be composed of up to 3 printable ASCII
Bearing turn = 3; // 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;
} }
// CommandRequest describes a set of commands to be requested for the rover // CommandRequest describes a set of commands to be requested for the rover
@ -160,6 +183,10 @@ enum Object {
// RockLarge is a large blocking rock // RockLarge is a large blocking rock
RockLarge = 4; RockLarge = 4;
// RoverParts is one unit of rover parts, used for repairing and fixing the
// rover
RoverParts = 5;
} }
enum Tile { enum Tile {