diff --git a/api/main.proto b/api/main.proto index 1a3ee65..df9eb97 100644 --- a/api/main.proto +++ b/api/main.proto @@ -2420,6 +2420,7 @@ message GetDevicesReq { optional int32 gameID = 1; } message GetDevicesRsp { repeated VenueDevice devices = 1; google.protobuf.Timestamp serverTime = 2; + VenueWiFi wifi = 3; // Entire venue, independent of the game filter. } message VenueDevice { @@ -2442,3 +2443,18 @@ message DeviceConnection { optional int32 screenID = 9; string kind = 10; // browser, organizer, team, screen } + +message VenueWiFi { + bool enabled = 1; + google.protobuf.Timestamp collectedAt = 2; + bool stale = 3; + bool hasError = 4; + repeated WiFiClient clients = 5; +} + +message WiFiClient { + string IP = 1; + string MAC = 2; + string band = 3; + optional int32 signal = 4; // dBm; absent when the router does not report it. +} diff --git a/cmd/evening_detective_server/cudy.go b/cmd/evening_detective_server/cudy.go new file mode 100644 index 0000000..561292a --- /dev/null +++ b/cmd/evening_detective_server/cudy.go @@ -0,0 +1,88 @@ +package main + +import ( + "context" + "encoding/json" + "log" + "os" + "os/signal" + "path/filepath" + "strings" + "syscall" + "time" + + "evening_detective_server/internal/modules/cudy" + "evening_detective_server/internal/services/wifi_service" +) + +func collectCudy() { + path := os.Getenv("CUDY_SNAPSHOT_FILE") + if path == "" { + log.Fatal("CUDY_SNAPSHOT_FILE is required") + } + ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) + defer stop() + snapshot := cudy.Snapshot{} + var reader cudy.Reader + passwordFile := os.Getenv("CUDY_PASSWORD_FILE") + if passwordFile != "" { + password, err := os.ReadFile(passwordFile) + if err != nil { + log.Fatal("cannot read Cudy credential file") + } + base := os.Getenv("CUDY_URL") + if base == "" { + base = "http://192.168.10.1" + } + reader, err = cudy.NewReader(base, strings.TrimRight(string(password), "\r\n")) + if err != nil { + log.Fatal("invalid Cudy configuration") + } + snapshot, _ = wifi_service.New(path).Get(ctx) + snapshot.Enabled = true + } + timer := time.NewTicker(10 * time.Second) + defer timer.Stop() + for { + if reader != nil { + clients, err := reader.Read(ctx) + snapshot.HasError = err != nil + if err == nil { + snapshot.Clients = clients + snapshot.CollectedAt = time.Now().UTC() + } + } + if err := writeCudySnapshot(path, snapshot); err != nil { + log.Print("cannot publish Cudy snapshot") + } + select { + case <-ctx.Done(): + return + case <-timer.C: + } + } +} + +func writeCudySnapshot(path string, snapshot cudy.Snapshot) error { + data, err := json.Marshal(snapshot) + if err != nil { + return err + } + file, err := os.CreateTemp(filepath.Dir(path), ".cudy-") + if err != nil { + return err + } + name := file.Name() + defer os.Remove(name) + if err = file.Chmod(0644); err == nil { + _, err = file.Write(data) + } + closeErr := file.Close() + if err != nil { + return err + } + if closeErr != nil { + return closeErr + } + return os.Rename(name, path) +} diff --git a/cmd/evening_detective_server/cudy_test.go b/cmd/evening_detective_server/cudy_test.go new file mode 100644 index 0000000..a40b583 --- /dev/null +++ b/cmd/evening_detective_server/cudy_test.go @@ -0,0 +1,34 @@ +package main + +import ( + "encoding/json" + "os" + "path/filepath" + "testing" + "time" + + "evening_detective_server/internal/modules/cudy" +) + +func TestAtomicCudySnapshot(t *testing.T) { + dir := t.TempDir() + path := filepath.Join(dir, "state.json") + for _, failed := range []bool{false, true} { + snapshot := cudy.Snapshot{Enabled: true, CollectedAt: time.Unix(123, 0), HasError: failed, Clients: []cudy.Client{{MAC: "AA:BB:CC:DD:EE:01"}}} + if err := writeCudySnapshot(path, snapshot); err != nil { + t.Fatal(err) + } + data, err := os.ReadFile(path) + if err != nil { + t.Fatal(err) + } + var result cudy.Snapshot + if json.Unmarshal(data, &result) != nil || result.HasError != failed || len(result.Clients) != 1 { + t.Fatal("snapshot corrupted") + } + } + files, err := os.ReadDir(dir) + if err != nil || len(files) != 1 { + t.Fatal("temporary snapshots left behind") + } +} diff --git a/cmd/evening_detective_server/main.go b/cmd/evening_detective_server/main.go index e3cd81c..4490d37 100644 --- a/cmd/evening_detective_server/main.go +++ b/cmd/evening_detective_server/main.go @@ -56,6 +56,7 @@ import ( "evening_detective_server/internal/services/ticket_service" "evening_detective_server/internal/services/ui_service" "evening_detective_server/internal/services/users_service" + "evening_detective_server/internal/services/wifi_service" proto "evening_detective_server/proto" "fmt" "log" @@ -89,6 +90,10 @@ const grpcMsgLimit = 257 << 20 func main() { _ = godotenv.Load() + if len(os.Args) == 2 && os.Args[1] == "--collect-cudy" { + collectCudy() + return + } mode := "prod" if os.Getenv("APP_MODE") == "venue" { mode = "venue" @@ -292,6 +297,7 @@ func main() { gameResultsService, gameResultsApplyService, devicePresenceService, + wifi_service.New(os.Getenv("CUDY_SNAPSHOT_FILE")), ), ) diff --git a/cmd/evening_detective_server/main.swagger.json b/cmd/evening_detective_server/main.swagger.json index 10187b1..7218d32 100644 --- a/cmd/evening_detective_server/main.swagger.json +++ b/cmd/evening_detective_server/main.swagger.json @@ -5056,6 +5056,10 @@ "serverTime": { "type": "string", "format": "date-time" + }, + "wifi": { + "$ref": "#/definitions/evening_detective_serverVenueWiFi", + "description": "Entire venue, independent of the game filter." } } }, @@ -6346,6 +6350,50 @@ } } }, + "evening_detective_serverVenueWiFi": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "collectedAt": { + "type": "string", + "format": "date-time" + }, + "stale": { + "type": "boolean" + }, + "hasError": { + "type": "boolean" + }, + "clients": { + "type": "array", + "items": { + "type": "object", + "$ref": "#/definitions/evening_detective_serverWiFiClient" + } + } + } + }, + "evening_detective_serverWiFiClient": { + "type": "object", + "properties": { + "IP": { + "type": "string" + }, + "MAC": { + "type": "string" + }, + "band": { + "type": "string" + }, + "signal": { + "type": "integer", + "format": "int32", + "description": "dBm; absent when the router does not report it." + } + } + }, "protobufAny": { "type": "object", "properties": { diff --git a/internal/app/devices.go b/internal/app/devices.go index bf33433..9a5f1a8 100644 --- a/internal/app/devices.go +++ b/internal/app/devices.go @@ -52,7 +52,9 @@ func (s *server) GetDevices(ctx context.Context, req *proto.GetDevicesReq) (*pro if err != nil { return nil, deviceErrorStatus(err) } + wifi, stale := s.wifiService.Get(ctx) return &proto.GetDevicesRsp{ + Wifi: mapWiFi(wifi, stale), Devices: mapVenueDevices(devices), ServerTime: timestamppb.New(now), }, nil diff --git a/internal/app/devices_mapper.go b/internal/app/devices_mapper.go index fffd6a0..9b66104 100644 --- a/internal/app/devices_mapper.go +++ b/internal/app/devices_mapper.go @@ -1,6 +1,7 @@ package app import ( + "evening_detective_server/internal/modules/cudy" "evening_detective_server/internal/services/device_presence_service" proto "evening_detective_server/proto" @@ -51,3 +52,29 @@ func mapVenueDevices(devices []device_presence_service.Device) []*proto.VenueDev } return result } + +func mapWiFi(snapshot cudy.Snapshot, stale bool) *proto.VenueWiFi { + result := &proto.VenueWiFi{ + Enabled: snapshot.Enabled, + Stale: stale, + HasError: snapshot.HasError, + Clients: make([]*proto.WiFiClient, 0, len(snapshot.Clients)), + } + if !snapshot.CollectedAt.IsZero() { + result.CollectedAt = timestamppb.New(snapshot.CollectedAt) + } + for _, client := range snapshot.Clients { + var signal *int32 + if client.Signal != nil { + value := int32(*client.Signal) + signal = &value + } + result.Clients = append(result.Clients, &proto.WiFiClient{ + IP: client.IP, + MAC: client.MAC, + Band: client.Band, + Signal: signal, + }) + } + return result +} diff --git a/internal/app/devices_test.go b/internal/app/devices_test.go index 43c6376..0ddb3e1 100644 --- a/internal/app/devices_test.go +++ b/internal/app/devices_test.go @@ -2,15 +2,21 @@ package app import ( "context" + "encoding/json" "errors" + "os" + "path/filepath" "strings" "testing" + "time" + "evening_detective_server/internal/modules/cudy" "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" + "evening_detective_server/internal/services/wifi_service" proto "evening_detective_server/proto" "github.com/google/uuid" @@ -97,3 +103,32 @@ func TestDeviceErrors(t *testing.T) { } } } + +func TestWiFiUsesExistingPresencePermissions(t *testing.T) { + path := filepath.Join(t.TempDir(), "state.json") + signal := -65 + snapshot := cudy.Snapshot{Enabled: true, CollectedAt: time.Now().UTC(), Clients: []cudy.Client{{IP: "192.168.10.9", MAC: "AA:BB:CC:DD:EE:01", Band: "5 GHz", Signal: &signal}}} + data, err := json.Marshal(snapshot) + if err != nil { + t.Fatal(err) + } + if err := os.WriteFile(path, data, 0600); err != nil { + t.Fatal(err) + } + s := &server{devicePresenceService: device_presence_service.NewDevicePresenceService(true, nil, nil, nil), wifiService: wifi_service.New(path)} + for _, role := range []string{"organizer", "developer", "admin"} { + ctx := processor_jwt.ContextWithClaims(context.Background(), &processor_jwt.JWTClaims{Roles: []string{role}}) + result, err := s.GetDevices(ctx, &proto.GetDevicesReq{}) + if err != nil || !result.Wifi.Enabled || result.Wifi.Stale || len(result.Wifi.Clients) != 1 || result.Wifi.Clients[0].GetSignal() != -65 { + t.Fatalf("Wi-Fi for %s: %v, %v", role, result, err) + } + } + if _, err := s.GetDevices(context.Background(), &proto.GetDevicesReq{}); status.Code(err) != codes.PermissionDenied { + t.Fatal(err) + } + s.devicePresenceService = device_presence_service.NewDevicePresenceService(false, nil, nil, nil) + ctx := processor_jwt.ContextWithClaims(context.Background(), &processor_jwt.JWTClaims{Roles: []string{"developer"}}) + if _, err := s.GetDevices(ctx, &proto.GetDevicesReq{}); status.Code(err) != codes.FailedPrecondition { + t.Fatal("prod exposes Wi-Fi", err) + } +} diff --git a/internal/app/server.go b/internal/app/server.go index 7139ec0..35b09cd 100644 --- a/internal/app/server.go +++ b/internal/app/server.go @@ -21,6 +21,7 @@ import ( "evening_detective_server/internal/services/ticket_service" "evening_detective_server/internal/services/ui_service" "evening_detective_server/internal/services/users_service" + "evening_detective_server/internal/services/wifi_service" proto "evening_detective_server/proto" ) @@ -28,6 +29,7 @@ type server struct { proto.UnsafeEveningDetectiveServerServer devicePresenceService *device_presence_service.DevicePresenceService + wifiService *wifi_service.Service usersService *users_service.UsersService uiService *ui_service.UIService taskService *task_service.TaskService @@ -73,9 +75,11 @@ func NewServer( gameResultsService *game_results_package_service.GameResultsPackageService, gameResultsApplyService *game_results_package_service.GameResultsApplyService, devicePresenceService *device_presence_service.DevicePresenceService, + wifiService *wifi_service.Service, ) proto.EveningDetectiveServerServer { return &server{ devicePresenceService: devicePresenceService, + wifiService: wifiService, gameImportService: gameImportService, gameResultsService: gameResultsService, gameResultsApplyService: gameResultsApplyService, diff --git a/internal/modules/cudy/client.go b/internal/modules/cudy/client.go new file mode 100644 index 0000000..23c73f0 --- /dev/null +++ b/internal/modules/cudy/client.go @@ -0,0 +1,175 @@ +package cudy + +import ( + "context" + "crypto/sha256" + "encoding/hex" + "errors" + "io" + "net/http" + "net/http/cookiejar" + "net/netip" + "net/url" + "strings" + "time" + + "golang.org/x/net/html" +) + +var ErrRead = errors.New("не удалось прочитать Wi-Fi Cudy") + +const maxBody = 2 << 20 + +type reader struct { + base string + password string + http *http.Client + authenticated bool +} + +func NewReader(base, password string) (Reader, error) { + u, err := url.Parse(base) + if err != nil || u.User != nil || u.RawQuery != "" || u.Fragment != "" || (u.Path != "" && u.Path != "/") || (u.Scheme != "http" && u.Scheme != "https") { + return nil, ErrRead + } + ip, err := netip.ParseAddr(u.Hostname()) + if err != nil || (!ip.IsPrivate() && !ip.IsLoopback()) || password == "" { + return nil, ErrRead + } + jar, _ := cookiejar.New(nil) + client := &http.Client{Timeout: 5 * time.Second, Jar: jar, Transport: &http.Transport{Proxy: nil}, CheckRedirect: func(req *http.Request, via []*http.Request) error { + if len(via) > 5 || req.URL.Host != u.Host || req.URL.Scheme != u.Scheme { + return ErrRead + } + return nil + }} + return &reader{base: strings.TrimRight(base, "/"), password: password, http: client}, nil +} + +func (r *reader) request(ctx context.Context, path string, form url.Values) ([]byte, int, error) { + method := http.MethodGet + var body io.Reader + if form != nil { + method = http.MethodPost + body = strings.NewReader(form.Encode()) + } + req, err := http.NewRequestWithContext(ctx, method, r.base+path, body) + if err != nil { + return nil, 0, ErrRead + } + if form != nil { + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + } + resp, err := r.http.Do(req) + if err != nil { + if ctx.Err() != nil { + return nil, 0, ctx.Err() + } + return nil, 0, ErrRead + } + defer resp.Body.Close() + data, err := io.ReadAll(io.LimitReader(resp.Body, maxBody+1)) + if err != nil || len(data) > maxBody { + return nil, resp.StatusCode, ErrRead + } + return data, resp.StatusCode, nil +} + +func (r *reader) login(ctx context.Context) error { + data, code, err := r.request(ctx, "/cgi-bin/luci/", nil) + if err != nil { + return err + } + if code != http.StatusForbidden && code != http.StatusOK { + return ErrRead + } + doc, err := html.Parse(strings.NewReader(string(data))) + if err != nil { + return ErrRead + } + form := url.Values{} + walk(doc, func(n *html.Node) { + if n.Type == html.ElementNode && n.Data == "input" && attr(n, "type") == "hidden" { + switch attr(n, "name") { + case "_csrf", "salt", "token": + form.Set(attr(n, "name"), attr(n, "value")) + } + } + }) + if form.Get("salt") == "" || form.Get("_csrf") == "" { + return ErrRead + } + token, code, err := r.request(ctx, "/cgi-bin/luci/admin/get_token", url.Values{}) + if err != nil { + return err + } + if code != http.StatusOK || len(token) > 256 || len(token) == 0 { + return ErrRead + } + hash := sha256.Sum256([]byte(r.password + form.Get("salt"))) + hash = sha256.Sum256([]byte(hex.EncodeToString(hash[:]) + strings.TrimSpace(string(token)))) + form.Set("luci_username", "admin") + form.Set("luci_password", hex.EncodeToString(hash[:])) + form.Set("token", strings.TrimSpace(string(token))) + form.Set("luci_language", "en") + data, code, err = r.request(ctx, "/cgi-bin/luci/", form) + if err != nil { + return err + } + if code != http.StatusOK || strings.Contains(string(data), "luci_password_login") { + return ErrRead + } + r.authenticated = true + return nil +} + +func (r *reader) Read(ctx context.Context) ([]Client, error) { + if !r.authenticated { + if err := r.login(ctx); err != nil { + return nil, err + } + } + data, code, err := r.request(ctx, "/cgi-bin/luci/admin/network/devices/devlist?detail=1", nil) + if err != nil { + return nil, err + } + if code == http.StatusForbidden || code == http.StatusUnauthorized || strings.Contains(string(data), "luci_password_login") { + r.authenticated = false + if err := r.login(ctx); err != nil { + return nil, err + } + data, code, err = r.request(ctx, "/cgi-bin/luci/admin/network/devices/devlist?detail=1", nil) + if err != nil { + return nil, err + } + } + if code != http.StatusOK { + return nil, ErrRead + } + return parseClients(data) +} + +func attr(n *html.Node, key string) string { + for _, a := range n.Attr { + if a.Key == key { + return a.Val + } + } + return "" +} +func walk(n *html.Node, visit func(*html.Node)) { + visit(n) + for c := n.FirstChild; c != nil; c = c.NextSibling { + walk(c, visit) + } +} +func nodeText(n *html.Node) string { + var out strings.Builder + walk(n, func(c *html.Node) { + if c.Type == html.TextNode { + out.WriteString(c.Data) + out.WriteByte(' ') + } + }) + return strings.TrimSpace(out.String()) +} diff --git a/internal/modules/cudy/client_parser.go b/internal/modules/cudy/client_parser.go new file mode 100644 index 0000000..1d49cf9 --- /dev/null +++ b/internal/modules/cudy/client_parser.go @@ -0,0 +1,123 @@ +package cudy + +import ( + "net" + "net/netip" + "sort" + "strconv" + "strings" + + "golang.org/x/net/html" +) + +func parseClients(data []byte) ([]Client, error) { + doc, err := html.Parse(strings.NewReader(string(data))) + if err != nil { + return nil, ErrRead + } + clients := []Client{} + var table *html.Node + walk(doc, func(n *html.Node) { + if n.Data == "th" && nodeText(n) == "IP / MAC Address" { + for parent := n.Parent; parent != nil; parent = parent.Parent { + if parent.Data == "table" { + table = parent + break + } + } + } + }) + if table == nil { + return nil, ErrRead + } + invalid := false + seen := map[string]bool{} + walk(table, func(row *html.Node) { + if row.Type != html.ElementNode { + return + } + if row.Data != "tr" { + return + } + hasData := false + for child := row.FirstChild; child != nil; child = child.NextSibling { + hasData = hasData || child.Data == "td" + } + if !hasData { + return + } + if !strings.HasPrefix(attr(row, "id"), "cbi-table-") { + invalid = true + return + } + fields := map[string]*html.Node{} + prefix := attr(row, "id") + "-" + walk(row, func(n *html.Node) { + if n.Data == "div" && strings.HasPrefix(attr(n, "id"), prefix) { + fields[strings.TrimPrefix(attr(n, "id"), prefix)] = n + } + }) + var band string + wired := false + if hostname := fields["hostname"]; hostname != nil { + walk(hostname, func(n *html.Node) { + if n.Data == "span" { + label := nodeText(n) + wired = wired || label == "Ethernet" + if label == "2.4G WiFi" { + band = "2.4 GHz" + } + if label == "5G WiFi" { + band = "5 GHz" + } + } + }) + } + // shortcut: stock TR3000 2.5.27 DOM and two bands; update parser fixtures for other firmware. + if band == "" { + if !wired { + invalid = true + } + return + } + client := Client{Band: band} + if field := fields["ipmac"]; field != nil { + walk(field, func(n *html.Node) { + if n.Data != "p" || client.MAC != "" { + return + } + for _, value := range strings.Fields(nodeText(n)) { + if ip, e := netip.ParseAddr(value); e == nil { + client.IP = ip.String() + } + if mac, e := net.ParseMAC(value); e == nil && len(mac) == 6 { + client.MAC = strings.ToUpper(mac.String()) + } + } + }) + } + if field := fields["signal"]; field != nil { + walk(field, func(n *html.Node) { + if n.Data == "p" && client.Signal == nil { + parts := strings.Fields(nodeText(n)) + if len(parts) == 2 && parts[1] == "dBm" { + if value, e := strconv.Atoi(parts[0]); e == nil && value >= -120 && value <= 0 { + client.Signal = &value + } + } + } + }) + } + if client.MAC == "" || seen[client.MAC] { + invalid = true + return + } + seen[client.MAC] = true + clients = append(clients, client) + }) + if invalid || len(clients) > 1024 { + return nil, ErrRead + } + sort.Slice(clients, func(i, j int) bool { return clients[i].MAC < clients[j].MAC }) + return clients, nil +} diff --git a/internal/modules/cudy/client_test.go b/internal/modules/cudy/client_test.go new file mode 100644 index 0000000..449eae8 --- /dev/null +++ b/internal/modules/cudy/client_test.go @@ -0,0 +1,120 @@ +package cudy + +import ( + "context" + "crypto/sha256" + "encoding/hex" + "encoding/json" + "errors" + "fmt" + "net/http" + "net/http/httptest" + "strings" + "testing" +) + +func clientRow(id, mac, band, signal string) string { + return fmt.Sprintf(`

PRIVATE NAME
%s

192.168.10.9
%s

192.168.10.9
%s

%s

%s

`, id, id, band, id, mac, mac, id, signal, signal) +} +func clientTable(rows string) string { + return `` + rows + `
IP / MAC Address
` +} + +func TestParseClients(t *testing.T) { + rows := clientRow("1", "AA:BB:CC:DD:EE:02", "2.4G WiFi", "-60 dBm") + clientRow("2", "AA:BB:CC:DD:EE:01", "5G WiFi", "—") + clientRow("3", "AA:BB:CC:DD:EE:03", "Ethernet", "—") + clients, err := parseClients([]byte(clientTable(rows))) + if err != nil || len(clients) != 2 { + t.Fatalf("reading: %v, count %d", err, len(clients)) + } + if clients[0].MAC != "AA:BB:CC:DD:EE:01" || clients[0].Signal != nil || clients[1].Signal == nil || *clients[1].Signal != -60 { + t.Fatalf("unexpected snapshot: %+v", clients) + } + data, _ := json.Marshal(clients) + if strings.Contains(string(data), "PRIVATE") { + t.Fatal("hostname leaked") + } + for _, text := range []string{`
Login
`, clientTable(clientRow("1", "bad", "5G WiFi", "-60 dBm")), clientTable(strings.ReplaceAll(clientRow("1", "AA:BB:CC:DD:EE:01", "5G WiFi", "-60 dBm"), "cbi-table-", "changed-")), clientTable(clientRow("1", "AA:BB:CC:DD:EE:01", "5 GHz", "-60 dBm")), clientTable(clientRow("1", "AA:BB:CC:DD:EE:01", "", "-60 dBm")), clientTable(rows + clientRow("4", "AA:BB:CC:DD:EE:01", "5G WiFi", "-70 dBm"))} { + if _, err := parseClients([]byte(text)); !errors.Is(err, ErrRead) { + t.Fatalf("invalid DOM accepted: %v", err) + } + } + clients, err = parseClients([]byte(clientTable(""))) + if err != nil || len(clients) != 0 { + t.Fatal("empty table rejected") + } +} + +func TestReaderSessionAndSafeErrors(t *testing.T) { + sessions := 0 + expire := false + mux := http.NewServeMux() + mux.HandleFunc("/cgi-bin/luci/", func(w http.ResponseWriter, r *http.Request) { + if r.Method == "POST" { + if err := r.ParseForm(); err != nil { + t.Error(err) + } + first := sha256.Sum256([]byte("private-password" + "salt")) + want := sha256.Sum256([]byte(hex.EncodeToString(first[:]) + "token")) + if r.Form.Get("luci_password") != hex.EncodeToString(want[:]) || r.Form.Get("_csrf") != "csrf" || r.Form.Get("luci_language") != "en" { + t.Error("incorrect login") + } + sessions++ + http.SetCookie(w, &http.Cookie{Name: "sysauth", Value: "session", Path: "/"}) + fmt.Fprint(w, "authenticated") + return + } + w.WriteHeader(403) + fmt.Fprint(w, `
`) + }) + mux.HandleFunc("/cgi-bin/luci/admin/get_token", func(w http.ResponseWriter, r *http.Request) { + if r.Method != "POST" { + t.Error("token method") + } + fmt.Fprint(w, "token") + }) + mux.HandleFunc("/cgi-bin/luci/admin/network/devices/devlist", func(w http.ResponseWriter, r *http.Request) { + if r.Method != "GET" || r.URL.RawQuery != "detail=1" { + t.Error("not a read-only request") + } + if _, err := r.Cookie("sysauth"); err != nil { + t.Error("session cookie missing") + } + if expire { + expire = false + w.WriteHeader(403) + return + } + fmt.Fprint(w, clientTable(clientRow("1", "AA:BB:CC:DD:EE:01", "5G WiFi", "-55 dBm"))) + }) + server := httptest.NewServer(mux) + defer server.Close() + reader, err := NewReader(server.URL, "private-password") + if err != nil { + t.Fatal(err) + } + for i := 0; i < 2; i++ { + if _, err := reader.Read(context.Background()); err != nil { + t.Fatal(err) + } + } + if sessions != 1 { + t.Fatal("session not reused") + } + expire = true + if _, err := reader.Read(context.Background()); err != nil { + t.Fatal(err) + } + if sessions != 2 { + t.Fatal("expired session not refreshed once") + } + ctx, cancel := context.WithCancel(context.Background()) + cancel() + if _, err := reader.Read(ctx); !errors.Is(err, context.Canceled) { + t.Fatal("cancellation lost") + } + for _, base := range []string{"https://example.com", "http://user:secret@192.168.10.1", "http://192.168.10.1/path", "http://8.8.8.8"} { + if _, err := NewReader(base, "secret"); !errors.Is(err, ErrRead) { + t.Fatal("untrusted URL accepted") + } + } +} diff --git a/internal/modules/cudy/interface.go b/internal/modules/cudy/interface.go new file mode 100644 index 0000000..ecbbf1a --- /dev/null +++ b/internal/modules/cudy/interface.go @@ -0,0 +1,26 @@ +// Package cudy reads Wi-Fi clients from the stock TR3000 web interface. +package cudy + +import ( + "context" + "time" +) + +type Client struct { + IP string `json:"IP"` + MAC string `json:"MAC"` + Band string `json:"band"` + Signal *int `json:"signal,omitempty"` +} + +// Snapshot holds only the latest sanitized reading, never names or credentials. +type Snapshot struct { + Enabled bool `json:"enabled"` + CollectedAt time.Time `json:"collectedAt"` + HasError bool `json:"hasError"` + Clients []Client `json:"clients"` +} + +type Reader interface { + Read(context.Context) ([]Client, error) +} diff --git a/internal/services/game_export_service/export_test.go b/internal/services/game_export_service/export_test.go index 8d74a41..1f55724 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, nil) + screen_service.NewScreenService(screen_sessions_repo.NewScreenSessionsRepo(f.pool)), nil, nil, map_service.NewMapService(mapRepo), nil, worker, nil, 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/wifi_service/service.go b/internal/services/wifi_service/service.go new file mode 100644 index 0000000..ebfd1f1 --- /dev/null +++ b/internal/services/wifi_service/service.go @@ -0,0 +1,43 @@ +// Package wifi_service reads the collector's latest snapshot without LAN access. +package wifi_service + +import ( + "context" + "encoding/json" + "io" + "os" + "time" + + "evening_detective_server/internal/modules/cudy" +) + +type Service struct { + path string + now func() time.Time +} + +func New(path string) *Service { return &Service{path: path, now: time.Now} } + +func (s *Service) Get(ctx context.Context) (cudy.Snapshot, bool) { + if s == nil || s.path == "" { + return cudy.Snapshot{}, false + } + snapshot := cudy.Snapshot{Enabled: true, HasError: true} + if ctx.Err() != nil { + return snapshot, true + } + file, err := os.Open(s.path) + if err != nil { + return snapshot, true + } + defer file.Close() + data, err := io.ReadAll(io.LimitReader(file, 2<<20+1)) + if err != nil || len(data) > 2<<20 || json.Unmarshal(data, &snapshot) != nil { + return cudy.Snapshot{Enabled: true, HasError: true}, true + } + if !snapshot.Enabled { + return snapshot, false + } + stale := snapshot.HasError || snapshot.CollectedAt.IsZero() || s.now().Sub(snapshot.CollectedAt) >= 30*time.Second || snapshot.CollectedAt.After(s.now()) + return snapshot, stale +} diff --git a/internal/services/wifi_service/service_test.go b/internal/services/wifi_service/service_test.go new file mode 100644 index 0000000..4a6f418 --- /dev/null +++ b/internal/services/wifi_service/service_test.go @@ -0,0 +1,58 @@ +package wifi_service + +import ( + "context" + "encoding/json" + "os" + "path/filepath" + "testing" + "time" + + "evening_detective_server/internal/modules/cudy" +) + +func TestSnapshotFreshnessAndFailures(t *testing.T) { + path := filepath.Join(t.TempDir(), "state.json") + now := time.Unix(1234, 0) + service := New(path) + service.now = func() time.Time { return now } + snapshot := cudy.Snapshot{Enabled: true, CollectedAt: now, Clients: []cudy.Client{{MAC: "AA:BB:CC:DD:EE:01"}}} + write := func() { + t.Helper() + data, _ := json.Marshal(snapshot) + if err := os.WriteFile(path, data, 0600); err != nil { + t.Fatal(err) + } + } + write() + result, stale := service.Get(context.Background()) + if stale || len(result.Clients) != 1 { + t.Fatal("fresh snapshot rejected") + } + now = now.Add(30 * time.Second) + if _, stale = service.Get(context.Background()); !stale { + t.Fatal("stale snapshot accepted") + } + snapshot.HasError = true + write() + result, stale = service.Get(context.Background()) + if !stale || !result.HasError || len(result.Clients) != 1 { + t.Fatal("failed refresh lost snapshot") + } + if err := os.WriteFile(path, []byte("bad JSON"), 0600); err != nil { + t.Fatal(err) + } + result, stale = service.Get(context.Background()) + if !stale || !result.HasError { + t.Fatal("corrupt snapshot accepted") + } + snapshot = cudy.Snapshot{} + write() + result, stale = service.Get(context.Background()) + if result.Enabled || stale { + t.Fatal("disabled source marked stale") + } + if result, stale = New("").Get(context.Background()); result.Enabled || stale { + t.Fatal("unconfigured source enabled") + } +} diff --git a/proto/main.pb.go b/proto/main.pb.go index 99404e8..47ec6f8 100644 --- a/proto/main.pb.go +++ b/proto/main.pb.go @@ -12406,6 +12406,7 @@ 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"` + Wifi *VenueWiFi `protobuf:"bytes,3,opt,name=wifi,proto3" json:"wifi,omitempty"` // Entire venue, independent of the game filter. unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -12454,6 +12455,13 @@ func (x *GetDevicesRsp) GetServerTime() *timestamppb.Timestamp { return nil } +func (x *GetDevicesRsp) GetWifi() *VenueWiFi { + if x != nil { + return x.Wifi + } + return nil +} + type VenueDevice struct { state protoimpl.MessageState `protogen:"open.v1"` ID string `protobuf:"bytes,1,opt,name=ID,proto3" json:"ID,omitempty"` @@ -12646,6 +12654,150 @@ func (x *DeviceConnection) GetKind() string { return "" } +type VenueWiFi struct { + state protoimpl.MessageState `protogen:"open.v1"` + Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"` + CollectedAt *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=collectedAt,proto3" json:"collectedAt,omitempty"` + Stale bool `protobuf:"varint,3,opt,name=stale,proto3" json:"stale,omitempty"` + HasError bool `protobuf:"varint,4,opt,name=hasError,proto3" json:"hasError,omitempty"` + Clients []*WiFiClient `protobuf:"bytes,5,rep,name=clients,proto3" json:"clients,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *VenueWiFi) Reset() { + *x = VenueWiFi{} + mi := &file_main_proto_msgTypes[238] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *VenueWiFi) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*VenueWiFi) ProtoMessage() {} + +func (x *VenueWiFi) ProtoReflect() protoreflect.Message { + mi := &file_main_proto_msgTypes[238] + 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 VenueWiFi.ProtoReflect.Descriptor instead. +func (*VenueWiFi) Descriptor() ([]byte, []int) { + return file_main_proto_rawDescGZIP(), []int{238} +} + +func (x *VenueWiFi) GetEnabled() bool { + if x != nil { + return x.Enabled + } + return false +} + +func (x *VenueWiFi) GetCollectedAt() *timestamppb.Timestamp { + if x != nil { + return x.CollectedAt + } + return nil +} + +func (x *VenueWiFi) GetStale() bool { + if x != nil { + return x.Stale + } + return false +} + +func (x *VenueWiFi) GetHasError() bool { + if x != nil { + return x.HasError + } + return false +} + +func (x *VenueWiFi) GetClients() []*WiFiClient { + if x != nil { + return x.Clients + } + return nil +} + +type WiFiClient struct { + state protoimpl.MessageState `protogen:"open.v1"` + IP string `protobuf:"bytes,1,opt,name=IP,proto3" json:"IP,omitempty"` + MAC string `protobuf:"bytes,2,opt,name=MAC,proto3" json:"MAC,omitempty"` + Band string `protobuf:"bytes,3,opt,name=band,proto3" json:"band,omitempty"` + Signal *int32 `protobuf:"varint,4,opt,name=signal,proto3,oneof" json:"signal,omitempty"` // dBm; absent when the router does not report it. + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *WiFiClient) Reset() { + *x = WiFiClient{} + mi := &file_main_proto_msgTypes[239] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *WiFiClient) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WiFiClient) ProtoMessage() {} + +func (x *WiFiClient) ProtoReflect() protoreflect.Message { + mi := &file_main_proto_msgTypes[239] + 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 WiFiClient.ProtoReflect.Descriptor instead. +func (*WiFiClient) Descriptor() ([]byte, []int) { + return file_main_proto_rawDescGZIP(), []int{239} +} + +func (x *WiFiClient) GetIP() string { + if x != nil { + return x.IP + } + return "" +} + +func (x *WiFiClient) GetMAC() string { + if x != nil { + return x.MAC + } + return "" +} + +func (x *WiFiClient) GetBand() string { + if x != nil { + return x.Band + } + return "" +} + +func (x *WiFiClient) GetSignal() int32 { + if x != nil && x.Signal != nil { + return *x.Signal + } + return 0 +} + var File_main_proto protoreflect.FileDescriptor const file_main_proto_rawDesc = "" + @@ -13433,12 +13585,13 @@ const file_main_proto_rawDesc = "" + "\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" + + "\a_gameID\"\xd1\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" + + "serverTime\x12=\n" + + "\x04wifi\x18\x03 \x01(\v2).crabs.evening_detective_server.VenueWiFiR\x04wifi\"\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" + @@ -13463,7 +13616,20 @@ const file_main_proto_rawDesc = "" + " \x01(\tR\x04kindB\t\n" + "\a_gameIDB\t\n" + "\a_teamIDB\v\n" + - "\t_screenID*\xf6\x01\n" + + "\t_screenID\"\xdb\x01\n" + + "\tVenueWiFi\x12\x18\n" + + "\aenabled\x18\x01 \x01(\bR\aenabled\x12<\n" + + "\vcollectedAt\x18\x02 \x01(\v2\x1a.google.protobuf.TimestampR\vcollectedAt\x12\x14\n" + + "\x05stale\x18\x03 \x01(\bR\x05stale\x12\x1a\n" + + "\bhasError\x18\x04 \x01(\bR\bhasError\x12D\n" + + "\aclients\x18\x05 \x03(\v2*.crabs.evening_detective_server.WiFiClientR\aclients\"j\n" + + "\n" + + "WiFiClient\x12\x0e\n" + + "\x02IP\x18\x01 \x01(\tR\x02IP\x12\x10\n" + + "\x03MAC\x18\x02 \x01(\tR\x03MAC\x12\x12\n" + + "\x04band\x18\x03 \x01(\tR\x04band\x12\x1b\n" + + "\x06signal\x18\x04 \x01(\x05H\x00R\x06signal\x88\x01\x01B\t\n" + + "\a_signal*\xf6\x01\n" + "\n" + "ScreenMode\x12\x1b\n" + "\x17SCREEN_MODE_UNSPECIFIED\x10\x00\x12\x15\n" + @@ -13799,7 +13965,7 @@ func file_main_proto_rawDescGZIP() []byte { } var file_main_proto_enumTypes = make([]protoimpl.EnumInfo, 4) -var file_main_proto_msgTypes = make([]protoimpl.MessageInfo, 238) +var file_main_proto_msgTypes = make([]protoimpl.MessageInfo, 240) var file_main_proto_goTypes = []any{ (ScreenMode)(0), // 0: crabs.evening_detective_server.ScreenMode (ScreenTimerState)(0), // 1: crabs.evening_detective_server.ScreenTimerState @@ -14043,12 +14209,14 @@ var file_main_proto_goTypes = []any{ (*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 + (*VenueWiFi)(nil), // 242: crabs.evening_detective_server.VenueWiFi + (*WiFiClient)(nil), // 243: crabs.evening_detective_server.WiFiClient + (*timestamppb.Timestamp)(nil), // 244: google.protobuf.Timestamp + (*httpbody.HttpBody)(nil), // 245: google.api.HttpBody } var file_main_proto_depIdxs = []int32{ 26, // 0: crabs.evening_detective_server.GetUsersRsp.users:type_name -> crabs.evening_detective_server.User - 242, // 1: crabs.evening_detective_server.User.created_at:type_name -> google.protobuf.Timestamp + 244, // 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 @@ -14061,9 +14229,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 - 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 + 244, // 14: crabs.evening_detective_server.Scenario.updated_at:type_name -> google.protobuf.Timestamp + 244, // 15: crabs.evening_detective_server.Scenario.created_at:type_name -> google.protobuf.Timestamp + 244, // 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 @@ -14080,13 +14248,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 - 242, // 33: crabs.evening_detective_server.AddGameReq.start_at:type_name -> google.protobuf.Timestamp + 244, // 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 - 242, // 35: crabs.evening_detective_server.Game.start_at:type_name -> google.protobuf.Timestamp + 244, // 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 - 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 + 244, // 38: crabs.evening_detective_server.Game.started_at:type_name -> google.protobuf.Timestamp + 244, // 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 @@ -14114,7 +14282,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 - 242, // 67: crabs.evening_detective_server.ParticipationRsp.start_at:type_name -> google.protobuf.Timestamp + 244, // 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 @@ -14123,11 +14291,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 - 242, // 76: crabs.evening_detective_server.GameReview.created_at:type_name -> google.protobuf.Timestamp + 244, // 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 - 242, // 80: crabs.evening_detective_server.UpdateGameReq.start_at:type_name -> google.protobuf.Timestamp + 244, // 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 @@ -14136,249 +14304,252 @@ 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 - 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 + 244, // 89: crabs.evening_detective_server.TicketPage.start_at:type_name -> google.protobuf.Timestamp + 244, // 90: crabs.evening_detective_server.TicketPage.used_at:type_name -> google.protobuf.Timestamp + 244, // 91: crabs.evening_detective_server.DeveloperTask.createdAt:type_name -> google.protobuf.Timestamp + 244, // 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 - 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 + 245, // 96: crabs.evening_detective_server.ApplyGameResultsPackageReq.package:type_name -> google.api.HttpBody + 244, // 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 + 244, // 99: crabs.evening_detective_server.GetDevicesRsp.serverTime:type_name -> google.protobuf.Timestamp + 242, // 100: crabs.evening_detective_server.GetDevicesRsp.wifi:type_name -> crabs.evening_detective_server.VenueWiFi + 244, // 101: crabs.evening_detective_server.VenueDevice.firstSeenAt:type_name -> google.protobuf.Timestamp + 244, // 102: crabs.evening_detective_server.VenueDevice.lastSeenAt:type_name -> google.protobuf.Timestamp + 241, // 103: crabs.evening_detective_server.VenueDevice.connections:type_name -> crabs.evening_detective_server.DeviceConnection + 244, // 104: crabs.evening_detective_server.DeviceConnection.firstSeenAt:type_name -> google.protobuf.Timestamp + 244, // 105: crabs.evening_detective_server.DeviceConnection.lastSeenAt:type_name -> google.protobuf.Timestamp + 244, // 106: crabs.evening_detective_server.VenueWiFi.collectedAt:type_name -> google.protobuf.Timestamp + 243, // 107: crabs.evening_detective_server.VenueWiFi.clients:type_name -> crabs.evening_detective_server.WiFiClient + 236, // 108: crabs.evening_detective_server.EveningDetectiveServer.DeviceHeartbeat:input_type -> crabs.evening_detective_server.DeviceHeartbeatReq + 238, // 109: crabs.evening_detective_server.EveningDetectiveServer.GetDevices:input_type -> crabs.evening_detective_server.GetDevicesReq + 223, // 110: crabs.evening_detective_server.EveningDetectiveServer.GetTasks:input_type -> crabs.evening_detective_server.GetTasksReq + 225, // 111: crabs.evening_detective_server.EveningDetectiveServer.GetTaskAssignees:input_type -> crabs.evening_detective_server.GetTaskAssigneesReq + 227, // 112: crabs.evening_detective_server.EveningDetectiveServer.CreateTask:input_type -> crabs.evening_detective_server.CreateTaskReq + 228, // 113: crabs.evening_detective_server.EveningDetectiveServer.UpdateTask:input_type -> crabs.evening_detective_server.UpdateTaskReq + 229, // 114: crabs.evening_detective_server.EveningDetectiveServer.SetTaskStatus:input_type -> crabs.evening_detective_server.SetTaskStatusReq + 230, // 115: crabs.evening_detective_server.EveningDetectiveServer.DeleteTask:input_type -> crabs.evening_detective_server.DeleteTaskReq + 155, // 116: crabs.evening_detective_server.EveningDetectiveServer.GetParticipation:input_type -> crabs.evening_detective_server.GetParticipationReq + 156, // 117: crabs.evening_detective_server.EveningDetectiveServer.AnswerParticipation:input_type -> crabs.evening_detective_server.AnswerParticipationReq + 4, // 118: crabs.evening_detective_server.EveningDetectiveServer.Ping:input_type -> crabs.evening_detective_server.PingReq + 6, // 119: crabs.evening_detective_server.EveningDetectiveServer.Echo:input_type -> crabs.evening_detective_server.EchoReq + 8, // 120: crabs.evening_detective_server.EveningDetectiveServer.Signup:input_type -> crabs.evening_detective_server.SignupReq + 10, // 121: crabs.evening_detective_server.EveningDetectiveServer.RefreshPassword:input_type -> crabs.evening_detective_server.RefreshPasswordReq + 12, // 122: crabs.evening_detective_server.EveningDetectiveServer.Login:input_type -> crabs.evening_detective_server.LoginReq + 14, // 123: crabs.evening_detective_server.EveningDetectiveServer.Refresh:input_type -> crabs.evening_detective_server.RefreshReq + 16, // 124: crabs.evening_detective_server.EveningDetectiveServer.DeleteAccount:input_type -> crabs.evening_detective_server.DeleteAccountReq + 18, // 125: crabs.evening_detective_server.EveningDetectiveServer.GetTerms:input_type -> crabs.evening_detective_server.GetTermsReq + 20, // 126: crabs.evening_detective_server.EveningDetectiveServer.GetPrivacy:input_type -> crabs.evening_detective_server.GetPrivacyReq + 22, // 127: crabs.evening_detective_server.EveningDetectiveServer.GetConsent:input_type -> crabs.evening_detective_server.GetConsentReq + 24, // 128: crabs.evening_detective_server.EveningDetectiveServer.GetUsers:input_type -> crabs.evening_detective_server.GetUsersReq + 27, // 129: crabs.evening_detective_server.EveningDetectiveServer.GetUserByID:input_type -> crabs.evening_detective_server.GetUserByIDReq + 29, // 130: crabs.evening_detective_server.EveningDetectiveServer.GetMe:input_type -> crabs.evening_detective_server.GetMeReq + 31, // 131: crabs.evening_detective_server.EveningDetectiveServer.ChangeUserPassword:input_type -> crabs.evening_detective_server.ChangeUserPasswordReq + 33, // 132: crabs.evening_detective_server.EveningDetectiveServer.AddUserRole:input_type -> crabs.evening_detective_server.AddUserRoleReq + 35, // 133: crabs.evening_detective_server.EveningDetectiveServer.DeleteUserRole:input_type -> crabs.evening_detective_server.DeleteUserRoleReq + 37, // 134: crabs.evening_detective_server.EveningDetectiveServer.GetPermissions:input_type -> crabs.evening_detective_server.GetPermissionsReq + 39, // 135: crabs.evening_detective_server.EveningDetectiveServer.UploadFile:input_type -> crabs.evening_detective_server.UploadFileReq + 41, // 136: crabs.evening_detective_server.EveningDetectiveServer.DownloadFile:input_type -> crabs.evening_detective_server.DownloadFileReq + 44, // 137: crabs.evening_detective_server.EveningDetectiveServer.GetPlaylists:input_type -> crabs.evening_detective_server.GetPlaylistsReq + 46, // 138: crabs.evening_detective_server.EveningDetectiveServer.CreatePlaylist:input_type -> crabs.evening_detective_server.CreatePlaylistReq + 48, // 139: crabs.evening_detective_server.EveningDetectiveServer.UpdatePlaylist:input_type -> crabs.evening_detective_server.UpdatePlaylistReq + 50, // 140: crabs.evening_detective_server.EveningDetectiveServer.DeletePlaylist:input_type -> crabs.evening_detective_server.DeletePlaylistReq + 52, // 141: crabs.evening_detective_server.EveningDetectiveServer.UploadPlaylistTrack:input_type -> crabs.evening_detective_server.UploadPlaylistTrackReq + 54, // 142: crabs.evening_detective_server.EveningDetectiveServer.DeletePlaylistTrack:input_type -> crabs.evening_detective_server.DeletePlaylistTrackReq + 56, // 143: crabs.evening_detective_server.EveningDetectiveServer.SetScenarioPlaylist:input_type -> crabs.evening_detective_server.SetScenarioPlaylistReq + 58, // 144: crabs.evening_detective_server.EveningDetectiveServer.AddScenario:input_type -> crabs.evening_detective_server.AddScenarioReq + 60, // 145: crabs.evening_detective_server.EveningDetectiveServer.GetMyScenarios:input_type -> crabs.evening_detective_server.GetMyScenariosReq + 62, // 146: crabs.evening_detective_server.EveningDetectiveServer.GetScenariosCatalog:input_type -> crabs.evening_detective_server.GetScenariosCatalogReq + 64, // 147: crabs.evening_detective_server.EveningDetectiveServer.GetFullScenario:input_type -> crabs.evening_detective_server.GetScenarioReq + 64, // 148: crabs.evening_detective_server.EveningDetectiveServer.GetScenario:input_type -> crabs.evening_detective_server.GetScenarioReq + 78, // 149: crabs.evening_detective_server.EveningDetectiveServer.UpdateScenario:input_type -> crabs.evening_detective_server.UpdateScenarioReq + 80, // 150: crabs.evening_detective_server.EveningDetectiveServer.PublicScenario:input_type -> crabs.evening_detective_server.PublicScenarioReq + 82, // 151: crabs.evening_detective_server.EveningDetectiveServer.DraftScenario:input_type -> crabs.evening_detective_server.DraftScenarioReq + 84, // 152: crabs.evening_detective_server.EveningDetectiveServer.DeleteScenario:input_type -> crabs.evening_detective_server.DeleteScenarioReq + 86, // 153: crabs.evening_detective_server.EveningDetectiveServer.AddScenarioPlace:input_type -> crabs.evening_detective_server.AddScenarioPlaceReq + 88, // 154: crabs.evening_detective_server.EveningDetectiveServer.UpdateScenarioPlace:input_type -> crabs.evening_detective_server.UpdateScenarioPlaceReq + 90, // 155: crabs.evening_detective_server.EveningDetectiveServer.DeleteScenarioPlace:input_type -> crabs.evening_detective_server.DeleteScenarioPlaceReq + 96, // 156: crabs.evening_detective_server.EveningDetectiveServer.AddScenarioQuestion:input_type -> crabs.evening_detective_server.AddScenarioQuestionReq + 98, // 157: crabs.evening_detective_server.EveningDetectiveServer.UpdateScenarioQuestion:input_type -> crabs.evening_detective_server.UpdateScenarioQuestionReq + 100, // 158: crabs.evening_detective_server.EveningDetectiveServer.DeleteScenarioQuestion:input_type -> crabs.evening_detective_server.DeleteScenarioQuestionReq + 211, // 159: crabs.evening_detective_server.EveningDetectiveServer.UpdateScenarioBackground:input_type -> crabs.evening_detective_server.UpdateScenarioBackgroundReq + 94, // 160: crabs.evening_detective_server.EveningDetectiveServer.UpdateScenarioIntro:input_type -> crabs.evening_detective_server.UpdateScenarioIntroReq + 92, // 161: crabs.evening_detective_server.EveningDetectiveServer.UpdateScenarioConclusion:input_type -> crabs.evening_detective_server.UpdateScenarioConclusionReq + 102, // 162: crabs.evening_detective_server.EveningDetectiveServer.DownloadScenarioArchive:input_type -> crabs.evening_detective_server.DownloadScenarioArchiveReq + 245, // 163: crabs.evening_detective_server.EveningDetectiveServer.UploadScenarioArchive:input_type -> google.api.HttpBody + 104, // 164: crabs.evening_detective_server.EveningDetectiveServer.AddGame:input_type -> crabs.evening_detective_server.AddGameReq + 106, // 165: crabs.evening_detective_server.EveningDetectiveServer.GetGames:input_type -> crabs.evening_detective_server.GetGamesReq + 159, // 166: crabs.evening_detective_server.EveningDetectiveServer.GetGame:input_type -> crabs.evening_detective_server.GetGameReq + 245, // 167: crabs.evening_detective_server.EveningDetectiveServer.ImportGame:input_type -> google.api.HttpBody + 233, // 168: crabs.evening_detective_server.EveningDetectiveServer.DownloadGameResultsPackage:input_type -> crabs.evening_detective_server.GameResultsPackageReq + 234, // 169: crabs.evening_detective_server.EveningDetectiveServer.ApplyGameResultsPackage:input_type -> crabs.evening_detective_server.ApplyGameResultsPackageReq + 111, // 170: crabs.evening_detective_server.EveningDetectiveServer.StartGameExport:input_type -> crabs.evening_detective_server.StartGameExportReq + 112, // 171: crabs.evening_detective_server.EveningDetectiveServer.GetGameExport:input_type -> crabs.evening_detective_server.GetGameExportReq + 213, // 172: crabs.evening_detective_server.EveningDetectiveServer.IssueGameTickets:input_type -> crabs.evening_detective_server.IssueGameTicketsReq + 216, // 173: crabs.evening_detective_server.EveningDetectiveServer.GetTicket:input_type -> crabs.evening_detective_server.GetTicketReq + 218, // 174: crabs.evening_detective_server.EveningDetectiveServer.AcceptTicket:input_type -> crabs.evening_detective_server.AcceptTicketReq + 115, // 175: crabs.evening_detective_server.EveningDetectiveServer.CreateScreenSession:input_type -> crabs.evening_detective_server.CreateScreenSessionReq + 117, // 176: crabs.evening_detective_server.EveningDetectiveServer.GetScreenState:input_type -> crabs.evening_detective_server.GetScreenStateReq + 119, // 177: crabs.evening_detective_server.EveningDetectiveServer.GetGameScreens:input_type -> crabs.evening_detective_server.GetGameScreensReq + 122, // 178: crabs.evening_detective_server.EveningDetectiveServer.GetGameMusic:input_type -> crabs.evening_detective_server.GetGameMusicReq + 124, // 179: crabs.evening_detective_server.EveningDetectiveServer.SetGameMusic:input_type -> crabs.evening_detective_server.SetGameMusicReq + 126, // 180: crabs.evening_detective_server.EveningDetectiveServer.AdvanceScreenMusic:input_type -> crabs.evening_detective_server.AdvanceScreenMusicReq + 128, // 181: crabs.evening_detective_server.EveningDetectiveServer.BindScreen:input_type -> crabs.evening_detective_server.BindScreenReq + 130, // 182: crabs.evening_detective_server.EveningDetectiveServer.UnbindScreen:input_type -> crabs.evening_detective_server.UnbindScreenReq + 132, // 183: crabs.evening_detective_server.EveningDetectiveServer.SetScreenSound:input_type -> crabs.evening_detective_server.SetScreenSoundReq + 135, // 184: crabs.evening_detective_server.EveningDetectiveServer.GetGameMap:input_type -> crabs.evening_detective_server.GetGameMapReq + 137, // 185: crabs.evening_detective_server.EveningDetectiveServer.UpdateGameMapItem:input_type -> crabs.evening_detective_server.UpdateGameMapItemReq + 139, // 186: crabs.evening_detective_server.EveningDetectiveServer.SetScreenMode:input_type -> crabs.evening_detective_server.SetScreenModeReq + 141, // 187: crabs.evening_detective_server.EveningDetectiveServer.SetScreenTimer:input_type -> crabs.evening_detective_server.SetScreenTimerReq + 143, // 188: crabs.evening_detective_server.EveningDetectiveServer.SendScreenCommand:input_type -> crabs.evening_detective_server.SendScreenCommandReq + 147, // 189: crabs.evening_detective_server.EveningDetectiveServer.GetScreenPlan:input_type -> crabs.evening_detective_server.GetScreenPlanReq + 149, // 190: crabs.evening_detective_server.EveningDetectiveServer.SaveScreenPlan:input_type -> crabs.evening_detective_server.SaveScreenPlanReq + 151, // 191: crabs.evening_detective_server.EveningDetectiveServer.SetScreenPlanStep:input_type -> crabs.evening_detective_server.SetScreenPlanStepReq + 153, // 192: crabs.evening_detective_server.EveningDetectiveServer.GetScreenResults:input_type -> crabs.evening_detective_server.GetScreenResultsReq + 159, // 193: crabs.evening_detective_server.EveningDetectiveServer.DownloadTeamQR:input_type -> crabs.evening_detective_server.GetGameReq + 161, // 194: crabs.evening_detective_server.EveningDetectiveServer.GetGameAnswers:input_type -> crabs.evening_detective_server.GetGameAnswersReq + 163, // 195: crabs.evening_detective_server.EveningDetectiveServer.GetGameResults:input_type -> crabs.evening_detective_server.GetGameResultsReq + 168, // 196: crabs.evening_detective_server.EveningDetectiveServer.CreateGameReview:input_type -> crabs.evening_detective_server.CreateGameReviewReq + 170, // 197: crabs.evening_detective_server.EveningDetectiveServer.GetGameReviews:input_type -> crabs.evening_detective_server.GetGameReviewsReq + 171, // 198: crabs.evening_detective_server.EveningDetectiveServer.GetScenarioReviews:input_type -> crabs.evening_detective_server.GetScenarioReviewsReq + 170, // 199: crabs.evening_detective_server.EveningDetectiveServer.GetGameReviewsForModeration:input_type -> crabs.evening_detective_server.GetGameReviewsReq + 172, // 200: crabs.evening_detective_server.EveningDetectiveServer.GetReviewsForModeration:input_type -> crabs.evening_detective_server.GetReviewsForModerationReq + 174, // 201: crabs.evening_detective_server.EveningDetectiveServer.ModerateGameReview:input_type -> crabs.evening_detective_server.ModerateGameReviewReq + 176, // 202: crabs.evening_detective_server.EveningDetectiveServer.UpdateGame:input_type -> crabs.evening_detective_server.UpdateGameReq + 178, // 203: crabs.evening_detective_server.EveningDetectiveServer.StartGame:input_type -> crabs.evening_detective_server.StartGameReq + 180, // 204: crabs.evening_detective_server.EveningDetectiveServer.PauseGame:input_type -> crabs.evening_detective_server.PauseGameReq + 182, // 205: crabs.evening_detective_server.EveningDetectiveServer.PlayGame:input_type -> crabs.evening_detective_server.PlayGameReq + 184, // 206: crabs.evening_detective_server.EveningDetectiveServer.FinishGame:input_type -> crabs.evening_detective_server.FinishGameReq + 186, // 207: crabs.evening_detective_server.EveningDetectiveServer.ResetGame:input_type -> crabs.evening_detective_server.ResetGameReq + 188, // 208: crabs.evening_detective_server.EveningDetectiveServer.DeleteGame:input_type -> crabs.evening_detective_server.DeleteGameReq + 190, // 209: crabs.evening_detective_server.EveningDetectiveServer.AddTeam:input_type -> crabs.evening_detective_server.AddTeamReq + 192, // 210: crabs.evening_detective_server.EveningDetectiveServer.UpdateTeam:input_type -> crabs.evening_detective_server.UpdateTeamReq + 194, // 211: crabs.evening_detective_server.EveningDetectiveServer.DeleteTeam:input_type -> crabs.evening_detective_server.DeleteTeamReq + 196, // 212: crabs.evening_detective_server.EveningDetectiveServer.GiveTeamApplications:input_type -> crabs.evening_detective_server.GiveTeamApplicationsReq + 198, // 213: crabs.evening_detective_server.EveningDetectiveServer.GetTeamStory:input_type -> crabs.evening_detective_server.GetTeamStoryReq + 201, // 214: crabs.evening_detective_server.EveningDetectiveServer.AddTeamAction:input_type -> crabs.evening_detective_server.AddTeamActionReq + 203, // 215: crabs.evening_detective_server.EveningDetectiveServer.DeleteLastTeamAction:input_type -> crabs.evening_detective_server.DeleteLastTeamActionReq + 205, // 216: crabs.evening_detective_server.EveningDetectiveServer.GetTeamAnswers:input_type -> crabs.evening_detective_server.GetTeamAnswersReq + 207, // 217: crabs.evening_detective_server.EveningDetectiveServer.SubmitTeamAnswers:input_type -> crabs.evening_detective_server.SubmitTeamAnswersReq + 209, // 218: crabs.evening_detective_server.EveningDetectiveServer.SetTeamAnswerScore:input_type -> crabs.evening_detective_server.SetTeamAnswerScoreReq + 237, // 219: crabs.evening_detective_server.EveningDetectiveServer.DeviceHeartbeat:output_type -> crabs.evening_detective_server.DeviceHeartbeatRsp + 239, // 220: crabs.evening_detective_server.EveningDetectiveServer.GetDevices:output_type -> crabs.evening_detective_server.GetDevicesRsp + 224, // 221: crabs.evening_detective_server.EveningDetectiveServer.GetTasks:output_type -> crabs.evening_detective_server.GetTasksRsp + 226, // 222: crabs.evening_detective_server.EveningDetectiveServer.GetTaskAssignees:output_type -> crabs.evening_detective_server.GetTaskAssigneesRsp + 231, // 223: crabs.evening_detective_server.EveningDetectiveServer.CreateTask:output_type -> crabs.evening_detective_server.TaskRsp + 231, // 224: crabs.evening_detective_server.EveningDetectiveServer.UpdateTask:output_type -> crabs.evening_detective_server.TaskRsp + 231, // 225: crabs.evening_detective_server.EveningDetectiveServer.SetTaskStatus:output_type -> crabs.evening_detective_server.TaskRsp + 232, // 226: crabs.evening_detective_server.EveningDetectiveServer.DeleteTask:output_type -> crabs.evening_detective_server.DeleteTaskRsp + 157, // 227: crabs.evening_detective_server.EveningDetectiveServer.GetParticipation:output_type -> crabs.evening_detective_server.ParticipationRsp + 157, // 228: crabs.evening_detective_server.EveningDetectiveServer.AnswerParticipation:output_type -> crabs.evening_detective_server.ParticipationRsp + 5, // 229: crabs.evening_detective_server.EveningDetectiveServer.Ping:output_type -> crabs.evening_detective_server.PingRsp + 7, // 230: crabs.evening_detective_server.EveningDetectiveServer.Echo:output_type -> crabs.evening_detective_server.EchoRsp + 9, // 231: crabs.evening_detective_server.EveningDetectiveServer.Signup:output_type -> crabs.evening_detective_server.SignupRsp + 11, // 232: crabs.evening_detective_server.EveningDetectiveServer.RefreshPassword:output_type -> crabs.evening_detective_server.RefreshPasswordRsp + 13, // 233: crabs.evening_detective_server.EveningDetectiveServer.Login:output_type -> crabs.evening_detective_server.LoginRsp + 15, // 234: crabs.evening_detective_server.EveningDetectiveServer.Refresh:output_type -> crabs.evening_detective_server.RefreshRsp + 17, // 235: crabs.evening_detective_server.EveningDetectiveServer.DeleteAccount:output_type -> crabs.evening_detective_server.DeleteAccountRsp + 19, // 236: crabs.evening_detective_server.EveningDetectiveServer.GetTerms:output_type -> crabs.evening_detective_server.GetTermsRsp + 21, // 237: crabs.evening_detective_server.EveningDetectiveServer.GetPrivacy:output_type -> crabs.evening_detective_server.GetPrivacyRsp + 23, // 238: crabs.evening_detective_server.EveningDetectiveServer.GetConsent:output_type -> crabs.evening_detective_server.GetConsentRsp + 25, // 239: crabs.evening_detective_server.EveningDetectiveServer.GetUsers:output_type -> crabs.evening_detective_server.GetUsersRsp + 28, // 240: crabs.evening_detective_server.EveningDetectiveServer.GetUserByID:output_type -> crabs.evening_detective_server.GetUserByIDRsp + 30, // 241: crabs.evening_detective_server.EveningDetectiveServer.GetMe:output_type -> crabs.evening_detective_server.GetMeRsp + 32, // 242: crabs.evening_detective_server.EveningDetectiveServer.ChangeUserPassword:output_type -> crabs.evening_detective_server.ChangeUserPasswordRsp + 34, // 243: crabs.evening_detective_server.EveningDetectiveServer.AddUserRole:output_type -> crabs.evening_detective_server.AddUserRoleRsp + 36, // 244: crabs.evening_detective_server.EveningDetectiveServer.DeleteUserRole:output_type -> crabs.evening_detective_server.DeleteUserRoleRsp + 38, // 245: crabs.evening_detective_server.EveningDetectiveServer.GetPermissions:output_type -> crabs.evening_detective_server.GetPermissionsRsp + 40, // 246: crabs.evening_detective_server.EveningDetectiveServer.UploadFile:output_type -> crabs.evening_detective_server.UploadFileRsp + 245, // 247: crabs.evening_detective_server.EveningDetectiveServer.DownloadFile:output_type -> google.api.HttpBody + 45, // 248: crabs.evening_detective_server.EveningDetectiveServer.GetPlaylists:output_type -> crabs.evening_detective_server.GetPlaylistsRsp + 47, // 249: crabs.evening_detective_server.EveningDetectiveServer.CreatePlaylist:output_type -> crabs.evening_detective_server.CreatePlaylistRsp + 49, // 250: crabs.evening_detective_server.EveningDetectiveServer.UpdatePlaylist:output_type -> crabs.evening_detective_server.UpdatePlaylistRsp + 51, // 251: crabs.evening_detective_server.EveningDetectiveServer.DeletePlaylist:output_type -> crabs.evening_detective_server.DeletePlaylistRsp + 53, // 252: crabs.evening_detective_server.EveningDetectiveServer.UploadPlaylistTrack:output_type -> crabs.evening_detective_server.UploadPlaylistTrackRsp + 55, // 253: crabs.evening_detective_server.EveningDetectiveServer.DeletePlaylistTrack:output_type -> crabs.evening_detective_server.DeletePlaylistTrackRsp + 57, // 254: crabs.evening_detective_server.EveningDetectiveServer.SetScenarioPlaylist:output_type -> crabs.evening_detective_server.SetScenarioPlaylistRsp + 59, // 255: crabs.evening_detective_server.EveningDetectiveServer.AddScenario:output_type -> crabs.evening_detective_server.AddScenarioRsp + 61, // 256: crabs.evening_detective_server.EveningDetectiveServer.GetMyScenarios:output_type -> crabs.evening_detective_server.GetMyScenariosRsp + 63, // 257: crabs.evening_detective_server.EveningDetectiveServer.GetScenariosCatalog:output_type -> crabs.evening_detective_server.GetScenariosCatalogRsp + 65, // 258: crabs.evening_detective_server.EveningDetectiveServer.GetFullScenario:output_type -> crabs.evening_detective_server.GetScenarioRsp + 65, // 259: crabs.evening_detective_server.EveningDetectiveServer.GetScenario:output_type -> crabs.evening_detective_server.GetScenarioRsp + 79, // 260: crabs.evening_detective_server.EveningDetectiveServer.UpdateScenario:output_type -> crabs.evening_detective_server.UpdateScenarioRsp + 81, // 261: crabs.evening_detective_server.EveningDetectiveServer.PublicScenario:output_type -> crabs.evening_detective_server.PublicScenarioRsp + 83, // 262: crabs.evening_detective_server.EveningDetectiveServer.DraftScenario:output_type -> crabs.evening_detective_server.DraftScenarioRsp + 85, // 263: crabs.evening_detective_server.EveningDetectiveServer.DeleteScenario:output_type -> crabs.evening_detective_server.DeleteScenarioRsp + 87, // 264: crabs.evening_detective_server.EveningDetectiveServer.AddScenarioPlace:output_type -> crabs.evening_detective_server.AddScenarioPlaceRsp + 89, // 265: crabs.evening_detective_server.EveningDetectiveServer.UpdateScenarioPlace:output_type -> crabs.evening_detective_server.UpdateScenarioPlaceRsp + 91, // 266: crabs.evening_detective_server.EveningDetectiveServer.DeleteScenarioPlace:output_type -> crabs.evening_detective_server.DeleteScenarioPlaceRsp + 97, // 267: crabs.evening_detective_server.EveningDetectiveServer.AddScenarioQuestion:output_type -> crabs.evening_detective_server.AddScenarioQuestionRsp + 99, // 268: crabs.evening_detective_server.EveningDetectiveServer.UpdateScenarioQuestion:output_type -> crabs.evening_detective_server.UpdateScenarioQuestionRsp + 101, // 269: crabs.evening_detective_server.EveningDetectiveServer.DeleteScenarioQuestion:output_type -> crabs.evening_detective_server.DeleteScenarioQuestionRsp + 212, // 270: crabs.evening_detective_server.EveningDetectiveServer.UpdateScenarioBackground:output_type -> crabs.evening_detective_server.UpdateScenarioBackgroundRsp + 95, // 271: crabs.evening_detective_server.EveningDetectiveServer.UpdateScenarioIntro:output_type -> crabs.evening_detective_server.UpdateScenarioIntroRsp + 93, // 272: crabs.evening_detective_server.EveningDetectiveServer.UpdateScenarioConclusion:output_type -> crabs.evening_detective_server.UpdateScenarioConclusionRsp + 245, // 273: crabs.evening_detective_server.EveningDetectiveServer.DownloadScenarioArchive:output_type -> google.api.HttpBody + 103, // 274: crabs.evening_detective_server.EveningDetectiveServer.UploadScenarioArchive:output_type -> crabs.evening_detective_server.UploadScenarioArchiveRsp + 105, // 275: crabs.evening_detective_server.EveningDetectiveServer.AddGame:output_type -> crabs.evening_detective_server.AddGameRsp + 107, // 276: crabs.evening_detective_server.EveningDetectiveServer.GetGames:output_type -> crabs.evening_detective_server.GetGamesRsp + 160, // 277: crabs.evening_detective_server.EveningDetectiveServer.GetGame:output_type -> crabs.evening_detective_server.GetGameRsp + 110, // 278: crabs.evening_detective_server.EveningDetectiveServer.ImportGame:output_type -> crabs.evening_detective_server.GameImportRsp + 245, // 279: crabs.evening_detective_server.EveningDetectiveServer.DownloadGameResultsPackage:output_type -> google.api.HttpBody + 235, // 280: crabs.evening_detective_server.EveningDetectiveServer.ApplyGameResultsPackage:output_type -> crabs.evening_detective_server.GameResultsPackageRsp + 113, // 281: crabs.evening_detective_server.EveningDetectiveServer.StartGameExport:output_type -> crabs.evening_detective_server.GameExportRsp + 113, // 282: crabs.evening_detective_server.EveningDetectiveServer.GetGameExport:output_type -> crabs.evening_detective_server.GameExportRsp + 214, // 283: crabs.evening_detective_server.EveningDetectiveServer.IssueGameTickets:output_type -> crabs.evening_detective_server.IssueGameTicketsRsp + 217, // 284: crabs.evening_detective_server.EveningDetectiveServer.GetTicket:output_type -> crabs.evening_detective_server.GetTicketRsp + 219, // 285: crabs.evening_detective_server.EveningDetectiveServer.AcceptTicket:output_type -> crabs.evening_detective_server.AcceptTicketRsp + 116, // 286: crabs.evening_detective_server.EveningDetectiveServer.CreateScreenSession:output_type -> crabs.evening_detective_server.CreateScreenSessionRsp + 118, // 287: crabs.evening_detective_server.EveningDetectiveServer.GetScreenState:output_type -> crabs.evening_detective_server.GetScreenStateRsp + 120, // 288: crabs.evening_detective_server.EveningDetectiveServer.GetGameScreens:output_type -> crabs.evening_detective_server.GetGameScreensRsp + 123, // 289: crabs.evening_detective_server.EveningDetectiveServer.GetGameMusic:output_type -> crabs.evening_detective_server.GetGameMusicRsp + 125, // 290: crabs.evening_detective_server.EveningDetectiveServer.SetGameMusic:output_type -> crabs.evening_detective_server.SetGameMusicRsp + 127, // 291: crabs.evening_detective_server.EveningDetectiveServer.AdvanceScreenMusic:output_type -> crabs.evening_detective_server.AdvanceScreenMusicRsp + 129, // 292: crabs.evening_detective_server.EveningDetectiveServer.BindScreen:output_type -> crabs.evening_detective_server.BindScreenRsp + 131, // 293: crabs.evening_detective_server.EveningDetectiveServer.UnbindScreen:output_type -> crabs.evening_detective_server.UnbindScreenRsp + 133, // 294: crabs.evening_detective_server.EveningDetectiveServer.SetScreenSound:output_type -> crabs.evening_detective_server.SetScreenSoundRsp + 136, // 295: crabs.evening_detective_server.EveningDetectiveServer.GetGameMap:output_type -> crabs.evening_detective_server.GetGameMapRsp + 138, // 296: crabs.evening_detective_server.EveningDetectiveServer.UpdateGameMapItem:output_type -> crabs.evening_detective_server.UpdateGameMapItemRsp + 140, // 297: crabs.evening_detective_server.EveningDetectiveServer.SetScreenMode:output_type -> crabs.evening_detective_server.SetScreenModeRsp + 142, // 298: crabs.evening_detective_server.EveningDetectiveServer.SetScreenTimer:output_type -> crabs.evening_detective_server.SetScreenTimerRsp + 144, // 299: crabs.evening_detective_server.EveningDetectiveServer.SendScreenCommand:output_type -> crabs.evening_detective_server.SendScreenCommandRsp + 148, // 300: crabs.evening_detective_server.EveningDetectiveServer.GetScreenPlan:output_type -> crabs.evening_detective_server.GetScreenPlanRsp + 150, // 301: crabs.evening_detective_server.EveningDetectiveServer.SaveScreenPlan:output_type -> crabs.evening_detective_server.SaveScreenPlanRsp + 152, // 302: crabs.evening_detective_server.EveningDetectiveServer.SetScreenPlanStep:output_type -> crabs.evening_detective_server.SetScreenPlanStepRsp + 154, // 303: crabs.evening_detective_server.EveningDetectiveServer.GetScreenResults:output_type -> crabs.evening_detective_server.GetScreenResultsRsp + 245, // 304: crabs.evening_detective_server.EveningDetectiveServer.DownloadTeamQR:output_type -> google.api.HttpBody + 162, // 305: crabs.evening_detective_server.EveningDetectiveServer.GetGameAnswers:output_type -> crabs.evening_detective_server.GetGameAnswersRsp + 166, // 306: crabs.evening_detective_server.EveningDetectiveServer.GetGameResults:output_type -> crabs.evening_detective_server.GetGameResultsRsp + 169, // 307: crabs.evening_detective_server.EveningDetectiveServer.CreateGameReview:output_type -> crabs.evening_detective_server.CreateGameReviewRsp + 173, // 308: crabs.evening_detective_server.EveningDetectiveServer.GetGameReviews:output_type -> crabs.evening_detective_server.GetGameReviewsRsp + 173, // 309: crabs.evening_detective_server.EveningDetectiveServer.GetScenarioReviews:output_type -> crabs.evening_detective_server.GetGameReviewsRsp + 173, // 310: crabs.evening_detective_server.EveningDetectiveServer.GetGameReviewsForModeration:output_type -> crabs.evening_detective_server.GetGameReviewsRsp + 173, // 311: crabs.evening_detective_server.EveningDetectiveServer.GetReviewsForModeration:output_type -> crabs.evening_detective_server.GetGameReviewsRsp + 175, // 312: crabs.evening_detective_server.EveningDetectiveServer.ModerateGameReview:output_type -> crabs.evening_detective_server.ModerateGameReviewRsp + 177, // 313: crabs.evening_detective_server.EveningDetectiveServer.UpdateGame:output_type -> crabs.evening_detective_server.UpdateGameRsp + 179, // 314: crabs.evening_detective_server.EveningDetectiveServer.StartGame:output_type -> crabs.evening_detective_server.StartGameRsp + 181, // 315: crabs.evening_detective_server.EveningDetectiveServer.PauseGame:output_type -> crabs.evening_detective_server.PauseGameRsp + 183, // 316: crabs.evening_detective_server.EveningDetectiveServer.PlayGame:output_type -> crabs.evening_detective_server.PlayGameRsp + 185, // 317: crabs.evening_detective_server.EveningDetectiveServer.FinishGame:output_type -> crabs.evening_detective_server.FinishGameRsp + 187, // 318: crabs.evening_detective_server.EveningDetectiveServer.ResetGame:output_type -> crabs.evening_detective_server.ResetGameRsp + 189, // 319: crabs.evening_detective_server.EveningDetectiveServer.DeleteGame:output_type -> crabs.evening_detective_server.DeleteGameRsp + 191, // 320: crabs.evening_detective_server.EveningDetectiveServer.AddTeam:output_type -> crabs.evening_detective_server.AddTeamRsp + 193, // 321: crabs.evening_detective_server.EveningDetectiveServer.UpdateTeam:output_type -> crabs.evening_detective_server.UpdateTeamRsp + 195, // 322: crabs.evening_detective_server.EveningDetectiveServer.DeleteTeam:output_type -> crabs.evening_detective_server.DeleteTeamRsp + 197, // 323: crabs.evening_detective_server.EveningDetectiveServer.GiveTeamApplications:output_type -> crabs.evening_detective_server.GiveTeamApplicationsRsp + 199, // 324: crabs.evening_detective_server.EveningDetectiveServer.GetTeamStory:output_type -> crabs.evening_detective_server.GetTeamStoryRsp + 202, // 325: crabs.evening_detective_server.EveningDetectiveServer.AddTeamAction:output_type -> crabs.evening_detective_server.AddTeamActionRsp + 204, // 326: crabs.evening_detective_server.EveningDetectiveServer.DeleteLastTeamAction:output_type -> crabs.evening_detective_server.DeleteLastTeamActionRsp + 206, // 327: crabs.evening_detective_server.EveningDetectiveServer.GetTeamAnswers:output_type -> crabs.evening_detective_server.GetTeamAnswersRsp + 208, // 328: crabs.evening_detective_server.EveningDetectiveServer.SubmitTeamAnswers:output_type -> crabs.evening_detective_server.SubmitTeamAnswersRsp + 210, // 329: crabs.evening_detective_server.EveningDetectiveServer.SetTeamAnswerScore:output_type -> crabs.evening_detective_server.SetTeamAnswerScoreRsp + 219, // [219:330] is the sub-list for method output_type + 108, // [108:219] is the sub-list for method input_type + 108, // [108:108] is the sub-list for extension type_name + 108, // [108:108] is the sub-list for extension extendee + 0, // [0:108] is the sub-list for field type_name } func init() { file_main_proto_init() } @@ -14399,13 +14570,14 @@ func file_main_proto_init() { file_main_proto_msgTypes[233].OneofWrappers = []any{} file_main_proto_msgTypes[234].OneofWrappers = []any{} file_main_proto_msgTypes[237].OneofWrappers = []any{} + file_main_proto_msgTypes[239].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: 238, + NumMessages: 240, NumExtensions: 0, NumServices: 1, },