diff --git a/README.md b/README.md index 3a4c2fd..5059c42 100644 --- a/README.md +++ b/README.md @@ -98,6 +98,16 @@ make test восстановление worker и команды проверки описаны в [`docs/GAME_PACKAGE.md`](docs/GAME_PACKAGE.md#экспорт-со-статусом-офлайн-режима-этап-5). +## Учёт устройств площадки + +В `APP_MODE=venue` публичный `POST /api/devices/heartbeat` учитывает подключения +браузеров в памяти; `GET /api/devices` доступен организатору, администратору и +разработчику, с необязательным `gameID`. В prod heartbeat возвращает +`enabled=false`, список недоступен. TTL связи — менее 60 секунд, удаление — +через 15 минут, предел — 1024 подключения. Credentials не сохраняются. +Подробности и ограничения описаны в +[workspace README](../README.md#учёт-устройств-этап-12). + ## Документация и юридические документы - [`docs/TERMS.md`](docs/TERMS.md) — Пользовательское соглашение (оферта); раздаётся сервером по постоянному URL `/api/terms`; diff --git a/api/main.proto b/api/main.proto index babdabd..1a3ee65 100644 --- a/api/main.proto +++ b/api/main.proto @@ -41,6 +41,23 @@ option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = { }; service EveningDetectiveServer { + rpc DeviceHeartbeat(DeviceHeartbeatReq) returns (DeviceHeartbeatRsp) { + option (google.api.http) = { post: "/api/devices/heartbeat" body: "*" }; + option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { + tags : "Устройства"; + summary : "Обновить присутствие подключения браузера"; + description: "Публичный endpoint. Контексты взаимоисключающие и проверяются сервером. В prod enabled=false без регистрации. UUID обозначает браузерный профиль, не физический телефон."; + security : {}; + }; + } + rpc GetDevices(GetDevicesReq) returns (GetDevicesRsp) { + option (google.api.http) = { get: "/api/devices" }; + option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { + tags : "Устройства"; + summary : "Получить устройства площадки"; + description: "Только venue; организатор, администратор или разработчик. Связь актуальна менее 60 секунд; подключения удаляются через 15 минут. Фильтр gameID оставляет только подключения указанной игры."; + }; + } rpc GetTasks(GetTasksReq) returns (GetTasksRsp) { option (google.api.http) = { get: "/api/tasks" }; option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { @@ -2382,3 +2399,46 @@ message GameResultsPackageRsp { string packageID = 2; string appliedAt = 3; } + +message DeviceHeartbeatReq { + string deviceID = 1; + string connectionID = 2; + optional int32 organizerGameID = 3; + optional int32 teamID = 4; + string password = 5; + string screenID = 6; + string accessToken = 7; +} + +message DeviceHeartbeatRsp { + optional bool enabled = 1; + google.protobuf.Timestamp serverTime = 2; +} + +message GetDevicesReq { optional int32 gameID = 1; } + +message GetDevicesRsp { + repeated VenueDevice devices = 1; + google.protobuf.Timestamp serverTime = 2; +} + +message VenueDevice { + string ID = 1; + google.protobuf.Timestamp firstSeenAt = 2; + google.protobuf.Timestamp lastSeenAt = 3; + bool connected = 4; + repeated DeviceConnection connections = 5; +} + +message DeviceConnection { + string ID = 1; + google.protobuf.Timestamp firstSeenAt = 2; + google.protobuf.Timestamp lastSeenAt = 3; + string IP = 4; + string userAgent = 5; + bool connected = 6; + optional int32 gameID = 7; + optional int32 teamID = 8; + optional int32 screenID = 9; + string kind = 10; // browser, organizer, team, screen +} diff --git a/bin/evening_detective_server b/bin/evening_detective_server index f5aac4b..b866579 100755 Binary files a/bin/evening_detective_server and b/bin/evening_detective_server differ diff --git a/cmd/evening_detective_server/main.go b/cmd/evening_detective_server/main.go index dc5258c..e3cd81c 100644 --- a/cmd/evening_detective_server/main.go +++ b/cmd/evening_detective_server/main.go @@ -35,6 +35,7 @@ import ( "evening_detective_server/internal/repos/tickets_repo" "evening_detective_server/internal/repos/users_repo" "evening_detective_server/internal/services/answer_service" + "evening_detective_server/internal/services/device_presence_service" "evening_detective_server/internal/services/file_service" "evening_detective_server/internal/services/game_export_service" "evening_detective_server/internal/services/game_import_service" @@ -228,6 +229,8 @@ func main() { log.Fatalln("Failed to listen:", err) } + devicePresenceService := device_presence_service.NewDevicePresenceService(venueMode, gameService, teamService, screenService) + s := grpc.NewServer( grpc.MaxRecvMsgSize(grpcMsgLimit), grpc.UnaryInterceptor( @@ -237,6 +240,7 @@ func main() { "/crabs.evening_detective_server.EveningDetectiveServer/GetParticipation": true, "/crabs.evening_detective_server.EveningDetectiveServer/GetTicket": true, "/crabs.evening_detective_server.EveningDetectiveServer/AnswerParticipation": true, + "/crabs.evening_detective_server.EveningDetectiveServer/DeviceHeartbeat": true, "/crabs.evening_detective_server.EveningDetectiveServer/Ping": true, "/crabs.evening_detective_server.EveningDetectiveServer/Echo": true, "/crabs.evening_detective_server.EveningDetectiveServer/Signup": true, @@ -287,6 +291,7 @@ func main() { gameImportService, gameResultsService, gameResultsApplyService, + devicePresenceService, ), ) diff --git a/cmd/evening_detective_server/main.swagger.json b/cmd/evening_detective_server/main.swagger.json index 7efff95..10187b1 100644 --- a/cmd/evening_detective_server/main.swagger.json +++ b/cmd/evening_detective_server/main.swagger.json @@ -333,6 +333,74 @@ ] } }, + "/api/devices": { + "get": { + "summary": "Получить устройства площадки", + "description": "Только venue; организатор, администратор или разработчик. Связь актуальна менее 60 секунд; подключения удаляются через 15 минут. Фильтр gameID оставляет только подключения указанной игры.", + "operationId": "EveningDetectiveServer_GetDevices", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/evening_detective_serverGetDevicesRsp" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "gameID", + "in": "query", + "required": false, + "type": "integer", + "format": "int32" + } + ], + "tags": [ + "Устройства" + ] + } + }, + "/api/devices/heartbeat": { + "post": { + "summary": "Обновить присутствие подключения браузера", + "description": "Публичный endpoint. Контексты взаимоисключающие и проверяются сервером. В prod enabled=false без регистрации. UUID обозначает браузерный профиль, не физический телефон.", + "operationId": "EveningDetectiveServer_DeviceHeartbeat", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/evening_detective_serverDeviceHeartbeatRsp" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/evening_detective_serverDeviceHeartbeatReq" + } + } + ], + "tags": [ + "Устройства" + ], + "security": [] + } + }, "/api/files/upload": { "post": { "summary": "Загрузить файл", @@ -4700,6 +4768,87 @@ }, "description": "Developer tasks use new, in_progress, review and done statuses." }, + "evening_detective_serverDeviceConnection": { + "type": "object", + "properties": { + "ID": { + "type": "string" + }, + "firstSeenAt": { + "type": "string", + "format": "date-time" + }, + "lastSeenAt": { + "type": "string", + "format": "date-time" + }, + "IP": { + "type": "string" + }, + "userAgent": { + "type": "string" + }, + "connected": { + "type": "boolean" + }, + "gameID": { + "type": "integer", + "format": "int32" + }, + "teamID": { + "type": "integer", + "format": "int32" + }, + "screenID": { + "type": "integer", + "format": "int32" + }, + "kind": { + "type": "string", + "title": "browser, organizer, team, screen" + } + } + }, + "evening_detective_serverDeviceHeartbeatReq": { + "type": "object", + "properties": { + "deviceID": { + "type": "string" + }, + "connectionID": { + "type": "string" + }, + "organizerGameID": { + "type": "integer", + "format": "int32" + }, + "teamID": { + "type": "integer", + "format": "int32" + }, + "password": { + "type": "string" + }, + "screenID": { + "type": "string" + }, + "accessToken": { + "type": "string" + } + } + }, + "evening_detective_serverDeviceHeartbeatRsp": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "serverTime": { + "type": "string", + "format": "date-time" + } + } + }, "evening_detective_serverDoor": { "type": "object", "properties": { @@ -4894,6 +5043,22 @@ } } }, + "evening_detective_serverGetDevicesRsp": { + "type": "object", + "properties": { + "devices": { + "type": "array", + "items": { + "type": "object", + "$ref": "#/definitions/evening_detective_serverVenueDevice" + } + }, + "serverTime": { + "type": "string", + "format": "date-time" + } + } + }, "evening_detective_serverGetGameAnswersRsp": { "type": "object", "properties": { @@ -6155,6 +6320,32 @@ } } }, + "evening_detective_serverVenueDevice": { + "type": "object", + "properties": { + "ID": { + "type": "string" + }, + "firstSeenAt": { + "type": "string", + "format": "date-time" + }, + "lastSeenAt": { + "type": "string", + "format": "date-time" + }, + "connected": { + "type": "boolean" + }, + "connections": { + "type": "array", + "items": { + "type": "object", + "$ref": "#/definitions/evening_detective_serverDeviceConnection" + } + } + } + }, "protobufAny": { "type": "object", "properties": { diff --git a/internal/app/devices.go b/internal/app/devices.go new file mode 100644 index 0000000..bf33433 --- /dev/null +++ b/internal/app/devices.go @@ -0,0 +1,59 @@ +package app + +import ( + "context" + + "evening_detective_server/internal/modules/processor_jwt" + "evening_detective_server/internal/modules/roles" + "evening_detective_server/internal/services/device_presence_service" + proto "evening_detective_server/proto" + + "google.golang.org/protobuf/types/known/timestamppb" +) + +func (s *server) DeviceHeartbeat(ctx context.Context, req *proto.DeviceHeartbeatReq) (*proto.DeviceHeartbeatRsp, error) { + if req == nil { + req = &proto.DeviceHeartbeatReq{} + } + ip, userAgent := clientInfoFromContext(ctx) + claims, authenticated := processor_jwt.ClaimsFromContext(ctx) + input := device_presence_service.Heartbeat{ + DeviceID: req.DeviceID, + ConnectionID: req.ConnectionID, + OrganizerGameID: deviceOptionalID(req.OrganizerGameID), + TeamID: deviceOptionalID(req.TeamID), + Password: req.Password, + ScreenID: req.ScreenID, + IP: ip, + UserAgent: userAgent, + IsAuthenticated: authenticated, + CanManage: authenticated && (roles.HasRole(claims, roles.Organizer) || roles.HasRole(claims, roles.Author)), + } + if req.AccessToken != "" { + input.ScreenTokenHash = hashScreenToken(req.AccessToken) + } + enabled, now, err := s.devicePresenceService.Heartbeat(ctx, input) + if err != nil { + return nil, deviceErrorStatus(err) + } + return &proto.DeviceHeartbeatRsp{ + Enabled: &enabled, + ServerTime: timestamppb.New(now), + }, nil +} + +func (s *server) GetDevices(ctx context.Context, req *proto.GetDevicesReq) (*proto.GetDevicesRsp, error) { + if req == nil { + req = &proto.GetDevicesReq{} + } + claims, authenticated := processor_jwt.ClaimsFromContext(ctx) + canRead := authenticated && (roles.HasRole(claims, roles.Organizer) || roles.HasRole(claims, roles.Developer)) + devices, now, err := s.devicePresenceService.List(ctx, deviceOptionalID(req.GameID), canRead) + if err != nil { + return nil, deviceErrorStatus(err) + } + return &proto.GetDevicesRsp{ + Devices: mapVenueDevices(devices), + ServerTime: timestamppb.New(now), + }, nil +} diff --git a/internal/app/devices_errors.go b/internal/app/devices_errors.go new file mode 100644 index 0000000..b737878 --- /dev/null +++ b/internal/app/devices_errors.go @@ -0,0 +1,43 @@ +package app + +import ( + "context" + "errors" + "log" + + "evening_detective_server/internal/services/device_presence_service" + "evening_detective_server/internal/services/game_service" + "evening_detective_server/internal/services/screen_service" + "evening_detective_server/internal/services/team_service" + + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +func deviceErrorStatus(err error) error { + switch { + case errors.Is(err, context.Canceled): + return status.Error(codes.Canceled, "запрос отменён") + case errors.Is(err, context.DeadlineExceeded): + return status.Error(codes.DeadlineExceeded, "время запроса истекло") + case errors.Is(err, device_presence_service.ErrInvalid): + return status.Error(codes.InvalidArgument, device_presence_service.ErrInvalid.Error()) + case errors.Is(err, device_presence_service.ErrDisabled): + return status.Error(codes.FailedPrecondition, device_presence_service.ErrDisabled.Error()) + case errors.Is(err, device_presence_service.ErrPermission): + return status.Error(codes.PermissionDenied, device_presence_service.ErrPermission.Error()) + case errors.Is(err, device_presence_service.ErrUnauthorized): + return status.Error(codes.Unauthenticated, device_presence_service.ErrUnauthorized.Error()) + case errors.Is(err, device_presence_service.ErrFull): + return status.Error(codes.ResourceExhausted, device_presence_service.ErrFull.Error()) + case errors.Is(err, team_service.ErrTeamNotFound): + return status.Error(codes.NotFound, team_service.ErrTeamNotFound.Error()) + case errors.Is(err, game_service.ErrGameNotFound): + return status.Error(codes.NotFound, game_service.ErrGameNotFound.Error()) + case errors.Is(err, screen_service.ErrScreenNotFound): + return status.Error(codes.Unauthenticated, "недействительная сессия экрана") + default: + log.Printf("device presence: %v", err) + return status.Error(codes.Internal, "не удалось обновить учёт устройств") + } +} diff --git a/internal/app/devices_mapper.go b/internal/app/devices_mapper.go new file mode 100644 index 0000000..fffd6a0 --- /dev/null +++ b/internal/app/devices_mapper.go @@ -0,0 +1,53 @@ +package app + +import ( + "evening_detective_server/internal/services/device_presence_service" + proto "evening_detective_server/proto" + + "google.golang.org/protobuf/types/known/timestamppb" +) + +func deviceOptionalID(id *int32) *int { + if id == nil { + return nil + } + value := int(*id) + return &value +} + +func devicePublicID(id int) *int32 { + if id == 0 { + return nil + } + value := int32(id) + return &value +} + +func mapVenueDevices(devices []device_presence_service.Device) []*proto.VenueDevice { + result := make([]*proto.VenueDevice, 0, len(devices)) + for _, device := range devices { + connections := make([]*proto.DeviceConnection, 0, len(device.Connections)) + for _, connection := range device.Connections { + connections = append(connections, &proto.DeviceConnection{ + ID: connection.ID, + FirstSeenAt: timestamppb.New(connection.FirstSeenAt), + LastSeenAt: timestamppb.New(connection.LastSeenAt), + IP: connection.IP, + UserAgent: connection.UserAgent, + Connected: connection.Connected, + GameID: devicePublicID(connection.GameID), + TeamID: devicePublicID(connection.TeamID), + ScreenID: devicePublicID(connection.ScreenID), + Kind: connection.Kind, + }) + } + result = append(result, &proto.VenueDevice{ + ID: device.ID, + FirstSeenAt: timestamppb.New(device.FirstSeenAt), + LastSeenAt: timestamppb.New(device.LastSeenAt), + Connected: device.Connected, + Connections: connections, + }) + } + return result +} diff --git a/internal/app/devices_test.go b/internal/app/devices_test.go new file mode 100644 index 0000000..43c6376 --- /dev/null +++ b/internal/app/devices_test.go @@ -0,0 +1,99 @@ +package app + +import ( + "context" + "errors" + "strings" + "testing" + + "evening_detective_server/internal/modules/processor_jwt" + "evening_detective_server/internal/services/device_presence_service" + "evening_detective_server/internal/services/game_service" + "evening_detective_server/internal/services/screen_service" + "evening_detective_server/internal/services/team_service" + proto "evening_detective_server/proto" + + "github.com/google/uuid" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/metadata" + "google.golang.org/grpc/status" + "google.golang.org/protobuf/encoding/protojson" +) + +func TestDeviceTransport(t *testing.T) { + s := &server{devicePresenceService: device_presence_service.NewDevicePresenceService(true, nil, nil, nil)} + ctx := metadata.NewIncomingContext(context.Background(), metadata.Pairs("x-real-ip", "192.0.2.3", "grpcgateway-user-agent", "test-browser")) + req := &proto.DeviceHeartbeatReq{DeviceID: uuid.NewString(), ConnectionID: uuid.NewString()} + rsp, err := s.DeviceHeartbeat(ctx, req) + if err != nil || !rsp.GetEnabled() || rsp.ServerTime == nil { + t.Fatal(rsp, err) + } + for _, role := range []string{"organizer", "admin", "developer", "author", "user", ""} { + t.Run(role, func(t *testing.T) { + roleCtx := processor_jwt.ContextWithClaims(ctx, &processor_jwt.JWTClaims{Roles: []string{role}}) + devices, err := s.GetDevices(roleCtx, &proto.GetDevicesReq{}) + if role == "author" || role == "user" || role == "" { + if status.Code(err) != codes.PermissionDenied { + t.Fatal(err) + } + return + } + if err != nil || len(devices.Devices) != 1 { + t.Fatal(devices, err) + } + connection := devices.Devices[0].Connections[0] + if connection.IP != "192.0.2.3" || connection.UserAgent != "test-browser" { + t.Fatal(connection) + } + raw, err := protojson.Marshal(devices) + if err != nil { + t.Fatal(err) + } + for _, secret := range []string{"password", "accessToken", "ScreenTokenHash", "email", "URL"} { + if strings.Contains(string(raw), secret) { + t.Fatal("secret field in response") + } + } + }) + } + req.DeviceID = "bad" + if _, err = s.DeviceHeartbeat(ctx, req); status.Code(err) != codes.InvalidArgument { + t.Fatal(err) + } + s.devicePresenceService = device_presence_service.NewDevicePresenceService(false, nil, nil, nil) + rsp, err = s.DeviceHeartbeat(ctx, req) + if err != nil || rsp.GetEnabled() { + t.Fatal(rsp, err) + } + raw, err := protojson.Marshal(rsp) + if err != nil || !strings.Contains(string(raw), `"enabled":false`) { + t.Fatal("disabled flag must be explicit", string(raw), err) + } + if _, err = s.GetDevices(ctx, &proto.GetDevicesReq{}); status.Code(err) != codes.FailedPrecondition { + t.Fatal(err) + } +} + +func TestDeviceErrors(t *testing.T) { + for _, test := range []struct { + err error + code codes.Code + }{ + {context.Canceled, codes.Canceled}, + {context.DeadlineExceeded, codes.DeadlineExceeded}, + {device_presence_service.ErrInvalid, codes.InvalidArgument}, + {device_presence_service.ErrDisabled, codes.FailedPrecondition}, + {device_presence_service.ErrPermission, codes.PermissionDenied}, + {device_presence_service.ErrUnauthorized, codes.Unauthenticated}, + {device_presence_service.ErrFull, codes.ResourceExhausted}, + {team_service.ErrTeamNotFound, codes.NotFound}, + {game_service.ErrGameNotFound, codes.NotFound}, + {screen_service.ErrScreenNotFound, codes.Unauthenticated}, + {errors.New("SQL password=secret /private/path"), codes.Internal}, + } { + err := deviceErrorStatus(test.err) + if status.Code(err) != test.code || strings.Contains(err.Error(), "secret") || strings.Contains(err.Error(), "/private") { + t.Fatal(err) + } + } +} diff --git a/internal/app/server.go b/internal/app/server.go index 888dcd8..7139ec0 100644 --- a/internal/app/server.go +++ b/internal/app/server.go @@ -2,6 +2,7 @@ package app import ( "evening_detective_server/internal/services/answer_service" + "evening_detective_server/internal/services/device_presence_service" "evening_detective_server/internal/services/file_service" "evening_detective_server/internal/services/game_export_service" "evening_detective_server/internal/services/game_import_service" @@ -26,6 +27,7 @@ import ( type server struct { proto.UnsafeEveningDetectiveServerServer + devicePresenceService *device_presence_service.DevicePresenceService usersService *users_service.UsersService uiService *ui_service.UIService taskService *task_service.TaskService @@ -70,8 +72,10 @@ func NewServer( gameImportService *game_import_service.GameImportService, gameResultsService *game_results_package_service.GameResultsPackageService, gameResultsApplyService *game_results_package_service.GameResultsApplyService, + devicePresenceService *device_presence_service.DevicePresenceService, ) proto.EveningDetectiveServerServer { return &server{ + devicePresenceService: devicePresenceService, gameImportService: gameImportService, gameResultsService: gameResultsService, gameResultsApplyService: gameResultsApplyService, diff --git a/internal/services/device_presence_service/service.go b/internal/services/device_presence_service/service.go new file mode 100644 index 0000000..3dafc2b --- /dev/null +++ b/internal/services/device_presence_service/service.go @@ -0,0 +1,269 @@ +// Package device_presence_service tracks browser connections in venue memory. +package device_presence_service + +import ( + "context" + "errors" + "sort" + "strconv" + "strings" + "sync" + "time" + + "evening_detective_server/internal/services/game_model" + "evening_detective_server/internal/services/screen_service" + + "github.com/google/uuid" +) + +var ( + ErrInvalid = errors.New("некорректные данные устройства") + ErrDisabled = errors.New("учёт устройств доступен только на площадке") + ErrPermission = errors.New("нет доступа к устройствам") + ErrUnauthorized = errors.New("требуется авторизация") + ErrFull = errors.New("слишком много подключений") +) + +const ( + connectedFor = time.Minute + retainedFor = 15 * time.Minute + maxConnections = 1024 +) + +type IGamePresence interface { + GetGame(context.Context, int) (*game_model.Game, error) +} +type ITeamPresence interface { + CheckPresence(context.Context, int, string) (int, error) +} +type IScreenPresence interface { + Get(context.Context, int, string) (*screen_service.ScreenSession, error) +} + +// Heartbeat contains transient credentials; only the verified connection is retained. +type Heartbeat struct { + DeviceID string + ConnectionID string + OrganizerGameID *int + TeamID *int + Password string + ScreenID string + ScreenTokenHash string + IP string + UserAgent string + IsAuthenticated bool + CanManage bool +} + +type Connection struct { + ID string + DeviceID string + FirstSeenAt time.Time + LastSeenAt time.Time + IP string + UserAgent string + Kind string + GameID int + TeamID int + ScreenID int + Connected bool +} + +type Device struct { + ID string + FirstSeenAt time.Time + LastSeenAt time.Time + Connected bool + Connections []Connection +} + +type DevicePresenceService struct { + enabled bool + games IGamePresence + teams ITeamPresence + screens IScreenPresence + now func() time.Time + mu sync.Mutex + connections map[string]Connection +} + +func NewDevicePresenceService(enabled bool, games IGamePresence, teams ITeamPresence, screens IScreenPresence) *DevicePresenceService { + return &DevicePresenceService{ + enabled: enabled, + games: games, + teams: teams, + screens: screens, + now: time.Now, + connections: make(map[string]Connection), + } +} + +func (s *DevicePresenceService) Heartbeat(ctx context.Context, input Heartbeat) (bool, time.Time, error) { + if err := ctx.Err(); err != nil { + return false, time.Time{}, err + } + if !s.enabled { + return false, s.now(), nil + } + connection, err := s.verify(ctx, input) + if err != nil { + return true, time.Time{}, err + } + s.mu.Lock() + defer s.mu.Unlock() + now := s.now() + s.prune(now) + old, exists := s.connections[connection.ID] + if exists && old.DeviceID != connection.DeviceID { + return true, now, ErrInvalid + } + if !exists && len(s.connections) >= maxConnections { + return true, now, ErrFull + } + connection.FirstSeenAt = now + if exists { + connection.FirstSeenAt = old.FirstSeenAt + } + connection.LastSeenAt = now + s.connections[connection.ID] = connection + return true, now, nil +} + +func (s *DevicePresenceService) List(ctx context.Context, gameID *int, canManage bool) ([]Device, time.Time, error) { + if err := ctx.Err(); err != nil { + return nil, time.Time{}, err + } + if !s.enabled { + return nil, time.Time{}, ErrDisabled + } + if !canManage { + return nil, time.Time{}, ErrPermission + } + if gameID != nil && *gameID <= 0 { + return nil, time.Time{}, ErrInvalid + } + s.mu.Lock() + defer s.mu.Unlock() + now := s.now() + s.prune(now) + devices := make(map[string]*Device) + for _, connection := range s.connections { + if gameID != nil && connection.GameID != *gameID { + continue + } + connection.Connected = now.Sub(connection.LastSeenAt) < connectedFor + device := devices[connection.DeviceID] + if device == nil { + device = &Device{ + ID: connection.DeviceID, + FirstSeenAt: connection.FirstSeenAt, + LastSeenAt: connection.LastSeenAt, + } + devices[connection.DeviceID] = device + } + if connection.FirstSeenAt.Before(device.FirstSeenAt) { + device.FirstSeenAt = connection.FirstSeenAt + } + if connection.LastSeenAt.After(device.LastSeenAt) { + device.LastSeenAt = connection.LastSeenAt + } + device.Connected = device.Connected || connection.Connected + device.Connections = append(device.Connections, connection) + } + result := make([]Device, 0, len(devices)) + for _, device := range devices { + sort.Slice(device.Connections, func(i, j int) bool { return device.Connections[i].ID < device.Connections[j].ID }) + result = append(result, *device) + } + sort.Slice(result, func(i, j int) bool { return result[i].ID < result[j].ID }) + return result, now, nil +} + +func (s *DevicePresenceService) prune(now time.Time) { + for id, connection := range s.connections { + if now.Sub(connection.LastSeenAt) >= retainedFor { + delete(s.connections, id) + } + } +} + +func (s *DevicePresenceService) verify(ctx context.Context, input Heartbeat) (Connection, error) { + deviceID, err := parseUUID(input.DeviceID) + if err != nil { + return Connection{}, err + } + connectionID, err := parseUUID(input.ConnectionID) + if err != nil { + return Connection{}, err + } + organizer := input.OrganizerGameID != nil + team := input.TeamID != nil || input.Password != "" + screen := input.ScreenID != "" || input.ScreenTokenHash != "" + contexts := 0 + for _, present := range []bool{organizer, team, screen} { + if present { + contexts++ + } + } + if contexts > 1 || organizer && *input.OrganizerGameID <= 0 || team && (input.TeamID == nil || *input.TeamID <= 0 || input.Password == "") || screen && (input.ScreenID == "" || input.ScreenTokenHash == "") { + return Connection{}, ErrInvalid + } + connection := Connection{ + ID: connectionID, + DeviceID: deviceID, + Kind: "browser", + IP: input.IP, + UserAgent: input.UserAgent, + } + // Bound untrusted metadata as well as the number of records. + if len(connection.UserAgent) > 1024 { + connection.UserAgent = connection.UserAgent[:1024] + } + connection.UserAgent = strings.ToValidUTF8(connection.UserAgent, "") + if organizer { + if !input.IsAuthenticated { + return Connection{}, ErrUnauthorized + } + if !input.CanManage { + return Connection{}, ErrPermission + } + if _, err := s.games.GetGame(ctx, *input.OrganizerGameID); err != nil { + return Connection{}, err + } + connection.Kind = "organizer" + connection.GameID = *input.OrganizerGameID + } + if team { + gameID, err := s.teams.CheckPresence(ctx, *input.TeamID, input.Password) + if err != nil { + return Connection{}, err + } + connection.Kind = "team" + connection.TeamID = *input.TeamID + connection.GameID = gameID + } + if screen { + id, err := strconv.Atoi(input.ScreenID) + if err != nil || id <= 0 || id > 2147483647 { + return Connection{}, ErrInvalid + } + session, err := s.screens.Get(ctx, id, input.ScreenTokenHash) + if err != nil { + return Connection{}, err + } + connection.Kind = "screen" + connection.ScreenID = session.ID + if session.GameID != nil { + connection.GameID = *session.GameID + } + } + return connection, nil +} + +func parseUUID(value string) (string, error) { + parsed, err := uuid.Parse(value) + if err != nil || len(value) != 36 || parsed == uuid.Nil { + return "", ErrInvalid + } + return parsed.String(), nil +} diff --git a/internal/services/device_presence_service/service_test.go b/internal/services/device_presence_service/service_test.go new file mode 100644 index 0000000..143d8fc --- /dev/null +++ b/internal/services/device_presence_service/service_test.go @@ -0,0 +1,258 @@ +package device_presence_service + +import ( + "context" + "errors" + "strings" + "sync" + "testing" + "time" + "unicode/utf8" + + "evening_detective_server/internal/services/game_model" + "evening_detective_server/internal/services/game_service" + "evening_detective_server/internal/services/screen_service" + "evening_detective_server/internal/services/team_service" + + "github.com/google/uuid" +) + +type presenceFixture struct{} + +func (presenceFixture) GetGame(_ context.Context, id int) (*game_model.Game, error) { + if id != 7 { + return nil, game_service.ErrGameNotFound + } + return &game_model.Game{}, nil +} +func (presenceFixture) CheckPresence(_ context.Context, id int, password string) (int, error) { + if id != 2 || password != "secret" { + return 0, team_service.ErrTeamNotFound + } + return 7, nil +} +func (presenceFixture) Get(_ context.Context, id int, hash string) (*screen_service.ScreenSession, error) { + if id != 3 || hash != "hash" { + return nil, screen_service.ErrScreenNotFound + } + gameID := 7 + return &screen_service.ScreenSession{ID: 3, GameID: &gameID}, nil +} +func newFixture() (*DevicePresenceService, Heartbeat) { + fixture := presenceFixture{} + return NewDevicePresenceService(true, fixture, fixture, fixture), Heartbeat{DeviceID: uuid.NewString(), ConnectionID: uuid.NewString()} +} +func pointer(id int) *int { return &id } +func send(t *testing.T, s *DevicePresenceService, input Heartbeat) { + t.Helper() + enabled, _, err := s.Heartbeat(context.Background(), input) + if !enabled || err != nil { + t.Fatalf("heartbeat: %v %v", enabled, err) + } +} +func list(t *testing.T, s *DevicePresenceService, gameID *int) []Device { + t.Helper() + devices, _, err := s.List(context.Background(), gameID, true) + if err != nil { + t.Fatal(err) + } + return devices +} + +func TestRegistrationAndContext(t *testing.T) { + s, input := newFixture() + now := time.Date(2026, 10, 11, 0, 0, 0, 0, time.UTC) + s.now = func() time.Time { return now } + input.TeamID, input.Password = pointer(2), "secret" + send(t, s, input) + now = now.Add(time.Second) + input.IP, input.UserAgent = "192.0.2.1", "updated" + send(t, s, input) + devices := list(t, s, pointer(7)) + if len(devices) != 1 || len(devices[0].Connections) != 1 { + t.Fatal(devices) + } + connection := devices[0].Connections[0] + if connection.IP != input.IP || connection.TeamID != 2 || connection.FirstSeenAt.Equal(connection.LastSeenAt) { + t.Fatal(connection) + } + tab := input + tab.ConnectionID = uuid.NewString() + tab.TeamID, tab.Password = nil, "" + tab.OrganizerGameID, tab.IsAuthenticated, tab.CanManage = pointer(7), true, true + send(t, s, tab) + if len(list(t, s, nil)[0].Connections) != 2 { + t.Fatal("tabs not grouped") + } + input.TeamID, input.Password = nil, "" + input.ScreenID, input.ScreenTokenHash = "3", "hash" + send(t, s, input) + connection = s.connections[input.ConnectionID] + if connection.TeamID != 0 || connection.ScreenID != 3 || connection.GameID != 7 { + t.Fatal(connection) + } + input.ScreenID, input.ScreenTokenHash = "", "" + send(t, s, input) + connection = s.connections[input.ConnectionID] + if connection.TeamID != 0 || connection.ScreenID != 0 || connection.GameID != 0 || connection.Kind != "browser" { + t.Fatal(connection) + } + if len(list(t, s, pointer(7))[0].Connections) != 1 { + t.Fatal("filter includes other contexts") + } + devices[0].Connections[0].GameID = 99 + if s.connections[input.ConnectionID].GameID != 0 { + t.Fatal("list exposes registry memory") + } +} + +func TestTimeBoundaries(t *testing.T) { + s, input := newFixture() + start := time.Now() + now := start + s.now = func() time.Time { return now } + send(t, s, input) + for _, test := range []struct { + age time.Duration + exists, connected bool + }{ + {time.Minute - time.Nanosecond, true, true}, + {time.Minute, true, false}, + {15*time.Minute - time.Nanosecond, true, false}, + {15 * time.Minute, false, false}, + } { + now = start.Add(test.age) + devices := list(t, s, nil) + if (len(devices) > 0) != test.exists { + t.Fatalf("retention at %v: %v", test.age, devices) + } + if test.exists && (devices[0].Connected != test.connected || devices[0].Connections[0].Connected != test.connected) { + t.Fatalf("connection at %v", test.age) + } + } + send(t, s, input) + now = now.Add(59 * time.Second) + tab := input + tab.ConnectionID = uuid.NewString() + send(t, s, tab) + now = now.Add(time.Second) + if !list(t, s, nil)[0].Connected { + t.Fatal("live tab must keep device connected") + } +} + +func TestCapacityAndPruning(t *testing.T) { + s, input := newFixture() + now := time.Now() + s.now = func() time.Time { return now } + for range maxConnections { + input.ConnectionID = uuid.NewString() + send(t, s, input) + } + send(t, s, input) + extra := input + extra.ConnectionID = uuid.NewString() + _, _, err := s.Heartbeat(context.Background(), extra) + if !errors.Is(err, ErrFull) || len(s.connections) != maxConnections { + t.Fatal(err) + } + now = now.Add(retainedFor) + send(t, s, extra) + if len(s.connections) != 1 { + t.Fatal("expired records not pruned on update") + } +} + +func TestInvalidContextDoesNotOverwrite(t *testing.T) { + tests := []struct { + name string + mutate func(*Heartbeat) + want error + }{ + {"device UUID", func(h *Heartbeat) { h.DeviceID = "invalid" }, ErrInvalid}, + {"connection UUID", func(h *Heartbeat) { h.ConnectionID = "" }, ErrInvalid}, + {"nil UUID", func(h *Heartbeat) { h.DeviceID = uuid.Nil.String() }, ErrInvalid}, + {"device reassignment", func(h *Heartbeat) { h.DeviceID = uuid.NewString() }, ErrInvalid}, + {"mixed", func(h *Heartbeat) { h.TeamID = pointer(2); h.Password = "secret"; h.OrganizerGameID = pointer(7) }, ErrInvalid}, + {"password only", func(h *Heartbeat) { h.Password = "secret" }, ErrInvalid}, + {"team only", func(h *Heartbeat) { h.TeamID = pointer(2) }, ErrInvalid}, + {"screen only", func(h *Heartbeat) { h.ScreenID = "3" }, ErrInvalid}, + {"token only", func(h *Heartbeat) { h.ScreenTokenHash = "hash" }, ErrInvalid}, + {"screen ID", func(h *Heartbeat) { h.ScreenID = "oops"; h.ScreenTokenHash = "hash" }, ErrInvalid}, + {"negative game", func(h *Heartbeat) { h.OrganizerGameID = pointer(-1) }, ErrInvalid}, + {"anonymous organizer", func(h *Heartbeat) { h.OrganizerGameID = pointer(7) }, ErrUnauthorized}, + {"user organizer", func(h *Heartbeat) { h.OrganizerGameID = pointer(7); h.IsAuthenticated = true }, ErrPermission}, + {"missing game", func(h *Heartbeat) { h.OrganizerGameID = pointer(8); h.IsAuthenticated = true; h.CanManage = true }, game_service.ErrGameNotFound}, + {"bad team", func(h *Heartbeat) { h.TeamID = pointer(2); h.Password = "bad" }, team_service.ErrTeamNotFound}, + {"bad screen", func(h *Heartbeat) { h.ScreenID = "3"; h.ScreenTokenHash = "bad" }, screen_service.ErrScreenNotFound}, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + s, input := newFixture() + send(t, s, input) + before := s.connections[input.ConnectionID] + test.mutate(&input) + _, _, err := s.Heartbeat(context.Background(), input) + if !errors.Is(err, test.want) { + t.Fatalf("got %v, want %v", err, test.want) + } + if s.connections[before.ID] != before || len(s.connections) != 1 { + t.Fatal("invalid request modified registry") + } + }) + } +} + +func TestModeAndReadAccess(t *testing.T) { + s, input := newFixture() + if _, _, err := s.List(context.Background(), nil, false); !errors.Is(err, ErrPermission) { + t.Fatal(err) + } + if _, _, err := s.List(context.Background(), pointer(0), true); !errors.Is(err, ErrInvalid) { + t.Fatal(err) + } + s.enabled = false + enabled, now, err := s.Heartbeat(context.Background(), Heartbeat{}) + if enabled || now.IsZero() || err != nil || len(s.connections) != 0 { + t.Fatal("prod registered device") + } + if _, _, err = s.List(context.Background(), nil, true); !errors.Is(err, ErrDisabled) { + t.Fatal(err) + } + ctx, cancel := context.WithCancel(context.Background()) + cancel() + if _, _, err = s.Heartbeat(ctx, input); !errors.Is(err, context.Canceled) { + t.Fatal(err) + } +} + +func TestConcurrentAccess(t *testing.T) { + s, input := newFixture() + var wg sync.WaitGroup + for range 20 { + wg.Go(func() { + tab := input + tab.ConnectionID = uuid.NewString() + for range 50 { + send(t, s, tab) + list(t, s, nil) + } + }) + } + wg.Wait() + if len(list(t, s, nil)[0].Connections) != 20 { + t.Fatal("lost connections") + } +} + +func TestMetadataUTF8(t *testing.T) { + for _, agent := range []string{strings.Repeat("x", 1023) + "я", "bad\xffagent"} { + s, input := newFixture() + input.UserAgent = agent + send(t, s, input) + stored := list(t, s, nil)[0].Connections[0].UserAgent + if len(stored) > 1024 || !utf8.ValidString(stored) { + t.Fatal("metadata must serialize safely") + } + } +} diff --git a/internal/services/game_export_service/export_test.go b/internal/services/game_export_service/export_test.go index edacd62..8d74a41 100644 --- a/internal/services/game_export_service/export_test.go +++ b/internal/services/game_export_service/export_test.go @@ -377,7 +377,7 @@ func verifyExportedGameReads(t *testing.T, f serviceFixture, worker *game_export games := game_service.NewGameService(f.games, f.teams, f.scenarios, nil, "", actions_repo.NewActionsRepo(f.pool), applications_repo.NewApplicationsRepo(f.pool)) mapRepo := game_map_repo.NewGameMapRepo(f.pool) server := app.NewServer(nil, nil, nil, nil, games, nil, nil, nil, nil, nil, nil, - screen_service.NewScreenService(screen_sessions_repo.NewScreenSessionsRepo(f.pool)), nil, nil, map_service.NewMapService(mapRepo), nil, worker, nil, nil, nil) + screen_service.NewScreenService(screen_sessions_repo.NewScreenSessionsRepo(f.pool)), nil, nil, map_service.NewMapService(mapRepo), nil, worker, nil, nil, nil, nil) ctx := processor_jwt.ContextWithClaims(context.Background(), &processor_jwt.JWTClaims{UserID: f.userID, Roles: []string{"organizer"}}) game, err := server.GetGame(ctx, &proto.GetGameReq{ID: int32(f.gameID)}) check(t, err) diff --git a/internal/services/team_service/service.go b/internal/services/team_service/service.go index 4ff4fcd..904a4c9 100644 --- a/internal/services/team_service/service.go +++ b/internal/services/team_service/service.go @@ -166,3 +166,18 @@ func (s *TeamService) DownloadTeamQR(ctx context.Context, id int) ([]byte, strin data, err := team_qr_pdf.Generate(cards) return data, filename, err } + +// CheckPresence validates team credentials without loading game progress or a scenario. +func (s *TeamService) CheckPresence(ctx context.Context, teamID int, password string) (int, error) { + team, err := s.teamsRepo.GetTeamByID(ctx, teamID) + if errors.Is(err, teams_repo.ErrTeamNotFound) { + return 0, ErrTeamNotFound + } + if err != nil { + return 0, err + } + if team.Password != password || team.ParticipationStatus == "cancelled" { + return 0, ErrTeamNotFound + } + return team.GameID, nil +} diff --git a/proto/main.pb.go b/proto/main.pb.go index 82d03b9..99404e8 100644 --- a/proto/main.pb.go +++ b/proto/main.pb.go @@ -12214,6 +12214,438 @@ func (x *GameResultsPackageRsp) GetAppliedAt() string { return "" } +type DeviceHeartbeatReq struct { + state protoimpl.MessageState `protogen:"open.v1"` + DeviceID string `protobuf:"bytes,1,opt,name=deviceID,proto3" json:"deviceID,omitempty"` + ConnectionID string `protobuf:"bytes,2,opt,name=connectionID,proto3" json:"connectionID,omitempty"` + OrganizerGameID *int32 `protobuf:"varint,3,opt,name=organizerGameID,proto3,oneof" json:"organizerGameID,omitempty"` + TeamID *int32 `protobuf:"varint,4,opt,name=teamID,proto3,oneof" json:"teamID,omitempty"` + Password string `protobuf:"bytes,5,opt,name=password,proto3" json:"password,omitempty"` + ScreenID string `protobuf:"bytes,6,opt,name=screenID,proto3" json:"screenID,omitempty"` + AccessToken string `protobuf:"bytes,7,opt,name=accessToken,proto3" json:"accessToken,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *DeviceHeartbeatReq) Reset() { + *x = DeviceHeartbeatReq{} + mi := &file_main_proto_msgTypes[232] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DeviceHeartbeatReq) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeviceHeartbeatReq) ProtoMessage() {} + +func (x *DeviceHeartbeatReq) ProtoReflect() protoreflect.Message { + mi := &file_main_proto_msgTypes[232] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeviceHeartbeatReq.ProtoReflect.Descriptor instead. +func (*DeviceHeartbeatReq) Descriptor() ([]byte, []int) { + return file_main_proto_rawDescGZIP(), []int{232} +} + +func (x *DeviceHeartbeatReq) GetDeviceID() string { + if x != nil { + return x.DeviceID + } + return "" +} + +func (x *DeviceHeartbeatReq) GetConnectionID() string { + if x != nil { + return x.ConnectionID + } + return "" +} + +func (x *DeviceHeartbeatReq) GetOrganizerGameID() int32 { + if x != nil && x.OrganizerGameID != nil { + return *x.OrganizerGameID + } + return 0 +} + +func (x *DeviceHeartbeatReq) GetTeamID() int32 { + if x != nil && x.TeamID != nil { + return *x.TeamID + } + return 0 +} + +func (x *DeviceHeartbeatReq) GetPassword() string { + if x != nil { + return x.Password + } + return "" +} + +func (x *DeviceHeartbeatReq) GetScreenID() string { + if x != nil { + return x.ScreenID + } + return "" +} + +func (x *DeviceHeartbeatReq) GetAccessToken() string { + if x != nil { + return x.AccessToken + } + return "" +} + +type DeviceHeartbeatRsp struct { + state protoimpl.MessageState `protogen:"open.v1"` + Enabled *bool `protobuf:"varint,1,opt,name=enabled,proto3,oneof" json:"enabled,omitempty"` + ServerTime *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=serverTime,proto3" json:"serverTime,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *DeviceHeartbeatRsp) Reset() { + *x = DeviceHeartbeatRsp{} + mi := &file_main_proto_msgTypes[233] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DeviceHeartbeatRsp) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeviceHeartbeatRsp) ProtoMessage() {} + +func (x *DeviceHeartbeatRsp) ProtoReflect() protoreflect.Message { + mi := &file_main_proto_msgTypes[233] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeviceHeartbeatRsp.ProtoReflect.Descriptor instead. +func (*DeviceHeartbeatRsp) Descriptor() ([]byte, []int) { + return file_main_proto_rawDescGZIP(), []int{233} +} + +func (x *DeviceHeartbeatRsp) GetEnabled() bool { + if x != nil && x.Enabled != nil { + return *x.Enabled + } + return false +} + +func (x *DeviceHeartbeatRsp) GetServerTime() *timestamppb.Timestamp { + if x != nil { + return x.ServerTime + } + return nil +} + +type GetDevicesReq struct { + state protoimpl.MessageState `protogen:"open.v1"` + GameID *int32 `protobuf:"varint,1,opt,name=gameID,proto3,oneof" json:"gameID,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetDevicesReq) Reset() { + *x = GetDevicesReq{} + mi := &file_main_proto_msgTypes[234] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetDevicesReq) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetDevicesReq) ProtoMessage() {} + +func (x *GetDevicesReq) ProtoReflect() protoreflect.Message { + mi := &file_main_proto_msgTypes[234] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetDevicesReq.ProtoReflect.Descriptor instead. +func (*GetDevicesReq) Descriptor() ([]byte, []int) { + return file_main_proto_rawDescGZIP(), []int{234} +} + +func (x *GetDevicesReq) GetGameID() int32 { + if x != nil && x.GameID != nil { + return *x.GameID + } + return 0 +} + +type GetDevicesRsp struct { + state protoimpl.MessageState `protogen:"open.v1"` + Devices []*VenueDevice `protobuf:"bytes,1,rep,name=devices,proto3" json:"devices,omitempty"` + ServerTime *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=serverTime,proto3" json:"serverTime,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetDevicesRsp) Reset() { + *x = GetDevicesRsp{} + mi := &file_main_proto_msgTypes[235] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetDevicesRsp) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetDevicesRsp) ProtoMessage() {} + +func (x *GetDevicesRsp) ProtoReflect() protoreflect.Message { + mi := &file_main_proto_msgTypes[235] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetDevicesRsp.ProtoReflect.Descriptor instead. +func (*GetDevicesRsp) Descriptor() ([]byte, []int) { + return file_main_proto_rawDescGZIP(), []int{235} +} + +func (x *GetDevicesRsp) GetDevices() []*VenueDevice { + if x != nil { + return x.Devices + } + return nil +} + +func (x *GetDevicesRsp) GetServerTime() *timestamppb.Timestamp { + if x != nil { + return x.ServerTime + } + return nil +} + +type VenueDevice struct { + state protoimpl.MessageState `protogen:"open.v1"` + ID string `protobuf:"bytes,1,opt,name=ID,proto3" json:"ID,omitempty"` + FirstSeenAt *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=firstSeenAt,proto3" json:"firstSeenAt,omitempty"` + LastSeenAt *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=lastSeenAt,proto3" json:"lastSeenAt,omitempty"` + Connected bool `protobuf:"varint,4,opt,name=connected,proto3" json:"connected,omitempty"` + Connections []*DeviceConnection `protobuf:"bytes,5,rep,name=connections,proto3" json:"connections,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *VenueDevice) Reset() { + *x = VenueDevice{} + mi := &file_main_proto_msgTypes[236] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *VenueDevice) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*VenueDevice) ProtoMessage() {} + +func (x *VenueDevice) ProtoReflect() protoreflect.Message { + mi := &file_main_proto_msgTypes[236] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use VenueDevice.ProtoReflect.Descriptor instead. +func (*VenueDevice) Descriptor() ([]byte, []int) { + return file_main_proto_rawDescGZIP(), []int{236} +} + +func (x *VenueDevice) GetID() string { + if x != nil { + return x.ID + } + return "" +} + +func (x *VenueDevice) GetFirstSeenAt() *timestamppb.Timestamp { + if x != nil { + return x.FirstSeenAt + } + return nil +} + +func (x *VenueDevice) GetLastSeenAt() *timestamppb.Timestamp { + if x != nil { + return x.LastSeenAt + } + return nil +} + +func (x *VenueDevice) GetConnected() bool { + if x != nil { + return x.Connected + } + return false +} + +func (x *VenueDevice) GetConnections() []*DeviceConnection { + if x != nil { + return x.Connections + } + return nil +} + +type DeviceConnection struct { + state protoimpl.MessageState `protogen:"open.v1"` + ID string `protobuf:"bytes,1,opt,name=ID,proto3" json:"ID,omitempty"` + FirstSeenAt *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=firstSeenAt,proto3" json:"firstSeenAt,omitempty"` + LastSeenAt *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=lastSeenAt,proto3" json:"lastSeenAt,omitempty"` + IP string `protobuf:"bytes,4,opt,name=IP,proto3" json:"IP,omitempty"` + UserAgent string `protobuf:"bytes,5,opt,name=userAgent,proto3" json:"userAgent,omitempty"` + Connected bool `protobuf:"varint,6,opt,name=connected,proto3" json:"connected,omitempty"` + GameID *int32 `protobuf:"varint,7,opt,name=gameID,proto3,oneof" json:"gameID,omitempty"` + TeamID *int32 `protobuf:"varint,8,opt,name=teamID,proto3,oneof" json:"teamID,omitempty"` + ScreenID *int32 `protobuf:"varint,9,opt,name=screenID,proto3,oneof" json:"screenID,omitempty"` + Kind string `protobuf:"bytes,10,opt,name=kind,proto3" json:"kind,omitempty"` // browser, organizer, team, screen + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *DeviceConnection) Reset() { + *x = DeviceConnection{} + mi := &file_main_proto_msgTypes[237] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DeviceConnection) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeviceConnection) ProtoMessage() {} + +func (x *DeviceConnection) ProtoReflect() protoreflect.Message { + mi := &file_main_proto_msgTypes[237] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeviceConnection.ProtoReflect.Descriptor instead. +func (*DeviceConnection) Descriptor() ([]byte, []int) { + return file_main_proto_rawDescGZIP(), []int{237} +} + +func (x *DeviceConnection) GetID() string { + if x != nil { + return x.ID + } + return "" +} + +func (x *DeviceConnection) GetFirstSeenAt() *timestamppb.Timestamp { + if x != nil { + return x.FirstSeenAt + } + return nil +} + +func (x *DeviceConnection) GetLastSeenAt() *timestamppb.Timestamp { + if x != nil { + return x.LastSeenAt + } + return nil +} + +func (x *DeviceConnection) GetIP() string { + if x != nil { + return x.IP + } + return "" +} + +func (x *DeviceConnection) GetUserAgent() string { + if x != nil { + return x.UserAgent + } + return "" +} + +func (x *DeviceConnection) GetConnected() bool { + if x != nil { + return x.Connected + } + return false +} + +func (x *DeviceConnection) GetGameID() int32 { + if x != nil && x.GameID != nil { + return *x.GameID + } + return 0 +} + +func (x *DeviceConnection) GetTeamID() int32 { + if x != nil && x.TeamID != nil { + return *x.TeamID + } + return 0 +} + +func (x *DeviceConnection) GetScreenID() int32 { + if x != nil && x.ScreenID != nil { + return *x.ScreenID + } + return 0 +} + +func (x *DeviceConnection) GetKind() string { + if x != nil { + return x.Kind + } + return "" +} + var File_main_proto protoreflect.FileDescriptor const file_main_proto_rawDesc = "" + @@ -12981,7 +13413,57 @@ const file_main_proto_rawDesc = "" + "\x15GameResultsPackageRsp\x12\x16\n" + "\x06gameID\x18\x01 \x01(\x05R\x06gameID\x12\x1c\n" + "\tpackageID\x18\x02 \x01(\tR\tpackageID\x12\x1c\n" + - "\tappliedAt\x18\x03 \x01(\tR\tappliedAt*\xf6\x01\n" + + "\tappliedAt\x18\x03 \x01(\tR\tappliedAt\"\x99\x02\n" + + "\x12DeviceHeartbeatReq\x12\x1a\n" + + "\bdeviceID\x18\x01 \x01(\tR\bdeviceID\x12\"\n" + + "\fconnectionID\x18\x02 \x01(\tR\fconnectionID\x12-\n" + + "\x0forganizerGameID\x18\x03 \x01(\x05H\x00R\x0forganizerGameID\x88\x01\x01\x12\x1b\n" + + "\x06teamID\x18\x04 \x01(\x05H\x01R\x06teamID\x88\x01\x01\x12\x1a\n" + + "\bpassword\x18\x05 \x01(\tR\bpassword\x12\x1a\n" + + "\bscreenID\x18\x06 \x01(\tR\bscreenID\x12 \n" + + "\vaccessToken\x18\a \x01(\tR\vaccessTokenB\x12\n" + + "\x10_organizerGameIDB\t\n" + + "\a_teamID\"{\n" + + "\x12DeviceHeartbeatRsp\x12\x1d\n" + + "\aenabled\x18\x01 \x01(\bH\x00R\aenabled\x88\x01\x01\x12:\n" + + "\n" + + "serverTime\x18\x02 \x01(\v2\x1a.google.protobuf.TimestampR\n" + + "serverTimeB\n" + + "\n" + + "\b_enabled\"7\n" + + "\rGetDevicesReq\x12\x1b\n" + + "\x06gameID\x18\x01 \x01(\x05H\x00R\x06gameID\x88\x01\x01B\t\n" + + "\a_gameID\"\x92\x01\n" + + "\rGetDevicesRsp\x12E\n" + + "\adevices\x18\x01 \x03(\v2+.crabs.evening_detective_server.VenueDeviceR\adevices\x12:\n" + + "\n" + + "serverTime\x18\x02 \x01(\v2\x1a.google.protobuf.TimestampR\n" + + "serverTime\"\x89\x02\n" + + "\vVenueDevice\x12\x0e\n" + + "\x02ID\x18\x01 \x01(\tR\x02ID\x12<\n" + + "\vfirstSeenAt\x18\x02 \x01(\v2\x1a.google.protobuf.TimestampR\vfirstSeenAt\x12:\n" + + "\n" + + "lastSeenAt\x18\x03 \x01(\v2\x1a.google.protobuf.TimestampR\n" + + "lastSeenAt\x12\x1c\n" + + "\tconnected\x18\x04 \x01(\bR\tconnected\x12R\n" + + "\vconnections\x18\x05 \x03(\v20.crabs.evening_detective_server.DeviceConnectionR\vconnections\"\xfa\x02\n" + + "\x10DeviceConnection\x12\x0e\n" + + "\x02ID\x18\x01 \x01(\tR\x02ID\x12<\n" + + "\vfirstSeenAt\x18\x02 \x01(\v2\x1a.google.protobuf.TimestampR\vfirstSeenAt\x12:\n" + + "\n" + + "lastSeenAt\x18\x03 \x01(\v2\x1a.google.protobuf.TimestampR\n" + + "lastSeenAt\x12\x0e\n" + + "\x02IP\x18\x04 \x01(\tR\x02IP\x12\x1c\n" + + "\tuserAgent\x18\x05 \x01(\tR\tuserAgent\x12\x1c\n" + + "\tconnected\x18\x06 \x01(\bR\tconnected\x12\x1b\n" + + "\x06gameID\x18\a \x01(\x05H\x00R\x06gameID\x88\x01\x01\x12\x1b\n" + + "\x06teamID\x18\b \x01(\x05H\x01R\x06teamID\x88\x01\x01\x12\x1f\n" + + "\bscreenID\x18\t \x01(\x05H\x02R\bscreenID\x88\x01\x01\x12\x12\n" + + "\x04kind\x18\n" + + " \x01(\tR\x04kindB\t\n" + + "\a_gameIDB\t\n" + + "\a_teamIDB\v\n" + + "\t_screenID*\xf6\x01\n" + "\n" + "ScreenMode\x12\x1b\n" + "\x17SCREEN_MODE_UNSPECIFIED\x10\x00\x12\x15\n" + @@ -13017,8 +13499,13 @@ const file_main_proto_rawDesc = "" + "\x1aMUSIC_PLAYBACK_UNSPECIFIED\x10\x00\x12\x1a\n" + "\x16MUSIC_PLAYBACK_STOPPED\x10\x01\x12\x1a\n" + "\x16MUSIC_PLAYBACK_PLAYING\x10\x02\x12\x19\n" + - "\x15MUSIC_PLAYBACK_PAUSED\x10\x032\xef\xd3\x01\n" + - "\x16EveningDetectiveServer\x12\x8c\x02\n" + + "\x15MUSIC_PLAYBACK_PAUSED\x10\x032\xbf\xdc\x01\n" + + "\x16EveningDetectiveServer\x12\xa9\x04\n" + + "\x0fDeviceHeartbeat\x122.crabs.evening_detective_server.DeviceHeartbeatReq\x1a2.crabs.evening_detective_server.DeviceHeartbeatRsp\"\xad\x03\x92A\x88\x03\n" + + "\x14Устройства\x12OОбновить присутствие подключения браузера\x1a\x9c\x02Публичный endpoint. Контексты взаимоисключающие и проверяются сервером. В prod enabled=false без регистрации. UUID обозначает браузерный профиль, не физический телефон.b\x00\x82\xd3\xe4\x93\x02\x1b:\x01*\"\x16/api/devices/heartbeat\x12\xa1\x04\n" + + "\n" + + "GetDevices\x12-.crabs.evening_detective_server.GetDevicesReq\x1a-.crabs.evening_detective_server.GetDevicesRsp\"\xb4\x03\x92A\x9c\x03\n" + + "\x14Устройства\x126Получить устройства площадки\x1a\xcb\x02Только venue; организатор, администратор или разработчик. Связь актуальна менее 60 секунд; подключения удаляются через 15 минут. Фильтр gameID оставляет только подключения указанной игры.\x82\xd3\xe4\x93\x02\x0e\x12\f/api/devices\x12\x8c\x02\n" + "\bGetTasks\x12+.crabs.evening_detective_server.GetTasksReq\x1a+.crabs.evening_detective_server.GetTasksRsp\"\xa5\x01\x92A\x8f\x01\n" + "\fЗадачи\x121Получить общую доску задач\x1aLДоступно разработчикам и администратору.\x82\xd3\xe4\x93\x02\f\x12\n" + "/api/tasks\x12\xc4\x02\n" + @@ -13312,7 +13799,7 @@ func file_main_proto_rawDescGZIP() []byte { } var file_main_proto_enumTypes = make([]protoimpl.EnumInfo, 4) -var file_main_proto_msgTypes = make([]protoimpl.MessageInfo, 232) +var file_main_proto_msgTypes = make([]protoimpl.MessageInfo, 238) var file_main_proto_goTypes = []any{ (ScreenMode)(0), // 0: crabs.evening_detective_server.ScreenMode (ScreenTimerState)(0), // 1: crabs.evening_detective_server.ScreenTimerState @@ -13550,12 +14037,18 @@ var file_main_proto_goTypes = []any{ (*GameResultsPackageReq)(nil), // 233: crabs.evening_detective_server.GameResultsPackageReq (*ApplyGameResultsPackageReq)(nil), // 234: crabs.evening_detective_server.ApplyGameResultsPackageReq (*GameResultsPackageRsp)(nil), // 235: crabs.evening_detective_server.GameResultsPackageRsp - (*timestamppb.Timestamp)(nil), // 236: google.protobuf.Timestamp - (*httpbody.HttpBody)(nil), // 237: google.api.HttpBody + (*DeviceHeartbeatReq)(nil), // 236: crabs.evening_detective_server.DeviceHeartbeatReq + (*DeviceHeartbeatRsp)(nil), // 237: crabs.evening_detective_server.DeviceHeartbeatRsp + (*GetDevicesReq)(nil), // 238: crabs.evening_detective_server.GetDevicesReq + (*GetDevicesRsp)(nil), // 239: crabs.evening_detective_server.GetDevicesRsp + (*VenueDevice)(nil), // 240: crabs.evening_detective_server.VenueDevice + (*DeviceConnection)(nil), // 241: crabs.evening_detective_server.DeviceConnection + (*timestamppb.Timestamp)(nil), // 242: google.protobuf.Timestamp + (*httpbody.HttpBody)(nil), // 243: google.api.HttpBody } var file_main_proto_depIdxs = []int32{ 26, // 0: crabs.evening_detective_server.GetUsersRsp.users:type_name -> crabs.evening_detective_server.User - 236, // 1: crabs.evening_detective_server.User.created_at:type_name -> google.protobuf.Timestamp + 242, // 1: crabs.evening_detective_server.User.created_at:type_name -> google.protobuf.Timestamp 26, // 2: crabs.evening_detective_server.GetUserByIDRsp.user:type_name -> crabs.evening_detective_server.User 26, // 3: crabs.evening_detective_server.GetMeRsp.user:type_name -> crabs.evening_detective_server.User 42, // 4: crabs.evening_detective_server.Playlist.tracks:type_name -> crabs.evening_detective_server.PlaylistTrack @@ -13568,9 +14061,9 @@ var file_main_proto_depIdxs = []int32{ 66, // 11: crabs.evening_detective_server.GetScenarioRsp.scenario:type_name -> crabs.evening_detective_server.Scenario 67, // 12: crabs.evening_detective_server.Scenario.story:type_name -> crabs.evening_detective_server.Story 26, // 13: crabs.evening_detective_server.Scenario.author:type_name -> crabs.evening_detective_server.User - 236, // 14: crabs.evening_detective_server.Scenario.updated_at:type_name -> google.protobuf.Timestamp - 236, // 15: crabs.evening_detective_server.Scenario.created_at:type_name -> google.protobuf.Timestamp - 236, // 16: crabs.evening_detective_server.Scenario.published_at:type_name -> google.protobuf.Timestamp + 242, // 14: crabs.evening_detective_server.Scenario.updated_at:type_name -> google.protobuf.Timestamp + 242, // 15: crabs.evening_detective_server.Scenario.created_at:type_name -> google.protobuf.Timestamp + 242, // 16: crabs.evening_detective_server.Scenario.published_at:type_name -> google.protobuf.Timestamp 70, // 17: crabs.evening_detective_server.Story.places:type_name -> crabs.evening_detective_server.Place 69, // 18: crabs.evening_detective_server.Story.introduction:type_name -> crabs.evening_detective_server.Introduction 74, // 19: crabs.evening_detective_server.Story.questions:type_name -> crabs.evening_detective_server.Question @@ -13587,13 +14080,13 @@ var file_main_proto_depIdxs = []int32{ 69, // 30: crabs.evening_detective_server.UpdateScenarioIntroReq.introduction:type_name -> crabs.evening_detective_server.Introduction 74, // 31: crabs.evening_detective_server.AddScenarioQuestionReq.question:type_name -> crabs.evening_detective_server.Question 74, // 32: crabs.evening_detective_server.UpdateScenarioQuestionReq.question:type_name -> crabs.evening_detective_server.Question - 236, // 33: crabs.evening_detective_server.AddGameReq.start_at:type_name -> google.protobuf.Timestamp + 242, // 33: crabs.evening_detective_server.AddGameReq.start_at:type_name -> google.protobuf.Timestamp 108, // 34: crabs.evening_detective_server.GetGamesRsp.games:type_name -> crabs.evening_detective_server.Game - 236, // 35: crabs.evening_detective_server.Game.start_at:type_name -> google.protobuf.Timestamp + 242, // 35: crabs.evening_detective_server.Game.start_at:type_name -> google.protobuf.Timestamp 66, // 36: crabs.evening_detective_server.Game.scenario:type_name -> crabs.evening_detective_server.Scenario 158, // 37: crabs.evening_detective_server.Game.teams:type_name -> crabs.evening_detective_server.Team - 236, // 38: crabs.evening_detective_server.Game.started_at:type_name -> google.protobuf.Timestamp - 236, // 39: crabs.evening_detective_server.Game.ended_at:type_name -> google.protobuf.Timestamp + 242, // 38: crabs.evening_detective_server.Game.started_at:type_name -> google.protobuf.Timestamp + 242, // 39: crabs.evening_detective_server.Game.ended_at:type_name -> google.protobuf.Timestamp 109, // 40: crabs.evening_detective_server.Game.export:type_name -> crabs.evening_detective_server.GameExport 109, // 41: crabs.evening_detective_server.GameExportRsp.export:type_name -> crabs.evening_detective_server.GameExport 0, // 42: crabs.evening_detective_server.ScreenState.mode:type_name -> crabs.evening_detective_server.ScreenMode @@ -13621,7 +14114,7 @@ var file_main_proto_depIdxs = []int32{ 146, // 64: crabs.evening_detective_server.SetScreenPlanStepRsp.plan:type_name -> crabs.evening_detective_server.ScreenPlan 164, // 65: crabs.evening_detective_server.GetScreenResultsRsp.questions:type_name -> crabs.evening_detective_server.ResultsQuestion 165, // 66: crabs.evening_detective_server.GetScreenResultsRsp.teams:type_name -> crabs.evening_detective_server.TeamResult - 236, // 67: crabs.evening_detective_server.ParticipationRsp.start_at:type_name -> google.protobuf.Timestamp + 242, // 67: crabs.evening_detective_server.ParticipationRsp.start_at:type_name -> google.protobuf.Timestamp 71, // 68: crabs.evening_detective_server.Team.applications:type_name -> crabs.evening_detective_server.Application 108, // 69: crabs.evening_detective_server.GetGameRsp.game:type_name -> crabs.evening_detective_server.Game 74, // 70: crabs.evening_detective_server.GetGameAnswersRsp.questions:type_name -> crabs.evening_detective_server.Question @@ -13630,11 +14123,11 @@ var file_main_proto_depIdxs = []int32{ 76, // 73: crabs.evening_detective_server.TeamResult.answers:type_name -> crabs.evening_detective_server.AnswerScore 164, // 74: crabs.evening_detective_server.GetGameResultsRsp.questions:type_name -> crabs.evening_detective_server.ResultsQuestion 165, // 75: crabs.evening_detective_server.GetGameResultsRsp.teams:type_name -> crabs.evening_detective_server.TeamResult - 236, // 76: crabs.evening_detective_server.GameReview.created_at:type_name -> google.protobuf.Timestamp + 242, // 76: crabs.evening_detective_server.GameReview.created_at:type_name -> google.protobuf.Timestamp 167, // 77: crabs.evening_detective_server.CreateGameReviewRsp.review:type_name -> crabs.evening_detective_server.GameReview 167, // 78: crabs.evening_detective_server.GetGameReviewsRsp.reviews:type_name -> crabs.evening_detective_server.GameReview 167, // 79: crabs.evening_detective_server.ModerateGameReviewRsp.review:type_name -> crabs.evening_detective_server.GameReview - 236, // 80: crabs.evening_detective_server.UpdateGameReq.start_at:type_name -> google.protobuf.Timestamp + 242, // 80: crabs.evening_detective_server.UpdateGameReq.start_at:type_name -> google.protobuf.Timestamp 67, // 81: crabs.evening_detective_server.GetTeamStoryRsp.story:type_name -> crabs.evening_detective_server.Story 108, // 82: crabs.evening_detective_server.GetTeamStoryRsp.game:type_name -> crabs.evening_detective_server.Game 200, // 83: crabs.evening_detective_server.GetTeamStoryRsp.actions:type_name -> crabs.evening_detective_server.TeamAction @@ -13643,237 +14136,249 @@ var file_main_proto_depIdxs = []int32{ 215, // 86: crabs.evening_detective_server.IssueGameTicketsRsp.tickets:type_name -> crabs.evening_detective_server.Ticket 220, // 87: crabs.evening_detective_server.GetTicketRsp.ticket:type_name -> crabs.evening_detective_server.TicketPage 220, // 88: crabs.evening_detective_server.AcceptTicketRsp.ticket:type_name -> crabs.evening_detective_server.TicketPage - 236, // 89: crabs.evening_detective_server.TicketPage.start_at:type_name -> google.protobuf.Timestamp - 236, // 90: crabs.evening_detective_server.TicketPage.used_at:type_name -> google.protobuf.Timestamp - 236, // 91: crabs.evening_detective_server.DeveloperTask.createdAt:type_name -> google.protobuf.Timestamp - 236, // 92: crabs.evening_detective_server.DeveloperTask.updatedAt:type_name -> google.protobuf.Timestamp + 242, // 89: crabs.evening_detective_server.TicketPage.start_at:type_name -> google.protobuf.Timestamp + 242, // 90: crabs.evening_detective_server.TicketPage.used_at:type_name -> google.protobuf.Timestamp + 242, // 91: crabs.evening_detective_server.DeveloperTask.createdAt:type_name -> google.protobuf.Timestamp + 242, // 92: crabs.evening_detective_server.DeveloperTask.updatedAt:type_name -> google.protobuf.Timestamp 221, // 93: crabs.evening_detective_server.GetTasksRsp.tasks:type_name -> crabs.evening_detective_server.DeveloperTask 222, // 94: crabs.evening_detective_server.GetTaskAssigneesRsp.assignees:type_name -> crabs.evening_detective_server.TaskAssignee 221, // 95: crabs.evening_detective_server.TaskRsp.task:type_name -> crabs.evening_detective_server.DeveloperTask - 237, // 96: crabs.evening_detective_server.ApplyGameResultsPackageReq.package:type_name -> google.api.HttpBody - 223, // 97: crabs.evening_detective_server.EveningDetectiveServer.GetTasks:input_type -> crabs.evening_detective_server.GetTasksReq - 225, // 98: crabs.evening_detective_server.EveningDetectiveServer.GetTaskAssignees:input_type -> crabs.evening_detective_server.GetTaskAssigneesReq - 227, // 99: crabs.evening_detective_server.EveningDetectiveServer.CreateTask:input_type -> crabs.evening_detective_server.CreateTaskReq - 228, // 100: crabs.evening_detective_server.EveningDetectiveServer.UpdateTask:input_type -> crabs.evening_detective_server.UpdateTaskReq - 229, // 101: crabs.evening_detective_server.EveningDetectiveServer.SetTaskStatus:input_type -> crabs.evening_detective_server.SetTaskStatusReq - 230, // 102: crabs.evening_detective_server.EveningDetectiveServer.DeleteTask:input_type -> crabs.evening_detective_server.DeleteTaskReq - 155, // 103: crabs.evening_detective_server.EveningDetectiveServer.GetParticipation:input_type -> crabs.evening_detective_server.GetParticipationReq - 156, // 104: crabs.evening_detective_server.EveningDetectiveServer.AnswerParticipation:input_type -> crabs.evening_detective_server.AnswerParticipationReq - 4, // 105: crabs.evening_detective_server.EveningDetectiveServer.Ping:input_type -> crabs.evening_detective_server.PingReq - 6, // 106: crabs.evening_detective_server.EveningDetectiveServer.Echo:input_type -> crabs.evening_detective_server.EchoReq - 8, // 107: crabs.evening_detective_server.EveningDetectiveServer.Signup:input_type -> crabs.evening_detective_server.SignupReq - 10, // 108: crabs.evening_detective_server.EveningDetectiveServer.RefreshPassword:input_type -> crabs.evening_detective_server.RefreshPasswordReq - 12, // 109: crabs.evening_detective_server.EveningDetectiveServer.Login:input_type -> crabs.evening_detective_server.LoginReq - 14, // 110: crabs.evening_detective_server.EveningDetectiveServer.Refresh:input_type -> crabs.evening_detective_server.RefreshReq - 16, // 111: crabs.evening_detective_server.EveningDetectiveServer.DeleteAccount:input_type -> crabs.evening_detective_server.DeleteAccountReq - 18, // 112: crabs.evening_detective_server.EveningDetectiveServer.GetTerms:input_type -> crabs.evening_detective_server.GetTermsReq - 20, // 113: crabs.evening_detective_server.EveningDetectiveServer.GetPrivacy:input_type -> crabs.evening_detective_server.GetPrivacyReq - 22, // 114: crabs.evening_detective_server.EveningDetectiveServer.GetConsent:input_type -> crabs.evening_detective_server.GetConsentReq - 24, // 115: crabs.evening_detective_server.EveningDetectiveServer.GetUsers:input_type -> crabs.evening_detective_server.GetUsersReq - 27, // 116: crabs.evening_detective_server.EveningDetectiveServer.GetUserByID:input_type -> crabs.evening_detective_server.GetUserByIDReq - 29, // 117: crabs.evening_detective_server.EveningDetectiveServer.GetMe:input_type -> crabs.evening_detective_server.GetMeReq - 31, // 118: crabs.evening_detective_server.EveningDetectiveServer.ChangeUserPassword:input_type -> crabs.evening_detective_server.ChangeUserPasswordReq - 33, // 119: crabs.evening_detective_server.EveningDetectiveServer.AddUserRole:input_type -> crabs.evening_detective_server.AddUserRoleReq - 35, // 120: crabs.evening_detective_server.EveningDetectiveServer.DeleteUserRole:input_type -> crabs.evening_detective_server.DeleteUserRoleReq - 37, // 121: crabs.evening_detective_server.EveningDetectiveServer.GetPermissions:input_type -> crabs.evening_detective_server.GetPermissionsReq - 39, // 122: crabs.evening_detective_server.EveningDetectiveServer.UploadFile:input_type -> crabs.evening_detective_server.UploadFileReq - 41, // 123: crabs.evening_detective_server.EveningDetectiveServer.DownloadFile:input_type -> crabs.evening_detective_server.DownloadFileReq - 44, // 124: crabs.evening_detective_server.EveningDetectiveServer.GetPlaylists:input_type -> crabs.evening_detective_server.GetPlaylistsReq - 46, // 125: crabs.evening_detective_server.EveningDetectiveServer.CreatePlaylist:input_type -> crabs.evening_detective_server.CreatePlaylistReq - 48, // 126: crabs.evening_detective_server.EveningDetectiveServer.UpdatePlaylist:input_type -> crabs.evening_detective_server.UpdatePlaylistReq - 50, // 127: crabs.evening_detective_server.EveningDetectiveServer.DeletePlaylist:input_type -> crabs.evening_detective_server.DeletePlaylistReq - 52, // 128: crabs.evening_detective_server.EveningDetectiveServer.UploadPlaylistTrack:input_type -> crabs.evening_detective_server.UploadPlaylistTrackReq - 54, // 129: crabs.evening_detective_server.EveningDetectiveServer.DeletePlaylistTrack:input_type -> crabs.evening_detective_server.DeletePlaylistTrackReq - 56, // 130: crabs.evening_detective_server.EveningDetectiveServer.SetScenarioPlaylist:input_type -> crabs.evening_detective_server.SetScenarioPlaylistReq - 58, // 131: crabs.evening_detective_server.EveningDetectiveServer.AddScenario:input_type -> crabs.evening_detective_server.AddScenarioReq - 60, // 132: crabs.evening_detective_server.EveningDetectiveServer.GetMyScenarios:input_type -> crabs.evening_detective_server.GetMyScenariosReq - 62, // 133: crabs.evening_detective_server.EveningDetectiveServer.GetScenariosCatalog:input_type -> crabs.evening_detective_server.GetScenariosCatalogReq - 64, // 134: crabs.evening_detective_server.EveningDetectiveServer.GetFullScenario:input_type -> crabs.evening_detective_server.GetScenarioReq - 64, // 135: crabs.evening_detective_server.EveningDetectiveServer.GetScenario:input_type -> crabs.evening_detective_server.GetScenarioReq - 78, // 136: crabs.evening_detective_server.EveningDetectiveServer.UpdateScenario:input_type -> crabs.evening_detective_server.UpdateScenarioReq - 80, // 137: crabs.evening_detective_server.EveningDetectiveServer.PublicScenario:input_type -> crabs.evening_detective_server.PublicScenarioReq - 82, // 138: crabs.evening_detective_server.EveningDetectiveServer.DraftScenario:input_type -> crabs.evening_detective_server.DraftScenarioReq - 84, // 139: crabs.evening_detective_server.EveningDetectiveServer.DeleteScenario:input_type -> crabs.evening_detective_server.DeleteScenarioReq - 86, // 140: crabs.evening_detective_server.EveningDetectiveServer.AddScenarioPlace:input_type -> crabs.evening_detective_server.AddScenarioPlaceReq - 88, // 141: crabs.evening_detective_server.EveningDetectiveServer.UpdateScenarioPlace:input_type -> crabs.evening_detective_server.UpdateScenarioPlaceReq - 90, // 142: crabs.evening_detective_server.EveningDetectiveServer.DeleteScenarioPlace:input_type -> crabs.evening_detective_server.DeleteScenarioPlaceReq - 96, // 143: crabs.evening_detective_server.EveningDetectiveServer.AddScenarioQuestion:input_type -> crabs.evening_detective_server.AddScenarioQuestionReq - 98, // 144: crabs.evening_detective_server.EveningDetectiveServer.UpdateScenarioQuestion:input_type -> crabs.evening_detective_server.UpdateScenarioQuestionReq - 100, // 145: crabs.evening_detective_server.EveningDetectiveServer.DeleteScenarioQuestion:input_type -> crabs.evening_detective_server.DeleteScenarioQuestionReq - 211, // 146: crabs.evening_detective_server.EveningDetectiveServer.UpdateScenarioBackground:input_type -> crabs.evening_detective_server.UpdateScenarioBackgroundReq - 94, // 147: crabs.evening_detective_server.EveningDetectiveServer.UpdateScenarioIntro:input_type -> crabs.evening_detective_server.UpdateScenarioIntroReq - 92, // 148: crabs.evening_detective_server.EveningDetectiveServer.UpdateScenarioConclusion:input_type -> crabs.evening_detective_server.UpdateScenarioConclusionReq - 102, // 149: crabs.evening_detective_server.EveningDetectiveServer.DownloadScenarioArchive:input_type -> crabs.evening_detective_server.DownloadScenarioArchiveReq - 237, // 150: crabs.evening_detective_server.EveningDetectiveServer.UploadScenarioArchive:input_type -> google.api.HttpBody - 104, // 151: crabs.evening_detective_server.EveningDetectiveServer.AddGame:input_type -> crabs.evening_detective_server.AddGameReq - 106, // 152: crabs.evening_detective_server.EveningDetectiveServer.GetGames:input_type -> crabs.evening_detective_server.GetGamesReq - 159, // 153: crabs.evening_detective_server.EveningDetectiveServer.GetGame:input_type -> crabs.evening_detective_server.GetGameReq - 237, // 154: crabs.evening_detective_server.EveningDetectiveServer.ImportGame:input_type -> google.api.HttpBody - 233, // 155: crabs.evening_detective_server.EveningDetectiveServer.DownloadGameResultsPackage:input_type -> crabs.evening_detective_server.GameResultsPackageReq - 234, // 156: crabs.evening_detective_server.EveningDetectiveServer.ApplyGameResultsPackage:input_type -> crabs.evening_detective_server.ApplyGameResultsPackageReq - 111, // 157: crabs.evening_detective_server.EveningDetectiveServer.StartGameExport:input_type -> crabs.evening_detective_server.StartGameExportReq - 112, // 158: crabs.evening_detective_server.EveningDetectiveServer.GetGameExport:input_type -> crabs.evening_detective_server.GetGameExportReq - 213, // 159: crabs.evening_detective_server.EveningDetectiveServer.IssueGameTickets:input_type -> crabs.evening_detective_server.IssueGameTicketsReq - 216, // 160: crabs.evening_detective_server.EveningDetectiveServer.GetTicket:input_type -> crabs.evening_detective_server.GetTicketReq - 218, // 161: crabs.evening_detective_server.EveningDetectiveServer.AcceptTicket:input_type -> crabs.evening_detective_server.AcceptTicketReq - 115, // 162: crabs.evening_detective_server.EveningDetectiveServer.CreateScreenSession:input_type -> crabs.evening_detective_server.CreateScreenSessionReq - 117, // 163: crabs.evening_detective_server.EveningDetectiveServer.GetScreenState:input_type -> crabs.evening_detective_server.GetScreenStateReq - 119, // 164: crabs.evening_detective_server.EveningDetectiveServer.GetGameScreens:input_type -> crabs.evening_detective_server.GetGameScreensReq - 122, // 165: crabs.evening_detective_server.EveningDetectiveServer.GetGameMusic:input_type -> crabs.evening_detective_server.GetGameMusicReq - 124, // 166: crabs.evening_detective_server.EveningDetectiveServer.SetGameMusic:input_type -> crabs.evening_detective_server.SetGameMusicReq - 126, // 167: crabs.evening_detective_server.EveningDetectiveServer.AdvanceScreenMusic:input_type -> crabs.evening_detective_server.AdvanceScreenMusicReq - 128, // 168: crabs.evening_detective_server.EveningDetectiveServer.BindScreen:input_type -> crabs.evening_detective_server.BindScreenReq - 130, // 169: crabs.evening_detective_server.EveningDetectiveServer.UnbindScreen:input_type -> crabs.evening_detective_server.UnbindScreenReq - 132, // 170: crabs.evening_detective_server.EveningDetectiveServer.SetScreenSound:input_type -> crabs.evening_detective_server.SetScreenSoundReq - 135, // 171: crabs.evening_detective_server.EveningDetectiveServer.GetGameMap:input_type -> crabs.evening_detective_server.GetGameMapReq - 137, // 172: crabs.evening_detective_server.EveningDetectiveServer.UpdateGameMapItem:input_type -> crabs.evening_detective_server.UpdateGameMapItemReq - 139, // 173: crabs.evening_detective_server.EveningDetectiveServer.SetScreenMode:input_type -> crabs.evening_detective_server.SetScreenModeReq - 141, // 174: crabs.evening_detective_server.EveningDetectiveServer.SetScreenTimer:input_type -> crabs.evening_detective_server.SetScreenTimerReq - 143, // 175: crabs.evening_detective_server.EveningDetectiveServer.SendScreenCommand:input_type -> crabs.evening_detective_server.SendScreenCommandReq - 147, // 176: crabs.evening_detective_server.EveningDetectiveServer.GetScreenPlan:input_type -> crabs.evening_detective_server.GetScreenPlanReq - 149, // 177: crabs.evening_detective_server.EveningDetectiveServer.SaveScreenPlan:input_type -> crabs.evening_detective_server.SaveScreenPlanReq - 151, // 178: crabs.evening_detective_server.EveningDetectiveServer.SetScreenPlanStep:input_type -> crabs.evening_detective_server.SetScreenPlanStepReq - 153, // 179: crabs.evening_detective_server.EveningDetectiveServer.GetScreenResults:input_type -> crabs.evening_detective_server.GetScreenResultsReq - 159, // 180: crabs.evening_detective_server.EveningDetectiveServer.DownloadTeamQR:input_type -> crabs.evening_detective_server.GetGameReq - 161, // 181: crabs.evening_detective_server.EveningDetectiveServer.GetGameAnswers:input_type -> crabs.evening_detective_server.GetGameAnswersReq - 163, // 182: crabs.evening_detective_server.EveningDetectiveServer.GetGameResults:input_type -> crabs.evening_detective_server.GetGameResultsReq - 168, // 183: crabs.evening_detective_server.EveningDetectiveServer.CreateGameReview:input_type -> crabs.evening_detective_server.CreateGameReviewReq - 170, // 184: crabs.evening_detective_server.EveningDetectiveServer.GetGameReviews:input_type -> crabs.evening_detective_server.GetGameReviewsReq - 171, // 185: crabs.evening_detective_server.EveningDetectiveServer.GetScenarioReviews:input_type -> crabs.evening_detective_server.GetScenarioReviewsReq - 170, // 186: crabs.evening_detective_server.EveningDetectiveServer.GetGameReviewsForModeration:input_type -> crabs.evening_detective_server.GetGameReviewsReq - 172, // 187: crabs.evening_detective_server.EveningDetectiveServer.GetReviewsForModeration:input_type -> crabs.evening_detective_server.GetReviewsForModerationReq - 174, // 188: crabs.evening_detective_server.EveningDetectiveServer.ModerateGameReview:input_type -> crabs.evening_detective_server.ModerateGameReviewReq - 176, // 189: crabs.evening_detective_server.EveningDetectiveServer.UpdateGame:input_type -> crabs.evening_detective_server.UpdateGameReq - 178, // 190: crabs.evening_detective_server.EveningDetectiveServer.StartGame:input_type -> crabs.evening_detective_server.StartGameReq - 180, // 191: crabs.evening_detective_server.EveningDetectiveServer.PauseGame:input_type -> crabs.evening_detective_server.PauseGameReq - 182, // 192: crabs.evening_detective_server.EveningDetectiveServer.PlayGame:input_type -> crabs.evening_detective_server.PlayGameReq - 184, // 193: crabs.evening_detective_server.EveningDetectiveServer.FinishGame:input_type -> crabs.evening_detective_server.FinishGameReq - 186, // 194: crabs.evening_detective_server.EveningDetectiveServer.ResetGame:input_type -> crabs.evening_detective_server.ResetGameReq - 188, // 195: crabs.evening_detective_server.EveningDetectiveServer.DeleteGame:input_type -> crabs.evening_detective_server.DeleteGameReq - 190, // 196: crabs.evening_detective_server.EveningDetectiveServer.AddTeam:input_type -> crabs.evening_detective_server.AddTeamReq - 192, // 197: crabs.evening_detective_server.EveningDetectiveServer.UpdateTeam:input_type -> crabs.evening_detective_server.UpdateTeamReq - 194, // 198: crabs.evening_detective_server.EveningDetectiveServer.DeleteTeam:input_type -> crabs.evening_detective_server.DeleteTeamReq - 196, // 199: crabs.evening_detective_server.EveningDetectiveServer.GiveTeamApplications:input_type -> crabs.evening_detective_server.GiveTeamApplicationsReq - 198, // 200: crabs.evening_detective_server.EveningDetectiveServer.GetTeamStory:input_type -> crabs.evening_detective_server.GetTeamStoryReq - 201, // 201: crabs.evening_detective_server.EveningDetectiveServer.AddTeamAction:input_type -> crabs.evening_detective_server.AddTeamActionReq - 203, // 202: crabs.evening_detective_server.EveningDetectiveServer.DeleteLastTeamAction:input_type -> crabs.evening_detective_server.DeleteLastTeamActionReq - 205, // 203: crabs.evening_detective_server.EveningDetectiveServer.GetTeamAnswers:input_type -> crabs.evening_detective_server.GetTeamAnswersReq - 207, // 204: crabs.evening_detective_server.EveningDetectiveServer.SubmitTeamAnswers:input_type -> crabs.evening_detective_server.SubmitTeamAnswersReq - 209, // 205: crabs.evening_detective_server.EveningDetectiveServer.SetTeamAnswerScore:input_type -> crabs.evening_detective_server.SetTeamAnswerScoreReq - 224, // 206: crabs.evening_detective_server.EveningDetectiveServer.GetTasks:output_type -> crabs.evening_detective_server.GetTasksRsp - 226, // 207: crabs.evening_detective_server.EveningDetectiveServer.GetTaskAssignees:output_type -> crabs.evening_detective_server.GetTaskAssigneesRsp - 231, // 208: crabs.evening_detective_server.EveningDetectiveServer.CreateTask:output_type -> crabs.evening_detective_server.TaskRsp - 231, // 209: crabs.evening_detective_server.EveningDetectiveServer.UpdateTask:output_type -> crabs.evening_detective_server.TaskRsp - 231, // 210: crabs.evening_detective_server.EveningDetectiveServer.SetTaskStatus:output_type -> crabs.evening_detective_server.TaskRsp - 232, // 211: crabs.evening_detective_server.EveningDetectiveServer.DeleteTask:output_type -> crabs.evening_detective_server.DeleteTaskRsp - 157, // 212: crabs.evening_detective_server.EveningDetectiveServer.GetParticipation:output_type -> crabs.evening_detective_server.ParticipationRsp - 157, // 213: crabs.evening_detective_server.EveningDetectiveServer.AnswerParticipation:output_type -> crabs.evening_detective_server.ParticipationRsp - 5, // 214: crabs.evening_detective_server.EveningDetectiveServer.Ping:output_type -> crabs.evening_detective_server.PingRsp - 7, // 215: crabs.evening_detective_server.EveningDetectiveServer.Echo:output_type -> crabs.evening_detective_server.EchoRsp - 9, // 216: crabs.evening_detective_server.EveningDetectiveServer.Signup:output_type -> crabs.evening_detective_server.SignupRsp - 11, // 217: crabs.evening_detective_server.EveningDetectiveServer.RefreshPassword:output_type -> crabs.evening_detective_server.RefreshPasswordRsp - 13, // 218: crabs.evening_detective_server.EveningDetectiveServer.Login:output_type -> crabs.evening_detective_server.LoginRsp - 15, // 219: crabs.evening_detective_server.EveningDetectiveServer.Refresh:output_type -> crabs.evening_detective_server.RefreshRsp - 17, // 220: crabs.evening_detective_server.EveningDetectiveServer.DeleteAccount:output_type -> crabs.evening_detective_server.DeleteAccountRsp - 19, // 221: crabs.evening_detective_server.EveningDetectiveServer.GetTerms:output_type -> crabs.evening_detective_server.GetTermsRsp - 21, // 222: crabs.evening_detective_server.EveningDetectiveServer.GetPrivacy:output_type -> crabs.evening_detective_server.GetPrivacyRsp - 23, // 223: crabs.evening_detective_server.EveningDetectiveServer.GetConsent:output_type -> crabs.evening_detective_server.GetConsentRsp - 25, // 224: crabs.evening_detective_server.EveningDetectiveServer.GetUsers:output_type -> crabs.evening_detective_server.GetUsersRsp - 28, // 225: crabs.evening_detective_server.EveningDetectiveServer.GetUserByID:output_type -> crabs.evening_detective_server.GetUserByIDRsp - 30, // 226: crabs.evening_detective_server.EveningDetectiveServer.GetMe:output_type -> crabs.evening_detective_server.GetMeRsp - 32, // 227: crabs.evening_detective_server.EveningDetectiveServer.ChangeUserPassword:output_type -> crabs.evening_detective_server.ChangeUserPasswordRsp - 34, // 228: crabs.evening_detective_server.EveningDetectiveServer.AddUserRole:output_type -> crabs.evening_detective_server.AddUserRoleRsp - 36, // 229: crabs.evening_detective_server.EveningDetectiveServer.DeleteUserRole:output_type -> crabs.evening_detective_server.DeleteUserRoleRsp - 38, // 230: crabs.evening_detective_server.EveningDetectiveServer.GetPermissions:output_type -> crabs.evening_detective_server.GetPermissionsRsp - 40, // 231: crabs.evening_detective_server.EveningDetectiveServer.UploadFile:output_type -> crabs.evening_detective_server.UploadFileRsp - 237, // 232: crabs.evening_detective_server.EveningDetectiveServer.DownloadFile:output_type -> google.api.HttpBody - 45, // 233: crabs.evening_detective_server.EveningDetectiveServer.GetPlaylists:output_type -> crabs.evening_detective_server.GetPlaylistsRsp - 47, // 234: crabs.evening_detective_server.EveningDetectiveServer.CreatePlaylist:output_type -> crabs.evening_detective_server.CreatePlaylistRsp - 49, // 235: crabs.evening_detective_server.EveningDetectiveServer.UpdatePlaylist:output_type -> crabs.evening_detective_server.UpdatePlaylistRsp - 51, // 236: crabs.evening_detective_server.EveningDetectiveServer.DeletePlaylist:output_type -> crabs.evening_detective_server.DeletePlaylistRsp - 53, // 237: crabs.evening_detective_server.EveningDetectiveServer.UploadPlaylistTrack:output_type -> crabs.evening_detective_server.UploadPlaylistTrackRsp - 55, // 238: crabs.evening_detective_server.EveningDetectiveServer.DeletePlaylistTrack:output_type -> crabs.evening_detective_server.DeletePlaylistTrackRsp - 57, // 239: crabs.evening_detective_server.EveningDetectiveServer.SetScenarioPlaylist:output_type -> crabs.evening_detective_server.SetScenarioPlaylistRsp - 59, // 240: crabs.evening_detective_server.EveningDetectiveServer.AddScenario:output_type -> crabs.evening_detective_server.AddScenarioRsp - 61, // 241: crabs.evening_detective_server.EveningDetectiveServer.GetMyScenarios:output_type -> crabs.evening_detective_server.GetMyScenariosRsp - 63, // 242: crabs.evening_detective_server.EveningDetectiveServer.GetScenariosCatalog:output_type -> crabs.evening_detective_server.GetScenariosCatalogRsp - 65, // 243: crabs.evening_detective_server.EveningDetectiveServer.GetFullScenario:output_type -> crabs.evening_detective_server.GetScenarioRsp - 65, // 244: crabs.evening_detective_server.EveningDetectiveServer.GetScenario:output_type -> crabs.evening_detective_server.GetScenarioRsp - 79, // 245: crabs.evening_detective_server.EveningDetectiveServer.UpdateScenario:output_type -> crabs.evening_detective_server.UpdateScenarioRsp - 81, // 246: crabs.evening_detective_server.EveningDetectiveServer.PublicScenario:output_type -> crabs.evening_detective_server.PublicScenarioRsp - 83, // 247: crabs.evening_detective_server.EveningDetectiveServer.DraftScenario:output_type -> crabs.evening_detective_server.DraftScenarioRsp - 85, // 248: crabs.evening_detective_server.EveningDetectiveServer.DeleteScenario:output_type -> crabs.evening_detective_server.DeleteScenarioRsp - 87, // 249: crabs.evening_detective_server.EveningDetectiveServer.AddScenarioPlace:output_type -> crabs.evening_detective_server.AddScenarioPlaceRsp - 89, // 250: crabs.evening_detective_server.EveningDetectiveServer.UpdateScenarioPlace:output_type -> crabs.evening_detective_server.UpdateScenarioPlaceRsp - 91, // 251: crabs.evening_detective_server.EveningDetectiveServer.DeleteScenarioPlace:output_type -> crabs.evening_detective_server.DeleteScenarioPlaceRsp - 97, // 252: crabs.evening_detective_server.EveningDetectiveServer.AddScenarioQuestion:output_type -> crabs.evening_detective_server.AddScenarioQuestionRsp - 99, // 253: crabs.evening_detective_server.EveningDetectiveServer.UpdateScenarioQuestion:output_type -> crabs.evening_detective_server.UpdateScenarioQuestionRsp - 101, // 254: crabs.evening_detective_server.EveningDetectiveServer.DeleteScenarioQuestion:output_type -> crabs.evening_detective_server.DeleteScenarioQuestionRsp - 212, // 255: crabs.evening_detective_server.EveningDetectiveServer.UpdateScenarioBackground:output_type -> crabs.evening_detective_server.UpdateScenarioBackgroundRsp - 95, // 256: crabs.evening_detective_server.EveningDetectiveServer.UpdateScenarioIntro:output_type -> crabs.evening_detective_server.UpdateScenarioIntroRsp - 93, // 257: crabs.evening_detective_server.EveningDetectiveServer.UpdateScenarioConclusion:output_type -> crabs.evening_detective_server.UpdateScenarioConclusionRsp - 237, // 258: crabs.evening_detective_server.EveningDetectiveServer.DownloadScenarioArchive:output_type -> google.api.HttpBody - 103, // 259: crabs.evening_detective_server.EveningDetectiveServer.UploadScenarioArchive:output_type -> crabs.evening_detective_server.UploadScenarioArchiveRsp - 105, // 260: crabs.evening_detective_server.EveningDetectiveServer.AddGame:output_type -> crabs.evening_detective_server.AddGameRsp - 107, // 261: crabs.evening_detective_server.EveningDetectiveServer.GetGames:output_type -> crabs.evening_detective_server.GetGamesRsp - 160, // 262: crabs.evening_detective_server.EveningDetectiveServer.GetGame:output_type -> crabs.evening_detective_server.GetGameRsp - 110, // 263: crabs.evening_detective_server.EveningDetectiveServer.ImportGame:output_type -> crabs.evening_detective_server.GameImportRsp - 237, // 264: crabs.evening_detective_server.EveningDetectiveServer.DownloadGameResultsPackage:output_type -> google.api.HttpBody - 235, // 265: crabs.evening_detective_server.EveningDetectiveServer.ApplyGameResultsPackage:output_type -> crabs.evening_detective_server.GameResultsPackageRsp - 113, // 266: crabs.evening_detective_server.EveningDetectiveServer.StartGameExport:output_type -> crabs.evening_detective_server.GameExportRsp - 113, // 267: crabs.evening_detective_server.EveningDetectiveServer.GetGameExport:output_type -> crabs.evening_detective_server.GameExportRsp - 214, // 268: crabs.evening_detective_server.EveningDetectiveServer.IssueGameTickets:output_type -> crabs.evening_detective_server.IssueGameTicketsRsp - 217, // 269: crabs.evening_detective_server.EveningDetectiveServer.GetTicket:output_type -> crabs.evening_detective_server.GetTicketRsp - 219, // 270: crabs.evening_detective_server.EveningDetectiveServer.AcceptTicket:output_type -> crabs.evening_detective_server.AcceptTicketRsp - 116, // 271: crabs.evening_detective_server.EveningDetectiveServer.CreateScreenSession:output_type -> crabs.evening_detective_server.CreateScreenSessionRsp - 118, // 272: crabs.evening_detective_server.EveningDetectiveServer.GetScreenState:output_type -> crabs.evening_detective_server.GetScreenStateRsp - 120, // 273: crabs.evening_detective_server.EveningDetectiveServer.GetGameScreens:output_type -> crabs.evening_detective_server.GetGameScreensRsp - 123, // 274: crabs.evening_detective_server.EveningDetectiveServer.GetGameMusic:output_type -> crabs.evening_detective_server.GetGameMusicRsp - 125, // 275: crabs.evening_detective_server.EveningDetectiveServer.SetGameMusic:output_type -> crabs.evening_detective_server.SetGameMusicRsp - 127, // 276: crabs.evening_detective_server.EveningDetectiveServer.AdvanceScreenMusic:output_type -> crabs.evening_detective_server.AdvanceScreenMusicRsp - 129, // 277: crabs.evening_detective_server.EveningDetectiveServer.BindScreen:output_type -> crabs.evening_detective_server.BindScreenRsp - 131, // 278: crabs.evening_detective_server.EveningDetectiveServer.UnbindScreen:output_type -> crabs.evening_detective_server.UnbindScreenRsp - 133, // 279: crabs.evening_detective_server.EveningDetectiveServer.SetScreenSound:output_type -> crabs.evening_detective_server.SetScreenSoundRsp - 136, // 280: crabs.evening_detective_server.EveningDetectiveServer.GetGameMap:output_type -> crabs.evening_detective_server.GetGameMapRsp - 138, // 281: crabs.evening_detective_server.EveningDetectiveServer.UpdateGameMapItem:output_type -> crabs.evening_detective_server.UpdateGameMapItemRsp - 140, // 282: crabs.evening_detective_server.EveningDetectiveServer.SetScreenMode:output_type -> crabs.evening_detective_server.SetScreenModeRsp - 142, // 283: crabs.evening_detective_server.EveningDetectiveServer.SetScreenTimer:output_type -> crabs.evening_detective_server.SetScreenTimerRsp - 144, // 284: crabs.evening_detective_server.EveningDetectiveServer.SendScreenCommand:output_type -> crabs.evening_detective_server.SendScreenCommandRsp - 148, // 285: crabs.evening_detective_server.EveningDetectiveServer.GetScreenPlan:output_type -> crabs.evening_detective_server.GetScreenPlanRsp - 150, // 286: crabs.evening_detective_server.EveningDetectiveServer.SaveScreenPlan:output_type -> crabs.evening_detective_server.SaveScreenPlanRsp - 152, // 287: crabs.evening_detective_server.EveningDetectiveServer.SetScreenPlanStep:output_type -> crabs.evening_detective_server.SetScreenPlanStepRsp - 154, // 288: crabs.evening_detective_server.EveningDetectiveServer.GetScreenResults:output_type -> crabs.evening_detective_server.GetScreenResultsRsp - 237, // 289: crabs.evening_detective_server.EveningDetectiveServer.DownloadTeamQR:output_type -> google.api.HttpBody - 162, // 290: crabs.evening_detective_server.EveningDetectiveServer.GetGameAnswers:output_type -> crabs.evening_detective_server.GetGameAnswersRsp - 166, // 291: crabs.evening_detective_server.EveningDetectiveServer.GetGameResults:output_type -> crabs.evening_detective_server.GetGameResultsRsp - 169, // 292: crabs.evening_detective_server.EveningDetectiveServer.CreateGameReview:output_type -> crabs.evening_detective_server.CreateGameReviewRsp - 173, // 293: crabs.evening_detective_server.EveningDetectiveServer.GetGameReviews:output_type -> crabs.evening_detective_server.GetGameReviewsRsp - 173, // 294: crabs.evening_detective_server.EveningDetectiveServer.GetScenarioReviews:output_type -> crabs.evening_detective_server.GetGameReviewsRsp - 173, // 295: crabs.evening_detective_server.EveningDetectiveServer.GetGameReviewsForModeration:output_type -> crabs.evening_detective_server.GetGameReviewsRsp - 173, // 296: crabs.evening_detective_server.EveningDetectiveServer.GetReviewsForModeration:output_type -> crabs.evening_detective_server.GetGameReviewsRsp - 175, // 297: crabs.evening_detective_server.EveningDetectiveServer.ModerateGameReview:output_type -> crabs.evening_detective_server.ModerateGameReviewRsp - 177, // 298: crabs.evening_detective_server.EveningDetectiveServer.UpdateGame:output_type -> crabs.evening_detective_server.UpdateGameRsp - 179, // 299: crabs.evening_detective_server.EveningDetectiveServer.StartGame:output_type -> crabs.evening_detective_server.StartGameRsp - 181, // 300: crabs.evening_detective_server.EveningDetectiveServer.PauseGame:output_type -> crabs.evening_detective_server.PauseGameRsp - 183, // 301: crabs.evening_detective_server.EveningDetectiveServer.PlayGame:output_type -> crabs.evening_detective_server.PlayGameRsp - 185, // 302: crabs.evening_detective_server.EveningDetectiveServer.FinishGame:output_type -> crabs.evening_detective_server.FinishGameRsp - 187, // 303: crabs.evening_detective_server.EveningDetectiveServer.ResetGame:output_type -> crabs.evening_detective_server.ResetGameRsp - 189, // 304: crabs.evening_detective_server.EveningDetectiveServer.DeleteGame:output_type -> crabs.evening_detective_server.DeleteGameRsp - 191, // 305: crabs.evening_detective_server.EveningDetectiveServer.AddTeam:output_type -> crabs.evening_detective_server.AddTeamRsp - 193, // 306: crabs.evening_detective_server.EveningDetectiveServer.UpdateTeam:output_type -> crabs.evening_detective_server.UpdateTeamRsp - 195, // 307: crabs.evening_detective_server.EveningDetectiveServer.DeleteTeam:output_type -> crabs.evening_detective_server.DeleteTeamRsp - 197, // 308: crabs.evening_detective_server.EveningDetectiveServer.GiveTeamApplications:output_type -> crabs.evening_detective_server.GiveTeamApplicationsRsp - 199, // 309: crabs.evening_detective_server.EveningDetectiveServer.GetTeamStory:output_type -> crabs.evening_detective_server.GetTeamStoryRsp - 202, // 310: crabs.evening_detective_server.EveningDetectiveServer.AddTeamAction:output_type -> crabs.evening_detective_server.AddTeamActionRsp - 204, // 311: crabs.evening_detective_server.EveningDetectiveServer.DeleteLastTeamAction:output_type -> crabs.evening_detective_server.DeleteLastTeamActionRsp - 206, // 312: crabs.evening_detective_server.EveningDetectiveServer.GetTeamAnswers:output_type -> crabs.evening_detective_server.GetTeamAnswersRsp - 208, // 313: crabs.evening_detective_server.EveningDetectiveServer.SubmitTeamAnswers:output_type -> crabs.evening_detective_server.SubmitTeamAnswersRsp - 210, // 314: crabs.evening_detective_server.EveningDetectiveServer.SetTeamAnswerScore:output_type -> crabs.evening_detective_server.SetTeamAnswerScoreRsp - 206, // [206:315] is the sub-list for method output_type - 97, // [97:206] is the sub-list for method input_type - 97, // [97:97] is the sub-list for extension type_name - 97, // [97:97] is the sub-list for extension extendee - 0, // [0:97] is the sub-list for field type_name + 243, // 96: crabs.evening_detective_server.ApplyGameResultsPackageReq.package:type_name -> google.api.HttpBody + 242, // 97: crabs.evening_detective_server.DeviceHeartbeatRsp.serverTime:type_name -> google.protobuf.Timestamp + 240, // 98: crabs.evening_detective_server.GetDevicesRsp.devices:type_name -> crabs.evening_detective_server.VenueDevice + 242, // 99: crabs.evening_detective_server.GetDevicesRsp.serverTime:type_name -> google.protobuf.Timestamp + 242, // 100: crabs.evening_detective_server.VenueDevice.firstSeenAt:type_name -> google.protobuf.Timestamp + 242, // 101: crabs.evening_detective_server.VenueDevice.lastSeenAt:type_name -> google.protobuf.Timestamp + 241, // 102: crabs.evening_detective_server.VenueDevice.connections:type_name -> crabs.evening_detective_server.DeviceConnection + 242, // 103: crabs.evening_detective_server.DeviceConnection.firstSeenAt:type_name -> google.protobuf.Timestamp + 242, // 104: crabs.evening_detective_server.DeviceConnection.lastSeenAt:type_name -> google.protobuf.Timestamp + 236, // 105: crabs.evening_detective_server.EveningDetectiveServer.DeviceHeartbeat:input_type -> crabs.evening_detective_server.DeviceHeartbeatReq + 238, // 106: crabs.evening_detective_server.EveningDetectiveServer.GetDevices:input_type -> crabs.evening_detective_server.GetDevicesReq + 223, // 107: crabs.evening_detective_server.EveningDetectiveServer.GetTasks:input_type -> crabs.evening_detective_server.GetTasksReq + 225, // 108: crabs.evening_detective_server.EveningDetectiveServer.GetTaskAssignees:input_type -> crabs.evening_detective_server.GetTaskAssigneesReq + 227, // 109: crabs.evening_detective_server.EveningDetectiveServer.CreateTask:input_type -> crabs.evening_detective_server.CreateTaskReq + 228, // 110: crabs.evening_detective_server.EveningDetectiveServer.UpdateTask:input_type -> crabs.evening_detective_server.UpdateTaskReq + 229, // 111: crabs.evening_detective_server.EveningDetectiveServer.SetTaskStatus:input_type -> crabs.evening_detective_server.SetTaskStatusReq + 230, // 112: crabs.evening_detective_server.EveningDetectiveServer.DeleteTask:input_type -> crabs.evening_detective_server.DeleteTaskReq + 155, // 113: crabs.evening_detective_server.EveningDetectiveServer.GetParticipation:input_type -> crabs.evening_detective_server.GetParticipationReq + 156, // 114: crabs.evening_detective_server.EveningDetectiveServer.AnswerParticipation:input_type -> crabs.evening_detective_server.AnswerParticipationReq + 4, // 115: crabs.evening_detective_server.EveningDetectiveServer.Ping:input_type -> crabs.evening_detective_server.PingReq + 6, // 116: crabs.evening_detective_server.EveningDetectiveServer.Echo:input_type -> crabs.evening_detective_server.EchoReq + 8, // 117: crabs.evening_detective_server.EveningDetectiveServer.Signup:input_type -> crabs.evening_detective_server.SignupReq + 10, // 118: crabs.evening_detective_server.EveningDetectiveServer.RefreshPassword:input_type -> crabs.evening_detective_server.RefreshPasswordReq + 12, // 119: crabs.evening_detective_server.EveningDetectiveServer.Login:input_type -> crabs.evening_detective_server.LoginReq + 14, // 120: crabs.evening_detective_server.EveningDetectiveServer.Refresh:input_type -> crabs.evening_detective_server.RefreshReq + 16, // 121: crabs.evening_detective_server.EveningDetectiveServer.DeleteAccount:input_type -> crabs.evening_detective_server.DeleteAccountReq + 18, // 122: crabs.evening_detective_server.EveningDetectiveServer.GetTerms:input_type -> crabs.evening_detective_server.GetTermsReq + 20, // 123: crabs.evening_detective_server.EveningDetectiveServer.GetPrivacy:input_type -> crabs.evening_detective_server.GetPrivacyReq + 22, // 124: crabs.evening_detective_server.EveningDetectiveServer.GetConsent:input_type -> crabs.evening_detective_server.GetConsentReq + 24, // 125: crabs.evening_detective_server.EveningDetectiveServer.GetUsers:input_type -> crabs.evening_detective_server.GetUsersReq + 27, // 126: crabs.evening_detective_server.EveningDetectiveServer.GetUserByID:input_type -> crabs.evening_detective_server.GetUserByIDReq + 29, // 127: crabs.evening_detective_server.EveningDetectiveServer.GetMe:input_type -> crabs.evening_detective_server.GetMeReq + 31, // 128: crabs.evening_detective_server.EveningDetectiveServer.ChangeUserPassword:input_type -> crabs.evening_detective_server.ChangeUserPasswordReq + 33, // 129: crabs.evening_detective_server.EveningDetectiveServer.AddUserRole:input_type -> crabs.evening_detective_server.AddUserRoleReq + 35, // 130: crabs.evening_detective_server.EveningDetectiveServer.DeleteUserRole:input_type -> crabs.evening_detective_server.DeleteUserRoleReq + 37, // 131: crabs.evening_detective_server.EveningDetectiveServer.GetPermissions:input_type -> crabs.evening_detective_server.GetPermissionsReq + 39, // 132: crabs.evening_detective_server.EveningDetectiveServer.UploadFile:input_type -> crabs.evening_detective_server.UploadFileReq + 41, // 133: crabs.evening_detective_server.EveningDetectiveServer.DownloadFile:input_type -> crabs.evening_detective_server.DownloadFileReq + 44, // 134: crabs.evening_detective_server.EveningDetectiveServer.GetPlaylists:input_type -> crabs.evening_detective_server.GetPlaylistsReq + 46, // 135: crabs.evening_detective_server.EveningDetectiveServer.CreatePlaylist:input_type -> crabs.evening_detective_server.CreatePlaylistReq + 48, // 136: crabs.evening_detective_server.EveningDetectiveServer.UpdatePlaylist:input_type -> crabs.evening_detective_server.UpdatePlaylistReq + 50, // 137: crabs.evening_detective_server.EveningDetectiveServer.DeletePlaylist:input_type -> crabs.evening_detective_server.DeletePlaylistReq + 52, // 138: crabs.evening_detective_server.EveningDetectiveServer.UploadPlaylistTrack:input_type -> crabs.evening_detective_server.UploadPlaylistTrackReq + 54, // 139: crabs.evening_detective_server.EveningDetectiveServer.DeletePlaylistTrack:input_type -> crabs.evening_detective_server.DeletePlaylistTrackReq + 56, // 140: crabs.evening_detective_server.EveningDetectiveServer.SetScenarioPlaylist:input_type -> crabs.evening_detective_server.SetScenarioPlaylistReq + 58, // 141: crabs.evening_detective_server.EveningDetectiveServer.AddScenario:input_type -> crabs.evening_detective_server.AddScenarioReq + 60, // 142: crabs.evening_detective_server.EveningDetectiveServer.GetMyScenarios:input_type -> crabs.evening_detective_server.GetMyScenariosReq + 62, // 143: crabs.evening_detective_server.EveningDetectiveServer.GetScenariosCatalog:input_type -> crabs.evening_detective_server.GetScenariosCatalogReq + 64, // 144: crabs.evening_detective_server.EveningDetectiveServer.GetFullScenario:input_type -> crabs.evening_detective_server.GetScenarioReq + 64, // 145: crabs.evening_detective_server.EveningDetectiveServer.GetScenario:input_type -> crabs.evening_detective_server.GetScenarioReq + 78, // 146: crabs.evening_detective_server.EveningDetectiveServer.UpdateScenario:input_type -> crabs.evening_detective_server.UpdateScenarioReq + 80, // 147: crabs.evening_detective_server.EveningDetectiveServer.PublicScenario:input_type -> crabs.evening_detective_server.PublicScenarioReq + 82, // 148: crabs.evening_detective_server.EveningDetectiveServer.DraftScenario:input_type -> crabs.evening_detective_server.DraftScenarioReq + 84, // 149: crabs.evening_detective_server.EveningDetectiveServer.DeleteScenario:input_type -> crabs.evening_detective_server.DeleteScenarioReq + 86, // 150: crabs.evening_detective_server.EveningDetectiveServer.AddScenarioPlace:input_type -> crabs.evening_detective_server.AddScenarioPlaceReq + 88, // 151: crabs.evening_detective_server.EveningDetectiveServer.UpdateScenarioPlace:input_type -> crabs.evening_detective_server.UpdateScenarioPlaceReq + 90, // 152: crabs.evening_detective_server.EveningDetectiveServer.DeleteScenarioPlace:input_type -> crabs.evening_detective_server.DeleteScenarioPlaceReq + 96, // 153: crabs.evening_detective_server.EveningDetectiveServer.AddScenarioQuestion:input_type -> crabs.evening_detective_server.AddScenarioQuestionReq + 98, // 154: crabs.evening_detective_server.EveningDetectiveServer.UpdateScenarioQuestion:input_type -> crabs.evening_detective_server.UpdateScenarioQuestionReq + 100, // 155: crabs.evening_detective_server.EveningDetectiveServer.DeleteScenarioQuestion:input_type -> crabs.evening_detective_server.DeleteScenarioQuestionReq + 211, // 156: crabs.evening_detective_server.EveningDetectiveServer.UpdateScenarioBackground:input_type -> crabs.evening_detective_server.UpdateScenarioBackgroundReq + 94, // 157: crabs.evening_detective_server.EveningDetectiveServer.UpdateScenarioIntro:input_type -> crabs.evening_detective_server.UpdateScenarioIntroReq + 92, // 158: crabs.evening_detective_server.EveningDetectiveServer.UpdateScenarioConclusion:input_type -> crabs.evening_detective_server.UpdateScenarioConclusionReq + 102, // 159: crabs.evening_detective_server.EveningDetectiveServer.DownloadScenarioArchive:input_type -> crabs.evening_detective_server.DownloadScenarioArchiveReq + 243, // 160: crabs.evening_detective_server.EveningDetectiveServer.UploadScenarioArchive:input_type -> google.api.HttpBody + 104, // 161: crabs.evening_detective_server.EveningDetectiveServer.AddGame:input_type -> crabs.evening_detective_server.AddGameReq + 106, // 162: crabs.evening_detective_server.EveningDetectiveServer.GetGames:input_type -> crabs.evening_detective_server.GetGamesReq + 159, // 163: crabs.evening_detective_server.EveningDetectiveServer.GetGame:input_type -> crabs.evening_detective_server.GetGameReq + 243, // 164: crabs.evening_detective_server.EveningDetectiveServer.ImportGame:input_type -> google.api.HttpBody + 233, // 165: crabs.evening_detective_server.EveningDetectiveServer.DownloadGameResultsPackage:input_type -> crabs.evening_detective_server.GameResultsPackageReq + 234, // 166: crabs.evening_detective_server.EveningDetectiveServer.ApplyGameResultsPackage:input_type -> crabs.evening_detective_server.ApplyGameResultsPackageReq + 111, // 167: crabs.evening_detective_server.EveningDetectiveServer.StartGameExport:input_type -> crabs.evening_detective_server.StartGameExportReq + 112, // 168: crabs.evening_detective_server.EveningDetectiveServer.GetGameExport:input_type -> crabs.evening_detective_server.GetGameExportReq + 213, // 169: crabs.evening_detective_server.EveningDetectiveServer.IssueGameTickets:input_type -> crabs.evening_detective_server.IssueGameTicketsReq + 216, // 170: crabs.evening_detective_server.EveningDetectiveServer.GetTicket:input_type -> crabs.evening_detective_server.GetTicketReq + 218, // 171: crabs.evening_detective_server.EveningDetectiveServer.AcceptTicket:input_type -> crabs.evening_detective_server.AcceptTicketReq + 115, // 172: crabs.evening_detective_server.EveningDetectiveServer.CreateScreenSession:input_type -> crabs.evening_detective_server.CreateScreenSessionReq + 117, // 173: crabs.evening_detective_server.EveningDetectiveServer.GetScreenState:input_type -> crabs.evening_detective_server.GetScreenStateReq + 119, // 174: crabs.evening_detective_server.EveningDetectiveServer.GetGameScreens:input_type -> crabs.evening_detective_server.GetGameScreensReq + 122, // 175: crabs.evening_detective_server.EveningDetectiveServer.GetGameMusic:input_type -> crabs.evening_detective_server.GetGameMusicReq + 124, // 176: crabs.evening_detective_server.EveningDetectiveServer.SetGameMusic:input_type -> crabs.evening_detective_server.SetGameMusicReq + 126, // 177: crabs.evening_detective_server.EveningDetectiveServer.AdvanceScreenMusic:input_type -> crabs.evening_detective_server.AdvanceScreenMusicReq + 128, // 178: crabs.evening_detective_server.EveningDetectiveServer.BindScreen:input_type -> crabs.evening_detective_server.BindScreenReq + 130, // 179: crabs.evening_detective_server.EveningDetectiveServer.UnbindScreen:input_type -> crabs.evening_detective_server.UnbindScreenReq + 132, // 180: crabs.evening_detective_server.EveningDetectiveServer.SetScreenSound:input_type -> crabs.evening_detective_server.SetScreenSoundReq + 135, // 181: crabs.evening_detective_server.EveningDetectiveServer.GetGameMap:input_type -> crabs.evening_detective_server.GetGameMapReq + 137, // 182: crabs.evening_detective_server.EveningDetectiveServer.UpdateGameMapItem:input_type -> crabs.evening_detective_server.UpdateGameMapItemReq + 139, // 183: crabs.evening_detective_server.EveningDetectiveServer.SetScreenMode:input_type -> crabs.evening_detective_server.SetScreenModeReq + 141, // 184: crabs.evening_detective_server.EveningDetectiveServer.SetScreenTimer:input_type -> crabs.evening_detective_server.SetScreenTimerReq + 143, // 185: crabs.evening_detective_server.EveningDetectiveServer.SendScreenCommand:input_type -> crabs.evening_detective_server.SendScreenCommandReq + 147, // 186: crabs.evening_detective_server.EveningDetectiveServer.GetScreenPlan:input_type -> crabs.evening_detective_server.GetScreenPlanReq + 149, // 187: crabs.evening_detective_server.EveningDetectiveServer.SaveScreenPlan:input_type -> crabs.evening_detective_server.SaveScreenPlanReq + 151, // 188: crabs.evening_detective_server.EveningDetectiveServer.SetScreenPlanStep:input_type -> crabs.evening_detective_server.SetScreenPlanStepReq + 153, // 189: crabs.evening_detective_server.EveningDetectiveServer.GetScreenResults:input_type -> crabs.evening_detective_server.GetScreenResultsReq + 159, // 190: crabs.evening_detective_server.EveningDetectiveServer.DownloadTeamQR:input_type -> crabs.evening_detective_server.GetGameReq + 161, // 191: crabs.evening_detective_server.EveningDetectiveServer.GetGameAnswers:input_type -> crabs.evening_detective_server.GetGameAnswersReq + 163, // 192: crabs.evening_detective_server.EveningDetectiveServer.GetGameResults:input_type -> crabs.evening_detective_server.GetGameResultsReq + 168, // 193: crabs.evening_detective_server.EveningDetectiveServer.CreateGameReview:input_type -> crabs.evening_detective_server.CreateGameReviewReq + 170, // 194: crabs.evening_detective_server.EveningDetectiveServer.GetGameReviews:input_type -> crabs.evening_detective_server.GetGameReviewsReq + 171, // 195: crabs.evening_detective_server.EveningDetectiveServer.GetScenarioReviews:input_type -> crabs.evening_detective_server.GetScenarioReviewsReq + 170, // 196: crabs.evening_detective_server.EveningDetectiveServer.GetGameReviewsForModeration:input_type -> crabs.evening_detective_server.GetGameReviewsReq + 172, // 197: crabs.evening_detective_server.EveningDetectiveServer.GetReviewsForModeration:input_type -> crabs.evening_detective_server.GetReviewsForModerationReq + 174, // 198: crabs.evening_detective_server.EveningDetectiveServer.ModerateGameReview:input_type -> crabs.evening_detective_server.ModerateGameReviewReq + 176, // 199: crabs.evening_detective_server.EveningDetectiveServer.UpdateGame:input_type -> crabs.evening_detective_server.UpdateGameReq + 178, // 200: crabs.evening_detective_server.EveningDetectiveServer.StartGame:input_type -> crabs.evening_detective_server.StartGameReq + 180, // 201: crabs.evening_detective_server.EveningDetectiveServer.PauseGame:input_type -> crabs.evening_detective_server.PauseGameReq + 182, // 202: crabs.evening_detective_server.EveningDetectiveServer.PlayGame:input_type -> crabs.evening_detective_server.PlayGameReq + 184, // 203: crabs.evening_detective_server.EveningDetectiveServer.FinishGame:input_type -> crabs.evening_detective_server.FinishGameReq + 186, // 204: crabs.evening_detective_server.EveningDetectiveServer.ResetGame:input_type -> crabs.evening_detective_server.ResetGameReq + 188, // 205: crabs.evening_detective_server.EveningDetectiveServer.DeleteGame:input_type -> crabs.evening_detective_server.DeleteGameReq + 190, // 206: crabs.evening_detective_server.EveningDetectiveServer.AddTeam:input_type -> crabs.evening_detective_server.AddTeamReq + 192, // 207: crabs.evening_detective_server.EveningDetectiveServer.UpdateTeam:input_type -> crabs.evening_detective_server.UpdateTeamReq + 194, // 208: crabs.evening_detective_server.EveningDetectiveServer.DeleteTeam:input_type -> crabs.evening_detective_server.DeleteTeamReq + 196, // 209: crabs.evening_detective_server.EveningDetectiveServer.GiveTeamApplications:input_type -> crabs.evening_detective_server.GiveTeamApplicationsReq + 198, // 210: crabs.evening_detective_server.EveningDetectiveServer.GetTeamStory:input_type -> crabs.evening_detective_server.GetTeamStoryReq + 201, // 211: crabs.evening_detective_server.EveningDetectiveServer.AddTeamAction:input_type -> crabs.evening_detective_server.AddTeamActionReq + 203, // 212: crabs.evening_detective_server.EveningDetectiveServer.DeleteLastTeamAction:input_type -> crabs.evening_detective_server.DeleteLastTeamActionReq + 205, // 213: crabs.evening_detective_server.EveningDetectiveServer.GetTeamAnswers:input_type -> crabs.evening_detective_server.GetTeamAnswersReq + 207, // 214: crabs.evening_detective_server.EveningDetectiveServer.SubmitTeamAnswers:input_type -> crabs.evening_detective_server.SubmitTeamAnswersReq + 209, // 215: crabs.evening_detective_server.EveningDetectiveServer.SetTeamAnswerScore:input_type -> crabs.evening_detective_server.SetTeamAnswerScoreReq + 237, // 216: crabs.evening_detective_server.EveningDetectiveServer.DeviceHeartbeat:output_type -> crabs.evening_detective_server.DeviceHeartbeatRsp + 239, // 217: crabs.evening_detective_server.EveningDetectiveServer.GetDevices:output_type -> crabs.evening_detective_server.GetDevicesRsp + 224, // 218: crabs.evening_detective_server.EveningDetectiveServer.GetTasks:output_type -> crabs.evening_detective_server.GetTasksRsp + 226, // 219: crabs.evening_detective_server.EveningDetectiveServer.GetTaskAssignees:output_type -> crabs.evening_detective_server.GetTaskAssigneesRsp + 231, // 220: crabs.evening_detective_server.EveningDetectiveServer.CreateTask:output_type -> crabs.evening_detective_server.TaskRsp + 231, // 221: crabs.evening_detective_server.EveningDetectiveServer.UpdateTask:output_type -> crabs.evening_detective_server.TaskRsp + 231, // 222: crabs.evening_detective_server.EveningDetectiveServer.SetTaskStatus:output_type -> crabs.evening_detective_server.TaskRsp + 232, // 223: crabs.evening_detective_server.EveningDetectiveServer.DeleteTask:output_type -> crabs.evening_detective_server.DeleteTaskRsp + 157, // 224: crabs.evening_detective_server.EveningDetectiveServer.GetParticipation:output_type -> crabs.evening_detective_server.ParticipationRsp + 157, // 225: crabs.evening_detective_server.EveningDetectiveServer.AnswerParticipation:output_type -> crabs.evening_detective_server.ParticipationRsp + 5, // 226: crabs.evening_detective_server.EveningDetectiveServer.Ping:output_type -> crabs.evening_detective_server.PingRsp + 7, // 227: crabs.evening_detective_server.EveningDetectiveServer.Echo:output_type -> crabs.evening_detective_server.EchoRsp + 9, // 228: crabs.evening_detective_server.EveningDetectiveServer.Signup:output_type -> crabs.evening_detective_server.SignupRsp + 11, // 229: crabs.evening_detective_server.EveningDetectiveServer.RefreshPassword:output_type -> crabs.evening_detective_server.RefreshPasswordRsp + 13, // 230: crabs.evening_detective_server.EveningDetectiveServer.Login:output_type -> crabs.evening_detective_server.LoginRsp + 15, // 231: crabs.evening_detective_server.EveningDetectiveServer.Refresh:output_type -> crabs.evening_detective_server.RefreshRsp + 17, // 232: crabs.evening_detective_server.EveningDetectiveServer.DeleteAccount:output_type -> crabs.evening_detective_server.DeleteAccountRsp + 19, // 233: crabs.evening_detective_server.EveningDetectiveServer.GetTerms:output_type -> crabs.evening_detective_server.GetTermsRsp + 21, // 234: crabs.evening_detective_server.EveningDetectiveServer.GetPrivacy:output_type -> crabs.evening_detective_server.GetPrivacyRsp + 23, // 235: crabs.evening_detective_server.EveningDetectiveServer.GetConsent:output_type -> crabs.evening_detective_server.GetConsentRsp + 25, // 236: crabs.evening_detective_server.EveningDetectiveServer.GetUsers:output_type -> crabs.evening_detective_server.GetUsersRsp + 28, // 237: crabs.evening_detective_server.EveningDetectiveServer.GetUserByID:output_type -> crabs.evening_detective_server.GetUserByIDRsp + 30, // 238: crabs.evening_detective_server.EveningDetectiveServer.GetMe:output_type -> crabs.evening_detective_server.GetMeRsp + 32, // 239: crabs.evening_detective_server.EveningDetectiveServer.ChangeUserPassword:output_type -> crabs.evening_detective_server.ChangeUserPasswordRsp + 34, // 240: crabs.evening_detective_server.EveningDetectiveServer.AddUserRole:output_type -> crabs.evening_detective_server.AddUserRoleRsp + 36, // 241: crabs.evening_detective_server.EveningDetectiveServer.DeleteUserRole:output_type -> crabs.evening_detective_server.DeleteUserRoleRsp + 38, // 242: crabs.evening_detective_server.EveningDetectiveServer.GetPermissions:output_type -> crabs.evening_detective_server.GetPermissionsRsp + 40, // 243: crabs.evening_detective_server.EveningDetectiveServer.UploadFile:output_type -> crabs.evening_detective_server.UploadFileRsp + 243, // 244: crabs.evening_detective_server.EveningDetectiveServer.DownloadFile:output_type -> google.api.HttpBody + 45, // 245: crabs.evening_detective_server.EveningDetectiveServer.GetPlaylists:output_type -> crabs.evening_detective_server.GetPlaylistsRsp + 47, // 246: crabs.evening_detective_server.EveningDetectiveServer.CreatePlaylist:output_type -> crabs.evening_detective_server.CreatePlaylistRsp + 49, // 247: crabs.evening_detective_server.EveningDetectiveServer.UpdatePlaylist:output_type -> crabs.evening_detective_server.UpdatePlaylistRsp + 51, // 248: crabs.evening_detective_server.EveningDetectiveServer.DeletePlaylist:output_type -> crabs.evening_detective_server.DeletePlaylistRsp + 53, // 249: crabs.evening_detective_server.EveningDetectiveServer.UploadPlaylistTrack:output_type -> crabs.evening_detective_server.UploadPlaylistTrackRsp + 55, // 250: crabs.evening_detective_server.EveningDetectiveServer.DeletePlaylistTrack:output_type -> crabs.evening_detective_server.DeletePlaylistTrackRsp + 57, // 251: crabs.evening_detective_server.EveningDetectiveServer.SetScenarioPlaylist:output_type -> crabs.evening_detective_server.SetScenarioPlaylistRsp + 59, // 252: crabs.evening_detective_server.EveningDetectiveServer.AddScenario:output_type -> crabs.evening_detective_server.AddScenarioRsp + 61, // 253: crabs.evening_detective_server.EveningDetectiveServer.GetMyScenarios:output_type -> crabs.evening_detective_server.GetMyScenariosRsp + 63, // 254: crabs.evening_detective_server.EveningDetectiveServer.GetScenariosCatalog:output_type -> crabs.evening_detective_server.GetScenariosCatalogRsp + 65, // 255: crabs.evening_detective_server.EveningDetectiveServer.GetFullScenario:output_type -> crabs.evening_detective_server.GetScenarioRsp + 65, // 256: crabs.evening_detective_server.EveningDetectiveServer.GetScenario:output_type -> crabs.evening_detective_server.GetScenarioRsp + 79, // 257: crabs.evening_detective_server.EveningDetectiveServer.UpdateScenario:output_type -> crabs.evening_detective_server.UpdateScenarioRsp + 81, // 258: crabs.evening_detective_server.EveningDetectiveServer.PublicScenario:output_type -> crabs.evening_detective_server.PublicScenarioRsp + 83, // 259: crabs.evening_detective_server.EveningDetectiveServer.DraftScenario:output_type -> crabs.evening_detective_server.DraftScenarioRsp + 85, // 260: crabs.evening_detective_server.EveningDetectiveServer.DeleteScenario:output_type -> crabs.evening_detective_server.DeleteScenarioRsp + 87, // 261: crabs.evening_detective_server.EveningDetectiveServer.AddScenarioPlace:output_type -> crabs.evening_detective_server.AddScenarioPlaceRsp + 89, // 262: crabs.evening_detective_server.EveningDetectiveServer.UpdateScenarioPlace:output_type -> crabs.evening_detective_server.UpdateScenarioPlaceRsp + 91, // 263: crabs.evening_detective_server.EveningDetectiveServer.DeleteScenarioPlace:output_type -> crabs.evening_detective_server.DeleteScenarioPlaceRsp + 97, // 264: crabs.evening_detective_server.EveningDetectiveServer.AddScenarioQuestion:output_type -> crabs.evening_detective_server.AddScenarioQuestionRsp + 99, // 265: crabs.evening_detective_server.EveningDetectiveServer.UpdateScenarioQuestion:output_type -> crabs.evening_detective_server.UpdateScenarioQuestionRsp + 101, // 266: crabs.evening_detective_server.EveningDetectiveServer.DeleteScenarioQuestion:output_type -> crabs.evening_detective_server.DeleteScenarioQuestionRsp + 212, // 267: crabs.evening_detective_server.EveningDetectiveServer.UpdateScenarioBackground:output_type -> crabs.evening_detective_server.UpdateScenarioBackgroundRsp + 95, // 268: crabs.evening_detective_server.EveningDetectiveServer.UpdateScenarioIntro:output_type -> crabs.evening_detective_server.UpdateScenarioIntroRsp + 93, // 269: crabs.evening_detective_server.EveningDetectiveServer.UpdateScenarioConclusion:output_type -> crabs.evening_detective_server.UpdateScenarioConclusionRsp + 243, // 270: crabs.evening_detective_server.EveningDetectiveServer.DownloadScenarioArchive:output_type -> google.api.HttpBody + 103, // 271: crabs.evening_detective_server.EveningDetectiveServer.UploadScenarioArchive:output_type -> crabs.evening_detective_server.UploadScenarioArchiveRsp + 105, // 272: crabs.evening_detective_server.EveningDetectiveServer.AddGame:output_type -> crabs.evening_detective_server.AddGameRsp + 107, // 273: crabs.evening_detective_server.EveningDetectiveServer.GetGames:output_type -> crabs.evening_detective_server.GetGamesRsp + 160, // 274: crabs.evening_detective_server.EveningDetectiveServer.GetGame:output_type -> crabs.evening_detective_server.GetGameRsp + 110, // 275: crabs.evening_detective_server.EveningDetectiveServer.ImportGame:output_type -> crabs.evening_detective_server.GameImportRsp + 243, // 276: crabs.evening_detective_server.EveningDetectiveServer.DownloadGameResultsPackage:output_type -> google.api.HttpBody + 235, // 277: crabs.evening_detective_server.EveningDetectiveServer.ApplyGameResultsPackage:output_type -> crabs.evening_detective_server.GameResultsPackageRsp + 113, // 278: crabs.evening_detective_server.EveningDetectiveServer.StartGameExport:output_type -> crabs.evening_detective_server.GameExportRsp + 113, // 279: crabs.evening_detective_server.EveningDetectiveServer.GetGameExport:output_type -> crabs.evening_detective_server.GameExportRsp + 214, // 280: crabs.evening_detective_server.EveningDetectiveServer.IssueGameTickets:output_type -> crabs.evening_detective_server.IssueGameTicketsRsp + 217, // 281: crabs.evening_detective_server.EveningDetectiveServer.GetTicket:output_type -> crabs.evening_detective_server.GetTicketRsp + 219, // 282: crabs.evening_detective_server.EveningDetectiveServer.AcceptTicket:output_type -> crabs.evening_detective_server.AcceptTicketRsp + 116, // 283: crabs.evening_detective_server.EveningDetectiveServer.CreateScreenSession:output_type -> crabs.evening_detective_server.CreateScreenSessionRsp + 118, // 284: crabs.evening_detective_server.EveningDetectiveServer.GetScreenState:output_type -> crabs.evening_detective_server.GetScreenStateRsp + 120, // 285: crabs.evening_detective_server.EveningDetectiveServer.GetGameScreens:output_type -> crabs.evening_detective_server.GetGameScreensRsp + 123, // 286: crabs.evening_detective_server.EveningDetectiveServer.GetGameMusic:output_type -> crabs.evening_detective_server.GetGameMusicRsp + 125, // 287: crabs.evening_detective_server.EveningDetectiveServer.SetGameMusic:output_type -> crabs.evening_detective_server.SetGameMusicRsp + 127, // 288: crabs.evening_detective_server.EveningDetectiveServer.AdvanceScreenMusic:output_type -> crabs.evening_detective_server.AdvanceScreenMusicRsp + 129, // 289: crabs.evening_detective_server.EveningDetectiveServer.BindScreen:output_type -> crabs.evening_detective_server.BindScreenRsp + 131, // 290: crabs.evening_detective_server.EveningDetectiveServer.UnbindScreen:output_type -> crabs.evening_detective_server.UnbindScreenRsp + 133, // 291: crabs.evening_detective_server.EveningDetectiveServer.SetScreenSound:output_type -> crabs.evening_detective_server.SetScreenSoundRsp + 136, // 292: crabs.evening_detective_server.EveningDetectiveServer.GetGameMap:output_type -> crabs.evening_detective_server.GetGameMapRsp + 138, // 293: crabs.evening_detective_server.EveningDetectiveServer.UpdateGameMapItem:output_type -> crabs.evening_detective_server.UpdateGameMapItemRsp + 140, // 294: crabs.evening_detective_server.EveningDetectiveServer.SetScreenMode:output_type -> crabs.evening_detective_server.SetScreenModeRsp + 142, // 295: crabs.evening_detective_server.EveningDetectiveServer.SetScreenTimer:output_type -> crabs.evening_detective_server.SetScreenTimerRsp + 144, // 296: crabs.evening_detective_server.EveningDetectiveServer.SendScreenCommand:output_type -> crabs.evening_detective_server.SendScreenCommandRsp + 148, // 297: crabs.evening_detective_server.EveningDetectiveServer.GetScreenPlan:output_type -> crabs.evening_detective_server.GetScreenPlanRsp + 150, // 298: crabs.evening_detective_server.EveningDetectiveServer.SaveScreenPlan:output_type -> crabs.evening_detective_server.SaveScreenPlanRsp + 152, // 299: crabs.evening_detective_server.EveningDetectiveServer.SetScreenPlanStep:output_type -> crabs.evening_detective_server.SetScreenPlanStepRsp + 154, // 300: crabs.evening_detective_server.EveningDetectiveServer.GetScreenResults:output_type -> crabs.evening_detective_server.GetScreenResultsRsp + 243, // 301: crabs.evening_detective_server.EveningDetectiveServer.DownloadTeamQR:output_type -> google.api.HttpBody + 162, // 302: crabs.evening_detective_server.EveningDetectiveServer.GetGameAnswers:output_type -> crabs.evening_detective_server.GetGameAnswersRsp + 166, // 303: crabs.evening_detective_server.EveningDetectiveServer.GetGameResults:output_type -> crabs.evening_detective_server.GetGameResultsRsp + 169, // 304: crabs.evening_detective_server.EveningDetectiveServer.CreateGameReview:output_type -> crabs.evening_detective_server.CreateGameReviewRsp + 173, // 305: crabs.evening_detective_server.EveningDetectiveServer.GetGameReviews:output_type -> crabs.evening_detective_server.GetGameReviewsRsp + 173, // 306: crabs.evening_detective_server.EveningDetectiveServer.GetScenarioReviews:output_type -> crabs.evening_detective_server.GetGameReviewsRsp + 173, // 307: crabs.evening_detective_server.EveningDetectiveServer.GetGameReviewsForModeration:output_type -> crabs.evening_detective_server.GetGameReviewsRsp + 173, // 308: crabs.evening_detective_server.EveningDetectiveServer.GetReviewsForModeration:output_type -> crabs.evening_detective_server.GetGameReviewsRsp + 175, // 309: crabs.evening_detective_server.EveningDetectiveServer.ModerateGameReview:output_type -> crabs.evening_detective_server.ModerateGameReviewRsp + 177, // 310: crabs.evening_detective_server.EveningDetectiveServer.UpdateGame:output_type -> crabs.evening_detective_server.UpdateGameRsp + 179, // 311: crabs.evening_detective_server.EveningDetectiveServer.StartGame:output_type -> crabs.evening_detective_server.StartGameRsp + 181, // 312: crabs.evening_detective_server.EveningDetectiveServer.PauseGame:output_type -> crabs.evening_detective_server.PauseGameRsp + 183, // 313: crabs.evening_detective_server.EveningDetectiveServer.PlayGame:output_type -> crabs.evening_detective_server.PlayGameRsp + 185, // 314: crabs.evening_detective_server.EveningDetectiveServer.FinishGame:output_type -> crabs.evening_detective_server.FinishGameRsp + 187, // 315: crabs.evening_detective_server.EveningDetectiveServer.ResetGame:output_type -> crabs.evening_detective_server.ResetGameRsp + 189, // 316: crabs.evening_detective_server.EveningDetectiveServer.DeleteGame:output_type -> crabs.evening_detective_server.DeleteGameRsp + 191, // 317: crabs.evening_detective_server.EveningDetectiveServer.AddTeam:output_type -> crabs.evening_detective_server.AddTeamRsp + 193, // 318: crabs.evening_detective_server.EveningDetectiveServer.UpdateTeam:output_type -> crabs.evening_detective_server.UpdateTeamRsp + 195, // 319: crabs.evening_detective_server.EveningDetectiveServer.DeleteTeam:output_type -> crabs.evening_detective_server.DeleteTeamRsp + 197, // 320: crabs.evening_detective_server.EveningDetectiveServer.GiveTeamApplications:output_type -> crabs.evening_detective_server.GiveTeamApplicationsRsp + 199, // 321: crabs.evening_detective_server.EveningDetectiveServer.GetTeamStory:output_type -> crabs.evening_detective_server.GetTeamStoryRsp + 202, // 322: crabs.evening_detective_server.EveningDetectiveServer.AddTeamAction:output_type -> crabs.evening_detective_server.AddTeamActionRsp + 204, // 323: crabs.evening_detective_server.EveningDetectiveServer.DeleteLastTeamAction:output_type -> crabs.evening_detective_server.DeleteLastTeamActionRsp + 206, // 324: crabs.evening_detective_server.EveningDetectiveServer.GetTeamAnswers:output_type -> crabs.evening_detective_server.GetTeamAnswersRsp + 208, // 325: crabs.evening_detective_server.EveningDetectiveServer.SubmitTeamAnswers:output_type -> crabs.evening_detective_server.SubmitTeamAnswersRsp + 210, // 326: crabs.evening_detective_server.EveningDetectiveServer.SetTeamAnswerScore:output_type -> crabs.evening_detective_server.SetTeamAnswerScoreRsp + 216, // [216:327] is the sub-list for method output_type + 105, // [105:216] is the sub-list for method input_type + 105, // [105:105] is the sub-list for extension type_name + 105, // [105:105] is the sub-list for extension extendee + 0, // [0:105] is the sub-list for field type_name } func init() { file_main_proto_init() } @@ -13890,13 +14395,17 @@ func file_main_proto_init() { file_main_proto_msgTypes[217].OneofWrappers = []any{} file_main_proto_msgTypes[223].OneofWrappers = []any{} file_main_proto_msgTypes[224].OneofWrappers = []any{} + file_main_proto_msgTypes[232].OneofWrappers = []any{} + file_main_proto_msgTypes[233].OneofWrappers = []any{} + file_main_proto_msgTypes[234].OneofWrappers = []any{} + file_main_proto_msgTypes[237].OneofWrappers = []any{} type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_main_proto_rawDesc), len(file_main_proto_rawDesc)), NumEnums: 4, - NumMessages: 232, + NumMessages: 238, NumExtensions: 0, NumServices: 1, }, diff --git a/proto/main.pb.gw.go b/proto/main.pb.gw.go index abdacf0..2fc5440 100644 --- a/proto/main.pb.gw.go +++ b/proto/main.pb.gw.go @@ -36,6 +36,68 @@ var ( _ = metadata.Join ) +func request_EveningDetectiveServer_DeviceHeartbeat_0(ctx context.Context, marshaler runtime.Marshaler, client EveningDetectiveServerClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var ( + protoReq DeviceHeartbeatReq + metadata runtime.ServerMetadata + ) + if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if req.Body != nil { + _, _ = io.Copy(io.Discard, req.Body) + } + msg, err := client.DeviceHeartbeat(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err +} + +func local_request_EveningDetectiveServer_DeviceHeartbeat_0(ctx context.Context, marshaler runtime.Marshaler, server EveningDetectiveServerServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var ( + protoReq DeviceHeartbeatReq + metadata runtime.ServerMetadata + ) + if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + msg, err := server.DeviceHeartbeat(ctx, &protoReq) + return msg, metadata, err +} + +var filter_EveningDetectiveServer_GetDevices_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} + +func request_EveningDetectiveServer_GetDevices_0(ctx context.Context, marshaler runtime.Marshaler, client EveningDetectiveServerClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var ( + protoReq GetDevicesReq + metadata runtime.ServerMetadata + ) + if req.Body != nil { + _, _ = io.Copy(io.Discard, req.Body) + } + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_EveningDetectiveServer_GetDevices_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + msg, err := client.GetDevices(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err +} + +func local_request_EveningDetectiveServer_GetDevices_0(ctx context.Context, marshaler runtime.Marshaler, server EveningDetectiveServerServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var ( + protoReq GetDevicesReq + metadata runtime.ServerMetadata + ) + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_EveningDetectiveServer_GetDevices_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + msg, err := server.GetDevices(ctx, &protoReq) + return msg, metadata, err +} + func request_EveningDetectiveServer_GetTasks_0(ctx context.Context, marshaler runtime.Marshaler, client EveningDetectiveServerClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var ( protoReq GetTasksReq @@ -4235,6 +4297,46 @@ func local_request_EveningDetectiveServer_SetTeamAnswerScore_0(ctx context.Conte // Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterEveningDetectiveServerHandlerFromEndpoint instead. // GRPC interceptors will not work for this type of registration. To use interceptors, you must use the "runtime.WithMiddlewares" option in the "runtime.NewServeMux" call. func RegisterEveningDetectiveServerHandlerServer(ctx context.Context, mux *runtime.ServeMux, server EveningDetectiveServerServer) error { + mux.Handle(http.MethodPost, pattern_EveningDetectiveServer_DeviceHeartbeat_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/crabs.evening_detective_server.EveningDetectiveServer/DeviceHeartbeat", runtime.WithHTTPPathPattern("/api/devices/heartbeat")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_EveningDetectiveServer_DeviceHeartbeat_0(annotatedContext, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + forward_EveningDetectiveServer_DeviceHeartbeat_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + }) + mux.Handle(http.MethodGet, pattern_EveningDetectiveServer_GetDevices_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/crabs.evening_detective_server.EveningDetectiveServer/GetDevices", runtime.WithHTTPPathPattern("/api/devices")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_EveningDetectiveServer_GetDevices_0(annotatedContext, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + forward_EveningDetectiveServer_GetDevices_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + }) mux.Handle(http.MethodGet, pattern_EveningDetectiveServer_GetTasks_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -6455,6 +6557,40 @@ func RegisterEveningDetectiveServerHandler(ctx context.Context, mux *runtime.Ser // doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in // "EveningDetectiveServerClient" to call the correct interceptors. This client ignores the HTTP middlewares. func RegisterEveningDetectiveServerHandlerClient(ctx context.Context, mux *runtime.ServeMux, client EveningDetectiveServerClient) error { + mux.Handle(http.MethodPost, pattern_EveningDetectiveServer_DeviceHeartbeat_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) + annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/crabs.evening_detective_server.EveningDetectiveServer/DeviceHeartbeat", runtime.WithHTTPPathPattern("/api/devices/heartbeat")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_EveningDetectiveServer_DeviceHeartbeat_0(annotatedContext, inboundMarshaler, client, req, pathParams) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + forward_EveningDetectiveServer_DeviceHeartbeat_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + }) + mux.Handle(http.MethodGet, pattern_EveningDetectiveServer_GetDevices_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) + annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/crabs.evening_detective_server.EveningDetectiveServer/GetDevices", runtime.WithHTTPPathPattern("/api/devices")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_EveningDetectiveServer_GetDevices_0(annotatedContext, inboundMarshaler, client, req, pathParams) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + forward_EveningDetectiveServer_GetDevices_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + }) mux.Handle(http.MethodGet, pattern_EveningDetectiveServer_GetTasks_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -8312,6 +8448,8 @@ func RegisterEveningDetectiveServerHandlerClient(ctx context.Context, mux *runti } var ( + pattern_EveningDetectiveServer_DeviceHeartbeat_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "devices", "heartbeat"}, "")) + pattern_EveningDetectiveServer_GetDevices_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"api", "devices"}, "")) pattern_EveningDetectiveServer_GetTasks_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"api", "tasks"}, "")) pattern_EveningDetectiveServer_GetTaskAssignees_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "tasks", "assignees"}, "")) pattern_EveningDetectiveServer_CreateTask_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"api", "tasks"}, "")) @@ -8424,6 +8562,8 @@ var ( ) var ( + forward_EveningDetectiveServer_DeviceHeartbeat_0 = runtime.ForwardResponseMessage + forward_EveningDetectiveServer_GetDevices_0 = runtime.ForwardResponseMessage forward_EveningDetectiveServer_GetTasks_0 = runtime.ForwardResponseMessage forward_EveningDetectiveServer_GetTaskAssignees_0 = runtime.ForwardResponseMessage forward_EveningDetectiveServer_CreateTask_0 = runtime.ForwardResponseMessage diff --git a/proto/main_grpc.pb.go b/proto/main_grpc.pb.go index d7e2710..7a91f95 100644 --- a/proto/main_grpc.pb.go +++ b/proto/main_grpc.pb.go @@ -26,6 +26,8 @@ import ( const _ = grpc.SupportPackageIsVersion9 const ( + EveningDetectiveServer_DeviceHeartbeat_FullMethodName = "/crabs.evening_detective_server.EveningDetectiveServer/DeviceHeartbeat" + EveningDetectiveServer_GetDevices_FullMethodName = "/crabs.evening_detective_server.EveningDetectiveServer/GetDevices" EveningDetectiveServer_GetTasks_FullMethodName = "/crabs.evening_detective_server.EveningDetectiveServer/GetTasks" EveningDetectiveServer_GetTaskAssignees_FullMethodName = "/crabs.evening_detective_server.EveningDetectiveServer/GetTaskAssignees" EveningDetectiveServer_CreateTask_FullMethodName = "/crabs.evening_detective_server.EveningDetectiveServer/CreateTask" @@ -141,6 +143,8 @@ const ( // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. type EveningDetectiveServerClient interface { + DeviceHeartbeat(ctx context.Context, in *DeviceHeartbeatReq, opts ...grpc.CallOption) (*DeviceHeartbeatRsp, error) + GetDevices(ctx context.Context, in *GetDevicesReq, opts ...grpc.CallOption) (*GetDevicesRsp, error) GetTasks(ctx context.Context, in *GetTasksReq, opts ...grpc.CallOption) (*GetTasksRsp, error) GetTaskAssignees(ctx context.Context, in *GetTaskAssigneesReq, opts ...grpc.CallOption) (*GetTaskAssigneesRsp, error) CreateTask(ctx context.Context, in *CreateTaskReq, opts ...grpc.CallOption) (*TaskRsp, error) @@ -272,6 +276,26 @@ func NewEveningDetectiveServerClient(cc grpc.ClientConnInterface) EveningDetecti return &eveningDetectiveServerClient{cc} } +func (c *eveningDetectiveServerClient) DeviceHeartbeat(ctx context.Context, in *DeviceHeartbeatReq, opts ...grpc.CallOption) (*DeviceHeartbeatRsp, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(DeviceHeartbeatRsp) + err := c.cc.Invoke(ctx, EveningDetectiveServer_DeviceHeartbeat_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *eveningDetectiveServerClient) GetDevices(ctx context.Context, in *GetDevicesReq, opts ...grpc.CallOption) (*GetDevicesRsp, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(GetDevicesRsp) + err := c.cc.Invoke(ctx, EveningDetectiveServer_GetDevices_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *eveningDetectiveServerClient) GetTasks(ctx context.Context, in *GetTasksReq, opts ...grpc.CallOption) (*GetTasksRsp, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(GetTasksRsp) @@ -1366,6 +1390,8 @@ func (c *eveningDetectiveServerClient) SetTeamAnswerScore(ctx context.Context, i // All implementations must embed UnimplementedEveningDetectiveServerServer // for forward compatibility. type EveningDetectiveServerServer interface { + DeviceHeartbeat(context.Context, *DeviceHeartbeatReq) (*DeviceHeartbeatRsp, error) + GetDevices(context.Context, *GetDevicesReq) (*GetDevicesRsp, error) GetTasks(context.Context, *GetTasksReq) (*GetTasksRsp, error) GetTaskAssignees(context.Context, *GetTaskAssigneesReq) (*GetTaskAssigneesRsp, error) CreateTask(context.Context, *CreateTaskReq) (*TaskRsp, error) @@ -1497,6 +1523,12 @@ type EveningDetectiveServerServer interface { // pointer dereference when methods are called. type UnimplementedEveningDetectiveServerServer struct{} +func (UnimplementedEveningDetectiveServerServer) DeviceHeartbeat(context.Context, *DeviceHeartbeatReq) (*DeviceHeartbeatRsp, error) { + return nil, status.Error(codes.Unimplemented, "method DeviceHeartbeat not implemented") +} +func (UnimplementedEveningDetectiveServerServer) GetDevices(context.Context, *GetDevicesReq) (*GetDevicesRsp, error) { + return nil, status.Error(codes.Unimplemented, "method GetDevices not implemented") +} func (UnimplementedEveningDetectiveServerServer) GetTasks(context.Context, *GetTasksReq) (*GetTasksRsp, error) { return nil, status.Error(codes.Unimplemented, "method GetTasks not implemented") } @@ -1846,6 +1878,42 @@ func RegisterEveningDetectiveServerServer(s grpc.ServiceRegistrar, srv EveningDe s.RegisterService(&EveningDetectiveServer_ServiceDesc, srv) } +func _EveningDetectiveServer_DeviceHeartbeat_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DeviceHeartbeatReq) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(EveningDetectiveServerServer).DeviceHeartbeat(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: EveningDetectiveServer_DeviceHeartbeat_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(EveningDetectiveServerServer).DeviceHeartbeat(ctx, req.(*DeviceHeartbeatReq)) + } + return interceptor(ctx, in, info, handler) +} + +func _EveningDetectiveServer_GetDevices_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetDevicesReq) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(EveningDetectiveServerServer).GetDevices(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: EveningDetectiveServer_GetDevices_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(EveningDetectiveServerServer).GetDevices(ctx, req.(*GetDevicesReq)) + } + return interceptor(ctx, in, info, handler) +} + func _EveningDetectiveServer_GetTasks_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(GetTasksReq) if err := dec(in); err != nil { @@ -3815,6 +3883,14 @@ var EveningDetectiveServer_ServiceDesc = grpc.ServiceDesc{ ServiceName: "crabs.evening_detective_server.EveningDetectiveServer", HandlerType: (*EveningDetectiveServerServer)(nil), Methods: []grpc.MethodDesc{ + { + MethodName: "DeviceHeartbeat", + Handler: _EveningDetectiveServer_DeviceHeartbeat_Handler, + }, + { + MethodName: "GetDevices", + Handler: _EveningDetectiveServer_GetDevices_Handler, + }, { MethodName: "GetTasks", Handler: _EveningDetectiveServer_GetTasks_Handler,