This commit is contained in:
2026-10-11 14:59:51 +07:00
parent e3576db693
commit 5da08f6916
17 changed files with 1235 additions and 258 deletions
+16
View File
@@ -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.
}
+88
View File
@@ -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)
}
+34
View File
@@ -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")
}
}
+6
View File
@@ -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")),
),
)
@@ -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": {
+2
View File
@@ -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
+27
View File
@@ -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
}
+35
View File
@@ -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)
}
}
+4
View File
@@ -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,
+175
View File
@@ -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())
}
+123
View File
@@ -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
}
+120
View File
@@ -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(`<tr id="cbi-table-%s"><td><div id="cbi-table-%s-hostname"><p>PRIVATE NAME<br><span>%s</span></p></div></td><td><div id="cbi-table-%s-ipmac"><p>192.168.10.9<br>%s</p><p>192.168.10.9<br>%s</p></div></td><td><div id="cbi-table-%s-signal"><p>%s</p><p>%s</p></div></td></tr>`, id, id, band, id, mac, mac, id, signal, signal)
}
func clientTable(rows string) string {
return `<table><thead><tr><th>IP / MAC Address</th></tr></thead><tbody>` + rows + `</tbody></table>`
}
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{`<form>Login</form>`, 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, `<form><input type="hidden" name="salt" value="salt"><input type="hidden" name="_csrf" value="csrf"></form>`)
})
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")
}
}
}
+26
View File
@@ -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)
}
@@ -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)
+43
View File
@@ -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
}
@@ -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")
}
}
+429 -257
View File
@@ -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,
},