diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml deleted file mode 100644 index 20dc273..0000000 --- a/.github/workflows/docker-image.yml +++ /dev/null @@ -1,45 +0,0 @@ -name: Build and Test - -on: - push: - branches: [ master ] - pull_request: - branches: [ master ] - -jobs: - - build: - name: Build and Test - runs-on: ubuntu-latest - - steps: - - name: Set up Go 1.x - uses: actions/setup-go@v2 - with: - go-version: ^1.13 - id: go - - - name: Install swagger - run: | - sudo curl -o /usr/local/bin/swagger -L'#' https://github.com/go-swagger/go-swagger/releases/download/v0.23.0/swagger_linux_amd64 - sudo chmod +x /usr/local/bin/swagger - - - name: Check out repo - uses: actions/checkout@v2 - - - name: Get go dependencies - run: | - go get -v -t -d ./... - if [ -f Gopkg.toml ]; then - curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh - dep ensure - fi - - - name: Build and Test - run: make test - - - name: Upload test coverage result - uses: actions/upload-artifact@v1 - with: - name: Coverage - path: /tmp/coverage.html diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 0000000..2126419 --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,25 @@ +name: Docker + +on: + push: + branches: [ master ] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + - name: Docker Login + uses: azure/docker-login@v1 + with: + login-server: docker.pkg.github.com + username: $GITHUB_ACTOR + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build the Docker image + run: | + VERSION=$(git describe --always --long --dirty --tags) + docker build . --tag docker.pkg.github.com/mdiluz/rove/rove:$VERSION --tag docker.pkg.github.com/mdiluz/rove/rove:latest + docker push docker.pkg.github.com/mdiluz/rove/rove:$VERSION + docker push docker.pkg.github.com/mdiluz/rove/rove:latest diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..82092a8 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,44 @@ +name: Tests + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + + build: + name: Build and Test + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Run golangci-lint + uses: actions-contrib/golangci-lint@v1 + + - name: Set up Go 1.x + uses: actions/setup-go@v2 + with: + go-version: ^1.13 + id: go + + - name: Check generated files + run: | + PROTOC_ZIP=protoc-3.6.1-linux-x86_64.zip + curl -OL https://github.com/protocolbuffers/protobuf/releases/download/v3.6.1/$PROTOC_ZIP + sudo unzip -o $PROTOC_ZIP -d /usr/local bin/protoc + sudo unzip -o $PROTOC_ZIP -d /usr/local 'include/*' + sudo chmod -R o+r /usr/local/include/google/ + rm -f $PROTOC_ZIP + make gen + git update-index --refresh || (git diff; exit 1) + + - name: Build and Test + run: make test + + - name: Upload test coverage result + uses: actions/upload-artifact@v1 + with: + name: Coverage + path: /tmp/coverage.html diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100644 index 539f842..0000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "name": "Launch Rove Server", - "type": "go", - "request": "launch", - "mode": "auto", - "program": "${workspaceFolder}/cmd/rove-server/main.go", - "cwd": "${workspaceFolder}", - "env": { - "WORDS_FILE": "${workspaceFolder}/data/words_alpha.txt", - }, - "args": [], - }, - { - "name": "Launch Rove", - "type": "go", - "request": "launch", - "mode": "auto", - "program": "${workspaceFolder}/cmd/rove/main.go", - "cwd": "${workspaceFolder}", - "env": {}, - "args": ["radar"], - } - ] -} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index b3be4a5..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "gopls": { - "buildFlags": ["-tags=integration"] - }, -} \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 7c3e6c1..74b87c7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,9 +6,8 @@ COPY . . RUN go mod download # Build the executables +RUN go build -o rove -ldflags="-X 'github.com/mdiluz/rove/pkg/version.Version=$(git describe --always --long --dirty --tags)'" cmd/rove/main.go RUN go build -o rove-server -ldflags="-X 'github.com/mdiluz/rove/pkg/version.Version=$(git describe --always --long --dirty --tags)'" cmd/rove-server/main.go -RUN go build -o rove-accountant cmd/rove-accountant/main.go -RUN go build -o rove-reverse-proxy cmd/rove-reverse-proxy/main.go CMD [ "./rove-server" ] diff --git a/Dockerfile.docs b/Dockerfile.docs deleted file mode 100644 index c74f94b..0000000 --- a/Dockerfile.docs +++ /dev/null @@ -1,8 +0,0 @@ -FROM quay.io/goswagger/swagger:latest -LABEL maintainer="Marc Di Luzio " - -WORKDIR /app -COPY . . - -CMD [ "serve", "pkg/rove/rove.swagger.json", "--no-open" ] - diff --git a/Makefile b/Makefile index 31d0ab3..3aedd83 100644 --- a/Makefile +++ b/Makefile @@ -1,28 +1,26 @@ VERSION := $(shell git describe --always --long --dirty --tags) build: + @echo Running no-output build go mod download - go build -ldflags="-X 'github.com/mdiluz/rove/pkg/version.Version=${VERSION}'" ./... + go build -ldflags="-X 'github.com/mdiluz/rove/cmd/version.Version=${VERSION}'" ./... install: + @echo Installing to GOPATH go mod download go install -ldflags="-X 'github.com/mdiluz/rove/pkg/version.Version=${VERSION}'" ./... gen: - @echo Generating accountant gRPC - protoc --proto_path proto --go_out=plugins=grpc:pkg/ --go_opt=paths=source_relative proto/accounts/accounts.proto + @echo Installing go dependencies + go install github.com/golang/protobuf/protoc-gen-go + go mod download @echo Generating rove server gRPC - protoc --proto_path proto --go_out=plugins=grpc:pkg/ --go_opt=paths=source_relative proto/rove/rove.proto - protoc --proto_path proto --grpc-gateway_out=paths=source_relative:pkg/ proto/rove/rove.proto - protoc --proto_path proto --swagger_out=logtostderr=true:pkg/ proto/rove/rove.proto + protoc --proto_path proto --go_out=plugins=grpc,paths=source_relative:proto/ proto/roveapi/roveapi.proto test: - @echo Unit tests - go test -v ./... - - @echo Integration tests - docker-compose up --build --exit-code-from=rove-tests --abort-on-container-exit rove-tests - docker-compose down + @echo Run unit and integration tests + docker-compose -f docker-compose-test.yml up --build --exit-code-from=rove-tests --abort-on-container-exit rove-tests + docker-compose -f docker-compose-test.yml down go tool cover -html=/tmp/coverage-data/c.out -o /tmp/coverage.html @echo Done, coverage data can be found in /tmp/coverage.html diff --git a/README.md b/README.md index fa4b149..aad1a6f 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,8 @@ Rove ==== +![Rove](data/icon.svg) + Rove is an asynchronous nomadic game about exploring as part of a loose community. -This repository is a [living document](https://github.com/mdiluz/rove/tree/master/docs) of current game design, as well as source code for the `rove-server` deployment and the `rove` command line client. - -See [api.go](https://github.com/mdiluz/rove/blob/master/pkg/rove/api.go) for the current server-client API. - -Build Status ------------- -![Build and Test](https://github.com/mdiluz/rove/workflows/Build%20and%20Test/badge.svg) [![rove](https://snapcraft.io//rove/badge.svg)](https://snapcraft.io/rove) - +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. diff --git a/cmd/rove-accountant/internal/accounts.go b/cmd/rove-accountant/internal/accounts.go deleted file mode 100644 index 01e29dc..0000000 --- a/cmd/rove-accountant/internal/accounts.go +++ /dev/null @@ -1,80 +0,0 @@ -package internal - -import ( - "fmt" - "time" -) - -const kAccountsFileName = "rove-accounts.json" - -// Account represents a registered user -type Account struct { - // Name simply describes the account and must be unique - Name string `json:"name"` - - // Data represents internal account data - Data map[string]string `json:"data"` -} - -// Represents the accountant data to store -type accountantData struct { -} - -// Accountant manages a set of accounts -type Accountant struct { - Accounts map[string]Account `json:"accounts"` -} - -// NewAccountant creates a new accountant -func NewAccountant() *Accountant { - return &Accountant{ - Accounts: make(map[string]Account), - } -} - -// RegisterAccount adds an account to the set of internal accounts -func (a *Accountant) RegisterAccount(name string) (acc Account, err error) { - - // Set the account name - acc.Name = name - acc.Data = make(map[string]string) - - // Verify this acount isn't already registered - for _, a := range a.Accounts { - if a.Name == acc.Name { - return Account{}, fmt.Errorf("account name already registered: %s", a.Name) - } - } - - // Set the creation time - acc.Data["created"] = time.Now().String() - - // Simply add the account to the map - a.Accounts[acc.Name] = acc - - return -} - -// AssignRover assigns data to an account -func (a *Accountant) AssignData(account string, key string, value string) error { - - // Find the account matching the ID - if this, ok := a.Accounts[account]; ok { - this.Data[key] = value - a.Accounts[account] = this - } else { - return fmt.Errorf("no account found for id: %s", account) - } - - return nil -} - -// GetRover gets the rover rover for the account -func (a *Accountant) GetValue(account string, key string) (string, error) { - // Find the account matching the ID - if this, ok := a.Accounts[account]; !ok { - return "", fmt.Errorf("no account found for id: %s", account) - } else { - return this.Data[key], nil - } -} diff --git a/cmd/rove-accountant/main.go b/cmd/rove-accountant/main.go deleted file mode 100644 index 31f2914..0000000 --- a/cmd/rove-accountant/main.go +++ /dev/null @@ -1,135 +0,0 @@ -package main - -import ( - "context" - "fmt" - "log" - "net" - "os" - "os/signal" - "strconv" - "sync" - "syscall" - - "github.com/mdiluz/rove/cmd/rove-accountant/internal" - "github.com/mdiluz/rove/pkg/accounts" - "github.com/mdiluz/rove/pkg/persistence" - "google.golang.org/grpc" -) - -var data = os.Getenv("DATA_PATH") - -// accountantServer is the internal object to manage the requests -type accountantServer struct { - accountant *internal.Accountant - sync sync.RWMutex -} - -// Register will register an account -func (a *accountantServer) Register(ctx context.Context, in *accounts.RegisterInfo) (*accounts.RegisterResponse, error) { - a.sync.Lock() - defer a.sync.Unlock() - - // Try and register the account itself - log.Printf("Registering account: %s\n", in.Name) - if _, err := a.accountant.RegisterAccount(in.Name); err != nil { - log.Printf("Error: %s\n", err) - return nil, err - } - - // Save out the accounts - if err := persistence.Save("accounts", a.accountant); err != nil { - log.Printf("Error: %s\n", err) - return nil, err - } - - return &accounts.RegisterResponse{}, nil -} - -// AssignData assigns a key value pair to an account -func (a *accountantServer) AssignValue(_ context.Context, in *accounts.DataKeyValue) (*accounts.DataKeyResponse, error) { - a.sync.RLock() - defer a.sync.RUnlock() - - // Try and assign the data - log.Printf("Assigning value for account %s: %s->%s\n", in.Account, in.Key, in.Value) - err := a.accountant.AssignData(in.Account, in.Key, in.Value) - if err != nil { - log.Printf("Error: %s\n", err) - return nil, err - } - - return &accounts.DataKeyResponse{}, nil - -} - -// GetData gets the value for a key -func (a *accountantServer) GetValue(_ context.Context, in *accounts.DataKey) (*accounts.DataResponse, error) { - a.sync.RLock() - defer a.sync.RUnlock() - - // Try and fetch the rover - log.Printf("Getting value for account %s: %s\n", in.Account, in.Key) - data, err := a.accountant.GetValue(in.Account, in.Key) - if err != nil { - log.Printf("Error: %s\n", err) - return nil, err - } - - return &accounts.DataResponse{Value: data}, nil - -} - -// main -func main() { - // Get the port - var iport int - var port = os.Getenv("PORT") - if len(port) == 0 { - iport = 9091 - } else { - var err error - iport, err = strconv.Atoi(port) - if err != nil { - log.Fatal("$PORT not valid int") - } - } - - persistence.SetPath(data) - - // Initialise and load the accountant - accountant := internal.NewAccountant() - if err := persistence.Load("accounts", accountant); err != nil { - log.Fatalf("failed to load account data: %s", err) - } - - // Set up the RPC server and register - lis, err := net.Listen("tcp", fmt.Sprintf(":%d", iport)) - if err != nil { - log.Fatalf("failed to listen: %v", err) - } - grpcServer := grpc.NewServer() - accounts.RegisterAccountantServer(grpcServer, &accountantServer{ - accountant: accountant, - }) - - // Set up the close handler - c := make(chan os.Signal) - signal.Notify(c, os.Interrupt, syscall.SIGTERM) - go func() { - <-c - log.Println("Quit requested, exiting...") - grpcServer.Stop() - }() - - // Serve the RPC server - log.Printf("Serving accountant on %s\n", port) - if err := grpcServer.Serve(lis); err != nil { - log.Fatalf("failed to serve gRPC: %s", err) - } - - // Save out the accountant data - if err := persistence.Save("accounts", accountant); err != nil { - log.Fatalf("failed to save accounts: %s", err) - } -} diff --git a/cmd/rove-reverse-proxy/http_test.go b/cmd/rove-reverse-proxy/http_test.go deleted file mode 100644 index 995c380..0000000 --- a/cmd/rove-reverse-proxy/http_test.go +++ /dev/null @@ -1,127 +0,0 @@ -// +build integration - -package main - -import ( - "bytes" - "encoding/json" - "fmt" - "io/ioutil" - "log" - "net/http" - "net/url" - "os" - "testing" - - "github.com/google/uuid" - "github.com/mdiluz/rove/pkg/rove" - "github.com/stretchr/testify/assert" -) - -// Server is a simple wrapper to a server path -type Server string - -// Request performs a HTTP -func (s Server) Request(method, path string, in, out interface{}) error { - u := url.URL{ - Scheme: "http", - Host: fmt.Sprintf("%s:8080", string(s)), - Path: path, - } - client := &http.Client{} - - // Marshal the input - marshalled, err := json.Marshal(in) - if err != nil { - return err - } - - // Set up the request - req, err := http.NewRequest(method, u.String(), bytes.NewReader(marshalled)) - if err != nil { - return err - } - - // Do the POST - req.Header.Set("Content-Type", "application/json") - if resp, err := client.Do(req); err != nil { - return err - - } else if resp.StatusCode != http.StatusOK { - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - return fmt.Errorf("failed to read response body to code %d", resp.StatusCode) - } - return fmt.Errorf("http returned status %d: %s", resp.StatusCode, string(body)) - - } else { - return json.NewDecoder(resp.Body).Decode(out) - } -} - -var serv = Server(os.Getenv("ROVE_HTTP")) - -func TestServer_Status(t *testing.T) { - req := &rove.StatusRequest{} - resp := &rove.StatusResponse{} - if err := serv.Request("GET", "status", req, resp); err != nil { - log.Fatal(err) - } -} - -func TestServer_Register(t *testing.T) { - req := &rove.RegisterRequest{Name: uuid.New().String()} - resp := &rove.RegisterResponse{} - err := serv.Request("POST", "register", req, resp) - assert.NoError(t, err, "First register attempt should pass") - err = serv.Request("POST", "register", req, resp) - assert.Error(t, err, "Second identical register attempt should fail") -} - -func TestServer_Command(t *testing.T) { - acc := uuid.New().String() - err := serv.Request("POST", "register", &rove.RegisterRequest{Name: acc}, &rove.RegisterResponse{}) - assert.NoError(t, err, "First register attempt should pass") - - err = serv.Request("POST", "commands", &rove.CommandsRequest{ - Account: acc, - Commands: []*rove.Command{ - { - Command: "move", - Bearing: "NE", - }, - }, - }, &rove.CommandsResponse{}) - assert.NoError(t, err, "Commands should should pass") -} - -func TestServer_Radar(t *testing.T) { - acc := uuid.New().String() - err := serv.Request("POST", "register", &rove.RegisterRequest{Name: acc}, &rove.RegisterResponse{}) - assert.NoError(t, err, "First register attempt should pass") - - resp := &rove.RadarResponse{} - err = serv.Request("POST", "radar", &rove.RadarRequest{ - Account: acc, - }, resp) - assert.NoError(t, err, "Radar sould pass should pass") - assert.NotZero(t, resp.Range, "Radar should return valid range") - w := int(resp.Range*2 + 1) - assert.Equal(t, w*w, len(resp.Tiles), "radar should return correct number of tiles") -} - -func TestServer_Rover(t *testing.T) { - acc := uuid.New().String() - err := serv.Request("POST", "register", &rove.RegisterRequest{Name: acc}, &rove.RegisterResponse{}) - assert.NoError(t, err, "First register attempt should pass") - - resp := &rove.RoverResponse{} - err = serv.Request("POST", "rover", &rove.RoverRequest{ - Account: acc, - }, resp) - assert.NoError(t, err, "Rover sould pass should pass") - assert.NotZero(t, resp.Range, "Rover should return valid range") - assert.NotZero(t, len(resp.Name), "Rover should return valid name") - assert.NotZero(t, resp.Position, "Rover should return valid position") - assert.NotZero(t, resp.Integrity, "Rover should have positive integrity") -} diff --git a/cmd/rove-reverse-proxy/main.go b/cmd/rove-reverse-proxy/main.go deleted file mode 100644 index 06653b6..0000000 --- a/cmd/rove-reverse-proxy/main.go +++ /dev/null @@ -1,51 +0,0 @@ -package main - -import ( - "context" - "fmt" - "log" - "net/http" - "os" - "strconv" - - "github.com/grpc-ecosystem/grpc-gateway/runtime" - "google.golang.org/grpc" - - "github.com/mdiluz/rove/pkg/rove" -) - -func main() { - ctx := context.Background() - ctx, cancel := context.WithCancel(ctx) - defer cancel() - - var endpoint = os.Getenv("ROVE_GRPC") - if len(endpoint) == 0 { - endpoint = "localhost:9090" - } - - var iport int - var port = os.Getenv("PORT") - if len(port) == 0 { - iport = 8080 - } else { - var err error - iport, err = strconv.Atoi(port) - if err != nil { - log.Fatal("$PORT not valid int") - } - } - - // Create a new mux and register it with the gRPC endpoint - fmt.Printf("Hosting reverse-proxy on %d for %s\n", iport, endpoint) - mux := runtime.NewServeMux() - opts := []grpc.DialOption{grpc.WithInsecure()} - if err := rove.RegisterRoveHandlerFromEndpoint(ctx, mux, endpoint, opts); err != nil { - log.Fatal(err) - } - - // Start the HTTP server and proxy calls to gRPC endpoint when needed - if err := http.ListenAndServe(fmt.Sprintf(":%d", iport), mux); err != nil { - log.Fatal(err) - } -} diff --git a/cmd/rove-server/internal/routes.go b/cmd/rove-server/internal/routes.go index f558f00..69dd820 100644 --- a/cmd/rove-server/internal/routes.go +++ b/cmd/rove-server/internal/routes.go @@ -3,23 +3,21 @@ package internal import ( "context" "fmt" + "log" - "github.com/mdiluz/rove/pkg/accounts" - "github.com/mdiluz/rove/pkg/game" - "github.com/mdiluz/rove/pkg/rove" "github.com/mdiluz/rove/pkg/version" - "google.golang.org/grpc" + "github.com/mdiluz/rove/proto/roveapi" ) -func (s *Server) Status(context.Context, *rove.StatusRequest) (*rove.StatusResponse, error) { - response := &rove.StatusResponse{ - Ready: true, - Version: version.Version, - Tick: int32(s.tick), +// ServerStatus returns the status of the current server to a gRPC request +func (s *Server) ServerStatus(context.Context, *roveapi.ServerStatusRequest) (*roveapi.ServerStatusResponse, error) { + response := &roveapi.ServerStatusResponse{ + Ready: true, + Version: version.Version, + TickRate: int32(s.minutesPerTick), + CurrentTick: int32(s.world.CurrentTicks), } - // TODO: Verify the accountant is up and ready too - // If there's a schedule, respond with it if len(s.schedule.Entries()) > 0 { response.NextTick = s.schedule.Entries()[0].Next.Format("15:04:05") @@ -28,12 +26,15 @@ func (s *Server) Status(context.Context, *rove.StatusRequest) (*rove.StatusRespo return response, nil } -func (s *Server) Register(ctx context.Context, req *rove.RegisterRequest) (*rove.RegisterResponse, error) { +// Register registers a new account for a gRPC request +func (s *Server) Register(ctx context.Context, req *roveapi.RegisterRequest) (*roveapi.RegisterResponse, error) { + log.Printf("Handling register request: %s\n", req.Name) + if len(req.Name) == 0 { return nil, fmt.Errorf("empty account name") } - if _, err := s.accountant.Register(ctx, &accounts.RegisterInfo{Name: req.Name}, grpc.WaitForReady(true)); err != nil { + if acc, err := s.world.Accountant.RegisterAccount(req.Name); err != nil { return nil, err } else if _, err := s.SpawnRoverForAccount(req.Name); err != nil { @@ -41,55 +42,102 @@ func (s *Server) Register(ctx context.Context, req *rove.RegisterRequest) (*rove } else if err := s.SaveWorld(); err != nil { return nil, fmt.Errorf("internal server error when saving world: %s", err) - } - return &rove.RegisterResponse{}, nil + } else { + return &roveapi.RegisterResponse{ + Account: &roveapi.Account{ + Name: acc.Name, + Secret: acc.Data["secret"], + }, + }, nil + } } -func (s *Server) Rover(ctx context.Context, req *rove.RoverRequest) (*rove.RoverResponse, error) { - response := &rove.RoverResponse{} - if len(req.Account) == 0 { - return nil, fmt.Errorf("empty account name") +// Status returns rover information for a gRPC request +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) - } else if resp, err := s.accountant.GetValue(ctx, &accounts.DataKey{Account: req.Account, Key: "rover"}); err != nil { - return nil, fmt.Errorf("gRPC failed to contact accountant: %s", err) + if valid, err := s.world.Accountant.VerifySecret(req.Account.Name, req.Account.Secret); err != nil { + return nil, err - } else if rover, err := s.world.GetRover(resp.Value); err != nil { + } else if !valid { + return nil, fmt.Errorf("Secret incorrect for account %s", req.Account.Name) + + } else if resp, err := s.world.Accountant.GetValue(req.Account.Name, "rover"); err != nil { + return nil, err + + } else if rover, err := s.world.GetRover(resp); err != nil { return nil, fmt.Errorf("error getting rover: %s", err) } else { - response = &rove.RoverResponse{ - Name: rover.Name, - Position: &rove.Vector{ - X: int32(rover.Pos.X), - Y: int32(rover.Pos.Y), + var inv []byte + for _, i := range rover.Inventory { + inv = append(inv, byte(i.Type)) + } + + queued := s.world.RoverCommands(resp) + var logs []*roveapi.Log + for _, log := range rover.Logs { + logs = append(logs, &roveapi.Log{ + Text: log.Text, + Time: fmt.Sprintf("%d", log.Time.Unix()), // proto uses strings under the hood for 64bit ints anyway + }) + } + + response = &roveapi.StatusResponse{ + Readings: &roveapi.RoverReadings{ + Position: &roveapi.Vector{ + X: int32(rover.Pos.X), + Y: int32(rover.Pos.Y), + }, + Logs: logs, + Wind: s.world.Wind, + }, + Spec: &roveapi.RoverSpecifications{ + Name: rover.Name, + Range: int32(rover.Range), + Capacity: int32(rover.Capacity), + MaximumIntegrity: int32(rover.MaximumIntegrity), + MaximumCharge: int32(rover.MaximumCharge), + }, + Status: &roveapi.RoverStatus{ + Bearing: rover.Bearing, + Inventory: inv, + Integrity: int32(rover.Integrity), + Charge: int32(rover.Charge), + QueuedCommands: queued, + SailPosition: rover.SailPosition, }, - Range: int32(rover.Range), - Inventory: rover.Inventory, - Integrity: int32(rover.Integrity), } } return response, nil } -func (s *Server) Radar(ctx context.Context, req *rove.RadarRequest) (*rove.RadarResponse, error) { - if len(req.Account) == 0 { - return nil, fmt.Errorf("empty account name") +// Radar returns the radar information for a rover +func (s *Server) Radar(ctx context.Context, req *roveapi.RadarRequest) (*roveapi.RadarResponse, error) { + log.Printf("Handling radar request: %s\n", req.Account.Name) + + if valid, err := s.world.Accountant.VerifySecret(req.Account.Name, req.Account.Secret); err != nil { + return nil, err + + } else if !valid { + return nil, fmt.Errorf("Secret incorrect for account %s", req.Account.Name) } - response := &rove.RadarResponse{} + response := &roveapi.RadarResponse{} - resp, err := s.accountant.GetValue(ctx, &accounts.DataKey{Account: req.Account, Key: "rover"}) + resp, err := s.world.Accountant.GetValue(req.Account.Name, "rover") if err != nil { - return nil, fmt.Errorf("gRPC failed to contact accountant: %s", err) + return nil, err - } else if rover, err := s.world.GetRover(resp.Value); err != nil { + } else if rover, err := s.world.GetRover(resp); err != nil { return nil, fmt.Errorf("error getting rover attributes: %s", err) - } else if radar, err := s.world.RadarFromRover(resp.Value); err != nil { + } else if radar, objs, err := s.world.RadarFromRover(resp); err != nil { return nil, fmt.Errorf("error getting radar from rover: %s", err) } else { + response.Objects = objs response.Tiles = radar response.Range = int32(rover.Range) } @@ -97,26 +145,25 @@ func (s *Server) Radar(ctx context.Context, req *rove.RadarRequest) (*rove.Radar return response, nil } -func (s *Server) Commands(ctx context.Context, req *rove.CommandsRequest) (*rove.CommandsResponse, error) { - if len(req.Account) == 0 { - return nil, fmt.Errorf("empty account") - } - resp, err := s.accountant.GetValue(ctx, &accounts.DataKey{Account: req.Account, Key: "rover"}) +// Command issues commands to the world based on a gRPC request +func (s *Server) Command(ctx context.Context, req *roveapi.CommandRequest) (*roveapi.CommandResponse, error) { + log.Printf("Handling command request: %s and %+v\n", req.Account.Name, req.Commands) + if valid, err := s.world.Accountant.VerifySecret(req.Account.Name, req.Account.Secret); err != nil { + return nil, err + + } else if !valid { + return nil, fmt.Errorf("Secret incorrect for account %s", req.Account.Name) + } + + resp, err := s.world.Accountant.GetValue(req.Account.Name, "rover") if err != nil { return nil, err } - var cmds []game.Command - for _, c := range req.Commands { - cmds = append(cmds, game.Command{ - Bearing: c.Bearing, - Command: c.Command}) - } - - if err := s.world.Enqueue(resp.Value, cmds...); err != nil { + if err := s.world.Enqueue(resp, req.Commands...); err != nil { return nil, err } - return &rove.CommandsResponse{}, nil + return &roveapi.CommandResponse{}, nil } diff --git a/cmd/rove-server/internal/server.go b/cmd/rove-server/internal/server.go index 0345936..e2b7e16 100644 --- a/cmd/rove-server/internal/server.go +++ b/cmd/rove-server/internal/server.go @@ -1,21 +1,24 @@ package internal import ( - "context" "fmt" "log" "net" "os" + "path" "sync" - "github.com/mdiluz/rove/pkg/accounts" - "github.com/mdiluz/rove/pkg/game" "github.com/mdiluz/rove/pkg/persistence" "github.com/mdiluz/rove/pkg/rove" + "github.com/mdiluz/rove/proto/roveapi" "github.com/robfig/cron" "google.golang.org/grpc" + "google.golang.org/grpc/credentials" + "google.golang.org/grpc/reflection" ) +var cert = os.Getenv("CERT_NAME") + const ( // PersistentData will allow the server to load and save it's state PersistentData = iota @@ -28,20 +31,16 @@ const ( type Server struct { // Internal state - world *game.World - - // Accountant server - accountant accounts.AccountantClient - clientConn *grpc.ClientConn + world *rove.World // gRPC server netListener net.Listener grpcServ *grpc.Server // Config settings - address string - persistence int - tick int + address string + persistence int + minutesPerTick int // sync point for sub-threads sync sync.WaitGroup @@ -71,7 +70,7 @@ func OptionPersistentData() ServerOption { // 0 means no automatic server tick func OptionTick(minutes int) ServerOption { return func(s *Server) { - s.tick = minutes + s.minutesPerTick = minutes } } @@ -83,6 +82,7 @@ func NewServer(opts ...ServerOption) *Server { address: "", persistence: EphemeralData, schedule: cron.New(), + world: rove.NewWorld(32), } // Apply all options @@ -90,9 +90,6 @@ func NewServer(opts ...ServerOption) *Server { o(s) } - // Start small, we can grow the world later - s.world = game.NewWorld(4, 8) - return s } @@ -102,23 +99,6 @@ func (s *Server) Initialise(fillWorld bool) (err error) { // Add to our sync s.sync.Add(1) - // Connect to the accountant - accountantAddress := os.Getenv("ROVE_ACCOUNTANT_GRPC") - if len(accountantAddress) == 0 { - accountantAddress = "localhost:9091" - } - log.Printf("Dialing accountant on %s\n", accountantAddress) - s.clientConn, err = grpc.Dial(accountantAddress, grpc.WithInsecure()) - if err != nil { - return err - } - s.accountant = accounts.NewAccountantClient(s.clientConn) - - // Spawn a border on the default world - if err := s.world.SpawnWorld(fillWorld); err != nil { - return err - } - // Load the world file if err := s.LoadWorld(); err != nil { return err @@ -129,8 +109,22 @@ func (s *Server) Initialise(fillWorld bool) (err error) { if err != nil { log.Fatalf("failed to listen: %v", err) } - s.grpcServ = grpc.NewServer() - rove.RegisterRoveServer(s.grpcServ, s) + + // 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) + reflection.Register(s.grpcServ) return nil } @@ -145,19 +139,21 @@ func (s *Server) Run() { defer s.sync.Done() // Set up the schedule if requested - if s.tick != 0 { - if err := s.schedule.AddFunc(fmt.Sprintf("0 */%d * * *", s.tick), func() { + if s.minutesPerTick != 0 { + if err := s.schedule.AddFunc(fmt.Sprintf("0 */%d * * *", s.minutesPerTick), func() { // Ensure we don't quit during this function s.sync.Add(1) defer s.sync.Done() log.Println("Executing server tick") - // Run the command queues - s.world.ExecuteCommandQueues() + // Tick the world + s.world.Tick() // Save out the new world state - s.SaveWorld() + if err := s.SaveWorld(); err != nil { + log.Fatalf("Failed to save the world: %s", err) + } }); err != nil { log.Fatal(err) } @@ -180,11 +176,6 @@ func (s *Server) Stop() error { // Stop the gRPC s.grpcServ.Stop() - // Close the accountant connection - if err := s.clientConn.Close(); err != nil { - return err - } - return nil } @@ -197,7 +188,7 @@ func (s *Server) Close() error { return s.SaveWorld() } -// Close waits until the server is finished and closes up shop +// StopAndClose waits until the server is finished and closes up shop func (s *Server) StopAndClose() error { // Stop the server if err := s.Stop(); err != nil { @@ -232,30 +223,12 @@ func (s *Server) LoadWorld() error { return nil } -// used as the type for the return struct -type BadRequestError struct { - Error string `json:"error"` -} - // SpawnRoverForAccount spawns the rover rover for an account func (s *Server) SpawnRoverForAccount(account string) (string, error) { - if inst, err := s.world.SpawnRover(); err != nil { + inst, err := s.world.SpawnRover(account) + if err != nil { return "", err - - } else { - keyval := accounts.DataKeyValue{Account: account, Key: "rover", Value: inst} - _, err := s.accountant.AssignValue(context.Background(), &keyval) - if err != nil { - log.Printf("Failed to assign rover to account, %s", err) - - // Try and clear up the rover - if err := s.world.DestroyRover(inst); err != nil { - log.Printf("Failed to destroy rover after failed rover assign: %s", err) - } - - return "", err - } else { - return inst, nil - } } + + return inst, nil } diff --git a/cmd/rove-server/internal/server_test.go b/cmd/rove-server/internal/server_test.go index 1297ec2..40d4b80 100644 --- a/cmd/rove-server/internal/server_test.go +++ b/cmd/rove-server/internal/server_test.go @@ -31,7 +31,7 @@ func TestNewServer_OptionPersistentData(t *testing.T) { } func TestServer_Run(t *testing.T) { - os.Setenv("ROVE_ACCOUNTANT_GRPC", "n/a") + os.Setenv("NO_TLS", "1") server := NewServer() if server == nil { t.Error("Failed to create server") @@ -47,7 +47,7 @@ func TestServer_Run(t *testing.T) { } func TestServer_RunPersistentData(t *testing.T) { - os.Setenv("ROVE_ACCOUNTANT_GRPC", "n/a") + os.Setenv("NO_TLS", "1") server := NewServer(OptionPersistentData()) if server == nil { t.Error("Failed to create server") diff --git a/cmd/rove-server/main.go b/cmd/rove-server/main.go index d2848b2..66621cf 100644 --- a/cmd/rove-server/main.go +++ b/cmd/rove-server/main.go @@ -4,10 +4,12 @@ import ( "flag" "fmt" "log" + "math/rand" "os" "os/signal" "strconv" "syscall" + "time" "github.com/mdiluz/rove/cmd/rove-server/internal" "github.com/mdiluz/rove/pkg/persistence" @@ -22,7 +24,11 @@ var data = os.Getenv("DATA_PATH") // The tick rate of the server in seconds var tick = os.Getenv("TICK_RATE") +// InnerMain is our main function so tests can run it func InnerMain() { + // Ensure we've seeded rand + rand.Seed(time.Now().UTC().UnixNano()) + flag.Parse() // Print the version if requested @@ -46,10 +52,14 @@ func InnerMain() { log.Printf("Initialising version %s...\n", version.Version) // Set the persistence path - persistence.SetPath(data) + if len(data) == 0 { + log.Fatal("DATA_PATH not set") + } else if err := persistence.SetPath(data); err != nil { + log.Fatal(err) + } // Convert the tick rate - tickRate := 5 + tickRate := 1 if len(tick) > 0 { var err error tickRate, err = strconv.Atoi(tick) diff --git a/cmd/rove-server/main_test.go b/cmd/rove-server/main_test.go index fcf9a55..e689ead 100644 --- a/cmd/rove-server/main_test.go +++ b/cmd/rove-server/main_test.go @@ -3,10 +3,12 @@ package main import ( "flag" "testing" + + "github.com/stretchr/testify/assert" ) func Test_InnerMain_Version(t *testing.T) { - flag.Set("version", "1") + assert.NoError(t, flag.Set("version", "1")) InnerMain() - flag.Set("version", "0") + assert.NoError(t, flag.Set("version", "0")) } diff --git a/cmd/rove/internal/glyph.go b/cmd/rove/internal/glyph.go new file mode 100644 index 0000000..4a774e7 --- /dev/null +++ b/cmd/rove/internal/glyph.go @@ -0,0 +1,70 @@ +package internal + +import ( + "log" + + "github.com/mdiluz/rove/proto/roveapi" +) + +// Glyph represents the text representation of something in the game +type Glyph byte + +const ( + // GlyphGroundRock is solid rock ground + GlyphGroundRock = Glyph('-') + + // GlyphGroundGravel is loose rocks + GlyphGroundGravel = Glyph(':') + + // GlyphGroundSand is sand + GlyphGroundSand = Glyph('~') + + // GlyphRoverLive represents a live rover + GlyphRoverLive = Glyph('R') + + // GlyphRoverDormant represents a dormant rover + GlyphRoverDormant = Glyph('r') + + // GlyphRoverParts represents spare rover parts + GlyphRoverParts = Glyph('*') + + // GlyphRockSmall is a small stashable rock + GlyphRockSmall = Glyph('o') + + // GlyphRockLarge is a large blocking rock + GlyphRockLarge = Glyph('O') +) + +// TileGlyph returns the glyph for this tile type +func TileGlyph(t roveapi.Tile) Glyph { + switch t { + case roveapi.Tile_Rock: + return GlyphGroundRock + case roveapi.Tile_Gravel: + return GlyphGroundGravel + case roveapi.Tile_Sand: + return GlyphGroundSand + } + + log.Fatalf("Unknown tile type: %c", t) + return 0 +} + +// ObjectGlyph returns the glyph for this object type +func ObjectGlyph(o roveapi.Object) Glyph { + switch o { + case roveapi.Object_RoverLive: + return GlyphRoverLive + case roveapi.Object_RockSmall: + return GlyphRockSmall + case roveapi.Object_RoverDormant: + return GlyphRoverDormant + case roveapi.Object_RockLarge: + return GlyphRockLarge + case roveapi.Object_RoverParts: + return GlyphRoverParts + } + + log.Fatalf("Unknown object type: %c", o) + return 0 +} diff --git a/cmd/rove/main.go b/cmd/rove/main.go index 15014a1..f1e993d 100644 --- a/cmd/rove/main.go +++ b/cmd/rove/main.go @@ -1,20 +1,23 @@ package main import ( + "crypto/tls" "encoding/json" "fmt" "io/ioutil" + "math" "os" "path" "path/filepath" + "strconv" "time" - "github.com/mdiluz/rove/pkg/bearing" - "github.com/mdiluz/rove/pkg/game" - "github.com/mdiluz/rove/pkg/rove" + "github.com/mdiluz/rove/cmd/rove/internal" "github.com/mdiluz/rove/pkg/version" + "github.com/mdiluz/rove/proto/roveapi" "golang.org/x/net/context" "google.golang.org/grpc" + "google.golang.org/grpc/credentials" ) var home = os.Getenv("HOME") @@ -22,30 +25,44 @@ var defaultDataPath = path.Join(home, ".local/share/") // Command usage func printUsage() { - fmt.Fprintf(os.Stderr, "Usage: rove COMMAND [ARGS...]\n") - fmt.Fprintln(os.Stderr, "\nCommands") - fmt.Fprintln(os.Stderr, "\tstatus prints the server status") - fmt.Fprintln(os.Stderr, "\tregister NAME registers an account and stores it (use with -name)") - fmt.Fprintln(os.Stderr, "\tcommands COMMAND [VAL...] issues move command to rover, see below") - fmt.Fprintln(os.Stderr, "\tradar gathers radar data for the current rover") - fmt.Fprintln(os.Stderr, "\trover gets data for current rover") - fmt.Fprintln(os.Stderr, "\tconfig [HOST] outputs the local config info, optionally sets host") - fmt.Fprintln(os.Stderr, "\thelp outputs this usage information") - fmt.Fprintln(os.Stderr, "\tversion outputs version info") - fmt.Fprintln(os.Stderr, "\nRover commands:") - fmt.Fprintln(os.Stderr, "\tmove BEARING moves the rover in the chosen direction") - fmt.Fprintln(os.Stderr, "\tstash stores the object at the rover location in the inventory") - fmt.Fprintln(os.Stderr, "\trepair uses an inventory object to repair the rover") - fmt.Fprintln(os.Stderr, "\nEnvironment") - fmt.Fprintln(os.Stderr, "\tROVE_USER_DATA path to user data, defaults to "+defaultDataPath) + fmt.Fprintln(os.Stderr, "Usage: rove ARG [OPT...]") + fmt.Fprintf(os.Stderr, "\n") + fmt.Fprintln(os.Stderr, "Arguments:") + fmt.Fprintln(os.Stderr, "\tversion outputs version") + fmt.Fprintln(os.Stderr, "\thelp outputs this usage text") + fmt.Fprintln(os.Stderr, "\tconfig [HOST] outputs the local config, optionally sets host") + fmt.Fprintln(os.Stderr, "\tserver-status prints the server status") + fmt.Fprintln(os.Stderr, "\tregister NAME registers an account and spawns a rover") + fmt.Fprintln(os.Stderr, "\tradar prints radar data in ASCII form") + fmt.Fprintln(os.Stderr, "\tstatus gets rover status") + fmt.Fprintln(os.Stderr, "\tcommand CMD [VAL...] [REPEAT] sets the command queue, accepts multiple in sequence") + fmt.Fprintf(os.Stderr, "\n") + fmt.Fprintln(os.Stderr, "Rover commands:") + fmt.Fprintln(os.Stderr, "\ttoggle toggles the current sail mode") + fmt.Fprintln(os.Stderr, "\tstash stores the object at the rover location in the inventory") + fmt.Fprintln(os.Stderr, "\trepair repairs the rover using inventory item") + fmt.Fprintln(os.Stderr, "\tbroadcast MSG broadcast a simple ASCII triplet to nearby rovers") + fmt.Fprintln(os.Stderr, "\tsalvage salvages a dormant rover for parts") + fmt.Fprintln(os.Stderr, "\ttransfer transfer's control into a dormant rover") + fmt.Fprintln(os.Stderr, "\tupgrade SPEC spends rover parts to upgrade one rover spec (capacity, range, integrity, charge") + fmt.Fprintln(os.Stderr, "\twait waits before performing the next command") + fmt.Fprintf(os.Stderr, "\n") + fmt.Fprintln(os.Stderr, "Environment") + fmt.Fprintln(os.Stderr, "\tROVE_USER_DATA path to user data, defaults to "+defaultDataPath) } const gRPCport = 9090 +// Account stores data for an account +type Account struct { + Name string + Secret string +} + // Config is used to store internal data type Config struct { - Host string `json:"host,omitempty"` - Accounts map[string]string `json:"accounts,omitempty"` + Host string + Account Account } // ConfigPath returns the configuration path @@ -56,7 +73,7 @@ func ConfigPath() string { if len(override) > 0 { datapath = override } - datapath = path.Join(datapath, "rove.json") + datapath = path.Join(datapath, "roveapi.json") return datapath } @@ -65,12 +82,13 @@ func ConfigPath() string { func LoadConfig() (config Config, err error) { datapath := ConfigPath() - config.Accounts = make(map[string]string) // Create the path if needed path := filepath.Dir(datapath) if _, err := os.Stat(path); os.IsNotExist(err) { - os.MkdirAll(path, os.ModePerm) + if err := os.MkdirAll(path, os.ModePerm); err != nil { + return Config{}, fmt.Errorf("Failed to create data path %s: %s", path, err) + } } else { // Read the file _, err = os.Stat(datapath) @@ -104,14 +122,39 @@ func SaveConfig(config Config) error { return nil } -// verifyID will verify an account ID -func verifyID(id string) error { - if len(id) == 0 { +// checkAccount will verify an account ID +func checkAccount(a Account) error { + if len(a.Name) == 0 { return fmt.Errorf("no account ID set, must register first") + } else if len(a.Secret) == 0 { + return fmt.Errorf("empty account secret, must register first") } return nil } +// BearingFromString converts a string to a bearing +func BearingFromString(s string) roveapi.Bearing { + switch s { + case "N": + return roveapi.Bearing_North + case "NE": + return roveapi.Bearing_NorthEast + case "E": + return roveapi.Bearing_East + case "SE": + return roveapi.Bearing_SouthEast + case "S": + return roveapi.Bearing_South + case "SW": + return roveapi.Bearing_SouthWest + case "W": + return roveapi.Bearing_West + case "NW": + return roveapi.Bearing_NorthWest + } + return roveapi.Bearing_BearingUnknown +} + // InnerMain wraps the main function so we can test it func InnerMain(command string, args ...string) error { @@ -136,7 +179,7 @@ func InnerMain(command string, args ...string) error { if len(args) > 0 { config.Host = args[0] } - fmt.Printf("host: %s\taccount: %s\n", config.Host, config.Accounts[config.Host]) + fmt.Printf("host: %s\taccount: %s\n", config.Host, config.Account) return SaveConfig(config) } @@ -145,19 +188,26 @@ func InnerMain(command string, args ...string) error { return fmt.Errorf("no host set in %s, set one with '%s config {HOST}'", ConfigPath(), os.Args[0]) } + var opts []grpc.DialOption + if len(os.Getenv("NO_TLS")) == 0 { + opts = append(opts, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{}))) + } else { + opts = append(opts, grpc.WithInsecure()) + } + // Set up the server - clientConn, err := grpc.Dial(fmt.Sprintf("%s:%d", config.Host, gRPCport), grpc.WithInsecure()) + clientConn, err := grpc.Dial(fmt.Sprintf("%s:%d", config.Host, gRPCport), opts...) if err != nil { return err } - var client = rove.NewRoveClient(clientConn) + var client = roveapi.NewRoveClient(clientConn) ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() // Handle all the commands switch command { - case "status": - response, err := client.Status(ctx, &rove.StatusRequest{}) + case "server-status": + response, err := client.ServerStatus(ctx, &roveapi.ServerStatusRequest{}) switch { case err != nil: return err @@ -165,69 +215,117 @@ func InnerMain(command string, args ...string) error { default: fmt.Printf("Ready: %t\n", response.Ready) fmt.Printf("Version: %s\n", response.Version) - fmt.Printf("Tick: %d\n", response.Tick) + fmt.Printf("Tick Rate: %d\n", response.TickRate) + fmt.Printf("Current Tick: %d\n", response.CurrentTick) fmt.Printf("Next Tick: %s\n", response.NextTick) } case "register": - if len(args) == 0 { + if len(args) == 0 || len(args[0]) == 0 { return fmt.Errorf("must pass name to 'register'") } - name := args[0] - d := rove.RegisterRequest{ - Name: name, - } - _, err := client.Register(ctx, &d) + + resp, err := client.Register(ctx, &roveapi.RegisterRequest{ + Name: args[0], + }) switch { case err != nil: return err default: - fmt.Printf("Registered account with id: %s\n", name) - config.Accounts[config.Host] = name + fmt.Printf("Registered account with id: %s\n", resp.Account.Name) + config.Account.Name = resp.Account.Name + config.Account.Secret = resp.Account.Secret } - case "commands": - if len(args) == 0 { + case "command": + if err := checkAccount(config.Account); err != nil { + return err + } else if len(args) == 0 { return fmt.Errorf("must pass commands to 'commands'") } // Iterate through each command - var commands []*rove.Command + var commands []*roveapi.Command for i := 0; i < len(args); i++ { + + var cmd *roveapi.Command switch args[i] { - case "move": + case "turn": i++ if len(args) == i { - return fmt.Errorf("move command must be passed bearing") - } else if _, err := bearing.FromString(args[i]); err != nil { - return err + return fmt.Errorf("turn command must be passed a compass bearing") + } + b := BearingFromString(args[i]) + if b == roveapi.Bearing_BearingUnknown { + return fmt.Errorf("turn command must be given a valid bearing %s", args[i]) + } + cmd = &roveapi.Command{ + Command: roveapi.CommandType_turn, + Bearing: b, + } + case "broadcast": + i++ + if len(args) == i { + return fmt.Errorf("broadcast command must be passed an ASCII triplet") + } else if len(args[i]) > 3 { + return fmt.Errorf("broadcast command must be given ASCII triplet of 3 or less: %s", args[i]) + } + cmd = &roveapi.Command{ + Command: roveapi.CommandType_broadcast, + Data: []byte(args[i]), + } + case "upgrade": + i++ + if len(args) == i { + return fmt.Errorf("upgrade command must be passed a spec to upgrade") + } + var u roveapi.RoverUpgrade + switch args[i] { + case "capacity": + u = roveapi.RoverUpgrade_Capacity + case "range": + u = roveapi.RoverUpgrade_Range + case "integrity": + u = roveapi.RoverUpgrade_MaximumIntegrity + case "charge": + u = roveapi.RoverUpgrade_MaximumCharge + default: + return fmt.Errorf("upgrade command must be passed a known upgrade spec") + } + cmd = &roveapi.Command{ + Command: roveapi.CommandType_upgrade, + Upgrade: u, + } + default: + // By default just use the command literally + cmd = &roveapi.Command{ + Command: roveapi.CommandType(roveapi.CommandType_value[args[i]]), } - commands = append(commands, - &rove.Command{ - Command: game.CommandMove, - Bearing: args[i], - }, - ) - case "stash": - commands = append(commands, - &rove.Command{ - Command: game.CommandStash, - }, - ) } + + // 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) } - d := rove.CommandsRequest{ - Account: config.Accounts[config.Host], + _, err := client.Command(ctx, &roveapi.CommandRequest{ + Account: &roveapi.Account{ + Name: config.Account.Name, + Secret: config.Account.Secret, + }, Commands: commands, - } + }) - if err := verifyID(d.Account); err != nil { - return err - } - - _, err := client.Commands(ctx, &d) switch { case err != nil: return err @@ -237,27 +335,51 @@ func InnerMain(command string, args ...string) error { } case "radar": - dat := rove.RadarRequest{Account: config.Accounts[config.Host]} - if err := verifyID(dat.Account); err != nil { + if err := checkAccount(config.Account); err != nil { return err } - response, err := client.Radar(ctx, &dat) + response, err := client.Radar(ctx, &roveapi.RadarRequest{ + Account: &roveapi.Account{ + Name: config.Account.Name, + Secret: config.Account.Secret, + }, + }) + switch { case err != nil: return err default: + // Print out the radar - game.PrintTiles(response.Tiles) + num := int(math.Sqrt(float64(len(response.Tiles)))) + for j := num - 1; j >= 0; j-- { + for i := 0; i < num; i++ { + t := response.Tiles[i+num*j] + o := response.Objects[i+num*j] + if o != roveapi.Object_ObjectUnknown { + fmt.Printf("%c", internal.ObjectGlyph(o)) + } else { + fmt.Printf("%c", internal.TileGlyph(t)) + } + + } + fmt.Print("\n") + } } - case "rover": - req := rove.RoverRequest{Account: config.Accounts[config.Host]} - if err := verifyID(req.Account); err != nil { + case "status": + if err := checkAccount(config.Account); err != nil { return err } - response, err := client.Rover(ctx, &req) + + response, err := client.Status(ctx, &roveapi.StatusRequest{ + Account: &roveapi.Account{ + Name: config.Account.Name, + Secret: config.Account.Secret, + }, + }) switch { case err != nil: @@ -281,6 +403,15 @@ func InnerMain(command string, args ...string) error { func main() { // Bail without any args if len(os.Args) == 1 { + fmt.Fprintf(os.Stderr, "\n") + fmt.Fprintln(os.Stderr, "m mm mmm m m mmm") + fmt.Fprintln(os.Stderr, "#\" \" #\" \"# \"m m\" #\" #") + fmt.Fprintln(os.Stderr, "# # # #m# #\"\"\"\"") + fmt.Fprintln(os.Stderr, "# \"#m#\" # \"#mm\"") + fmt.Fprintf(os.Stderr, "\n") + fmt.Fprintln(os.Stderr, "Rove is an asychronous nomadic game about exploring a planet as part of a loose community.") + fmt.Fprintln(os.Stderr, "Visit https://mdiluz.github.io/rove/ for more information.") + fmt.Fprintf(os.Stderr, "\n") printUsage() os.Exit(1) } diff --git a/cmd/rove/main_test.go b/cmd/rove/main_test.go index 1c06374..250988a 100644 --- a/cmd/rove/main_test.go +++ b/cmd/rove/main_test.go @@ -13,6 +13,7 @@ import ( ) func Test_InnerMain(t *testing.T) { + os.Setenv("NO_TLS", "1") // Use temporary local user data tmp, err := ioutil.TempDir(os.TempDir(), "rove-") @@ -26,34 +27,41 @@ func Test_InnerMain(t *testing.T) { } // First attempt should error without a host - assert.Error(t, InnerMain("status")) + assert.Error(t, InnerMain("server-status")) // Set the host in the config assert.NoError(t, InnerMain("config", address)) - assert.NoError(t, InnerMain("status")) + assert.NoError(t, InnerMain("server-status")) // Register should fail without a name assert.Error(t, InnerMain("register")) // These methods should fail without an account assert.Error(t, InnerMain("radar")) - assert.Error(t, InnerMain("rover")) + assert.Error(t, InnerMain("status")) // Now set the name assert.NoError(t, InnerMain("register", uuid.New().String())) // These should now work assert.NoError(t, InnerMain("radar")) - assert.NoError(t, InnerMain("rover")) + assert.NoError(t, InnerMain("status")) // Commands should fail with no commands - assert.Error(t, InnerMain("commands")) + assert.Error(t, InnerMain("command")) // Give it commands - assert.NoError(t, InnerMain("commands", "move", "N")) - assert.NoError(t, InnerMain("commands", "stash")) - assert.NoError(t, InnerMain("commands", "repair")) + assert.NoError(t, InnerMain("command", "toggle")) + assert.NoError(t, InnerMain("command", "stash")) + assert.NoError(t, InnerMain("command", "repair")) + assert.NoError(t, InnerMain("command", "upgrade", "capacity")) + assert.NoError(t, InnerMain("command", "broadcast", "abc")) + assert.NoError(t, InnerMain("command", "wait", "10")) + assert.NoError(t, InnerMain("command", "wait", "1", "turn", "NW", "toggle", "broadcast", "zyx")) // Give it malformed commands - assert.Error(t, InnerMain("commands", "move", "stash")) + assert.Error(t, InnerMain("command", "unknown")) + assert.Error(t, InnerMain("command", "broadcast")) + assert.Error(t, InnerMain("command", "upgrade")) + assert.Error(t, InnerMain("command", "1")) } diff --git a/data/io.github.mdiluz.Rove.json b/data/io.github.mdiluz.Rove.json new file mode 100644 index 0000000..d206be5 --- /dev/null +++ b/data/io.github.mdiluz.Rove.json @@ -0,0 +1,36 @@ +{ + "app-id": "io.github.mdiluz.Rove", + "runtime": "org.freedesktop.Platform", + "runtime-version": "19.08", + "sdk": "org.freedesktop.Sdk", + "sdk-extensions" : [ + "org.freedesktop.Sdk.Extension.golang" + ], + "finish-args" : [ + "--share=network" + ], + "command": "rove", + "modules": [ + { + "name": "rove", + "buildsystem": "simple", + "build-options": { + "env": { + "GOBIN": "/app/bin/" + }, + "build-args": [ + "--share=network" + ] + }, + "build-commands" : [ + ". /usr/lib/sdk/golang/enable.sh; make install" + ], + "sources": [ + { + "type": "dir", + "path": ".." + } + ] + } + ] +} \ No newline at end of file diff --git a/docker-compose-test.yml b/docker-compose-test.yml new file mode 100644 index 0000000..aec8cee --- /dev/null +++ b/docker-compose-test.yml @@ -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 + + + diff --git a/docker-compose.yml b/docker-compose.yml index 91c69c1..d5606f7 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -4,32 +4,7 @@ volumes: persistent-data: services: - rove-accountant: - build: - context: . - dockerfile: Dockerfile - image: rove:latest - ports: - - "9091:9091" - environment: - - PORT=9091 - - DATA_PATH=/mnt/rove-server - volumes: - - persistent-data:/mnt/rove-server:rw - command: [ ./rove-accountant ] - - rove-docs: - build: - context: . - dockerfile: Dockerfile.docs - image: rove-docs:latest - ports: - - "80:80" - environment: - - PORT=80 - rove-server: - depends_on: [ rove-accountant ] build: context: . dockerfile: Dockerfile @@ -39,38 +14,12 @@ services: environment: - PORT=9090 - DATA_PATH=/mnt/rove-server - - ROVE_ACCOUNTANT_GRPC=rove-accountant:9091 - WORDS_FILE=data/words_alpha.txt + - TICK_RATE=3 + - CERT_NAME=${CERT_NAME} volumes: - persistent-data:/mnt/rove-server:rw - command: [ "./script/wait-for-it.sh", "rove-accountant:9091", "--", "./rove-server"] - - rove: - depends_on: [ rove-server, rove-docs ] - build: - context: . - dockerfile: Dockerfile - image: rove:latest - ports: - - "8080:8080" - environment: - - PORT=8080 - - ROVE_GRPC=rove-server:9090 - command: [ "./script/wait-for-it.sh", "rove-server:9090", "--", "./rove-reverse-proxy" ] - - rove-tests: - depends_on: [ rove ] - build: - context: . - dockerfile: Dockerfile - image: rove:latest - environment: - - ROVE_ACCOUNTANT_GRPC=rove-accountant:9091 - - ROVE_HTTP=rove - - ROVE_GRPC=rove-server - command: [ "./script/wait-for-it.sh", "rove:8080", "--", "go", "test", "-v", "./...", "--tags=integration", "-cover", "-coverprofile=/mnt/coverage-data/c.out", "-count", "1" ] - volumes: - - /tmp/coverage-data:/mnt/coverage-data:rw - + - /etc/letsencrypt/:/etc/letsencrypt/ + command: [ "./rove-server"] diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..2a0e6db --- /dev/null +++ b/docs/README.md @@ -0,0 +1,61 @@ +Rove +===== + +Rove is an asynchronous nomadic game about exploring a planet as part of a loose community. + +------------------------------------------- + +## Core gameplay + +Remotely explore the surface of a planet with an upgradable and customisable rover. Send commands to be executed asynchronously, view the rover's radar, and communicate and coordinate with other nearby rovers. + +### Key Components + +* Navigate an expansive world +* Collect resources to repair and upgrade +* Keep the rover batteries charged as you explore +* Help other players on their journey +* Explore north to discover more + +------------------------------------------- + +## Installing + +On Ubuntu: +``` +$ snap install rove +``` + +Elsewhere (with [go](https://golang.org/doc/install) installed) +``` +go get github.com/mdiluz/rove +cd $GOPATH/src/github.com/mdiluz/rove/ +make install +``` + +------------------------------------------- + +### Implementation Details + +`rove-server` hosts the game world and a gRPC server to allow users to interact from any client. + +`rove` is a basic example command-line client that allows for simple play, to explore it's usage, see the output of `rove help` + +------------------------------------------- + +### "Find the fun" issues to solve + +* What kinds of progression/upgrades exist? +* How does the game encourage cooperation? +* How would the gameplay prevent griefing? +* What drives the exploration? + +------------------------------------------- + +### Key ideas left to integrate + +* Feeling “known for” something - the person who did X thing. Limit number of X things that can be done, possibly over time. +* A significant aspect of failure - failing must be a focus of the game. Winning the game might actually be failing in some specific way. +* A clear and well defined investment vs. payoff curve. +* Not an infinite game, let the game have a point where you’re done and can move on. + diff --git a/docs/_config.yml b/docs/_config.yml new file mode 100644 index 0000000..c50ff38 --- /dev/null +++ b/docs/_config.yml @@ -0,0 +1 @@ +theme: jekyll-theme-merlot \ No newline at end of file diff --git a/docs/design.md b/docs/design.md deleted file mode 100644 index 578c5e6..0000000 --- a/docs/design.md +++ /dev/null @@ -1,86 +0,0 @@ -Rove -===== - -An asynchronous nomadic game about exploring a planet as part of a loose -community. - -------------------------------------------- - -## The Basics - -### Core - -Control a rover on the surface of the planet using a remote control interface. - -Commands are sent and happen asynchronously, and the rover feeds back information about position and surroundings, as well as photos. - -### Goal - -To reach the pole. - -### General - -Movement is slow and sometimes dangerous. Hazards damage the rover. - -Resources can be collected to fix and upgrade the rover. - -Rovers recharge power during the day. - -Enough collected resources allow you to create and fire a new rover a significant distance in any direction, losing control of the current one and leaving it dormant. - -Finding a dormant rover gives you a choice - scrap it to gain minor resources, or fire it a distance just like a new rover, taking control of it. - -“Dying” triggers a self destruct and fires a new basic rover in a random direction towards the equator - -## Multiplayer - -The planet itself and things that happen on it are persistent. Players can view each other, and use very rudimentary signals. - -Dormant rovers store full history of travel, owners, and keep damage, improvements and resources. - -Players have no other forms of direct communication. - -Players can view progress of all rovers attached to their name. - -Limit too many players in one location with a simple interference mechanic - only a certain density can exist at once to operate properly, additional players can’t move within range. - -------------------------------------------- - -### Implementation - -Two functional parts - -A server that receives the commands, sends out data, and handles interactions between players. - -An app, or apps, that interface with the server to let you control and view rover information - -------------------------------------------- - -### To Solve - -#### What kinds of progression/upgrades exist? -Needs a very simple set of rover internals defined, each of which can be upgraded. - -#### How does the game encourage lateral movement? -Could simply be the terrain is constructed in very lateral ways, blocking progress frequently - -#### How does the game encourage cooperation? -How exactly would a time delay mechanic enhance the experience? -Currently it’s just to make the multiplayer easier to use, and to make interactions a little more complicated. The game could limit the number of bytes (commands) you can send over time. - -#### How would the gameplay prevent griefing? - -------------------------------------------- - -### Key ideas left to integrate - -Feeling “known for” something - the person who did X thing. Limit number of X things that can be done, possibly over time. - -Build up a certain level of knowledge and ownership of a place, but then destroy it or give it up. Or build up a character and then leave it behind. - -A significant aspect of failure - failing must be a focus of the game. Winning the game might actually be failing in some specific way. - -A clear and well defined investment vs. payoff curve. - -Not an infinite game, let the game have a point where you’re done and can move on. - diff --git a/docs/status.md b/docs/status.md deleted file mode 100644 index 5045f0a..0000000 --- a/docs/status.md +++ /dev/null @@ -1,28 +0,0 @@ -Development status -================ - -This page tracks the current feature set and the features to implement next - -### Complete features - -* Users can register account and spawn rover -* Users can command the rover to move -* Server can return rover's current radar -* Server supports multiple users and rovers -* Map is populated with rocks -* Commands are queued and then are executed in sequence -* Rover inventory -* Collectable materials -* Rover integrity and damage -* Rover can repair - -### Incoming features - -* Rover replacement and advancement mechanic -* HTTPS - -### Stretch goals - -* Render rover camera view -* Account token security - diff --git a/go.mod b/go.mod index e21de42..32caba4 100644 --- a/go.mod +++ b/go.mod @@ -3,16 +3,19 @@ module github.com/mdiluz/rove go 1.14 require ( - github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b + github.com/davecgh/go-spew v1.1.1 // indirect github.com/golang/protobuf v1.4.2 github.com/google/uuid v1.1.1 github.com/grpc-ecosystem/grpc-gateway v1.14.6 - github.com/onsi/ginkgo v1.12.3 // indirect + github.com/kr/pretty v0.1.0 // indirect + github.com/ojrac/opensimplex-go v1.0.1 github.com/robfig/cron v1.2.0 github.com/stretchr/testify v1.6.0 - github.com/tjarratt/babble v0.0.0-20191209142150-eecdf8c2339d - golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7 - google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884 - google.golang.org/grpc v1.29.1 - google.golang.org/protobuf v1.23.0 + golang.org/x/net v0.0.0-20200602114024-627f9648deb9 + golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980 // indirect + golang.org/x/text v0.3.3 // indirect + google.golang.org/grpc v1.30.0 + google.golang.org/protobuf v1.25.0 + gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect + gopkg.in/yaml.v2 v2.3.0 // indirect ) diff --git a/go.sum b/go.sum index 57044e0..a2bd9a2 100644 --- a/go.sum +++ b/go.sum @@ -1,19 +1,23 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0 h1:eOI3/cP2VTU6uZLDYAoic+eyzzB9YyGmJ7eIjl8rOPg= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f h1:WBZRG4aNOuI15bLRrCgN8fCq8E5Xuty6jGbmSNEvSsU= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4 h1:rEvIZUSZ3fx39WIi3JkQqQBitGwpELBIYWeBVh6wn+E= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= -github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= @@ -27,6 +31,7 @@ github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:x github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -34,20 +39,19 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/grpc-ecosystem/grpc-gateway v1.14.6 h1:8ERzHx8aj1Sc47mu9n/AksaKCSWrMchFtkdrS4BIj5o= github.com/grpc-ecosystem/grpc-gateway v1.14.6/go.mod h1:zdiPV4Yse/1gnckTHtghG4GkDEdKCRJduHpTxT3/jcw= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= -github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.12.3 h1:+RYp9QczoWz9zfUyLP/5SLXQVhfr6gZOoKGfQqHuLZQ= -github.com/onsi/ginkgo v1.12.3/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= -github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= -github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/ojrac/opensimplex-go v1.0.1 h1:XslvpLP6XqQSATUtsOnGBYtFPw7FQ6h6y0ihjVeOLHo= +github.com/ojrac/opensimplex-go v1.0.1/go.mod h1:MoSgj04tZpH8U0RefZabnHV2AbLgv/2mo3hLJtWqSEs= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -57,8 +61,6 @@ github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6L github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.6.0 h1:jlIyCplCJFULU/01vCkhKuTyc3OorI3bJFuw6obfgho= github.com/stretchr/testify v1.6.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/tjarratt/babble v0.0.0-20191209142150-eecdf8c2339d h1:b7oHBI6TgTdCDuqTijsVldzlh+6cfQpdYLz1EKtCAoY= -github.com/tjarratt/babble v0.0.0-20191209142150-eecdf8c2339d/go.mod h1:O5hBrCGqzfb+8WyY8ico2AyQau7XQwAfEQeEQ5/5V9E= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -66,34 +68,31 @@ golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvx golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20191002035440-2ec189313ef0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7 h1:AeiKBIuRw3UomYXSbLy0Mc2dDLfdtbT/IVn4keq83P0= -golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200602114024-627f9648deb9 h1:pNX+40auqi2JqRfOP1akLGtYcn15TUbkhwuCO3foqqM= +golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200519105757-fe76b779f299 h1:DYfZAGf2WMFjMxbgTjaC+2HC7NkNAQs+6Q8b9WEB/F4= -golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980 h1:OjiUf46hAmXblsZdnoSXsEUSKU8r1UEzcL5RVZ4gO9Y= +golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -109,29 +108,36 @@ google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2El google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884 h1:fiNLklpBwWK1mth30Hlwk+fcdBmIALlgF5iy77O37Ig= google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.29.1 h1:EC2SB8S04d2r73uptxphDSUG+kTKVgjRPF+N3xpxRB4= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0 h1:M5a8xTlYTxwMn5ZFkwhRabsygDY5G8TYLyQDBxJNAxE= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc h1:/hemPrYIhOhy8zYrNj+069zDB68us2sMGsfkFJO0iZs= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/pkg/accounts/accounts.go b/pkg/accounts/accounts.go new file mode 100644 index 0000000..eb8637d --- /dev/null +++ b/pkg/accounts/accounts.go @@ -0,0 +1,28 @@ +package accounts + +// Accountant decribes something that stores accounts and account values +type Accountant interface { + // RegisterAccount will register a new account and return it's info + RegisterAccount(name string) (acc Account, err error) + + // AssignData stores a custom account key value pair + AssignData(account string, key string, value string) error + + // GetValue returns custom account data for a specific key + GetValue(account string, key string) (string, error) + + // VerifySecret will verify whether the account secret matches with the + VerifySecret(account string, secret string) (bool, error) + + // GetSecret gets the secret associated with an account + GetSecret(account string) (string, error) +} + +// Account represents a registered user +type Account struct { + // Name simply describes the account and must be unique + Name string + + // Data represents internal account data + Data map[string]string +} diff --git a/pkg/accounts/accounts.pb.go b/pkg/accounts/accounts.pb.go deleted file mode 100644 index ad19fae..0000000 --- a/pkg/accounts/accounts.pb.go +++ /dev/null @@ -1,663 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.23.0 -// protoc v3.6.1 -// source: accounts/accounts.proto - -package accounts - -import ( - context "context" - proto "github.com/golang/protobuf/proto" - grpc "google.golang.org/grpc" - codes "google.golang.org/grpc/codes" - status "google.golang.org/grpc/status" - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -// This is a compile-time assertion that a sufficiently up-to-date version -// of the legacy proto package is being used. -const _ = proto.ProtoPackageIsVersion4 - -// RegisterInfo contains the information needed to register an account -type RegisterInfo struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // The name for the account, must be unique - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` -} - -func (x *RegisterInfo) Reset() { - *x = RegisterInfo{} - if protoimpl.UnsafeEnabled { - mi := &file_accounts_accounts_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *RegisterInfo) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*RegisterInfo) ProtoMessage() {} - -func (x *RegisterInfo) ProtoReflect() protoreflect.Message { - mi := &file_accounts_accounts_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use RegisterInfo.ProtoReflect.Descriptor instead. -func (*RegisterInfo) Descriptor() ([]byte, []int) { - return file_accounts_accounts_proto_rawDescGZIP(), []int{0} -} - -func (x *RegisterInfo) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -// RegisterResponse is the response information from registering an account -type RegisterResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields -} - -func (x *RegisterResponse) Reset() { - *x = RegisterResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_accounts_accounts_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *RegisterResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*RegisterResponse) ProtoMessage() {} - -func (x *RegisterResponse) ProtoReflect() protoreflect.Message { - mi := &file_accounts_accounts_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use RegisterResponse.ProtoReflect.Descriptor instead. -func (*RegisterResponse) Descriptor() ([]byte, []int) { - return file_accounts_accounts_proto_rawDescGZIP(), []int{1} -} - -// DataKeyValue represents a simple key value pair to assign to an account -type DataKeyValue struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // The account to assign the new key value pair to - Account string `protobuf:"bytes,1,opt,name=account,proto3" json:"account,omitempty"` - // The key value pair to assign - Key string `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"` - Value string `protobuf:"bytes,3,opt,name=value,proto3" json:"value,omitempty"` -} - -func (x *DataKeyValue) Reset() { - *x = DataKeyValue{} - if protoimpl.UnsafeEnabled { - mi := &file_accounts_accounts_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *DataKeyValue) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DataKeyValue) ProtoMessage() {} - -func (x *DataKeyValue) ProtoReflect() protoreflect.Message { - mi := &file_accounts_accounts_proto_msgTypes[2] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DataKeyValue.ProtoReflect.Descriptor instead. -func (*DataKeyValue) Descriptor() ([]byte, []int) { - return file_accounts_accounts_proto_rawDescGZIP(), []int{2} -} - -func (x *DataKeyValue) GetAccount() string { - if x != nil { - return x.Account - } - return "" -} - -func (x *DataKeyValue) GetKey() string { - if x != nil { - return x.Key - } - return "" -} - -func (x *DataKeyValue) GetValue() string { - if x != nil { - return x.Value - } - return "" -} - -// DataKeyResponse is a simple response -type DataKeyResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields -} - -func (x *DataKeyResponse) Reset() { - *x = DataKeyResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_accounts_accounts_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *DataKeyResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DataKeyResponse) ProtoMessage() {} - -func (x *DataKeyResponse) ProtoReflect() protoreflect.Message { - mi := &file_accounts_accounts_proto_msgTypes[3] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DataKeyResponse.ProtoReflect.Descriptor instead. -func (*DataKeyResponse) Descriptor() ([]byte, []int) { - return file_accounts_accounts_proto_rawDescGZIP(), []int{3} -} - -// DataKey describes a simple key value with an account, for fetching -type DataKey struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // The account to fetch data for - Account string `protobuf:"bytes,1,opt,name=account,proto3" json:"account,omitempty"` - // The key to fetch - Key string `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"` -} - -func (x *DataKey) Reset() { - *x = DataKey{} - if protoimpl.UnsafeEnabled { - mi := &file_accounts_accounts_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *DataKey) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DataKey) ProtoMessage() {} - -func (x *DataKey) ProtoReflect() protoreflect.Message { - mi := &file_accounts_accounts_proto_msgTypes[4] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DataKey.ProtoReflect.Descriptor instead. -func (*DataKey) Descriptor() ([]byte, []int) { - return file_accounts_accounts_proto_rawDescGZIP(), []int{4} -} - -func (x *DataKey) GetAccount() string { - if x != nil { - return x.Account - } - return "" -} - -func (x *DataKey) GetKey() string { - if x != nil { - return x.Key - } - return "" -} - -// DataResponse describes a data fetch response -type DataResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // The value of the key - Value string `protobuf:"bytes,3,opt,name=value,proto3" json:"value,omitempty"` -} - -func (x *DataResponse) Reset() { - *x = DataResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_accounts_accounts_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *DataResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DataResponse) ProtoMessage() {} - -func (x *DataResponse) ProtoReflect() protoreflect.Message { - mi := &file_accounts_accounts_proto_msgTypes[5] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DataResponse.ProtoReflect.Descriptor instead. -func (*DataResponse) Descriptor() ([]byte, []int) { - return file_accounts_accounts_proto_rawDescGZIP(), []int{5} -} - -func (x *DataResponse) GetValue() string { - if x != nil { - return x.Value - } - return "" -} - -var File_accounts_accounts_proto protoreflect.FileDescriptor - -var file_accounts_accounts_proto_rawDesc = []byte{ - 0x0a, 0x17, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x2f, 0x61, 0x63, 0x63, 0x6f, 0x75, - 0x6e, 0x74, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, 0x61, 0x63, 0x63, 0x6f, 0x75, - 0x6e, 0x74, 0x73, 0x22, 0x22, 0x0a, 0x0c, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x49, - 0x6e, 0x66, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x12, 0x0a, 0x10, 0x52, 0x65, 0x67, 0x69, 0x73, - 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x50, 0x0a, 0x0c, 0x44, - 0x61, 0x74, 0x61, 0x4b, 0x65, 0x79, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x61, - 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x63, - 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x11, 0x0a, - 0x0f, 0x44, 0x61, 0x74, 0x61, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x35, 0x0a, 0x07, 0x44, 0x61, 0x74, 0x61, 0x4b, 0x65, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x61, - 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x63, - 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x22, 0x24, 0x0a, 0x0c, 0x44, 0x61, 0x74, 0x61, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x32, 0xcb, 0x01, - 0x0a, 0x0a, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x61, 0x6e, 0x74, 0x12, 0x40, 0x0a, 0x08, - 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x12, 0x16, 0x2e, 0x61, 0x63, 0x63, 0x6f, 0x75, - 0x6e, 0x74, 0x73, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, - 0x1a, 0x1a, 0x2e, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x2e, 0x52, 0x65, 0x67, 0x69, - 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x42, - 0x0a, 0x0b, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x16, 0x2e, - 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x4b, 0x65, 0x79, - 0x56, 0x61, 0x6c, 0x75, 0x65, 0x1a, 0x19, 0x2e, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, - 0x2e, 0x44, 0x61, 0x74, 0x61, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x00, 0x12, 0x37, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x11, - 0x2e, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x4b, 0x65, - 0x79, 0x1a, 0x16, 0x2e, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x2e, 0x44, 0x61, 0x74, - 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x25, 0x5a, 0x23, 0x67, - 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6d, 0x64, 0x69, 0x6c, 0x75, 0x7a, - 0x2f, 0x72, 0x6f, 0x76, 0x65, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, - 0x74, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, -} - -var ( - file_accounts_accounts_proto_rawDescOnce sync.Once - file_accounts_accounts_proto_rawDescData = file_accounts_accounts_proto_rawDesc -) - -func file_accounts_accounts_proto_rawDescGZIP() []byte { - file_accounts_accounts_proto_rawDescOnce.Do(func() { - file_accounts_accounts_proto_rawDescData = protoimpl.X.CompressGZIP(file_accounts_accounts_proto_rawDescData) - }) - return file_accounts_accounts_proto_rawDescData -} - -var file_accounts_accounts_proto_msgTypes = make([]protoimpl.MessageInfo, 6) -var file_accounts_accounts_proto_goTypes = []interface{}{ - (*RegisterInfo)(nil), // 0: accounts.RegisterInfo - (*RegisterResponse)(nil), // 1: accounts.RegisterResponse - (*DataKeyValue)(nil), // 2: accounts.DataKeyValue - (*DataKeyResponse)(nil), // 3: accounts.DataKeyResponse - (*DataKey)(nil), // 4: accounts.DataKey - (*DataResponse)(nil), // 5: accounts.DataResponse -} -var file_accounts_accounts_proto_depIdxs = []int32{ - 0, // 0: accounts.Accountant.Register:input_type -> accounts.RegisterInfo - 2, // 1: accounts.Accountant.AssignValue:input_type -> accounts.DataKeyValue - 4, // 2: accounts.Accountant.GetValue:input_type -> accounts.DataKey - 1, // 3: accounts.Accountant.Register:output_type -> accounts.RegisterResponse - 3, // 4: accounts.Accountant.AssignValue:output_type -> accounts.DataKeyResponse - 5, // 5: accounts.Accountant.GetValue:output_type -> accounts.DataResponse - 3, // [3:6] is the sub-list for method output_type - 0, // [0:3] is the sub-list for method input_type - 0, // [0:0] is the sub-list for extension type_name - 0, // [0:0] is the sub-list for extension extendee - 0, // [0:0] is the sub-list for field type_name -} - -func init() { file_accounts_accounts_proto_init() } -func file_accounts_accounts_proto_init() { - if File_accounts_accounts_proto != nil { - return - } - if !protoimpl.UnsafeEnabled { - file_accounts_accounts_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RegisterInfo); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_accounts_accounts_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RegisterResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_accounts_accounts_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DataKeyValue); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_accounts_accounts_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DataKeyResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_accounts_accounts_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DataKey); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_accounts_accounts_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DataResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_accounts_accounts_proto_rawDesc, - NumEnums: 0, - NumMessages: 6, - NumExtensions: 0, - NumServices: 1, - }, - GoTypes: file_accounts_accounts_proto_goTypes, - DependencyIndexes: file_accounts_accounts_proto_depIdxs, - MessageInfos: file_accounts_accounts_proto_msgTypes, - }.Build() - File_accounts_accounts_proto = out.File - file_accounts_accounts_proto_rawDesc = nil - file_accounts_accounts_proto_goTypes = nil - file_accounts_accounts_proto_depIdxs = nil -} - -// Reference imports to suppress errors if they are not otherwise used. -var _ context.Context -var _ grpc.ClientConnInterface - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the grpc package it is being compiled against. -const _ = grpc.SupportPackageIsVersion6 - -// AccountantClient is the client API for Accountant service. -// -// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. -type AccountantClient interface { - // Register should create a new account in the database - // It will return an error if the account already exists - Register(ctx context.Context, in *RegisterInfo, opts ...grpc.CallOption) (*RegisterResponse, error) - // AssignValue assigns a key-value pair to an account, or overwrites an existing key - AssignValue(ctx context.Context, in *DataKeyValue, opts ...grpc.CallOption) (*DataKeyResponse, error) - // GetValue will get the value for a key for an account - GetValue(ctx context.Context, in *DataKey, opts ...grpc.CallOption) (*DataResponse, error) -} - -type accountantClient struct { - cc grpc.ClientConnInterface -} - -func NewAccountantClient(cc grpc.ClientConnInterface) AccountantClient { - return &accountantClient{cc} -} - -func (c *accountantClient) Register(ctx context.Context, in *RegisterInfo, opts ...grpc.CallOption) (*RegisterResponse, error) { - out := new(RegisterResponse) - err := c.cc.Invoke(ctx, "/accounts.Accountant/Register", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *accountantClient) AssignValue(ctx context.Context, in *DataKeyValue, opts ...grpc.CallOption) (*DataKeyResponse, error) { - out := new(DataKeyResponse) - err := c.cc.Invoke(ctx, "/accounts.Accountant/AssignValue", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *accountantClient) GetValue(ctx context.Context, in *DataKey, opts ...grpc.CallOption) (*DataResponse, error) { - out := new(DataResponse) - err := c.cc.Invoke(ctx, "/accounts.Accountant/GetValue", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -// AccountantServer is the server API for Accountant service. -type AccountantServer interface { - // Register should create a new account in the database - // It will return an error if the account already exists - Register(context.Context, *RegisterInfo) (*RegisterResponse, error) - // AssignValue assigns a key-value pair to an account, or overwrites an existing key - AssignValue(context.Context, *DataKeyValue) (*DataKeyResponse, error) - // GetValue will get the value for a key for an account - GetValue(context.Context, *DataKey) (*DataResponse, error) -} - -// UnimplementedAccountantServer can be embedded to have forward compatible implementations. -type UnimplementedAccountantServer struct { -} - -func (*UnimplementedAccountantServer) Register(context.Context, *RegisterInfo) (*RegisterResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method Register not implemented") -} -func (*UnimplementedAccountantServer) AssignValue(context.Context, *DataKeyValue) (*DataKeyResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method AssignValue not implemented") -} -func (*UnimplementedAccountantServer) GetValue(context.Context, *DataKey) (*DataResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method GetValue not implemented") -} - -func RegisterAccountantServer(s *grpc.Server, srv AccountantServer) { - s.RegisterService(&_Accountant_serviceDesc, srv) -} - -func _Accountant_Register_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(RegisterInfo) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(AccountantServer).Register(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/accounts.Accountant/Register", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(AccountantServer).Register(ctx, req.(*RegisterInfo)) - } - return interceptor(ctx, in, info, handler) -} - -func _Accountant_AssignValue_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(DataKeyValue) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(AccountantServer).AssignValue(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/accounts.Accountant/AssignValue", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(AccountantServer).AssignValue(ctx, req.(*DataKeyValue)) - } - return interceptor(ctx, in, info, handler) -} - -func _Accountant_GetValue_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(DataKey) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(AccountantServer).GetValue(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/accounts.Accountant/GetValue", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(AccountantServer).GetValue(ctx, req.(*DataKey)) - } - return interceptor(ctx, in, info, handler) -} - -var _Accountant_serviceDesc = grpc.ServiceDesc{ - ServiceName: "accounts.Accountant", - HandlerType: (*AccountantServer)(nil), - Methods: []grpc.MethodDesc{ - { - MethodName: "Register", - Handler: _Accountant_Register_Handler, - }, - { - MethodName: "AssignValue", - Handler: _Accountant_AssignValue_Handler, - }, - { - MethodName: "GetValue", - Handler: _Accountant_GetValue_Handler, - }, - }, - Streams: []grpc.StreamDesc{}, - Metadata: "accounts/accounts.proto", -} diff --git a/cmd/rove-accountant/internal/accounts_test.go b/pkg/accounts/accounts_test.go similarity index 86% rename from cmd/rove-accountant/internal/accounts_test.go rename to pkg/accounts/accounts_test.go index 8ac3315..bd2416f 100644 --- a/cmd/rove-accountant/internal/accounts_test.go +++ b/pkg/accounts/accounts_test.go @@ -1,4 +1,4 @@ -package internal +package accounts import ( "testing" @@ -8,7 +8,7 @@ import ( func TestNewAccountant(t *testing.T) { // Very basic verify here for now - accountant := NewAccountant() + accountant := NewSimpleAccountant() if accountant == nil { t.Error("Failed to create accountant") } @@ -16,7 +16,7 @@ func TestNewAccountant(t *testing.T) { func TestAccountant_RegisterAccount(t *testing.T) { - accountant := NewAccountant() + accountant := NewSimpleAccountant() // Start by making two accounts @@ -44,10 +44,7 @@ func TestAccountant_RegisterAccount(t *testing.T) { } func TestAccountant_AssignGetData(t *testing.T) { - accountant := NewAccountant() - if len(accountant.Accounts) != 0 { - t.Error("New accountant created with non-zero account number") - } + accountant := NewSimpleAccountant() name := uuid.New().String() a, err := accountant.RegisterAccount(name) diff --git a/pkg/accounts/simpleAccountant.go b/pkg/accounts/simpleAccountant.go new file mode 100644 index 0000000..9d6c43f --- /dev/null +++ b/pkg/accounts/simpleAccountant.go @@ -0,0 +1,90 @@ +package accounts + +import ( + "fmt" + "time" + + "github.com/google/uuid" +) + +// SimpleAccountant manages a set of accounts +type SimpleAccountant struct { + Accounts map[string]Account +} + +// NewSimpleAccountant creates a new accountant +func NewSimpleAccountant() Accountant { + return &SimpleAccountant{ + Accounts: make(map[string]Account), + } +} + +// RegisterAccount adds an account to the set of internal accounts +func (a *SimpleAccountant) RegisterAccount(name string) (acc Account, err error) { + + // Set up the account info + acc.Name = name + acc.Data = make(map[string]string) + + // Verify this acount isn't already registered + for _, a := range a.Accounts { + if a.Name == acc.Name { + return Account{}, fmt.Errorf("account name already registered: %s", a.Name) + } + } + + // Set the creation time + acc.Data["created"] = time.Now().String() + + // Create a secret + acc.Data["secret"] = uuid.New().String() + + // Simply add the account to the map + a.Accounts[acc.Name] = acc + + return +} + +// VerifySecret verifies if an account secret is correct +func (a *SimpleAccountant) VerifySecret(account string, secret string) (bool, error) { + // Find the account matching the ID + if this, ok := a.Accounts[account]; ok { + return this.Data["secret"] == secret, nil + } + + return false, fmt.Errorf("no account found for id: %s", account) +} + +// GetSecret gets the internal secret +func (a *SimpleAccountant) GetSecret(account string) (string, error) { + // Find the account matching the ID + if this, ok := a.Accounts[account]; ok { + return this.Data["secret"], nil + } + + return "", fmt.Errorf("no account found for id: %s", account) +} + +// AssignData assigns data to an account +func (a *SimpleAccountant) AssignData(account string, key string, value string) error { + + // Find the account matching the ID + if this, ok := a.Accounts[account]; ok { + this.Data[key] = value + a.Accounts[account] = this + } else { + return fmt.Errorf("no account found for id: %s", account) + } + + return nil +} + +// GetValue gets the rover rover for the account +func (a *SimpleAccountant) GetValue(account string, key string) (string, error) { + // Find the account matching the ID + this, ok := a.Accounts[account] + if !ok { + return "", fmt.Errorf("no account found for id: %s", account) + } + return this.Data[key], nil +} diff --git a/pkg/atlas/atlas.go b/pkg/atlas/atlas.go deleted file mode 100644 index 429efcb..0000000 --- a/pkg/atlas/atlas.go +++ /dev/null @@ -1,196 +0,0 @@ -package atlas - -import ( - "fmt" - "log" - "math/rand" - - "github.com/mdiluz/rove/pkg/maths" - "github.com/mdiluz/rove/pkg/objects" - "github.com/mdiluz/rove/pkg/vector" -) - -// Chunk represents a fixed square grid of tiles -type Chunk struct { - // Tiles represents the tiles within the chunk - Tiles []byte `json:"tiles"` -} - -// Atlas represents a grid of Chunks -type Atlas struct { - // Chunks represents all chunks in the world - // This is intentionally not a 2D array so it can be expanded in all directions - Chunks []Chunk `json:"chunks"` - - // size is the current width/height of the given atlas - Size int `json:"size"` - - // ChunkSize is the dimensions of each chunk - ChunkSize int `json:"chunksize"` -} - -// NewAtlas creates a new empty atlas -func NewAtlas(size, chunkSize int) Atlas { - if size%2 != 0 { - log.Fatal("atlas size must always be even") - } - - a := Atlas{ - Size: size, - Chunks: make([]Chunk, size*size), - ChunkSize: chunkSize, - } - - // Initialise all the chunks - for i := range a.Chunks { - tiles := make([]byte, chunkSize*chunkSize) - for i := 0; i < len(tiles); i++ { - tiles[i] = objects.Empty - } - a.Chunks[i] = Chunk{ - Tiles: tiles, - } - } - - return a -} - -// SpawnRocks peppers the world with rocks -func (a *Atlas) SpawnRocks() error { - extent := a.ChunkSize * (a.Size / 2) - - // Pepper the current world with rocks - for i := -extent; i < extent; i++ { - for j := -extent; j < extent; j++ { - if rand.Intn(16) == 0 { - if err := a.SetTile(vector.Vector{X: i, Y: j}, objects.SmallRock); err != nil { - return err - } - } - } - } - - return nil -} - -// SpawnWalls spawns the around the world -func (a *Atlas) SpawnWalls() error { - extent := a.ChunkSize * (a.Size / 2) - - // Surround the atlas in walls - for i := -extent; i < extent; i++ { - - if err := a.SetTile(vector.Vector{X: i, Y: extent - 1}, objects.LargeRock); err != nil { // N - return err - } else if err := a.SetTile(vector.Vector{X: extent - 1, Y: i}, objects.LargeRock); err != nil { // E - return err - } else if err := a.SetTile(vector.Vector{X: i, Y: -extent}, objects.LargeRock); err != nil { // S - return err - } else if err := a.SetTile(vector.Vector{X: -extent, Y: i}, objects.LargeRock); err != nil { // W - return err - } - } - - return nil -} - -// SetTile sets an individual tile's kind -func (a *Atlas) SetTile(v vector.Vector, tile byte) error { - chunk := a.toChunk(v) - if chunk >= len(a.Chunks) { - return fmt.Errorf("location outside of allocated atlas") - } - - local := a.toChunkLocal(v) - tileId := local.X + local.Y*a.ChunkSize - if tileId >= len(a.Chunks[chunk].Tiles) { - return fmt.Errorf("location outside of allocated chunk") - } - a.Chunks[chunk].Tiles[tileId] = tile - return nil -} - -// GetTile will return an individual tile -func (a *Atlas) GetTile(v vector.Vector) (byte, error) { - chunk := a.toChunk(v) - if chunk >= len(a.Chunks) { - return 0, fmt.Errorf("location outside of allocated atlas") - } - - local := a.toChunkLocal(v) - tileId := local.X + local.Y*a.ChunkSize - if tileId >= len(a.Chunks[chunk].Tiles) { - return 0, fmt.Errorf("location outside of allocated chunk") - } - - return a.Chunks[chunk].Tiles[tileId], nil -} - -// toChunkLocal gets a chunk local coordinate for a tile -func (a *Atlas) toChunkLocal(v vector.Vector) vector.Vector { - return vector.Vector{X: maths.Pmod(v.X, a.ChunkSize), Y: maths.Pmod(v.Y, a.ChunkSize)} -} - -// GetChunkID gets the chunk ID for a position in the world -func (a *Atlas) toChunk(v vector.Vector) int { - local := a.toChunkLocal(v) - // Get the chunk origin itself - origin := v.Added(local.Negated()) - // Divided it by the number of chunks - origin = origin.Divided(a.ChunkSize) - // Shift it by our size (our origin is in the middle) - origin = origin.Added(vector.Vector{X: a.Size / 2, Y: a.Size / 2}) - // Get the ID based on the final values - return (a.Size * origin.Y) + origin.X -} - -// chunkOrigin gets the chunk origin for a given chunk index -func (a *Atlas) chunkOrigin(chunk int) vector.Vector { - v := vector.Vector{ - X: maths.Pmod(chunk, a.Size) - (a.Size / 2), - Y: (chunk / a.Size) - (a.Size / 2), - } - - return v.Multiplied(a.ChunkSize) -} - -// GetWorldExtent gets the min and max valid coordinates of world -func (a *Atlas) GetWorldExtents() (min, max vector.Vector) { - min = vector.Vector{ - X: -(a.Size / 2) * a.ChunkSize, - Y: -(a.Size / 2) * a.ChunkSize, - } - max = vector.Vector{ - X: -min.X - 1, - Y: -min.Y - 1, - } - return -} - -// Grow will return a grown copy of the current atlas -func (a *Atlas) Grow(size int) error { - if size%2 != 0 { - return fmt.Errorf("atlas size must always be even") - } - delta := size - a.Size - if delta < 0 { - return fmt.Errorf("cannot shrink an atlas") - } else if delta == 0 { - return nil - } - - // Create a new atlas - newAtlas := NewAtlas(size, a.ChunkSize) - - // Copy old chunks into new chunks - for index, chunk := range a.Chunks { - // Calculate the new chunk location and copy over the data - newAtlas.Chunks[newAtlas.toChunk(a.chunkOrigin(index))] = chunk - } - - // Copy the new atlas data into this one - *a = newAtlas - - // Return the new atlas - return nil -} diff --git a/pkg/atlas/atlas_test.go b/pkg/atlas/atlas_test.go deleted file mode 100644 index f8cc63a..0000000 --- a/pkg/atlas/atlas_test.go +++ /dev/null @@ -1,170 +0,0 @@ -package atlas - -import ( - "testing" - - "github.com/mdiluz/rove/pkg/objects" - "github.com/mdiluz/rove/pkg/vector" - "github.com/stretchr/testify/assert" -) - -func TestAtlas_NewAtlas(t *testing.T) { - a := NewAtlas(2, 1) - assert.NotNil(t, a) - // Tiles should look like: 2 | 3 - // ----- - // 0 | 1 - assert.Equal(t, 4, len(a.Chunks)) - - a = NewAtlas(4, 1) - assert.NotNil(t, a) - // Tiles should look like: 2 | 3 - // ----- - // 0 | 1 - assert.Equal(t, 16, len(a.Chunks)) -} - -func TestAtlas_toChunk(t *testing.T) { - a := NewAtlas(2, 1) - assert.NotNil(t, a) - // Tiles should look like: 2 | 3 - // ----- - // 0 | 1 - tile := a.toChunk(vector.Vector{X: 0, Y: 0}) - assert.Equal(t, 3, tile) - tile = a.toChunk(vector.Vector{X: 0, Y: -1}) - assert.Equal(t, 1, tile) - tile = a.toChunk(vector.Vector{X: -1, Y: -1}) - assert.Equal(t, 0, tile) - tile = a.toChunk(vector.Vector{X: -1, Y: 0}) - assert.Equal(t, 2, tile) - - a = NewAtlas(2, 2) - assert.NotNil(t, a) - // Tiles should look like: - // 2 | 3 - // ----- - // 0 | 1 - tile = a.toChunk(vector.Vector{X: 1, Y: 1}) - assert.Equal(t, 3, tile) - tile = a.toChunk(vector.Vector{X: 1, Y: -2}) - assert.Equal(t, 1, tile) - tile = a.toChunk(vector.Vector{X: -2, Y: -2}) - assert.Equal(t, 0, tile) - tile = a.toChunk(vector.Vector{X: -2, Y: 1}) - assert.Equal(t, 2, tile) - - a = NewAtlas(4, 2) - assert.NotNil(t, a) - // Tiles should look like: - // 12| 13|| 14| 15 - // ---------------- - // 8 | 9 || 10| 11 - // ================ - // 4 | 5 || 6 | 7 - // ---------------- - // 0 | 1 || 2 | 3 - tile = a.toChunk(vector.Vector{X: 1, Y: 3}) - assert.Equal(t, 14, tile) - tile = a.toChunk(vector.Vector{X: 1, Y: -3}) - assert.Equal(t, 2, tile) - tile = a.toChunk(vector.Vector{X: -1, Y: -1}) - assert.Equal(t, 5, tile) - tile = a.toChunk(vector.Vector{X: -2, Y: 2}) - assert.Equal(t, 13, tile) -} - -func TestAtlas_GetSetTile(t *testing.T) { - a := NewAtlas(4, 10) - assert.NotNil(t, a) - - // Set the origin tile to 1 and test it - assert.NoError(t, a.SetTile(vector.Vector{X: 0, Y: 0}, 1)) - tile, err := a.GetTile(vector.Vector{X: 0, Y: 0}) - assert.NoError(t, err) - assert.Equal(t, byte(1), tile) - - // Set another tile to 1 and test it - assert.NoError(t, a.SetTile(vector.Vector{X: 5, Y: -2}, 2)) - tile, err = a.GetTile(vector.Vector{X: 5, Y: -2}) - assert.NoError(t, err) - assert.Equal(t, byte(2), tile) -} - -func TestAtlas_Grown(t *testing.T) { - // Start with a small example - a := NewAtlas(2, 2) - assert.NotNil(t, a) - assert.Equal(t, 4, len(a.Chunks)) - - // Set a few tiles to values - assert.NoError(t, a.SetTile(vector.Vector{X: 0, Y: 0}, 1)) - assert.NoError(t, a.SetTile(vector.Vector{X: -1, Y: -1}, 2)) - assert.NoError(t, a.SetTile(vector.Vector{X: 1, Y: -2}, 3)) - - // Grow once to just double it - err := a.Grow(4) - assert.NoError(t, err) - assert.Equal(t, 16, len(a.Chunks)) - - tile, err := a.GetTile(vector.Vector{X: 0, Y: 0}) - assert.NoError(t, err) - assert.Equal(t, byte(1), tile) - - tile, err = a.GetTile(vector.Vector{X: -1, Y: -1}) - assert.NoError(t, err) - assert.Equal(t, byte(2), tile) - - tile, err = a.GetTile(vector.Vector{X: 1, Y: -2}) - assert.NoError(t, err) - assert.Equal(t, byte(3), tile) - - // Grow it again even bigger - err = a.Grow(10) - assert.NoError(t, err) - assert.Equal(t, 100, len(a.Chunks)) - - tile, err = a.GetTile(vector.Vector{X: 0, Y: 0}) - assert.NoError(t, err) - assert.Equal(t, byte(1), tile) - - tile, err = a.GetTile(vector.Vector{X: -1, Y: -1}) - assert.NoError(t, err) - assert.Equal(t, byte(2), tile) - - tile, err = a.GetTile(vector.Vector{X: 1, Y: -2}) - assert.NoError(t, err) - assert.Equal(t, byte(3), tile) -} - -func TestAtlas_SpawnWorld(t *testing.T) { - // Start with a small example - a := NewAtlas(2, 4) - assert.NotNil(t, a) - assert.Equal(t, 4, len(a.Chunks)) - assert.NoError(t, a.SpawnWalls()) - - for i := -4; i < 4; i++ { - tile, err := a.GetTile(vector.Vector{X: i, Y: -4}) - assert.NoError(t, err) - assert.Equal(t, objects.LargeRock, tile) - } - - for i := -4; i < 4; i++ { - tile, err := a.GetTile(vector.Vector{X: -4, Y: i}) - assert.NoError(t, err) - assert.Equal(t, objects.LargeRock, tile) - } - - for i := -4; i < 4; i++ { - tile, err := a.GetTile(vector.Vector{X: 3, Y: i}) - assert.NoError(t, err) - assert.Equal(t, objects.LargeRock, tile) - } - - for i := -4; i < 4; i++ { - tile, err := a.GetTile(vector.Vector{X: i, Y: 3}) - assert.NoError(t, err) - assert.Equal(t, objects.LargeRock, tile) - } -} diff --git a/pkg/bearing/bearing.go b/pkg/bearing/bearing.go deleted file mode 100644 index 6eec327..0000000 --- a/pkg/bearing/bearing.go +++ /dev/null @@ -1,76 +0,0 @@ -package bearing - -import ( - "fmt" - "strings" - - "github.com/mdiluz/rove/pkg/vector" -) - -// Bearing describes a compass direction -type Bearing int - -const ( - North Bearing = iota - NorthEast - East - SouthEast - South - SouthWest - West - NorthWest -) - -// bearingString simply describes the strings associated with a direction -type bearingString struct { - Long string - Short string -} - -// bearingStrings is the set of strings for each direction -var bearingStrings = []bearingString{ - {"North", "N"}, - {"NorthEast", "NE"}, - {"East", "E"}, - {"SouthEast", "SE"}, - {"South", "S"}, - {"SouthWest", "SW"}, - {"West", "W"}, - {"NorthWest", "NW"}, -} - -// String converts a Direction to a String -func (d Bearing) String() string { - return bearingStrings[d].Long -} - -// ShortString converts a Direction to a short string version -func (d Bearing) ShortString() string { - return bearingStrings[d].Short -} - -// FromString gets the Direction from a string -func FromString(s string) (Bearing, error) { - for i, d := range bearingStrings { - if strings.ToLower(d.Long) == strings.ToLower(s) || strings.ToLower(d.Short) == strings.ToLower(s) { - return Bearing(i), nil - } - } - return -1, fmt.Errorf("unknown bearing: %s", s) -} - -var bearingVectors = []vector.Vector{ - {X: 0, Y: 1}, // N - {X: 1, Y: 1}, // NE - {X: 1, Y: 0}, // E - {X: 1, Y: -1}, // SE - {X: 0, Y: -1}, // S - {X: -1, Y: 1}, // SW - {X: -1, Y: 0}, // W - {X: -1, Y: 1}, // NW -} - -// Vector converts a Direction to a Vector -func (d Bearing) Vector() vector.Vector { - return bearingVectors[d] -} diff --git a/pkg/bearing/bearing_test.go b/pkg/bearing/bearing_test.go deleted file mode 100644 index 1232239..0000000 --- a/pkg/bearing/bearing_test.go +++ /dev/null @@ -1,32 +0,0 @@ -package bearing - -import ( - "testing" - - "github.com/mdiluz/rove/pkg/vector" - "github.com/stretchr/testify/assert" -) - -func TestDirection(t *testing.T) { - dir := North - - assert.Equal(t, "North", dir.String()) - assert.Equal(t, "N", dir.ShortString()) - assert.Equal(t, vector.Vector{X: 0, Y: 1}, dir.Vector()) - - dir, err := FromString("N") - assert.NoError(t, err) - assert.Equal(t, North, dir) - - dir, err = FromString("n") - assert.NoError(t, err) - assert.Equal(t, North, dir) - - dir, err = FromString("north") - assert.NoError(t, err) - assert.Equal(t, North, dir) - - dir, err = FromString("NorthWest") - assert.NoError(t, err) - assert.Equal(t, NorthWest, dir) -} diff --git a/pkg/game/command.go b/pkg/game/command.go deleted file mode 100644 index 2a5f875..0000000 --- a/pkg/game/command.go +++ /dev/null @@ -1,23 +0,0 @@ -package game - -const ( - // Moves the rover in the chosen bearing - CommandMove = "move" - - // Will attempt to stash the object at the current location - CommandStash = "stash" - - // Will attempt to repair the rover with an inventory object - CommandRepair = "repair" -) - -// Command represends a single command to execute -type Command struct { - Command string `json:"command"` - - // Used in the move command - Bearing string `json:"bearing,omitempty"` -} - -// CommandStream is a list of commands to execute in order -type CommandStream []Command diff --git a/pkg/game/command_test.go b/pkg/game/command_test.go deleted file mode 100644 index 11da854..0000000 --- a/pkg/game/command_test.go +++ /dev/null @@ -1,34 +0,0 @@ -package game - -import ( - "testing" - - "github.com/mdiluz/rove/pkg/vector" - "github.com/stretchr/testify/assert" -) - -func TestCommand_Move(t *testing.T) { - world := NewWorld(2, 8) - a, err := world.SpawnRover() - assert.NoError(t, err) - pos := vector.Vector{ - X: 1.0, - Y: 2.0, - } - - err = world.WarpRover(a, pos) - assert.NoError(t, err, "Failed to set position for rover") - - // Try the move command - moveCommand := Command{Command: CommandMove, Bearing: "N"} - assert.NoError(t, world.Enqueue(a, moveCommand), "Failed to execute move command") - - // Tick the world - world.EnqueueAllIncoming() - world.ExecuteCommandQueues() - - newPos, err := world.RoverPosition(a) - assert.NoError(t, err, "Failed to set position for rover") - pos.Add(vector.Vector{X: 0.0, Y: 1}) - assert.Equal(t, pos, newPos, "Failed to correctly set position for rover") -} diff --git a/pkg/game/rover.go b/pkg/game/rover.go deleted file mode 100644 index e315363..0000000 --- a/pkg/game/rover.go +++ /dev/null @@ -1,23 +0,0 @@ -package game - -import ( - "github.com/mdiluz/rove/pkg/vector" -) - -// Rover describes a single rover in the world -type Rover struct { - // Unique name of this rover - Name string `json:"name"` - - // Pos represents where this rover is in the world - Pos vector.Vector `json:"pos"` - - // Range represents the distance the unit's radar can see - Range int `json:"range"` - - // Inventory represents any items the rover is carrying - Inventory []byte `json:"inventory"` - - // Integrity represents current rover health - Integrity int `json:"integrity"` -} diff --git a/pkg/game/world.go b/pkg/game/world.go deleted file mode 100644 index 8491a7d..0000000 --- a/pkg/game/world.go +++ /dev/null @@ -1,501 +0,0 @@ -package game - -import ( - "bufio" - "fmt" - "log" - "math" - "math/rand" - "os" - "sync" - - "github.com/google/uuid" - "github.com/mdiluz/rove/pkg/atlas" - "github.com/mdiluz/rove/pkg/bearing" - "github.com/mdiluz/rove/pkg/maths" - "github.com/mdiluz/rove/pkg/objects" - "github.com/mdiluz/rove/pkg/vector" -) - -// World describes a self contained universe and everything in it -type World struct { - // Rovers is a id->data map of all the rovers in the game - Rovers map[string]Rover `json:"rovers"` - - // Atlas represends the world map of chunks and tiles - Atlas atlas.Atlas `json:"atlas"` - - // Mutex to lock around all world operations - worldMutex sync.RWMutex - - // Commands is the set of currently executing command streams per rover - CommandQueue map[string]CommandStream `json:"commands"` - - // Incoming represents the set of commands to add to the queue at the end of the current tick - Incoming map[string]CommandStream `json:"incoming"` - - // Mutex to lock around command operations - cmdMutex sync.RWMutex - - // Set of possible words to use for names - words []string -} - -var wordsFile = os.Getenv("WORDS_FILE") - -// NewWorld creates a new world object -func NewWorld(size, chunkSize int) *World { - - // Try and load the words file - var lines []string - if file, err := os.Open(wordsFile); err != nil { - log.Printf("Couldn't read words file [%s], running without words: %s\n", wordsFile, err) - } else { - defer file.Close() - scanner := bufio.NewScanner(file) - for scanner.Scan() { - lines = append(lines, scanner.Text()) - } - if scanner.Err() != nil { - log.Printf("Failure during word file scan: %s\n", scanner.Err()) - } - } - - return &World{ - Rovers: make(map[string]Rover), - CommandQueue: make(map[string]CommandStream), - Incoming: make(map[string]CommandStream), - Atlas: atlas.NewAtlas(size, chunkSize), - words: lines, - } -} - -// SpawnWorld spawns a border at the edge of the world atlas -func (w *World) SpawnWorld(fillWorld bool) error { - w.worldMutex.Lock() - defer w.worldMutex.Unlock() - if fillWorld { - if err := w.Atlas.SpawnRocks(); err != nil { - return err - } - } - return w.Atlas.SpawnWalls() -} - -// SpawnRover adds an rover to the game -func (w *World) SpawnRover() (string, error) { - w.worldMutex.Lock() - defer w.worldMutex.Unlock() - - // Initialise the rover - rover := Rover{ - Range: 4.0, - Integrity: 10, - Name: uuid.New().String(), - } - - // Assign a random name if we have words - if len(w.words) > 0 { - for { - // Loop until we find a unique name - name := fmt.Sprintf("%s-%s", w.words[rand.Intn(len(w.words))], w.words[rand.Intn(len(w.words))]) - if _, ok := w.Rovers[name]; !ok { - rover.Name = name - break - } - } - } - - // Spawn in a random place near the origin - rover.Pos = vector.Vector{ - X: w.Atlas.ChunkSize/2 - rand.Intn(w.Atlas.ChunkSize), - Y: w.Atlas.ChunkSize/2 - rand.Intn(w.Atlas.ChunkSize), - } - - // Seach until we error (run out of world) - for { - if tile, err := w.Atlas.GetTile(rover.Pos); err != nil { - return "", err - } else { - if !objects.IsBlocking(tile) { - break - } else { - // Try and spawn to the east of the blockage - rover.Pos.Add(vector.Vector{X: 1, Y: 0}) - } - } - } - - log.Printf("Spawned rover at %+v\n", rover.Pos) - - // Append the rover to the list - w.Rovers[rover.Name] = rover - - return rover.Name, nil -} - -// GetRover gets a specific rover by name -func (w *World) GetRover(rover string) (Rover, error) { - w.worldMutex.RLock() - defer w.worldMutex.RUnlock() - - if i, ok := w.Rovers[rover]; ok { - return i, nil - } else { - return Rover{}, fmt.Errorf("Failed to find rover with name: %s", rover) - } -} - -// Removes an rover from the game -func (w *World) DestroyRover(rover string) error { - w.worldMutex.Lock() - defer w.worldMutex.Unlock() - - if i, ok := w.Rovers[rover]; ok { - // Clear the tile - if err := w.Atlas.SetTile(i.Pos, objects.Empty); err != nil { - return fmt.Errorf("coudln't clear old rover tile: %s", err) - } - delete(w.Rovers, rover) - } else { - return fmt.Errorf("no rover matching id") - } - return nil -} - -// RoverPosition returns the position of the rover -func (w *World) RoverPosition(rover string) (vector.Vector, error) { - w.worldMutex.RLock() - defer w.worldMutex.RUnlock() - - if i, ok := w.Rovers[rover]; ok { - return i.Pos, nil - } else { - return vector.Vector{}, fmt.Errorf("no rover matching id") - } -} - -// SetRoverPosition sets the position of the rover -func (w *World) SetRoverPosition(rover string, pos vector.Vector) error { - w.worldMutex.Lock() - defer w.worldMutex.Unlock() - - if i, ok := w.Rovers[rover]; ok { - i.Pos = pos - w.Rovers[rover] = i - return nil - } else { - return fmt.Errorf("no rover matching id") - } -} - -// RoverInventory returns the inventory of a requested rover -func (w *World) RoverInventory(rover string) ([]byte, error) { - w.worldMutex.RLock() - defer w.worldMutex.RUnlock() - - if i, ok := w.Rovers[rover]; ok { - return i.Inventory, nil - } else { - return nil, fmt.Errorf("no rover matching id") - } -} - -// WarpRover sets an rovers position -func (w *World) WarpRover(rover string, pos vector.Vector) error { - w.worldMutex.Lock() - defer w.worldMutex.Unlock() - - if i, ok := w.Rovers[rover]; ok { - // Nothing to do if these positions match - if i.Pos == pos { - return nil - } - - // Check the tile is not blocked - if tile, err := w.Atlas.GetTile(pos); err != nil { - return fmt.Errorf("coudln't get state of destination rover tile: %s", err) - } else if objects.IsBlocking(tile) { - return fmt.Errorf("can't warp rover to occupied tile, check before warping") - } - - i.Pos = pos - w.Rovers[rover] = i - return nil - } else { - return fmt.Errorf("no rover matching id") - } -} - -// SetPosition sets an rovers position -func (w *World) MoveRover(rover string, b bearing.Bearing) (vector.Vector, error) { - w.worldMutex.Lock() - defer w.worldMutex.Unlock() - - if i, ok := w.Rovers[rover]; ok { - // Try the new move position - newPos := i.Pos.Added(b.Vector()) - - // Get the tile and verify it's empty - if tile, err := w.Atlas.GetTile(newPos); err != nil { - return vector.Vector{}, fmt.Errorf("couldn't get tile for new position: %s", err) - } else if !objects.IsBlocking(tile) { - // Perform the move - i.Pos = newPos - w.Rovers[rover] = i - } else { - // If it is a blocking tile, reduce the rover integrity - i.Integrity = i.Integrity - 1 - if i.Integrity == 0 { - // TODO: The rover needs to be left dormant with the player - } else { - w.Rovers[rover] = i - } - } - - return i.Pos, nil - } else { - return vector.Vector{}, fmt.Errorf("no rover matching id") - } -} - -// RoverStash will stash an item at the current rovers position -func (w *World) RoverStash(rover string) (byte, error) { - w.worldMutex.Lock() - defer w.worldMutex.Unlock() - - if r, ok := w.Rovers[rover]; ok { - if tile, err := w.Atlas.GetTile(r.Pos); err != nil { - return objects.Empty, err - } else { - if objects.IsStashable(tile) { - r.Inventory = append(r.Inventory, tile) - w.Rovers[rover] = r - if err := w.Atlas.SetTile(r.Pos, objects.Empty); err != nil { - return objects.Empty, err - } else { - return tile, nil - } - } - } - - } else { - return objects.Empty, fmt.Errorf("no rover matching id") - } - - return objects.Empty, nil -} - -// RadarFromRover can be used to query what a rover can currently see -func (w *World) RadarFromRover(rover string) ([]byte, error) { - w.worldMutex.RLock() - defer w.worldMutex.RUnlock() - - if r, ok := w.Rovers[rover]; ok { - // The radar should span in range direction on each axis, plus the row/column the rover is currently on - radarSpan := (r.Range * 2) + 1 - roverPos := r.Pos - - // Get the radar min and max values - radarMin := vector.Vector{ - X: roverPos.X - r.Range, - Y: roverPos.Y - r.Range, - } - radarMax := vector.Vector{ - X: roverPos.X + r.Range, - Y: roverPos.Y + r.Range, - } - - // Make sure we only query within the actual world - worldMin, worldMax := w.Atlas.GetWorldExtents() - scanMin := vector.Vector{ - X: maths.Max(radarMin.X, worldMin.X), - Y: maths.Max(radarMin.Y, worldMin.Y), - } - scanMax := vector.Vector{ - X: maths.Min(radarMax.X, worldMax.X), - Y: maths.Min(radarMax.Y, worldMax.Y), - } - - // Gather up all tiles within the range - var radar = make([]byte, radarSpan*radarSpan) - for j := scanMin.Y; j <= scanMax.Y; j++ { - for i := scanMin.X; i <= scanMax.X; i++ { - q := vector.Vector{X: i, Y: j} - - if tile, err := w.Atlas.GetTile(q); err != nil { - return nil, fmt.Errorf("failed to query tile: %s", err) - - } else { - // Get the position relative to the bottom left of the radar - relative := q.Added(radarMin.Negated()) - index := relative.X + relative.Y*radarSpan - radar[index] = tile - } - } - } - - // Add all rovers to the radar - for _, r := range w.Rovers { - // If the rover is in range - dist := r.Pos.Added(roverPos.Negated()) - dist = dist.Abs() - - if dist.X <= r.Range && dist.Y <= r.Range { - relative := r.Pos.Added(radarMin.Negated()) - index := relative.X + relative.Y*radarSpan - radar[index] = objects.Rover - } - } - - // Add this rover - radar[len(radar)/2] = objects.Rover - - return radar, nil - } else { - return nil, fmt.Errorf("no rover matching id") - } -} - -// Enqueue will queue the commands given -func (w *World) Enqueue(rover string, commands ...Command) error { - - // First validate the commands - for _, c := range commands { - switch c.Command { - case CommandMove: - if _, err := bearing.FromString(c.Bearing); err != nil { - return fmt.Errorf("unknown bearing: %s", c.Bearing) - } - case CommandStash: - case CommandRepair: - // Nothing to verify - default: - return fmt.Errorf("unknown command: %s", c.Command) - } - } - - // Lock our commands edit - w.cmdMutex.Lock() - defer w.cmdMutex.Unlock() - - // Append the commands to the incoming set - if cmds, ok := w.Incoming[rover]; ok { - w.Incoming[rover] = append(cmds, commands...) - } else { - w.Incoming[rover] = commands - } - - return nil -} - -// EnqueueAllIncoming will enqueue the incoming commands -func (w *World) EnqueueAllIncoming() { - // Add any incoming commands from this tick and clear that queue - for id, incoming := range w.Incoming { - commands := w.CommandQueue[id] - commands = append(commands, incoming...) - w.CommandQueue[id] = commands - } - w.Incoming = make(map[string]CommandStream) -} - -// Execute will execute any commands in the current command queue -func (w *World) ExecuteCommandQueues() { - w.cmdMutex.Lock() - defer w.cmdMutex.Unlock() - - // Iterate through all the current commands - for rover, cmds := range w.CommandQueue { - if len(cmds) != 0 { - // Extract the first command in the queue - c := cmds[0] - w.CommandQueue[rover] = cmds[1:] - - // Execute the command - if err := w.ExecuteCommand(&c, rover); err != nil { - log.Println(err) - // TODO: Report this error somehow - } - - } else { - // Clean out the empty entry - delete(w.CommandQueue, rover) - } - } - - // Add any incoming commands from this tick and clear that queue - w.EnqueueAllIncoming() -} - -// ExecuteCommand will execute a single command -func (w *World) ExecuteCommand(c *Command, rover string) (err error) { - log.Printf("Executing command: %+v\n", *c) - - switch c.Command { - case CommandMove: - if dir, err := bearing.FromString(c.Bearing); err != nil { - return err - - } else if _, err := w.MoveRover(rover, dir); err != nil { - return err - - } - - case CommandStash: - if _, err := w.RoverStash(rover); err != nil { - return err - } - - case CommandRepair: - if r, err := w.GetRover(rover); err != nil { - return err - } else { - // Consume an inventory item to repair - if len(r.Inventory) > 0 { - r.Inventory = r.Inventory[:len(r.Inventory)-1] - r.Integrity = r.Integrity + 1 - w.Rovers[rover] = r - } - } - default: - return fmt.Errorf("unknown command: %s", c.Command) - } - - return -} - -// PrintTiles simply prints the input tiles directly for debug -func PrintTiles(tiles []byte) { - num := int(math.Sqrt(float64(len(tiles)))) - for j := num - 1; j >= 0; j-- { - for i := 0; i < num; i++ { - fmt.Printf("%c", tiles[i+num*j]) - } - fmt.Print("\n") - } -} - -// RLock read locks the world -func (w *World) RLock() { - w.worldMutex.RLock() - w.cmdMutex.RLock() -} - -// RUnlock read unlocks the world -func (w *World) RUnlock() { - w.worldMutex.RUnlock() - w.cmdMutex.RUnlock() -} - -// Lock locks the world -func (w *World) Lock() { - w.worldMutex.Lock() - w.cmdMutex.Lock() -} - -// Unlock unlocks the world -func (w *World) Unlock() { - w.worldMutex.Unlock() - w.cmdMutex.Unlock() -} diff --git a/pkg/game/world_test.go b/pkg/game/world_test.go deleted file mode 100644 index 5a48231..0000000 --- a/pkg/game/world_test.go +++ /dev/null @@ -1,234 +0,0 @@ -package game - -import ( - "testing" - - "github.com/mdiluz/rove/pkg/bearing" - "github.com/mdiluz/rove/pkg/objects" - "github.com/mdiluz/rove/pkg/vector" - "github.com/stretchr/testify/assert" -) - -func TestNewWorld(t *testing.T) { - // Very basic for now, nothing to verify - world := NewWorld(4, 4) - if world == nil { - t.Error("Failed to create world") - } -} - -func TestWorld_CreateRover(t *testing.T) { - world := NewWorld(2, 8) - a, err := world.SpawnRover() - assert.NoError(t, err) - b, err := world.SpawnRover() - assert.NoError(t, err) - - // Basic duplicate check - if a == b { - t.Errorf("Created identical rovers") - } else if len(world.Rovers) != 2 { - t.Errorf("Incorrect number of rovers created") - } -} - -func TestWorld_GetRover(t *testing.T) { - world := NewWorld(2, 4) - a, err := world.SpawnRover() - assert.NoError(t, err) - - rover, err := world.GetRover(a) - assert.NoError(t, err, "Failed to get rover attribs") - assert.NotZero(t, rover.Range, "Rover should not be spawned blind") -} - -func TestWorld_DestroyRover(t *testing.T) { - world := NewWorld(4, 1) - a, err := world.SpawnRover() - assert.NoError(t, err) - b, err := world.SpawnRover() - assert.NoError(t, err) - - err = world.DestroyRover(a) - assert.NoError(t, err, "Error returned from rover destroy") - - // Basic duplicate check - if len(world.Rovers) != 1 { - t.Error("Too many rovers left in world") - } else if _, ok := world.Rovers[b]; !ok { - t.Error("Remaining rover is incorrect") - } -} - -func TestWorld_GetSetMovePosition(t *testing.T) { - world := NewWorld(4, 4) - a, err := world.SpawnRover() - assert.NoError(t, err) - - pos := vector.Vector{ - X: 0.0, - Y: 0.0, - } - - err = world.WarpRover(a, pos) - assert.NoError(t, err, "Failed to set position for rover") - - newPos, err := world.RoverPosition(a) - assert.NoError(t, err, "Failed to set position for rover") - assert.Equal(t, pos, newPos, "Failed to correctly set position for rover") - - b := bearing.North - newPos, err = world.MoveRover(a, b) - assert.NoError(t, err, "Failed to set position for rover") - pos.Add(vector.Vector{X: 0, Y: 1}) - assert.Equal(t, pos, newPos, "Failed to correctly move position for rover") - - // Place a tile in front of the rover - assert.NoError(t, world.Atlas.SetTile(vector.Vector{X: 0, Y: 2}, objects.LargeRock)) - newPos, err = world.MoveRover(a, b) - assert.Equal(t, pos, newPos, "Failed to correctly not move position for rover into wall") -} - -func TestWorld_RadarFromRover(t *testing.T) { - // Create world that should have visible walls on the radar - world := NewWorld(4, 2) - a, err := world.SpawnRover() - assert.NoError(t, err) - b, err := world.SpawnRover() - assert.NoError(t, err) - - // Warp the rovers into position - bpos := vector.Vector{X: -3, Y: -3} - assert.NoError(t, world.WarpRover(b, bpos), "Failed to warp rover") - assert.NoError(t, world.WarpRover(a, vector.Vector{X: 0, Y: 0}), "Failed to warp rover") - - // Spawn the world wall - err = world.Atlas.SpawnWalls() - assert.NoError(t, err) - - radar, err := world.RadarFromRover(a) - assert.NoError(t, err, "Failed to get radar from rover") - fullRange := 4 + 4 + 1 - assert.Equal(t, fullRange*fullRange, len(radar), "Radar returned wrong length") - - // It should look like: - // --------- - // OOOOOOOO- - // O------O- - // O------O- - // O---R--O- - // O------O- - // O------O- - // OR-----O- - // OOOOOOOO- - PrintTiles(radar) - - // Test all expected values - assert.Equal(t, objects.Rover, radar[1+fullRange]) - assert.Equal(t, objects.Rover, radar[4+4*fullRange]) - for i := 0; i < 8; i++ { - assert.Equal(t, objects.LargeRock, radar[i]) - assert.Equal(t, objects.LargeRock, radar[i+(7*9)]) - assert.Equal(t, objects.LargeRock, radar[i*9]) - assert.Equal(t, objects.LargeRock, radar[(i*9)+7]) - } -} - -func TestWorld_RoverStash(t *testing.T) { - world := NewWorld(2, 2) - a, err := world.SpawnRover() - assert.NoError(t, err) - - pos := vector.Vector{ - X: 0.0, - Y: 0.0, - } - - err = world.WarpRover(a, pos) - assert.NoError(t, err, "Failed to set position for rover") - - err = world.Atlas.SetTile(pos, objects.SmallRock) - assert.NoError(t, err, "Failed to set tile to rock") - - o, err := world.RoverStash(a) - assert.NoError(t, err, "Failed to stash") - assert.Equal(t, objects.SmallRock, o, "Failed to get correct object") - - tile, err := world.Atlas.GetTile(pos) - assert.NoError(t, err, "Failed to get tile") - assert.Equal(t, objects.Empty, tile, "Stash failed to remove object from atlas") - - inv, err := world.RoverInventory(a) - assert.NoError(t, err, "Failed to get inventory") - assert.Equal(t, objects.SmallRock, inv[0]) -} - -func TestWorld_RoverDamage(t *testing.T) { - world := NewWorld(2, 2) - a, err := world.SpawnRover() - assert.NoError(t, err) - - pos := vector.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) - assert.NoError(t, err, "couldn't get rover info") - - err = world.Atlas.SetTile(vector.Vector{X: 0.0, Y: 1.0}, objects.LargeRock) - assert.NoError(t, err, "Failed to set tile to rock") - - vec, err := world.MoveRover(a, 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, info.Integrity-1, newinfo.Integrity, "rover should have lost integrity") -} - -func TestWorld_RoverRepair(t *testing.T) { - world := NewWorld(2, 2) - a, err := world.SpawnRover() - assert.NoError(t, err) - - pos := vector.Vector{ - X: 0.0, - Y: 0.0, - } - - err = world.WarpRover(a, pos) - assert.NoError(t, err, "Failed to set position for rover") - - originalInfo, err := world.GetRover(a) - assert.NoError(t, err, "couldn't get rover info") - - err = world.Atlas.SetTile(pos, objects.SmallRock) - assert.NoError(t, err, "Failed to set tile to rock") - - o, err := world.RoverStash(a) - assert.NoError(t, err, "Failed to stash") - assert.Equal(t, objects.SmallRock, o, "Failed to get correct object") - - err = world.Atlas.SetTile(vector.Vector{X: 0.0, Y: 1.0}, objects.LargeRock) - assert.NoError(t, err, "Failed to set tile to rock") - - vec, err := world.MoveRover(a, bearing.North) - assert.NoError(t, err, "Failed to move rover") - assert.Equal(t, pos, vec, "Rover managed to move into large rock") - - newinfo, err := world.GetRover(a) - assert.NoError(t, err, "couldn't get rover info") - assert.Equal(t, originalInfo.Integrity-1, newinfo.Integrity, "rover should have lost integrity") - - err = world.ExecuteCommand(&Command{Command: CommandRepair}, 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") -} diff --git a/pkg/maths/maths.go b/pkg/maths/maths.go index 7e1d0fc..cd40516 100644 --- a/pkg/maths/maths.go +++ b/pkg/maths/maths.go @@ -8,7 +8,7 @@ func Abs(x int) int { return x } -// pmod is a mositive modulo +// Pmod is a mositive modulo // golang's % is a "remainder" function si misbehaves for negative modulus inputs func Pmod(x, d int) int { if x == 0 || d == 0 { @@ -39,3 +39,19 @@ func Min(x, y int) int { } return x } + +// RoundUp rounds a value up to the nearest multiple +func RoundUp(toRound int, multiple int) int { + remainder := Pmod(toRound, multiple) + if remainder == 0 { + return toRound + } + + return (multiple - remainder) + toRound +} + +// RoundDown rounds a value down to the nearest multiple +func RoundDown(toRound int, multiple int) int { + remainder := Pmod(toRound, multiple) + return toRound - remainder +} diff --git a/pkg/maths/maths_test.go b/pkg/maths/maths_test.go index cf2c180..730da77 100644 --- a/pkg/maths/maths_test.go +++ b/pkg/maths/maths_test.go @@ -29,5 +29,19 @@ func TestMin(t *testing.T) { assert.Equal(t, 100, Min(100, 500)) assert.Equal(t, -4, Min(-4, 1)) assert.Equal(t, -4, Min(-4, -2)) +} + +func TestRoundUp(t *testing.T) { + assert.Equal(t, 10, RoundUp(10, 5)) + assert.Equal(t, 12, RoundUp(10, 4)) + assert.Equal(t, -8, RoundUp(-8, 4)) + assert.Equal(t, -4, RoundUp(-7, 4)) +} + +func TestRoundDown(t *testing.T) { + assert.Equal(t, 10, RoundDown(10, 5)) + assert.Equal(t, 8, RoundDown(10, 4)) + assert.Equal(t, -8, RoundDown(-8, 4)) + assert.Equal(t, -8, RoundDown(-7, 4)) } diff --git a/pkg/maths/vector.go b/pkg/maths/vector.go new file mode 100644 index 0000000..a74ff16 --- /dev/null +++ b/pkg/maths/vector.go @@ -0,0 +1,119 @@ +package maths + +import ( + "math" + + "github.com/mdiluz/rove/proto/roveapi" +) + +// Vector desribes a 3D vector +type Vector struct { + X int + Y int +} + +// Add adds one vector to another +func (v *Vector) Add(v2 Vector) { + v.X += v2.X + v.Y += v2.Y +} + +// Added calculates a new vector +func (v Vector) Added(v2 Vector) Vector { + v.Add(v2) + return v +} + +// Negated returns a negated vector +func (v Vector) Negated() Vector { + return Vector{-v.X, -v.Y} +} + +// Length returns the length of the vector +func (v Vector) Length() float64 { + return math.Sqrt(float64(v.X*v.X + v.Y*v.Y)) +} + +// Distance returns the distance between two vectors +func (v Vector) Distance(v2 Vector) float64 { + // Negate the two vectors and calciate the length + return v.Added(v2.Negated()).Length() +} + +// Multiplied returns the vector multiplied by an int +func (v Vector) Multiplied(val int) Vector { + return Vector{v.X * val, v.Y * val} +} + +// Divided returns the vector divided by an int +func (v Vector) Divided(val int) Vector { + return Vector{v.X / val, v.Y / val} +} + +// DividedFloor returns the vector divided but floors the value regardless +func (v Vector) DividedFloor(val int) Vector { + x := float64(v.X) / float64(val) + + if x < 0 { + x = math.Floor(x) + } else { + x = math.Floor(x) + } + y := float64(v.Y) / float64(val) + if y < 0 { + y = math.Floor(y) + } else { + y = math.Floor(y) + } + + return Vector{X: int(x), Y: int(y)} +} + +// Abs returns an absolute version of the vector +func (v Vector) Abs() Vector { + return Vector{Abs(v.X), Abs(v.Y)} +} + +// Min2 returns the minimum values in both vectors +func Min2(v1 Vector, v2 Vector) Vector { + return Vector{Min(v1.X, v2.X), Min(v1.Y, v2.Y)} +} + +// Max2 returns the max values in both vectors +func Max2(v1 Vector, v2 Vector) Vector { + return Vector{Max(v1.X, v2.X), Max(v1.Y, v2.Y)} +} + +// BearingToVector converts a bearing to a vector +func BearingToVector(b roveapi.Bearing) Vector { + switch b { + case roveapi.Bearing_North: + return Vector{Y: 1} + case roveapi.Bearing_NorthEast: + return Vector{X: 1, Y: 1} + case roveapi.Bearing_East: + return Vector{X: 1} + case roveapi.Bearing_SouthEast: + return Vector{X: 1, Y: -1} + case roveapi.Bearing_South: + return Vector{Y: -1} + case roveapi.Bearing_SouthWest: + return Vector{X: -1, Y: -1} + case roveapi.Bearing_West: + return Vector{X: -1} + case roveapi.Bearing_NorthWest: + return Vector{X: -1, Y: 1} + } + + return Vector{} +} + +// Dot returns the dot product of two vectors +func Dot(a Vector, b Vector) int { + return a.X*b.X + a.Y*b.Y +} + +// AngleCos returns the cosine of the angle between two vectors +func AngleCos(a Vector, b Vector) float64 { + return float64(Dot(a, b)) / a.Length() * b.Length() +} diff --git a/pkg/vector/vector_test.go b/pkg/maths/vector_test.go similarity index 99% rename from pkg/vector/vector_test.go rename to pkg/maths/vector_test.go index 5d92136..4177780 100644 --- a/pkg/vector/vector_test.go +++ b/pkg/maths/vector_test.go @@ -1,4 +1,4 @@ -package vector +package maths import ( "math" diff --git a/pkg/objects/objects.go b/pkg/objects/objects.go deleted file mode 100644 index 2f509c6..0000000 --- a/pkg/objects/objects.go +++ /dev/null @@ -1,37 +0,0 @@ -package objects - -const ( - Empty = byte(' ') - Rover = byte('R') - SmallRock = byte('o') - LargeRock = byte('O') -) - -// Check if an object is a blocking object -func IsBlocking(object byte) bool { - var blocking = [...]byte{ - Rover, - LargeRock, - } - - for _, t := range blocking { - if object == t { - return true - } - } - return false -} - -// Check if an object is stashable -func IsStashable(object byte) bool { - var stashable = [...]byte{ - SmallRock, - } - - for _, t := range stashable { - if object == t { - return true - } - } - return false -} diff --git a/pkg/persistence/persistence.go b/pkg/persistence/persistence.go index 8bb2e9d..fbe57b0 100644 --- a/pkg/persistence/persistence.go +++ b/pkg/persistence/persistence.go @@ -31,12 +31,13 @@ func jsonPath(name string) string { // Save will serialise the interface into a json file func Save(name string, data interface{}) error { p := jsonPath(name) - if b, err := json.MarshalIndent(data, "", " "); err != nil { + b, err := json.MarshalIndent(data, "", " ") + if err != nil { + return err + } + + if err := ioutil.WriteFile(p, b, os.ModePerm); err != nil { return err - } else { - if err := ioutil.WriteFile(p, b, os.ModePerm); err != nil { - return err - } } log.Printf("Saved %s\n", p) diff --git a/pkg/rove/atlas.go b/pkg/rove/atlas.go new file mode 100644 index 0000000..ab09dba --- /dev/null +++ b/pkg/rove/atlas.go @@ -0,0 +1,18 @@ +package rove + +import ( + "github.com/mdiluz/rove/pkg/maths" + "github.com/mdiluz/rove/proto/roveapi" +) + +// Atlas represents a 2D world atlas of tiles and objects +type Atlas interface { + // SetTile sets a location on the Atlas to a type of tile + SetTile(v maths.Vector, tile roveapi.Tile) + + // SetObject will set a location on the Atlas to contain an object + SetObject(v maths.Vector, obj Object) + + // QueryPosition queries a position on the atlas + QueryPosition(v maths.Vector) (roveapi.Tile, Object) +} diff --git a/pkg/rove/atlas_test.go b/pkg/rove/atlas_test.go new file mode 100644 index 0000000..56e5d8d --- /dev/null +++ b/pkg/rove/atlas_test.go @@ -0,0 +1,257 @@ +package rove + +import ( + "testing" + + "github.com/mdiluz/rove/pkg/maths" + "github.com/mdiluz/rove/proto/roveapi" + "github.com/stretchr/testify/assert" +) + +func TestAtlas_NewAtlas(t *testing.T) { + a := NewChunkAtlas(1).(*chunkBasedAtlas) + assert.NotNil(t, a) + assert.Equal(t, 1, a.ChunkSize) + assert.Equal(t, 1, len(a.Chunks)) // Should start empty +} + +func TestAtlas_toChunk(t *testing.T) { + a := NewChunkAtlas(1).(*chunkBasedAtlas) + assert.NotNil(t, a) + + // Get a tile to spawn the chunks + a.QueryPosition(maths.Vector{X: -1, Y: -1}) + a.QueryPosition(maths.Vector{X: 0, Y: 0}) + assert.Equal(t, 2*2, len(a.Chunks)) + + // Chunks should look like: + // 2 | 3 + // ----- + // 0 | 1 + chunkID := a.worldSpaceToChunkIndex(maths.Vector{X: 0, Y: 0}) + assert.Equal(t, 3, chunkID) + chunkID = a.worldSpaceToChunkIndex(maths.Vector{X: 0, Y: -1}) + assert.Equal(t, 1, chunkID) + chunkID = a.worldSpaceToChunkIndex(maths.Vector{X: -1, Y: -1}) + assert.Equal(t, 0, chunkID) + chunkID = a.worldSpaceToChunkIndex(maths.Vector{X: -1, Y: 0}) + assert.Equal(t, 2, chunkID) + + a = NewChunkAtlas(2).(*chunkBasedAtlas) + assert.NotNil(t, a) + // Get a tile to spawn the chunks + a.QueryPosition(maths.Vector{X: -2, Y: -2}) + assert.Equal(t, 2*2, len(a.Chunks)) + a.QueryPosition(maths.Vector{X: 1, Y: 1}) + assert.Equal(t, 2*2, len(a.Chunks)) + // Chunks should look like: + // 2 | 3 + // ----- + // 0 | 1 + chunkID = a.worldSpaceToChunkIndex(maths.Vector{X: 1, Y: 1}) + assert.Equal(t, 3, chunkID) + chunkID = a.worldSpaceToChunkIndex(maths.Vector{X: 1, Y: -2}) + assert.Equal(t, 1, chunkID) + chunkID = a.worldSpaceToChunkIndex(maths.Vector{X: -2, Y: -2}) + assert.Equal(t, 0, chunkID) + chunkID = a.worldSpaceToChunkIndex(maths.Vector{X: -2, Y: 1}) + assert.Equal(t, 2, chunkID) + + a = NewChunkAtlas(2).(*chunkBasedAtlas) + assert.NotNil(t, a) + // Get a tile to spawn a 4x4 grid of chunks + a.QueryPosition(maths.Vector{X: 3, Y: 3}) + assert.Equal(t, 2*2, len(a.Chunks)) + a.QueryPosition(maths.Vector{X: -3, Y: -3}) + assert.Equal(t, 4*4, len(a.Chunks)) + + // Chunks should look like: + // 12| 13|| 14| 15 + // ---------------- + // 8 | 9 || 10| 11 + // ================ + // 4 | 5 || 6 | 7 + // ---------------- + // 0 | 1 || 2 | 3 + chunkID = a.worldSpaceToChunkIndex(maths.Vector{X: 1, Y: 3}) + assert.Equal(t, 14, chunkID) + chunkID = a.worldSpaceToChunkIndex(maths.Vector{X: 1, Y: -3}) + assert.Equal(t, 2, chunkID) + chunkID = a.worldSpaceToChunkIndex(maths.Vector{X: -1, Y: -1}) + assert.Equal(t, 5, chunkID) + chunkID = a.worldSpaceToChunkIndex(maths.Vector{X: -2, Y: 2}) + assert.Equal(t, 13, chunkID) + + a = NewChunkAtlas(3).(*chunkBasedAtlas) + assert.NotNil(t, a) + // Get a tile to spawn a 4x4 grid of chunks + a.QueryPosition(maths.Vector{X: 3, Y: 3}) + assert.Equal(t, 2*2, len(a.Chunks)) + + // Chunks should look like: + // || 2| 3 + // ------- + // || 0| 1 + // ======= + chunkID = a.worldSpaceToChunkIndex(maths.Vector{X: 1, Y: 1}) + assert.Equal(t, 0, chunkID) + chunkID = a.worldSpaceToChunkIndex(maths.Vector{X: 3, Y: 1}) + assert.Equal(t, 1, chunkID) + chunkID = a.worldSpaceToChunkIndex(maths.Vector{X: 1, Y: 4}) + assert.Equal(t, 2, chunkID) + chunkID = a.worldSpaceToChunkIndex(maths.Vector{X: 5, Y: 5}) + assert.Equal(t, 3, chunkID) +} + +func TestAtlas_toWorld(t *testing.T) { + a := NewChunkAtlas(1).(*chunkBasedAtlas) + assert.NotNil(t, a) + + // Get a tile to spawn some chunks + a.QueryPosition(maths.Vector{X: -1, Y: -1}) + assert.Equal(t, 2*2, len(a.Chunks)) + + // Chunks should look like: + // 2 | 3 + // ----- + // 0 | 1 + assert.Equal(t, maths.Vector{X: -1, Y: -1}, a.chunkOriginInWorldSpace(0)) + assert.Equal(t, maths.Vector{X: 0, Y: -1}, a.chunkOriginInWorldSpace(1)) + + a = NewChunkAtlas(2).(*chunkBasedAtlas) + assert.NotNil(t, a) + // Get a tile to spawn the chunks + a.QueryPosition(maths.Vector{X: -2, Y: -2}) + assert.Equal(t, 2*2, len(a.Chunks)) + a.QueryPosition(maths.Vector{X: 1, Y: 1}) + assert.Equal(t, 2*2, len(a.Chunks)) + // Chunks should look like: + // 2 | 3 + // ----- + // 0 | 1 + assert.Equal(t, maths.Vector{X: -2, Y: -2}, a.chunkOriginInWorldSpace(0)) + assert.Equal(t, maths.Vector{X: -2, Y: 0}, a.chunkOriginInWorldSpace(2)) + + a = NewChunkAtlas(2).(*chunkBasedAtlas) + assert.NotNil(t, a) + // Get a tile to spawn a 4x4 grid of chunks + a.QueryPosition(maths.Vector{X: 3, Y: 3}) + assert.Equal(t, 2*2, len(a.Chunks)) + a.QueryPosition(maths.Vector{X: -3, Y: -3}) + assert.Equal(t, 4*4, len(a.Chunks)) + + // Chunks should look like: + // 12| 13|| 14| 15 + // ---------------- + // 8 | 9 || 10| 11 + // ================ + // 4 | 5 || 6 | 7 + // ---------------- + // 0 | 1 || 2 | 3 + assert.Equal(t, maths.Vector{X: -4, Y: -4}, a.chunkOriginInWorldSpace(0)) + assert.Equal(t, maths.Vector{X: 2, Y: -2}, a.chunkOriginInWorldSpace(7)) + + a = NewChunkAtlas(3).(*chunkBasedAtlas) + assert.NotNil(t, a) + // Get a tile to spawn a 4x4 grid of chunks + a.QueryPosition(maths.Vector{X: 3, Y: 3}) + assert.Equal(t, 2*2, len(a.Chunks)) + + // Chunks should look like: + // || 2| 3 + // ------- + // || 0| 1 + // ======= + assert.Equal(t, maths.Vector{X: 0, Y: 0}, a.chunkOriginInWorldSpace(0)) +} + +func TestAtlas_GetSetTile(t *testing.T) { + a := NewChunkAtlas(10) + assert.NotNil(t, a) + + // Set the origin tile and test it + a.SetTile(maths.Vector{X: 0, Y: 0}, roveapi.Tile_Gravel) + tile, _ := a.QueryPosition(maths.Vector{X: 0, Y: 0}) + assert.Equal(t, roveapi.Tile_Gravel, tile) + + // Set another tile and test it + a.SetTile(maths.Vector{X: 5, Y: -2}, roveapi.Tile_Rock) + tile, _ = a.QueryPosition(maths.Vector{X: 5, Y: -2}) + assert.Equal(t, roveapi.Tile_Rock, tile) +} + +func TestAtlas_GetSetObject(t *testing.T) { + a := NewChunkAtlas(10) + assert.NotNil(t, a) + + // Set the origin tile to 1 and test it + a.SetObject(maths.Vector{X: 0, Y: 0}, Object{Type: roveapi.Object_RockLarge}) + _, obj := a.QueryPosition(maths.Vector{X: 0, Y: 0}) + assert.Equal(t, Object{Type: roveapi.Object_RockLarge}, obj) + + // Set another tile to 1 and test it + a.SetObject(maths.Vector{X: 5, Y: -2}, Object{Type: roveapi.Object_RockSmall}) + _, obj = a.QueryPosition(maths.Vector{X: 5, Y: -2}) + assert.Equal(t, Object{Type: roveapi.Object_RockSmall}, obj) +} + +func TestAtlas_Grown(t *testing.T) { + // Start with a small example + a := NewChunkAtlas(2).(*chunkBasedAtlas) + assert.NotNil(t, a) + assert.Equal(t, 1, len(a.Chunks)) + + // Set a few tiles to values + a.SetTile(maths.Vector{X: 0, Y: 0}, roveapi.Tile_Gravel) + a.SetTile(maths.Vector{X: -1, Y: -1}, roveapi.Tile_Rock) + a.SetTile(maths.Vector{X: 1, Y: -2}, roveapi.Tile_Sand) + + // Check tile values + tile, _ := a.QueryPosition(maths.Vector{X: 0, Y: 0}) + assert.Equal(t, roveapi.Tile_Gravel, tile) + + tile, _ = a.QueryPosition(maths.Vector{X: -1, Y: -1}) + assert.Equal(t, roveapi.Tile_Rock, tile) + + tile, _ = a.QueryPosition(maths.Vector{X: 1, Y: -2}) + assert.Equal(t, roveapi.Tile_Sand, tile) + + tile, _ = a.QueryPosition(maths.Vector{X: 0, Y: 0}) + assert.Equal(t, roveapi.Tile_Gravel, tile) + + tile, _ = a.QueryPosition(maths.Vector{X: -1, Y: -1}) + assert.Equal(t, roveapi.Tile_Rock, tile) + + tile, _ = a.QueryPosition(maths.Vector{X: 1, Y: -2}) + assert.Equal(t, roveapi.Tile_Sand, tile) +} + +func TestAtlas_GetSetCorrect(t *testing.T) { + // Big stress test to ensure we do actually properly expand for all reasonable values + for i := 1; i <= 4; i++ { + + for x := -i * 2; x < i*2; x++ { + for y := -i * 2; y < i*2; y++ { + a := NewChunkAtlas(i).(*chunkBasedAtlas) + assert.NotNil(t, a) + assert.Equal(t, 1, len(a.Chunks)) + + pos := maths.Vector{X: x, Y: y} + a.SetTile(pos, roveapi.Tile_Rock) + a.SetObject(pos, Object{Type: roveapi.Object_RockLarge}) + tile, obj := a.QueryPosition(pos) + + assert.Equal(t, roveapi.Tile_Rock, roveapi.Tile(tile)) + assert.Equal(t, Object{Type: roveapi.Object_RockLarge}, obj) + + } + } + } +} + +func TestAtlas_WorldGen(t *testing.T) { + a := NewChunkAtlas(8) + + // Spawn a large world + _, _ = a.QueryPosition(maths.Vector{X: 20, Y: 20}) +} diff --git a/pkg/rove/chunkAtlas.go b/pkg/rove/chunkAtlas.go new file mode 100644 index 0000000..1903c7b --- /dev/null +++ b/pkg/rove/chunkAtlas.go @@ -0,0 +1,231 @@ +package rove + +import ( + "log" + + "github.com/mdiluz/rove/pkg/maths" + "github.com/mdiluz/rove/proto/roveapi" +) + +// chunk represents a fixed square grid of tiles +type chunk struct { + // Tiles represents the tiles within the chunk + Tiles []byte + + // Objects represents the objects within the chunk + // only one possible object per tile for now + Objects map[int]Object +} + +// chunkBasedAtlas represents a grid of Chunks +type chunkBasedAtlas struct { + // Chunks represents all chunks in the world + // This is intentionally not a 2D array so it can be expanded in all directions + Chunks []chunk + + // LowerBound is the origin of the bottom left corner of the current chunks in world space (current chunks cover >= this value) + LowerBound maths.Vector + + // UpperBound is the top left corner of the current chunks (curent chunks cover < this value) + UpperBound maths.Vector + + // ChunkSize is the x/y dimensions of each square chunk + ChunkSize int + + // worldGen is the internal world generator + worldGen WorldGen +} + +const ( + noiseSeed = 1024 +) + +// NewChunkAtlas creates a new empty atlas +func NewChunkAtlas(chunkSize int) Atlas { + // Start up with one chunk + a := chunkBasedAtlas{ + ChunkSize: chunkSize, + Chunks: make([]chunk, 1), + LowerBound: maths.Vector{X: 0, Y: 0}, + UpperBound: maths.Vector{X: chunkSize, Y: chunkSize}, + worldGen: NewNoiseWorldGen(noiseSeed), + } + // Initialise the first chunk + a.populate(0) + return &a +} + +// SetTile sets an individual tile's kind +func (a *chunkBasedAtlas) SetTile(v maths.Vector, tile roveapi.Tile) { + c := a.worldSpaceToChunkWithGrow(v) + local := a.worldSpaceToChunkLocal(v) + a.setTile(c, local, byte(tile)) +} + +// SetObject sets the object on a tile +func (a *chunkBasedAtlas) SetObject(v maths.Vector, obj Object) { + c := a.worldSpaceToChunkWithGrow(v) + local := a.worldSpaceToChunkLocal(v) + a.setObject(c, local, obj) +} + +// QueryPosition will return information for a specific position +func (a *chunkBasedAtlas) QueryPosition(v maths.Vector) (roveapi.Tile, Object) { + c := a.worldSpaceToChunkWithGrow(v) + local := a.worldSpaceToChunkLocal(v) + a.populate(c) + chunk := a.Chunks[c] + i := a.chunkTileIndex(local) + return roveapi.Tile(chunk.Tiles[i]), chunk.Objects[i] +} + +// chunkTileID returns the tile index within a chunk +func (a *chunkBasedAtlas) chunkTileIndex(local maths.Vector) int { + return local.X + local.Y*a.ChunkSize +} + +// populate will fill a chunk with data +func (a *chunkBasedAtlas) populate(chunk int) { + c := a.Chunks[chunk] + if c.Tiles != nil { + return + } + + c.Tiles = make([]byte, a.ChunkSize*a.ChunkSize) + c.Objects = make(map[int]Object) + + origin := a.chunkOriginInWorldSpace(chunk) + for i := 0; i < a.ChunkSize; i++ { + for j := 0; j < a.ChunkSize; j++ { + loc := maths.Vector{X: origin.X + i, Y: origin.Y + j} + + // Set the tile + c.Tiles[j*a.ChunkSize+i] = byte(a.worldGen.GetTile(loc)) + + // Set the object + obj := a.worldGen.GetObject(loc) + if obj.Type != roveapi.Object_ObjectUnknown { + c.Objects[j*a.ChunkSize+i] = obj + } + } + } + + a.Chunks[chunk] = c +} + +// setTile sets a tile in a specific chunk +func (a *chunkBasedAtlas) setTile(chunk int, local maths.Vector, tile byte) { + a.populate(chunk) + c := a.Chunks[chunk] + c.Tiles[a.chunkTileIndex(local)] = tile + a.Chunks[chunk] = c +} + +// setObject sets an object in a specific chunk +func (a *chunkBasedAtlas) setObject(chunk int, local maths.Vector, object Object) { + a.populate(chunk) + + c := a.Chunks[chunk] + i := a.chunkTileIndex(local) + if object.Type != roveapi.Object_ObjectUnknown { + c.Objects[i] = object + } else { + delete(c.Objects, i) + } + a.Chunks[chunk] = c +} + +// worldSpaceToChunkLocal gets a chunk local coordinate for a tile +func (a *chunkBasedAtlas) worldSpaceToChunkLocal(v maths.Vector) maths.Vector { + return maths.Vector{X: maths.Pmod(v.X, a.ChunkSize), Y: maths.Pmod(v.Y, a.ChunkSize)} +} + +// worldSpaceToChunkID gets the current chunk ID for a position in the world +func (a *chunkBasedAtlas) worldSpaceToChunkIndex(v maths.Vector) int { + // Shift the vector by our current min + v = v.Added(a.LowerBound.Negated()) + + // Divide by the current size and floor, to get chunk-scaled vector from the lower bound + v = v.DividedFloor(a.ChunkSize) + + // Calculate the width + width := a.UpperBound.X - a.LowerBound.X + widthInChunks := width / a.ChunkSize + + // Along the corridor and up the stairs + return (v.Y * widthInChunks) + v.X +} + +// chunkOriginInWorldSpace returns the origin of the chunk in world space +func (a *chunkBasedAtlas) chunkOriginInWorldSpace(chunk int) maths.Vector { + // Calculate the width + width := a.UpperBound.X - a.LowerBound.X + widthInChunks := width / a.ChunkSize + + // Reverse the along the corridor and up the stairs + v := maths.Vector{ + X: chunk % widthInChunks, + Y: chunk / widthInChunks, + } + // Multiply up to world scale + v = v.Multiplied(a.ChunkSize) + // Shift by the lower bound + return v.Added(a.LowerBound) +} + +// getNewBounds gets new lower and upper bounds for the world space given a vector +func (a *chunkBasedAtlas) getNewBounds(v maths.Vector) (lower maths.Vector, upper maths.Vector) { + lower = maths.Min2(v, a.LowerBound) + upper = maths.Max2(v.Added(maths.Vector{X: 1, Y: 1}), a.UpperBound) + + lower = maths.Vector{ + X: maths.RoundDown(lower.X, a.ChunkSize), + Y: maths.RoundDown(lower.Y, a.ChunkSize), + } + upper = maths.Vector{ + X: maths.RoundUp(upper.X, a.ChunkSize), + Y: maths.RoundUp(upper.Y, a.ChunkSize), + } + return +} + +// worldSpaceToTrunkWithGrow will expand the current atlas for a given world space position if needed +func (a *chunkBasedAtlas) worldSpaceToChunkWithGrow(v maths.Vector) int { + // If we're within bounds, just return the current chunk + if v.X >= a.LowerBound.X && v.Y >= a.LowerBound.Y && v.X < a.UpperBound.X && v.Y < a.UpperBound.Y { + return a.worldSpaceToChunkIndex(v) + } + + // Calculate the new bounds + lower, upper := a.getNewBounds(v) + size := upper.Added(lower.Negated()) + size = size.Divided(a.ChunkSize) + + // Create the new empty atlas + newAtlas := chunkBasedAtlas{ + ChunkSize: a.ChunkSize, + LowerBound: lower, + UpperBound: upper, + Chunks: make([]chunk, size.X*size.Y), + worldGen: a.worldGen, + } + + // Log that we're resizing + log.Printf("Re-allocating world, old: %+v,%+v new: %+v,%+v\n", a.LowerBound, a.UpperBound, newAtlas.LowerBound, newAtlas.UpperBound) + + // Copy all old chunks into the new atlas + for chunk, chunkData := range a.Chunks { + + // Calculate the chunk ID in the new atlas + origin := a.chunkOriginInWorldSpace(chunk) + newChunk := newAtlas.worldSpaceToChunkIndex(origin) + + // Copy over the old chunk to the new atlas + newAtlas.Chunks[newChunk] = chunkData + } + + // Overwrite the old atlas with this one + *a = newAtlas + + return a.worldSpaceToChunkIndex(v) +} diff --git a/pkg/rove/command_test.go b/pkg/rove/command_test.go new file mode 100644 index 0000000..bf1eed6 --- /dev/null +++ b/pkg/rove/command_test.go @@ -0,0 +1,305 @@ +package rove + +import ( + "encoding/json" + "testing" + + "github.com/mdiluz/rove/pkg/maths" + "github.com/mdiluz/rove/proto/roveapi" + "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) { + w := NewWorld(8) + a, err := w.SpawnRover("") + assert.NoError(t, err) + + r, err := w.GetRover(a) + assert.NoError(t, err) + assert.Equal(t, roveapi.SailPosition_SolarCharging, r.SailPosition) + + err = w.Enqueue(a, &roveapi.Command{Command: roveapi.CommandType_toggle}) + assert.NoError(t, err) + w.Tick() + + r, err = w.GetRover(a) + assert.NoError(t, err) + assert.Equal(t, roveapi.SailPosition_CatchingWind, r.SailPosition) + + err = w.Enqueue(a, &roveapi.Command{Command: roveapi.CommandType_toggle}) + assert.NoError(t, err) + w.Tick() + + r, err = w.GetRover(a) + assert.NoError(t, err) + assert.Equal(t, roveapi.SailPosition_SolarCharging, r.SailPosition) +} + +func TestCommand_Turn(t *testing.T) { + w := NewWorld(8) + a, err := w.SpawnRover("") + assert.NoError(t, err) + + err = w.Enqueue(a, &roveapi.Command{Command: roveapi.CommandType_turn, Bearing: roveapi.Bearing_NorthWest}) + assert.NoError(t, err) + w.Tick() + + r, err := w.GetRover(a) + assert.NoError(t, err) + assert.Equal(t, roveapi.Bearing_NorthWest, r.Bearing) +} + +func TestCommand_Stash(t *testing.T) { + w := NewWorld(8) + name, err := w.SpawnRover("") + assert.NoError(t, err) + + info, err := w.GetRover(name) + assert.NoError(t, err) + assert.Empty(t, info.Inventory) + + // Drop a pickup below us + w.Atlas.SetObject(info.Pos, Object{Type: roveapi.Object_RockSmall}) + + // Try and stash it + err = w.Enqueue(name, &roveapi.Command{Command: roveapi.CommandType_stash}) + assert.NoError(t, err) + w.Tick() + + // Check we now have it in the inventory + info, err = w.GetRover(name) + assert.NoError(t, err) + assert.Equal(t, 1, len(info.Inventory)) + assert.Equal(t, Object{Type: roveapi.Object_RockSmall}, info.Inventory[0]) + + // Check it's no longer on the atlas + _, obj := w.Atlas.QueryPosition(info.Pos) + assert.Equal(t, Object{Type: roveapi.Object_ObjectUnknown}, obj) +} + +func TestCommand_Repair(t *testing.T) { + w := NewWorld(8) + name, err := w.SpawnRover("") + assert.NoError(t, err) + + info, err := w.GetRover(name) + assert.NoError(t, err) + assert.Equal(t, info.MaximumIntegrity, info.Integrity) + + // Put a blocking rock to the north + w.Atlas.SetObject(info.Pos.Added(maths.Vector{X: 0, Y: 1}), Object{Type: roveapi.Object_RockLarge}) + + // Try and move and make sure we're blocked + newpos, err := w.TryMoveRover(name, roveapi.Bearing_North) + assert.NoError(t, err) + assert.Equal(t, info.Pos, newpos) + + // Check we're damaged + info, err = w.GetRover(name) + assert.NoError(t, err) + assert.Equal(t, info.MaximumIntegrity-1, info.Integrity) + + // Stash a repair object + w.Atlas.SetObject(info.Pos, Object{Type: roveapi.Object_RoverParts}) + obj, err := w.RoverStash(name) + assert.NoError(t, err) + assert.Equal(t, roveapi.Object_RoverParts, obj) + + // Enqueue the repair and tick + err = w.Enqueue(name, &roveapi.Command{Command: roveapi.CommandType_repair}) + assert.NoError(t, err) + w.Tick() + + // Check we're repaired + info, err = w.GetRover(name) + assert.NoError(t, err) + assert.Equal(t, info.MaximumIntegrity, info.Integrity) + assert.Equal(t, 0, len(info.Inventory)) +} + +func TestCommand_Broadcast(t *testing.T) { + w := NewWorld(8) + name, err := w.SpawnRover("") + assert.NoError(t, err) + + // Enqueue the broadcast and tick + err = w.Enqueue(name, &roveapi.Command{Command: roveapi.CommandType_broadcast, Data: []byte("ABC")}) + assert.NoError(t, err) + w.Tick() + + info, err := w.GetRover(name) + assert.NoError(t, err) + assert.Contains(t, info.Logs[len(info.Logs)-1].Text, "ABC") +} + +func TestCommand_Salvage(t *testing.T) { + w := NewWorld(8) + name, err := w.SpawnRover("") + assert.NoError(t, err) + + info, err := w.GetRover(name) + assert.NoError(t, err) + + w.Atlas.SetObject(info.Pos, Object{Type: roveapi.Object_RoverDormant}) + + // Enqueue the broadcast and tick + err = w.Enqueue(name, &roveapi.Command{Command: roveapi.CommandType_salvage}) + assert.NoError(t, err) + w.Tick() + + // Check we now have some rover parts + info, err = w.GetRover(name) + assert.NoError(t, err) + assert.NotEmpty(t, info.Inventory) + for _, i := range info.Inventory { + assert.Equal(t, roveapi.Object_RoverParts, i.Type) + } + + // 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) +} diff --git a/pkg/rove/objects.go b/pkg/rove/objects.go new file mode 100644 index 0000000..df10453 --- /dev/null +++ b/pkg/rove/objects.go @@ -0,0 +1,44 @@ +package rove + +import ( + "github.com/mdiluz/rove/proto/roveapi" +) + +// Object represents an object in the world +type Object struct { + // The type of the object + Type roveapi.Object + + // Data is an internal type used for certain types of object + Data []byte +} + +// IsBlocking checks if an object is a blocking object +func (o *Object) IsBlocking() bool { + var blocking = [...]roveapi.Object{ + roveapi.Object_RoverLive, + roveapi.Object_RockLarge, + } + + for _, t := range blocking { + if o.Type == t { + return true + } + } + return false +} + +// IsStashable checks if an object is stashable +func (o *Object) IsStashable() bool { + var stashable = [...]roveapi.Object{ + roveapi.Object_RockSmall, + roveapi.Object_RoverParts, + } + + for _, t := range stashable { + if o.Type == t { + return true + } + } + return false +} diff --git a/pkg/rove/rove.pb.go b/pkg/rove/rove.pb.go deleted file mode 100644 index faf1b9f..0000000 --- a/pkg/rove/rove.pb.go +++ /dev/null @@ -1,1297 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.23.0 -// protoc v3.6.1 -// source: rove/rove.proto - -// Rove -// -// Rove is an asychronous nomadic game about exploring a planet as part of a loose community - -package rove - -import ( - context "context" - proto "github.com/golang/protobuf/proto" - _ "google.golang.org/genproto/googleapis/api/annotations" - grpc "google.golang.org/grpc" - codes "google.golang.org/grpc/codes" - status "google.golang.org/grpc/status" - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -// This is a compile-time assertion that a sufficiently up-to-date version -// of the legacy proto package is being used. -const _ = proto.ProtoPackageIsVersion4 - -type Command struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // The command to execute - // "move" - Move the rover in a direction, requires bearing - // "stash" - Stashes item at current location in rover inventory - Command string `protobuf:"bytes,1,opt,name=command,proto3" json:"command,omitempty"` - // The bearing, example: NE - Bearing string `protobuf:"bytes,2,opt,name=bearing,proto3" json:"bearing,omitempty"` -} - -func (x *Command) Reset() { - *x = Command{} - if protoimpl.UnsafeEnabled { - mi := &file_rove_rove_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Command) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Command) ProtoMessage() {} - -func (x *Command) ProtoReflect() protoreflect.Message { - mi := &file_rove_rove_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Command.ProtoReflect.Descriptor instead. -func (*Command) Descriptor() ([]byte, []int) { - return file_rove_rove_proto_rawDescGZIP(), []int{0} -} - -func (x *Command) GetCommand() string { - if x != nil { - return x.Command - } - return "" -} - -func (x *Command) GetBearing() string { - if x != nil { - return x.Bearing - } - return "" -} - -type CommandsRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // The account to execute these commands - Account string `protobuf:"bytes,1,opt,name=account,proto3" json:"account,omitempty"` - // The set of desired commands - Commands []*Command `protobuf:"bytes,2,rep,name=commands,proto3" json:"commands,omitempty"` -} - -func (x *CommandsRequest) Reset() { - *x = CommandsRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_rove_rove_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *CommandsRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CommandsRequest) ProtoMessage() {} - -func (x *CommandsRequest) ProtoReflect() protoreflect.Message { - mi := &file_rove_rove_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use CommandsRequest.ProtoReflect.Descriptor instead. -func (*CommandsRequest) Descriptor() ([]byte, []int) { - return file_rove_rove_proto_rawDescGZIP(), []int{1} -} - -func (x *CommandsRequest) GetAccount() string { - if x != nil { - return x.Account - } - return "" -} - -func (x *CommandsRequest) GetCommands() []*Command { - if x != nil { - return x.Commands - } - return nil -} - -// Empty placeholder -type CommandsResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields -} - -func (x *CommandsResponse) Reset() { - *x = CommandsResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_rove_rove_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *CommandsResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CommandsResponse) ProtoMessage() {} - -func (x *CommandsResponse) ProtoReflect() protoreflect.Message { - mi := &file_rove_rove_proto_msgTypes[2] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use CommandsResponse.ProtoReflect.Descriptor instead. -func (*CommandsResponse) Descriptor() ([]byte, []int) { - return file_rove_rove_proto_rawDescGZIP(), []int{2} -} - -type Error struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // An explanation for the HTTP error returned - Error string `protobuf:"bytes,1,opt,name=error,proto3" json:"error,omitempty"` -} - -func (x *Error) Reset() { - *x = Error{} - if protoimpl.UnsafeEnabled { - mi := &file_rove_rove_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Error) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Error) ProtoMessage() {} - -func (x *Error) ProtoReflect() protoreflect.Message { - mi := &file_rove_rove_proto_msgTypes[3] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Error.ProtoReflect.Descriptor instead. -func (*Error) Descriptor() ([]byte, []int) { - return file_rove_rove_proto_rawDescGZIP(), []int{3} -} - -func (x *Error) GetError() string { - if x != nil { - return x.Error - } - return "" -} - -type RadarRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // The account for this request - Account string `protobuf:"bytes,1,opt,name=account,proto3" json:"account,omitempty"` -} - -func (x *RadarRequest) Reset() { - *x = RadarRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_rove_rove_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *RadarRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*RadarRequest) ProtoMessage() {} - -func (x *RadarRequest) ProtoReflect() protoreflect.Message { - mi := &file_rove_rove_proto_msgTypes[4] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use RadarRequest.ProtoReflect.Descriptor instead. -func (*RadarRequest) Descriptor() ([]byte, []int) { - return file_rove_rove_proto_rawDescGZIP(), []int{4} -} - -func (x *RadarRequest) GetAccount() string { - if x != nil { - return x.Account - } - return "" -} - -type RadarResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // The range in tiles from the rover of the radar data - Range int32 `protobuf:"varint,1,opt,name=range,proto3" json:"range,omitempty"` - // A 1D array representing range*2 + 1 squared set of tiles, origin bottom left and in row->column order - Tiles []byte `protobuf:"bytes,2,opt,name=tiles,proto3" json:"tiles,omitempty"` -} - -func (x *RadarResponse) Reset() { - *x = RadarResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_rove_rove_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *RadarResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*RadarResponse) ProtoMessage() {} - -func (x *RadarResponse) ProtoReflect() protoreflect.Message { - mi := &file_rove_rove_proto_msgTypes[5] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use RadarResponse.ProtoReflect.Descriptor instead. -func (*RadarResponse) Descriptor() ([]byte, []int) { - return file_rove_rove_proto_rawDescGZIP(), []int{5} -} - -func (x *RadarResponse) GetRange() int32 { - if x != nil { - return x.Range - } - return 0 -} - -func (x *RadarResponse) GetTiles() []byte { - if x != nil { - return x.Tiles - } - return nil -} - -// Empty placeholder -type RegisterResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields -} - -func (x *RegisterResponse) Reset() { - *x = RegisterResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_rove_rove_proto_msgTypes[6] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *RegisterResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*RegisterResponse) ProtoMessage() {} - -func (x *RegisterResponse) ProtoReflect() protoreflect.Message { - mi := &file_rove_rove_proto_msgTypes[6] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use RegisterResponse.ProtoReflect.Descriptor instead. -func (*RegisterResponse) Descriptor() ([]byte, []int) { - return file_rove_rove_proto_rawDescGZIP(), []int{6} -} - -type RegisterRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // The desired account name - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` -} - -func (x *RegisterRequest) Reset() { - *x = RegisterRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_rove_rove_proto_msgTypes[7] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *RegisterRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*RegisterRequest) ProtoMessage() {} - -func (x *RegisterRequest) ProtoReflect() protoreflect.Message { - mi := &file_rove_rove_proto_msgTypes[7] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use RegisterRequest.ProtoReflect.Descriptor instead. -func (*RegisterRequest) Descriptor() ([]byte, []int) { - return file_rove_rove_proto_rawDescGZIP(), []int{7} -} - -func (x *RegisterRequest) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -type RoverRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // The account for this request - Account string `protobuf:"bytes,1,opt,name=account,proto3" json:"account,omitempty"` -} - -func (x *RoverRequest) Reset() { - *x = RoverRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_rove_rove_proto_msgTypes[8] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *RoverRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*RoverRequest) ProtoMessage() {} - -func (x *RoverRequest) ProtoReflect() protoreflect.Message { - mi := &file_rove_rove_proto_msgTypes[8] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use RoverRequest.ProtoReflect.Descriptor instead. -func (*RoverRequest) Descriptor() ([]byte, []int) { - return file_rove_rove_proto_rawDescGZIP(), []int{8} -} - -func (x *RoverRequest) GetAccount() string { - if x != nil { - return x.Account - } - return "" -} - -type RoverResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // The name of the rover - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - // Position of the rover in world coordinates - Position *Vector `protobuf:"bytes,2,opt,name=position,proto3" json:"position,omitempty"` - // The range of this rover's radar - Range int32 `protobuf:"varint,3,opt,name=range,proto3" json:"range,omitempty"` - // The items in the rover inventory - Inventory []byte `protobuf:"bytes,4,opt,name=inventory,proto3" json:"inventory,omitempty"` - // The current health of the rover - Integrity int32 `protobuf:"varint,5,opt,name=Integrity,proto3" json:"Integrity,omitempty"` -} - -func (x *RoverResponse) Reset() { - *x = RoverResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_rove_rove_proto_msgTypes[9] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *RoverResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*RoverResponse) ProtoMessage() {} - -func (x *RoverResponse) ProtoReflect() protoreflect.Message { - mi := &file_rove_rove_proto_msgTypes[9] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use RoverResponse.ProtoReflect.Descriptor instead. -func (*RoverResponse) Descriptor() ([]byte, []int) { - return file_rove_rove_proto_rawDescGZIP(), []int{9} -} - -func (x *RoverResponse) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *RoverResponse) GetPosition() *Vector { - if x != nil { - return x.Position - } - return nil -} - -func (x *RoverResponse) GetRange() int32 { - if x != nil { - return x.Range - } - return 0 -} - -func (x *RoverResponse) GetInventory() []byte { - if x != nil { - return x.Inventory - } - return nil -} - -func (x *RoverResponse) GetIntegrity() int32 { - if x != nil { - return x.Integrity - } - return 0 -} - -// Empty placeholder -type StatusRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields -} - -func (x *StatusRequest) Reset() { - *x = StatusRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_rove_rove_proto_msgTypes[10] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *StatusRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*StatusRequest) ProtoMessage() {} - -func (x *StatusRequest) ProtoReflect() protoreflect.Message { - mi := &file_rove_rove_proto_msgTypes[10] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use StatusRequest.ProtoReflect.Descriptor instead. -func (*StatusRequest) Descriptor() ([]byte, []int) { - return file_rove_rove_proto_rawDescGZIP(), []int{10} -} - -type StatusResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // The time the next tick will occur - NextTick string `protobuf:"bytes,1,opt,name=next_tick,json=nextTick,proto3" json:"next_tick,omitempty"` - // Whether the server is ready to accept requests - Ready bool `protobuf:"varint,2,opt,name=ready,proto3" json:"ready,omitempty"` - // The tick rate of the server in minutes (how many minutes per tick) - Tick int32 `protobuf:"varint,3,opt,name=tick,proto3" json:"tick,omitempty"` - // The version of the server in v{major}.{minor}-{delta}-{sha} form - Version string `protobuf:"bytes,4,opt,name=version,proto3" json:"version,omitempty"` -} - -func (x *StatusResponse) Reset() { - *x = StatusResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_rove_rove_proto_msgTypes[11] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *StatusResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*StatusResponse) ProtoMessage() {} - -func (x *StatusResponse) ProtoReflect() protoreflect.Message { - mi := &file_rove_rove_proto_msgTypes[11] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use StatusResponse.ProtoReflect.Descriptor instead. -func (*StatusResponse) Descriptor() ([]byte, []int) { - return file_rove_rove_proto_rawDescGZIP(), []int{11} -} - -func (x *StatusResponse) GetNextTick() string { - if x != nil { - return x.NextTick - } - return "" -} - -func (x *StatusResponse) GetReady() bool { - if x != nil { - return x.Ready - } - return false -} - -func (x *StatusResponse) GetTick() int32 { - if x != nil { - return x.Tick - } - return 0 -} - -func (x *StatusResponse) GetVersion() string { - if x != nil { - return x.Version - } - return "" -} - -type Vector struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - X int32 `protobuf:"varint,1,opt,name=x,proto3" json:"x,omitempty"` - Y int32 `protobuf:"varint,2,opt,name=y,proto3" json:"y,omitempty"` -} - -func (x *Vector) Reset() { - *x = Vector{} - if protoimpl.UnsafeEnabled { - mi := &file_rove_rove_proto_msgTypes[12] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Vector) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Vector) ProtoMessage() {} - -func (x *Vector) ProtoReflect() protoreflect.Message { - mi := &file_rove_rove_proto_msgTypes[12] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Vector.ProtoReflect.Descriptor instead. -func (*Vector) Descriptor() ([]byte, []int) { - return file_rove_rove_proto_rawDescGZIP(), []int{12} -} - -func (x *Vector) GetX() int32 { - if x != nil { - return x.X - } - return 0 -} - -func (x *Vector) GetY() int32 { - if x != nil { - return x.Y - } - return 0 -} - -var File_rove_rove_proto protoreflect.FileDescriptor - -var file_rove_rove_proto_rawDesc = []byte{ - 0x0a, 0x0f, 0x72, 0x6f, 0x76, 0x65, 0x2f, 0x72, 0x6f, 0x76, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x12, 0x04, 0x72, 0x6f, 0x76, 0x65, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, - 0x61, 0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x3d, 0x0a, 0x07, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, - 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x62, 0x65, - 0x61, 0x72, 0x69, 0x6e, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x62, 0x65, 0x61, - 0x72, 0x69, 0x6e, 0x67, 0x22, 0x56, 0x0a, 0x0f, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x63, 0x63, 0x6f, 0x75, - 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, - 0x74, 0x12, 0x29, 0x0a, 0x08, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x18, 0x02, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, - 0x6e, 0x64, 0x52, 0x08, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x22, 0x12, 0x0a, 0x10, - 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x1d, 0x0a, 0x05, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, - 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, - 0x28, 0x0a, 0x0c, 0x52, 0x61, 0x64, 0x61, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x18, 0x0a, 0x07, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x07, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x3b, 0x0a, 0x0d, 0x52, 0x61, 0x64, - 0x61, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x61, - 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x72, 0x61, 0x6e, 0x67, 0x65, - 0x12, 0x14, 0x0a, 0x05, 0x74, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x05, 0x74, 0x69, 0x6c, 0x65, 0x73, 0x22, 0x12, 0x0a, 0x10, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, - 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x25, 0x0a, 0x0f, 0x52, 0x65, - 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, - 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, - 0x65, 0x22, 0x28, 0x0a, 0x0c, 0x52, 0x6f, 0x76, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x07, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x9f, 0x01, 0x0a, 0x0d, - 0x52, 0x6f, 0x76, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, - 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, - 0x65, 0x12, 0x28, 0x0a, 0x08, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x2e, 0x56, 0x65, 0x63, 0x74, 0x6f, - 0x72, 0x52, 0x08, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x72, - 0x61, 0x6e, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x72, 0x61, 0x6e, 0x67, - 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x69, 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x69, 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x6f, 0x72, 0x79, 0x12, - 0x1c, 0x0a, 0x09, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x69, 0x74, 0x79, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x05, 0x52, 0x09, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x69, 0x74, 0x79, 0x22, 0x0f, 0x0a, - 0x0d, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x71, - 0x0a, 0x0e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x74, 0x69, 0x63, 0x6b, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x08, 0x6e, 0x65, 0x78, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x12, 0x14, 0x0a, - 0x05, 0x72, 0x65, 0x61, 0x64, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x72, 0x65, - 0x61, 0x64, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x69, 0x63, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x05, 0x52, 0x04, 0x74, 0x69, 0x63, 0x6b, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, - 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, - 0x6e, 0x22, 0x24, 0x0a, 0x06, 0x56, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x0c, 0x0a, 0x01, 0x78, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x01, 0x78, 0x12, 0x0c, 0x0a, 0x01, 0x79, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x05, 0x52, 0x01, 0x79, 0x32, 0xf8, 0x02, 0x0a, 0x04, 0x52, 0x6f, 0x76, 0x65, - 0x12, 0x44, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x13, 0x2e, 0x72, 0x6f, 0x76, - 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x14, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x0f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x09, 0x12, 0x07, 0x2f, - 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x4f, 0x0a, 0x08, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, - 0x65, 0x72, 0x12, 0x15, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, - 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x72, 0x6f, 0x76, 0x65, - 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x14, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0e, 0x22, 0x09, 0x2f, 0x72, 0x65, 0x67, 0x69, - 0x73, 0x74, 0x65, 0x72, 0x3a, 0x01, 0x2a, 0x12, 0x4f, 0x0a, 0x08, 0x43, 0x6f, 0x6d, 0x6d, 0x61, - 0x6e, 0x64, 0x73, 0x12, 0x15, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, - 0x6e, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x72, 0x6f, 0x76, - 0x65, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x14, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0e, 0x22, 0x09, 0x2f, 0x63, 0x6f, 0x6d, - 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x43, 0x0a, 0x05, 0x52, 0x61, 0x64, 0x61, - 0x72, 0x12, 0x12, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x2e, 0x52, 0x61, 0x64, 0x61, 0x72, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x2e, 0x52, 0x61, 0x64, - 0x61, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x11, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x0b, 0x22, 0x06, 0x2f, 0x72, 0x61, 0x64, 0x61, 0x72, 0x3a, 0x01, 0x2a, 0x12, 0x43, 0x0a, - 0x05, 0x52, 0x6f, 0x76, 0x65, 0x72, 0x12, 0x12, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x2e, 0x52, 0x6f, - 0x76, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x72, 0x6f, 0x76, - 0x65, 0x2e, 0x52, 0x6f, 0x76, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x11, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0b, 0x22, 0x06, 0x2f, 0x72, 0x6f, 0x76, 0x65, 0x72, 0x3a, - 0x01, 0x2a, 0x42, 0x21, 0x5a, 0x1f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, - 0x2f, 0x6d, 0x64, 0x69, 0x6c, 0x75, 0x7a, 0x2f, 0x72, 0x6f, 0x76, 0x65, 0x2f, 0x70, 0x6b, 0x67, - 0x2f, 0x72, 0x6f, 0x76, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, -} - -var ( - file_rove_rove_proto_rawDescOnce sync.Once - file_rove_rove_proto_rawDescData = file_rove_rove_proto_rawDesc -) - -func file_rove_rove_proto_rawDescGZIP() []byte { - file_rove_rove_proto_rawDescOnce.Do(func() { - file_rove_rove_proto_rawDescData = protoimpl.X.CompressGZIP(file_rove_rove_proto_rawDescData) - }) - return file_rove_rove_proto_rawDescData -} - -var file_rove_rove_proto_msgTypes = make([]protoimpl.MessageInfo, 13) -var file_rove_rove_proto_goTypes = []interface{}{ - (*Command)(nil), // 0: rove.Command - (*CommandsRequest)(nil), // 1: rove.CommandsRequest - (*CommandsResponse)(nil), // 2: rove.CommandsResponse - (*Error)(nil), // 3: rove.Error - (*RadarRequest)(nil), // 4: rove.RadarRequest - (*RadarResponse)(nil), // 5: rove.RadarResponse - (*RegisterResponse)(nil), // 6: rove.RegisterResponse - (*RegisterRequest)(nil), // 7: rove.RegisterRequest - (*RoverRequest)(nil), // 8: rove.RoverRequest - (*RoverResponse)(nil), // 9: rove.RoverResponse - (*StatusRequest)(nil), // 10: rove.StatusRequest - (*StatusResponse)(nil), // 11: rove.StatusResponse - (*Vector)(nil), // 12: rove.Vector -} -var file_rove_rove_proto_depIdxs = []int32{ - 0, // 0: rove.CommandsRequest.commands:type_name -> rove.Command - 12, // 1: rove.RoverResponse.position:type_name -> rove.Vector - 10, // 2: rove.Rove.Status:input_type -> rove.StatusRequest - 7, // 3: rove.Rove.Register:input_type -> rove.RegisterRequest - 1, // 4: rove.Rove.Commands:input_type -> rove.CommandsRequest - 4, // 5: rove.Rove.Radar:input_type -> rove.RadarRequest - 8, // 6: rove.Rove.Rover:input_type -> rove.RoverRequest - 11, // 7: rove.Rove.Status:output_type -> rove.StatusResponse - 6, // 8: rove.Rove.Register:output_type -> rove.RegisterResponse - 2, // 9: rove.Rove.Commands:output_type -> rove.CommandsResponse - 5, // 10: rove.Rove.Radar:output_type -> rove.RadarResponse - 9, // 11: rove.Rove.Rover:output_type -> rove.RoverResponse - 7, // [7:12] is the sub-list for method output_type - 2, // [2:7] is the sub-list for method input_type - 2, // [2:2] is the sub-list for extension type_name - 2, // [2:2] is the sub-list for extension extendee - 0, // [0:2] is the sub-list for field type_name -} - -func init() { file_rove_rove_proto_init() } -func file_rove_rove_proto_init() { - if File_rove_rove_proto != nil { - return - } - if !protoimpl.UnsafeEnabled { - file_rove_rove_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Command); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rove_rove_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CommandsRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rove_rove_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CommandsResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rove_rove_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Error); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rove_rove_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RadarRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rove_rove_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RadarResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rove_rove_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RegisterResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rove_rove_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RegisterRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rove_rove_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RoverRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rove_rove_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RoverResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rove_rove_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*StatusRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rove_rove_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*StatusResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rove_rove_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Vector); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_rove_rove_proto_rawDesc, - NumEnums: 0, - NumMessages: 13, - NumExtensions: 0, - NumServices: 1, - }, - GoTypes: file_rove_rove_proto_goTypes, - DependencyIndexes: file_rove_rove_proto_depIdxs, - MessageInfos: file_rove_rove_proto_msgTypes, - }.Build() - File_rove_rove_proto = out.File - file_rove_rove_proto_rawDesc = nil - file_rove_rove_proto_goTypes = nil - file_rove_rove_proto_depIdxs = nil -} - -// Reference imports to suppress errors if they are not otherwise used. -var _ context.Context -var _ grpc.ClientConnInterface - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the grpc package it is being compiled against. -const _ = grpc.SupportPackageIsVersion6 - -// RoveClient is the client API for Rove service. -// -// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. -type RoveClient interface { - // Server status - // - // Responds with various details about the current server status - Status(ctx context.Context, in *StatusRequest, opts ...grpc.CallOption) (*StatusResponse, error) - // Register an account - // - // Tries to register an account with the given name - Register(ctx context.Context, in *RegisterRequest, opts ...grpc.CallOption) (*RegisterResponse, error) - // Send commands to rover - // - // Sending commands to this endpoint will queue them to be executed during the following ticks, in the order sent - Commands(ctx context.Context, in *CommandsRequest, opts ...grpc.CallOption) (*CommandsResponse, error) - // Get radar information - // - // Gets the radar output for the given rover - Radar(ctx context.Context, in *RadarRequest, opts ...grpc.CallOption) (*RadarResponse, error) - // Get rover information - // - // Gets information for the account's rover - Rover(ctx context.Context, in *RoverRequest, opts ...grpc.CallOption) (*RoverResponse, error) -} - -type roveClient struct { - cc grpc.ClientConnInterface -} - -func NewRoveClient(cc grpc.ClientConnInterface) RoveClient { - return &roveClient{cc} -} - -func (c *roveClient) Status(ctx context.Context, in *StatusRequest, opts ...grpc.CallOption) (*StatusResponse, error) { - out := new(StatusResponse) - err := c.cc.Invoke(ctx, "/rove.Rove/Status", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *roveClient) Register(ctx context.Context, in *RegisterRequest, opts ...grpc.CallOption) (*RegisterResponse, error) { - out := new(RegisterResponse) - err := c.cc.Invoke(ctx, "/rove.Rove/Register", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *roveClient) Commands(ctx context.Context, in *CommandsRequest, opts ...grpc.CallOption) (*CommandsResponse, error) { - out := new(CommandsResponse) - err := c.cc.Invoke(ctx, "/rove.Rove/Commands", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *roveClient) Radar(ctx context.Context, in *RadarRequest, opts ...grpc.CallOption) (*RadarResponse, error) { - out := new(RadarResponse) - err := c.cc.Invoke(ctx, "/rove.Rove/Radar", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *roveClient) Rover(ctx context.Context, in *RoverRequest, opts ...grpc.CallOption) (*RoverResponse, error) { - out := new(RoverResponse) - err := c.cc.Invoke(ctx, "/rove.Rove/Rover", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -// RoveServer is the server API for Rove service. -type RoveServer interface { - // Server status - // - // Responds with various details about the current server status - Status(context.Context, *StatusRequest) (*StatusResponse, error) - // Register an account - // - // Tries to register an account with the given name - Register(context.Context, *RegisterRequest) (*RegisterResponse, error) - // Send commands to rover - // - // Sending commands to this endpoint will queue them to be executed during the following ticks, in the order sent - Commands(context.Context, *CommandsRequest) (*CommandsResponse, error) - // Get radar information - // - // Gets the radar output for the given rover - Radar(context.Context, *RadarRequest) (*RadarResponse, error) - // Get rover information - // - // Gets information for the account's rover - Rover(context.Context, *RoverRequest) (*RoverResponse, error) -} - -// UnimplementedRoveServer can be embedded to have forward compatible implementations. -type UnimplementedRoveServer struct { -} - -func (*UnimplementedRoveServer) Status(context.Context, *StatusRequest) (*StatusResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method Status not implemented") -} -func (*UnimplementedRoveServer) Register(context.Context, *RegisterRequest) (*RegisterResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method Register not implemented") -} -func (*UnimplementedRoveServer) Commands(context.Context, *CommandsRequest) (*CommandsResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method Commands not implemented") -} -func (*UnimplementedRoveServer) Radar(context.Context, *RadarRequest) (*RadarResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method Radar not implemented") -} -func (*UnimplementedRoveServer) Rover(context.Context, *RoverRequest) (*RoverResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method Rover not implemented") -} - -func RegisterRoveServer(s *grpc.Server, srv RoveServer) { - s.RegisterService(&_Rove_serviceDesc, srv) -} - -func _Rove_Status_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(StatusRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(RoveServer).Status(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/rove.Rove/Status", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(RoveServer).Status(ctx, req.(*StatusRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _Rove_Register_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(RegisterRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(RoveServer).Register(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/rove.Rove/Register", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(RoveServer).Register(ctx, req.(*RegisterRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _Rove_Commands_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(CommandsRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(RoveServer).Commands(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/rove.Rove/Commands", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(RoveServer).Commands(ctx, req.(*CommandsRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _Rove_Radar_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(RadarRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(RoveServer).Radar(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/rove.Rove/Radar", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(RoveServer).Radar(ctx, req.(*RadarRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _Rove_Rover_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(RoverRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(RoveServer).Rover(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/rove.Rove/Rover", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(RoveServer).Rover(ctx, req.(*RoverRequest)) - } - return interceptor(ctx, in, info, handler) -} - -var _Rove_serviceDesc = grpc.ServiceDesc{ - ServiceName: "rove.Rove", - HandlerType: (*RoveServer)(nil), - Methods: []grpc.MethodDesc{ - { - MethodName: "Status", - Handler: _Rove_Status_Handler, - }, - { - MethodName: "Register", - Handler: _Rove_Register_Handler, - }, - { - MethodName: "Commands", - Handler: _Rove_Commands_Handler, - }, - { - MethodName: "Radar", - Handler: _Rove_Radar_Handler, - }, - { - MethodName: "Rover", - Handler: _Rove_Rover_Handler, - }, - }, - Streams: []grpc.StreamDesc{}, - Metadata: "rove/rove.proto", -} diff --git a/pkg/rove/rove.pb.gw.go b/pkg/rove/rove.pb.gw.go deleted file mode 100644 index 0d19891..0000000 --- a/pkg/rove/rove.pb.gw.go +++ /dev/null @@ -1,459 +0,0 @@ -// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. -// source: rove/rove.proto - -/* -Package rove is a reverse proxy. - -It translates gRPC into RESTful JSON APIs. -*/ -package rove - -import ( - "context" - "io" - "net/http" - - "github.com/golang/protobuf/descriptor" - "github.com/golang/protobuf/proto" - "github.com/grpc-ecosystem/grpc-gateway/runtime" - "github.com/grpc-ecosystem/grpc-gateway/utilities" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/grpclog" - "google.golang.org/grpc/status" -) - -// Suppress "imported and not used" errors -var _ codes.Code -var _ io.Reader -var _ status.Status -var _ = runtime.String -var _ = utilities.NewDoubleArray -var _ = descriptor.ForMessage - -func request_Rove_Status_0(ctx context.Context, marshaler runtime.Marshaler, client RoveClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq StatusRequest - var metadata runtime.ServerMetadata - - msg, err := client.Status(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) - return msg, metadata, err - -} - -func local_request_Rove_Status_0(ctx context.Context, marshaler runtime.Marshaler, server RoveServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq StatusRequest - var metadata runtime.ServerMetadata - - msg, err := server.Status(ctx, &protoReq) - return msg, metadata, err - -} - -func request_Rove_Register_0(ctx context.Context, marshaler runtime.Marshaler, client RoveClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq RegisterRequest - var metadata runtime.ServerMetadata - - newReader, berr := utilities.IOReaderFactory(req.Body) - if berr != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) - } - if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - - msg, err := client.Register(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) - return msg, metadata, err - -} - -func local_request_Rove_Register_0(ctx context.Context, marshaler runtime.Marshaler, server RoveServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq RegisterRequest - var metadata runtime.ServerMetadata - - newReader, berr := utilities.IOReaderFactory(req.Body) - if berr != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) - } - if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - - msg, err := server.Register(ctx, &protoReq) - return msg, metadata, err - -} - -func request_Rove_Commands_0(ctx context.Context, marshaler runtime.Marshaler, client RoveClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq CommandsRequest - var metadata runtime.ServerMetadata - - newReader, berr := utilities.IOReaderFactory(req.Body) - if berr != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) - } - if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - - msg, err := client.Commands(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) - return msg, metadata, err - -} - -func local_request_Rove_Commands_0(ctx context.Context, marshaler runtime.Marshaler, server RoveServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq CommandsRequest - var metadata runtime.ServerMetadata - - newReader, berr := utilities.IOReaderFactory(req.Body) - if berr != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) - } - if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - - msg, err := server.Commands(ctx, &protoReq) - return msg, metadata, err - -} - -func request_Rove_Radar_0(ctx context.Context, marshaler runtime.Marshaler, client RoveClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq RadarRequest - var metadata runtime.ServerMetadata - - newReader, berr := utilities.IOReaderFactory(req.Body) - if berr != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) - } - if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - - msg, err := client.Radar(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) - return msg, metadata, err - -} - -func local_request_Rove_Radar_0(ctx context.Context, marshaler runtime.Marshaler, server RoveServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq RadarRequest - var metadata runtime.ServerMetadata - - newReader, berr := utilities.IOReaderFactory(req.Body) - if berr != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) - } - if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - - msg, err := server.Radar(ctx, &protoReq) - return msg, metadata, err - -} - -func request_Rove_Rover_0(ctx context.Context, marshaler runtime.Marshaler, client RoveClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq RoverRequest - var metadata runtime.ServerMetadata - - newReader, berr := utilities.IOReaderFactory(req.Body) - if berr != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) - } - if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - - msg, err := client.Rover(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) - return msg, metadata, err - -} - -func local_request_Rove_Rover_0(ctx context.Context, marshaler runtime.Marshaler, server RoveServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq RoverRequest - var metadata runtime.ServerMetadata - - newReader, berr := utilities.IOReaderFactory(req.Body) - if berr != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) - } - if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - - msg, err := server.Rover(ctx, &protoReq) - return msg, metadata, err - -} - -// RegisterRoveHandlerServer registers the http handlers for service Rove to "mux". -// UnaryRPC :call RoveServer directly. -// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. -func RegisterRoveHandlerServer(ctx context.Context, mux *runtime.ServeMux, server RoveServer) error { - - mux.Handle("GET", pattern_Rove_Status_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := local_request_Rove_Status_0(rctx, inboundMarshaler, server, req, pathParams) - ctx = runtime.NewServerMetadataContext(ctx, md) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - - forward_Rove_Status_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - - }) - - mux.Handle("POST", pattern_Rove_Register_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := local_request_Rove_Register_0(rctx, inboundMarshaler, server, req, pathParams) - ctx = runtime.NewServerMetadataContext(ctx, md) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - - forward_Rove_Register_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - - }) - - mux.Handle("POST", pattern_Rove_Commands_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := local_request_Rove_Commands_0(rctx, inboundMarshaler, server, req, pathParams) - ctx = runtime.NewServerMetadataContext(ctx, md) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - - forward_Rove_Commands_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - - }) - - mux.Handle("POST", pattern_Rove_Radar_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := local_request_Rove_Radar_0(rctx, inboundMarshaler, server, req, pathParams) - ctx = runtime.NewServerMetadataContext(ctx, md) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - - forward_Rove_Radar_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - - }) - - mux.Handle("POST", pattern_Rove_Rover_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := local_request_Rove_Rover_0(rctx, inboundMarshaler, server, req, pathParams) - ctx = runtime.NewServerMetadataContext(ctx, md) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - - forward_Rove_Rover_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - - }) - - return nil -} - -// RegisterRoveHandlerFromEndpoint is same as RegisterRoveHandler but -// automatically dials to "endpoint" and closes the connection when "ctx" gets done. -func RegisterRoveHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { - conn, err := grpc.Dial(endpoint, opts...) - if err != nil { - return err - } - defer func() { - if err != nil { - if cerr := conn.Close(); cerr != nil { - grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) - } - return - } - go func() { - <-ctx.Done() - if cerr := conn.Close(); cerr != nil { - grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) - } - }() - }() - - return RegisterRoveHandler(ctx, mux, conn) -} - -// RegisterRoveHandler registers the http handlers for service Rove to "mux". -// The handlers forward requests to the grpc endpoint over "conn". -func RegisterRoveHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { - return RegisterRoveHandlerClient(ctx, mux, NewRoveClient(conn)) -} - -// RegisterRoveHandlerClient registers the http handlers for service Rove -// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "RoveClient". -// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "RoveClient" -// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in -// "RoveClient" to call the correct interceptors. -func RegisterRoveHandlerClient(ctx context.Context, mux *runtime.ServeMux, client RoveClient) error { - - mux.Handle("GET", pattern_Rove_Status_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := request_Rove_Status_0(rctx, inboundMarshaler, client, req, pathParams) - ctx = runtime.NewServerMetadataContext(ctx, md) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - - forward_Rove_Status_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - - }) - - mux.Handle("POST", pattern_Rove_Register_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := request_Rove_Register_0(rctx, inboundMarshaler, client, req, pathParams) - ctx = runtime.NewServerMetadataContext(ctx, md) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - - forward_Rove_Register_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - - }) - - mux.Handle("POST", pattern_Rove_Commands_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := request_Rove_Commands_0(rctx, inboundMarshaler, client, req, pathParams) - ctx = runtime.NewServerMetadataContext(ctx, md) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - - forward_Rove_Commands_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - - }) - - mux.Handle("POST", pattern_Rove_Radar_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := request_Rove_Radar_0(rctx, inboundMarshaler, client, req, pathParams) - ctx = runtime.NewServerMetadataContext(ctx, md) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - - forward_Rove_Radar_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - - }) - - mux.Handle("POST", pattern_Rove_Rover_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := request_Rove_Rover_0(rctx, inboundMarshaler, client, req, pathParams) - ctx = runtime.NewServerMetadataContext(ctx, md) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - - forward_Rove_Rover_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - - }) - - return nil -} - -var ( - pattern_Rove_Status_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0}, []string{"status"}, "", runtime.AssumeColonVerbOpt(true))) - - pattern_Rove_Register_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0}, []string{"register"}, "", runtime.AssumeColonVerbOpt(true))) - - pattern_Rove_Commands_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0}, []string{"commands"}, "", runtime.AssumeColonVerbOpt(true))) - - pattern_Rove_Radar_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0}, []string{"radar"}, "", runtime.AssumeColonVerbOpt(true))) - - pattern_Rove_Rover_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0}, []string{"rover"}, "", runtime.AssumeColonVerbOpt(true))) -) - -var ( - forward_Rove_Status_0 = runtime.ForwardResponseMessage - - forward_Rove_Register_0 = runtime.ForwardResponseMessage - - forward_Rove_Commands_0 = runtime.ForwardResponseMessage - - forward_Rove_Radar_0 = runtime.ForwardResponseMessage - - forward_Rove_Rover_0 = runtime.ForwardResponseMessage -) diff --git a/pkg/rove/rove.swagger.json b/pkg/rove/rove.swagger.json deleted file mode 100644 index 3325d41..0000000 --- a/pkg/rove/rove.swagger.json +++ /dev/null @@ -1,354 +0,0 @@ -{ - "swagger": "2.0", - "info": { - "title": "Rove", - "description": "Rove is an asychronous nomadic game about exploring a planet as part of a loose community", - "version": "version not set" - }, - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "paths": { - "/commands": { - "post": { - "summary": "Send commands to rover", - "description": "Sending commands to this endpoint will queue them to be executed during the following ticks, in the order sent", - "operationId": "Rove_Commands", - "responses": { - "200": { - "description": "A successful response.", - "schema": { - "$ref": "#/definitions/roveCommandsResponse" - } - }, - "default": { - "description": "An unexpected error response", - "schema": { - "$ref": "#/definitions/gatewayruntimeError" - } - } - }, - "parameters": [ - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/roveCommandsRequest" - } - } - ], - "tags": [ - "Rove" - ] - } - }, - "/radar": { - "post": { - "summary": "Get radar information", - "description": "Gets the radar output for the given rover", - "operationId": "Rove_Radar", - "responses": { - "200": { - "description": "A successful response.", - "schema": { - "$ref": "#/definitions/roveRadarResponse" - } - }, - "default": { - "description": "An unexpected error response", - "schema": { - "$ref": "#/definitions/gatewayruntimeError" - } - } - }, - "parameters": [ - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/roveRadarRequest" - } - } - ], - "tags": [ - "Rove" - ] - } - }, - "/register": { - "post": { - "summary": "Register an account", - "description": "Tries to register an account with the given name", - "operationId": "Rove_Register", - "responses": { - "200": { - "description": "A successful response.", - "schema": { - "$ref": "#/definitions/roveRegisterResponse" - } - }, - "default": { - "description": "An unexpected error response", - "schema": { - "$ref": "#/definitions/gatewayruntimeError" - } - } - }, - "parameters": [ - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/roveRegisterRequest" - } - } - ], - "tags": [ - "Rove" - ] - } - }, - "/rover": { - "post": { - "summary": "Get rover information", - "description": "Gets information for the account's rover", - "operationId": "Rove_Rover", - "responses": { - "200": { - "description": "A successful response.", - "schema": { - "$ref": "#/definitions/roveRoverResponse" - } - }, - "default": { - "description": "An unexpected error response", - "schema": { - "$ref": "#/definitions/gatewayruntimeError" - } - } - }, - "parameters": [ - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/roveRoverRequest" - } - } - ], - "tags": [ - "Rove" - ] - } - }, - "/status": { - "get": { - "summary": "Server status", - "description": "Responds with various details about the current server status", - "operationId": "Rove_Status", - "responses": { - "200": { - "description": "A successful response.", - "schema": { - "$ref": "#/definitions/roveStatusResponse" - } - }, - "default": { - "description": "An unexpected error response", - "schema": { - "$ref": "#/definitions/gatewayruntimeError" - } - } - }, - "tags": [ - "Rove" - ] - } - } - }, - "definitions": { - "gatewayruntimeError": { - "type": "object", - "properties": { - "error": { - "type": "string" - }, - "code": { - "type": "integer", - "format": "int32" - }, - "message": { - "type": "string" - }, - "details": { - "type": "array", - "items": { - "$ref": "#/definitions/protobufAny" - } - } - } - }, - "protobufAny": { - "type": "object", - "properties": { - "type_url": { - "type": "string" - }, - "value": { - "type": "string", - "format": "byte" - } - } - }, - "roveCommand": { - "type": "object", - "properties": { - "command": { - "type": "string", - "title": "The command to execute\n\"move\" - Move the rover in a direction, requires bearing\n\"stash\" - Stashes item at current location in rover inventory" - }, - "bearing": { - "type": "string", - "title": "The bearing, example: NE" - } - } - }, - "roveCommandsRequest": { - "type": "object", - "properties": { - "account": { - "type": "string", - "title": "The account to execute these commands" - }, - "commands": { - "type": "array", - "items": { - "$ref": "#/definitions/roveCommand" - }, - "title": "The set of desired commands" - } - } - }, - "roveCommandsResponse": { - "type": "object", - "title": "Empty placeholder" - }, - "roveRadarRequest": { - "type": "object", - "properties": { - "account": { - "type": "string", - "title": "The account for this request" - } - } - }, - "roveRadarResponse": { - "type": "object", - "properties": { - "range": { - "type": "integer", - "format": "int32", - "title": "The range in tiles from the rover of the radar data" - }, - "tiles": { - "type": "string", - "format": "byte", - "title": "A 1D array representing range*2 + 1 squared set of tiles, origin bottom left and in row-\u003ecolumn order" - } - } - }, - "roveRegisterRequest": { - "type": "object", - "properties": { - "name": { - "type": "string", - "title": "The desired account name" - } - } - }, - "roveRegisterResponse": { - "type": "object", - "title": "Empty placeholder" - }, - "roveRoverRequest": { - "type": "object", - "properties": { - "account": { - "type": "string", - "title": "The account for this request" - } - } - }, - "roveRoverResponse": { - "type": "object", - "properties": { - "name": { - "type": "string", - "title": "The name of the rover" - }, - "position": { - "$ref": "#/definitions/roveVector", - "title": "Position of the rover in world coordinates" - }, - "range": { - "type": "integer", - "format": "int32", - "title": "The range of this rover's radar" - }, - "inventory": { - "type": "string", - "format": "byte", - "title": "The items in the rover inventory" - }, - "Integrity": { - "type": "integer", - "format": "int32", - "title": "The current health of the rover" - } - } - }, - "roveStatusResponse": { - "type": "object", - "properties": { - "next_tick": { - "type": "string", - "title": "The time the next tick will occur" - }, - "ready": { - "type": "boolean", - "format": "boolean", - "title": "Whether the server is ready to accept requests" - }, - "tick": { - "type": "integer", - "format": "int32", - "title": "The tick rate of the server in minutes (how many minutes per tick)" - }, - "version": { - "type": "string", - "title": "The version of the server in v{major}.{minor}-{delta}-{sha} form" - } - } - }, - "roveVector": { - "type": "object", - "properties": { - "x": { - "type": "integer", - "format": "int32" - }, - "y": { - "type": "integer", - "format": "int32" - } - } - } - } -} diff --git a/pkg/rove/rover.go b/pkg/rove/rover.go new file mode 100644 index 0000000..bebde50 --- /dev/null +++ b/pkg/rove/rover.go @@ -0,0 +1,137 @@ +package rove + +import ( + "bufio" + "fmt" + "log" + "math/rand" + "os" + "time" + + "github.com/google/uuid" + "github.com/mdiluz/rove/pkg/maths" + "github.com/mdiluz/rove/proto/roveapi" +) + +const ( + maxLogEntries = 16 +) + +// RoverLogEntry describes a single log entry for the rover +type RoverLogEntry struct { + // Time is the timestamp of the entry + Time time.Time + + // Text contains the information in this log entry + Text string +} + +// Rover describes a single rover in the world +type Rover struct { + // Unique name of this rover + Name string + + // Pos represents where this rover is in the world + Pos maths.Vector + + // Bearing is the current direction the rover is facing + Bearing roveapi.Bearing + + // Range represents the distance the unit's radar can see + Range int + + // Inventory represents any items the rover is carrying + Inventory []Object + + // Capacity is the maximum number of inventory items + Capacity int + + // Integrity represents current rover health + Integrity int + + // MaximumIntegrity is the full integrity of the rover + MaximumIntegrity int + + // Charge is the amount of energy the rover has + Charge int + + // MaximumCharge is the maximum charge able to be stored + MaximumCharge int + + // SailPosition is the current position of the sails + SailPosition roveapi.SailPosition + + // Current number of ticks in this move, used for sailing speeds + MoveTicks int + + // Logs Stores log of information + Logs []RoverLogEntry + + // The account that owns this rover + Owner string +} + +// DefaultRover returns a default rover object with default settings +func DefaultRover() *Rover { + return &Rover{ + Range: 10, + Integrity: 10, + MaximumIntegrity: 10, + Capacity: 10, + Charge: 10, + MaximumCharge: 10, + Bearing: roveapi.Bearing_North, + SailPosition: roveapi.SailPosition_SolarCharging, + Name: GenerateRoverName(), + } +} + +// AddLogEntryf adds an entry to the rovers log +func (r *Rover) AddLogEntryf(format string, args ...interface{}) { + text := fmt.Sprintf(format, args...) + log.Printf("%s log entry: %s", r.Name, text) + r.Logs = append(r.Logs, + RoverLogEntry{ + Time: time.Now(), + Text: text, + }, + ) + + // Limit the number of logs + if len(r.Logs) > maxLogEntries { + r.Logs = r.Logs[len(r.Logs)-maxLogEntries:] + } +} + +var wordsFile = os.Getenv("WORDS_FILE") +var roverWords []string + +// GenerateRoverName generates a new rover name +func GenerateRoverName() string { + + // Try and load the rover words file + if len(roverWords) == 0 { + // Try and load the words file + if file, err := os.Open(wordsFile); err != nil { + log.Printf("Couldn't read words file [%s], running without words: %s\n", wordsFile, err) + } else { + defer file.Close() + scanner := bufio.NewScanner(file) + for scanner.Scan() { + roverWords = append(roverWords, scanner.Text()) + } + if scanner.Err() != nil { + log.Printf("Failure during word file scan: %s\n", scanner.Err()) + } + } + } + + // Assign a random name if we have words + if len(roverWords) > 0 { + // Loop until we find a unique name + return fmt.Sprintf("%s-%s", roverWords[rand.Intn(len(roverWords))], roverWords[rand.Intn(len(roverWords))]) + } + + // Default to a unique string + return uuid.New().String() +} diff --git a/pkg/rove/world.go b/pkg/rove/world.go new file mode 100644 index 0000000..d509d8d --- /dev/null +++ b/pkg/rove/world.go @@ -0,0 +1,840 @@ +package rove + +import ( + "encoding/json" + "fmt" + "log" + "math/rand" + "sync" + + "github.com/mdiluz/rove/pkg/accounts" + "github.com/mdiluz/rove/pkg/maths" + "github.com/mdiluz/rove/proto/roveapi" +) + +const ( + // ticksPerNormalMove defines the number of ticks it should take for a "normal" speed move + ticksPerNormalMove = 4 + + // upgradeCost is the cost in rover parts needed to upgrade a rover specification + upgradeCost = 5 +) + +// CommandStream is a list of commands to execute in order +type CommandStream []*roveapi.Command + +// World describes a self contained universe and everything in it +type World struct { + + // TicksPerDay is the amount of ticks in a single day + TicksPerDay int + + // Current number of ticks from the start + CurrentTicks int + + // Rovers is a id->data map of all the rovers in the game + Rovers map[string]*Rover + + // Atlas represends the world map of chunks and tiles + Atlas Atlas + + // Wind is the current wind direction + Wind roveapi.Bearing + + // Commands is the set of currently executing command streams per rover + CommandQueue map[string]CommandStream + + // Accountant + Accountant accounts.Accountant + + // Mutex to lock around all world operations + worldMutex sync.RWMutex + // Mutex to lock around command operations + cmdMutex sync.RWMutex +} + +// NewWorld creates a new world object +func NewWorld(chunkSize int) *World { + return &World{ + Rovers: make(map[string]*Rover), + CommandQueue: make(map[string]CommandStream), + Atlas: NewChunkAtlas(chunkSize), + TicksPerDay: 24, + CurrentTicks: 0, + Accountant: accounts.NewSimpleAccountant(), + Wind: roveapi.Bearing_North, + } +} + +// SpawnRover adds an rover to the game (without lock) +func (w *World) SpawnRover(account string) (string, error) { + w.worldMutex.Lock() + defer w.worldMutex.Unlock() + + // Initialise the rover + rover := DefaultRover() + + // Assign the owner + rover.Owner = account + + // Spawn in a random place near the origin + rover.Pos = maths.Vector{ + X: 10 - rand.Intn(20), + Y: 10 - rand.Intn(20), + } + + // Seach until we error (run out of world) + for { + _, obj := w.Atlas.QueryPosition(rover.Pos) + if !obj.IsBlocking() { + break + } else { + // Try and spawn to the east of the blockage + rover.Pos.Add(maths.Vector{X: 1, Y: 0}) + } + + } + + // Add a log entry for robot creation + rover.AddLogEntryf("created at %+v", rover.Pos) + + // Append the rover to the list + w.Rovers[rover.Name] = rover + + var err error + // Only assign if we've been given an account + if len(account) > 0 { + err = w.Accountant.AssignData(account, "rover", rover.Name) + } + + return rover.Name, err +} + +// GetRover gets a specific rover by name +func (w *World) GetRover(rover string) (Rover, error) { + w.worldMutex.RLock() + defer w.worldMutex.RUnlock() + + i, ok := w.Rovers[rover] + if !ok { + return Rover{}, fmt.Errorf("Failed to find rover with name: %s", rover) + } + return *i, nil +} + +// RoverRecharge charges up a rover +func (w *World) RoverRecharge(rover string) (int, error) { + w.worldMutex.Lock() + defer w.worldMutex.Unlock() + + i, ok := w.Rovers[rover] + if !ok { + return 0, fmt.Errorf("Failed to find rover with name: %s", rover) + } + + // We can only recharge during the day + if !w.Daytime() { + return i.Charge, nil + } + + // Add one charge + if i.Charge < i.MaximumCharge { + i.Charge++ + i.AddLogEntryf("recharged to %d", i.Charge) + } + + return i.Charge, nil +} + +// RoverBroadcast broadcasts a message to nearby rovers +func (w *World) RoverBroadcast(rover string, message []byte) (err error) { + w.worldMutex.Lock() + defer w.worldMutex.Unlock() + + i, ok := w.Rovers[rover] + if !ok { + return fmt.Errorf("Failed to find rover with name: %s", rover) + } + + // Use up a charge as needed, if available + if i.Charge == 0 { + return + } + i.Charge-- + + // Check all rovers + for r, rover := range w.Rovers { + if rover.Name == i.Name { + continue + } + + // Check if this rover is within range + if i.Pos.Distance(rover.Pos) < float64(i.Range) { + rover.AddLogEntryf("recieved %s from %s", string(message), i.Name) + w.Rovers[r] = rover + } + } + + i.AddLogEntryf("broadcasted %s", string(message)) + return +} + +// DestroyRover Removes an rover from the game +func (w *World) DestroyRover(rover string) error { + w.worldMutex.Lock() + defer w.worldMutex.Unlock() + + r, ok := w.Rovers[rover] + if !ok { + return fmt.Errorf("no rover matching id") + } + + // Remove this rover from tracked rovers + delete(w.Rovers, rover) + + r.Owner = "" + r.AddLogEntryf("rover destroyed") + + // Marshal the rover data + data, err := json.Marshal(r) + if err != nil { + return err + } + + // Place the dormant rover down + w.Atlas.SetObject(r.Pos, Object{Type: roveapi.Object_RoverDormant, Data: data}) + + return nil +} + +// RoverPosition returns the position of the rover +func (w *World) RoverPosition(rover string) (maths.Vector, error) { + w.worldMutex.RLock() + defer w.worldMutex.RUnlock() + + i, ok := w.Rovers[rover] + if !ok { + return maths.Vector{}, fmt.Errorf("no rover matching id") + } + return i.Pos, nil +} + +// SetRoverPosition sets the position of the rover +func (w *World) SetRoverPosition(rover string, pos maths.Vector) error { + w.worldMutex.Lock() + defer w.worldMutex.Unlock() + + i, ok := w.Rovers[rover] + if !ok { + return fmt.Errorf("no rover matching id") + } + + i.Pos = pos + return nil +} + +// RoverInventory returns the inventory of a requested rover +func (w *World) RoverInventory(rover string) ([]Object, error) { + w.worldMutex.RLock() + defer w.worldMutex.RUnlock() + + i, ok := w.Rovers[rover] + if !ok { + return nil, fmt.Errorf("no rover matching id") + } + return i.Inventory, nil +} + +// WarpRover sets an rovers position +func (w *World) WarpRover(rover string, pos maths.Vector) error { + w.worldMutex.Lock() + defer w.worldMutex.Unlock() + + i, ok := w.Rovers[rover] + if !ok { + return fmt.Errorf("no rover matching id") + } + // Nothing to do if these positions match + if i.Pos == pos { + return nil + } + + // Check the tile is not blocked + _, obj := w.Atlas.QueryPosition(pos) + if obj.IsBlocking() { + return fmt.Errorf("can't warp rover to occupied tile, check before warping") + } + + i.Pos = pos + return nil +} + +// TryMoveRover attempts to move a rover in a specific direction +func (w *World) TryMoveRover(rover string, b roveapi.Bearing) (maths.Vector, error) { + w.worldMutex.Lock() + defer w.worldMutex.Unlock() + + i, ok := w.Rovers[rover] + if !ok { + return maths.Vector{}, fmt.Errorf("no rover matching id") + } + + // Try the new move position + newPos := i.Pos.Added(maths.BearingToVector(b)) + + // Get the tile and verify it's empty + _, obj := w.Atlas.QueryPosition(newPos) + if !obj.IsBlocking() { + i.AddLogEntryf("moved %s to %+v", b.String(), newPos) + // Perform the move + i.Pos = newPos + } else { + // If it is a blocking tile, reduce the rover integrity + i.AddLogEntryf("tried to move %s to %+v", b.String(), newPos) + i.Integrity = i.Integrity - 1 + i.AddLogEntryf("had a collision, new integrity %d", i.Integrity) + } + + return i.Pos, nil +} + +// RoverStash will stash an item at the current rovers position +func (w *World) RoverStash(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 stash object but inventory was full") + return roveapi.Object_ObjectUnknown, nil + } + + // Ensure the rover has energy + if r.Charge <= 0 { + r.AddLogEntryf("tried to stash object but had no charge") + return roveapi.Object_ObjectUnknown, nil + } + r.Charge-- + + _, obj := w.Atlas.QueryPosition(r.Pos) + if !obj.IsStashable() { + return roveapi.Object_ObjectUnknown, nil + } + + r.AddLogEntryf("stashed %c", obj.Type) + r.Inventory = append(r.Inventory, obj) + w.Atlas.SetObject(r.Pos, Object{Type: roveapi.Object_ObjectUnknown}) + return obj.Type, nil +} + +// RoverSalvage will salvage a rover for parts +func (w *World) RoverSalvage(rover string) (roveapi.Object, error) { + w.worldMutex.Lock() + defer w.worldMutex.Unlock() + + r, ok := w.Rovers[rover] + if !ok { + return roveapi.Object_ObjectUnknown, fmt.Errorf("no rover matching id") + } + + // Can't pick up when full + if len(r.Inventory) >= r.Capacity { + r.AddLogEntryf("tried to salvage dormant rover but inventory was full") + return roveapi.Object_ObjectUnknown, nil + } + + // Ensure the rover has energy + if r.Charge <= 0 { + r.AddLogEntryf("tried to salvage dormant rover but had no charge") + return roveapi.Object_ObjectUnknown, nil + } + r.Charge-- + + _, obj := w.Atlas.QueryPosition(r.Pos) + if obj.Type != roveapi.Object_RoverDormant { + r.AddLogEntryf("tried to salvage dormant rover but found no rover to salvage") + return roveapi.Object_ObjectUnknown, nil + } + + r.AddLogEntryf("salvaged dormant rover") + for i := 0; i < 5; i++ { + if len(r.Inventory) == r.Capacity { + break + } + r.Inventory = append(r.Inventory, Object{Type: roveapi.Object_RoverParts}) + } + w.Atlas.SetObject(r.Pos, Object{Type: roveapi.Object_ObjectUnknown}) + return obj.Type, nil +} + +// RoverTransfer will transfer rover control to dormant rover +func (w *World) RoverTransfer(rover string) (string, error) { + w.worldMutex.Lock() + defer w.worldMutex.Unlock() + + oldRover, ok := w.Rovers[rover] + if !ok { + return "", fmt.Errorf("no rover matching id") + } + + _, obj := w.Atlas.QueryPosition(oldRover.Pos) + if obj.Type != roveapi.Object_RoverDormant { + oldRover.AddLogEntryf("tried to transfer to dormant rover but found no rover") + return "", nil + } + + // Unmarshal the dormant rover + var newRover Rover + err := json.Unmarshal(obj.Data, &newRover) + if err != nil { + return "", err + } + + // Add logs + oldRover.AddLogEntryf("transferring to dormant rover %s", newRover.Name) + newRover.AddLogEntryf("transferred from rover %s", oldRover.Name) + + // Transfer the ownership + err = w.Accountant.AssignData(oldRover.Owner, "rover", newRover.Name) + if err != nil { + return "", err + } + newRover.Owner = oldRover.Owner + oldRover.Owner = "" + + // Place the old rover in the world + oldRoverData, err := json.Marshal(oldRover) + if err != nil { + return "", err + } + w.Atlas.SetObject(oldRover.Pos, Object{Type: roveapi.Object_RoverDormant, Data: oldRoverData}) + + // Swap the rovers in the tracking + w.Rovers[newRover.Name] = &newRover + delete(w.Rovers, oldRover.Name) + + // Clear the command queues for both rovers + delete(w.CommandQueue, oldRover.Name) + delete(w.CommandQueue, newRover.Name) + + return newRover.Name, nil +} + +// RoverToggle will toggle the sail position +func (w *World) RoverToggle(rover string) (roveapi.SailPosition, error) { + w.worldMutex.Lock() + defer w.worldMutex.Unlock() + + r, ok := w.Rovers[rover] + if !ok { + return roveapi.SailPosition_UnknownSailPosition, fmt.Errorf("no rover matching id") + } + + // Swap the sail position + switch r.SailPosition { + case roveapi.SailPosition_CatchingWind: + r.SailPosition = roveapi.SailPosition_SolarCharging + case roveapi.SailPosition_SolarCharging: + r.SailPosition = roveapi.SailPosition_CatchingWind + } + + // Reset the movement ticks + r.MoveTicks = 0 + + return r.SailPosition, nil +} + +// RoverUpgrade will try to upgrade the rover +func (w *World) RoverUpgrade(rover string, upgrade roveapi.RoverUpgrade) (int, error) { + w.worldMutex.Lock() + defer w.worldMutex.Unlock() + + r, ok := w.Rovers[rover] + if !ok { + return 0, fmt.Errorf("no rover matching id") + } + + cost := upgradeCost + num := 0 + for i := range r.Inventory { + if r.Inventory[i].Type == roveapi.Object_RoverParts { + num++ + } + } + + if num < cost { + r.AddLogEntryf("tried to upgrade but lacked rover parts") + return 0, nil + } + + // Apply the upgrade + var ret int + switch upgrade { + case roveapi.RoverUpgrade_Capacity: + r.Capacity++ + ret = r.Capacity + case roveapi.RoverUpgrade_Range: + r.Range++ + ret = r.Range + case roveapi.RoverUpgrade_MaximumCharge: + r.MaximumCharge++ + ret = r.MaximumCharge + case roveapi.RoverUpgrade_MaximumIntegrity: + r.MaximumIntegrity++ + ret = r.MaximumIntegrity + default: + return 0, fmt.Errorf("unknown upgrade: %s", upgrade) + } + + // Remove the cost in rover parts + var n []Object + for _, o := range r.Inventory { + if o.Type == roveapi.Object_RoverParts && cost > 0 { + cost-- + } else { + n = append(n, o) + } + } + // Assign back the inventory + r.Inventory = n + + r.AddLogEntryf("upgraded %s to %d", upgrade, ret) + + return ret, nil +} + +// RoverTurn will turn the rover +func (w *World) RoverTurn(rover string, bearing roveapi.Bearing) (roveapi.Bearing, error) { + w.worldMutex.Lock() + defer w.worldMutex.Unlock() + + r, ok := w.Rovers[rover] + if !ok { + return roveapi.Bearing_BearingUnknown, fmt.Errorf("no rover matching id") + } + + // Set the new bearing + r.Bearing = bearing + // Reset the movement ticks + r.MoveTicks = 0 + + return r.Bearing, nil +} + +// RoverRepair will turn the rover +func (w *World) RoverRepair(rover string) (int, error) { + w.worldMutex.Lock() + defer w.worldMutex.Unlock() + + r, ok := w.Rovers[rover] + if !ok { + return 0, fmt.Errorf("no rover matching id") + } + + // Can't repair past max + if r.Integrity >= r.MaximumIntegrity { + return r.Integrity, nil + } + + // Find rover parts in inventory + for i, o := range r.Inventory { + if o.Type == roveapi.Object_RoverParts { + + // Copy-erase from slice + r.Inventory[i] = r.Inventory[len(r.Inventory)-1] + r.Inventory = r.Inventory[:len(r.Inventory)-1] + + // Repair + r.Integrity = r.Integrity + 1 + r.AddLogEntryf("repaired self to %d", r.Integrity) + break + } + } + + return r.Integrity, nil +} + +// RadarFromRover can be used to query what a rover can currently see +func (w *World) RadarFromRover(rover string) (radar []roveapi.Tile, objs []roveapi.Object, err error) { + w.worldMutex.RLock() + defer w.worldMutex.RUnlock() + + r, ok := w.Rovers[rover] + if !ok { + err = fmt.Errorf("no rover matching id") + return + } + + // The radar should span in range direction on each axis, plus the row/column the rover is currently on + radarSpan := (r.Range * 2) + 1 + roverPos := r.Pos + + // Get the radar min and max values + radarMin := maths.Vector{ + X: roverPos.X - r.Range, + Y: roverPos.Y - r.Range, + } + radarMax := maths.Vector{ + X: roverPos.X + r.Range, + Y: roverPos.Y + r.Range, + } + + // Gather up all tiles within the range + radar = make([]roveapi.Tile, radarSpan*radarSpan) + objs = make([]roveapi.Object, radarSpan*radarSpan) + for j := radarMin.Y; j <= radarMax.Y; j++ { + for i := radarMin.X; i <= radarMax.X; i++ { + q := maths.Vector{X: i, Y: j} + + tile, obj := w.Atlas.QueryPosition(q) + + // Get the position relative to the bottom left of the radar + relative := q.Added(radarMin.Negated()) + index := relative.X + relative.Y*radarSpan + radar[index] = tile + objs[index] = obj.Type + } + } + + // Add all rovers to the radar + for _, r := range w.Rovers { + // If the rover is in range + dist := r.Pos.Added(roverPos.Negated()) + dist = dist.Abs() + + if dist.X <= r.Range && dist.Y <= r.Range { + relative := r.Pos.Added(radarMin.Negated()) + index := relative.X + relative.Y*radarSpan + objs[index] = roveapi.Object_RoverLive + } + } + + return radar, objs, nil +} + +// RoverCommands returns current commands for the given rover +func (w *World) RoverCommands(rover string) (queued CommandStream) { + if c, ok := w.CommandQueue[rover]; ok { + queued = c + } + return +} + +// Enqueue will queue the commands given +func (w *World) Enqueue(rover string, commands ...*roveapi.Command) error { + + // First validate the commands + for _, c := range commands { + switch c.Command { + case roveapi.CommandType_broadcast: + if len(c.GetData()) > 3 { + return fmt.Errorf("too many characters in message (limit 3): %d", len(c.GetData())) + } + for _, b := range c.GetData() { + if b < 37 || b > 126 { + return fmt.Errorf("invalid message character: %c", b) + } + } + case roveapi.CommandType_turn: + if c.GetBearing() == roveapi.Bearing_BearingUnknown { + return fmt.Errorf("turn command given unknown bearing") + } + case roveapi.CommandType_upgrade: + if c.GetUpgrade() == roveapi.RoverUpgrade_RoverUpgradeUnknown { + return fmt.Errorf("upgrade command given unknown upgrade") + } + case roveapi.CommandType_wait: + case roveapi.CommandType_toggle: + case roveapi.CommandType_stash: + case roveapi.CommandType_repair: + case roveapi.CommandType_salvage: + case roveapi.CommandType_transfer: + // Nothing to verify + default: + return fmt.Errorf("unknown command: %s", c.Command) + } + } + + // Lock our commands edit + w.cmdMutex.Lock() + defer w.cmdMutex.Unlock() + + w.CommandQueue[rover] = commands + + return nil +} + +// Tick will execute any commands in the current command queue and tick the world +func (w *World) Tick() { + w.cmdMutex.Lock() + defer w.cmdMutex.Unlock() + + // Iterate through all the current commands + for rover, cmds := range w.CommandQueue { + if len(cmds) != 0 { + + // Execute the command + if done, err := w.ExecuteCommand(cmds[0], rover); err != nil { + log.Println(err) + // TODO: Report this error somehow + + } else if done { + // Extract the first command in the queue + // Only if the command queue still has entries (the command may have modified this queue) + if _, ok := w.CommandQueue[rover]; ok { + w.CommandQueue[rover] = cmds[1:] + } + } + + } else { + // Clean out the empty entry + delete(w.CommandQueue, rover) + } + } + + // Move all the rovers based on current wind and sails + for n, r := range w.Rovers { + // Skip if we're not catching the wind + if r.SailPosition != roveapi.SailPosition_CatchingWind { + continue + } + + // Increment the current move ticks + r.MoveTicks++ + + // Get the difference between the two bearings + // Normalise, we don't care about clockwise/anticlockwise + diff := maths.Abs(int(w.Wind - r.Bearing)) + if diff > 4 { + diff = 8 - diff + } + + // Calculate the travel "ticks" + var ticksToMove int + switch diff { + case 0: + // Going with the wind, travel at base speed of once every 4 ticks + ticksToMove = ticksPerNormalMove + case 1: + // At a slight angle, we can go a little faster + ticksToMove = ticksPerNormalMove / 2 + case 2: + // Perpendicular to wind, max speed + ticksToMove = 1 + case 3: + // Heading at 45 degrees into the wind, back to min speed + ticksToMove = ticksPerNormalMove + case 4: + // Heading durectly into the wind, no movement at all + default: + log.Fatalf("bearing difference of %d should be impossible", diff) + } + + // If we've incremented over the current move ticks on the rover, we can try and make the move + if ticksToMove != 0 && r.MoveTicks >= ticksToMove { + _, err := w.TryMoveRover(n, r.Bearing) + if err != nil { + log.Println(err) + // TODO: Report this error somehow + } + + // Reset the move ticks + r.MoveTicks = 0 + } + } + + // Check all rover integrities + for _, r := range w.Rovers { + if r.Integrity <= 0 { + // The rover has died destroy it + err := w.DestroyRover(r.Name) + if err != nil { + log.Println(err) + // TODO: Report this error somehow + } + + // Spawn a new one for this account + _, err = w.SpawnRover(r.Owner) + if err != nil { + log.Println(err) + // TODO: Report this error somehow + } + } + } + + // Increment the current tick count + w.CurrentTicks++ + + // Change the wind every day + if (w.CurrentTicks % w.TicksPerDay) == 0 { + w.Wind = roveapi.Bearing((rand.Int() % 8) + 1) // Random cardinal bearing + } +} + +// ExecuteCommand will execute a single command +func (w *World) ExecuteCommand(c *roveapi.Command, rover string) (done bool, err error) { + log.Printf("Executing command: %+v for %s\n", c.Command, rover) + + switch c.Command { + case roveapi.CommandType_toggle: + _, err = w.RoverToggle(rover) + case roveapi.CommandType_stash: + _, err = w.RoverStash(rover) + case roveapi.CommandType_repair: + _, err = w.RoverRepair(rover) + case roveapi.CommandType_broadcast: + err = w.RoverBroadcast(rover, c.GetData()) + case roveapi.CommandType_turn: + _, err = w.RoverTurn(rover, c.GetBearing()) + case roveapi.CommandType_salvage: + _, err = w.RoverSalvage(rover) + case roveapi.CommandType_transfer: + _, err = w.RoverTransfer(rover) + case roveapi.CommandType_upgrade: + _, err = w.RoverUpgrade(rover, c.GetUpgrade()) + case roveapi.CommandType_wait: + // Nothing to do + default: + return true, fmt.Errorf("unknown command: %s", c.Command) + } + + // Decrement the repeat number + c.Repeat-- + return c.Repeat < 0, err +} + +// Daytime returns if it's currently daytime +// for simplicity this uses the 1st half of the day as daytime, the 2nd half as nighttime +func (w *World) Daytime() bool { + tickInDay := w.CurrentTicks % w.TicksPerDay + return tickInDay < w.TicksPerDay/2 +} + +// RLock read locks the world +func (w *World) RLock() { + w.worldMutex.RLock() + w.cmdMutex.RLock() +} + +// RUnlock read unlocks the world +func (w *World) RUnlock() { + w.worldMutex.RUnlock() + w.cmdMutex.RUnlock() +} + +// Lock locks the world +func (w *World) Lock() { + w.worldMutex.Lock() + w.cmdMutex.Lock() +} + +// Unlock unlocks the world +func (w *World) Unlock() { + w.worldMutex.Unlock() + w.cmdMutex.Unlock() +} diff --git a/pkg/rove/world_test.go b/pkg/rove/world_test.go new file mode 100644 index 0000000..f085d89 --- /dev/null +++ b/pkg/rove/world_test.go @@ -0,0 +1,359 @@ +package rove + +import ( + "testing" + + "github.com/mdiluz/rove/pkg/maths" + "github.com/mdiluz/rove/proto/roveapi" + "github.com/stretchr/testify/assert" +) + +func TestNewWorld(t *testing.T) { + // Very basic for now, nothing to verify + world := NewWorld(4) + if world == nil { + t.Error("Failed to create world") + } +} + +func TestWorld_CreateRover(t *testing.T) { + world := NewWorld(8) + a, err := world.SpawnRover("") + assert.NoError(t, err) + b, err := world.SpawnRover("") + assert.NoError(t, err) + + // Basic duplicate check + if a == b { + t.Errorf("Created identical rovers") + } else if len(world.Rovers) != 2 { + t.Errorf("Incorrect number of rovers created") + } +} + +func TestWorld_GetRover(t *testing.T) { + world := NewWorld(4) + a, err := world.SpawnRover("") + assert.NoError(t, err) + + rover, err := world.GetRover(a) + assert.NoError(t, err, "Failed to get rover attribs") + assert.NotZero(t, rover.Range, "Rover should not be spawned blind") + assert.Contains(t, rover.Logs[len(rover.Logs)-1].Text, "created", "Rover logs should contain the creation") +} + +func TestWorld_DestroyRover(t *testing.T) { + world := NewWorld(1) + a, err := world.SpawnRover("") + assert.NoError(t, err) + b, err := world.SpawnRover("") + assert.NoError(t, err) + + err = world.DestroyRover(a) + assert.NoError(t, err, "Error returned from rover destroy") + + // Basic duplicate check + if len(world.Rovers) != 1 { + t.Error("Too many rovers left in world") + } else if _, ok := world.Rovers[b]; !ok { + t.Error("Remaining rover is incorrect") + } +} + +func TestWorld_GetSetMovePosition(t *testing.T) { + world := NewWorld(4) + 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") + + newPos, err := world.RoverPosition(a) + assert.NoError(t, err, "Failed to set position for rover") + assert.Equal(t, pos, newPos, "Failed to correctly set position for rover") + + b := roveapi.Bearing_North + newPos, err = world.TryMoveRover(a, b) + assert.NoError(t, err, "Failed to set position for rover") + pos.Add(maths.Vector{X: 0, Y: 1}) + assert.Equal(t, pos, newPos, "Failed to correctly move position for rover") + + rover, err := world.GetRover(a) + assert.NoError(t, err, "Failed to get rover information") + assert.Contains(t, rover.Logs[len(rover.Logs)-1].Text, "moved", "Rover logs should contain the move") + + // Place a tile in front of the rover + world.Atlas.SetObject(maths.Vector{X: 0, Y: 2}, Object{Type: roveapi.Object_RockLarge}) + newPos, err = world.TryMoveRover(a, b) + assert.NoError(t, err, "Failed to move rover") + assert.Equal(t, pos, newPos, "Failed to correctly not move position for rover into wall") +} + +func TestWorld_RadarFromRover(t *testing.T) { + // Create world that should have visible walls on the radar + world := NewWorld(2) + a, err := world.SpawnRover("") + assert.NoError(t, err) + b, err := world.SpawnRover("") + assert.NoError(t, err) + + // Warp the rovers into position + bpos := maths.Vector{X: -3, Y: -3} + world.Atlas.SetObject(bpos, Object{Type: roveapi.Object_ObjectUnknown}) + assert.NoError(t, world.WarpRover(b, bpos), "Failed to warp rover") + world.Atlas.SetObject(maths.Vector{X: 0, Y: 0}, Object{Type: roveapi.Object_ObjectUnknown}) + assert.NoError(t, world.WarpRover(a, maths.Vector{X: 0, Y: 0}), "Failed to warp rover") + + r, err := world.GetRover(a) + assert.NoError(t, err) + + radar, objs, err := world.RadarFromRover(a) + assert.NoError(t, err, "Failed to get radar from rover") + fullRange := r.Range + r.Range + 1 + assert.Equal(t, fullRange*fullRange, len(radar), "Radar returned wrong length") + assert.Equal(t, fullRange*fullRange, len(objs), "Radar returned wrong length") + + // TODO: Verify the other rover is on the radar + + // Check the radar results are stable + radar1, objs1, err := world.RadarFromRover(a) + assert.NoError(t, err) + radar2, objs2, err := world.RadarFromRover(a) + assert.NoError(t, err) + assert.Equal(t, radar1, radar2) + assert.Equal(t, objs1, objs2) +} + +func TestWorld_RoverDamage(t *testing.T) { + world := NewWorld(2) + acc, err := world.Accountant.RegisterAccount("tmp") + assert.NoError(t, err) + a, err := world.SpawnRover(acc.Name) + assert.NoError(t, err) + + pos := maths.Vector{ + X: 0.0, + Y: 0.0, + } + + world.Atlas.SetObject(pos, Object{Type: roveapi.Object_ObjectUnknown}) + err = world.WarpRover(a, pos) + assert.NoError(t, err, "Failed to set position for rover") + + info, err := world.GetRover(a) + assert.NoError(t, err, "couldn't get rover info") + + world.Atlas.SetObject(maths.Vector{X: 0.0, Y: 1.0}, Object{Type: roveapi.Object_RockLarge}) + + 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, info.Integrity-1, newinfo.Integrity, "rover should have lost integrity") + assert.Contains(t, newinfo.Logs[len(newinfo.Logs)-1].Text, "collision", "Rover logs should contain the collision") + + // Keep moving to damage the rover + for i := 0; i < info.Integrity-1; i++ { + vec, err := world.TryMoveRover(a, roveapi.Bearing_North) + assert.NoError(t, err, "Failed to move rover") + assert.Equal(t, pos, vec, "Rover managed to move into large rock") + } + + // Tick the world to check for rover deaths + world.Tick() + + // Rover should have been destroyed now + _, err = world.GetRover(a) + assert.Error(t, err) + + _, obj := world.Atlas.QueryPosition(info.Pos) + assert.Equal(t, roveapi.Object_RoverDormant, obj.Type) +} + +func TestWorld_Daytime(t *testing.T) { + world := NewWorld(1) + + a, err := world.SpawnRover("") + assert.NoError(t, err) + + // Remove rover charge + rover := world.Rovers[a] + rover.Charge = 0 + world.Rovers[a] = rover + + // Try and recharge, should work + _, err = world.RoverRecharge(a) + assert.NoError(t, err) + assert.Equal(t, 1, world.Rovers[a].Charge) + + // Loop for half the day + for i := 0; i < world.TicksPerDay/2; i++ { + assert.True(t, world.Daytime()) + world.Tick() + } + + // Remove rover charge again + rover = world.Rovers[a] + rover.Charge = 0 + world.Rovers[a] = rover + + // Try and recharge, should fail + _, err = world.RoverRecharge(a) + assert.NoError(t, err) + assert.Equal(t, 0, world.Rovers[a].Charge) + + // Loop for half the day + for i := 0; i < world.TicksPerDay/2; i++ { + assert.False(t, world.Daytime()) + world.Tick() + } +} + +func TestWorld_Broadcast(t *testing.T) { + world := NewWorld(8) + + a, err := world.SpawnRover("") + assert.NoError(t, err) + + b, err := world.SpawnRover("") + assert.NoError(t, err) + + // Warp rovers near to eachother + world.Atlas.SetObject(maths.Vector{X: 0, Y: 0}, Object{Type: roveapi.Object_ObjectUnknown}) + world.Atlas.SetObject(maths.Vector{X: 1, Y: 0}, Object{Type: roveapi.Object_ObjectUnknown}) + assert.NoError(t, world.WarpRover(a, maths.Vector{X: 0, Y: 0})) + assert.NoError(t, world.WarpRover(b, maths.Vector{X: 1, Y: 0})) + + // Broadcast from a + assert.NoError(t, world.RoverBroadcast(a, []byte{'A', 'B', 'C'})) + + // Check if b heard it + ra, err := world.GetRover(a) + assert.NoError(t, err) + assert.Equal(t, ra.MaximumCharge-1, ra.Charge, "A should have used a charge to broadcast") + assert.Contains(t, ra.Logs[len(ra.Logs)-1].Text, "ABC", "Rover B should have heard the broadcast") + + // Check if a logged it + rb, err := world.GetRover(b) + assert.NoError(t, err) + assert.Contains(t, rb.Logs[len(rb.Logs)-1].Text, "ABC", "Rover A should have logged it's broadcast") + + // Warp B outside of the range of A + world.Atlas.SetObject(maths.Vector{X: ra.Range, Y: 0}, Object{Type: roveapi.Object_ObjectUnknown}) + assert.NoError(t, world.WarpRover(b, maths.Vector{X: ra.Range, Y: 0})) + + // Broadcast from a again + assert.NoError(t, world.RoverBroadcast(a, []byte{'X', 'Y', 'Z'})) + + // Check if b heard it + ra, err = world.GetRover(b) + assert.NoError(t, err) + assert.NotContains(t, ra.Logs[len(ra.Logs)-1].Text, "XYZ", "Rover B should not have heard the broadcast") + + // Check if a logged it + rb, err = world.GetRover(a) + assert.NoError(t, err) + assert.Contains(t, rb.Logs[len(rb.Logs)-1].Text, "XYZ", "Rover A should have logged it's broadcast") + + // Warp B outside of the range of A + world.Atlas.SetObject(maths.Vector{X: ra.Range + 1, Y: 0}, Object{Type: roveapi.Object_ObjectUnknown}) + assert.NoError(t, world.WarpRover(b, maths.Vector{X: ra.Range + 1, Y: 0})) + + // Broadcast from a again + assert.NoError(t, world.RoverBroadcast(a, []byte{'H', 'J', 'K'})) + + // Check if b heard it + ra, err = world.GetRover(b) + assert.NoError(t, err) + assert.NotContains(t, ra.Logs[len(ra.Logs)-1].Text, "HJK", "Rover B should have heard the broadcast") + + // Check if a logged it + rb, err = world.GetRover(a) + assert.NoError(t, err) + assert.Contains(t, rb.Logs[len(rb.Logs)-1].Text, "HJK", "Rover A should have logged it's broadcast") +} + +func TestWorld_Sailing(t *testing.T) { + world := NewWorld(8) + world.Tick() // One initial tick to set the wind direction the first time + world.Wind = roveapi.Bearing_North // Set the wind direction to north + + name, err := world.SpawnRover("") + assert.NoError(t, err) + + // Warp the rover to 0,0 after clearing it + world.Atlas.SetObject(maths.Vector{X: 0, Y: 0}, Object{Type: roveapi.Object_ObjectUnknown}) + assert.NoError(t, world.WarpRover(name, maths.Vector{X: 0, Y: 0})) + + s, err := world.RoverToggle(name) + assert.NoError(t, err) + assert.Equal(t, roveapi.SailPosition_CatchingWind, s) + + b, err := world.RoverTurn(name, roveapi.Bearing_North) + assert.NoError(t, err) + assert.Equal(t, roveapi.Bearing_North, b) + + // Clear the space to the north + world.Atlas.SetObject(maths.Vector{X: 0, Y: 1}, Object{Type: roveapi.Object_ObjectUnknown}) + + // Tick the world and check we've moved not moved + world.Tick() + info, err := world.GetRover(name) + assert.NoError(t, err) + assert.Equal(t, maths.Vector{Y: 0}, info.Pos) + + // Loop a few more times + for i := 0; i < ticksPerNormalMove-2; i++ { + world.Tick() + info, err := world.GetRover(name) + assert.NoError(t, err) + assert.Equal(t, maths.Vector{Y: 0}, info.Pos) + } + + // Now check we've moved (after the TicksPerNormalMove number of ticks) + world.Tick() + info, err = world.GetRover(name) + assert.NoError(t, err) + assert.Equal(t, maths.Vector{Y: 1}, info.Pos) + + // Reset the world ticks back to stop any wind changes etc. + world.CurrentTicks = 1 + + // Face the rover south, into the wind + b, err = world.RoverTurn(name, roveapi.Bearing_South) + assert.NoError(t, err) + assert.Equal(t, roveapi.Bearing_South, b) + + // Tick a bunch, we should never move + for i := 0; i < ticksPerNormalMove*2; i++ { + world.Tick() + info, err := world.GetRover(name) + assert.NoError(t, err) + assert.Equal(t, maths.Vector{Y: 1}, info.Pos) + } + + // Reset the world ticks back to stop any wind changes etc. + world.CurrentTicks = 1 + world.Wind = roveapi.Bearing_SouthEast // Set up a south easternly wind + + // Turn the rover perpendicular + b, err = world.RoverTurn(name, roveapi.Bearing_NorthEast) + assert.NoError(t, err) + assert.Equal(t, roveapi.Bearing_NorthEast, b) + + // Clear a space + world.Atlas.SetObject(maths.Vector{X: 1, Y: 2}, Object{Type: roveapi.Object_ObjectUnknown}) + + // Now check we've moved immediately + world.Tick() + info, err = world.GetRover(name) + assert.NoError(t, err) + assert.Equal(t, maths.Vector{X: 1, Y: 2}, info.Pos) +} diff --git a/pkg/rove/worldgen.go b/pkg/rove/worldgen.go new file mode 100644 index 0000000..6cb425f --- /dev/null +++ b/pkg/rove/worldgen.go @@ -0,0 +1,70 @@ +package rove + +import ( + "github.com/mdiluz/rove/pkg/maths" + "github.com/mdiluz/rove/proto/roveapi" + "github.com/ojrac/opensimplex-go" +) + +// WorldGen describes a world gen algorythm +type WorldGen interface { + // GetTile generates a tile for a location + GetTile(v maths.Vector) roveapi.Tile + + // GetObject generates an object for a location + GetObject(v maths.Vector) Object +} + +// NoiseWorldGen returns a noise based world generator +type NoiseWorldGen struct { + // noise describes the noise function + noise opensimplex.Noise +} + +// NewNoiseWorldGen creates a new noise based world generator +func NewNoiseWorldGen(seed int64) WorldGen { + return &NoiseWorldGen{ + noise: opensimplex.New(seed), + } +} + +const ( + terrainNoiseScale = 15 + rockNoiseScale = 3 + partsNoiseScale = 2 +) + +// GetTile returns the chosen tile at a location +func (g *NoiseWorldGen) GetTile(v maths.Vector) roveapi.Tile { + t := g.noise.Eval2(float64(v.X)/terrainNoiseScale, float64(v.Y)/terrainNoiseScale) + switch { + case t > 0.5: + return roveapi.Tile_Gravel + case t > 0.05: + return roveapi.Tile_Sand + default: + return roveapi.Tile_Rock + } +} + +// GetObject returns the chosen object at a location +func (g *NoiseWorldGen) GetObject(v maths.Vector) (obj Object) { + r := g.noise.Eval2(float64(v.X)/rockNoiseScale, float64(v.Y)/rockNoiseScale) + switch { + // Prioritise rocks + case r > 0.6: + obj.Type = roveapi.Object_RockLarge + case r > 0.5: + obj.Type = roveapi.Object_RockSmall + + default: + // Otherwise, try some rover parts + p := g.noise.Eval2(float64(v.X)/partsNoiseScale, float64(v.Y)/partsNoiseScale) + switch { + case p > 0.7: + obj.Type = roveapi.Object_RoverParts + } + } + + return obj +} diff --git a/pkg/vector/vector.go b/pkg/vector/vector.go deleted file mode 100644 index 1b82c5a..0000000 --- a/pkg/vector/vector.go +++ /dev/null @@ -1,56 +0,0 @@ -package vector - -import ( - "math" - - "github.com/mdiluz/rove/pkg/maths" -) - -// Vector desribes a 3D vector -type Vector struct { - X int `json:"x"` - Y int `json:"y"` -} - -// Add adds one vector to another -func (v *Vector) Add(v2 Vector) { - v.X += v2.X - v.Y += v2.Y -} - -// Added calculates a new vector -func (v Vector) Added(v2 Vector) Vector { - v.Add(v2) - return v -} - -// Negated returns a negated vector -func (v Vector) Negated() Vector { - return Vector{-v.X, -v.Y} -} - -// Length returns the length of the vector -func (v Vector) Length() float64 { - return math.Sqrt(float64(v.X*v.X + v.Y*v.Y)) -} - -// Distance returns the distance between two vectors -func (v Vector) Distance(v2 Vector) float64 { - // Negate the two vectors and calciate the length - return v.Added(v2.Negated()).Length() -} - -// Multiplied returns the vector multiplied by an int -func (v Vector) Multiplied(val int) Vector { - return Vector{v.X * val, v.Y * val} -} - -// Divided returns the vector divided by an int -func (v Vector) Divided(val int) Vector { - return Vector{v.X / val, v.Y / val} -} - -// Abs returns an absolute version of the vector -func (v Vector) Abs() Vector { - return Vector{maths.Abs(v.X), maths.Abs(v.Y)} -} diff --git a/pkg/version/version.go b/pkg/version/version.go index b8b26bb..0daefb3 100644 --- a/pkg/version/version.go +++ b/pkg/version/version.go @@ -1,3 +1,4 @@ package version +// Version represents a version to be overrided with -ldflags var Version = "undefined" diff --git a/proto/accounts/accounts.proto b/proto/accounts/accounts.proto deleted file mode 100644 index 50f0dd6..0000000 --- a/proto/accounts/accounts.proto +++ /dev/null @@ -1,54 +0,0 @@ -syntax = "proto3"; - -option go_package = "github.com/mdiluz/rove/pkg/accounts"; - -package accounts; - -service Accountant { - // Register should create a new account in the database - // It will return an error if the account already exists - rpc Register(RegisterInfo) returns (RegisterResponse) {} - - // AssignValue assigns a key-value pair to an account, or overwrites an existing key - rpc AssignValue(DataKeyValue) returns (DataKeyResponse) {} - - // GetValue will get the value for a key for an account - rpc GetValue(DataKey) returns (DataResponse) {} -} - -// RegisterInfo contains the information needed to register an account -message RegisterInfo { - // The name for the account, must be unique - string name = 1; -} - -// RegisterResponse is the response information from registering an account -message RegisterResponse {} - -// DataKeyValue represents a simple key value pair to assign to an account -message DataKeyValue { - // The account to assign the new key value pair to - string account = 1; - - // The key value pair to assign - string key = 2; - string value = 3; -} - -// DataKeyResponse is a simple response -message DataKeyResponse {} - -// DataKey describes a simple key value with an account, for fetching -message DataKey { - // The account to fetch data for - string account = 1; - - // The key to fetch - string key = 2; -} - -// DataResponse describes a data fetch response -message DataResponse { - // The value of the key - string value = 3; -} diff --git a/proto/google/README.grpc-gateway b/proto/google/README.grpc-gateway deleted file mode 100644 index b7d1bea..0000000 --- a/proto/google/README.grpc-gateway +++ /dev/null @@ -1,23 +0,0 @@ -Google APIs -============ - -Project: Google APIs -URL: https://github.com/google/googleapis -Revision: 3544ab16c3342d790b00764251e348705991ea4b -License: Apache License 2.0 - - -Imported Files ---------------- - -- google/api/annotations.proto -- google/api/http.proto -- google/api/httpbody.proto - - -Generated Files ----------------- - -They are generated from the .proto files by protoc-gen-go. -- google/api/annotations.pb.go -- google/api/http.pb.go diff --git a/proto/google/api/annotations.proto b/proto/google/api/annotations.proto deleted file mode 100644 index 85c361b..0000000 --- a/proto/google/api/annotations.proto +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) 2015, Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -syntax = "proto3"; - -package google.api; - -import "google/api/http.proto"; -import "google/protobuf/descriptor.proto"; - -option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations"; -option java_multiple_files = true; -option java_outer_classname = "AnnotationsProto"; -option java_package = "com.google.api"; -option objc_class_prefix = "GAPI"; - -extend google.protobuf.MethodOptions { - // See `HttpRule`. - HttpRule http = 72295728; -} diff --git a/proto/google/api/http.proto b/proto/google/api/http.proto deleted file mode 100644 index 2bd3a19..0000000 --- a/proto/google/api/http.proto +++ /dev/null @@ -1,318 +0,0 @@ -// Copyright 2018 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -syntax = "proto3"; - -package google.api; - -option cc_enable_arenas = true; -option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations"; -option java_multiple_files = true; -option java_outer_classname = "HttpProto"; -option java_package = "com.google.api"; -option objc_class_prefix = "GAPI"; - - -// Defines the HTTP configuration for an API service. It contains a list of -// [HttpRule][google.api.HttpRule], each specifying the mapping of an RPC method -// to one or more HTTP REST API methods. -message Http { - // A list of HTTP configuration rules that apply to individual API methods. - // - // **NOTE:** All service configuration rules follow "last one wins" order. - repeated HttpRule rules = 1; - - // When set to true, URL path parmeters will be fully URI-decoded except in - // cases of single segment matches in reserved expansion, where "%2F" will be - // left encoded. - // - // The default behavior is to not decode RFC 6570 reserved characters in multi - // segment matches. - bool fully_decode_reserved_expansion = 2; -} - -// `HttpRule` defines the mapping of an RPC method to one or more HTTP -// REST API methods. The mapping specifies how different portions of the RPC -// request message are mapped to URL path, URL query parameters, and -// HTTP request body. The mapping is typically specified as an -// `google.api.http` annotation on the RPC method, -// see "google/api/annotations.proto" for details. -// -// The mapping consists of a field specifying the path template and -// method kind. The path template can refer to fields in the request -// message, as in the example below which describes a REST GET -// operation on a resource collection of messages: -// -// -// service Messaging { -// rpc GetMessage(GetMessageRequest) returns (Message) { -// option (google.api.http).get = "/v1/messages/{message_id}/{sub.subfield}"; -// } -// } -// message GetMessageRequest { -// message SubMessage { -// string subfield = 1; -// } -// string message_id = 1; // mapped to the URL -// SubMessage sub = 2; // `sub.subfield` is url-mapped -// } -// message Message { -// string text = 1; // content of the resource -// } -// -// The same http annotation can alternatively be expressed inside the -// `GRPC API Configuration` YAML file. -// -// http: -// rules: -// - selector: .Messaging.GetMessage -// get: /v1/messages/{message_id}/{sub.subfield} -// -// This definition enables an automatic, bidrectional mapping of HTTP -// JSON to RPC. Example: -// -// HTTP | RPC -// -----|----- -// `GET /v1/messages/123456/foo` | `GetMessage(message_id: "123456" sub: SubMessage(subfield: "foo"))` -// -// In general, not only fields but also field paths can be referenced -// from a path pattern. Fields mapped to the path pattern cannot be -// repeated and must have a primitive (non-message) type. -// -// Any fields in the request message which are not bound by the path -// pattern automatically become (optional) HTTP query -// parameters. Assume the following definition of the request message: -// -// -// service Messaging { -// rpc GetMessage(GetMessageRequest) returns (Message) { -// option (google.api.http).get = "/v1/messages/{message_id}"; -// } -// } -// message GetMessageRequest { -// message SubMessage { -// string subfield = 1; -// } -// string message_id = 1; // mapped to the URL -// int64 revision = 2; // becomes a parameter -// SubMessage sub = 3; // `sub.subfield` becomes a parameter -// } -// -// -// This enables a HTTP JSON to RPC mapping as below: -// -// HTTP | RPC -// -----|----- -// `GET /v1/messages/123456?revision=2&sub.subfield=foo` | `GetMessage(message_id: "123456" revision: 2 sub: SubMessage(subfield: "foo"))` -// -// Note that fields which are mapped to HTTP parameters must have a -// primitive type or a repeated primitive type. Message types are not -// allowed. In the case of a repeated type, the parameter can be -// repeated in the URL, as in `...?param=A¶m=B`. -// -// For HTTP method kinds which allow a request body, the `body` field -// specifies the mapping. Consider a REST update method on the -// message resource collection: -// -// -// service Messaging { -// rpc UpdateMessage(UpdateMessageRequest) returns (Message) { -// option (google.api.http) = { -// put: "/v1/messages/{message_id}" -// body: "message" -// }; -// } -// } -// message UpdateMessageRequest { -// string message_id = 1; // mapped to the URL -// Message message = 2; // mapped to the body -// } -// -// -// The following HTTP JSON to RPC mapping is enabled, where the -// representation of the JSON in the request body is determined by -// protos JSON encoding: -// -// HTTP | RPC -// -----|----- -// `PUT /v1/messages/123456 { "text": "Hi!" }` | `UpdateMessage(message_id: "123456" message { text: "Hi!" })` -// -// The special name `*` can be used in the body mapping to define that -// every field not bound by the path template should be mapped to the -// request body. This enables the following alternative definition of -// the update method: -// -// service Messaging { -// rpc UpdateMessage(Message) returns (Message) { -// option (google.api.http) = { -// put: "/v1/messages/{message_id}" -// body: "*" -// }; -// } -// } -// message Message { -// string message_id = 1; -// string text = 2; -// } -// -// -// The following HTTP JSON to RPC mapping is enabled: -// -// HTTP | RPC -// -----|----- -// `PUT /v1/messages/123456 { "text": "Hi!" }` | `UpdateMessage(message_id: "123456" text: "Hi!")` -// -// Note that when using `*` in the body mapping, it is not possible to -// have HTTP parameters, as all fields not bound by the path end in -// the body. This makes this option more rarely used in practice of -// defining REST APIs. The common usage of `*` is in custom methods -// which don't use the URL at all for transferring data. -// -// It is possible to define multiple HTTP methods for one RPC by using -// the `additional_bindings` option. Example: -// -// service Messaging { -// rpc GetMessage(GetMessageRequest) returns (Message) { -// option (google.api.http) = { -// get: "/v1/messages/{message_id}" -// additional_bindings { -// get: "/v1/users/{user_id}/messages/{message_id}" -// } -// }; -// } -// } -// message GetMessageRequest { -// string message_id = 1; -// string user_id = 2; -// } -// -// -// This enables the following two alternative HTTP JSON to RPC -// mappings: -// -// HTTP | RPC -// -----|----- -// `GET /v1/messages/123456` | `GetMessage(message_id: "123456")` -// `GET /v1/users/me/messages/123456` | `GetMessage(user_id: "me" message_id: "123456")` -// -// # Rules for HTTP mapping -// -// The rules for mapping HTTP path, query parameters, and body fields -// to the request message are as follows: -// -// 1. The `body` field specifies either `*` or a field path, or is -// omitted. If omitted, it indicates there is no HTTP request body. -// 2. Leaf fields (recursive expansion of nested messages in the -// request) can be classified into three types: -// (a) Matched in the URL template. -// (b) Covered by body (if body is `*`, everything except (a) fields; -// else everything under the body field) -// (c) All other fields. -// 3. URL query parameters found in the HTTP request are mapped to (c) fields. -// 4. Any body sent with an HTTP request can contain only (b) fields. -// -// The syntax of the path template is as follows: -// -// Template = "/" Segments [ Verb ] ; -// Segments = Segment { "/" Segment } ; -// Segment = "*" | "**" | LITERAL | Variable ; -// Variable = "{" FieldPath [ "=" Segments ] "}" ; -// FieldPath = IDENT { "." IDENT } ; -// Verb = ":" LITERAL ; -// -// The syntax `*` matches a single path segment. The syntax `**` matches zero -// or more path segments, which must be the last part of the path except the -// `Verb`. The syntax `LITERAL` matches literal text in the path. -// -// The syntax `Variable` matches part of the URL path as specified by its -// template. A variable template must not contain other variables. If a variable -// matches a single path segment, its template may be omitted, e.g. `{var}` -// is equivalent to `{var=*}`. -// -// If a variable contains exactly one path segment, such as `"{var}"` or -// `"{var=*}"`, when such a variable is expanded into a URL path, all characters -// except `[-_.~0-9a-zA-Z]` are percent-encoded. Such variables show up in the -// Discovery Document as `{var}`. -// -// If a variable contains one or more path segments, such as `"{var=foo/*}"` -// or `"{var=**}"`, when such a variable is expanded into a URL path, all -// characters except `[-_.~/0-9a-zA-Z]` are percent-encoded. Such variables -// show up in the Discovery Document as `{+var}`. -// -// NOTE: While the single segment variable matches the semantics of -// [RFC 6570](https://tools.ietf.org/html/rfc6570) Section 3.2.2 -// Simple String Expansion, the multi segment variable **does not** match -// RFC 6570 Reserved Expansion. The reason is that the Reserved Expansion -// does not expand special characters like `?` and `#`, which would lead -// to invalid URLs. -// -// NOTE: the field paths in variables and in the `body` must not refer to -// repeated fields or map fields. -message HttpRule { - // Selects methods to which this rule applies. - // - // Refer to [selector][google.api.DocumentationRule.selector] for syntax details. - string selector = 1; - - // Determines the URL pattern is matched by this rules. This pattern can be - // used with any of the {get|put|post|delete|patch} methods. A custom method - // can be defined using the 'custom' field. - oneof pattern { - // Used for listing and getting information about resources. - string get = 2; - - // Used for updating a resource. - string put = 3; - - // Used for creating a resource. - string post = 4; - - // Used for deleting a resource. - string delete = 5; - - // Used for updating a resource. - string patch = 6; - - // The custom pattern is used for specifying an HTTP method that is not - // included in the `pattern` field, such as HEAD, or "*" to leave the - // HTTP method unspecified for this rule. The wild-card rule is useful - // for services that provide content to Web (HTML) clients. - CustomHttpPattern custom = 8; - } - - // The name of the request field whose value is mapped to the HTTP body, or - // `*` for mapping all fields not captured by the path pattern to the HTTP - // body. NOTE: the referred field must not be a repeated field and must be - // present at the top-level of request message type. - string body = 7; - - // Optional. The name of the response field whose value is mapped to the HTTP - // body of response. Other response fields are ignored. When - // not set, the response message will be used as HTTP body of response. - string response_body = 12; - - // Additional HTTP bindings for the selector. Nested bindings must - // not contain an `additional_bindings` field themselves (that is, - // the nesting may only be one level deep). - repeated HttpRule additional_bindings = 11; -} - -// A custom pattern is used for defining custom HTTP verb. -message CustomHttpPattern { - // The name of this custom HTTP verb. - string kind = 1; - - // The path matched by this custom verb. - string path = 2; -} diff --git a/proto/google/api/httpbody.proto b/proto/google/api/httpbody.proto deleted file mode 100644 index 4428515..0000000 --- a/proto/google/api/httpbody.proto +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright 2018 Google LLC. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -syntax = "proto3"; - -package google.api; - -import "google/protobuf/any.proto"; - -option cc_enable_arenas = true; -option go_package = "google.golang.org/genproto/googleapis/api/httpbody;httpbody"; -option java_multiple_files = true; -option java_outer_classname = "HttpBodyProto"; -option java_package = "com.google.api"; -option objc_class_prefix = "GAPI"; - -// Message that represents an arbitrary HTTP body. It should only be used for -// payload formats that can't be represented as JSON, such as raw binary or -// an HTML page. -// -// -// This message can be used both in streaming and non-streaming API methods in -// the request as well as the response. -// -// It can be used as a top-level request field, which is convenient if one -// wants to extract parameters from either the URL or HTTP template into the -// request fields and also want access to the raw HTTP body. -// -// Example: -// -// message GetResourceRequest { -// // A unique request id. -// string request_id = 1; -// -// // The raw HTTP body is bound to this field. -// google.api.HttpBody http_body = 2; -// } -// -// service ResourceService { -// rpc GetResource(GetResourceRequest) returns (google.api.HttpBody); -// rpc UpdateResource(google.api.HttpBody) returns -// (google.protobuf.Empty); -// } -// -// Example with streaming methods: -// -// service CaldavService { -// rpc GetCalendar(stream google.api.HttpBody) -// returns (stream google.api.HttpBody); -// rpc UpdateCalendar(stream google.api.HttpBody) -// returns (stream google.api.HttpBody); -// } -// -// Use of this type only changes how the request and response bodies are -// handled, all other features will continue to work unchanged. -message HttpBody { - // The HTTP Content-Type header value specifying the content type of the body. - string content_type = 1; - - // The HTTP request/response body as raw binary. - bytes data = 2; - - // Application specific response metadata. Must be set in the first response - // for streaming APIs. - repeated google.protobuf.Any extensions = 3; -} \ No newline at end of file diff --git a/proto/google/rpc/code.proto b/proto/google/rpc/code.proto deleted file mode 100644 index 8fef411..0000000 --- a/proto/google/rpc/code.proto +++ /dev/null @@ -1,186 +0,0 @@ -// Copyright 2017 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -syntax = "proto3"; - -package google.rpc; - -option go_package = "google.golang.org/genproto/googleapis/rpc/code;code"; -option java_multiple_files = true; -option java_outer_classname = "CodeProto"; -option java_package = "com.google.rpc"; -option objc_class_prefix = "RPC"; - - -// The canonical error codes for Google APIs. -// -// -// Sometimes multiple error codes may apply. Services should return -// the most specific error code that applies. For example, prefer -// `OUT_OF_RANGE` over `FAILED_PRECONDITION` if both codes apply. -// Similarly prefer `NOT_FOUND` or `ALREADY_EXISTS` over `FAILED_PRECONDITION`. -enum Code { - // Not an error; returned on success - // - // HTTP Mapping: 200 OK - OK = 0; - - // The operation was cancelled, typically by the caller. - // - // HTTP Mapping: 499 Client Closed Request - CANCELLED = 1; - - // Unknown error. For example, this error may be returned when - // a `Status` value received from another address space belongs to - // an error space that is not known in this address space. Also - // errors raised by APIs that do not return enough error information - // may be converted to this error. - // - // HTTP Mapping: 500 Internal Server Error - UNKNOWN = 2; - - // The client specified an invalid argument. Note that this differs - // from `FAILED_PRECONDITION`. `INVALID_ARGUMENT` indicates arguments - // that are problematic regardless of the state of the system - // (e.g., a malformed file name). - // - // HTTP Mapping: 400 Bad Request - INVALID_ARGUMENT = 3; - - // The deadline expired before the operation could complete. For operations - // that change the state of the system, this error may be returned - // even if the operation has completed successfully. For example, a - // successful response from a server could have been delayed long - // enough for the deadline to expire. - // - // HTTP Mapping: 504 Gateway Timeout - DEADLINE_EXCEEDED = 4; - - // Some requested entity (e.g., file or directory) was not found. - // - // Note to server developers: if a request is denied for an entire class - // of users, such as gradual feature rollout or undocumented whitelist, - // `NOT_FOUND` may be used. If a request is denied for some users within - // a class of users, such as user-based access control, `PERMISSION_DENIED` - // must be used. - // - // HTTP Mapping: 404 Not Found - NOT_FOUND = 5; - - // The entity that a client attempted to create (e.g., file or directory) - // already exists. - // - // HTTP Mapping: 409 Conflict - ALREADY_EXISTS = 6; - - // The caller does not have permission to execute the specified - // operation. `PERMISSION_DENIED` must not be used for rejections - // caused by exhausting some resource (use `RESOURCE_EXHAUSTED` - // instead for those errors). `PERMISSION_DENIED` must not be - // used if the caller can not be identified (use `UNAUTHENTICATED` - // instead for those errors). This error code does not imply the - // request is valid or the requested entity exists or satisfies - // other pre-conditions. - // - // HTTP Mapping: 403 Forbidden - PERMISSION_DENIED = 7; - - // The request does not have valid authentication credentials for the - // operation. - // - // HTTP Mapping: 401 Unauthorized - UNAUTHENTICATED = 16; - - // Some resource has been exhausted, perhaps a per-user quota, or - // perhaps the entire file system is out of space. - // - // HTTP Mapping: 429 Too Many Requests - RESOURCE_EXHAUSTED = 8; - - // The operation was rejected because the system is not in a state - // required for the operation's execution. For example, the directory - // to be deleted is non-empty, an rmdir operation is applied to - // a non-directory, etc. - // - // Service implementors can use the following guidelines to decide - // between `FAILED_PRECONDITION`, `ABORTED`, and `UNAVAILABLE`: - // (a) Use `UNAVAILABLE` if the client can retry just the failing call. - // (b) Use `ABORTED` if the client should retry at a higher level - // (e.g., when a client-specified test-and-set fails, indicating the - // client should restart a read-modify-write sequence). - // (c) Use `FAILED_PRECONDITION` if the client should not retry until - // the system state has been explicitly fixed. E.g., if an "rmdir" - // fails because the directory is non-empty, `FAILED_PRECONDITION` - // should be returned since the client should not retry unless - // the files are deleted from the directory. - // - // HTTP Mapping: 400 Bad Request - FAILED_PRECONDITION = 9; - - // The operation was aborted, typically due to a concurrency issue such as - // a sequencer check failure or transaction abort. - // - // See the guidelines above for deciding between `FAILED_PRECONDITION`, - // `ABORTED`, and `UNAVAILABLE`. - // - // HTTP Mapping: 409 Conflict - ABORTED = 10; - - // The operation was attempted past the valid range. E.g., seeking or - // reading past end-of-file. - // - // Unlike `INVALID_ARGUMENT`, this error indicates a problem that may - // be fixed if the system state changes. For example, a 32-bit file - // system will generate `INVALID_ARGUMENT` if asked to read at an - // offset that is not in the range [0,2^32-1], but it will generate - // `OUT_OF_RANGE` if asked to read from an offset past the current - // file size. - // - // There is a fair bit of overlap between `FAILED_PRECONDITION` and - // `OUT_OF_RANGE`. We recommend using `OUT_OF_RANGE` (the more specific - // error) when it applies so that callers who are iterating through - // a space can easily look for an `OUT_OF_RANGE` error to detect when - // they are done. - // - // HTTP Mapping: 400 Bad Request - OUT_OF_RANGE = 11; - - // The operation is not implemented or is not supported/enabled in this - // service. - // - // HTTP Mapping: 501 Not Implemented - UNIMPLEMENTED = 12; - - // Internal errors. This means that some invariants expected by the - // underlying system have been broken. This error code is reserved - // for serious errors. - // - // HTTP Mapping: 500 Internal Server Error - INTERNAL = 13; - - // The service is currently unavailable. This is most likely a - // transient condition, which can be corrected by retrying with - // a backoff. - // - // See the guidelines above for deciding between `FAILED_PRECONDITION`, - // `ABORTED`, and `UNAVAILABLE`. - // - // HTTP Mapping: 503 Service Unavailable - UNAVAILABLE = 14; - - // Unrecoverable data loss or corruption. - // - // HTTP Mapping: 500 Internal Server Error - DATA_LOSS = 15; -} diff --git a/proto/google/rpc/error_details.proto b/proto/google/rpc/error_details.proto deleted file mode 100644 index f24ae00..0000000 --- a/proto/google/rpc/error_details.proto +++ /dev/null @@ -1,200 +0,0 @@ -// Copyright 2017 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -syntax = "proto3"; - -package google.rpc; - -import "google/protobuf/duration.proto"; - -option go_package = "google.golang.org/genproto/googleapis/rpc/errdetails;errdetails"; -option java_multiple_files = true; -option java_outer_classname = "ErrorDetailsProto"; -option java_package = "com.google.rpc"; -option objc_class_prefix = "RPC"; - - -// Describes when the clients can retry a failed request. Clients could ignore -// the recommendation here or retry when this information is missing from error -// responses. -// -// It's always recommended that clients should use exponential backoff when -// retrying. -// -// Clients should wait until `retry_delay` amount of time has passed since -// receiving the error response before retrying. If retrying requests also -// fail, clients should use an exponential backoff scheme to gradually increase -// the delay between retries based on `retry_delay`, until either a maximum -// number of retires have been reached or a maximum retry delay cap has been -// reached. -message RetryInfo { - // Clients should wait at least this long between retrying the same request. - google.protobuf.Duration retry_delay = 1; -} - -// Describes additional debugging info. -message DebugInfo { - // The stack trace entries indicating where the error occurred. - repeated string stack_entries = 1; - - // Additional debugging information provided by the server. - string detail = 2; -} - -// Describes how a quota check failed. -// -// For example if a daily limit was exceeded for the calling project, -// a service could respond with a QuotaFailure detail containing the project -// id and the description of the quota limit that was exceeded. If the -// calling project hasn't enabled the service in the developer console, then -// a service could respond with the project id and set `service_disabled` -// to true. -// -// Also see RetryDetail and Help types for other details about handling a -// quota failure. -message QuotaFailure { - // A message type used to describe a single quota violation. For example, a - // daily quota or a custom quota that was exceeded. - message Violation { - // The subject on which the quota check failed. - // For example, "clientip:" or "project:". - string subject = 1; - - // A description of how the quota check failed. Clients can use this - // description to find more about the quota configuration in the service's - // public documentation, or find the relevant quota limit to adjust through - // developer console. - // - // For example: "Service disabled" or "Daily Limit for read operations - // exceeded". - string description = 2; - } - - // Describes all quota violations. - repeated Violation violations = 1; -} - -// Describes what preconditions have failed. -// -// For example, if an RPC failed because it required the Terms of Service to be -// acknowledged, it could list the terms of service violation in the -// PreconditionFailure message. -message PreconditionFailure { - // A message type used to describe a single precondition failure. - message Violation { - // The type of PreconditionFailure. We recommend using a service-specific - // enum type to define the supported precondition violation types. For - // example, "TOS" for "Terms of Service violation". - string type = 1; - - // The subject, relative to the type, that failed. - // For example, "google.com/cloud" relative to the "TOS" type would - // indicate which terms of service is being referenced. - string subject = 2; - - // A description of how the precondition failed. Developers can use this - // description to understand how to fix the failure. - // - // For example: "Terms of service not accepted". - string description = 3; - } - - // Describes all precondition violations. - repeated Violation violations = 1; -} - -// Describes violations in a client request. This error type focuses on the -// syntactic aspects of the request. -message BadRequest { - // A message type used to describe a single bad request field. - message FieldViolation { - // A path leading to a field in the request body. The value will be a - // sequence of dot-separated identifiers that identify a protocol buffer - // field. E.g., "field_violations.field" would identify this field. - string field = 1; - - // A description of why the request element is bad. - string description = 2; - } - - // Describes all violations in a client request. - repeated FieldViolation field_violations = 1; -} - -// Contains metadata about the request that clients can attach when filing a bug -// or providing other forms of feedback. -message RequestInfo { - // An opaque string that should only be interpreted by the service generating - // it. For example, it can be used to identify requests in the service's logs. - string request_id = 1; - - // Any data that was used to serve this request. For example, an encrypted - // stack trace that can be sent back to the service provider for debugging. - string serving_data = 2; -} - -// Describes the resource that is being accessed. -message ResourceInfo { - // A name for the type of resource being accessed, e.g. "sql table", - // "cloud storage bucket", "file", "Google calendar"; or the type URL - // of the resource: e.g. "type.googleapis.com/google.pubsub.v1.Topic". - string resource_type = 1; - - // The name of the resource being accessed. For example, a shared calendar - // name: "example.com_4fghdhgsrgh@group.calendar.google.com", if the current - // error is [google.rpc.Code.PERMISSION_DENIED][google.rpc.Code.PERMISSION_DENIED]. - string resource_name = 2; - - // The owner of the resource (optional). - // For example, "user:" or "project:". - string owner = 3; - - // Describes what error is encountered when accessing this resource. - // For example, updating a cloud project may require the `writer` permission - // on the developer console project. - string description = 4; -} - -// Provides links to documentation or for performing an out of band action. -// -// For example, if a quota check failed with an error indicating the calling -// project hasn't enabled the accessed service, this can contain a URL pointing -// directly to the right place in the developer console to flip the bit. -message Help { - // Describes a URL link. - message Link { - // Describes what the link offers. - string description = 1; - - // The URL of the link. - string url = 2; - } - - // URL(s) pointing to additional information on handling the current error. - repeated Link links = 1; -} - -// Provides a localized error message that is safe to return to the user -// which can be attached to an RPC error. -message LocalizedMessage { - // The locale used following the specification defined at - // http://www.rfc-editor.org/rfc/bcp/bcp47.txt. - // Examples are: "en-US", "fr-CH", "es-MX" - string locale = 1; - - // The localized error message in the above locale. - string message = 2; -} diff --git a/proto/google/rpc/status.proto b/proto/google/rpc/status.proto deleted file mode 100644 index 0839ee9..0000000 --- a/proto/google/rpc/status.proto +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright 2017 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -syntax = "proto3"; - -package google.rpc; - -import "google/protobuf/any.proto"; - -option go_package = "google.golang.org/genproto/googleapis/rpc/status;status"; -option java_multiple_files = true; -option java_outer_classname = "StatusProto"; -option java_package = "com.google.rpc"; -option objc_class_prefix = "RPC"; - - -// The `Status` type defines a logical error model that is suitable for different -// programming environments, including REST APIs and RPC APIs. It is used by -// [gRPC](https://github.com/grpc). The error model is designed to be: -// -// - Simple to use and understand for most users -// - Flexible enough to meet unexpected needs -// -// # Overview -// -// The `Status` message contains three pieces of data: error code, error message, -// and error details. The error code should be an enum value of -// [google.rpc.Code][google.rpc.Code], but it may accept additional error codes if needed. The -// error message should be a developer-facing English message that helps -// developers *understand* and *resolve* the error. If a localized user-facing -// error message is needed, put the localized message in the error details or -// localize it in the client. The optional error details may contain arbitrary -// information about the error. There is a predefined set of error detail types -// in the package `google.rpc` that can be used for common error conditions. -// -// # Language mapping -// -// The `Status` message is the logical representation of the error model, but it -// is not necessarily the actual wire format. When the `Status` message is -// exposed in different client libraries and different wire protocols, it can be -// mapped differently. For example, it will likely be mapped to some exceptions -// in Java, but more likely mapped to some error codes in C. -// -// # Other uses -// -// The error model and the `Status` message can be used in a variety of -// environments, either with or without APIs, to provide a -// consistent developer experience across different environments. -// -// Example uses of this error model include: -// -// - Partial errors. If a service needs to return partial errors to the client, -// it may embed the `Status` in the normal response to indicate the partial -// errors. -// -// - Workflow errors. A typical workflow has multiple steps. Each step may -// have a `Status` message for error reporting. -// -// - Batch operations. If a client uses batch request and batch response, the -// `Status` message should be used directly inside batch response, one for -// each error sub-response. -// -// - Asynchronous operations. If an API call embeds asynchronous operation -// results in its response, the status of those operations should be -// represented directly using the `Status` message. -// -// - Logging. If some API errors are stored in logs, the message `Status` could -// be used directly after any stripping needed for security/privacy reasons. -message Status { - // The status code, which should be an enum value of [google.rpc.Code][google.rpc.Code]. - int32 code = 1; - - // A developer-facing error message, which should be in English. Any - // user-facing error message should be localized and sent in the - // [google.rpc.Status.details][google.rpc.Status.details] field, or localized by the client. - string message = 2; - - // A list of messages that carry the error details. There is a common set of - // message types for APIs to use. - repeated google.protobuf.Any details = 3; -} diff --git a/proto/rove/rove.proto b/proto/rove/rove.proto deleted file mode 100644 index f1c6c3d..0000000 --- a/proto/rove/rove.proto +++ /dev/null @@ -1,153 +0,0 @@ -syntax = "proto3"; - -// Rove -// -// Rove is an asychronous nomadic game about exploring a planet as part of a loose community -package rove; - -option go_package = "github.com/mdiluz/rove/pkg/rove"; - -import "google/api/annotations.proto"; - -service Rove { - // Server status - // - // Responds with various details about the current server status - rpc Status(StatusRequest) returns (StatusResponse) { - option (google.api.http) = { - get: "/status" - }; - } - - // Register an account - // - // Tries to register an account with the given name - rpc Register(RegisterRequest) returns (RegisterResponse) { - option (google.api.http) = { - post: "/register" - body: "*" - }; - } - - // Send commands to rover - // - // Sending commands to this endpoint will queue them to be executed during the following ticks, in the order sent - rpc Commands(CommandsRequest) returns (CommandsResponse) { - option (google.api.http) = { - post: "/commands" - body: "*" - }; - } - - // Get radar information - // - // Gets the radar output for the given rover - rpc Radar(RadarRequest) returns (RadarResponse) { - option (google.api.http) = { - post: "/radar" - body: "*" - }; - } - - // Get rover information - // - // Gets information for the account's rover - rpc Rover(RoverRequest) returns (RoverResponse) { - option (google.api.http) = { - post: "/rover" - body: "*" - }; - } -} - -message Command { - // The command to execute - // "move" - Move the rover in a direction, requires bearing - // "stash" - Stashes item at current location in rover inventory - // "repair" - Repairs the rover using an inventory object - string command = 1; - - // The bearing, example: NE - string bearing = 2; -} - -message CommandsRequest { - // The account to execute these commands - string account = 1; - - // The set of desired commands - repeated Command commands = 2; -} - -// Empty placeholder -message CommandsResponse {} - -message Error { - // An explanation for the HTTP error returned - string error = 1; -} - -message RadarRequest { - // The account for this request - string account = 1; -} - -message RadarResponse { - // The range in tiles from the rover of the radar data - int32 range = 1; - - // A 1D array representing range*2 + 1 squared set of tiles, origin bottom left and in row->column order - bytes tiles = 2; -} - -// Empty placeholder -message RegisterResponse{} - -message RegisterRequest { - // The desired account name - string name = 1; -} - -message RoverRequest { - // The account for this request - string account = 1; -} - -message RoverResponse { - // The name of the rover - string name = 1; - - // Position of the rover in world coordinates - Vector position = 2; - - // The range of this rover's radar - int32 range = 3; - - // The items in the rover inventory - bytes inventory = 4; - - // The current health of the rover - int32 Integrity = 5; -} - -// Empty placeholder -message StatusRequest {} - -message StatusResponse { - // The time the next tick will occur - string next_tick = 1; - - // Whether the server is ready to accept requests - bool ready = 2; - - // The tick rate of the server in minutes (how many minutes per tick) - int32 tick = 3; - - // The version of the server in v{major}.{minor}-{delta}-{sha} form - string version = 4; -} - -message Vector { - int32 x = 1; - int32 y = 2; -} \ No newline at end of file diff --git a/proto/roveapi/roveapi.pb.go b/proto/roveapi/roveapi.pb.go new file mode 100644 index 0000000..81a6cb5 --- /dev/null +++ b/proto/roveapi/roveapi.pb.go @@ -0,0 +1,2202 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.25.0 +// protoc v3.6.1 +// source: roveapi/roveapi.proto + +// Rove +// +// Rove is an asychronous nomadic game about exploring a planet as part of a +// loose community + +package roveapi + +import ( + context "context" + proto "github.com/golang/protobuf/proto" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// This is a compile-time assertion that a sufficiently up-to-date version +// of the legacy proto package is being used. +const _ = proto.ProtoPackageIsVersion4 + +// CommandType defines the type of a command to give to the rover +type CommandType int32 + +const ( + CommandType_none CommandType = 0 + // Waits before performing the next command + CommandType_wait CommandType = 1 + // Toggles the sails, either catching the wind, or charging from the sun + CommandType_toggle CommandType = 2 + // Turns the rover in the specified bearing (requires bearing) + CommandType_turn CommandType = 3 + // Stashes item at current location in rover inventory + CommandType_stash CommandType = 4 + // Repairs the rover using an inventory object + CommandType_repair CommandType = 5 + // Broadcasts a message to nearby rovers (requires data) + CommandType_broadcast CommandType = 6 + // Salvages a neighboring dormant rover for parts + CommandType_salvage CommandType = 7 + // Transfers remote control into dormant rover + CommandType_transfer CommandType = 8 + // Upgrades a chosen rover specification using 5 rover parts + CommandType_upgrade CommandType = 9 +) + +// Enum value maps for CommandType. +var ( + CommandType_name = map[int32]string{ + 0: "none", + 1: "wait", + 2: "toggle", + 3: "turn", + 4: "stash", + 5: "repair", + 6: "broadcast", + 7: "salvage", + 8: "transfer", + 9: "upgrade", + } + CommandType_value = map[string]int32{ + "none": 0, + "wait": 1, + "toggle": 2, + "turn": 3, + "stash": 4, + "repair": 5, + "broadcast": 6, + "salvage": 7, + "transfer": 8, + "upgrade": 9, + } +) + +func (x CommandType) Enum() *CommandType { + p := new(CommandType) + *p = x + return p +} + +func (x CommandType) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (CommandType) Descriptor() protoreflect.EnumDescriptor { + return file_roveapi_roveapi_proto_enumTypes[0].Descriptor() +} + +func (CommandType) Type() protoreflect.EnumType { + return &file_roveapi_roveapi_proto_enumTypes[0] +} + +func (x CommandType) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use CommandType.Descriptor instead. +func (CommandType) EnumDescriptor() ([]byte, []int) { + return file_roveapi_roveapi_proto_rawDescGZIP(), []int{0} +} + +// Bearing represents a compass direction +type Bearing int32 + +const ( + // BearingUnknown an unknown invalid bearing + Bearing_BearingUnknown Bearing = 0 + Bearing_North Bearing = 1 + Bearing_NorthEast Bearing = 2 + Bearing_East Bearing = 3 + Bearing_SouthEast Bearing = 4 + Bearing_South Bearing = 5 + Bearing_SouthWest Bearing = 6 + Bearing_West Bearing = 7 + Bearing_NorthWest Bearing = 8 +) + +// Enum value maps for Bearing. +var ( + Bearing_name = map[int32]string{ + 0: "BearingUnknown", + 1: "North", + 2: "NorthEast", + 3: "East", + 4: "SouthEast", + 5: "South", + 6: "SouthWest", + 7: "West", + 8: "NorthWest", + } + Bearing_value = map[string]int32{ + "BearingUnknown": 0, + "North": 1, + "NorthEast": 2, + "East": 3, + "SouthEast": 4, + "South": 5, + "SouthWest": 6, + "West": 7, + "NorthWest": 8, + } +) + +func (x Bearing) Enum() *Bearing { + p := new(Bearing) + *p = x + return p +} + +func (x Bearing) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (Bearing) Descriptor() protoreflect.EnumDescriptor { + return file_roveapi_roveapi_proto_enumTypes[1].Descriptor() +} + +func (Bearing) Type() protoreflect.EnumType { + return &file_roveapi_roveapi_proto_enumTypes[1] +} + +func (x Bearing) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use Bearing.Descriptor instead. +func (Bearing) EnumDescriptor() ([]byte, []int) { + return file_roveapi_roveapi_proto_rawDescGZIP(), []int{1} +} + +// Describes the type of upgrade +type RoverUpgrade int32 + +const ( + RoverUpgrade_RoverUpgradeUnknown RoverUpgrade = 0 + RoverUpgrade_Range RoverUpgrade = 1 + RoverUpgrade_Capacity RoverUpgrade = 2 + RoverUpgrade_MaximumIntegrity RoverUpgrade = 3 + RoverUpgrade_MaximumCharge RoverUpgrade = 4 +) + +// Enum value maps for RoverUpgrade. +var ( + RoverUpgrade_name = map[int32]string{ + 0: "RoverUpgradeUnknown", + 1: "Range", + 2: "Capacity", + 3: "MaximumIntegrity", + 4: "MaximumCharge", + } + RoverUpgrade_value = map[string]int32{ + "RoverUpgradeUnknown": 0, + "Range": 1, + "Capacity": 2, + "MaximumIntegrity": 3, + "MaximumCharge": 4, + } +) + +func (x RoverUpgrade) Enum() *RoverUpgrade { + p := new(RoverUpgrade) + *p = x + return p +} + +func (x RoverUpgrade) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (RoverUpgrade) Descriptor() protoreflect.EnumDescriptor { + return file_roveapi_roveapi_proto_enumTypes[2].Descriptor() +} + +func (RoverUpgrade) Type() protoreflect.EnumType { + return &file_roveapi_roveapi_proto_enumTypes[2] +} + +func (x RoverUpgrade) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use RoverUpgrade.Descriptor instead. +func (RoverUpgrade) EnumDescriptor() ([]byte, []int) { + return file_roveapi_roveapi_proto_rawDescGZIP(), []int{2} +} + +// Types of objects +type Object int32 + +const ( + // ObjectUnknown represents no object at all + Object_ObjectUnknown Object = 0 + // RoverLive represents a live rover + Object_RoverLive Object = 1 + // RoverDormant describes a dormant rover + Object_RoverDormant Object = 2 + // RockSmall is a small stashable rock + Object_RockSmall Object = 3 + // RockLarge is a large blocking rock + Object_RockLarge Object = 4 + // RoverParts is one unit of rover parts, used for repairing and fixing the + // rover + Object_RoverParts Object = 5 +) + +// Enum value maps for Object. +var ( + Object_name = map[int32]string{ + 0: "ObjectUnknown", + 1: "RoverLive", + 2: "RoverDormant", + 3: "RockSmall", + 4: "RockLarge", + 5: "RoverParts", + } + Object_value = map[string]int32{ + "ObjectUnknown": 0, + "RoverLive": 1, + "RoverDormant": 2, + "RockSmall": 3, + "RockLarge": 4, + "RoverParts": 5, + } +) + +func (x Object) Enum() *Object { + p := new(Object) + *p = x + return p +} + +func (x Object) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (Object) Descriptor() protoreflect.EnumDescriptor { + return file_roveapi_roveapi_proto_enumTypes[3].Descriptor() +} + +func (Object) Type() protoreflect.EnumType { + return &file_roveapi_roveapi_proto_enumTypes[3] +} + +func (x Object) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use Object.Descriptor instead. +func (Object) EnumDescriptor() ([]byte, []int) { + return file_roveapi_roveapi_proto_rawDescGZIP(), []int{3} +} + +type Tile int32 + +const ( + // TileUnknown is a keyword for nothing + Tile_TileUnknown Tile = 0 + // Rock is solid rock ground + Tile_Rock Tile = 1 + // Gravel is loose rocks + Tile_Gravel Tile = 2 + // Sand is sand + Tile_Sand Tile = 3 +) + +// Enum value maps for Tile. +var ( + Tile_name = map[int32]string{ + 0: "TileUnknown", + 1: "Rock", + 2: "Gravel", + 3: "Sand", + } + Tile_value = map[string]int32{ + "TileUnknown": 0, + "Rock": 1, + "Gravel": 2, + "Sand": 3, + } +) + +func (x Tile) Enum() *Tile { + p := new(Tile) + *p = x + return p +} + +func (x Tile) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (Tile) Descriptor() protoreflect.EnumDescriptor { + return file_roveapi_roveapi_proto_enumTypes[4].Descriptor() +} + +func (Tile) Type() protoreflect.EnumType { + return &file_roveapi_roveapi_proto_enumTypes[4] +} + +func (x Tile) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use Tile.Descriptor instead. +func (Tile) EnumDescriptor() ([]byte, []int) { + return file_roveapi_roveapi_proto_rawDescGZIP(), []int{4} +} + +// SailPosition represents the position of the sola sail +type SailPosition int32 + +const ( + SailPosition_UnknownSailPosition SailPosition = 0 + // CatchingWind means the sail is catching the wind and moving the rover + SailPosition_CatchingWind SailPosition = 1 + // SolarCharging means the sail is facing the sun and charging + SailPosition_SolarCharging SailPosition = 2 +) + +// Enum value maps for SailPosition. +var ( + SailPosition_name = map[int32]string{ + 0: "UnknownSailPosition", + 1: "CatchingWind", + 2: "SolarCharging", + } + SailPosition_value = map[string]int32{ + "UnknownSailPosition": 0, + "CatchingWind": 1, + "SolarCharging": 2, + } +) + +func (x SailPosition) Enum() *SailPosition { + p := new(SailPosition) + *p = x + return p +} + +func (x SailPosition) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (SailPosition) Descriptor() protoreflect.EnumDescriptor { + return file_roveapi_roveapi_proto_enumTypes[5].Descriptor() +} + +func (SailPosition) Type() protoreflect.EnumType { + return &file_roveapi_roveapi_proto_enumTypes[5] +} + +func (x SailPosition) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use SailPosition.Descriptor instead. +func (SailPosition) EnumDescriptor() ([]byte, []int) { + return file_roveapi_roveapi_proto_rawDescGZIP(), []int{5} +} + +// ServerStatusRequest is an empty placeholder +type ServerStatusRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *ServerStatusRequest) Reset() { + *x = ServerStatusRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_roveapi_roveapi_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ServerStatusRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ServerStatusRequest) ProtoMessage() {} + +func (x *ServerStatusRequest) ProtoReflect() protoreflect.Message { + mi := &file_roveapi_roveapi_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ServerStatusRequest.ProtoReflect.Descriptor instead. +func (*ServerStatusRequest) Descriptor() ([]byte, []int) { + return file_roveapi_roveapi_proto_rawDescGZIP(), []int{0} +} + +// ServerStatusResponse is a response with useful server information +type ServerStatusResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The version of the server in v{major}.{minor}-{delta}-{sha} form + Version string `protobuf:"bytes,1,opt,name=version,proto3" json:"version,omitempty"` + // Whether the server is ready to accept requests + Ready bool `protobuf:"varint,2,opt,name=ready,proto3" json:"ready,omitempty"` + // The tick rate of the server in minutes (how many minutes per tick) + TickRate int32 `protobuf:"varint,3,opt,name=tickRate,proto3" json:"tickRate,omitempty"` + // The current tick of the server + CurrentTick int32 `protobuf:"varint,4,opt,name=currentTick,proto3" json:"currentTick,omitempty"` + // The time the next tick will occur + NextTick string `protobuf:"bytes,5,opt,name=next_tick,json=nextTick,proto3" json:"next_tick,omitempty"` +} + +func (x *ServerStatusResponse) Reset() { + *x = ServerStatusResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_roveapi_roveapi_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ServerStatusResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ServerStatusResponse) ProtoMessage() {} + +func (x *ServerStatusResponse) ProtoReflect() protoreflect.Message { + mi := &file_roveapi_roveapi_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ServerStatusResponse.ProtoReflect.Descriptor instead. +func (*ServerStatusResponse) Descriptor() ([]byte, []int) { + return file_roveapi_roveapi_proto_rawDescGZIP(), []int{1} +} + +func (x *ServerStatusResponse) GetVersion() string { + if x != nil { + return x.Version + } + return "" +} + +func (x *ServerStatusResponse) GetReady() bool { + if x != nil { + return x.Ready + } + return false +} + +func (x *ServerStatusResponse) GetTickRate() int32 { + if x != nil { + return x.TickRate + } + return 0 +} + +func (x *ServerStatusResponse) GetCurrentTick() int32 { + if x != nil { + return x.CurrentTick + } + return 0 +} + +func (x *ServerStatusResponse) GetNextTick() string { + if x != nil { + return x.NextTick + } + return "" +} + +// RegisterRequest contains data to register an account +type RegisterRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The desired account name + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` +} + +func (x *RegisterRequest) Reset() { + *x = RegisterRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_roveapi_roveapi_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RegisterRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RegisterRequest) ProtoMessage() {} + +func (x *RegisterRequest) ProtoReflect() protoreflect.Message { + mi := &file_roveapi_roveapi_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RegisterRequest.ProtoReflect.Descriptor instead. +func (*RegisterRequest) Descriptor() ([]byte, []int) { + return file_roveapi_roveapi_proto_rawDescGZIP(), []int{2} +} + +func (x *RegisterRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +// Account describes a registered account +type Account struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The account name + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + // The account secret value, given when creating the account + Secret string `protobuf:"bytes,2,opt,name=secret,proto3" json:"secret,omitempty"` +} + +func (x *Account) Reset() { + *x = Account{} + if protoimpl.UnsafeEnabled { + mi := &file_roveapi_roveapi_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Account) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Account) ProtoMessage() {} + +func (x *Account) ProtoReflect() protoreflect.Message { + mi := &file_roveapi_roveapi_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Account.ProtoReflect.Descriptor instead. +func (*Account) Descriptor() ([]byte, []int) { + return file_roveapi_roveapi_proto_rawDescGZIP(), []int{3} +} + +func (x *Account) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Account) GetSecret() string { + if x != nil { + return x.Secret + } + return "" +} + +// RegisterResponse is the response given to registering an account +type RegisterResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The registered account information + Account *Account `protobuf:"bytes,1,opt,name=account,proto3" json:"account,omitempty"` +} + +func (x *RegisterResponse) Reset() { + *x = RegisterResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_roveapi_roveapi_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RegisterResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RegisterResponse) ProtoMessage() {} + +func (x *RegisterResponse) ProtoReflect() protoreflect.Message { + mi := &file_roveapi_roveapi_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RegisterResponse.ProtoReflect.Descriptor instead. +func (*RegisterResponse) Descriptor() ([]byte, []int) { + return file_roveapi_roveapi_proto_rawDescGZIP(), []int{4} +} + +func (x *RegisterResponse) GetAccount() *Account { + if x != nil { + return x.Account + } + return nil +} + +// Command is a single command for a rover +type Command struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The command type + Command CommandType `protobuf:"varint,1,opt,name=command,proto3,enum=roveapi.CommandType" json:"command,omitempty"` + // The number of times to repeat the command after the first + Repeat int32 `protobuf:"varint,2,opt,name=repeat,proto3" json:"repeat,omitempty"` + // broadcast - a simple message, must be composed of up to 3 printable ASCII + // glyphs (32-126) + Data []byte `protobuf:"bytes,3,opt,name=data,proto3" json:"data,omitempty"` + // move - the bearing for the rover to turn to + Bearing Bearing `protobuf:"varint,4,opt,name=bearing,proto3,enum=roveapi.Bearing" json:"bearing,omitempty"` + // upgrade - the upgrade to apply to the rover + Upgrade RoverUpgrade `protobuf:"varint,5,opt,name=upgrade,proto3,enum=roveapi.RoverUpgrade" json:"upgrade,omitempty"` +} + +func (x *Command) Reset() { + *x = Command{} + if protoimpl.UnsafeEnabled { + mi := &file_roveapi_roveapi_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Command) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Command) ProtoMessage() {} + +func (x *Command) ProtoReflect() protoreflect.Message { + mi := &file_roveapi_roveapi_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Command.ProtoReflect.Descriptor instead. +func (*Command) Descriptor() ([]byte, []int) { + return file_roveapi_roveapi_proto_rawDescGZIP(), []int{5} +} + +func (x *Command) GetCommand() CommandType { + if x != nil { + return x.Command + } + return CommandType_none +} + +func (x *Command) GetRepeat() int32 { + if x != nil { + return x.Repeat + } + return 0 +} + +func (x *Command) GetData() []byte { + if x != nil { + return x.Data + } + return nil +} + +func (x *Command) GetBearing() Bearing { + if x != nil { + return x.Bearing + } + 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 +type CommandRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The account to execute these commands + Account *Account `protobuf:"bytes,1,opt,name=account,proto3" json:"account,omitempty"` + // The set of desired commands + Commands []*Command `protobuf:"bytes,2,rep,name=commands,proto3" json:"commands,omitempty"` +} + +func (x *CommandRequest) Reset() { + *x = CommandRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_roveapi_roveapi_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CommandRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CommandRequest) ProtoMessage() {} + +func (x *CommandRequest) ProtoReflect() protoreflect.Message { + mi := &file_roveapi_roveapi_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CommandRequest.ProtoReflect.Descriptor instead. +func (*CommandRequest) Descriptor() ([]byte, []int) { + return file_roveapi_roveapi_proto_rawDescGZIP(), []int{6} +} + +func (x *CommandRequest) GetAccount() *Account { + if x != nil { + return x.Account + } + return nil +} + +func (x *CommandRequest) GetCommands() []*Command { + if x != nil { + return x.Commands + } + return nil +} + +// CommandResponse is an empty placeholder +type CommandResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *CommandResponse) Reset() { + *x = CommandResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_roveapi_roveapi_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CommandResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CommandResponse) ProtoMessage() {} + +func (x *CommandResponse) ProtoReflect() protoreflect.Message { + mi := &file_roveapi_roveapi_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CommandResponse.ProtoReflect.Descriptor instead. +func (*CommandResponse) Descriptor() ([]byte, []int) { + return file_roveapi_roveapi_proto_rawDescGZIP(), []int{7} +} + +// RadarRequest is the data needed to request the radar for a rover +type RadarRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The account for this request + Account *Account `protobuf:"bytes,1,opt,name=account,proto3" json:"account,omitempty"` +} + +func (x *RadarRequest) Reset() { + *x = RadarRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_roveapi_roveapi_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RadarRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RadarRequest) ProtoMessage() {} + +func (x *RadarRequest) ProtoReflect() protoreflect.Message { + mi := &file_roveapi_roveapi_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RadarRequest.ProtoReflect.Descriptor instead. +func (*RadarRequest) Descriptor() ([]byte, []int) { + return file_roveapi_roveapi_proto_rawDescGZIP(), []int{8} +} + +func (x *RadarRequest) GetAccount() *Account { + if x != nil { + return x.Account + } + return nil +} + +// RadarResponse describes radar information +type RadarResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The range in tiles from the rover of the radar data + Range int32 `protobuf:"varint,1,opt,name=range,proto3" json:"range,omitempty"` + // A 1D array representing range*2 + 1 squared set of tiles, origin bottom + // left and in row->column order + Tiles []Tile `protobuf:"varint,2,rep,packed,name=tiles,proto3,enum=roveapi.Tile" json:"tiles,omitempty"` + // A similar array to the tile array, but containing objects + Objects []Object `protobuf:"varint,3,rep,packed,name=objects,proto3,enum=roveapi.Object" json:"objects,omitempty"` +} + +func (x *RadarResponse) Reset() { + *x = RadarResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_roveapi_roveapi_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RadarResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RadarResponse) ProtoMessage() {} + +func (x *RadarResponse) ProtoReflect() protoreflect.Message { + mi := &file_roveapi_roveapi_proto_msgTypes[9] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RadarResponse.ProtoReflect.Descriptor instead. +func (*RadarResponse) Descriptor() ([]byte, []int) { + return file_roveapi_roveapi_proto_rawDescGZIP(), []int{9} +} + +func (x *RadarResponse) GetRange() int32 { + if x != nil { + return x.Range + } + return 0 +} + +func (x *RadarResponse) GetTiles() []Tile { + if x != nil { + return x.Tiles + } + return nil +} + +func (x *RadarResponse) GetObjects() []Object { + if x != nil { + return x.Objects + } + return nil +} + +// StatusRequest is information needed to request rover status +type StatusRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The account for this request + Account *Account `protobuf:"bytes,1,opt,name=account,proto3" json:"account,omitempty"` +} + +func (x *StatusRequest) Reset() { + *x = StatusRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_roveapi_roveapi_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StatusRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StatusRequest) ProtoMessage() {} + +func (x *StatusRequest) ProtoReflect() protoreflect.Message { + mi := &file_roveapi_roveapi_proto_msgTypes[10] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StatusRequest.ProtoReflect.Descriptor instead. +func (*StatusRequest) Descriptor() ([]byte, []int) { + return file_roveapi_roveapi_proto_rawDescGZIP(), []int{10} +} + +func (x *StatusRequest) GetAccount() *Account { + if x != nil { + return x.Account + } + return nil +} + +// Log is a single log item +type Log struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The unix timestamp of the log + Time string `protobuf:"bytes,1,opt,name=time,proto3" json:"time,omitempty"` + // The text of the log + Text string `protobuf:"bytes,2,opt,name=text,proto3" json:"text,omitempty"` +} + +func (x *Log) Reset() { + *x = Log{} + if protoimpl.UnsafeEnabled { + mi := &file_roveapi_roveapi_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Log) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Log) ProtoMessage() {} + +func (x *Log) ProtoReflect() protoreflect.Message { + mi := &file_roveapi_roveapi_proto_msgTypes[11] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Log.ProtoReflect.Descriptor instead. +func (*Log) Descriptor() ([]byte, []int) { + return file_roveapi_roveapi_proto_rawDescGZIP(), []int{11} +} + +func (x *Log) GetTime() string { + if x != nil { + return x.Time + } + return "" +} + +func (x *Log) GetText() string { + if x != nil { + return x.Text + } + return "" +} + +// Vector describes a point or vector in 2D space +type Vector struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + X int32 `protobuf:"varint,1,opt,name=x,proto3" json:"x,omitempty"` + Y int32 `protobuf:"varint,2,opt,name=y,proto3" json:"y,omitempty"` +} + +func (x *Vector) Reset() { + *x = Vector{} + if protoimpl.UnsafeEnabled { + mi := &file_roveapi_roveapi_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Vector) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Vector) ProtoMessage() {} + +func (x *Vector) ProtoReflect() protoreflect.Message { + mi := &file_roveapi_roveapi_proto_msgTypes[12] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Vector.ProtoReflect.Descriptor instead. +func (*Vector) Descriptor() ([]byte, []int) { + return file_roveapi_roveapi_proto_rawDescGZIP(), []int{12} +} + +func (x *Vector) GetX() int32 { + if x != nil { + return x.X + } + return 0 +} + +func (x *Vector) GetY() int32 { + if x != nil { + return x.Y + } + return 0 +} + +type RoverSpecifications struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The name of the rover + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + // The range of this rover's radar and broadcasting + Range int32 `protobuf:"varint,2,opt,name=range,proto3" json:"range,omitempty"` + // The capacity of the inventory + Capacity int32 `protobuf:"varint,3,opt,name=capacity,proto3" json:"capacity,omitempty"` + // The maximum health of the rover + MaximumIntegrity int32 `protobuf:"varint,4,opt,name=maximumIntegrity,proto3" json:"maximumIntegrity,omitempty"` + // The max energy the rover can store + MaximumCharge int32 `protobuf:"varint,5,opt,name=maximumCharge,proto3" json:"maximumCharge,omitempty"` +} + +func (x *RoverSpecifications) Reset() { + *x = RoverSpecifications{} + if protoimpl.UnsafeEnabled { + mi := &file_roveapi_roveapi_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RoverSpecifications) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RoverSpecifications) ProtoMessage() {} + +func (x *RoverSpecifications) ProtoReflect() protoreflect.Message { + mi := &file_roveapi_roveapi_proto_msgTypes[13] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RoverSpecifications.ProtoReflect.Descriptor instead. +func (*RoverSpecifications) Descriptor() ([]byte, []int) { + return file_roveapi_roveapi_proto_rawDescGZIP(), []int{13} +} + +func (x *RoverSpecifications) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *RoverSpecifications) GetRange() int32 { + if x != nil { + return x.Range + } + return 0 +} + +func (x *RoverSpecifications) GetCapacity() int32 { + if x != nil { + return x.Capacity + } + return 0 +} + +func (x *RoverSpecifications) GetMaximumIntegrity() int32 { + if x != nil { + return x.MaximumIntegrity + } + return 0 +} + +func (x *RoverSpecifications) GetMaximumCharge() int32 { + if x != nil { + return x.MaximumCharge + } + return 0 +} + +type RoverStatus struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The current direction of the rover + Bearing Bearing `protobuf:"varint,1,opt,name=bearing,proto3,enum=roveapi.Bearing" json:"bearing,omitempty"` + // The current position of the sails + SailPosition SailPosition `protobuf:"varint,2,opt,name=sailPosition,proto3,enum=roveapi.SailPosition" json:"sailPosition,omitempty"` + // The items in the rover inventory + Inventory []byte `protobuf:"bytes,3,opt,name=inventory,proto3" json:"inventory,omitempty"` + // The current health of the rover + Integrity int32 `protobuf:"varint,4,opt,name=integrity,proto3" json:"integrity,omitempty"` + // The energy stored in the rover + Charge int32 `protobuf:"varint,5,opt,name=charge,proto3" json:"charge,omitempty"` + // The set of currently queued commands + QueuedCommands []*Command `protobuf:"bytes,6,rep,name=queuedCommands,proto3" json:"queuedCommands,omitempty"` +} + +func (x *RoverStatus) Reset() { + *x = RoverStatus{} + if protoimpl.UnsafeEnabled { + mi := &file_roveapi_roveapi_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RoverStatus) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RoverStatus) ProtoMessage() {} + +func (x *RoverStatus) ProtoReflect() protoreflect.Message { + mi := &file_roveapi_roveapi_proto_msgTypes[14] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RoverStatus.ProtoReflect.Descriptor instead. +func (*RoverStatus) Descriptor() ([]byte, []int) { + return file_roveapi_roveapi_proto_rawDescGZIP(), []int{14} +} + +func (x *RoverStatus) GetBearing() Bearing { + if x != nil { + return x.Bearing + } + return Bearing_BearingUnknown +} + +func (x *RoverStatus) GetSailPosition() SailPosition { + if x != nil { + return x.SailPosition + } + return SailPosition_UnknownSailPosition +} + +func (x *RoverStatus) GetInventory() []byte { + if x != nil { + return x.Inventory + } + return nil +} + +func (x *RoverStatus) GetIntegrity() int32 { + if x != nil { + return x.Integrity + } + return 0 +} + +func (x *RoverStatus) GetCharge() int32 { + if x != nil { + return x.Charge + } + return 0 +} + +func (x *RoverStatus) GetQueuedCommands() []*Command { + if x != nil { + return x.QueuedCommands + } + return nil +} + +type RoverReadings struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Position of the rover in world coordinates + Position *Vector `protobuf:"bytes,1,opt,name=position,proto3" json:"position,omitempty"` + // The current wind direction + Wind Bearing `protobuf:"varint,2,opt,name=wind,proto3,enum=roveapi.Bearing" json:"wind,omitempty"` + // The most recent logs + Logs []*Log `protobuf:"bytes,3,rep,name=logs,proto3" json:"logs,omitempty"` +} + +func (x *RoverReadings) Reset() { + *x = RoverReadings{} + if protoimpl.UnsafeEnabled { + mi := &file_roveapi_roveapi_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RoverReadings) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RoverReadings) ProtoMessage() {} + +func (x *RoverReadings) ProtoReflect() protoreflect.Message { + mi := &file_roveapi_roveapi_proto_msgTypes[15] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RoverReadings.ProtoReflect.Descriptor instead. +func (*RoverReadings) Descriptor() ([]byte, []int) { + return file_roveapi_roveapi_proto_rawDescGZIP(), []int{15} +} + +func (x *RoverReadings) GetPosition() *Vector { + if x != nil { + return x.Position + } + return nil +} + +func (x *RoverReadings) GetWind() Bearing { + if x != nil { + return x.Wind + } + return Bearing_BearingUnknown +} + +func (x *RoverReadings) GetLogs() []*Log { + if x != nil { + return x.Logs + } + return nil +} + +// StatusResponse is the response given to a status request +type StatusResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The static rover information + Spec *RoverSpecifications `protobuf:"bytes,1,opt,name=spec,proto3" json:"spec,omitempty"` + // Current rover status + Status *RoverStatus `protobuf:"bytes,2,opt,name=status,proto3" json:"status,omitempty"` + // Current rover readings + Readings *RoverReadings `protobuf:"bytes,3,opt,name=readings,proto3" json:"readings,omitempty"` +} + +func (x *StatusResponse) Reset() { + *x = StatusResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_roveapi_roveapi_proto_msgTypes[16] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StatusResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StatusResponse) ProtoMessage() {} + +func (x *StatusResponse) ProtoReflect() protoreflect.Message { + mi := &file_roveapi_roveapi_proto_msgTypes[16] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StatusResponse.ProtoReflect.Descriptor instead. +func (*StatusResponse) Descriptor() ([]byte, []int) { + return file_roveapi_roveapi_proto_rawDescGZIP(), []int{16} +} + +func (x *StatusResponse) GetSpec() *RoverSpecifications { + if x != nil { + return x.Spec + } + return nil +} + +func (x *StatusResponse) GetStatus() *RoverStatus { + if x != nil { + return x.Status + } + return nil +} + +func (x *StatusResponse) GetReadings() *RoverReadings { + if x != nil { + return x.Readings + } + return nil +} + +var File_roveapi_roveapi_proto protoreflect.FileDescriptor + +var file_roveapi_roveapi_proto_rawDesc = []byte{ + 0x0a, 0x15, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2f, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, + 0x69, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x07, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, + 0x22, 0x15, 0x0a, 0x13, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xa1, 0x01, 0x0a, 0x14, 0x53, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x65, + 0x61, 0x64, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x72, 0x65, 0x61, 0x64, 0x79, + 0x12, 0x1a, 0x0a, 0x08, 0x74, 0x69, 0x63, 0x6b, 0x52, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x08, 0x74, 0x69, 0x63, 0x6b, 0x52, 0x61, 0x74, 0x65, 0x12, 0x20, 0x0a, 0x0b, + 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x0b, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x12, 0x1b, + 0x0a, 0x09, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x74, 0x69, 0x63, 0x6b, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x08, 0x6e, 0x65, 0x78, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x22, 0x25, 0x0a, 0x0f, 0x52, + 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, + 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x22, 0x35, 0x0a, 0x07, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x12, 0x0a, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x22, 0x3e, 0x0a, 0x10, 0x52, 0x65, 0x67, + 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, + 0x07, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, + 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x52, 0x07, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0xc2, 0x01, 0x0a, 0x07, 0x43, 0x6f, + 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x2e, 0x0a, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x14, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, + 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x54, 0x79, 0x70, 0x65, 0x52, 0x07, 0x63, 0x6f, + 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x12, 0x12, 0x0a, + 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, + 0x61, 0x12, 0x2a, 0x0a, 0x07, 0x62, 0x65, 0x61, 0x72, 0x69, 0x6e, 0x67, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x0e, 0x32, 0x10, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x42, 0x65, 0x61, + 0x72, 0x69, 0x6e, 0x67, 0x52, 0x07, 0x62, 0x65, 0x61, 0x72, 0x69, 0x6e, 0x67, 0x12, 0x2f, 0x0a, + 0x07, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, + 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x52, 0x6f, 0x76, 0x65, 0x72, 0x55, 0x70, + 0x67, 0x72, 0x61, 0x64, 0x65, 0x52, 0x07, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x22, 0x6a, + 0x0a, 0x0e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x2a, 0x0a, 0x07, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x10, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x41, 0x63, 0x63, 0x6f, + 0x75, 0x6e, 0x74, 0x52, 0x07, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2c, 0x0a, 0x08, + 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, + 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, + 0x52, 0x08, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x22, 0x11, 0x0a, 0x0f, 0x43, 0x6f, + 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3a, 0x0a, + 0x0c, 0x52, 0x61, 0x64, 0x61, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2a, 0x0a, + 0x07, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, + 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x52, 0x07, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x75, 0x0a, 0x0d, 0x52, 0x61, 0x64, + 0x61, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x61, + 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x72, 0x61, 0x6e, 0x67, 0x65, + 0x12, 0x23, 0x0a, 0x05, 0x74, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0e, 0x32, + 0x0d, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x54, 0x69, 0x6c, 0x65, 0x52, 0x05, + 0x74, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x29, 0x0a, 0x07, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, + 0x18, 0x03, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x0f, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, + 0x2e, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x07, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, + 0x22, 0x3b, 0x0a, 0x0d, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x2a, 0x0a, 0x07, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x41, 0x63, 0x63, + 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x07, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x2d, 0x0a, + 0x03, 0x4c, 0x6f, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, 0x78, 0x74, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x65, 0x78, 0x74, 0x22, 0x24, 0x0a, 0x06, + 0x56, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x0c, 0x0a, 0x01, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x01, 0x78, 0x12, 0x0c, 0x0a, 0x01, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, + 0x01, 0x79, 0x22, 0xad, 0x01, 0x0a, 0x13, 0x52, 0x6f, 0x76, 0x65, 0x72, 0x53, 0x70, 0x65, 0x63, + 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, + 0x0a, 0x05, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x72, + 0x61, 0x6e, 0x67, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, + 0x12, 0x2a, 0x0a, 0x10, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x49, 0x6e, 0x74, 0x65, 0x67, + 0x72, 0x69, 0x74, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x10, 0x6d, 0x61, 0x78, 0x69, + 0x6d, 0x75, 0x6d, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x69, 0x74, 0x79, 0x12, 0x24, 0x0a, 0x0d, + 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x43, 0x68, 0x61, 0x72, 0x67, 0x65, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x0d, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x43, 0x68, 0x61, 0x72, + 0x67, 0x65, 0x22, 0x82, 0x02, 0x0a, 0x0b, 0x52, 0x6f, 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x12, 0x2a, 0x0a, 0x07, 0x62, 0x65, 0x61, 0x72, 0x69, 0x6e, 0x67, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0e, 0x32, 0x10, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x42, 0x65, + 0x61, 0x72, 0x69, 0x6e, 0x67, 0x52, 0x07, 0x62, 0x65, 0x61, 0x72, 0x69, 0x6e, 0x67, 0x12, 0x39, + 0x0a, 0x0c, 0x73, 0x61, 0x69, 0x6c, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x53, + 0x61, 0x69, 0x6c, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0c, 0x73, 0x61, 0x69, + 0x6c, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x69, 0x6e, 0x76, + 0x65, 0x6e, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x69, 0x6e, + 0x76, 0x65, 0x6e, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x1c, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x65, 0x67, + 0x72, 0x69, 0x74, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x69, 0x6e, 0x74, 0x65, + 0x67, 0x72, 0x69, 0x74, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x63, 0x68, 0x61, 0x72, 0x67, 0x65, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x63, 0x68, 0x61, 0x72, 0x67, 0x65, 0x12, 0x38, 0x0a, + 0x0e, 0x71, 0x75, 0x65, 0x75, 0x65, 0x64, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x18, + 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, + 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x52, 0x0e, 0x71, 0x75, 0x65, 0x75, 0x65, 0x64, 0x43, + 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x22, 0x84, 0x01, 0x0a, 0x0d, 0x52, 0x6f, 0x76, 0x65, + 0x72, 0x52, 0x65, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x2b, 0x0a, 0x08, 0x70, 0x6f, 0x73, + 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x72, 0x6f, + 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x56, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x08, 0x70, 0x6f, + 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x24, 0x0a, 0x04, 0x77, 0x69, 0x6e, 0x64, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0e, 0x32, 0x10, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x42, + 0x65, 0x61, 0x72, 0x69, 0x6e, 0x67, 0x52, 0x04, 0x77, 0x69, 0x6e, 0x64, 0x12, 0x20, 0x0a, 0x04, + 0x6c, 0x6f, 0x67, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x72, 0x6f, 0x76, + 0x65, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x6f, 0x67, 0x52, 0x04, 0x6c, 0x6f, 0x67, 0x73, 0x22, 0xa4, + 0x01, 0x0a, 0x0e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x30, 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1c, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x52, 0x6f, 0x76, 0x65, 0x72, 0x53, + 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x04, 0x73, + 0x70, 0x65, 0x63, 0x12, 0x2c, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x52, 0x6f, + 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x12, 0x32, 0x0a, 0x08, 0x72, 0x65, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x52, 0x6f, + 0x76, 0x65, 0x72, 0x52, 0x65, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x08, 0x72, 0x65, 0x61, + 0x64, 0x69, 0x6e, 0x67, 0x73, 0x2a, 0x85, 0x01, 0x0a, 0x0b, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, + 0x64, 0x54, 0x79, 0x70, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x6e, 0x6f, 0x6e, 0x65, 0x10, 0x00, 0x12, + 0x08, 0x0a, 0x04, 0x77, 0x61, 0x69, 0x74, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x74, 0x6f, 0x67, + 0x67, 0x6c, 0x65, 0x10, 0x02, 0x12, 0x08, 0x0a, 0x04, 0x74, 0x75, 0x72, 0x6e, 0x10, 0x03, 0x12, + 0x09, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x73, 0x68, 0x10, 0x04, 0x12, 0x0a, 0x0a, 0x06, 0x72, 0x65, + 0x70, 0x61, 0x69, 0x72, 0x10, 0x05, 0x12, 0x0d, 0x0a, 0x09, 0x62, 0x72, 0x6f, 0x61, 0x64, 0x63, + 0x61, 0x73, 0x74, 0x10, 0x06, 0x12, 0x0b, 0x0a, 0x07, 0x73, 0x61, 0x6c, 0x76, 0x61, 0x67, 0x65, + 0x10, 0x07, 0x12, 0x0c, 0x0a, 0x08, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x10, 0x08, + 0x12, 0x0b, 0x0a, 0x07, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x10, 0x09, 0x2a, 0x83, 0x01, + 0x0a, 0x07, 0x42, 0x65, 0x61, 0x72, 0x69, 0x6e, 0x67, 0x12, 0x12, 0x0a, 0x0e, 0x42, 0x65, 0x61, + 0x72, 0x69, 0x6e, 0x67, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x10, 0x00, 0x12, 0x09, 0x0a, + 0x05, 0x4e, 0x6f, 0x72, 0x74, 0x68, 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x6f, 0x72, 0x74, + 0x68, 0x45, 0x61, 0x73, 0x74, 0x10, 0x02, 0x12, 0x08, 0x0a, 0x04, 0x45, 0x61, 0x73, 0x74, 0x10, + 0x03, 0x12, 0x0d, 0x0a, 0x09, 0x53, 0x6f, 0x75, 0x74, 0x68, 0x45, 0x61, 0x73, 0x74, 0x10, 0x04, + 0x12, 0x09, 0x0a, 0x05, 0x53, 0x6f, 0x75, 0x74, 0x68, 0x10, 0x05, 0x12, 0x0d, 0x0a, 0x09, 0x53, + 0x6f, 0x75, 0x74, 0x68, 0x57, 0x65, 0x73, 0x74, 0x10, 0x06, 0x12, 0x08, 0x0a, 0x04, 0x57, 0x65, + 0x73, 0x74, 0x10, 0x07, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x6f, 0x72, 0x74, 0x68, 0x57, 0x65, 0x73, + 0x74, 0x10, 0x08, 0x2a, 0x69, 0x0a, 0x0c, 0x52, 0x6f, 0x76, 0x65, 0x72, 0x55, 0x70, 0x67, 0x72, + 0x61, 0x64, 0x65, 0x12, 0x17, 0x0a, 0x13, 0x52, 0x6f, 0x76, 0x65, 0x72, 0x55, 0x70, 0x67, 0x72, + 0x61, 0x64, 0x65, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, + 0x52, 0x61, 0x6e, 0x67, 0x65, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x43, 0x61, 0x70, 0x61, 0x63, + 0x69, 0x74, 0x79, 0x10, 0x02, 0x12, 0x14, 0x0a, 0x10, 0x4d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, + 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x69, 0x74, 0x79, 0x10, 0x03, 0x12, 0x11, 0x0a, 0x0d, 0x4d, + 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x43, 0x68, 0x61, 0x72, 0x67, 0x65, 0x10, 0x04, 0x2a, 0x6a, + 0x0a, 0x06, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x11, 0x0a, 0x0d, 0x4f, 0x62, 0x6a, 0x65, + 0x63, 0x74, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x52, + 0x6f, 0x76, 0x65, 0x72, 0x4c, 0x69, 0x76, 0x65, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x52, 0x6f, + 0x76, 0x65, 0x72, 0x44, 0x6f, 0x72, 0x6d, 0x61, 0x6e, 0x74, 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09, + 0x52, 0x6f, 0x63, 0x6b, 0x53, 0x6d, 0x61, 0x6c, 0x6c, 0x10, 0x03, 0x12, 0x0d, 0x0a, 0x09, 0x52, + 0x6f, 0x63, 0x6b, 0x4c, 0x61, 0x72, 0x67, 0x65, 0x10, 0x04, 0x12, 0x0e, 0x0a, 0x0a, 0x52, 0x6f, + 0x76, 0x65, 0x72, 0x50, 0x61, 0x72, 0x74, 0x73, 0x10, 0x05, 0x2a, 0x37, 0x0a, 0x04, 0x54, 0x69, + 0x6c, 0x65, 0x12, 0x0f, 0x0a, 0x0b, 0x54, 0x69, 0x6c, 0x65, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, + 0x6e, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x52, 0x6f, 0x63, 0x6b, 0x10, 0x01, 0x12, 0x0a, 0x0a, + 0x06, 0x47, 0x72, 0x61, 0x76, 0x65, 0x6c, 0x10, 0x02, 0x12, 0x08, 0x0a, 0x04, 0x53, 0x61, 0x6e, + 0x64, 0x10, 0x03, 0x2a, 0x4c, 0x0a, 0x0c, 0x53, 0x61, 0x69, 0x6c, 0x50, 0x6f, 0x73, 0x69, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x17, 0x0a, 0x13, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x53, 0x61, + 0x69, 0x6c, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x10, 0x00, 0x12, 0x10, 0x0a, 0x0c, + 0x43, 0x61, 0x74, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x57, 0x69, 0x6e, 0x64, 0x10, 0x01, 0x12, 0x11, + 0x0a, 0x0d, 0x53, 0x6f, 0x6c, 0x61, 0x72, 0x43, 0x68, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x67, 0x10, + 0x02, 0x32, 0xcf, 0x02, 0x0a, 0x04, 0x52, 0x6f, 0x76, 0x65, 0x12, 0x4d, 0x0a, 0x0c, 0x53, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1c, 0x2e, 0x72, 0x6f, 0x76, + 0x65, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, + 0x70, 0x69, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x41, 0x0a, 0x08, 0x52, 0x65, 0x67, + 0x69, 0x73, 0x74, 0x65, 0x72, 0x12, 0x18, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, + 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x19, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, + 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3e, 0x0a, 0x07, + 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x17, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, + 0x69, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x18, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, + 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x38, 0x0a, 0x05, + 0x52, 0x61, 0x64, 0x61, 0x72, 0x12, 0x15, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, + 0x52, 0x61, 0x64, 0x61, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x72, + 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x52, 0x61, 0x64, 0x61, 0x72, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3b, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x12, 0x16, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, + 0x70, 0x69, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x00, 0x42, 0x26, 0x5a, 0x24, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x6d, 0x64, 0x69, 0x6c, 0x75, 0x7a, 0x2f, 0x72, 0x6f, 0x76, 0x65, 0x2f, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x2f, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x62, 0x06, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x33, +} + +var ( + file_roveapi_roveapi_proto_rawDescOnce sync.Once + file_roveapi_roveapi_proto_rawDescData = file_roveapi_roveapi_proto_rawDesc +) + +func file_roveapi_roveapi_proto_rawDescGZIP() []byte { + file_roveapi_roveapi_proto_rawDescOnce.Do(func() { + file_roveapi_roveapi_proto_rawDescData = protoimpl.X.CompressGZIP(file_roveapi_roveapi_proto_rawDescData) + }) + return file_roveapi_roveapi_proto_rawDescData +} + +var file_roveapi_roveapi_proto_enumTypes = make([]protoimpl.EnumInfo, 6) +var file_roveapi_roveapi_proto_msgTypes = make([]protoimpl.MessageInfo, 17) +var file_roveapi_roveapi_proto_goTypes = []interface{}{ + (CommandType)(0), // 0: roveapi.CommandType + (Bearing)(0), // 1: roveapi.Bearing + (RoverUpgrade)(0), // 2: roveapi.RoverUpgrade + (Object)(0), // 3: roveapi.Object + (Tile)(0), // 4: roveapi.Tile + (SailPosition)(0), // 5: roveapi.SailPosition + (*ServerStatusRequest)(nil), // 6: roveapi.ServerStatusRequest + (*ServerStatusResponse)(nil), // 7: roveapi.ServerStatusResponse + (*RegisterRequest)(nil), // 8: roveapi.RegisterRequest + (*Account)(nil), // 9: roveapi.Account + (*RegisterResponse)(nil), // 10: roveapi.RegisterResponse + (*Command)(nil), // 11: roveapi.Command + (*CommandRequest)(nil), // 12: roveapi.CommandRequest + (*CommandResponse)(nil), // 13: roveapi.CommandResponse + (*RadarRequest)(nil), // 14: roveapi.RadarRequest + (*RadarResponse)(nil), // 15: roveapi.RadarResponse + (*StatusRequest)(nil), // 16: roveapi.StatusRequest + (*Log)(nil), // 17: roveapi.Log + (*Vector)(nil), // 18: roveapi.Vector + (*RoverSpecifications)(nil), // 19: roveapi.RoverSpecifications + (*RoverStatus)(nil), // 20: roveapi.RoverStatus + (*RoverReadings)(nil), // 21: roveapi.RoverReadings + (*StatusResponse)(nil), // 22: roveapi.StatusResponse +} +var file_roveapi_roveapi_proto_depIdxs = []int32{ + 9, // 0: roveapi.RegisterResponse.account:type_name -> roveapi.Account + 0, // 1: roveapi.Command.command:type_name -> roveapi.CommandType + 1, // 2: roveapi.Command.bearing:type_name -> roveapi.Bearing + 2, // 3: roveapi.Command.upgrade:type_name -> roveapi.RoverUpgrade + 9, // 4: roveapi.CommandRequest.account:type_name -> roveapi.Account + 11, // 5: roveapi.CommandRequest.commands:type_name -> roveapi.Command + 9, // 6: roveapi.RadarRequest.account:type_name -> roveapi.Account + 4, // 7: roveapi.RadarResponse.tiles:type_name -> roveapi.Tile + 3, // 8: roveapi.RadarResponse.objects:type_name -> roveapi.Object + 9, // 9: roveapi.StatusRequest.account:type_name -> roveapi.Account + 1, // 10: roveapi.RoverStatus.bearing:type_name -> roveapi.Bearing + 5, // 11: roveapi.RoverStatus.sailPosition:type_name -> roveapi.SailPosition + 11, // 12: roveapi.RoverStatus.queuedCommands:type_name -> roveapi.Command + 18, // 13: roveapi.RoverReadings.position:type_name -> roveapi.Vector + 1, // 14: roveapi.RoverReadings.wind:type_name -> roveapi.Bearing + 17, // 15: roveapi.RoverReadings.logs:type_name -> roveapi.Log + 19, // 16: roveapi.StatusResponse.spec:type_name -> roveapi.RoverSpecifications + 20, // 17: roveapi.StatusResponse.status:type_name -> roveapi.RoverStatus + 21, // 18: roveapi.StatusResponse.readings:type_name -> roveapi.RoverReadings + 6, // 19: roveapi.Rove.ServerStatus:input_type -> roveapi.ServerStatusRequest + 8, // 20: roveapi.Rove.Register:input_type -> roveapi.RegisterRequest + 12, // 21: roveapi.Rove.Command:input_type -> roveapi.CommandRequest + 14, // 22: roveapi.Rove.Radar:input_type -> roveapi.RadarRequest + 16, // 23: roveapi.Rove.Status:input_type -> roveapi.StatusRequest + 7, // 24: roveapi.Rove.ServerStatus:output_type -> roveapi.ServerStatusResponse + 10, // 25: roveapi.Rove.Register:output_type -> roveapi.RegisterResponse + 13, // 26: roveapi.Rove.Command:output_type -> roveapi.CommandResponse + 15, // 27: roveapi.Rove.Radar:output_type -> roveapi.RadarResponse + 22, // 28: roveapi.Rove.Status:output_type -> roveapi.StatusResponse + 24, // [24:29] is the sub-list for method output_type + 19, // [19:24] is the sub-list for method input_type + 19, // [19:19] is the sub-list for extension type_name + 19, // [19:19] is the sub-list for extension extendee + 0, // [0:19] is the sub-list for field type_name +} + +func init() { file_roveapi_roveapi_proto_init() } +func file_roveapi_roveapi_proto_init() { + if File_roveapi_roveapi_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_roveapi_roveapi_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ServerStatusRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_roveapi_roveapi_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ServerStatusResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_roveapi_roveapi_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RegisterRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_roveapi_roveapi_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Account); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_roveapi_roveapi_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RegisterResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_roveapi_roveapi_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Command); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_roveapi_roveapi_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CommandRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_roveapi_roveapi_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CommandResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_roveapi_roveapi_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RadarRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_roveapi_roveapi_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RadarResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_roveapi_roveapi_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StatusRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_roveapi_roveapi_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Log); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_roveapi_roveapi_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Vector); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_roveapi_roveapi_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RoverSpecifications); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_roveapi_roveapi_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RoverStatus); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_roveapi_roveapi_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RoverReadings); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_roveapi_roveapi_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StatusResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_roveapi_roveapi_proto_rawDesc, + NumEnums: 6, + NumMessages: 17, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_roveapi_roveapi_proto_goTypes, + DependencyIndexes: file_roveapi_roveapi_proto_depIdxs, + EnumInfos: file_roveapi_roveapi_proto_enumTypes, + MessageInfos: file_roveapi_roveapi_proto_msgTypes, + }.Build() + File_roveapi_roveapi_proto = out.File + file_roveapi_roveapi_proto_rawDesc = nil + file_roveapi_roveapi_proto_goTypes = nil + file_roveapi_roveapi_proto_depIdxs = nil +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConnInterface + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion6 + +// RoveClient is the client API for Rove service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type RoveClient interface { + // Server status + // Responds with various details about the current server status + ServerStatus(ctx context.Context, in *ServerStatusRequest, opts ...grpc.CallOption) (*ServerStatusResponse, error) + // Register an account + // Tries to register an account with the given name + Register(ctx context.Context, in *RegisterRequest, opts ...grpc.CallOption) (*RegisterResponse, error) + // Send commands to rover + // Sending commands to this endpoint will queue them to be executed during the + // following ticks, in the order sent. Commands sent within the same tick will + // overwrite until the tick has finished and the commands are queued + Command(ctx context.Context, in *CommandRequest, opts ...grpc.CallOption) (*CommandResponse, error) + // Get radar information + // Gets the radar output for the given rover + Radar(ctx context.Context, in *RadarRequest, opts ...grpc.CallOption) (*RadarResponse, error) + // Get rover information + // Gets information for the account's rover + Status(ctx context.Context, in *StatusRequest, opts ...grpc.CallOption) (*StatusResponse, error) +} + +type roveClient struct { + cc grpc.ClientConnInterface +} + +func NewRoveClient(cc grpc.ClientConnInterface) RoveClient { + return &roveClient{cc} +} + +func (c *roveClient) ServerStatus(ctx context.Context, in *ServerStatusRequest, opts ...grpc.CallOption) (*ServerStatusResponse, error) { + out := new(ServerStatusResponse) + err := c.cc.Invoke(ctx, "/roveapi.Rove/ServerStatus", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *roveClient) Register(ctx context.Context, in *RegisterRequest, opts ...grpc.CallOption) (*RegisterResponse, error) { + out := new(RegisterResponse) + err := c.cc.Invoke(ctx, "/roveapi.Rove/Register", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *roveClient) Command(ctx context.Context, in *CommandRequest, opts ...grpc.CallOption) (*CommandResponse, error) { + out := new(CommandResponse) + err := c.cc.Invoke(ctx, "/roveapi.Rove/Command", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *roveClient) Radar(ctx context.Context, in *RadarRequest, opts ...grpc.CallOption) (*RadarResponse, error) { + out := new(RadarResponse) + err := c.cc.Invoke(ctx, "/roveapi.Rove/Radar", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *roveClient) Status(ctx context.Context, in *StatusRequest, opts ...grpc.CallOption) (*StatusResponse, error) { + out := new(StatusResponse) + err := c.cc.Invoke(ctx, "/roveapi.Rove/Status", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// RoveServer is the server API for Rove service. +type RoveServer interface { + // Server status + // Responds with various details about the current server status + ServerStatus(context.Context, *ServerStatusRequest) (*ServerStatusResponse, error) + // Register an account + // Tries to register an account with the given name + Register(context.Context, *RegisterRequest) (*RegisterResponse, error) + // Send commands to rover + // Sending commands to this endpoint will queue them to be executed during the + // following ticks, in the order sent. Commands sent within the same tick will + // overwrite until the tick has finished and the commands are queued + Command(context.Context, *CommandRequest) (*CommandResponse, error) + // Get radar information + // Gets the radar output for the given rover + Radar(context.Context, *RadarRequest) (*RadarResponse, error) + // Get rover information + // Gets information for the account's rover + Status(context.Context, *StatusRequest) (*StatusResponse, error) +} + +// UnimplementedRoveServer can be embedded to have forward compatible implementations. +type UnimplementedRoveServer struct { +} + +func (*UnimplementedRoveServer) ServerStatus(context.Context, *ServerStatusRequest) (*ServerStatusResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ServerStatus not implemented") +} +func (*UnimplementedRoveServer) Register(context.Context, *RegisterRequest) (*RegisterResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Register not implemented") +} +func (*UnimplementedRoveServer) Command(context.Context, *CommandRequest) (*CommandResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Command not implemented") +} +func (*UnimplementedRoveServer) Radar(context.Context, *RadarRequest) (*RadarResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Radar not implemented") +} +func (*UnimplementedRoveServer) Status(context.Context, *StatusRequest) (*StatusResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Status not implemented") +} + +func RegisterRoveServer(s *grpc.Server, srv RoveServer) { + s.RegisterService(&_Rove_serviceDesc, srv) +} + +func _Rove_ServerStatus_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ServerStatusRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(RoveServer).ServerStatus(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/roveapi.Rove/ServerStatus", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(RoveServer).ServerStatus(ctx, req.(*ServerStatusRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Rove_Register_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RegisterRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(RoveServer).Register(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/roveapi.Rove/Register", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(RoveServer).Register(ctx, req.(*RegisterRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Rove_Command_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CommandRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(RoveServer).Command(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/roveapi.Rove/Command", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(RoveServer).Command(ctx, req.(*CommandRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Rove_Radar_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RadarRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(RoveServer).Radar(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/roveapi.Rove/Radar", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(RoveServer).Radar(ctx, req.(*RadarRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Rove_Status_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(StatusRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(RoveServer).Status(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/roveapi.Rove/Status", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(RoveServer).Status(ctx, req.(*StatusRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _Rove_serviceDesc = grpc.ServiceDesc{ + ServiceName: "roveapi.Rove", + HandlerType: (*RoveServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "ServerStatus", + Handler: _Rove_ServerStatus_Handler, + }, + { + MethodName: "Register", + Handler: _Rove_Register_Handler, + }, + { + MethodName: "Command", + Handler: _Rove_Command_Handler, + }, + { + MethodName: "Radar", + Handler: _Rove_Radar_Handler, + }, + { + MethodName: "Status", + Handler: _Rove_Status_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "roveapi/roveapi.proto", +} diff --git a/proto/roveapi/roveapi.proto b/proto/roveapi/roveapi.proto new file mode 100644 index 0000000..72a1fc2 --- /dev/null +++ b/proto/roveapi/roveapi.proto @@ -0,0 +1,322 @@ +syntax = "proto3"; + +// Rove +// +// Rove is an asychronous nomadic game about exploring a planet as part of a +// loose community +package roveapi; +option go_package = "github.com/mdiluz/rove/proto/roveapi"; + +// The Rove server hosts a single game session and world with multiple players +service Rove { + // Server status + // Responds with various details about the current server status + rpc ServerStatus(ServerStatusRequest) returns (ServerStatusResponse) {} + + // Register an account + // Tries to register an account with the given name + rpc Register(RegisterRequest) returns (RegisterResponse) {} + + // Send commands to rover + // Sending commands to this endpoint will queue them to be executed during the + // following ticks, in the order sent. Commands sent within the same tick will + // overwrite until the tick has finished and the commands are queued + rpc Command(CommandRequest) returns (CommandResponse) {} + + // Get radar information + // Gets the radar output for the given rover + rpc Radar(RadarRequest) returns (RadarResponse) {} + + // Get rover information + // Gets information for the account's rover + rpc Status(StatusRequest) returns (StatusResponse) {} +} + +// +// ServerStatus +// + +// ServerStatusRequest is an empty placeholder +message ServerStatusRequest {} + +// ServerStatusResponse is a response with useful server information +message ServerStatusResponse { + // The version of the server in v{major}.{minor}-{delta}-{sha} form + string version = 1; + + // Whether the server is ready to accept requests + bool ready = 2; + + // The tick rate of the server in minutes (how many minutes per tick) + int32 tickRate = 3; + + // The current tick of the server + int32 currentTick = 4; + + // The time the next tick will occur + string next_tick = 5; +} + +// +// Register +// + +// RegisterRequest contains data to register an account +message RegisterRequest { + // The desired account name + string name = 1; +} + +// Account describes a registered account +message Account { + // The account name + string name = 1; + + // The account secret value, given when creating the account + string secret = 2; +} + +// RegisterResponse is the response given to registering an account +message RegisterResponse { + // The registered account information + Account account = 1; +} + +// +// Command +// + +// CommandType defines the type of a command to give to the rover +enum CommandType { + none = 0; + // Waits before performing the next command + wait = 1; + // Toggles the sails, either catching the wind, or charging from the sun + toggle = 2; + // Turns the rover in the specified bearing (requires bearing) + turn = 3; + // Stashes item at current location in rover inventory + stash = 4; + // Repairs the rover using an inventory object + repair = 5; + // Broadcasts a message to nearby rovers (requires data) + broadcast = 6; + // Salvages a neighboring dormant rover for parts + salvage = 7; + // Transfers remote control into dormant rover + transfer = 8; + // Upgrades a chosen rover specification using 5 rover parts + upgrade = 9; +} + +// Bearing represents a compass direction +enum Bearing { + // BearingUnknown an unknown invalid bearing + BearingUnknown = 0; + North = 1; + NorthEast = 2; + East = 3; + SouthEast = 4; + South = 5; + SouthWest = 6; + West = 7; + NorthWest = 8; +} + +// Describes the type of upgrade +enum RoverUpgrade { + RoverUpgradeUnknown = 0; + Range = 1; + Capacity = 2; + MaximumIntegrity = 3; + MaximumCharge = 4; +} + +// Command is a single command for a rover +message Command { + // The command type + CommandType command = 1; + + // The number of times to repeat the command after the first + int32 repeat = 2; + + // broadcast - a simple message, must be composed of up to 3 printable ASCII + // glyphs (32-126) + bytes data = 3; + + // move - the bearing for the rover to turn to + Bearing bearing = 4; + + // upgrade - the upgrade to apply to the rover + RoverUpgrade upgrade = 5; +} + +// CommandRequest describes a set of commands to be requested for the rover +message CommandRequest { + // The account to execute these commands + Account account = 1; + + // The set of desired commands + repeated Command commands = 2; +} + +// CommandResponse is an empty placeholder +message CommandResponse {} + +// +// Radar +// + +// Types of objects +enum Object { + // ObjectUnknown represents no object at all + ObjectUnknown = 0; + + // RoverLive represents a live rover + RoverLive = 1; + + // RoverDormant describes a dormant rover + RoverDormant = 2; + + // RockSmall is a small stashable rock + RockSmall = 3; + + // RockLarge is a large blocking rock + RockLarge = 4; + + // RoverParts is one unit of rover parts, used for repairing and fixing the + // rover + RoverParts = 5; +} + +enum Tile { + // TileUnknown is a keyword for nothing + TileUnknown = 0; + + // Rock is solid rock ground + Rock = 1; + + // Gravel is loose rocks + Gravel = 2; + + // Sand is sand + Sand = 3; +} + +// RadarRequest is the data needed to request the radar for a rover +message RadarRequest { + // The account for this request + Account account = 1; +} + +// RadarResponse describes radar information +message RadarResponse { + // The range in tiles from the rover of the radar data + int32 range = 1; + + // A 1D array representing range*2 + 1 squared set of tiles, origin bottom + // left and in row->column order + repeated Tile tiles = 2; + + // A similar array to the tile array, but containing objects + repeated Object objects = 3; +} + +// +// Status +// + +// StatusRequest is information needed to request rover status +message StatusRequest { + // The account for this request + Account account = 1; +} + +// Log is a single log item +message Log { + // The unix timestamp of the log + string time = 1; + + // The text of the log + string text = 2; +} + +// Vector describes a point or vector in 2D space +message Vector { + int32 x = 1; + int32 y = 2; +} + +// SailPosition represents the position of the sola sail +enum SailPosition { + UnknownSailPosition = 0; + + // CatchingWind means the sail is catching the wind and moving the rover + CatchingWind = 1; + + // SolarCharging means the sail is facing the sun and charging + SolarCharging = 2; +} + +message RoverSpecifications { + + // The name of the rover + string name = 1; + + // The range of this rover's radar and broadcasting + int32 range = 2; + + // The capacity of the inventory + int32 capacity = 3; + + // The maximum health of the rover + int32 maximumIntegrity = 4; + + // The max energy the rover can store + int32 maximumCharge = 5; +} + +message RoverStatus { + + // The current direction of the rover + Bearing bearing = 1; + + // The current position of the sails + SailPosition sailPosition = 2; + + // The items in the rover inventory + bytes inventory = 3; + + // The current health of the rover + int32 integrity = 4; + + // The energy stored in the rover + int32 charge = 5; + + // The set of currently queued commands + repeated Command queuedCommands = 6; +} + +message RoverReadings { + // Position of the rover in world coordinates + Vector position = 1; + + // The current wind direction + Bearing wind = 2; + + // The most recent logs + repeated Log logs = 3; +} + +// StatusResponse is the response given to a status request +message StatusResponse { + + // The static rover information + RoverSpecifications spec = 1; + + // Current rover status + RoverStatus status = 2; + + // Current rover readings + RoverReadings readings = 3; +} \ No newline at end of file diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index 27bbb85..86a2118 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -18,6 +18,7 @@ apps: - network environment: ROVE_USER_DATA: $SNAP_USER_DATA + rove-server: command: bin/rove-server plugs: @@ -26,21 +27,7 @@ apps: environment: WORDS_FILE : "$SNAP/data/words_alpha.txt" DATA_PATH : $SNAP_USER_DATA - rove-accountant: - command: bin/rove-accountant - plugs: - - network - - network-bind - environment: - DATA_PATH : $SNAP_USER_DATA - rove-rest-server: - command: bin/rove-reverse-proxy - plugs: - - network - - network-bind - environment: - DATA_PATH : $SNAP_USER_DATA - + parts: go-rove: plugin: go