├── .gitignore ├── .vscode └── launch.json ├── README.md ├── Rising Thunder CE EULA.pdf ├── TODO.md ├── generate_python.py ├── protos ├── tbadmin │ ├── account.proto │ ├── audit.proto │ ├── config.proto │ ├── match.proto │ ├── report.proto │ ├── shop.proto │ └── stats.proto ├── tbmatch │ ├── account.proto │ ├── crash.proto │ ├── event.proto │ ├── lobby.proto │ ├── log.proto │ ├── match.proto │ ├── query.proto │ ├── session.proto │ ├── shop.proto │ └── user.proto ├── tbportal │ └── portal.proto ├── tbrpc │ └── tbrpc.proto └── tbui │ └── tbcharacter.proto ├── rtd.py ├── scripts ├── generate_protos.cmd ├── generate_python.cmd ├── launch_rt.cmd ├── setup.cmd └── templates │ ├── routes.template │ └── rpc_client.template ├── server ├── __init__.py ├── config.py ├── generated_routes.py ├── models │ ├── __init__.py │ ├── lobbies.py │ ├── match.py │ ├── matchmaker.py │ ├── portal.py │ └── users.py ├── rpc.py └── services │ ├── __init__.py │ ├── event_service.py │ ├── lobby_service.py │ ├── match_service.py │ └── session_service.py ├── tbadmin ├── __init__.py ├── account_pb2.py ├── audit_pb2.py ├── config_pb2.py ├── match_pb2.py ├── report_pb2.py ├── shop_pb2.py └── stats_pb2.py ├── tbmatch ├── __init__.py ├── account_pb2.py ├── crash_pb2.py ├── event_pb2.py ├── lobby_pb2.py ├── log_pb2.py ├── match_pb2.py ├── query_pb2.py ├── session_pb2.py ├── shop_pb2.py └── user_pb2.py ├── tbportal ├── __init__.py └── portal_pb2.py ├── tbrpc ├── __init__.py └── tbrpc_pb2.py ├── tbui ├── __init__.py └── tbcharacter_pb2.py └── tests ├── __init__.py ├── game_client.py ├── rpc_client.py ├── test_home_screen.py ├── test_lobbies.py └── test_matchmaker.py /.gitignore: -------------------------------------------------------------------------------- 1 | .cache 2 | *.pyc 3 | tests/__pycache__ 4 | Rising Thunder/* 5 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Rising Thunder CE", 6 | "type": "python", 7 | "request": "launch", 8 | "stopOnEntry": false, 9 | "pythonPath": "${config:python.pythonPath}", 10 | "program": "${workspaceRoot}/rtd.py", 11 | "cwd": "${workspaceRoot}", 12 | "env": {}, 13 | "envFile": "${workspaceRoot}/.env", 14 | "args": [ 15 | "--logging=debug" 16 | ], 17 | "debugOptions": [ 18 | "WaitOnAbnormalExit", 19 | "RedirectOutput" 20 | ] 21 | }, 22 | { 23 | "name": "Python", 24 | "type": "python", 25 | "request": "launch", 26 | "stopOnEntry": false, 27 | "pythonPath": "${config:python.pythonPath}", 28 | "program": "${file}", 29 | "cwd": "${workspaceRoot}", 30 | "env": {}, 31 | "envFile": "${workspaceRoot}/.env", 32 | "debugOptions": [ 33 | "WaitOnAbnormalExit", 34 | "RedirectOutput" 35 | ] 36 | }, 37 | { 38 | "name": "PySpark", 39 | "type": "python", 40 | "request": "launch", 41 | "stopOnEntry": true, 42 | "osx": { 43 | "pythonPath": "${env:SPARK_HOME}/bin/spark-submit" 44 | }, 45 | "windows": { 46 | "pythonPath": "${env:SPARK_HOME}/bin/spark-submit.cmd" 47 | }, 48 | "linux": { 49 | "pythonPath": "${env:SPARK_HOME}/bin/spark-submit" 50 | }, 51 | "program": "${file}", 52 | "cwd": "${workspaceRoot}", 53 | "env": {}, 54 | "envFile": "${workspaceRoot}/.env", 55 | "debugOptions": [ 56 | "WaitOnAbnormalExit", 57 | "WaitOnNormalExit", 58 | "RedirectOutput" 59 | ] 60 | }, 61 | { 62 | "name": "Python Module", 63 | "type": "python", 64 | "request": "launch", 65 | "stopOnEntry": true, 66 | "pythonPath": "${config:python.pythonPath}", 67 | "module": "module.name", 68 | "cwd": "${workspaceRoot}", 69 | "env": {}, 70 | "envFile": "${workspaceRoot}/.env", 71 | "debugOptions": [ 72 | "WaitOnAbnormalExit", 73 | "WaitOnNormalExit", 74 | "RedirectOutput" 75 | ] 76 | }, 77 | { 78 | "name": "Integrated Terminal/Console", 79 | "type": "python", 80 | "request": "launch", 81 | "stopOnEntry": true, 82 | "pythonPath": "${config:python.pythonPath}", 83 | "program": "${file}", 84 | "cwd": "", 85 | "console": "integratedTerminal", 86 | "env": {}, 87 | "envFile": "${workspaceRoot}/.env", 88 | "debugOptions": [ 89 | "WaitOnAbnormalExit", 90 | "WaitOnNormalExit" 91 | ] 92 | }, 93 | { 94 | "name": "External Terminal/Console", 95 | "type": "python", 96 | "request": "launch", 97 | "stopOnEntry": true, 98 | "pythonPath": "${config:python.pythonPath}", 99 | "program": "${file}", 100 | "cwd": "", 101 | "console": "externalTerminal", 102 | "env": {}, 103 | "envFile": "${workspaceRoot}/.env", 104 | "debugOptions": [ 105 | "WaitOnAbnormalExit", 106 | "WaitOnNormalExit" 107 | ] 108 | }, 109 | { 110 | "name": "Django", 111 | "type": "python", 112 | "request": "launch", 113 | "stopOnEntry": true, 114 | "pythonPath": "${config:python.pythonPath}", 115 | "program": "${workspaceRoot}/manage.py", 116 | "cwd": "${workspaceRoot}", 117 | "args": [ 118 | "runserver", 119 | "--noreload", 120 | "--nothreading" 121 | ], 122 | "env": {}, 123 | "envFile": "${workspaceRoot}/.env", 124 | "debugOptions": [ 125 | "WaitOnAbnormalExit", 126 | "WaitOnNormalExit", 127 | "RedirectOutput", 128 | "DjangoDebugging" 129 | ] 130 | }, 131 | { 132 | "name": "Flask", 133 | "type": "python", 134 | "request": "launch", 135 | "stopOnEntry": false, 136 | "pythonPath": "${config:python.pythonPath}", 137 | "module": "flask", 138 | "cwd": "${workspaceRoot}", 139 | "env": { 140 | "FLASK_APP": "rtd.py" 141 | }, 142 | "args": [ 143 | "run", 144 | "--no-debugger", 145 | "--no-reload", 146 | "--port=1337" 147 | ], 148 | "envFile": "${workspaceRoot}/.env", 149 | "debugOptions": [ 150 | "WaitOnAbnormalExit", 151 | "WaitOnNormalExit", 152 | "RedirectOutput" 153 | ] 154 | }, 155 | { 156 | "name": "Flask (old)", 157 | "type": "python", 158 | "request": "launch", 159 | "stopOnEntry": false, 160 | "pythonPath": "${config:python.pythonPath}", 161 | "program": "${workspaceRoot}/run.py", 162 | "cwd": "${workspaceRoot}", 163 | "args": [], 164 | "env": {}, 165 | "envFile": "${workspaceRoot}/.env", 166 | "debugOptions": [ 167 | "WaitOnAbnormalExit", 168 | "WaitOnNormalExit", 169 | "RedirectOutput" 170 | ] 171 | }, 172 | { 173 | "name": "Pyramid", 174 | "type": "python", 175 | "request": "launch", 176 | "stopOnEntry": true, 177 | "pythonPath": "${config:python.pythonPath}", 178 | "cwd": "${workspaceRoot}", 179 | "env": {}, 180 | "envFile": "${workspaceRoot}/.env", 181 | "args": [ 182 | "${workspaceRoot}/development.ini" 183 | ], 184 | "debugOptions": [ 185 | "WaitOnAbnormalExit", 186 | "WaitOnNormalExit", 187 | "RedirectOutput", 188 | "Pyramid" 189 | ] 190 | }, 191 | { 192 | "name": "Watson", 193 | "type": "python", 194 | "request": "launch", 195 | "stopOnEntry": true, 196 | "pythonPath": "${config:python.pythonPath}", 197 | "program": "${workspaceRoot}/console.py", 198 | "cwd": "${workspaceRoot}", 199 | "args": [ 200 | "dev", 201 | "runserver", 202 | "--noreload=True" 203 | ], 204 | "env": {}, 205 | "envFile": "${workspaceRoot}/.env", 206 | "debugOptions": [ 207 | "WaitOnAbnormalExit", 208 | "WaitOnNormalExit", 209 | "RedirectOutput" 210 | ] 211 | }, 212 | { 213 | "name": "Attach (Remote Debug)", 214 | "type": "python", 215 | "request": "attach", 216 | "localRoot": "${workspaceRoot}", 217 | "remoteRoot": "${workspaceRoot}", 218 | "port": 3000, 219 | "secret": "my_secret", 220 | "host": "localhost" 221 | } 222 | ] 223 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rising Thunder Community Editon 2 | Open Rising Thunder open server implementation 3 | 4 | ## Getting Started 5 | 6 | This should get you going: 7 | 1. Install the latest version of Python 2.x 8 | 2. Install the following dependecies: tornado, grpc, protobuf. I prefer to use pip (i.e. `pip install tornado, grpc, protobuf`), but whatever floats your boat. 9 | 3. Run the server via `python rtd.py` 10 | 11 | ## Guided Tour 12 | 13 | Here's a brief overview of the source to get you started: 14 | 15 | * **protos** - Contains the source to the protocol buffers which describe the Rising Thunder server RPC system. We've included all of the protobufs used for Rising Thunder, including some which are used in portions of the game not currently implemented by this server (e.g. the account creation bits on our website). 16 | * **scripts** - Some helper scripts useful for working with the server. See the comments in each individual script for more details. 17 | * **server** - The server implementation. Uses the Tornado python web framework and code generated from the protos to implement a very, very basic Rising Thunder server 18 | * **tbadmin**, **tbui**, **etc.** - These files implement a python interface to the data structures described in the protos. They're automatically generated by the scripts/generate_protos.cmd script 19 | tests - Some automated tests, using the pytest framework. 20 | 21 | ## Terminalogy 22 | 23 | * **tb** - The prefix we used to namespace Rising Thunder code during development. Everywhere you see "tb" you can think "rt" and be pretty safe (e.g. tbrpc describes the RT RPC layer). What tb stands for remains a mystery. 24 | * **rpc** - Most of the non-game functionality of the server is implemented with an RPC layer over HTTP built using Google Protocol Buffers. Whenever the code refers to RPCs, it's talking about that. 25 | * **portal** - Most of the game-dependant functionality of the server. The portal is a UDP server which facilates game creation, spectating, detecting disconnected players, and implements the state machine required to implement RT's 2/3 games per-match flow. Its implementation is greatly complicated by the fact that UDP is a lossy protocol. Much of the state machine should probably have been implemented with RPCs, but c'est la' vie. 26 | 27 | 28 | -------------------------------------------------------------------------------- /Rising Thunder CE EULA.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RiotGames/rtce-server/5d1b9222b7f90db94e1f424986253d6dff103484/Rising Thunder CE EULA.pdf -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | # TODO 2 | 3 | ### matchmaker: 4 | - [ ] implement re-queue: 5 | - [ ] echo test failure between the clients when match about to start 6 | - [ ] test when echo test between player is too high (of a ping test), both re-queue 7 | - [ ] one of the client is not connected 8 | 9 | ### persistence: 10 | - [ ] create acccount on-demand during login 11 | - [ ] simple webpage to create account 12 | - [ ] store username, password, players preference (loadouts) 13 | 14 | ### spectating: 15 | - [ ] make portal support spectating 16 | 17 | ### accounts: 18 | - [ ] allow players to set their name instead of using randomly generated names 19 | 20 | ### lobbies: 21 | - [ ] increase capacity if spectating is available, otherwise limit to 2 players 22 | - [ ] lobby management (banning, kicking, setting ownership) 23 | 24 | ### portal: 25 | - [ ] complete portal state machine 26 | 27 | ### testing: 28 | - [ ] test that the server works on different home & work network configurations 29 | - [ ] test error cases in portal, including unexpected client disconnect 30 | 31 | -------------------------------------------------------------------------------- /generate_python.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=C0103 2 | 3 | """ 4 | create_routes.py 5 | 6 | Dynamically loads the generated protobuf python files and creates flask routes 7 | to handle RPCs advertized by the server. 8 | """ 9 | 10 | import os 11 | import sys 12 | import mako.template 13 | import mako.runtime 14 | 15 | def CreateRoutes(templateFile): 16 | """ 17 | Create flask routes for each RPC endpoint for all services. 18 | """ 19 | 20 | # The name of a type in the .proto doesn't exactly match the name of 21 | # the generated python type. We'll use this typemap to translate between 22 | # the two so we can generate correct code in the routes.py file. 23 | typemap = {} 24 | 25 | # Accumulate a list of all the services we find so we can iterate through 26 | # them in the template 27 | services = [] 28 | 29 | # Keep a list of all the protobuf python generated files we process so we 30 | # can mass import them at the top of the routes.py file. 31 | imports = [] 32 | 33 | for root, _, files in os.walk('.'): 34 | for f in files: 35 | if f.endswith('_pb2.py'): 36 | filename = os.path.join(root, f[:-3]) # make sure we pull off the .py at the end 37 | parts = os.path.normpath(filename).split(os.sep) 38 | modulename = '.'.join(parts) 39 | 40 | # load the module. since modulename is some dotted path (e.g. tbmatch.session_pb2) 41 | # the actual module with the protobuf definition is contained therein. 42 | imports.append(modulename) 43 | module = __import__(modulename) 44 | for part in parts[1:]: 45 | module = getattr(module, part) 46 | 47 | # iterate through all the symbols in the module, looking for services and message 48 | # declarations 49 | for name in dir(module): 50 | obj = getattr(module, name) 51 | typename = str(type(obj)) 52 | if 'GeneratedProtocolMessageType' in typename or 'EnumTypeWrapper' in typename: 53 | # This is a protocol message or an enum. Keep track of it in the typemap 54 | # The full_name is something like 'tbrpc.Empty', which is actually defined 55 | # in 'tbrpc.tbrpc_pb2.Empty'. 56 | typemap[obj.DESCRIPTOR.full_name] = modulename + '.' + name 57 | continue 58 | 59 | if 'ServiceDescriptor' in typename: 60 | # This is a service! Accumulate information on all the rpc entry points 61 | # it defines, then add it to the services list 62 | methods = [] 63 | for method in obj.methods: 64 | if '--ignore' + method.name not in sys.argv: 65 | methods.append({ 66 | 'name' : method.name, 67 | 'input' : method.input_type.full_name, 68 | 'output' : method.output_type.full_name, 69 | }) 70 | 71 | services.append({ 72 | 'name' : obj.full_name, 73 | 'methods' : methods, 74 | }) 75 | 76 | # Create a mako context with all the data the template will need, then render it 77 | # to routes.py. 78 | ctx = { 79 | 'imports' : imports, 80 | 'services' : services, 81 | 'typemap' : typemap, 82 | } 83 | print mako.template.Template(filename=templateFile).render(**ctx).replace('\r', '') 84 | 85 | if __name__ == "__main__": 86 | if len(sys.argv) < 2: 87 | print 'syntax: {0} template-file'.format(__file__) 88 | sys.exit(1) 89 | CreateRoutes(sys.argv[1]) 90 | -------------------------------------------------------------------------------- /protos/tbadmin/account.proto: -------------------------------------------------------------------------------- 1 | // Services related to management of user account data. 2 | 3 | 4 | import "tbrpc/tbrpc.proto"; 5 | import "tbmatch/account.proto"; 6 | import "tbmatch/query.proto"; 7 | 8 | package tbadmin; 9 | 10 | // Admin user lookups. 11 | message LookupUserRequest { 12 | // Find user account ID. 13 | optional int64 account_id = 1; 14 | // Find user by e-mail address. 15 | optional string email = 2; 16 | // Find user by player handle. 17 | optional string handle = 3; 18 | } 19 | 20 | // How to match a user search. 21 | message UserCriteria { 22 | optional tbmatch.StringMatch email = 1; 23 | optional tbmatch.StringMatch handle = 2; 24 | optional tbmatch.StringMatch given_name = 3; 25 | optional tbmatch.StringMatch family_name = 4; 26 | 27 | optional string country_code = 5; 28 | 29 | optional tbmatch.TimeRange create_time = 10; 30 | optional tbmatch.TimeRange modify_time = 11; 31 | optional tbmatch.TimeRange last_login_time = 12; 32 | } 33 | 34 | // How to order a user search. 35 | message UserSort { 36 | enum SortBy { 37 | ACCOUNT_ID = 1; 38 | EMAIL = 2; 39 | HANDLE = 3; 40 | GIVEN_NAME = 4; 41 | FAMILY_NAME = 5; 42 | CREATE_TIME = 6; 43 | MODIFY_TIME = 7; 44 | LAST_LOGIN_TIME = 8; 45 | } 46 | 47 | optional SortBy first = 1 [default = ACCOUNT_ID]; 48 | optional SortBy second = 2; 49 | 50 | optional bool descending = 3; 51 | } 52 | 53 | message HandleChange { 54 | // Unix timestamp of the handle change time. 55 | optional int64 time = 1; 56 | // Old handle. 57 | optional string old = 2; 58 | // New handle. 59 | optional string new = 3; 60 | } 61 | 62 | // Admin user search requests. 63 | message SearchUsersRequest { 64 | optional UserCriteria criteria = 1; 65 | optional UserSort sort = 2; 66 | 67 | // Pagination. 68 | optional int32 offset = 5; 69 | optional int32 limit = 6 [default = 10]; 70 | } 71 | message SearchUsersResult { 72 | // Results do not include preferences or contact address. 73 | repeated tbmatch.UserInfo users = 1; 74 | optional bool end_of_data = 2; 75 | } 76 | 77 | // Admin account changes. 78 | message UpdateUserRequest { 79 | optional int64 account_id = 1; 80 | 81 | // Changes to account record. 82 | optional tbmatch.UserAccountSpec spec = 2; 83 | 84 | // Changes to preferences. 85 | optional tbmatch.UserPrefs prefs = 3; 86 | } 87 | 88 | // Create a new user. 89 | message CreateUserRequest { 90 | // New account owner, identifiers and authentication. 91 | optional tbmatch.UserAccountSpec spec = 1; 92 | // Optional preferences. 93 | optional tbmatch.UserPrefs prefs = 2; 94 | 95 | // Permission bits for the new account. Can't be more 96 | // permissive than the invoking account. 97 | optional int32 access = 10; 98 | 99 | // Login state of the account. 100 | optional tbmatch.LoginStatus login_status = 11 [default = DISABLED]; 101 | 102 | } 103 | message CreateUserResult { 104 | optional int64 account_id = 1; 105 | } 106 | 107 | // Update the user's contact address. 108 | message SetUserContactAddressRequest { 109 | optional int64 account_id = 1; 110 | optional tbmatch.ContactAddress contact_address = 2; 111 | } 112 | 113 | // Change permission bits for a user account. 114 | message SetUserAccessRequest { 115 | optional int64 account_id = 1; 116 | 117 | // Roles to grant. 118 | optional int32 grant = 3; 119 | 120 | // Roles to revoke. 121 | optional int32 revoke = 4; 122 | 123 | optional int32 __deprecated_access = 2; 124 | } 125 | 126 | // Set the login state of an account. 127 | message SetUserLoginStatusRequest { 128 | optional int64 account_id = 1; 129 | optional tbmatch.LoginStatus login_status = 2; 130 | } 131 | 132 | // Set the account password to the one given. 133 | message ResetUserPasswordRequest { 134 | optional int64 account_id = 1; 135 | optional string password = 2; 136 | } 137 | 138 | message NukeHandleRequest { 139 | // Account with the offensive handle. 140 | optional int64 account_id = 1; 141 | } 142 | message NukeHandleResult { 143 | optional int64 account_id = 1; 144 | // New handle assigned to the account. 145 | optional string new_handle = 2; 146 | } 147 | 148 | message GetHandleHistoryRequest { 149 | optional int64 account_id = 1; 150 | } 151 | message GetHandleHistoryResult { 152 | optional int64 account_id = 1; 153 | repeated HandleChange change = 2; 154 | } 155 | 156 | message SendUserEmailNotificationRequest { 157 | enum NotifyType { 158 | // "Welcome to the Rising Thunder alpha!" 159 | ALPHA_ACCESS = 1; 160 | 161 | // Access codes to give out. 162 | FRIEND_CODES = 2; 163 | } 164 | 165 | // Target account. Must have a validated e-mail. 166 | optional int64 account_id = 1; 167 | 168 | optional NotifyType type = 2; 169 | 170 | message FriendCodes { 171 | // How many codes to generate and include in the mail. 172 | optional int32 code_count = 1 [default = 0]; 173 | // Access flags the codes will bestow. 174 | optional int32 access_flags = 2 [default = 0]; 175 | } 176 | optional FriendCodes friend_codes = 12; 177 | } 178 | 179 | 180 | service AccountAdminService { 181 | // Look up a user account by primary key. 182 | rpc LookupUser (LookupUserRequest) returns (tbmatch.UserInfo) { 183 | option (tbrpc.access) = USER_MGMT; 184 | } 185 | 186 | // Find user accounts. 187 | rpc SearchUsers (SearchUsersRequest) returns (SearchUsersResult) { 188 | option (tbrpc.access) = USER_MGMT; 189 | } 190 | 191 | // Create a new user account. 192 | rpc CreateUser (CreateUserRequest) returns (CreateUserResult) { 193 | option (tbrpc.access) = USER_MGMT; 194 | } 195 | 196 | // Modify a user's primary fields and/or preferences. 197 | rpc UpdateUser (UpdateUserRequest) returns (tbrpc.Empty) { 198 | option (tbrpc.access) = USER_MGMT; 199 | } 200 | 201 | // Set or modify a user's contact address. 202 | rpc SetUserContactAddress(SetUserContactAddressRequest) returns (tbrpc.Empty) { 203 | option (tbrpc.access) = USER_MGMT; 204 | } 205 | 206 | // Modify a user's access permissions. 207 | rpc SetUserAccess (SetUserAccessRequest) returns (tbrpc.Empty) { 208 | option (tbrpc.access) = USER_MGMT; 209 | } 210 | 211 | // Assign the user an explicit login status. 212 | rpc SetUserLoginStatus (SetUserLoginStatusRequest) returns (tbrpc.Empty) { 213 | option (tbrpc.access) = USER_MGMT; 214 | } 215 | 216 | // Reset the user's password to the one provided. 217 | // Also resets any temporary abuse lock on the account. 218 | rpc ResetUserPassword (ResetUserPasswordRequest) returns (tbrpc.Empty) { 219 | option (tbrpc.access) = USER_MGMT; 220 | } 221 | 222 | // Rename an offensive handle with an automatically generated one, 223 | // and send a notification e-mail. 224 | rpc NukeHandle (NukeHandleRequest) returns (NukeHandleResult) { 225 | option (tbrpc.access) = USER_MGMT; 226 | } 227 | 228 | // Get the history of handle changes for an account. 229 | rpc GetHandleHistory (GetHandleHistoryRequest) returns (GetHandleHistoryResult) { 230 | option (tbrpc.access) = USER_MGMT; 231 | } 232 | 233 | // Send a system-generated notification e-mail to the address on file. 234 | // Account must have a validated e-mail address. 235 | rpc SendUserEmailNotification (SendUserEmailNotificationRequest) returns (tbrpc.Empty) { 236 | option (tbrpc.access) = USER_MGMT; 237 | } 238 | } 239 | -------------------------------------------------------------------------------- /protos/tbadmin/audit.proto: -------------------------------------------------------------------------------- 1 | // History tracking. 2 | 3 | package tbadmin; 4 | 5 | import "tbrpc/tbrpc.proto"; 6 | import "tbmatch/account.proto"; 7 | import "tbmatch/shop.proto"; 8 | import "tbmatch/query.proto"; 9 | 10 | enum AuditEventType { 11 | USER = 1; 12 | ACCOUNT = 2; 13 | ORDER = 3; 14 | QUERY = 4; 15 | } 16 | 17 | message AccountSummary { 18 | optional int64 account_id = 1; 19 | optional string email = 2; 20 | optional string handle = 3; 21 | } 22 | 23 | message AuditAccount { 24 | // Account that was affected. 25 | optional AccountSummary user = 1; 26 | optional tbmatch.LoginStatus login_status = 2; 27 | } 28 | 29 | message AuditOrder { 30 | // Order that was affected. 31 | optional int64 order_number = 1; 32 | // Order status as of the end of this event. 33 | optional tbmatch.OrderStatus status = 2; 34 | } 35 | 36 | message AuditEvent { 37 | // User who initiated the operation. 38 | optional AccountSummary actor = 2; 39 | 40 | // When the operation occurred. 41 | optional string event_time = 3; 42 | 43 | // Category of the event. 44 | optional AuditEventType event_type = 4; 45 | 46 | // IP address of the initiating session. 47 | optional string remote_ip = 5; 48 | 49 | // Hostname of the server that handled the request. 50 | optional string server_host = 6; 51 | 52 | // Name of the operation. 53 | optional string operation = 9; 54 | 55 | // Parameters of the query, if one was invoked. 56 | optional string query = 10; 57 | 58 | // Account that was affected, if any. 59 | optional AuditAccount account = 11; 60 | 61 | // Order that was affected, if any. 62 | optional AuditOrder order = 12; 63 | } 64 | 65 | // Criteria that can constrain a search for audit events. 66 | message AuditEventCriteria { 67 | optional int64 actor_account_id = 1; 68 | optional tbmatch.TimeRange event_time = 2; 69 | optional AuditEventType event_type = 3; 70 | optional string remote_ip_net = 4; // CIDR netmask 71 | optional tbmatch.StringMatch server_host = 5; 72 | optional tbmatch.StringMatch operation = 6; 73 | optional tbmatch.StringMatch query = 7; 74 | optional int64 account_id = 11; 75 | optional int64 order_id = 12; 76 | } 77 | 78 | // Options for sorting audit events returned by a query. 79 | message AuditEventSort { 80 | enum SortBy { 81 | ACTOR_ID = 1; 82 | EVENT_TIME = 2; 83 | REMOTE_IP = 3; 84 | SERVER_HOST = 4; 85 | OPERATION = 5; 86 | ACCOUNT_ID = 6; 87 | ORDER_ID = 7; 88 | } 89 | 90 | optional SortBy first = 1 [default = EVENT_TIME]; 91 | optional SortBy second = 2; 92 | optional bool descending = 3; 93 | } 94 | 95 | message SearchAuditHistoryRequest { 96 | optional AuditEventCriteria criteria = 1; 97 | optional AuditEventSort sort = 2; 98 | optional int32 offset = 5; 99 | optional int32 limit = 6 [default = 25]; 100 | } 101 | 102 | message SearchAuditHistoryResult { 103 | repeated AuditEvent events = 1; 104 | optional bool end_of_data = 2; 105 | } 106 | 107 | service AuditService { 108 | // Display a portion of audit log history. 109 | rpc SearchAuditHistory (SearchAuditHistoryRequest) returns (SearchAuditHistoryResult) { 110 | option (tbrpc.access) = AUDIT; 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /protos/tbadmin/match.proto: -------------------------------------------------------------------------------- 1 | package tbadmin; 2 | 3 | import "tbrpc/tbrpc.proto"; 4 | import "tbmatch/match.proto"; 5 | import "tbmatch/lobby.proto"; 6 | 7 | message QueueUser { 8 | optional int64 account_id = 1; 9 | optional string handle = 2; 10 | optional string build_version = 3; 11 | 12 | optional tbmatch.NetworkAddr network = 10; 13 | optional tbmatch.CharacterSpec character = 11; 14 | 15 | optional double wait_time = 20; 16 | optional double rating = 21; 17 | optional int64 last_opp_id = 23; 18 | optional string last_opp_handle = 24; 19 | optional string continent_code = 25; 20 | optional double latitude = 26; 21 | optional double longitude = 27; 22 | 23 | optional int32 echelon = 30; 24 | optional int32 tier = 31; 25 | optional int32 points = 32; 26 | } 27 | 28 | message LobbySummary { 29 | optional tbmatch.Lobby lobby = 1; 30 | 31 | optional string build_version = 10; 32 | optional int32 members = 11; 33 | 34 | optional int64 create_time_unix = 31; 35 | optional int64 last_active_time_unix = 32; 36 | 37 | optional double __deprecated_age = 21; 38 | optional double __deprecated_idle = 22; 39 | } 40 | 41 | message ActiveMatch { 42 | optional int64 match_id = 1; 43 | optional tbmatch.GameDescriptor desc = 2; 44 | optional int64 start_time_unix = 3; 45 | } 46 | 47 | message RecentMatch { 48 | optional int64 match_id = 1; 49 | optional int64 start_time_unix = 2; 50 | optional double duration_sec = 3; 51 | optional bool rated = 4; 52 | optional string build_version = 5; 53 | optional string log = 6; 54 | optional bool abandoned = 7; 55 | 56 | message Player { 57 | optional int64 account_id = 1; 58 | optional string handle = 2; 59 | optional string game_char = 3; 60 | optional bool winner = 4; 61 | optional bool disconnect = 5; 62 | } 63 | repeated Player player = 10; 64 | 65 | message Game { 66 | optional tbmatch.GameConfig game = 1; 67 | optional int32 win_slot = 2; 68 | optional int64 start_time_unix = 3; 69 | optional double duration_sec = 4; 70 | } 71 | repeated Game game = 20; 72 | } 73 | 74 | message MatchPlayer { 75 | optional int64 account_id = 1; 76 | optional string handle = 2; 77 | optional int64 create_time_unix = 3; 78 | optional int64 modify_time_unix = 4; 79 | 80 | message ServerStats { 81 | optional int32 attempts = 1; 82 | optional int32 complete = 2; 83 | optional int32 disconnects = 3; 84 | } 85 | optional ServerStats server_stats = 10; 86 | 87 | optional tbmatch.PlayerStats player_stats = 11; 88 | 89 | message Rating { 90 | optional tbmatch.MatchType type = 1; 91 | optional double rating = 2; 92 | optional double deviation = 3; 93 | optional double volatility = 4; 94 | 95 | optional double session_rating = 5; 96 | 97 | optional int64 last_rating_time_unix = 10; 98 | } 99 | repeated Rating rating = 20; 100 | } 101 | 102 | message GetMatchQueueUsersRequest { 103 | optional tbmatch.MatchType queue = 1; 104 | } 105 | 106 | message GetMatchQueueUsersResult { 107 | repeated QueueUser user = 1; 108 | } 109 | 110 | message GetLobbiesRequest { 111 | optional int32 limit = 2 [default = 200]; 112 | } 113 | message GetLobbiesResult { 114 | repeated LobbySummary lobby = 1; 115 | } 116 | message GetLobbyRequest { 117 | optional int64 lobby_id = 1; 118 | } 119 | message GetLobbyResult { 120 | optional LobbySummary lobby = 1; 121 | } 122 | 123 | message GetActiveMatchesRequest { 124 | optional tbmatch.MatchType match_type = 1 [ default = MT_RANKED]; 125 | optional int32 limit = 2 [default = 200]; 126 | } 127 | message GetActiveMatchesResult { 128 | repeated ActiveMatch match = 1; 129 | } 130 | 131 | message GetActiveMatchRequest { 132 | optional tbmatch.MatchType match_type = 1 [ default = MT_RANKED]; 133 | optional int64 match_id = 2; 134 | } 135 | message GetActiveMatchResult { 136 | optional ActiveMatch match = 1; 137 | } 138 | 139 | message GetRecentMatchesRequest { 140 | optional tbmatch.MatchType match_type = 1; 141 | optional int32 limit = 2 [ default = 200 ]; 142 | 143 | // Limit results to those with the given player involved. 144 | optional int64 player_account_id = 3; 145 | } 146 | message GetRecentMatchesResult { 147 | repeated RecentMatch match = 1; 148 | } 149 | 150 | message GetMatchDetailRequest { 151 | optional int64 match_id = 1; 152 | } 153 | message GetMatchDetailResult { 154 | optional RecentMatch match = 1; 155 | } 156 | 157 | message GetPlayerDetailRequest { 158 | optional int64 account_id = 1; 159 | } 160 | message GetPlayerDetailResult { 161 | optional MatchPlayer player = 1; 162 | } 163 | 164 | 165 | service MatchAdminService { 166 | rpc GetMatchQueueUsers(GetMatchQueueUsersRequest) returns (GetMatchQueueUsersResult) { 167 | option (tbrpc.access) = STATS_READ; 168 | } 169 | 170 | rpc GetLobbies(GetLobbiesRequest) returns (GetLobbiesResult) { 171 | option (tbrpc.access) = STATS_READ; 172 | } 173 | 174 | rpc GetLobby(GetLobbyRequest) returns (GetLobbyResult) { 175 | option (tbrpc.access) = STATS_READ; 176 | } 177 | 178 | rpc GetActiveMatches(GetActiveMatchesRequest) returns (GetActiveMatchesResult) { 179 | option (tbrpc.access) = STATS_READ; 180 | } 181 | 182 | rpc GetActiveMatch(GetActiveMatchRequest) returns (GetActiveMatchResult) { 183 | option (tbrpc.access) = STATS_READ; 184 | } 185 | 186 | rpc GetRecentMatches(GetRecentMatchesRequest) returns (GetRecentMatchesResult) { 187 | option (tbrpc.access) = STATS_READ; 188 | } 189 | 190 | rpc GetMatchDetail(GetMatchDetailRequest) returns (GetMatchDetailResult) { 191 | option (tbrpc.access) = STATS_READ; 192 | } 193 | 194 | rpc GetPlayerDetail(GetPlayerDetailRequest) returns (GetPlayerDetailResult) { 195 | option (tbrpc.access) = STATS_READ; 196 | } 197 | } 198 | 199 | -------------------------------------------------------------------------------- /protos/tbadmin/report.proto: -------------------------------------------------------------------------------- 1 | package tbadmin; 2 | 3 | import "tbrpc/tbrpc.proto"; 4 | import "tbmatch/match.proto"; 5 | 6 | message CrashBuildIdentifier { 7 | optional string build_identifier = 1; 8 | optional string app_name = 2; 9 | optional string build_version = 3; 10 | } 11 | 12 | message CrashCollection { 13 | optional string crash_identifier = 1; // The top line of the stack trace, used as a unique identifier to group together the same types of crash. 14 | optional string build_identifier = 2; 15 | optional uint64 occurrences = 3; 16 | optional uint64 users_affected = 4; // The number of unique users affected by the crash. Less than num_occurrences if a crash happens to the same person more than once. 17 | optional string stack_trace = 5; 18 | } 19 | 20 | message SymbolHeader { 21 | optional string symbol_path = 1; // The path on gcs 22 | optional string app_name = 2; 23 | optional string build_version = 3; 24 | optional string symbol_directory = 4; //The directory minidump_stackwalk will look for the symbol file in, retrieved from the first line of the sym file 25 | } 26 | 27 | message CrashReportHeader { 28 | optional string report_path = 1; // The path on gcs 29 | optional int64 timestamp = 2; 30 | optional string app_name = 3; 31 | optional string build_version = 4; 32 | optional string machine_id = 5; 33 | optional string crash_number = 6; // The crash number on a per-build version basis 34 | } 35 | 36 | message ListDesyncsRequest { 37 | optional int32 offset = 1; 38 | optional int32 limit = 2 [default = 200]; 39 | } 40 | message ListDesyncsResult { 41 | repeated tbmatch.DesyncReportHeader desync = 1; 42 | } 43 | 44 | message ListCrashesRequest { 45 | optional int32 offset = 1; 46 | optional int32 limit = 2 [default = 200]; 47 | optional string build_identifier = 3; 48 | optional string crash_identifier = 4; 49 | } 50 | 51 | message ListCrashesResult { 52 | optional CrashCollection collection = 1; 53 | repeated CrashReportHeader reports = 2; 54 | } 55 | 56 | message ListCrashBuildIdentifiersRequest { 57 | optional int32 offset = 1; 58 | optional int32 limit = 2 [default = 200]; 59 | } 60 | 61 | message ListCrashBuildIdentifiersResult { 62 | repeated CrashBuildIdentifier identifiers = 1; 63 | } 64 | 65 | message ListCrashCollectionsRequest { 66 | optional int32 offset = 1; 67 | optional int32 limit = 2 [default = 200]; 68 | optional string build_identifier = 3; 69 | } 70 | 71 | message ListCrashCollectionsResult { 72 | repeated CrashCollection collections = 1; 73 | } 74 | 75 | service FailReportService { 76 | 77 | rpc ListDesyncs(ListDesyncsRequest) returns (ListDesyncsResult) { 78 | option (tbrpc.access) = STATS_READ; 79 | } 80 | 81 | rpc ListCrashes(ListCrashesRequest) returns (ListCrashesResult) { 82 | option (tbrpc.access) = STATS_READ; 83 | } 84 | 85 | rpc ListCrashBuildIdentifiers(ListCrashBuildIdentifiersRequest) returns (ListCrashBuildIdentifiersResult) { 86 | option (tbrpc.access) = STATS_READ; 87 | } 88 | 89 | rpc ListCrashCollections(ListCrashCollectionsRequest) returns (ListCrashCollectionsResult) { 90 | option (tbrpc.access) = STATS_READ; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /protos/tbadmin/shop.proto: -------------------------------------------------------------------------------- 1 | // Order processing and administration. 2 | 3 | package tbadmin; 4 | 5 | import "tbrpc/tbrpc.proto"; 6 | import "tbmatch/shop.proto"; 7 | import "tbmatch/query.proto"; 8 | 9 | 10 | // Optional restrictions for an order search. 11 | message OrderCriteria { 12 | // If set, restrict by owning account. 13 | optional int64 account_id = 1; 14 | 15 | // If set, restrict by specific order status. 16 | optional tbmatch.OrderStatus order_status = 2; 17 | 18 | // Restrict time range for last modify time. 19 | optional tbmatch.TimeRange modify_time = 3; 20 | 21 | // Total amount range. 22 | optional tbrpc.Money min_total_amount = 5; 23 | optional tbrpc.Money max_total_amount = 6; 24 | 25 | // If set, orders that use account balance (or not). 26 | optional bool use_account_balance = 10; 27 | 28 | // Match billing fields. 29 | optional tbmatch.StringMatch bill_last_name = 11; 30 | optional tbmatch.StringMatch bill_city = 12; 31 | optional tbmatch.StringMatch bill_region = 13; 32 | 33 | // Limit to country. 34 | optional string bill_country = 14; 35 | } 36 | 37 | 38 | // *********************** RPC Request/Response Types ************************** 39 | 40 | // Search for purchase orders. 41 | message SearchPurchaseOrdersRequest { 42 | // Restrictions on the search. 43 | optional OrderCriteria criteria = 1; 44 | 45 | // Order sorting 46 | optional tbmatch.OrderSort sort = 2; 47 | 48 | // Skip past this many records in the result. 49 | optional int32 offset = 5; 50 | 51 | // Max number of results to return. 52 | optional int32 limit = 6 [default = 10]; 53 | } 54 | message SearchPurchaseOrdersResult { 55 | // Orders that matched the criteria. 56 | repeated tbmatch.PurchaseOrder orders = 1; 57 | 58 | // True when the included results reached the end of the query result. 59 | optional bool end_of_data = 2; 60 | } 61 | 62 | // Reconcile a purchase order state with external 63 | // payment processing transaction state. 64 | message SyncPurchaseOrderRequest { 65 | optional int64 order_number = 1; 66 | } 67 | 68 | // Cancel a pending purchase order. 69 | message VoidPurchaseOrderRequest { 70 | optional int64 order_number = 1; 71 | } 72 | 73 | // Refund a completed purchase order with settled payment. 74 | message RefundPurchaseOrderRequest { 75 | optional int64 order_number = 1; 76 | } 77 | 78 | 79 | service ShopAdminService { 80 | // Search over all purchase orders. Admin only. Returns headers only, 81 | // no line items. 82 | rpc SearchPurchaseOrders (SearchPurchaseOrdersRequest) returns (SearchPurchaseOrdersResult) { 83 | option (tbrpc.access) = ORDER_READ_SYNC; 84 | } 85 | 86 | // Bring purchase order up to date with external transaction state. 87 | // This operation causes goods to be delivered or revoked. 88 | rpc SyncPurchaseOrder (SyncPurchaseOrderRequest) returns (tbrpc.Empty) { 89 | option (tbrpc.access) = ORDER_READ_SYNC; 90 | } 91 | 92 | // Cancel payment for a purchase order still pending settlement. 93 | rpc VoidPurchaseOrder (VoidPurchaseOrderRequest) returns (tbrpc.Empty) { 94 | option (tbrpc.access) = ORDER_MANAGER; 95 | } 96 | 97 | // Refund payment for a settled order. 98 | rpc RefundPurchaseOrder (RefundPurchaseOrderRequest) returns (tbrpc.Empty) { 99 | option (tbrpc.access) = ORDER_MANAGER; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /protos/tbadmin/stats.proto: -------------------------------------------------------------------------------- 1 | // Various pre-packaged stats queries. 2 | 3 | package tbadmin; 4 | 5 | import "tbrpc/tbrpc.proto"; 6 | import "tbmatch/match.proto"; 7 | 8 | message UserCountStats { 9 | optional int64 total = 1; 10 | optional int64 no_access = 2; 11 | 12 | message RoleCount { 13 | optional tbrpc.Access role = 1; 14 | optional int64 count = 2; 15 | } 16 | repeated RoleCount role = 3; 17 | } 18 | 19 | message Session { 20 | optional int64 account_id = 1; 21 | optional string handle = 2; 22 | optional string email = 3; 23 | optional string build_version = 4; 24 | optional int64 start_time_unix = 5; 25 | optional int64 last_active_time_unix = 6; 26 | optional string remote_ip = 7; 27 | optional int32 type = 8; 28 | } 29 | 30 | message CharacterReportOptions { 31 | optional int64 start_time_unix = 1; // Time of first match to include, 0 = all 32 | optional int64 end_time_unix = 2; // Time of last match to include, 0 = now 33 | 34 | // Global filter options. 35 | optional string build_version = 4; // Limit results to a specific build. 36 | optional tbmatch.MatchType match_type = 5; // Limit results to a specific match type. 37 | optional bool include_disconnect = 6; // Include disconnected matches. 38 | optional bool include_disagree = 7; // Include foul-play matches. 39 | 40 | // Win ratio options. 41 | optional int32 max_rating_diff = 10 [default = 100]; // Most allowed rating difference for a win ratio query. 42 | optional int32 rating_bucket_size = 11 [default = 150]; // Width of Glicko2 rating buckets. 43 | } 44 | 45 | message Matchup { 46 | // Char type vs. opponent type. 47 | optional string char_type = 1; 48 | optional string versus_type = 2; 49 | 50 | optional uint32 wins = 20; 51 | optional uint32 total = 21; 52 | } 53 | 54 | message RatingWinRatioReport { 55 | // Max Glicko2 rating for these matchups. 56 | optional int32 rating_cap = 1; 57 | 58 | repeated Matchup matchup = 2; 59 | } 60 | 61 | 62 | // Overall stats for an individual character. 63 | message CharacterUsage { 64 | optional string type_name = 1; 65 | 66 | optional uint32 total_matches = 2; // Number of match slots involved in. 67 | optional uint32 unique_users = 3; // Number of unique users who played the character. 68 | optional uint32 wins = 4; 69 | optional uint32 disconnects = 5; 70 | 71 | message VariantUsage { 72 | optional string specials = 1; 73 | optional uint32 count = 2; 74 | } 75 | repeated VariantUsage variant_top_n = 6; 76 | } 77 | 78 | message CharacterUsageReport { 79 | // Time this report was generated. 80 | optional int64 report_time = 1; 81 | 82 | // Query parameters that bounded the report scope. 83 | optional CharacterReportOptions options = 2; 84 | 85 | // Matches observed in this report. 86 | optional uint32 total_matches = 10; 87 | 88 | // Total unique users considered for the report. 89 | optional uint32 total_users = 11; 90 | 91 | // Summary stats for each character. 92 | repeated CharacterUsage char_usage = 13; 93 | 94 | // Half-matrix of win/loss ratios for each character combo & rating band. 95 | repeated RatingWinRatioReport win_ratio = 14; 96 | } 97 | 98 | // ------------------------------ RPC Service Messages ------------------------------- 99 | 100 | message GetSessionsRequest { 101 | optional int32 max_age_sec = 1 [default = 300]; 102 | optional int32 limit = 2 [default = 1000]; 103 | } 104 | 105 | message GetSessionsResult { 106 | repeated Session game = 1; 107 | repeated Session launcher = 2; 108 | repeated Session web = 3; 109 | 110 | // Total sessions active. 111 | optional int32 total = 10; 112 | } 113 | 114 | message GetCharacterUsageRequest { 115 | optional CharacterReportOptions options = 1; 116 | } 117 | 118 | message GetCharacterUsageResult { 119 | optional CharacterUsageReport report = 1; 120 | } 121 | 122 | service StatsService { 123 | rpc CountUsers(tbrpc.Empty) returns (UserCountStats) { 124 | option (tbrpc.access) = STATS_READ; 125 | } 126 | 127 | rpc GetSessions(GetSessionsRequest) returns (GetSessionsResult) { 128 | option (tbrpc.access) = STATS_READ; 129 | } 130 | 131 | rpc GetCharacterUsage(GetCharacterUsageRequest) returns (GetCharacterUsageResult) { 132 | option (tbrpc.access) = STATS_READ; 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /protos/tbmatch/account.proto: -------------------------------------------------------------------------------- 1 | // Services related to management of user account data. 2 | 3 | 4 | import "tbrpc/tbrpc.proto"; 5 | 6 | package tbmatch; 7 | 8 | // Used to create or update accounts. 9 | message UserAccountSpec { 10 | // E-mail address. Required. 11 | optional string email = 1; 12 | 13 | // Desired password. Required for CreateUser and Register; 14 | // existing accounts must use ResetPassword. 15 | optional string password = 2; 16 | 17 | // Username as seen by other users. 18 | optional string handle = 3; 19 | 20 | // Given (first) name. Required. 21 | optional string given_name = 4; 22 | 23 | // Family (last) name. Required. 24 | optional string family_name = 5; 25 | 26 | // Salutation, optional. 27 | optional string salutation = 6; 28 | 29 | // Date of birth (RFC3339 string). Required. 30 | optional string birth_date = 10; 31 | 32 | // 3-letter code for country of residence. 33 | optional string country = 11; 34 | 35 | // Locale for messages, currency format, etc. 36 | optional string locale = 12; 37 | 38 | // Secret question message ID. Required. 39 | optional string secret_question_msg = 20; 40 | 41 | // Answer to the secret question. Required. 42 | optional string secret_question_answer = 21; 43 | } 44 | 45 | // User settable options and preferences for the website. 46 | message UserPrefs { 47 | // Allow using account balance for in-game purchases. 48 | optional bool balance_purchase_in_game = 1 [default = true]; 49 | 50 | // Elect to receive special offers and promotional e-mail. 51 | optional bool email_offers = 20 [default = true]; 52 | 53 | // Currency last used/selected. 54 | optional string default_currency = 30; 55 | } 56 | 57 | // User's contact address fields. 58 | message ContactAddress { 59 | optional string address_1 = 1; 60 | optional string address_2 = 2; 61 | optional string city = 3; 62 | optional string region = 4; 63 | optional string zip_code = 5; 64 | optional string phone_number = 6; 65 | } 66 | 67 | 68 | enum LoginStatus { 69 | // Account accepts logins with a valid password. 70 | ACTIVE = 1; 71 | 72 | // Account is permanently frozen and can only be enabled by an administrator. 73 | DISABLED = 3; 74 | 75 | // Account uses an e-mail address that has not yet been confirmed. 76 | PENDING_VALIDATION = 4; 77 | } 78 | 79 | // Full details and status of a user account. Can be returned 80 | // both for the active session, and for account administration. 81 | message UserInfo { 82 | optional int64 account_id = 1; 83 | 84 | optional UserAccountSpec spec = 2; 85 | optional ContactAddress contact_address = 3; 86 | optional UserPrefs prefs = 4; 87 | 88 | optional int32 access_bits = 10; 89 | optional LoginStatus login_status = 11; 90 | 91 | // When account was created (RFC 3339 string). 92 | optional string create_time = 60; 93 | // Last time account was modified (RFC 3339 string). 94 | optional string modify_time = 61; 95 | // Last login time, if any (RFC3339 string). 96 | optional string last_login_time = 62; 97 | } 98 | 99 | // Request to change the default locale of the account. 100 | message SetLocaleRequest { 101 | optional string locale = 1; 102 | } 103 | 104 | // Change the password for this account. 105 | message ChangePasswordRequest { 106 | // Provide the correct existing account password. 107 | optional string old_password = 1; 108 | 109 | // Provide a new password that adheres to the password requirements. 110 | optional string new_password = 2; 111 | } 112 | 113 | // Change the e-mail address for this account. 114 | message ChangeEmailAddressRequest { 115 | optional string new_email_address = 1; 116 | 117 | // Provide 2 forms of authentication. 118 | optional string secret_answer = 2; 119 | optional string password = 3; 120 | } 121 | 122 | // Redeem an access code to grant the account additional roles or objects. 123 | message RedeemAccessCodeRequest { 124 | optional string code = 1; 125 | } 126 | 127 | // Request to create an account. 128 | message RegisterUserRequest { 129 | optional UserAccountSpec spec = 1; 130 | optional UserPrefs prefs = 2; 131 | 132 | // Registration key (if required) 133 | optional string registration_key = 10; 134 | } 135 | 136 | // Validate a pending account or e-mail address change. 137 | message ValidateUserRequest { 138 | optional string nonce = 1; 139 | } 140 | 141 | message GetSecretQuestionRequest { 142 | optional string email = 1; 143 | } 144 | 145 | message GetSecretQuestionResult { 146 | optional string secret_question_msg = 1; 147 | } 148 | 149 | message SendPasswordResetEmailRequest { 150 | optional string email = 1; 151 | // Secret question message ID for the account. 152 | optional string secret_question_msg = 2; 153 | // Answer to secret question. 154 | optional string secret_question_answer = 3; 155 | } 156 | 157 | message VerifyResetPasswordCodeRequest { 158 | // Account to verify. 159 | optional string email = 1; 160 | // String from the email sent by SendPasswordResetRequest. 161 | optional string validation_code = 2; 162 | } 163 | 164 | message ResetPasswordRequest { 165 | // Account to reset. 166 | optional string email = 1; 167 | // String from the email sent by SendPasswordResetRequest. 168 | optional string validation_code = 2; 169 | // New password to set. Must obey password restrictions. 170 | optional string new_password = 3; 171 | } 172 | 173 | message CheckHandleRequest { 174 | optional string handle = 1; 175 | } 176 | 177 | 178 | service AccountService { 179 | 180 | // *************** Public methods ****************** 181 | 182 | // Check if a user handle is valid and available for use. 183 | rpc CheckHandle (CheckHandleRequest) returns (tbrpc.Empty) { 184 | option (tbrpc.access) = ANON; 185 | } 186 | 187 | // Apply for an account. 188 | rpc RegisterUser (RegisterUserRequest) returns (tbrpc.Empty) { 189 | option (tbrpc.access) = ANON; 190 | } 191 | 192 | // Validate a pending account using the secret key. 193 | rpc ValidateUser (ValidateUserRequest) returns (UserInfo) { 194 | option (tbrpc.access) = ANON; 195 | } 196 | 197 | // Get the secret question for sending a password reset code mail. 198 | rpc GetSecretQuestion (GetSecretQuestionRequest) returns (GetSecretQuestionResult) { 199 | option (tbrpc.access) = ANON; 200 | } 201 | 202 | // Send a password reset mail with a temporary validation code. 203 | // Must provide valid secondary authentication. 204 | rpc SendPasswordResetEmail (SendPasswordResetEmailRequest) returns (tbrpc.Empty) { 205 | option (tbrpc.access) = ANON; 206 | } 207 | 208 | // Checks a reset code for validity. 209 | rpc VerifyResetPasswordCode (VerifyResetPasswordCodeRequest) returns (tbrpc.Empty) { 210 | option (tbrpc.access) = ANON; 211 | } 212 | 213 | // Reset the account password using a validation code. 214 | rpc ResetPassword (ResetPasswordRequest) returns (tbrpc.Empty) { 215 | option (tbrpc.access) = ANON; 216 | } 217 | 218 | // *************** Logged-in Users ****************** 219 | 220 | // Fetch basic account information 221 | rpc GetUserInfo (tbrpc.Empty) returns (UserInfo) { 222 | option (tbrpc.access) = LOGIN; 223 | } 224 | 225 | // For unverified accounts, send a new validation e-mail. 226 | rpc ResendValidationEmail (tbrpc.Empty) returns (tbrpc.Empty) { 227 | option (tbrpc.access) = LOGIN; 228 | } 229 | 230 | // Change the user's locale. Fails of the given 231 | // locale string is not supported. 232 | rpc SetLocale (SetLocaleRequest) returns (tbrpc.Empty) { 233 | option (tbrpc.access) = LOGIN; 234 | } 235 | 236 | // Update the user's contact address fields. A new account has 237 | // no contact address; but once set it cannot be removed. 238 | rpc SetContactAddress(ContactAddress) returns (tbrpc.Empty) { 239 | option (tbrpc.access) = LOGIN; 240 | } 241 | 242 | // Fetch current account preferences. 243 | rpc GetUserPrefs (tbrpc.Empty) returns (UserPrefs) { 244 | option (tbrpc.access) = LOGIN; 245 | } 246 | 247 | // Update account preferences. Only set fields will 248 | // be updated. 249 | rpc SetUserPrefs (UserPrefs) returns (tbrpc.Empty) { 250 | option (tbrpc.access) = LOGIN; 251 | } 252 | 253 | // Update the account password. 254 | rpc ChangePassword(ChangePasswordRequest) returns (tbrpc.Empty) { 255 | option (tbrpc.access) = LOGIN; 256 | } 257 | 258 | // Update the account login email address. 259 | rpc ChangeEmailAddress(ChangeEmailAddressRequest) returns (tbrpc.Empty) { 260 | option (tbrpc.access) = LOGIN; 261 | } 262 | 263 | // Apply a registration or access code. 264 | rpc RedeemAccessCode(RedeemAccessCodeRequest) returns (tbrpc.Empty) { 265 | option (tbrpc.access) = LOGIN; 266 | } 267 | } 268 | -------------------------------------------------------------------------------- /protos/tbmatch/crash.proto: -------------------------------------------------------------------------------- 1 | // For sending logs and crash dumps to the server for storage 2 | 3 | import "tbrpc/tbrpc.proto"; 4 | 5 | package tbmatch; 6 | 7 | message CrashReportRequest { 8 | optional string app_name = 1; 9 | optional string build_version = 2; 10 | optional string machine_id = 3; // Unique Windows identifier for the machine that crashed. 11 | optional bytes bundle = 4; // Zip of crash report contents 12 | } 13 | 14 | service CrashReportService { 15 | 16 | rpc CrashReport (CrashReportRequest) returns (tbrpc.Empty) { 17 | option (tbrpc.access) = ANON; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /protos/tbmatch/event.proto: -------------------------------------------------------------------------------- 1 | 2 | package tbmatch; 3 | 4 | import "tbrpc/tbrpc.proto"; 5 | import "tbmatch/match.proto"; 6 | import "tbmatch/lobby.proto"; 7 | 8 | // The event filter has been updated; any pending 9 | // GetEvent call should reload the filter. 10 | // Target: q_user_N 11 | message FilterChangedEvent { 12 | } 13 | 14 | // Progress updates from the GetMatch method. 15 | // Target: q_user_N 16 | message WaitMatchProgressEvent { 17 | enum Status { 18 | // Waiting for an available match. 19 | WAITING = 1; 20 | 21 | // Gave up waiting. 22 | TIMEOUT = 2; 23 | 24 | // Match found. 25 | MATCH = 3; 26 | 27 | // Request canceled by user. 28 | CANCEL = 4; 29 | 30 | // Failed to enter queue. A ping test must be performed before 31 | // re-queuing for a match. 32 | PING_TEST_REQUIRED = 5; 33 | 34 | // The build version is not allowed to enter the queue. 35 | // A client update is required. 36 | DENY_VERSION = 6; 37 | 38 | // The match-making server is down for maintenance. Try again later. 39 | SERVER_MAINTENANCE = 7; 40 | } 41 | 42 | optional Status status = 1; 43 | 44 | // The ID of the newly created match. 45 | optional int64 match_id = 2; 46 | 47 | // Game config to run for game 0 (when status == MATCH). 48 | optional GameConfig config = 3; 49 | 50 | // Player slot and connection for game 0 (when status == MATCH). 51 | optional GameEndpointConfig endpoint = 4; 52 | 53 | // Some extra stats for a WAITING progress. 54 | optional int32 users_waiting = 21; 55 | 56 | 57 | // DEPRECATED FIELDS 58 | optional int32 __deprecated_users_online = 20; 59 | } 60 | 61 | // A new match was created. 62 | // Target queue: match 63 | message MatchCreatedEvent { 64 | optional GameConfig game_config = 3; 65 | 66 | optional int64 __deprecated_match_id = 1; 67 | repeated int64 __deprecated_user_id = 2; 68 | } 69 | 70 | // Observed match has successfully established player connection 71 | // and loading has begun. 72 | // Target queue: match 73 | message MatchConnectedEvent { 74 | optional int64 match_id = 1; 75 | } 76 | 77 | // The next game is beginning in this match. 78 | // Target queue: match 79 | message GameBeginEvent { 80 | optional int64 match_id = 1; 81 | optional NextGameConfig next_game = 2; 82 | 83 | // Populated from the sequenceId field in Redis under the match's key. 84 | // Should be used by the client, if present, to determine whether to process this event. 85 | optional int32 sequence_id = 3 [default = -1]; 86 | } 87 | 88 | // Batch of compressed inputs. 89 | // Target queue: match 90 | message GameInputEvent { 91 | optional int64 match_id = 1; 92 | 93 | // First frame number of input stored in bits. 94 | optional int32 start_frame = 2; 95 | 96 | // Number of bits to read from bits. 97 | optional int32 num_bits = 3; 98 | 99 | // Encoded frame inputs. 100 | optional bytes bits = 4; 101 | 102 | // True if this is the last set of inputs for a game. 103 | optional bool final = 5; 104 | } 105 | 106 | // Game reached a valid outcome. 107 | // Target queue: match 108 | message GameOverEvent { 109 | optional int64 match_id = 1; 110 | optional GameReport report = 2; 111 | optional int32 sequence_id = 3 [default = -1]; 112 | } 113 | 114 | // A match user was playing has been abandoned, because players sent conflicting goodbye packets 115 | // (either intentionally or because the p2p connection got broken) 116 | // Stop playing the match and exit back to the menu. 117 | // Target queue: user 118 | message MatchAbandonedEvent { 119 | optional int64 match_id = 1; 120 | } 121 | 122 | // Observed match has completed. 123 | // Target queue: match 124 | message MatchOverEvent { 125 | optional int64 match_id = 1; 126 | 127 | enum Status { 128 | // Match did not complete due to a server error, desync 129 | // or a network problem that affected both players. 130 | ABANDONED = 1; 131 | 132 | // Match ended because one player disconnected. 133 | DISCONNECT = 2; 134 | 135 | // Match ended with a valid outcome. 136 | VALID = 3; 137 | } 138 | 139 | optional Status status = 2; 140 | 141 | // if status==VALID, player slot that won if any. 142 | optional int32 win_slot = 3; 143 | 144 | // if status==VALID, true iff the result was a draw. 145 | optional bool draw = 4; 146 | } 147 | 148 | 149 | // Match completed that user participated in. Notify about what the server recorded. 150 | // This event could arrive anywhere along the game sequence, since it may also indicate 151 | // early disconnection of the opponent. 152 | message MatchOutcomeEvent { 153 | // What happened in the match. 154 | optional MatchResult result = 1; 155 | // Spoils of war. 156 | optional MatchExperience exp = 2; 157 | // Updates to user stats. 158 | optional PlayerStats stats_update = 3; 159 | } 160 | 161 | // User failed to handshake during a match, check network settings. 162 | message UserCheckNetworkEvent { 163 | } 164 | 165 | // The session is being forcibly closed. 166 | // Could be due to login on another session, terminated by 167 | // an administrator. 168 | message UserSessionClosedEvent { 169 | optional string reason_msg_id = 1; 170 | } 171 | 172 | // The requested ping test has completed - now eligible to join a matching queue. 173 | message PingTestCompleteEvent { 174 | // Average measured round-trip time. 175 | optional int32 avg_rtt_ms = 1; 176 | 177 | // Max round-trip time. 178 | optional int32 max_rtt_ms = 2; 179 | 180 | // Fraction of packets lost. 181 | optional float loss_ratio = 3; 182 | 183 | // Was the ping successful or not? 184 | optional bool success = 4; 185 | } 186 | 187 | 188 | // *************************** Lobby Events ********************************** 189 | 190 | // Player has now joined a lobby. 191 | message LobbyJoinEvent { 192 | // Current state of the joined lobby. 193 | optional Lobby lobby = 1; 194 | } 195 | 196 | // Player has left the current lobby, either voluntarily, because they were 197 | // banned, or because the last owner left. 198 | message LobbyLeaveEvent { 199 | optional int64 lobby_id = 1; 200 | 201 | enum Reason { 202 | // Left lobby voluntarily. 203 | LEFT = 1; 204 | 205 | // Banned from the lobby. 206 | BANNED = 2; 207 | 208 | // Removed by owner, but not banned permanently 209 | REMOVED = 3; 210 | } 211 | optional Reason reason = 2; 212 | } 213 | 214 | 215 | // Sent to current members about changes in the lobby state. 216 | // Notifies users joining and leaving, change of ready states, 217 | // queue order, and other lobby states. Any fields not set 218 | // should be left at their previous state. 219 | message LobbyUpdateEvent { 220 | optional int64 lobby_id = 1; 221 | 222 | // Members added or whose state changed. 223 | repeated LobbyMember update = 2; 224 | 225 | // Members who left. 226 | repeated int64 removed = 3; 227 | 228 | // Current state of the lobby. 229 | optional LobbyState state = 4; 230 | 231 | // For state == LS_MATCH, GameConfig that started the running match. 232 | optional GameConfig game_config = 5; 233 | 234 | // Order of queued members by account_id. 235 | repeated int64 queue = 6; 236 | 237 | // Options for games generated by this lobby. 238 | optional GameOptions options = 7; 239 | } 240 | 241 | 242 | // An idle lobby has two ready players. 243 | // New match started, and the recipient is involved. 244 | message LobbyMatchStartEvent { 245 | optional int64 lobby_id = 1; 246 | 247 | // The ID of the newly created match. 248 | optional int64 match_id = 2; 249 | 250 | // Game config for first game of the match. 251 | optional GameConfig config = 3; 252 | 253 | // Player slot and connection. 254 | optional GameEndpointConfig endpoint = 4; 255 | } 256 | 257 | 258 | 259 | // Event container union. Returned in GetEvent. 260 | message Event { 261 | enum Type { 262 | E_UNKNOWN = 0; 263 | E_FILTER_CHANGED = 13; 264 | E_WAIT_MATCH_PROGRESS = 14; 265 | E_MATCH_CREATED = 15; 266 | E_MATCH_OUTCOME = 18; 267 | E_MATCH_ABANDONED = 21; 268 | 269 | E_MATCH_CONNECTED = 23; 270 | E_MATCH_OVER = 24; 271 | 272 | E_GAME_BEGIN = 25; 273 | E_GAME_INPUT = 26; 274 | E_GAME_OVER = 27; 275 | 276 | E_USER_CHECK_NETWORK = 30; 277 | E_USER_SESSION_CLOSED = 31; 278 | 279 | E_PING_TEST_COMPLETE = 40; 280 | 281 | E_LOBBY_JOIN = 50; 282 | E_LOBBY_LEAVE = 51; 283 | E_LOBBY_UPDATE = 52; 284 | E_LOBBY_MATCH_START = 53; 285 | } 286 | 287 | // Unique ID for the event. 288 | optional int64 event_id = 1; 289 | 290 | // Type of the event. 291 | optional Type type = 2 [default = E_UNKNOWN]; 292 | 293 | // Time the event was generated. 294 | optional int64 timestamp_msec = 3; 295 | 296 | // Event fields based on type. 297 | optional FilterChangedEvent filter_changed = 13; 298 | optional WaitMatchProgressEvent wait_match_progress = 14; 299 | optional MatchCreatedEvent match_created = 15; 300 | optional MatchOutcomeEvent match_outcome = 18; 301 | optional MatchAbandonedEvent match_abandoned = 21; 302 | 303 | optional MatchConnectedEvent match_connected = 23; 304 | optional MatchOverEvent match_over = 24; 305 | 306 | optional GameBeginEvent game_begin = 25; 307 | optional GameInputEvent game_input = 26; 308 | optional GameOverEvent game_over = 27; 309 | 310 | optional UserCheckNetworkEvent user_check_network = 30; 311 | optional UserSessionClosedEvent user_session_closed = 31; 312 | 313 | optional PingTestCompleteEvent ping_test_complete = 40; 314 | 315 | optional LobbyJoinEvent lobby_join = 50; 316 | optional LobbyLeaveEvent lobby_leave = 51; 317 | optional LobbyUpdateEvent lobby_update = 52; 318 | optional LobbyMatchStartEvent lobby_match_start = 53; 319 | } 320 | 321 | // Wrapper for server-stored events. 322 | message QueuedEvent { 323 | // The event queued for delivery. 324 | optional Event event = 1; 325 | 326 | // If set, only return the event if the requesting session matches this ID. 327 | optional string restrict_session_id = 2; 328 | 329 | // If set, discard the event after this time is reached. 330 | optional int64 expiry_timestamp_msec = 3; 331 | } 332 | 333 | message GetEventRequest { 334 | optional string version = 1; 335 | } 336 | 337 | message GetEventResult { 338 | optional string version = 1; 339 | repeated Event event = 2; 340 | } 341 | 342 | service EventService { 343 | // Get any pending events matching the current filter, 344 | // or wait for new events. 345 | rpc GetEvent (GetEventRequest) returns (GetEventResult) { 346 | option (tbrpc.access) = LOGIN; 347 | option (tbrpc.no_op) = true; 348 | } 349 | 350 | // No-op ping method for client to (re-)establish connectivity. 351 | rpc EventPing (tbrpc.Empty) returns (tbrpc.Empty) { 352 | option (tbrpc.access) = LOGIN; 353 | option (tbrpc.no_op) = true; 354 | } 355 | } 356 | -------------------------------------------------------------------------------- /protos/tbmatch/lobby.proto: -------------------------------------------------------------------------------- 1 | package tbmatch; 2 | 3 | import "tbrpc/tbrpc.proto"; 4 | import "tbmatch/match.proto"; 5 | 6 | // ********************* Enum Types ************************ 7 | 8 | enum LobbyType { 9 | LT_QUEUED = 1; 10 | } 11 | 12 | enum LobbyState { 13 | // Waiting for 2 ready players. 14 | LS_IDLE = 1; 15 | 16 | // Match is ready to start. 17 | LS_MATCH_PENDING = 2; 18 | 19 | // A match is in progress. 20 | LS_MATCH = 3; 21 | } 22 | 23 | 24 | // ******************* Lobby Message Types ********************** 25 | 26 | message LobbyMember { 27 | optional int64 account_id = 1; 28 | optional string handle = 2; 29 | 30 | // Lobby owner? 31 | optional bool owner = 10; 32 | 33 | // Ready for next match? 34 | optional bool ready = 11; 35 | } 36 | 37 | 38 | message Lobby { 39 | // Unique identifier for this lobby. 40 | optional int64 lobby_id = 1; 41 | 42 | // Style of gameplay conducted in the lobby. 43 | optional LobbyType type = 2; 44 | 45 | // Name given by creator. 46 | optional string name = 3; 47 | 48 | // Current state of the lobby. 49 | // Updated by LobbyStatusEvent. 50 | optional LobbyState state = 5; 51 | 52 | // People currently in the lobby. 53 | // Updated by LobbyMemberUpdateEvent. 54 | repeated LobbyMember member = 10; 55 | 56 | // Options included for the next match generated by this lobby. 57 | optional GameOptions options = 11; 58 | 59 | // For state == LS_MATCH, start config of the game being played. 60 | optional GameConfig game_config = 20; 61 | 62 | // For type == LT_QUEUED, this is the current queue order of account IDs. 63 | // Updated by LobbyMemberUpdateEvent. 64 | repeated int64 queue = 30; 65 | } 66 | 67 | 68 | 69 | // ******************* RPC Request / Response types ******************** 70 | 71 | message CreateLobbyRequest { 72 | optional LobbyType type = 1; 73 | optional string name = 2; 74 | } 75 | 76 | 77 | message GetLobbyJoinCodeRequest { 78 | optional int64 lobby_id = 1; 79 | } 80 | message GetLobbyJoinCodeResult { 81 | optional string join_code = 1; 82 | } 83 | 84 | 85 | message JoinLobbyByCodeRequest { 86 | optional string code = 1; 87 | } 88 | 89 | 90 | message LobbySetReadyRequest { 91 | optional bool ready = 2; 92 | 93 | // If ready == true, layer network and character data for the next match. 94 | optional NetworkAddr network = 10; 95 | optional CharacterSpec character = 11; 96 | } 97 | 98 | message LobbySetGameOptionsRequest { 99 | // Any set fields will be updated in the lobby GameConfig template. 100 | optional GameOptions options = 1; 101 | } 102 | 103 | message LobbySetOwnerRequest { 104 | optional int64 lobby_id = 1; 105 | optional int64 account_id = 2; 106 | optional bool set_owner = 3; 107 | } 108 | 109 | 110 | message LobbyBanUserRequest { 111 | optional int64 lobby_id = 1; 112 | optional int64 account_id = 2; 113 | } 114 | 115 | message LobbyRemoveUserRequest { 116 | // ID of the lobby. 117 | optional int64 lobby_id = 1; 118 | // Account ID of the user to kick. 119 | optional int64 account_id = 2; 120 | } 121 | 122 | 123 | service LobbyService { 124 | // Create a new lobby and join it, with the creator as owner. 125 | // Fails if user is already in a lobby. 126 | // Success confirmed by LobbyJoin event. 127 | rpc CreateLobby (CreateLobbyRequest) returns (tbrpc.Empty) { 128 | option (tbrpc.session) = GAME_RT; 129 | option (tbrpc.access) = MATCH; 130 | } 131 | 132 | // Get the secret code for joining this lobby. Only members marked as 133 | // owner may request the code. 134 | rpc GetLobbyJoinCode (GetLobbyJoinCodeRequest) returns (GetLobbyJoinCodeResult) { 135 | option (tbrpc.session) = GAME_RT; 136 | option (tbrpc.access) = MATCH; 137 | } 138 | 139 | // Join a lobby using a secret code provided by a lobby owner. 140 | // Fail if user is already joined to another lobby. Success confirmed by LobbyJoin event. 141 | rpc JoinLobbyByCode (JoinLobbyByCodeRequest) returns (tbrpc.Empty) { 142 | option (tbrpc.session) = GAME_RT; 143 | option (tbrpc.access) = MATCH; 144 | } 145 | 146 | // Leave the currently joined lobby. If the player is the last owner, another 147 | // owner is assigned at random. If player is last member, lobby is destroyed. 148 | // Success confirmed by LobbyLeave event. 149 | rpc LeaveLobby (tbrpc.Empty) returns (tbrpc.Empty) { 150 | option (tbrpc.session) = GAME_RT; 151 | option (tbrpc.access) = MATCH; 152 | } 153 | 154 | // Set ourselves ready or not-ready for a match in the joined lobby if the lobby state allows it. 155 | rpc LobbySetReady (LobbySetReadyRequest) returns (tbrpc.Empty) { 156 | option (tbrpc.session) = GAME_RT; 157 | option (tbrpc.access) = MATCH; 158 | } 159 | 160 | // Grant or revoke owner status to another member of the lobby -- owner only. 161 | rpc LobbySetOwner (LobbySetOwnerRequest) returns (tbrpc.Empty) { 162 | option (tbrpc.session) = GAME_RT; 163 | option (tbrpc.access) = MATCH; 164 | } 165 | 166 | // Set the global options for matches generated by this lobby -- owner only. 167 | rpc LobbySetGameOptions (LobbySetGameOptionsRequest) returns (tbrpc.Empty) { 168 | option (tbrpc.session) = GAME_RT; 169 | option (tbrpc.access) = MATCH; 170 | } 171 | 172 | // Remove a user from the lobby and permanently restrict them from re-entering. 173 | rpc LobbyBanUser (LobbyBanUserRequest) returns (tbrpc.Empty) { 174 | option (tbrpc.session) = GAME_RT; 175 | option (tbrpc.access) = MATCH; 176 | } 177 | 178 | // Remove a user from the lobby. 179 | rpc LobbyRemoveUser (LobbyRemoveUserRequest) returns (tbrpc.Empty) { 180 | option (tbrpc.session) = GAME_RT; 181 | option (tbrpc.access) = MATCH; 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /protos/tbmatch/log.proto: -------------------------------------------------------------------------------- 1 | // For reporting and aggregating log messages pertaining to matches. 2 | 3 | package tbmatch; 4 | 5 | // This represents a single log message generated by an application pertaining to some entity. 6 | // e.g.: One log message from Portal for a given Match. 7 | message LogMessage { 8 | // The type of entity to which the log message pertains 9 | enum EntityType { 10 | // An online match 11 | MATCH = 1; 12 | } 13 | 14 | // Information about the originator of the log message 15 | message Source { 16 | // The name of the application that generated the message. 17 | optional string application = 1; 18 | 19 | // The IP address or hostname of the machine from which the message originated. 20 | optional string address = 2; 21 | } 22 | 23 | // The various possible log levels 24 | enum Priority { 25 | // The application will probably terminate after this message is logged. 26 | FATAL = 1; 27 | 28 | // An error was encountered, but the application will resume. 29 | ERROR = 2; 30 | 31 | // Something unexpected or noteworthy occurred that didn't break anything. 32 | INFO = 3; 33 | 34 | // Verbose log entries, debug information, etc. 35 | TRIVIA = 4; 36 | } 37 | 38 | // The timestamp at which this message was generated. 39 | optional int64 timestamp_msec = 1; 40 | 41 | // The log or debug message. 42 | optional string message = 2; 43 | 44 | // The log level of the message. 45 | optional Priority priority = 3; 46 | 47 | // The source of the log message. 48 | optional Source source = 4; 49 | } -------------------------------------------------------------------------------- /protos/tbmatch/query.proto: -------------------------------------------------------------------------------- 1 | package tbmatch; 2 | 3 | enum MatchPattern { 4 | // String must equal the match value exactly. 5 | EQUALS = 1; 6 | 7 | // String begins with the match value. 8 | PREFIX = 2; 9 | 10 | // String contains the match value. 11 | CONTAINS = 3; 12 | } 13 | 14 | // Matches a string value in a search criteria. 15 | message StringMatch { 16 | optional MatchPattern pattern = 1 [default = EQUALS]; 17 | optional string value = 2; 18 | } 19 | 20 | // Possible limits for a time range. 21 | message TimeRange { 22 | // Beginning of the allowed time range (RFC3339) 23 | optional string start = 1; 24 | // End of the allowed time range (RFC3339) 25 | optional string end = 2; 26 | } 27 | -------------------------------------------------------------------------------- /protos/tbmatch/session.proto: -------------------------------------------------------------------------------- 1 | 2 | // Authentication and session management. 3 | 4 | package tbmatch; 5 | 6 | import "tbrpc/tbrpc.proto"; 7 | 8 | 9 | enum GameType { 10 | // Rising Thunder. 11 | GT_RISING_THUNDER = 100; 12 | } 13 | 14 | message LoginRequest { 15 | // Either handle or e-mail address can be used. 16 | optional string login = 1; 17 | optional string password = 2; 18 | 19 | // Client build. May require certain version to login. 20 | optional string build_version = 5; 21 | 22 | // Remote IP now passed via XFF header by TBFE 23 | optional string __deprecated_remote_ip = 10; 24 | } 25 | 26 | message GetGameSessionTicketResult { 27 | optional GameType game = 1; 28 | optional string nonce = 2; 29 | } 30 | 31 | message RedeemGameSessionTicketRequest { 32 | optional GameType game = 1; 33 | optional string nonce = 2; 34 | 35 | // Client build. Server may require a certain version to login. 36 | optional string build_version = 5; 37 | } 38 | 39 | message GetGameSessionTicketRequest { 40 | optional GameType game = 1; 41 | 42 | // Build number of the client. If set, checks that the build is allowed. 43 | optional string client_build_version = 2; 44 | } 45 | 46 | 47 | service SessionService { 48 | // Establish an authenticated session. 49 | rpc Login (LoginRequest) returns (tbrpc.Empty) { 50 | option (tbrpc.access) = ANON; 51 | } 52 | 53 | // Get a ticket for the indicated game type. 54 | // May only be granted if the corresponding game access bit is held by the user. 55 | rpc GetGameSessionTicket(GetGameSessionTicketRequest) returns (GetGameSessionTicketResult) { 56 | option (tbrpc.access) = LOGIN; 57 | } 58 | 59 | // Redeem a game session ticket. The resulting session is distinct from the launcher 60 | // session but enforced max one per account. 61 | rpc RedeemGameSessionTicket(RedeemGameSessionTicketRequest) returns (tbrpc.Empty) { 62 | option (tbrpc.access) = ANON; 63 | } 64 | 65 | // Invalidate session. 66 | rpc Logout (tbrpc.Empty) returns (tbrpc.Empty) { 67 | option (tbrpc.access) = LOGIN; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /protos/tbmatch/shop.proto: -------------------------------------------------------------------------------- 1 | // Service to handle shopping and payments. 2 | 3 | package tbmatch; 4 | 5 | import "tbrpc/tbrpc.proto"; 6 | import "tbmatch/query.proto"; 7 | 8 | enum OrderStatus { 9 | // New order that has not been processed yet. 10 | NEW = 1; 11 | // Order accepted and is pending settlement. 12 | PENDING = 2; 13 | // Order payment method failed. 14 | FAILED = 3; 15 | // Payment has been accepted and goods delivered. 16 | // For item orders, this state occurs on approval. 17 | // For balance loads, after settlement has been observed. 18 | COMPLETE = 4; 19 | // Order canceled before payment settled. 20 | // Any goods that delivered in advance have been returned. 21 | CANCELED = 5; 22 | // Item return / refund requested; pending settlement. 23 | RETURN_PENDING = 6; 24 | // Refund was approved but failed to settle. Items not returned. 25 | RETURN_FAILED = 7; 26 | // Refund settled and items returned. 27 | RETURNED = 8; 28 | } 29 | 30 | message BillingAddress { 31 | // AuthNet customerAddressType 32 | optional string first_name = 1; // 50 33 | optional string last_name = 2; // 50 34 | optional string company = 3; // 50 35 | 36 | // Obscured on fetch. Submit obscured value to leave unchanged. 37 | optional string address = 4; // 60 38 | 39 | optional string city = 5; // 40 40 | optional string state = 6; // 40 41 | optional string zip = 7; // 20 42 | optional string country = 8; // 60 43 | 44 | // Obscured on fetch. Submit obscured value to leave unchanged. 45 | optional string phone_number = 9; // 255 46 | } 47 | 48 | message PaymentMethod { 49 | // Unique ID provided by server. Leave unset during creation. 50 | optional int64 method_id = 1; 51 | 52 | // 13-16 digits, no dashes or spaces. 53 | // Obscured on fetch. Submit obscured value to leave unchanged. 54 | optional string card_number = 2; 55 | 56 | // YYYY-MM 57 | // Obscured on fetch. Submit obscured value to leave unchanged. 58 | optional string card_exp = 3; 59 | 60 | // 3 or 4 digit CCV numeric value. Not stored in AuthNet, but used 61 | // during validation of create stored method, update stored method, and 62 | // for transactions with a custom payment method. 63 | optional string card_ccv = 4; 64 | 65 | // Observed card issuer type (Visa, Discover, etc). This field is 66 | // ignored for stored payment methods, but may be returned for 67 | // processed orders. 68 | optional string card_type = 5; 69 | 70 | // The billing address. 71 | // 72 | // Unset indicates this payment method should use the account's 73 | // first and last name, country, and current contact address. 74 | // It is an error to leave unset when the contact address has not 75 | // yet been stored. 76 | optional BillingAddress billing = 10; 77 | 78 | // Payment label, used to easily identify the card to the user. 79 | optional string label = 60; 80 | } 81 | 82 | message GetStoredPaymentMethodsResult { 83 | // List of all stored payment methods. 84 | repeated PaymentMethod method_list = 1; 85 | 86 | // Which method is set as the default. 87 | optional int64 default_method_id = 2; 88 | } 89 | 90 | message PaymentMethodId { 91 | optional int64 method_id = 1; 92 | } 93 | 94 | // State of a merchant gateway transaction. 95 | message Transaction { 96 | // External payment transaction ID. 97 | optional int64 transaction_id = 33; 98 | // Payment processor generated authorization code. 99 | optional string authorization_code = 34; 100 | // Last observed external transaction state. 101 | // See http://www.authorize.net/support/ReportingGuide_XML.pdf page 26. 102 | optional string transaction_status = 35; 103 | } 104 | 105 | message Funding { 106 | enum Source { 107 | // None indicated. 108 | FS_NONE = 0; 109 | // Use available account balance for the order's currency. 110 | FS_ACCOUNT_BALANCE = 1; 111 | // Use the current default stored payment method. 112 | FS_STORED_DEFAULT = 2; 113 | // Use the stored payment method identified by stored_method_id. 114 | FS_STORED = 3; 115 | // Use a custom payment method described by custom_method. 116 | FS_CUSTOM = 4; 117 | } 118 | optional Source source = 1 [default = FS_NONE]; 119 | 120 | optional int64 stored_method_id = 2; 121 | 122 | // When using a stored method, use this CVV value for validation. 123 | optional string stored_method_card_code = 3; 124 | 125 | optional PaymentMethod custom_method = 4; 126 | } 127 | 128 | message PurchaseOrderItem { 129 | // Catalog item ID of the item. 130 | optional int64 item_id = 1 [default = -1]; 131 | // Quantity of this item to be purchased. 132 | optional int32 quantity = 10; 133 | // Unit price for the item. 134 | optional tbrpc.Money unit_price = 11; 135 | } 136 | 137 | message PurchaseOrder { 138 | // System-generated order number. Unset in new requests. 139 | optional int64 order_number = 1; 140 | 141 | 142 | // ***** Required fields ***** 143 | // These must be set on creation. 144 | 145 | // Currency used for all items and amounts in this order. 146 | // For game currency, account balance funding source is required. 147 | optional string currency = 2; 148 | 149 | // List of items requested for purchase. Minimum 1, max 30. 150 | repeated PurchaseOrderItem item = 3; 151 | // Amount of tax and shipping included in the total. 152 | optional tbrpc.Money tax_amount = 4; 153 | optional tbrpc.Money ship_amount = 5; 154 | // Total amount to charge the customer for this order. 155 | // Must be equal to the sum of each item unit price times quantity, plus tax. 156 | optional tbrpc.Money total_amount = 6; 157 | 158 | // ***** End required fields ***** 159 | 160 | // Status of the order. 161 | optional OrderStatus status = 10; 162 | 163 | // Was account balance used? 164 | optional bool use_account_balance = 11; 165 | // If not drawn from account balance, snapshot of the payment method used. 166 | optional PaymentMethod payment_method = 12; 167 | 168 | // Creation and last modification time (RFC3339). 169 | optional string create_time = 13; 170 | optional string modify_time = 14; 171 | 172 | // Account that created the order. 173 | optional int64 account_id = 32; 174 | 175 | optional Transaction payment_transaction = 33; 176 | optional Transaction refund_transaction = 34; 177 | } 178 | 179 | message OrderSort { 180 | enum SortBy { 181 | ORDER_ID = 1; 182 | STATUS = 2; 183 | CREATE_TIME = 3; 184 | MODIFY_TIME = 4; 185 | TOTAL_AMOUNT = 5; 186 | LAST_NAME = 6; 187 | } 188 | 189 | // First sort-by column. 190 | optional SortBy first = 1 [default = ORDER_ID]; 191 | 192 | // Secondary sort-by column. 193 | optional SortBy second = 2; 194 | 195 | // Set to true for highest values first. 196 | optional bool descending = 3; 197 | } 198 | 199 | message BalanceEntrySort { 200 | enum SortBy { 201 | ORDER_ID = 1; 202 | STATUS = 2; 203 | CREATE_TIME = 3; 204 | ENTRY_AMOUNT = 4; 205 | } 206 | 207 | // Sort-by column. 208 | optional SortBy sort = 1 [default = ORDER_ID]; 209 | 210 | // Set to true for highest values first. 211 | optional bool descending = 3; 212 | } 213 | 214 | 215 | // Summary of an order that affected the account balance 216 | message BalanceEntry { 217 | // Order number. 218 | optional int64 order_number = 1; 219 | 220 | // True for balance load events. 221 | optional bool balance_load = 2; 222 | 223 | // Amount of the balance change. Balance loads will be positive, 224 | // deductions will be negative. 225 | optional tbrpc.Money amount = 3; 226 | 227 | // Status of the order. 228 | optional OrderStatus order_status = 4; 229 | 230 | // Creation time of the order. 231 | optional string create_time = 5; 232 | } 233 | 234 | 235 | 236 | 237 | // *********************** RPC Request/Response Types ************************** 238 | 239 | 240 | // Submit a purchase order. Provide minimum required fields 241 | // in PurchaseOrder, and specify a funding source. 242 | message SubmitPurchaseOrderRequest { 243 | optional PurchaseOrder order = 1; 244 | optional Funding funding = 20; 245 | } 246 | message SubmitPurchaseOrderResult { 247 | // Generated order number. 248 | optional int64 order_number = 1; 249 | // Current status of the order. 250 | optional OrderStatus order_status = 2; 251 | } 252 | 253 | // Load a specific purchase order. 254 | message GetPurchaseOrderRequest { 255 | optional int64 order_number = 1; 256 | } 257 | 258 | // Get the account's purchase order history. Result size limit 259 | // controlled by server configuration. 260 | message GetAccountOrderHistoryRequest { 261 | // Order sorting 262 | optional OrderSort sort = 2; 263 | 264 | // Skip past this many records in the result. 265 | optional int32 offset = 5; 266 | } 267 | message GetAccountOrderHistoryResult { 268 | // Orders that matched the criteria. 269 | repeated PurchaseOrder orders = 1; 270 | 271 | // True when the included results reached the end of the query result. 272 | optional bool end_of_data = 2; 273 | } 274 | 275 | message GetAccountBalanceHistoryRequest { 276 | // Currency of the balance. If not specified, will use the 277 | // account's default currency. 278 | optional string currency = 1; 279 | 280 | // Creation date time range. Server may impose limit; 281 | // will return last 30 days if not set. 282 | optional TimeRange create_time = 2; 283 | 284 | // Sort order to sort the orders in short order. 285 | optional BalanceEntrySort sort = 4; 286 | 287 | // Skip past this many records in the result. 288 | optional int32 offset = 5; 289 | } 290 | message GetAccountBalanceHistoryResult { 291 | // Currency of the affected balance. 292 | optional string currency = 1; 293 | 294 | // Summaries of orders that affected the balance. 295 | repeated BalanceEntry entry = 2; 296 | 297 | // True when the included results reached the end of the query result. 298 | optional bool end_of_data = 3; 299 | } 300 | 301 | message GetAccountBalanceRequest { 302 | // Currency of the balance to retreive. If not given, 303 | // uses the user's default currency. 304 | optional string currency = 1; 305 | optional bool include_pending = 2; 306 | } 307 | message GetAccountBalanceResult { 308 | optional string currency = 1; 309 | optional tbrpc.Money balance = 2; 310 | optional tbrpc.Money pending = 3; 311 | } 312 | 313 | 314 | service ShopService { 315 | // ********************** Stored Payment Methods ************************* 316 | 317 | // Get the current stored payment method. Returns 318 | // msg.shop.paymentMethod.notExist if none has been set. 319 | rpc GetStoredPaymentMethods (tbrpc.Empty) returns (GetStoredPaymentMethodsResult) { 320 | option (tbrpc.access) = SHOP; 321 | } 322 | 323 | // Add a stored payment method used to make in-game purchases. 324 | // Also makes it the default method. 325 | rpc CreateStoredPaymentMethod (PaymentMethod) returns (tbrpc.Empty) { 326 | option (tbrpc.access) = SHOP; 327 | } 328 | 329 | // This overwrites all existing values. method_id must be set. 330 | // Masked values returned by GetStoredPaymentInfo will keep their original values. 331 | rpc UpdateStoredPaymentMethod (PaymentMethod) returns (tbrpc.Empty) { 332 | option (tbrpc.access) = SHOP; 333 | } 334 | 335 | // Set the payment method as the default. 336 | rpc SetDefaultStoredPaymentMethod (PaymentMethodId) returns (tbrpc.Empty) { 337 | option (tbrpc.access) = SHOP; 338 | } 339 | 340 | // Remove the stored payment method from the account. 341 | // If the default method is removed, no method will be marked as 342 | // default; the user must elect a new one. 343 | rpc RemoveStoredPaymentMethod (PaymentMethodId) returns (tbrpc.Empty) { 344 | option (tbrpc.access) = SHOP; 345 | } 346 | 347 | 348 | // ************************** Purchase Orders ***************************** 349 | 350 | // Submit a new purchase order. 351 | rpc SubmitPurchaseOrder (SubmitPurchaseOrderRequest) returns (SubmitPurchaseOrderResult) { 352 | option (tbrpc.access) = SHOP; 353 | } 354 | 355 | // Get an existing purchase order. For a non-admin user, the query will be restricted 356 | // to those orders belonging to the account. 357 | rpc GetPurchaseOrder (GetPurchaseOrderRequest) returns (PurchaseOrder) { 358 | option (tbrpc.access) = SHOP; 359 | } 360 | 361 | // Get order records belonging to the calling account. 362 | // Server-enforced result size limit per-call. 363 | rpc GetAccountOrderHistory (GetAccountOrderHistoryRequest) returns (GetAccountOrderHistoryResult) { 364 | option (tbrpc.access) = SHOP; 365 | } 366 | 367 | // Get order records that affected the account balance. 368 | // Server-enforced limit on time range and result size. 369 | rpc GetAccountBalanceHistory (GetAccountBalanceHistoryRequest) returns (GetAccountBalanceHistoryResult) { 370 | option (tbrpc.access) = SHOP; 371 | } 372 | 373 | 374 | // ************************** Account Balance ***************************** 375 | 376 | // Get the current account balance. 377 | rpc GetAccountBalance(GetAccountBalanceRequest) returns (GetAccountBalanceResult) { 378 | option (tbrpc.access) = SHOP; 379 | } 380 | } 381 | -------------------------------------------------------------------------------- /protos/tbmatch/user.proto: -------------------------------------------------------------------------------- 1 | 2 | import "tbmatch/match.proto"; 3 | 4 | package tbmatch; 5 | 6 | enum PlayerState { 7 | PS_MENU = 101; 8 | PS_QUEUE = 102; 9 | PS_LOBBY = 103; 10 | } 11 | 12 | 13 | message PlayerSession { 14 | optional PlayerState state = 1; 15 | 16 | message ActiveMatch { 17 | optional int64 match_id = 1; 18 | optional bool spectator = 2; 19 | } 20 | optional ActiveMatch active_match = 2; 21 | 22 | message Menu { 23 | enum Screen { 24 | SUMMARY = 1; 25 | PLAYER_STATS = 2; 26 | PLAYER_HISTORY = 3; 27 | STORE = 4; 28 | } 29 | optional Screen screen = 1; 30 | }; 31 | optional Menu menu = 101; 32 | 33 | message Queue { 34 | optional tbmatch.MatchType match_type = 1; 35 | optional int64 start_time = 2; 36 | } 37 | optional Queue queue = 102; 38 | 39 | message Lobby { 40 | optional int64 lobby_id = 1; 41 | } 42 | optional Lobby lobby = 103; 43 | } 44 | -------------------------------------------------------------------------------- /protos/tbportal/portal.proto: -------------------------------------------------------------------------------- 1 | package tbportal; 2 | 3 | import "tbmatch/match.proto"; 4 | 5 | // ************ Configuration *************** 6 | 7 | message RedisListenConfig { 8 | // Address of the Redis server. 9 | optional string redis_host = 1 [default="localhost"]; 10 | optional int32 redis_port = 2 [default=6379]; 11 | 12 | // Redis key to BLPOP for Request messages. 13 | optional string listen_key = 3 [default="portal:request"]; 14 | 15 | // Requests older than this many seconds are discarded with no response. 16 | optional int32 req_expire_sec = 10 [default=30]; 17 | 18 | // Response keys are set to EXPIRE in Redis after this many seconds. 19 | // Set to 0 to disable expiry. 20 | optional int32 resp_expire_sec = 11 [default=300]; 21 | } 22 | 23 | message NetConfig { 24 | // IP address to return in responses. 25 | optional string public_ip = 1 [default="127.0.0.1"]; 26 | 27 | // Allocate from port numbers [ port_base, port_base+port_range*instance ) 28 | optional int32 port_base = 2 [default=42424]; 29 | optional int32 port_range = 3 [default=4096]; 30 | optional int32 instance = 4 [default=0]; 31 | } 32 | 33 | message StatusConfig { 34 | // Prometheus status, 0 to disable. 35 | optional int32 port = 1 [default=8118]; 36 | 37 | // URI to respond on for metrics. 38 | optional string uri = 2 [default="/metrics"]; 39 | 40 | // Health check endpoint. 41 | optional string health_uri = 3 [default="/healthz"]; 42 | 43 | // List of CIDR net ranges to allow. Empty to allow all. 44 | repeated string allow_net = 4; 45 | } 46 | 47 | message GameSessionConfig { 48 | // Don't consume requests when session count reaches this limit. 49 | // Cannot be higher than port_range / 2. 50 | optional int32 max_sessions = 1 [default=2048]; 51 | 52 | // Max # of auth failures by either client before closing. 53 | optional int32 max_auth_fail = 30 [default=3]; 54 | 55 | // Close if either client hasn't sent a handshakes after this long. 56 | optional int32 connect_timeout_ms = 31 [default=30000]; 57 | 58 | // Should match UTBNetSession::HandshakeTimeoutSecs 59 | optional int32 handshake_report_timeout_ms = 37 [default=5500]; 60 | 61 | // Close if one client stops sending for too long. 62 | // Must be long enough to cover the window between handshake and sync start. 63 | // Must be longer than goodbye_timeout_ms. 64 | optional int32 inactive_timeout_ms = 32 [default=7000]; 65 | 66 | // Close after this long, no matter what. 67 | optional int32 active_timeout_ms = 33 [default=14400000]; // 4h = 60 * 60 * 4 * 1000 68 | 69 | // Time between first and second client goodbye, after which 70 | // the session is marked as disconnect. 71 | optional int32 goodbye_timeout_ms = 34 [default=5000]; // 4h = 60 * 60 * 4 * 1000 72 | 73 | // If other player disconnects within this time, call it double-disconnect. 74 | optional int32 double_disconnect_timeout_ms = 35 [default=2250]; 75 | 76 | // Time allowed to select new variants 77 | optional int32 var_change_timeout_ms = 36 [default=30000]; 78 | 79 | // Time to allow after final Goodbye for input reporting stream to terminate. 80 | optional int32 input_linger_timeout_ms = 38 [default=10000]; 81 | 82 | 83 | // Max number of handshake reply packets to send. 84 | optional int32 max_handshake_replies = 40 [default = 10]; 85 | 86 | // Milliseconds between handshake replies. 87 | optional int32 handshake_reply_interval_ms = 41 [default = 200]; 88 | 89 | // Max number of variant change reply packets to send. 90 | optional int32 max_var_change_replies = 45 [default = 5]; 91 | 92 | // Milliseconds between variang change replies. 93 | optional int32 var_change_reply_interval_ms = 46 [default = 200]; 94 | 95 | // Number of games wins to determine the victor of the match. 96 | optional int32 games_to_win = 50 [default = 2]; 97 | } 98 | 99 | message ObserverConfig { 100 | // Number of frames of input per client report. 101 | optional uint32 report_size = 1 [default = 60]; 102 | // Number of frames of input per outgoing event bucket. 103 | optional uint32 bucket_size = 2 [default = 180]; 104 | } 105 | 106 | message PingTestConfig { 107 | // Number of packets to bounce 108 | optional int32 ping_count = 1 [default = 10]; 109 | 110 | // Give waiting up after how many ms? 111 | optional int32 wait_timeout_ms = 2 [default = 6500]; 112 | 113 | // Send next packet after no valid response for how many ms? 114 | optional int32 next_ping_timeout_ms = 3 [default = 500]; 115 | } 116 | 117 | // Server reads this proto in text format as a configuration file. 118 | message ServerConfig { 119 | optional RedisListenConfig redis = 1; 120 | optional StatusConfig status = 2; 121 | optional NetConfig net = 3; 122 | 123 | optional GameSessionConfig session = 10; 124 | optional PingTestConfig ping_test = 11; 125 | 126 | optional ObserverConfig observer = 20; 127 | } 128 | 129 | 130 | 131 | 132 | // ********* Request / response protocol ********** 133 | 134 | message ClientSpec { 135 | // Secret keys for authorize client packets. 136 | optional fixed64 secret = 1; 137 | 138 | // Client IP address. Host byte order. 139 | // XXX: Not actually used. 140 | optional int32 client_ip = 2; 141 | 142 | // The initial character spec for the client. 143 | optional tbmatch.CharacterSpec character = 3; 144 | 145 | // The 128-bit shared key used to hash client packets 146 | optional bytes shared_key = 4; 147 | } 148 | 149 | message EventQueue { 150 | optional string type = 1; 151 | optional string id = 2; 152 | } 153 | 154 | // Request a new game session. 155 | message GameSessionRequest { 156 | // Clients of the portal, should be exactly 2. 157 | repeated ClientSpec spec = 1; 158 | 159 | // Queue to publish input events. 160 | optional EventQueue observer = 2; 161 | 162 | // Allow GameResetRequest to reset options for next game? 163 | optional bool allow_reset = 3; 164 | } 165 | 166 | // Enqueued by portal in response to handling an GameSessionResponse. 167 | message GameSessionResponse { 168 | // Public IP address of the portal server. Host byte order. 169 | optional int32 public_ip = 2; 170 | 171 | // Listening ports for each client of the portal, in the same 172 | // order as client_ip in the request. (e.g. client_ip[0] 173 | // should connect to public_ip:public_port[0]). 174 | repeated int32 public_port = 3; 175 | } 176 | 177 | // Pushed to a Redis list when a game session is over. 178 | message GameSessionReport { 179 | // 128-bit UUID of the session. 180 | optional bytes uuid = 1; 181 | 182 | enum Resolution { 183 | UNKNOWN = 0; 184 | 185 | GOODBYE = 1; // All clients closed gracefully. 186 | HANDSHAKE_TIMEOUT = 2; // One or more clients failed to contact the server, or report peer-to-peer status. 187 | DISCONNECT = 3; // Handshake succeeded, but one or both clients stopped sending packets 188 | // without saying goodbye. 189 | MAX_LIFETIME = 4; // Session exceeded the max lifetime. 190 | 191 | HANDSHAKE_FAIL = 5; // Clients reached the server but not each other. 192 | } 193 | optional Resolution resolution = 3; 194 | 195 | // Unix timestamp when session was opened. 196 | optional int64 open_time = 4; 197 | // Unix timestamp when session closed, gracefully or not. 198 | optional int64 close_time = 5; 199 | 200 | // If HANDSHAKE_TIMEOUT or DISCONNECT, the list of player slots (0, 1) 201 | // who failed to handshake or failed to Goodbye within the time window of the opponent. 202 | repeated int32 timeout_slot = 6; 203 | 204 | // If HANDSHAKE_FAIL, the reason given by clients. 205 | enum HandshakeStatus { 206 | OK = 0; 207 | UNREACHABLE = 1; 208 | HIGH_PING = 2; 209 | } 210 | optional HandshakeStatus handshake_status = 10; 211 | 212 | // The ping report (averaged between the two clients). 213 | // Present if HANDSHAKE_FAIL && HIGH_PING, GOODBYE, or DISCONNECT 214 | message PingReport { 215 | optional int32 min_ping_ms = 1; 216 | optional int32 avg_ping_ms = 2; 217 | optional int32 max_ping_ms = 3; 218 | } 219 | optional PingReport ping_report = 13; 220 | 221 | optional tbmatch.MatchReport match_report = 9; 222 | 223 | // Stats collected from individual players. 224 | message PlayerStat { 225 | optional int32 avg_ping_ms = 1; 226 | optional int32 avg_fps = 2; 227 | 228 | // Seconds between handshake report and first game report with frame_count > 0. 229 | optional int32 level_load_time = 3; 230 | } 231 | repeated PlayerStat player_stat = 11; 232 | 233 | // If Resolution == DISCONNECT, the reason for the disconnect. 234 | enum DisconnectReason { 235 | NONE = 0; // No disconnect (default value) 236 | INACTIVITY = 1; // One or both clients stopped sending packets 237 | GOODBYE_TIMEOUT = 2; // One client sent a goodbye and the other client didn't 238 | // send a goodbye in time. 239 | } 240 | optional DisconnectReason disconnect_reason = 12; 241 | 242 | // Deprecated fields. 243 | optional bool __deprecated_outcome_agree = 7; 244 | optional bytes __deprecated_outcome = 8; 245 | 246 | } 247 | 248 | message PingTestRequest { 249 | // Client that will be performing the ping. 250 | optional ClientSpec spec = 1; 251 | } 252 | 253 | message PingTestResponse { 254 | // Public IP address of the portal server running the ping test. Host byte order. 255 | optional int32 public_ip = 2; 256 | 257 | // Listening port of the ping test. 258 | optional int32 public_port = 6; 259 | } 260 | 261 | message PingTestReport { 262 | enum Resolution { 263 | UNKNOWN = 0; 264 | SUCCESS = 1; // Ping test completed successfully. 265 | TIMEOUT = 2; // No responses from client. 266 | } 267 | 268 | optional Resolution resolution = 2; 269 | 270 | // Number of pings sent and responses received. 271 | optional int32 sent = 10; 272 | optional int32 received = 11; 273 | 274 | // Max latency observed in milliseconds. 275 | optional int32 max_latency_ms = 12; 276 | 277 | // Average latency observed in milliseconds. 278 | optional int32 avg_latency_ms = 13; 279 | } 280 | 281 | message GameResetNotification { 282 | optional bytes portal_uuid = 1; 283 | } 284 | 285 | enum RequestType { 286 | GAME_SESSION = 10; 287 | PING_TEST = 11; 288 | GAME_RESET_NOTIFICATION = 12; 289 | } 290 | 291 | // Incoming request; may be either for a session or ping test. 292 | message Request { 293 | optional RequestType type = 1; 294 | 295 | // Caller-specified value that will be provided in future messages 296 | // about this request. This is actually the matchId for game sessions. 297 | optional int64 caller_id = 2; 298 | 299 | // Redis channel to publish the request handling response. 300 | optional string response_channel = 3; 301 | 302 | // Unix timestamp when request was queued. Requests too old will be ignored. 303 | optional int64 timestamp = 4; 304 | 305 | // List to RPUSH the serialized report when the workflow completes. 306 | optional string report_key = 5; 307 | 308 | optional GameSessionRequest game = 10; 309 | optional PingTestRequest ping_test = 11; 310 | optional GameResetNotification game_reset = 12; 311 | } 312 | 313 | // Response to a request, published to Request.response_channel. 314 | message Response { 315 | optional RequestType type = 1; 316 | optional int64 caller_id = 2; 317 | 318 | enum Result { 319 | UNKNOWN = 0; 320 | SUCCESS = 1; // Session / ping test active. 321 | BAD_REQUEST = 2; // Request received but not valid. 322 | SERVER_BUSY = 3; // Server was too busy to handle the request. 323 | NETWORK_FAIL = 4; // Failed in network layer (bind). 324 | } 325 | 326 | optional Result result = 3; 327 | 328 | // 128-bit uuid of the operation on success. 329 | optional bytes op_uuid = 4; 330 | 331 | optional GameSessionResponse game = 10; 332 | optional PingTestResponse ping_test = 11; 333 | } 334 | 335 | // Report for action completion. 336 | message Report { 337 | optional RequestType type = 1; 338 | optional int64 caller_id = 2; 339 | optional bytes op_uuid = 3; 340 | 341 | optional GameSessionReport game = 10; 342 | optional PingTestReport ping_test = 11; 343 | } 344 | 345 | -------------------------------------------------------------------------------- /protos/tbrpc/tbrpc.proto: -------------------------------------------------------------------------------- 1 | // RPC transport layer encapsulation. 2 | 3 | import "google/protobuf/descriptor.proto"; 4 | 5 | package tbrpc; 6 | 7 | enum Status { 8 | // The request succeeded. 9 | S_SUCCESS = 0; 10 | 11 | // The request could not succeed because it was invalid. 12 | S_ERROR = 1; 13 | 14 | // The request was valid but the server encountered a problem. 15 | S_SERVER_ERROR = 2; 16 | 17 | // The request took too long to complete. 18 | S_TIMEOUT = 3; 19 | 20 | // An error occurred at the protocol layer or below. 21 | S_TRANSPORT_ERROR = 4; 22 | 23 | // Nothing is (yet) known about this request. 24 | S_UNKNOWN = 128; 25 | } 26 | 27 | // Types of access control for RPC methods. 28 | enum Access { 29 | // Anonymous; allowed for unauthenticated sessions. 30 | ANON = 0; 31 | 32 | // Access that merely requires an authenticated session. 33 | LOGIN = 1; 34 | 35 | // Access all user-facing shop methods; manage stored payments, 36 | // place orders, check status of orders. 37 | SHOP = 10; 38 | 39 | // Able to login and play the RisingThunder game client and call match-making functions. 40 | MATCH = 11; 41 | 42 | // Allowed to use the RisingThunder.com user forum. 43 | RT_FORUM_USER = 12; 44 | 45 | // Invoke stats reporting queries. 46 | STATS_READ = 18; 47 | 48 | // Server account that can read global metadata. 49 | APP = 20; 50 | 51 | // Create, modify, and search user accounts. 52 | USER_MGMT = 21; 53 | 54 | // Search and sync purchase orders. 55 | ORDER_READ_SYNC = 22; 56 | 57 | // Void and refund purchase orders. 58 | ORDER_MANAGER = 23; 59 | 60 | // View the audit log. 61 | AUDIT = 24; 62 | 63 | // RT Forum administrator 64 | RT_FORUM_MGMT = 25; 65 | 66 | // RT Forum moderator 67 | RT_FORUM_MOD = 26; 68 | 69 | // Read & write run-time config state. 70 | CONFIG_READ = 27; 71 | CONFIG_WRITE = 28; 72 | 73 | // Super-user who is granted all rights. 74 | SUPER = 30; 75 | } 76 | 77 | enum SessionType { 78 | // Default session type, used for web sessions. 79 | DEFAULT = 0; 80 | 81 | // Rising Thunder game session. 82 | GAME_RT = 100; 83 | } 84 | 85 | extend google.protobuf.MethodOptions { 86 | // Role required to invoke. 87 | optional Access access = 51001; 88 | 89 | // If true, an invocation doesn't count as user activity (e.g. GetEvent) 90 | optional bool no_op = 51002; 91 | 92 | // If set, session type required to invoke. 93 | optional SessionType session = 51003; 94 | } 95 | 96 | extend google.protobuf.MessageOptions { 97 | // Iff non-empty, identifies message as a redis/lua config type, for which 98 | // redis/lua config stubs will be generated. 99 | // The value given will be the type name used in Lua, and also forms 100 | // the redis key prefix for the config fields as ":config:" 101 | optional string lua_config = 51101; 102 | } 103 | 104 | extend google.protobuf.FieldOptions { 105 | // For lua message types, override the default lua name to use. 106 | optional string lua = 51201; 107 | } 108 | 109 | // Parameters to a message. Using a message type 110 | // for each primitive works around protobuf-net's limitation 111 | // of not recording unset primitives. 112 | message MsgParam { 113 | message Str { 114 | optional string value = 1; 115 | } 116 | message Int { 117 | optional int32 value = 1; 118 | } 119 | message Float { 120 | optional float value = 1; 121 | } 122 | optional Str str = 1; 123 | optional Int int = 2; 124 | optional Float flt = 3; 125 | } 126 | 127 | // A localized message and optional positional values. 128 | message LocMsg { 129 | optional string id = 1; 130 | repeated MsgParam param = 2; 131 | } 132 | 133 | message Result { 134 | // Result code of the invocation 135 | optional Status result = 1 [default = S_UNKNOWN]; 136 | 137 | // Optional result message, usually set when result != S_SUCCESS. 138 | optional LocMsg msg = 4; 139 | 140 | // Serialized message result of invoked method. 141 | optional bytes content = 10; 142 | 143 | 144 | // deprecated fields 145 | optional string deprecated_msg_id = 2; 146 | repeated MsgParam deprecated_msg_params = 3; 147 | } 148 | 149 | // Used for rpc methods that take no arguments or return 150 | // no result content. Since all empty messages serialize 151 | // the same, we can create types as needed when extending an 152 | // existing method. 153 | message Empty { 154 | } 155 | 156 | // A monetary value. We can't represent these with float values 157 | // due to rounding errors for values like 0.1. Another option is 158 | // representing everything in cents, but that can lead to bugs. 159 | // This is the most unambiguous wire format. 160 | // 161 | // SIGN: When the value is negative, both fields express the sign. 162 | // For example, -$2.25 would be (-2), (-25). 163 | // The correctly signed value in cents can always be computed by: 164 | // 165 | // (Whole * 100) + Cents 166 | // 167 | // The value is invalid if signs differ, or Cents is out of range. 168 | // 169 | // Note also that the value cannot be interpreted without a 170 | // relevant currency, which is always taken from context. 171 | message Money { 172 | // Amount of whole units of currency. 173 | optional int64 whole = 1; 174 | 175 | // Amount of 1/100 units of currency. 176 | optional int32 cents = 2; 177 | } 178 | 179 | 180 | 181 | // *************** Load Balancer Client ****************** 182 | 183 | message RpcClientConfig { 184 | // Limit on idle connections per Match API server. 185 | optional int32 max_idle_conns_per_host = 1 [default = 16]; 186 | 187 | // How long to wait for RPCs to complete. 188 | optional int32 rpc_timeout_ms = 2 [default = 5000]; 189 | } 190 | 191 | 192 | message LBTargetGCEBackendService { 193 | // Name of the service account to get an access token for. 194 | optional string service_account = 1 [default = "default"]; 195 | 196 | // HTTPS proxy URL to use for API calls. 197 | optional string api_proxy_url = 2; 198 | 199 | // Name of Google project. 200 | optional string project = 3 [default = "radiant-cloud"]; 201 | 202 | // Name of the backend service to discover healthy instances in. 203 | optional string backend_service = 4; 204 | 205 | // How often to poll (fuzzed by 10%). 206 | optional int32 poll_interval_secs = 10 [default = 30]; 207 | 208 | // URI prefix for requests to this endpoint. 209 | optional string endpoint_uri = 11 [default = "/rpc"]; 210 | } 211 | 212 | 213 | message LBClientConfig { 214 | // Backend service to use for API discovery monitor. 215 | optional LBTargetGCEBackendService discovery = 1; 216 | 217 | // Static list of hosts to connect to. Only one of [backend_service, host*] may be specified. 218 | repeated string host = 2; 219 | 220 | // Per-host connection pool settings. 221 | optional RpcClientConfig client = 4; 222 | 223 | // After failure to invoke or ping a backend, how long before we will try again. 224 | optional int32 failure_retry_sec = 10 [default = 10]; 225 | 226 | // Ping a backend before invoking if it hasn't been used in this long. 227 | optional int32 idle_ping_sec = 11 [default = 60]; 228 | } 229 | -------------------------------------------------------------------------------- /protos/tbui/tbcharacter.proto: -------------------------------------------------------------------------------- 1 | package tbui; 2 | 3 | message UIVariantSpec { 4 | optional int32 id = 1; 5 | 6 | optional string name = 2; 7 | 8 | optional string description = 3; 9 | } 10 | 11 | message UISpecialsSpec { 12 | repeated UIVariantSpec variants = 2; 13 | } 14 | 15 | message UIMechSpec { 16 | optional string name = 1; 17 | } 18 | 19 | message UIPilotSpec { 20 | optional string name = 1; 21 | 22 | optional int32 age = 2; 23 | 24 | optional string origin = 3; 25 | } 26 | 27 | message UICharacterSpec { 28 | optional string type_name = 1; 29 | 30 | optional UIMechSpec mech = 2; 31 | 32 | optional UIPilotSpec pilot = 3; 33 | 34 | repeated UISpecialsSpec specials = 4; 35 | 36 | optional int32 ui_order = 5; 37 | } 38 | 39 | message UICharacterData { 40 | repeated UICharacterSpec char_specs = 1; 41 | 42 | // When starting UI in editor, want to initialize to the currently selected character and opponent. 43 | optional string selected_char = 2; 44 | 45 | optional string selected_opp = 3; 46 | } 47 | 48 | enum WindowResolutionType { 49 | WRT_FULLSCREEN = 1; 50 | 51 | WRT_FULLSCREEN_WINDOWED = 2; 52 | 53 | WRT_WINDOWED = 3; 54 | } 55 | 56 | enum GraphicsQualityType { 57 | // Special level for aggregator qualities (most notably "Overall Quality") 58 | GQT_CUSTOM = 0; 59 | 60 | GQT_LOW = 1; 61 | 62 | GQT_MEDIUM = 2; 63 | 64 | GQT_HIGH = 3; 65 | 66 | GQT_EPIC = 4; 67 | } 68 | 69 | message GraphicsSettings { 70 | optional bool vsync = 1; 71 | 72 | optional GraphicsQualityType overall_quality = 2; 73 | 74 | optional GraphicsQualityType resolution_quality = 3; 75 | 76 | optional GraphicsQualityType anti_aliasing_quality = 4; 77 | 78 | optional GraphicsQualityType shadow_quality = 5; 79 | 80 | optional GraphicsQualityType post_process_quality = 6; 81 | 82 | optional GraphicsQualityType texture_quality = 7; 83 | 84 | optional GraphicsQualityType effects_quality = 8; 85 | } 86 | 87 | message Resolution { 88 | optional int32 width = 1; 89 | 90 | optional int32 height = 2; 91 | } 92 | 93 | message ResolutionSettings { 94 | repeated Resolution available_resolutions = 1; 95 | 96 | // Index into available resolutions. 97 | optional int32 current_resolution = 2; 98 | 99 | optional WindowResolutionType type = 3; 100 | 101 | } 102 | 103 | message GraphicsSpec { 104 | optional ResolutionSettings resolution = 1; 105 | 106 | optional GraphicsSettings graphics = 2; 107 | } 108 | 109 | // Encapsulates all persisted local preferences that are account-specific 110 | // (as opposed to machine-specific) 111 | message LocalPlayerPreferences { 112 | optional KeyBindingConfig key_bindings = 1; 113 | 114 | optional int32 skill_estimate = 2; 115 | } 116 | 117 | message KeyBinding { 118 | // Multiple keys can be bound to one action (for now limited to 2). 119 | repeated string bound_keys = 2; 120 | } 121 | 122 | message KeyBindingSet { 123 | // The id of the binding is its position in the array. 124 | repeated KeyBinding bindings = 1; 125 | } 126 | 127 | message KeyBindingConfig { 128 | // A set of bindings for every type of input (controller, keyboard) 129 | repeated KeyBindingSet input_binding = 1; 130 | } 131 | 132 | // Settings the UI always loads at startup. 133 | message GlobalSettings { 134 | optional string server_url = 1; 135 | optional int32 listen_port = 2; 136 | optional string build_version = 3; 137 | optional bool auto_play = 4; 138 | optional int32 inactive_timeout_sec = 5; 139 | } 140 | 141 | message NetworkSpec { 142 | // Bind ports and set up P2P network access automatically. 143 | optional bool auto_conf = 1; 144 | 145 | // If not auto-configuring, the local port to bind to. 146 | optional int32 local_port = 2; 147 | 148 | // If not auto-configuring, the external port to advertise to other players. 149 | optional int32 external_port = 3; 150 | } 151 | 152 | // Volume integers range from 0 to volume_max 153 | message SoundSpec { 154 | optional int32 music_volume = 1; 155 | 156 | optional int32 vo_volume = 2; 157 | 158 | optional int32 fx_volume = 3; 159 | 160 | optional int32 volume_max = 4; 161 | } 162 | 163 | message GameOutcome { 164 | // Did local player win the game? 165 | optional bool winner = 1; 166 | 167 | // Did local player lose the game? 168 | optional bool loser = 2; 169 | 170 | // Was the gamea draw? 171 | optional bool draw = 3; 172 | 173 | // How many games has p1 won? 174 | optional int32 p1_won = 4; 175 | 176 | // How many games has p2 won? 177 | optional int32 p2_won = 5; 178 | } 179 | 180 | message DebugSpec { 181 | // Log GGPO activity 182 | optional bool ggpo_log = 1; 183 | } 184 | 185 | // Make sure the values are the same as ERoundResult in TBGameState 186 | enum RoundResultType { 187 | RRT_UNPLAYED = 0; 188 | RRT_NORMAL = 1; 189 | RRT_SUPER = 2; 190 | RRT_CHIP = 3; 191 | RRT_TIME = 4; 192 | RRT_DRAW = 5; // Double KO 193 | RRT_PERFECT = 6; 194 | } 195 | 196 | message PlayerRoundHistory { 197 | repeated RoundResultType history = 1; 198 | } 199 | 200 | message RoundHistory { 201 | repeated PlayerRoundHistory player_history = 1; 202 | } 203 | -------------------------------------------------------------------------------- /rtd.py: -------------------------------------------------------------------------------- 1 | """ 2 | """ 3 | 4 | import server 5 | 6 | if __name__ == "__main__": 7 | server.Start() 8 | -------------------------------------------------------------------------------- /scripts/generate_protos.cmd: -------------------------------------------------------------------------------- 1 | :: Usage: scripts\generate_protos.cmd 2 | :: 3 | :: Generate the python protobuf implementation 4 | 5 | python -m grpc_tools.protoc -Iprotos --python_out=. protos/tbmatch/session.proto 6 | python -m grpc_tools.protoc -Iprotos --python_out=. protos/tbadmin/account.proto 7 | python -m grpc_tools.protoc -Iprotos --python_out=. protos/tbadmin/audit.proto 8 | python -m grpc_tools.protoc -Iprotos --python_out=. protos/tbadmin/config.proto 9 | python -m grpc_tools.protoc -Iprotos --python_out=. protos/tbadmin/match.proto 10 | python -m grpc_tools.protoc -Iprotos --python_out=. protos/tbadmin/report.proto 11 | python -m grpc_tools.protoc -Iprotos --python_out=. protos/tbadmin/shop.proto 12 | python -m grpc_tools.protoc -Iprotos --python_out=. protos/tbadmin/stats.proto 13 | python -m grpc_tools.protoc -Iprotos --python_out=. protos/tbmatch/account.proto 14 | python -m grpc_tools.protoc -Iprotos --python_out=. protos/tbmatch/crash.proto 15 | python -m grpc_tools.protoc -Iprotos --python_out=. protos/tbmatch/event.proto 16 | python -m grpc_tools.protoc -Iprotos --python_out=. protos/tbmatch/lobby.proto 17 | python -m grpc_tools.protoc -Iprotos --python_out=. protos/tbmatch/log.proto 18 | python -m grpc_tools.protoc -Iprotos --python_out=. protos/tbmatch/match.proto 19 | python -m grpc_tools.protoc -Iprotos --python_out=. protos/tbmatch/query.proto 20 | python -m grpc_tools.protoc -Iprotos --python_out=. protos/tbmatch/session.proto 21 | python -m grpc_tools.protoc -Iprotos --python_out=. protos/tbmatch/shop.proto 22 | python -m grpc_tools.protoc -Iprotos --python_out=. protos/tbmatch/user.proto 23 | python -m grpc_tools.protoc -Iprotos --python_out=. protos/tbrpc/tbrpc.proto 24 | python -m grpc_tools.protoc -Iprotos --python_out=. protos/tbui/tbcharacter.proto 25 | python -m grpc_tools.protoc -Iprotos --python_out=. protos/tbportal/portal.proto 26 | -------------------------------------------------------------------------------- /scripts/generate_python.cmd: -------------------------------------------------------------------------------- 1 | :: Usage: scripts\generate_python.cmd 2 | :: 3 | :: Create the generated python files 4 | python generate_python.py scripts\templates\routes.template --ignoreGetEvent > server\generated_routes.py 5 | python generate_python.py scripts\templates\rpc_client.template > tests\rpc_client.py 6 | -------------------------------------------------------------------------------- /scripts/launch_rt.cmd: -------------------------------------------------------------------------------- 1 | :: Usage: scripts\launch_rt.cmd 2 | :: 3 | :: Connect Rising Thunder to a local instance of the server running on port 1337. 4 | :: Assumes you have a copy of RT in ..\Rising Thunder 5 | 6 | set tb.gameinst=ServerUri=http://localhost:1337/_01/ 7 | start "..\RisingThunder\RisingThunder.exe" 8 | 9 | -------------------------------------------------------------------------------- /scripts/setup.cmd: -------------------------------------------------------------------------------- 1 | :: Install all python dependencies 2 | python -m pip install grpcio grpcio-tools mako tornado pytest requests 3 | -------------------------------------------------------------------------------- /scripts/templates/routes.template: -------------------------------------------------------------------------------- 1 | # This file is automatically generated by geneate_python.py. Do NOT modify!! 2 | """ 3 | generated_routes.py 4 | 5 | Implement low-level route handlers for all the protobuf services. In the handlers, 6 | either call the RPC handler registered in the app server or throw an exception if 7 | no such handler exists. 8 | 9 | See server/rpc.py for more information. 10 | 11 | """ 12 | 13 | import server.rpc 14 | import tornado.web 15 | 16 | % for i in imports: 17 | import ${i} 18 | % endfor 19 | 20 | % for service in services: 21 | 22 | # ${service['name']} service 23 | % for method in service['methods']: 24 | class ${method['name']}Handler(tornado.web.RequestHandler): 25 | def post(self): 26 | request = ${typemap[method['input']]}() 27 | request.ParseFromString(self.request.body) 28 | server.rpc.LogProto('received ${method['name']} ', request) 29 | 30 | handler = server.rpc.GetRouteHandler('${method["name"]}') 31 | if not handler: 32 | raise NotImplementedError('${service["name"]} ${method["name"]} not implemented!') 33 | 34 | response = ${typemap[method['output']]}() 35 | result = handler(request, response, self) 36 | server.rpc.LogProto('replying with ', response) 37 | 38 | # wrap the response in a tbrpc.Result and return 39 | result = tbrpc.tbrpc_pb2.Result() 40 | result.result = tbrpc.tbrpc_pb2.S_SUCCESS 41 | result.content = response.SerializeToString() 42 | self.write(result.SerializeToString()) 43 | 44 | % endfor 45 | % endfor 46 | 47 | def GetRoutes(): 48 | """ 49 | Return all the routes defined in this module. 50 | """ 51 | return [ 52 | % for service in services: 53 | % for method in service['methods']: 54 | (r'/_01/rpc/${method['name']}', ${method['name']}Handler), 55 | % endfor 56 | % endfor 57 | ] -------------------------------------------------------------------------------- /scripts/templates/rpc_client.template: -------------------------------------------------------------------------------- 1 | # This file is automatically generated by create_routes.py. Do NOT modify!! 2 | """ 3 | rpc_client.py 4 | 5 | Implement a simple HTTP client with methods to send RPCs to the Rising 6 | Thunder server. 7 | """ 8 | 9 | import requests 10 | import google.protobuf.json_format 11 | 12 | % for i in imports: 13 | import ${i} 14 | % endfor 15 | 16 | class RpcClient(object): 17 | def __init__(self): 18 | self.session = requests.Session() 19 | 20 | % for service in services: 21 | 22 | # ${service['name']} service 23 | % for method in service['methods']: 24 | def ${method['name']}(self, request = None): 25 | if not request: 26 | request = tbrpc.tbrpc_pb2.Empty() 27 | assert type(request) == ${typemap[method['input']]} 28 | payload = request.SerializeToString() 29 | url = 'http://localhost:1337/_01/rpc/' + '${method["name"]}' 30 | r = self.session.post(url, data=payload) 31 | if r.status_code != 200: 32 | raise RuntimeError('rpc failed') 33 | 34 | r.raw.decode_content = True 35 | content = '' 36 | for chunk in r.iter_content(1024): 37 | content += chunk 38 | result = tbrpc.tbrpc_pb2.Result() 39 | result.ParseFromString(content) 40 | 41 | print google.protobuf.json_format.MessageToJson(result) 42 | if result.result != tbrpc.tbrpc_pb2.S_SUCCESS: 43 | raise RuntimeError('rpc failed') 44 | 45 | response = ${typemap[method['output']]}() 46 | response.ParseFromString(result.content) 47 | print google.protobuf.json_format.MessageToJson(response) 48 | 49 | return response 50 | 51 | % endfor 52 | % endfor 53 | -------------------------------------------------------------------------------- /server/__init__.py: -------------------------------------------------------------------------------- 1 | import uuid 2 | import tornado.log 3 | import tornado.options 4 | import tornado.web 5 | import tornado.ioloop 6 | import server.generated_routes 7 | import server.models.portal 8 | import server.models.matchmaker 9 | import server.models.users 10 | import server.models.lobbies 11 | 12 | tornado.options.parse_command_line() 13 | tornado.log.enable_pretty_logging() 14 | 15 | app = tornado.web.Application() 16 | ioloop = tornado.ioloop.IOLoop.current() 17 | 18 | users = server.models.users.Users() 19 | matchmaker = server.models.matchmaker.Matchmaker() 20 | portal = server.models.portal.Portal() 21 | lobbies = server.models.lobbies.Lobbies() 22 | 23 | import server.services.match_service 24 | import server.services.lobby_service 25 | import server.services.event_service 26 | import server.services.session_service 27 | 28 | def Start(): 29 | tornado.log.logging.info("Server starting at {0}".format(server.config.hostname)) 30 | 31 | matchmaker.StartPolling() 32 | 33 | routes = server.generated_routes.GetRoutes() 34 | routes.append((r'/_01/rpc/GetEvent', server.services.event_service.GetEventHandler)) 35 | app.add_handlers(r'.*', routes) 36 | app.listen(server.config.port) 37 | ioloop.start() 38 | 39 | 40 | NEXT_UNIQUE_ID = 0 41 | def GetNextUniqueId(): 42 | """ 43 | Useful for people who need a unique id for something (having all objects in the system having 44 | a unique id is less error-prone) 45 | """ 46 | global NEXT_UNIQUE_ID 47 | NEXT_UNIQUE_ID = NEXT_UNIQUE_ID + 1 48 | return NEXT_UNIQUE_ID 49 | 50 | def GetNewSecret(): 51 | return uuid.uuid4().int & (1<<64)-1 -------------------------------------------------------------------------------- /server/config.py: -------------------------------------------------------------------------------- 1 | """ 2 | config.py 3 | """ 4 | 5 | import os 6 | import tbmatch.match_pb2 7 | import tbportal.portal_pb2 8 | 9 | # The external hostname of the Rising Thunder server. 10 | # If environment variable RT_SERVER_HOSTNAME is defined, it will be used, otherwise 11 | # default to 127.0.0.1 for localhost. 12 | hostname = os.environ.get('RT_SERVER_HOSTNAME', '127.0.0.1') 13 | 14 | # Web port to listen on for RPCs 15 | port = 1337 16 | 17 | # The UDP port on this server that clients can use for ping testing. 18 | portal_port_base = 42424 19 | portal_port_range = 4096 20 | 21 | # number of times to ping for the ping test 22 | portal_ping_count = 5 23 | 24 | # amount of time to wait on the client to allow for UI setup before redeeming the game session ticket 25 | game_session_ticket_wait_interval_ms = 0.25 26 | 27 | game_session_config = tbportal.portal_pb2.GameSessionConfig() 28 | game_session_config.games_to_win = 2 29 | game_session_config.handshake_reply_interval_ms = 2000 30 | 31 | # Enabled features. 32 | featureSet = tbmatch.match_pb2.ClientFeatureSet() 33 | -------------------------------------------------------------------------------- /server/models/__init__.py: -------------------------------------------------------------------------------- 1 | # server/models __init__.py file -------------------------------------------------------------------------------- /server/models/lobbies.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=C0103 2 | 3 | """ 4 | lobbies.py 5 | 6 | Lobby management 7 | """ 8 | 9 | import server 10 | import string 11 | import random 12 | import logging 13 | import tbmatch.match_pb2 14 | import tbmatch.lobby_pb2 15 | 16 | class LobbyMember(object): 17 | def __init__(self, user, lobby): 18 | self.user = user 19 | self.ready = False 20 | self.character = None 21 | self.owner = user.user_id == lobby.owner_user_id 22 | self.lobby = lobby 23 | 24 | def EncodeState(self, state): 25 | state.account_id = self.user.user_id 26 | state.handle = self.user.handle 27 | state.owner = self.user.user_id == self.lobby.owner_user_id 28 | state.ready = self.ready 29 | 30 | class Lobby(object): 31 | def __init__(self, name): 32 | self.name = name 33 | self.members = {} 34 | self.queue = [] 35 | self.lobby_id = server.GetNextUniqueId() 36 | self.owner_user_id = None 37 | self.join_code = ''.join(random.choice(string.letters).upper() for i in xrange(5)) 38 | 39 | def EncodeState(self): 40 | state = tbmatch.lobby_pb2.Lobby() 41 | state.name = self.name 42 | state.lobby_id = self.lobby_id 43 | state.state = tbmatch.lobby_pb2.LS_IDLE 44 | state.type = tbmatch.lobby_pb2.LT_QUEUED 45 | state.game_config.options.mode = tbmatch.match_pb2.GameOptions.GM_FIGHT 46 | 47 | for m in self.members.values(): 48 | member = state.member.add() 49 | member.account_id = m.user.user_id 50 | member.handle = m.user.handle 51 | member.owner = m.user.user_id == self.owner_user_id 52 | member.ready = m.ready 53 | 54 | for user_id in self.queue: 55 | state.queue.extend([user_id]) 56 | 57 | return state 58 | 59 | def SetOwner(self, user): 60 | self.owner_user_id = user.user_id 61 | member = self.members[user.user_id] 62 | member.owner = True 63 | 64 | #update all users in the lobby with the new owner 65 | updateEvent = tbmatch.event_pb2.Event() 66 | updateEvent.type = tbmatch.event_pb2.Event.E_LOBBY_UPDATE 67 | updateEvent.lobby_update.lobby_id = self.lobby_id 68 | updateEvent.lobby_update.queue.extend(self.queue) 69 | member.EncodeState(updateEvent.lobby_update.update.add()) 70 | 71 | for m in self.members.values(): 72 | m.user.SendEvent(updateEvent) 73 | 74 | def AddUser(self, user): 75 | member = LobbyMember(user, self) 76 | self.members[user.user_id] = member 77 | 78 | # add them to the back of the queue, too 79 | self.queue.append(user.user_id) 80 | 81 | # if new user is only user, set as owner 82 | if len(self.members) == 1: 83 | member.owner = True 84 | self.owner_user_id = user.user_id 85 | 86 | # tell the new user to join the lobby 87 | joinEvent = tbmatch.event_pb2.Event() 88 | joinEvent.type = tbmatch.event_pb2.Event.E_LOBBY_JOIN 89 | joinEvent.lobby_join.lobby.CopyFrom(self.EncodeState()) 90 | user.SendEvent(joinEvent) 91 | 92 | #tell existing users to update the lobby 93 | updateEvent = tbmatch.event_pb2.Event() 94 | updateEvent.type = tbmatch.event_pb2.Event.E_LOBBY_UPDATE 95 | updateEvent.lobby_update.lobby_id = self.lobby_id 96 | updateEvent.lobby_update.queue.extend(self.queue) 97 | member.EncodeState(updateEvent.lobby_update.update.add()) 98 | 99 | for m in self.members.values(): 100 | if user.user_id != m.user.user_id: 101 | m.user.SendEvent(updateEvent) 102 | 103 | def RemoveUser(self, user): 104 | # remove user from member list and from queue 105 | self.members.pop(user.user_id, None) 106 | self.queue.remove(user.user_id) 107 | 108 | # if user who left was owner, assign new owner for lobby 109 | if user.user_id == self.owner_user_id and len(self.members) > 0: 110 | self.SetOwner(self.members.itervalues().next().user) 111 | 112 | # tell the existing user to leave the lobby 113 | leaveEvent = tbmatch.event_pb2.Event() 114 | leaveEvent.type = tbmatch.event_pb2.Event.E_LOBBY_LEAVE 115 | leaveEvent.lobby_leave.lobby_id = self.lobby_id 116 | leaveEvent.lobby_leave.reason = tbmatch.event_pb2.LobbyLeaveEvent.LEFT 117 | user.SendEvent(leaveEvent) 118 | 119 | #tell existing users to update the lobby 120 | updateEvent = tbmatch.event_pb2.Event() 121 | updateEvent.type = tbmatch.event_pb2.Event.E_LOBBY_UPDATE 122 | updateEvent.lobby_update.lobby_id = self.lobby_id 123 | updateEvent.lobby_update.queue.extend(self.queue) 124 | updateEvent.lobby_update.removed.extend([user.user_id]) 125 | 126 | for m in self.members.values(): 127 | if user.user_id != m.user.user_id: 128 | m.user.SendEvent(updateEvent) 129 | 130 | def GetNumberOfUsers(self): 131 | return len(self.members) 132 | 133 | def SetUserReady(self, user, ready, character): 134 | self.members[user.user_id].ready = ready 135 | self.members[user.user_id].character = character 136 | 137 | event = tbmatch.event_pb2.Event() 138 | event.type = tbmatch.event_pb2.Event.E_LOBBY_UPDATE 139 | event.lobby_update.lobby_id = self.lobby_id 140 | self.members[user.user_id].EncodeState(event.lobby_update.update.add()) 141 | 142 | for m in self.members.values(): 143 | m.user.SendEvent(event) 144 | 145 | def StartMatchIfReady(self): 146 | if len(self.members) < 2: 147 | return 148 | 149 | p1 = self.members[self.queue[0]] 150 | p2 = self.members[self.queue[1]] 151 | 152 | if not p1.ready or not p2.ready: 153 | return 154 | 155 | # found a match 156 | match_id = server.GetNextUniqueId() 157 | 158 | # create intermediate proto structures 159 | game_config = server.models.match.CreateGameConfig(match_id, p1.user, p1.character, p2.user, p2.character) 160 | 161 | game_session = server.models.match.CreateGameSessionRequest(p1.character, p2.character) 162 | p1port, p2port = server.portal.StartGameSession(game_session, game_config, p1.user, p2.user) 163 | 164 | game_endpoint_config1 = server.models.match.CreateGameEndpointConfig(0, p1port, game_session.spec[0].secret) 165 | game_endpoint_config2 = server.models.match.CreateGameEndpointConfig(1, p2port, game_session.spec[1].secret) 166 | 167 | # send lobby match start events to both players 168 | event1 = tbmatch.event_pb2.Event() 169 | event1.type = tbmatch.event_pb2.Event.E_LOBBY_MATCH_START 170 | event1.lobby_match_start.lobby_id = self.lobby_id 171 | event1.lobby_match_start.match_id = match_id 172 | event1.lobby_match_start.config.CopyFrom(game_config) 173 | event1.lobby_match_start.endpoint.CopyFrom(game_endpoint_config1) 174 | p1.user.SendEvent(event1) 175 | 176 | event2 = tbmatch.event_pb2.Event() 177 | event2.type = tbmatch.event_pb2.Event.E_LOBBY_MATCH_START 178 | event2.lobby_match_start.lobby_id = self.lobby_id 179 | event2.lobby_match_start.match_id = match_id 180 | event2.lobby_match_start.config.CopyFrom(game_config) 181 | event2.lobby_match_start.endpoint.CopyFrom(game_endpoint_config2) 182 | p2.user.SendEvent(event2) 183 | 184 | class Lobbies(object): 185 | def __init__(self): 186 | self.lobbies = {} 187 | 188 | def CreateLobby(self, name, owner): 189 | lobby = Lobby(name) 190 | self.lobbies[lobby.lobby_id] = lobby 191 | return lobby 192 | 193 | def GetLobby(self, lobby_id): 194 | return self.lobbies[lobby_id] 195 | 196 | def FindLobbyWithUser(self, user): 197 | for _, lobby in self.lobbies.iteritems(): 198 | if user.user_id in lobby.members: 199 | return lobby 200 | return lobby 201 | 202 | def FindLobbyWithCode(self, code): 203 | for _, lobby in self.lobbies.iteritems(): 204 | if lobby.join_code.lower() == code.lower(): 205 | return lobby 206 | 207 | def RemoveUserFromLobby(self, user): 208 | lobby = self.FindLobbyWithUser(user) 209 | if lobby: 210 | lobby.RemoveUser(user) 211 | 212 | # delete lobby if there are no users in it 213 | if lobby.GetNumberOfUsers() == 0: 214 | self.lobbies.pop(lobby.lobby_id, None) 215 | -------------------------------------------------------------------------------- /server/models/match.py: -------------------------------------------------------------------------------- 1 | """ 2 | match.py 3 | 4 | Match related events. 5 | """ 6 | 7 | import server 8 | import server.config 9 | import tbmatch.event_pb2 10 | import tbmatch.match_pb2 11 | import tbportal.portal_pb2 12 | 13 | def CreateGameSessionRequest(p1_character, p2_character): 14 | game_session = tbportal.portal_pb2.GameSessionRequest() 15 | p1_session = game_session.spec.add() 16 | p1_session.secret = server.GetNewSecret() 17 | p1_session.character.CopyFrom(p1_character) 18 | 19 | p2_session = game_session.spec.add() 20 | p2_session.secret = server.GetNewSecret() 21 | p2_session.character.CopyFrom(p2_character) 22 | 23 | return game_session 24 | 25 | #TODO Rename Gameplay options 26 | def CreateGameConfig(match_id, p1, p1_character, p2, p2_character): 27 | game_config = tbmatch.match_pb2.GameConfig() 28 | 29 | player1 = game_config.player.add() 30 | player1.user_id = p1.user_id 31 | player1.handle = p1.handle 32 | player1.character.CopyFrom(p1_character) 33 | 34 | player2 = game_config.player.add() 35 | player2.user_id = p2.user_id 36 | player2.handle = p2.handle 37 | player2.character.CopyFrom(p2_character) 38 | 39 | game_config.options.CopyFrom(tbmatch.match_pb2.GameOptions()) 40 | game_config.match_id = match_id 41 | 42 | return game_config 43 | 44 | def CreateGameEndpointConfig(slot, port, secret): 45 | game_endpoint_config = tbmatch.match_pb2.GameEndpointConfig() 46 | game_endpoint_config.slot = slot 47 | game_endpoint_config.server.host_name = server.config.hostname 48 | game_endpoint_config.server.port = port 49 | game_endpoint_config.secret = secret 50 | game_endpoint_config.ping_score_threshold = 200.0 51 | 52 | return game_endpoint_config 53 | 54 | def CreateWaitMatchProgressEvent( 55 | status, 56 | match_id = 0, 57 | game_config = None, 58 | game_endpoint_config = None): 59 | wait_match_progress = tbmatch.event_pb2.WaitMatchProgressEvent() 60 | 61 | wait_match_progress.status = status 62 | if match_id: 63 | wait_match_progress.match_id = match_id 64 | if game_config: 65 | wait_match_progress.config.CopyFrom(game_config) 66 | if game_endpoint_config: 67 | wait_match_progress.endpoint.CopyFrom(game_endpoint_config) 68 | wait_match_progress.users_waiting = 0 69 | 70 | return wait_match_progress 71 | -------------------------------------------------------------------------------- /server/models/matchmaker.py: -------------------------------------------------------------------------------- 1 | """ 2 | matchmaker.py 3 | 4 | Matchmaker model definition and registry. 5 | """ 6 | 7 | import logging 8 | import server 9 | import server.config 10 | import server.models.match 11 | import tbmatch.event_pb2 12 | import tornado.ioloop 13 | 14 | class QueueUser(object): 15 | def __init__(self, user, gameplay_options): 16 | self.user = user 17 | self.gameplay_options = gameplay_options 18 | 19 | class Matchmaker(object): 20 | def __init__(self): 21 | self.queue_users = [] 22 | self.poll_timer = tornado.ioloop.PeriodicCallback(lambda: self.Poll(), 5000) 23 | 24 | def JoinQueue(self, user, gameplay_options): 25 | queue_user = QueueUser(user, gameplay_options) 26 | logging.debug('queue user {0} joined queue'.format(user.user_id)) 27 | self.queue_users.insert(0, queue_user) 28 | 29 | event = tbmatch.event_pb2.Event() 30 | event.type = tbmatch.event_pb2.Event.E_WAIT_MATCH_PROGRESS 31 | status = tbmatch.event_pb2.WaitMatchProgressEvent.WAITING 32 | event.wait_match_progress.CopyFrom(server.models.match.CreateWaitMatchProgressEvent(status)) 33 | user.SendEvent(event) 34 | 35 | def LeaveQueue(self, user): 36 | logging.debug('queue user {0} leaved queue'.format(user.user_id)) 37 | for u in self.queue_users: 38 | if u.user.user_id == user.user_id: 39 | self.queue_users.remove(u) 40 | 41 | event = tbmatch.event_pb2.Event() 42 | event.type = tbmatch.event_pb2.Event.E_WAIT_MATCH_PROGRESS 43 | status = tbmatch.event_pb2.WaitMatchProgressEvent.CANCEL 44 | event.wait_match_progress.CopyFrom(server.models.match.CreateWaitMatchProgressEvent(status)) 45 | user.SendEvent(event) 46 | return 47 | 48 | def StartPolling(self): 49 | logging.debug("start matchmaker polling") 50 | self.poll_timer.start() 51 | 52 | def Poll(self): 53 | logging.debug('running matchmaker poll (%d users)', len(self.queue_users)) 54 | 55 | while len(self.queue_users) >= 2: 56 | p1 = self.queue_users.pop() 57 | p2 = self.queue_users.pop() 58 | 59 | # TODO put these 2 users in a temporary queue for re-queue in the case echo test failed 60 | 61 | # found a match 62 | match_id = server.GetNextUniqueId() 63 | 64 | # create intermediate proto structures 65 | game_config = server.models.match.CreateGameConfig(match_id, p1.user, p1.gameplay_options.character, p2.user, p2.gameplay_options.character) 66 | 67 | game_session = server.models.match.CreateGameSessionRequest(p1.gameplay_options.character, p2.gameplay_options.character) 68 | p1port, p2port = server.portal.StartGameSession(game_session, game_config, p1.user, p2.user) 69 | 70 | game_endpoint_config1 = server.models.match.CreateGameEndpointConfig(0, p1port, game_session.spec[0].secret) 71 | game_endpoint_config2 = server.models.match.CreateGameEndpointConfig(1, p2port, game_session.spec[1].secret) 72 | 73 | # create the final proto payload and send them as events to the matched users 74 | status = tbmatch.event_pb2.WaitMatchProgressEvent.MATCH 75 | wait_match_progress_event1 = server.models.match.CreateWaitMatchProgressEvent(status, match_id, game_config, game_endpoint_config1) 76 | event1 = tbmatch.event_pb2.Event() 77 | event1.type = tbmatch.event_pb2.Event.E_WAIT_MATCH_PROGRESS 78 | event1.wait_match_progress.CopyFrom(wait_match_progress_event1) 79 | p1.user.SendEvent(event1) 80 | 81 | event2 = tbmatch.event_pb2.Event() 82 | wait_match_progress_event2 = server.models.match.CreateWaitMatchProgressEvent(status, match_id, game_config, game_endpoint_config2) 83 | event2.type = tbmatch.event_pb2.Event.E_WAIT_MATCH_PROGRESS 84 | event2.wait_match_progress.CopyFrom(wait_match_progress_event2) 85 | p2.user.SendEvent(event2) 86 | -------------------------------------------------------------------------------- /server/models/users.py: -------------------------------------------------------------------------------- 1 | """ 2 | users.py 3 | 4 | User model definition and registry. 5 | """ 6 | 7 | import server 8 | import logging 9 | import tbmatch.event_pb2 10 | import tbmatch.match_pb2 11 | import tbrpc.tbrpc_pb2 12 | 13 | class User(object): 14 | def __init__(self): 15 | self.user_id = server.GetNextUniqueId() 16 | self.events = [] 17 | self.handle = 'User %03d' % server.GetNextUniqueId() 18 | self.given_name = 'Ana Itza' 19 | self.locale = 'en-US' 20 | self.get_event_handler = None 21 | self.prefs = tbmatch.match_pb2.PlayerPreferences() 22 | 23 | def Log(self, s): 24 | logging.debug('[user:%d %s] %s' % (self.user_id, self.handle, s)) 25 | 26 | def SetPlayerPreferences(self, prefs): 27 | self.prefs.CopyFrom(prefs) 28 | 29 | def SendEvent(self, e): 30 | # sometimes it's useful to broadcast one event to multiple parties. 31 | # make a copy of the event structure here so those have the proper 32 | # event id 33 | event = tbmatch.event_pb2.Event() 34 | event.CopyFrom(e) 35 | 36 | event.event_id = server.GetNextUniqueId() 37 | self.events.append(event) 38 | self.Log('appending event to list (id:{0})'.format(event.event_id)) 39 | 40 | handler = self.get_event_handler 41 | if handler: 42 | self.Log('calling pending get event handler to send events') 43 | self.SendPendingEvents(handler) 44 | self.get_event_handler = None 45 | 46 | def RemoveEvents(self, lastId): 47 | # drop all the events which have been ack'ed. 48 | self.Log('removing events up to %d' % lastId) 49 | while len(self.events) > 0 and lastId >= self.events[0].event_id: 50 | event = self.events.pop(0) 51 | 52 | def SendPendingEvents(self, handler): 53 | if len(self.events) == 0: 54 | self.Log('no events now. holding into event handler') 55 | self.get_event_handler = handler 56 | return 57 | 58 | response = tbmatch.event_pb2.GetEventResult() 59 | response.version = str(self.events[-1].event_id) 60 | for event in self.events: 61 | response.event.add().CopyFrom(event) 62 | self.Log('sending events {0}'.format(response)) 63 | 64 | result = tbrpc.tbrpc_pb2.Result() 65 | result.result = tbrpc.tbrpc_pb2.S_SUCCESS 66 | result.content = response.SerializeToString() 67 | 68 | handler.write(result.SerializeToString()) 69 | handler.finish() 70 | 71 | class Users(object): 72 | def __init__(self): 73 | self.users = {} 74 | 75 | def GetCurrentUser(self, handler): 76 | session_key = handler.get_cookie('session') 77 | logging.debug('session key is {0}'.format(session_key)) 78 | if session_key: 79 | return self.users[session_key] 80 | 81 | def CreateSession(self, handler, session_key): 82 | logging.debug('creating new user session with key {0}.'.format(session_key)) 83 | handler.set_cookie('session', session_key) 84 | self.users[session_key] = User() 85 | 86 | def DestroySession(self, handler): 87 | session_key = handler.get_cookie('session') 88 | if session_key: 89 | user = self.users[session_key] 90 | logging.debug('destroying session for {0} {1}.'.format(session_key, user.handle)) 91 | del self.users[session_key] 92 | -------------------------------------------------------------------------------- /server/rpc.py: -------------------------------------------------------------------------------- 1 | """ 2 | server.py 3 | 4 | The Rising Thunder application server layer. Simply holds the server configuration and 5 | makes it really easy to implement RPCs. Most of the heavy lifting is done by flask. 6 | """ 7 | 8 | import logging 9 | import google.protobuf.json_format 10 | 11 | log = logging.getLogger("tornado.general") 12 | log.debug('loading server') 13 | 14 | # Create the flask application. We do this here so scripts which implement RPCs can 15 | # easily get to the flask app via 16 | # 17 | # import server 18 | # server.app # this is the flask app! 19 | # 20 | 21 | _RPC_HANDLERS = {} 22 | def HandleRpc(route): 23 | """ 24 | Used by RPC implementors to handle an RPC. Typical usage looks like: 25 | 26 | import server 27 | 28 | @server.HandleRpc('Login') 29 | def Login(request, response): 30 | # do something here. 31 | 32 | The type of request and response are determined by the service proto definition. 33 | For example, for Login, the type of request is tbmatch.session_pb2.LoginRequest(). 34 | One easy way to find the type of your RPC is to just look for the app route in 35 | routes.py and read the code. 36 | 37 | RPC handlers should do their work, write to response, and return. If something 38 | goes wrong, raise an exception. 39 | """ 40 | def AddRoute(fn): 41 | log.debug('adding rpc handler to {0}'.format(route)) 42 | _RPC_HANDLERS[route] = fn 43 | return AddRoute 44 | 45 | def GetRouteHandler(route): 46 | """ 47 | Used by routes.py to lookup RPC handlers in the flask route handler. If you're 48 | implementing an RPC, you can just ignore this one. 49 | """ 50 | return _RPC_HANDLERS.get(route, None) 51 | 52 | def LogProto(reason, proto): 53 | json = google.protobuf.json_format.MessageToJson(proto) 54 | for line in json.split('\n'): 55 | logging.debug(reason + line) 56 | reason = '' 57 | 58 | -------------------------------------------------------------------------------- /server/services/__init__.py: -------------------------------------------------------------------------------- 1 | # server/services __init__.py file -------------------------------------------------------------------------------- /server/services/event_service.py: -------------------------------------------------------------------------------- 1 | import server.rpc 2 | import tbmatch.event_pb2 3 | import tornado.web 4 | import logging 5 | 6 | class GetEventHandler(tornado.web.RequestHandler): 7 | @tornado.web.asynchronous 8 | def post(self): 9 | request = tbmatch.event_pb2.GetEventRequest() 10 | request.ParseFromString(self.request.body) 11 | logging.debug('received GetEvent {0}'.format(str(request))) 12 | 13 | user = server.users.GetCurrentUser(self) 14 | if request.version: 15 | user.RemoveEvents(int(request.version)) 16 | user.SendPendingEvents(self) 17 | 18 | @server.rpc.HandleRpc('EventPing') 19 | def PingTest(request, response, handler): 20 | server.users.GetCurrentUser(handler) 21 | -------------------------------------------------------------------------------- /server/services/lobby_service.py: -------------------------------------------------------------------------------- 1 | import server 2 | import server.rpc 3 | import server.config 4 | import tbmatch.event_pb2 5 | import uuid 6 | 7 | @server.rpc.HandleRpc('CreateLobby') 8 | def CreateLobby(request, response, handler): 9 | """ 10 | Create a new lobby and join it, with the creator as owner. 11 | Fails if user is already in a lobby. 12 | Success confirmed by LobbyJoin event. 13 | """ 14 | user = server.users.GetCurrentUser(handler) 15 | 16 | # get the name of the lobby. the ui doesn't care, but lets shove 17 | # this in there anyway. 18 | name = request.name and str(uuid.uuid4()) 19 | lobby = server.lobbies.CreateLobby(name, user) 20 | 21 | lobby.AddUser(user) 22 | lobby.SetOwner(user) 23 | 24 | @server.rpc.HandleRpc('LobbySetReady') 25 | def LobbySetReady(request, response, handler): 26 | """ 27 | Set the ready state for the current user. 28 | """ 29 | user = server.users.GetCurrentUser(handler) 30 | 31 | lobby = server.lobbies.FindLobbyWithUser(user) 32 | if lobby: 33 | lobby.SetUserReady(user, request.ready, request.character) 34 | 35 | # check to see if both players are ready, if so start the match 36 | lobby.StartMatchIfReady() 37 | 38 | @server.rpc.HandleRpc('GetLobbyJoinCode') 39 | def GetLobbyJoinCode(request, response, handler): 40 | lobby = server.lobbies.GetLobby(request.lobby_id) 41 | response.join_code = lobby.join_code 42 | 43 | @server.rpc.HandleRpc('JoinLobbyByCode') 44 | def JoinLobbyByCode(request, response, handler): 45 | """ 46 | Join an existing lobby via a string code inputted by the user. 47 | """ 48 | 49 | user = server.users.GetCurrentUser(handler) 50 | 51 | code = request.code 52 | lobby = server.lobbies.FindLobbyWithCode(code) 53 | if lobby: 54 | lobby.AddUser(user) 55 | 56 | @server.rpc.HandleRpc('LeaveLobby') 57 | def LeaveLobby(request, response, handler): 58 | """ 59 | Leave an existing lobby. 60 | """ 61 | 62 | user = server.users.GetCurrentUser(handler) 63 | server.lobbies.RemoveUserFromLobby(user) 64 | 65 | -------------------------------------------------------------------------------- /server/services/match_service.py: -------------------------------------------------------------------------------- 1 | import uuid 2 | import server 3 | import server.rpc 4 | import server.config 5 | import tbportal.portal_pb2 6 | 7 | @server.rpc.HandleRpc('PingTest') 8 | def PingTest(request, response, handler): 9 | user = server.users.GetCurrentUser(handler) 10 | 11 | client_spec = tbportal.portal_pb2.ClientSpec() 12 | client_spec.secret = server.GetNewSecret() 13 | 14 | ping_test = server.portal.StartPingTest(user, client_spec) 15 | response.config.server.host_name = server.config.hostname 16 | response.config.server.port = ping_test.port 17 | response.config.secret = client_spec.secret 18 | 19 | @server.rpc.HandleRpc('GetGameProfile') 20 | def GetGameProfile(request, response, handler): 21 | user = server.users.GetCurrentUser(handler) 22 | response.account_id = user.user_id 23 | response.handle = user.handle 24 | response.given_name = user.given_name 25 | response.locale = user.locale 26 | response.feature_set.CopyFrom(server.config.featureSet) 27 | 28 | @server.rpc.HandleRpc('GetRecentGames') 29 | def GetRecentGames(request, response, handler): 30 | pass 31 | 32 | @server.rpc.HandleRpc('UpdatePlayerPreferences') 33 | def UpdatePlayerPreferences(request, response, handler): 34 | user = server.users.GetCurrentUser(handler) 35 | user.SetPlayerPreferences(request.updated_prefs) 36 | 37 | @server.rpc.HandleRpc('GetMatch') 38 | def GetMatch(request, response, handler): 39 | user = server.users.GetCurrentUser(handler) 40 | gameplay_options = request 41 | 42 | server.matchmaker.JoinQueue(user, gameplay_options) 43 | 44 | @server.rpc.HandleRpc('CancelGetMatch') 45 | def CancelGetMatch(request, response, handler): 46 | user = server.users.GetCurrentUser(handler) 47 | server.matchmaker.LeaveQueue(user) 48 | -------------------------------------------------------------------------------- /server/services/session_service.py: -------------------------------------------------------------------------------- 1 | import server.rpc 2 | import time 3 | import uuid 4 | 5 | @server.rpc.HandleRpc('Login') 6 | def Login(request, response, handler): 7 | pass 8 | 9 | @server.rpc.HandleRpc('Logout') 10 | def Logout(request, response, handler): 11 | server.users.DestroySession(handler) 12 | 13 | @server.rpc.HandleRpc('GetGameSessionTicket') 14 | def GetGameSessionTicket(request, response, handler): 15 | response.game = request.game 16 | response.nonce = str(uuid.uuid4()) 17 | 18 | @server.rpc.HandleRpc('RedeemGameSessionTicket') 19 | def RedeemGameSessionTicket(request, response, handler): 20 | # TODO: Remove this sleep statement and instead implement an asynchronous event handler in the same style as GetEvent 21 | time.sleep(server.config.game_session_ticket_wait_interval_ms) 22 | 23 | session_key = request.nonce 24 | server.users.CreateSession(handler, session_key) 25 | -------------------------------------------------------------------------------- /tbadmin/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RiotGames/rtce-server/5d1b9222b7f90db94e1f424986253d6dff103484/tbadmin/__init__.py -------------------------------------------------------------------------------- /tbadmin/shop_pb2.py: -------------------------------------------------------------------------------- 1 | # Generated by the protocol buffer compiler. DO NOT EDIT! 2 | # source: tbadmin/shop.proto 3 | 4 | import sys 5 | _b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) 6 | from google.protobuf import descriptor as _descriptor 7 | from google.protobuf import message as _message 8 | from google.protobuf import reflection as _reflection 9 | from google.protobuf import symbol_database as _symbol_database 10 | from google.protobuf import descriptor_pb2 11 | # @@protoc_insertion_point(imports) 12 | 13 | _sym_db = _symbol_database.Default() 14 | 15 | 16 | from tbrpc import tbrpc_pb2 as tbrpc_dot_tbrpc__pb2 17 | from tbmatch import shop_pb2 as tbmatch_dot_shop__pb2 18 | from tbmatch import query_pb2 as tbmatch_dot_query__pb2 19 | 20 | 21 | DESCRIPTOR = _descriptor.FileDescriptor( 22 | name='tbadmin/shop.proto', 23 | package='tbadmin', 24 | syntax='proto2', 25 | serialized_pb=_b('\n\x12tbadmin/shop.proto\x12\x07tbadmin\x1a\x11tbrpc/tbrpc.proto\x1a\x12tbmatch/shop.proto\x1a\x13tbmatch/query.proto\"\xfd\x02\n\rOrderCriteria\x12\x12\n\naccount_id\x18\x01 \x01(\x03\x12*\n\x0corder_status\x18\x02 \x01(\x0e\x32\x14.tbmatch.OrderStatus\x12\'\n\x0bmodify_time\x18\x03 \x01(\x0b\x32\x12.tbmatch.TimeRange\x12&\n\x10min_total_amount\x18\x05 \x01(\x0b\x32\x0c.tbrpc.Money\x12&\n\x10max_total_amount\x18\x06 \x01(\x0b\x32\x0c.tbrpc.Money\x12\x1b\n\x13use_account_balance\x18\n \x01(\x08\x12,\n\x0e\x62ill_last_name\x18\x0b \x01(\x0b\x32\x14.tbmatch.StringMatch\x12\'\n\tbill_city\x18\x0c \x01(\x0b\x32\x14.tbmatch.StringMatch\x12)\n\x0b\x62ill_region\x18\r \x01(\x0b\x32\x14.tbmatch.StringMatch\x12\x14\n\x0c\x62ill_country\x18\x0e \x01(\t\"\x8c\x01\n\x1bSearchPurchaseOrdersRequest\x12(\n\x08\x63riteria\x18\x01 \x01(\x0b\x32\x16.tbadmin.OrderCriteria\x12 \n\x04sort\x18\x02 \x01(\x0b\x32\x12.tbmatch.OrderSort\x12\x0e\n\x06offset\x18\x05 \x01(\x05\x12\x11\n\x05limit\x18\x06 \x01(\x05:\x02\x31\x30\"Y\n\x1aSearchPurchaseOrdersResult\x12&\n\x06orders\x18\x01 \x03(\x0b\x32\x16.tbmatch.PurchaseOrder\x12\x13\n\x0b\x65nd_of_data\x18\x02 \x01(\x08\"0\n\x18SyncPurchaseOrderRequest\x12\x14\n\x0corder_number\x18\x01 \x01(\x03\"0\n\x18VoidPurchaseOrderRequest\x12\x14\n\x0corder_number\x18\x01 \x01(\x03\"2\n\x1aRefundPurchaseOrderRequest\x12\x14\n\x0corder_number\x18\x01 \x01(\x03\x32\xe3\x02\n\x10ShopAdminService\x12g\n\x14SearchPurchaseOrders\x12$.tbadmin.SearchPurchaseOrdersRequest\x1a#.tbadmin.SearchPurchaseOrdersResult\"\x04\xc8\xf3\x18\x16\x12J\n\x11SyncPurchaseOrder\x12!.tbadmin.SyncPurchaseOrderRequest\x1a\x0c.tbrpc.Empty\"\x04\xc8\xf3\x18\x16\x12J\n\x11VoidPurchaseOrder\x12!.tbadmin.VoidPurchaseOrderRequest\x1a\x0c.tbrpc.Empty\"\x04\xc8\xf3\x18\x17\x12N\n\x13RefundPurchaseOrder\x12#.tbadmin.RefundPurchaseOrderRequest\x1a\x0c.tbrpc.Empty\"\x04\xc8\xf3\x18\x17') 26 | , 27 | dependencies=[tbrpc_dot_tbrpc__pb2.DESCRIPTOR,tbmatch_dot_shop__pb2.DESCRIPTOR,tbmatch_dot_query__pb2.DESCRIPTOR,]) 28 | 29 | 30 | 31 | 32 | _ORDERCRITERIA = _descriptor.Descriptor( 33 | name='OrderCriteria', 34 | full_name='tbadmin.OrderCriteria', 35 | filename=None, 36 | file=DESCRIPTOR, 37 | containing_type=None, 38 | fields=[ 39 | _descriptor.FieldDescriptor( 40 | name='account_id', full_name='tbadmin.OrderCriteria.account_id', index=0, 41 | number=1, type=3, cpp_type=2, label=1, 42 | has_default_value=False, default_value=0, 43 | message_type=None, enum_type=None, containing_type=None, 44 | is_extension=False, extension_scope=None, 45 | options=None), 46 | _descriptor.FieldDescriptor( 47 | name='order_status', full_name='tbadmin.OrderCriteria.order_status', index=1, 48 | number=2, type=14, cpp_type=8, label=1, 49 | has_default_value=False, default_value=1, 50 | message_type=None, enum_type=None, containing_type=None, 51 | is_extension=False, extension_scope=None, 52 | options=None), 53 | _descriptor.FieldDescriptor( 54 | name='modify_time', full_name='tbadmin.OrderCriteria.modify_time', index=2, 55 | number=3, type=11, cpp_type=10, label=1, 56 | has_default_value=False, default_value=None, 57 | message_type=None, enum_type=None, containing_type=None, 58 | is_extension=False, extension_scope=None, 59 | options=None), 60 | _descriptor.FieldDescriptor( 61 | name='min_total_amount', full_name='tbadmin.OrderCriteria.min_total_amount', index=3, 62 | number=5, type=11, cpp_type=10, label=1, 63 | has_default_value=False, default_value=None, 64 | message_type=None, enum_type=None, containing_type=None, 65 | is_extension=False, extension_scope=None, 66 | options=None), 67 | _descriptor.FieldDescriptor( 68 | name='max_total_amount', full_name='tbadmin.OrderCriteria.max_total_amount', index=4, 69 | number=6, type=11, cpp_type=10, label=1, 70 | has_default_value=False, default_value=None, 71 | message_type=None, enum_type=None, containing_type=None, 72 | is_extension=False, extension_scope=None, 73 | options=None), 74 | _descriptor.FieldDescriptor( 75 | name='use_account_balance', full_name='tbadmin.OrderCriteria.use_account_balance', index=5, 76 | number=10, type=8, cpp_type=7, label=1, 77 | has_default_value=False, default_value=False, 78 | message_type=None, enum_type=None, containing_type=None, 79 | is_extension=False, extension_scope=None, 80 | options=None), 81 | _descriptor.FieldDescriptor( 82 | name='bill_last_name', full_name='tbadmin.OrderCriteria.bill_last_name', index=6, 83 | number=11, type=11, cpp_type=10, label=1, 84 | has_default_value=False, default_value=None, 85 | message_type=None, enum_type=None, containing_type=None, 86 | is_extension=False, extension_scope=None, 87 | options=None), 88 | _descriptor.FieldDescriptor( 89 | name='bill_city', full_name='tbadmin.OrderCriteria.bill_city', index=7, 90 | number=12, type=11, cpp_type=10, label=1, 91 | has_default_value=False, default_value=None, 92 | message_type=None, enum_type=None, containing_type=None, 93 | is_extension=False, extension_scope=None, 94 | options=None), 95 | _descriptor.FieldDescriptor( 96 | name='bill_region', full_name='tbadmin.OrderCriteria.bill_region', index=8, 97 | number=13, type=11, cpp_type=10, label=1, 98 | has_default_value=False, default_value=None, 99 | message_type=None, enum_type=None, containing_type=None, 100 | is_extension=False, extension_scope=None, 101 | options=None), 102 | _descriptor.FieldDescriptor( 103 | name='bill_country', full_name='tbadmin.OrderCriteria.bill_country', index=9, 104 | number=14, type=9, cpp_type=9, label=1, 105 | has_default_value=False, default_value=_b("").decode('utf-8'), 106 | message_type=None, enum_type=None, containing_type=None, 107 | is_extension=False, extension_scope=None, 108 | options=None), 109 | ], 110 | extensions=[ 111 | ], 112 | nested_types=[], 113 | enum_types=[ 114 | ], 115 | options=None, 116 | is_extendable=False, 117 | syntax='proto2', 118 | extension_ranges=[], 119 | oneofs=[ 120 | ], 121 | serialized_start=92, 122 | serialized_end=473, 123 | ) 124 | 125 | 126 | _SEARCHPURCHASEORDERSREQUEST = _descriptor.Descriptor( 127 | name='SearchPurchaseOrdersRequest', 128 | full_name='tbadmin.SearchPurchaseOrdersRequest', 129 | filename=None, 130 | file=DESCRIPTOR, 131 | containing_type=None, 132 | fields=[ 133 | _descriptor.FieldDescriptor( 134 | name='criteria', full_name='tbadmin.SearchPurchaseOrdersRequest.criteria', index=0, 135 | number=1, type=11, cpp_type=10, label=1, 136 | has_default_value=False, default_value=None, 137 | message_type=None, enum_type=None, containing_type=None, 138 | is_extension=False, extension_scope=None, 139 | options=None), 140 | _descriptor.FieldDescriptor( 141 | name='sort', full_name='tbadmin.SearchPurchaseOrdersRequest.sort', index=1, 142 | number=2, type=11, cpp_type=10, label=1, 143 | has_default_value=False, default_value=None, 144 | message_type=None, enum_type=None, containing_type=None, 145 | is_extension=False, extension_scope=None, 146 | options=None), 147 | _descriptor.FieldDescriptor( 148 | name='offset', full_name='tbadmin.SearchPurchaseOrdersRequest.offset', index=2, 149 | number=5, type=5, cpp_type=1, label=1, 150 | has_default_value=False, default_value=0, 151 | message_type=None, enum_type=None, containing_type=None, 152 | is_extension=False, extension_scope=None, 153 | options=None), 154 | _descriptor.FieldDescriptor( 155 | name='limit', full_name='tbadmin.SearchPurchaseOrdersRequest.limit', index=3, 156 | number=6, type=5, cpp_type=1, label=1, 157 | has_default_value=True, default_value=10, 158 | message_type=None, enum_type=None, containing_type=None, 159 | is_extension=False, extension_scope=None, 160 | options=None), 161 | ], 162 | extensions=[ 163 | ], 164 | nested_types=[], 165 | enum_types=[ 166 | ], 167 | options=None, 168 | is_extendable=False, 169 | syntax='proto2', 170 | extension_ranges=[], 171 | oneofs=[ 172 | ], 173 | serialized_start=476, 174 | serialized_end=616, 175 | ) 176 | 177 | 178 | _SEARCHPURCHASEORDERSRESULT = _descriptor.Descriptor( 179 | name='SearchPurchaseOrdersResult', 180 | full_name='tbadmin.SearchPurchaseOrdersResult', 181 | filename=None, 182 | file=DESCRIPTOR, 183 | containing_type=None, 184 | fields=[ 185 | _descriptor.FieldDescriptor( 186 | name='orders', full_name='tbadmin.SearchPurchaseOrdersResult.orders', index=0, 187 | number=1, type=11, cpp_type=10, label=3, 188 | has_default_value=False, default_value=[], 189 | message_type=None, enum_type=None, containing_type=None, 190 | is_extension=False, extension_scope=None, 191 | options=None), 192 | _descriptor.FieldDescriptor( 193 | name='end_of_data', full_name='tbadmin.SearchPurchaseOrdersResult.end_of_data', index=1, 194 | number=2, type=8, cpp_type=7, label=1, 195 | has_default_value=False, default_value=False, 196 | message_type=None, enum_type=None, containing_type=None, 197 | is_extension=False, extension_scope=None, 198 | options=None), 199 | ], 200 | extensions=[ 201 | ], 202 | nested_types=[], 203 | enum_types=[ 204 | ], 205 | options=None, 206 | is_extendable=False, 207 | syntax='proto2', 208 | extension_ranges=[], 209 | oneofs=[ 210 | ], 211 | serialized_start=618, 212 | serialized_end=707, 213 | ) 214 | 215 | 216 | _SYNCPURCHASEORDERREQUEST = _descriptor.Descriptor( 217 | name='SyncPurchaseOrderRequest', 218 | full_name='tbadmin.SyncPurchaseOrderRequest', 219 | filename=None, 220 | file=DESCRIPTOR, 221 | containing_type=None, 222 | fields=[ 223 | _descriptor.FieldDescriptor( 224 | name='order_number', full_name='tbadmin.SyncPurchaseOrderRequest.order_number', index=0, 225 | number=1, type=3, cpp_type=2, label=1, 226 | has_default_value=False, default_value=0, 227 | message_type=None, enum_type=None, containing_type=None, 228 | is_extension=False, extension_scope=None, 229 | options=None), 230 | ], 231 | extensions=[ 232 | ], 233 | nested_types=[], 234 | enum_types=[ 235 | ], 236 | options=None, 237 | is_extendable=False, 238 | syntax='proto2', 239 | extension_ranges=[], 240 | oneofs=[ 241 | ], 242 | serialized_start=709, 243 | serialized_end=757, 244 | ) 245 | 246 | 247 | _VOIDPURCHASEORDERREQUEST = _descriptor.Descriptor( 248 | name='VoidPurchaseOrderRequest', 249 | full_name='tbadmin.VoidPurchaseOrderRequest', 250 | filename=None, 251 | file=DESCRIPTOR, 252 | containing_type=None, 253 | fields=[ 254 | _descriptor.FieldDescriptor( 255 | name='order_number', full_name='tbadmin.VoidPurchaseOrderRequest.order_number', index=0, 256 | number=1, type=3, cpp_type=2, label=1, 257 | has_default_value=False, default_value=0, 258 | message_type=None, enum_type=None, containing_type=None, 259 | is_extension=False, extension_scope=None, 260 | options=None), 261 | ], 262 | extensions=[ 263 | ], 264 | nested_types=[], 265 | enum_types=[ 266 | ], 267 | options=None, 268 | is_extendable=False, 269 | syntax='proto2', 270 | extension_ranges=[], 271 | oneofs=[ 272 | ], 273 | serialized_start=759, 274 | serialized_end=807, 275 | ) 276 | 277 | 278 | _REFUNDPURCHASEORDERREQUEST = _descriptor.Descriptor( 279 | name='RefundPurchaseOrderRequest', 280 | full_name='tbadmin.RefundPurchaseOrderRequest', 281 | filename=None, 282 | file=DESCRIPTOR, 283 | containing_type=None, 284 | fields=[ 285 | _descriptor.FieldDescriptor( 286 | name='order_number', full_name='tbadmin.RefundPurchaseOrderRequest.order_number', index=0, 287 | number=1, type=3, cpp_type=2, label=1, 288 | has_default_value=False, default_value=0, 289 | message_type=None, enum_type=None, containing_type=None, 290 | is_extension=False, extension_scope=None, 291 | options=None), 292 | ], 293 | extensions=[ 294 | ], 295 | nested_types=[], 296 | enum_types=[ 297 | ], 298 | options=None, 299 | is_extendable=False, 300 | syntax='proto2', 301 | extension_ranges=[], 302 | oneofs=[ 303 | ], 304 | serialized_start=809, 305 | serialized_end=859, 306 | ) 307 | 308 | _ORDERCRITERIA.fields_by_name['order_status'].enum_type = tbmatch_dot_shop__pb2._ORDERSTATUS 309 | _ORDERCRITERIA.fields_by_name['modify_time'].message_type = tbmatch_dot_query__pb2._TIMERANGE 310 | _ORDERCRITERIA.fields_by_name['min_total_amount'].message_type = tbrpc_dot_tbrpc__pb2._MONEY 311 | _ORDERCRITERIA.fields_by_name['max_total_amount'].message_type = tbrpc_dot_tbrpc__pb2._MONEY 312 | _ORDERCRITERIA.fields_by_name['bill_last_name'].message_type = tbmatch_dot_query__pb2._STRINGMATCH 313 | _ORDERCRITERIA.fields_by_name['bill_city'].message_type = tbmatch_dot_query__pb2._STRINGMATCH 314 | _ORDERCRITERIA.fields_by_name['bill_region'].message_type = tbmatch_dot_query__pb2._STRINGMATCH 315 | _SEARCHPURCHASEORDERSREQUEST.fields_by_name['criteria'].message_type = _ORDERCRITERIA 316 | _SEARCHPURCHASEORDERSREQUEST.fields_by_name['sort'].message_type = tbmatch_dot_shop__pb2._ORDERSORT 317 | _SEARCHPURCHASEORDERSRESULT.fields_by_name['orders'].message_type = tbmatch_dot_shop__pb2._PURCHASEORDER 318 | DESCRIPTOR.message_types_by_name['OrderCriteria'] = _ORDERCRITERIA 319 | DESCRIPTOR.message_types_by_name['SearchPurchaseOrdersRequest'] = _SEARCHPURCHASEORDERSREQUEST 320 | DESCRIPTOR.message_types_by_name['SearchPurchaseOrdersResult'] = _SEARCHPURCHASEORDERSRESULT 321 | DESCRIPTOR.message_types_by_name['SyncPurchaseOrderRequest'] = _SYNCPURCHASEORDERREQUEST 322 | DESCRIPTOR.message_types_by_name['VoidPurchaseOrderRequest'] = _VOIDPURCHASEORDERREQUEST 323 | DESCRIPTOR.message_types_by_name['RefundPurchaseOrderRequest'] = _REFUNDPURCHASEORDERREQUEST 324 | _sym_db.RegisterFileDescriptor(DESCRIPTOR) 325 | 326 | OrderCriteria = _reflection.GeneratedProtocolMessageType('OrderCriteria', (_message.Message,), dict( 327 | DESCRIPTOR = _ORDERCRITERIA, 328 | __module__ = 'tbadmin.shop_pb2' 329 | # @@protoc_insertion_point(class_scope:tbadmin.OrderCriteria) 330 | )) 331 | _sym_db.RegisterMessage(OrderCriteria) 332 | 333 | SearchPurchaseOrdersRequest = _reflection.GeneratedProtocolMessageType('SearchPurchaseOrdersRequest', (_message.Message,), dict( 334 | DESCRIPTOR = _SEARCHPURCHASEORDERSREQUEST, 335 | __module__ = 'tbadmin.shop_pb2' 336 | # @@protoc_insertion_point(class_scope:tbadmin.SearchPurchaseOrdersRequest) 337 | )) 338 | _sym_db.RegisterMessage(SearchPurchaseOrdersRequest) 339 | 340 | SearchPurchaseOrdersResult = _reflection.GeneratedProtocolMessageType('SearchPurchaseOrdersResult', (_message.Message,), dict( 341 | DESCRIPTOR = _SEARCHPURCHASEORDERSRESULT, 342 | __module__ = 'tbadmin.shop_pb2' 343 | # @@protoc_insertion_point(class_scope:tbadmin.SearchPurchaseOrdersResult) 344 | )) 345 | _sym_db.RegisterMessage(SearchPurchaseOrdersResult) 346 | 347 | SyncPurchaseOrderRequest = _reflection.GeneratedProtocolMessageType('SyncPurchaseOrderRequest', (_message.Message,), dict( 348 | DESCRIPTOR = _SYNCPURCHASEORDERREQUEST, 349 | __module__ = 'tbadmin.shop_pb2' 350 | # @@protoc_insertion_point(class_scope:tbadmin.SyncPurchaseOrderRequest) 351 | )) 352 | _sym_db.RegisterMessage(SyncPurchaseOrderRequest) 353 | 354 | VoidPurchaseOrderRequest = _reflection.GeneratedProtocolMessageType('VoidPurchaseOrderRequest', (_message.Message,), dict( 355 | DESCRIPTOR = _VOIDPURCHASEORDERREQUEST, 356 | __module__ = 'tbadmin.shop_pb2' 357 | # @@protoc_insertion_point(class_scope:tbadmin.VoidPurchaseOrderRequest) 358 | )) 359 | _sym_db.RegisterMessage(VoidPurchaseOrderRequest) 360 | 361 | RefundPurchaseOrderRequest = _reflection.GeneratedProtocolMessageType('RefundPurchaseOrderRequest', (_message.Message,), dict( 362 | DESCRIPTOR = _REFUNDPURCHASEORDERREQUEST, 363 | __module__ = 'tbadmin.shop_pb2' 364 | # @@protoc_insertion_point(class_scope:tbadmin.RefundPurchaseOrderRequest) 365 | )) 366 | _sym_db.RegisterMessage(RefundPurchaseOrderRequest) 367 | 368 | 369 | 370 | _SHOPADMINSERVICE = _descriptor.ServiceDescriptor( 371 | name='ShopAdminService', 372 | full_name='tbadmin.ShopAdminService', 373 | file=DESCRIPTOR, 374 | index=0, 375 | options=None, 376 | serialized_start=862, 377 | serialized_end=1217, 378 | methods=[ 379 | _descriptor.MethodDescriptor( 380 | name='SearchPurchaseOrders', 381 | full_name='tbadmin.ShopAdminService.SearchPurchaseOrders', 382 | index=0, 383 | containing_service=None, 384 | input_type=_SEARCHPURCHASEORDERSREQUEST, 385 | output_type=_SEARCHPURCHASEORDERSRESULT, 386 | options=_descriptor._ParseOptions(descriptor_pb2.MethodOptions(), _b('\310\363\030\026')), 387 | ), 388 | _descriptor.MethodDescriptor( 389 | name='SyncPurchaseOrder', 390 | full_name='tbadmin.ShopAdminService.SyncPurchaseOrder', 391 | index=1, 392 | containing_service=None, 393 | input_type=_SYNCPURCHASEORDERREQUEST, 394 | output_type=tbrpc_dot_tbrpc__pb2._EMPTY, 395 | options=_descriptor._ParseOptions(descriptor_pb2.MethodOptions(), _b('\310\363\030\026')), 396 | ), 397 | _descriptor.MethodDescriptor( 398 | name='VoidPurchaseOrder', 399 | full_name='tbadmin.ShopAdminService.VoidPurchaseOrder', 400 | index=2, 401 | containing_service=None, 402 | input_type=_VOIDPURCHASEORDERREQUEST, 403 | output_type=tbrpc_dot_tbrpc__pb2._EMPTY, 404 | options=_descriptor._ParseOptions(descriptor_pb2.MethodOptions(), _b('\310\363\030\027')), 405 | ), 406 | _descriptor.MethodDescriptor( 407 | name='RefundPurchaseOrder', 408 | full_name='tbadmin.ShopAdminService.RefundPurchaseOrder', 409 | index=3, 410 | containing_service=None, 411 | input_type=_REFUNDPURCHASEORDERREQUEST, 412 | output_type=tbrpc_dot_tbrpc__pb2._EMPTY, 413 | options=_descriptor._ParseOptions(descriptor_pb2.MethodOptions(), _b('\310\363\030\027')), 414 | ), 415 | ]) 416 | _sym_db.RegisterServiceDescriptor(_SHOPADMINSERVICE) 417 | 418 | DESCRIPTOR.services_by_name['ShopAdminService'] = _SHOPADMINSERVICE 419 | 420 | # @@protoc_insertion_point(module_scope) 421 | -------------------------------------------------------------------------------- /tbmatch/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RiotGames/rtce-server/5d1b9222b7f90db94e1f424986253d6dff103484/tbmatch/__init__.py -------------------------------------------------------------------------------- /tbmatch/crash_pb2.py: -------------------------------------------------------------------------------- 1 | # Generated by the protocol buffer compiler. DO NOT EDIT! 2 | # source: tbmatch/crash.proto 3 | 4 | import sys 5 | _b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) 6 | from google.protobuf import descriptor as _descriptor 7 | from google.protobuf import message as _message 8 | from google.protobuf import reflection as _reflection 9 | from google.protobuf import symbol_database as _symbol_database 10 | from google.protobuf import descriptor_pb2 11 | # @@protoc_insertion_point(imports) 12 | 13 | _sym_db = _symbol_database.Default() 14 | 15 | 16 | from tbrpc import tbrpc_pb2 as tbrpc_dot_tbrpc__pb2 17 | 18 | 19 | DESCRIPTOR = _descriptor.FileDescriptor( 20 | name='tbmatch/crash.proto', 21 | package='tbmatch', 22 | syntax='proto2', 23 | serialized_pb=_b('\n\x13tbmatch/crash.proto\x12\x07tbmatch\x1a\x11tbrpc/tbrpc.proto\"a\n\x12\x43rashReportRequest\x12\x10\n\x08\x61pp_name\x18\x01 \x01(\t\x12\x15\n\rbuild_version\x18\x02 \x01(\t\x12\x12\n\nmachine_id\x18\x03 \x01(\t\x12\x0e\n\x06\x62undle\x18\x04 \x01(\x0c\x32T\n\x12\x43rashReportService\x12>\n\x0b\x43rashReport\x12\x1b.tbmatch.CrashReportRequest\x1a\x0c.tbrpc.Empty\"\x04\xc8\xf3\x18\x00') 24 | , 25 | dependencies=[tbrpc_dot_tbrpc__pb2.DESCRIPTOR,]) 26 | 27 | 28 | 29 | 30 | _CRASHREPORTREQUEST = _descriptor.Descriptor( 31 | name='CrashReportRequest', 32 | full_name='tbmatch.CrashReportRequest', 33 | filename=None, 34 | file=DESCRIPTOR, 35 | containing_type=None, 36 | fields=[ 37 | _descriptor.FieldDescriptor( 38 | name='app_name', full_name='tbmatch.CrashReportRequest.app_name', index=0, 39 | number=1, type=9, cpp_type=9, label=1, 40 | has_default_value=False, default_value=_b("").decode('utf-8'), 41 | message_type=None, enum_type=None, containing_type=None, 42 | is_extension=False, extension_scope=None, 43 | options=None), 44 | _descriptor.FieldDescriptor( 45 | name='build_version', full_name='tbmatch.CrashReportRequest.build_version', index=1, 46 | number=2, type=9, cpp_type=9, label=1, 47 | has_default_value=False, default_value=_b("").decode('utf-8'), 48 | message_type=None, enum_type=None, containing_type=None, 49 | is_extension=False, extension_scope=None, 50 | options=None), 51 | _descriptor.FieldDescriptor( 52 | name='machine_id', full_name='tbmatch.CrashReportRequest.machine_id', index=2, 53 | number=3, type=9, cpp_type=9, label=1, 54 | has_default_value=False, default_value=_b("").decode('utf-8'), 55 | message_type=None, enum_type=None, containing_type=None, 56 | is_extension=False, extension_scope=None, 57 | options=None), 58 | _descriptor.FieldDescriptor( 59 | name='bundle', full_name='tbmatch.CrashReportRequest.bundle', index=3, 60 | number=4, type=12, cpp_type=9, label=1, 61 | has_default_value=False, default_value=_b(""), 62 | message_type=None, enum_type=None, containing_type=None, 63 | is_extension=False, extension_scope=None, 64 | options=None), 65 | ], 66 | extensions=[ 67 | ], 68 | nested_types=[], 69 | enum_types=[ 70 | ], 71 | options=None, 72 | is_extendable=False, 73 | syntax='proto2', 74 | extension_ranges=[], 75 | oneofs=[ 76 | ], 77 | serialized_start=51, 78 | serialized_end=148, 79 | ) 80 | 81 | DESCRIPTOR.message_types_by_name['CrashReportRequest'] = _CRASHREPORTREQUEST 82 | _sym_db.RegisterFileDescriptor(DESCRIPTOR) 83 | 84 | CrashReportRequest = _reflection.GeneratedProtocolMessageType('CrashReportRequest', (_message.Message,), dict( 85 | DESCRIPTOR = _CRASHREPORTREQUEST, 86 | __module__ = 'tbmatch.crash_pb2' 87 | # @@protoc_insertion_point(class_scope:tbmatch.CrashReportRequest) 88 | )) 89 | _sym_db.RegisterMessage(CrashReportRequest) 90 | 91 | 92 | 93 | _CRASHREPORTSERVICE = _descriptor.ServiceDescriptor( 94 | name='CrashReportService', 95 | full_name='tbmatch.CrashReportService', 96 | file=DESCRIPTOR, 97 | index=0, 98 | options=None, 99 | serialized_start=150, 100 | serialized_end=234, 101 | methods=[ 102 | _descriptor.MethodDescriptor( 103 | name='CrashReport', 104 | full_name='tbmatch.CrashReportService.CrashReport', 105 | index=0, 106 | containing_service=None, 107 | input_type=_CRASHREPORTREQUEST, 108 | output_type=tbrpc_dot_tbrpc__pb2._EMPTY, 109 | options=_descriptor._ParseOptions(descriptor_pb2.MethodOptions(), _b('\310\363\030\000')), 110 | ), 111 | ]) 112 | _sym_db.RegisterServiceDescriptor(_CRASHREPORTSERVICE) 113 | 114 | DESCRIPTOR.services_by_name['CrashReportService'] = _CRASHREPORTSERVICE 115 | 116 | # @@protoc_insertion_point(module_scope) 117 | -------------------------------------------------------------------------------- /tbmatch/log_pb2.py: -------------------------------------------------------------------------------- 1 | # Generated by the protocol buffer compiler. DO NOT EDIT! 2 | # source: tbmatch/log.proto 3 | 4 | import sys 5 | _b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) 6 | from google.protobuf import descriptor as _descriptor 7 | from google.protobuf import message as _message 8 | from google.protobuf import reflection as _reflection 9 | from google.protobuf import symbol_database as _symbol_database 10 | from google.protobuf import descriptor_pb2 11 | # @@protoc_insertion_point(imports) 12 | 13 | _sym_db = _symbol_database.Default() 14 | 15 | 16 | 17 | 18 | DESCRIPTOR = _descriptor.FileDescriptor( 19 | name='tbmatch/log.proto', 20 | package='tbmatch', 21 | syntax='proto2', 22 | serialized_pb=_b('\n\x11tbmatch/log.proto\x12\x07tbmatch\"\x92\x02\n\nLogMessage\x12\x16\n\x0etimestamp_msec\x18\x01 \x01(\x03\x12\x0f\n\x07message\x18\x02 \x01(\t\x12.\n\x08priority\x18\x03 \x01(\x0e\x32\x1c.tbmatch.LogMessage.Priority\x12*\n\x06source\x18\x04 \x01(\x0b\x32\x1a.tbmatch.LogMessage.Source\x1a.\n\x06Source\x12\x13\n\x0b\x61pplication\x18\x01 \x01(\t\x12\x0f\n\x07\x61\x64\x64ress\x18\x02 \x01(\t\"\x17\n\nEntityType\x12\t\n\x05MATCH\x10\x01\"6\n\x08Priority\x12\t\n\x05\x46\x41TAL\x10\x01\x12\t\n\x05\x45RROR\x10\x02\x12\x08\n\x04INFO\x10\x03\x12\n\n\x06TRIVIA\x10\x04') 23 | ) 24 | 25 | 26 | 27 | _LOGMESSAGE_ENTITYTYPE = _descriptor.EnumDescriptor( 28 | name='EntityType', 29 | full_name='tbmatch.LogMessage.EntityType', 30 | filename=None, 31 | file=DESCRIPTOR, 32 | values=[ 33 | _descriptor.EnumValueDescriptor( 34 | name='MATCH', index=0, number=1, 35 | options=None, 36 | type=None), 37 | ], 38 | containing_type=None, 39 | options=None, 40 | serialized_start=226, 41 | serialized_end=249, 42 | ) 43 | _sym_db.RegisterEnumDescriptor(_LOGMESSAGE_ENTITYTYPE) 44 | 45 | _LOGMESSAGE_PRIORITY = _descriptor.EnumDescriptor( 46 | name='Priority', 47 | full_name='tbmatch.LogMessage.Priority', 48 | filename=None, 49 | file=DESCRIPTOR, 50 | values=[ 51 | _descriptor.EnumValueDescriptor( 52 | name='FATAL', index=0, number=1, 53 | options=None, 54 | type=None), 55 | _descriptor.EnumValueDescriptor( 56 | name='ERROR', index=1, number=2, 57 | options=None, 58 | type=None), 59 | _descriptor.EnumValueDescriptor( 60 | name='INFO', index=2, number=3, 61 | options=None, 62 | type=None), 63 | _descriptor.EnumValueDescriptor( 64 | name='TRIVIA', index=3, number=4, 65 | options=None, 66 | type=None), 67 | ], 68 | containing_type=None, 69 | options=None, 70 | serialized_start=251, 71 | serialized_end=305, 72 | ) 73 | _sym_db.RegisterEnumDescriptor(_LOGMESSAGE_PRIORITY) 74 | 75 | 76 | _LOGMESSAGE_SOURCE = _descriptor.Descriptor( 77 | name='Source', 78 | full_name='tbmatch.LogMessage.Source', 79 | filename=None, 80 | file=DESCRIPTOR, 81 | containing_type=None, 82 | fields=[ 83 | _descriptor.FieldDescriptor( 84 | name='application', full_name='tbmatch.LogMessage.Source.application', index=0, 85 | number=1, type=9, cpp_type=9, label=1, 86 | has_default_value=False, default_value=_b("").decode('utf-8'), 87 | message_type=None, enum_type=None, containing_type=None, 88 | is_extension=False, extension_scope=None, 89 | options=None), 90 | _descriptor.FieldDescriptor( 91 | name='address', full_name='tbmatch.LogMessage.Source.address', index=1, 92 | number=2, type=9, cpp_type=9, label=1, 93 | has_default_value=False, default_value=_b("").decode('utf-8'), 94 | message_type=None, enum_type=None, containing_type=None, 95 | is_extension=False, extension_scope=None, 96 | options=None), 97 | ], 98 | extensions=[ 99 | ], 100 | nested_types=[], 101 | enum_types=[ 102 | ], 103 | options=None, 104 | is_extendable=False, 105 | syntax='proto2', 106 | extension_ranges=[], 107 | oneofs=[ 108 | ], 109 | serialized_start=178, 110 | serialized_end=224, 111 | ) 112 | 113 | _LOGMESSAGE = _descriptor.Descriptor( 114 | name='LogMessage', 115 | full_name='tbmatch.LogMessage', 116 | filename=None, 117 | file=DESCRIPTOR, 118 | containing_type=None, 119 | fields=[ 120 | _descriptor.FieldDescriptor( 121 | name='timestamp_msec', full_name='tbmatch.LogMessage.timestamp_msec', index=0, 122 | number=1, type=3, cpp_type=2, label=1, 123 | has_default_value=False, default_value=0, 124 | message_type=None, enum_type=None, containing_type=None, 125 | is_extension=False, extension_scope=None, 126 | options=None), 127 | _descriptor.FieldDescriptor( 128 | name='message', full_name='tbmatch.LogMessage.message', index=1, 129 | number=2, type=9, cpp_type=9, label=1, 130 | has_default_value=False, default_value=_b("").decode('utf-8'), 131 | message_type=None, enum_type=None, containing_type=None, 132 | is_extension=False, extension_scope=None, 133 | options=None), 134 | _descriptor.FieldDescriptor( 135 | name='priority', full_name='tbmatch.LogMessage.priority', index=2, 136 | number=3, type=14, cpp_type=8, label=1, 137 | has_default_value=False, default_value=1, 138 | message_type=None, enum_type=None, containing_type=None, 139 | is_extension=False, extension_scope=None, 140 | options=None), 141 | _descriptor.FieldDescriptor( 142 | name='source', full_name='tbmatch.LogMessage.source', index=3, 143 | number=4, type=11, cpp_type=10, label=1, 144 | has_default_value=False, default_value=None, 145 | message_type=None, enum_type=None, containing_type=None, 146 | is_extension=False, extension_scope=None, 147 | options=None), 148 | ], 149 | extensions=[ 150 | ], 151 | nested_types=[_LOGMESSAGE_SOURCE, ], 152 | enum_types=[ 153 | _LOGMESSAGE_ENTITYTYPE, 154 | _LOGMESSAGE_PRIORITY, 155 | ], 156 | options=None, 157 | is_extendable=False, 158 | syntax='proto2', 159 | extension_ranges=[], 160 | oneofs=[ 161 | ], 162 | serialized_start=31, 163 | serialized_end=305, 164 | ) 165 | 166 | _LOGMESSAGE_SOURCE.containing_type = _LOGMESSAGE 167 | _LOGMESSAGE.fields_by_name['priority'].enum_type = _LOGMESSAGE_PRIORITY 168 | _LOGMESSAGE.fields_by_name['source'].message_type = _LOGMESSAGE_SOURCE 169 | _LOGMESSAGE_ENTITYTYPE.containing_type = _LOGMESSAGE 170 | _LOGMESSAGE_PRIORITY.containing_type = _LOGMESSAGE 171 | DESCRIPTOR.message_types_by_name['LogMessage'] = _LOGMESSAGE 172 | _sym_db.RegisterFileDescriptor(DESCRIPTOR) 173 | 174 | LogMessage = _reflection.GeneratedProtocolMessageType('LogMessage', (_message.Message,), dict( 175 | 176 | Source = _reflection.GeneratedProtocolMessageType('Source', (_message.Message,), dict( 177 | DESCRIPTOR = _LOGMESSAGE_SOURCE, 178 | __module__ = 'tbmatch.log_pb2' 179 | # @@protoc_insertion_point(class_scope:tbmatch.LogMessage.Source) 180 | )) 181 | , 182 | DESCRIPTOR = _LOGMESSAGE, 183 | __module__ = 'tbmatch.log_pb2' 184 | # @@protoc_insertion_point(class_scope:tbmatch.LogMessage) 185 | )) 186 | _sym_db.RegisterMessage(LogMessage) 187 | _sym_db.RegisterMessage(LogMessage.Source) 188 | 189 | 190 | # @@protoc_insertion_point(module_scope) 191 | -------------------------------------------------------------------------------- /tbmatch/query_pb2.py: -------------------------------------------------------------------------------- 1 | # Generated by the protocol buffer compiler. DO NOT EDIT! 2 | # source: tbmatch/query.proto 3 | 4 | import sys 5 | _b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) 6 | from google.protobuf.internal import enum_type_wrapper 7 | from google.protobuf import descriptor as _descriptor 8 | from google.protobuf import message as _message 9 | from google.protobuf import reflection as _reflection 10 | from google.protobuf import symbol_database as _symbol_database 11 | from google.protobuf import descriptor_pb2 12 | # @@protoc_insertion_point(imports) 13 | 14 | _sym_db = _symbol_database.Default() 15 | 16 | 17 | 18 | 19 | DESCRIPTOR = _descriptor.FileDescriptor( 20 | name='tbmatch/query.proto', 21 | package='tbmatch', 22 | syntax='proto2', 23 | serialized_pb=_b('\n\x13tbmatch/query.proto\x12\x07tbmatch\"L\n\x0bStringMatch\x12.\n\x07pattern\x18\x01 \x01(\x0e\x32\x15.tbmatch.MatchPattern:\x06\x45QUALS\x12\r\n\x05value\x18\x02 \x01(\t\"\'\n\tTimeRange\x12\r\n\x05start\x18\x01 \x01(\t\x12\x0b\n\x03\x65nd\x18\x02 \x01(\t*4\n\x0cMatchPattern\x12\n\n\x06\x45QUALS\x10\x01\x12\n\n\x06PREFIX\x10\x02\x12\x0c\n\x08\x43ONTAINS\x10\x03') 24 | ) 25 | 26 | _MATCHPATTERN = _descriptor.EnumDescriptor( 27 | name='MatchPattern', 28 | full_name='tbmatch.MatchPattern', 29 | filename=None, 30 | file=DESCRIPTOR, 31 | values=[ 32 | _descriptor.EnumValueDescriptor( 33 | name='EQUALS', index=0, number=1, 34 | options=None, 35 | type=None), 36 | _descriptor.EnumValueDescriptor( 37 | name='PREFIX', index=1, number=2, 38 | options=None, 39 | type=None), 40 | _descriptor.EnumValueDescriptor( 41 | name='CONTAINS', index=2, number=3, 42 | options=None, 43 | type=None), 44 | ], 45 | containing_type=None, 46 | options=None, 47 | serialized_start=151, 48 | serialized_end=203, 49 | ) 50 | _sym_db.RegisterEnumDescriptor(_MATCHPATTERN) 51 | 52 | MatchPattern = enum_type_wrapper.EnumTypeWrapper(_MATCHPATTERN) 53 | EQUALS = 1 54 | PREFIX = 2 55 | CONTAINS = 3 56 | 57 | 58 | 59 | _STRINGMATCH = _descriptor.Descriptor( 60 | name='StringMatch', 61 | full_name='tbmatch.StringMatch', 62 | filename=None, 63 | file=DESCRIPTOR, 64 | containing_type=None, 65 | fields=[ 66 | _descriptor.FieldDescriptor( 67 | name='pattern', full_name='tbmatch.StringMatch.pattern', index=0, 68 | number=1, type=14, cpp_type=8, label=1, 69 | has_default_value=True, default_value=1, 70 | message_type=None, enum_type=None, containing_type=None, 71 | is_extension=False, extension_scope=None, 72 | options=None), 73 | _descriptor.FieldDescriptor( 74 | name='value', full_name='tbmatch.StringMatch.value', index=1, 75 | number=2, type=9, cpp_type=9, label=1, 76 | has_default_value=False, default_value=_b("").decode('utf-8'), 77 | message_type=None, enum_type=None, containing_type=None, 78 | is_extension=False, extension_scope=None, 79 | options=None), 80 | ], 81 | extensions=[ 82 | ], 83 | nested_types=[], 84 | enum_types=[ 85 | ], 86 | options=None, 87 | is_extendable=False, 88 | syntax='proto2', 89 | extension_ranges=[], 90 | oneofs=[ 91 | ], 92 | serialized_start=32, 93 | serialized_end=108, 94 | ) 95 | 96 | 97 | _TIMERANGE = _descriptor.Descriptor( 98 | name='TimeRange', 99 | full_name='tbmatch.TimeRange', 100 | filename=None, 101 | file=DESCRIPTOR, 102 | containing_type=None, 103 | fields=[ 104 | _descriptor.FieldDescriptor( 105 | name='start', full_name='tbmatch.TimeRange.start', index=0, 106 | number=1, type=9, cpp_type=9, label=1, 107 | has_default_value=False, default_value=_b("").decode('utf-8'), 108 | message_type=None, enum_type=None, containing_type=None, 109 | is_extension=False, extension_scope=None, 110 | options=None), 111 | _descriptor.FieldDescriptor( 112 | name='end', full_name='tbmatch.TimeRange.end', index=1, 113 | number=2, type=9, cpp_type=9, label=1, 114 | has_default_value=False, default_value=_b("").decode('utf-8'), 115 | message_type=None, enum_type=None, containing_type=None, 116 | is_extension=False, extension_scope=None, 117 | options=None), 118 | ], 119 | extensions=[ 120 | ], 121 | nested_types=[], 122 | enum_types=[ 123 | ], 124 | options=None, 125 | is_extendable=False, 126 | syntax='proto2', 127 | extension_ranges=[], 128 | oneofs=[ 129 | ], 130 | serialized_start=110, 131 | serialized_end=149, 132 | ) 133 | 134 | _STRINGMATCH.fields_by_name['pattern'].enum_type = _MATCHPATTERN 135 | DESCRIPTOR.message_types_by_name['StringMatch'] = _STRINGMATCH 136 | DESCRIPTOR.message_types_by_name['TimeRange'] = _TIMERANGE 137 | DESCRIPTOR.enum_types_by_name['MatchPattern'] = _MATCHPATTERN 138 | _sym_db.RegisterFileDescriptor(DESCRIPTOR) 139 | 140 | StringMatch = _reflection.GeneratedProtocolMessageType('StringMatch', (_message.Message,), dict( 141 | DESCRIPTOR = _STRINGMATCH, 142 | __module__ = 'tbmatch.query_pb2' 143 | # @@protoc_insertion_point(class_scope:tbmatch.StringMatch) 144 | )) 145 | _sym_db.RegisterMessage(StringMatch) 146 | 147 | TimeRange = _reflection.GeneratedProtocolMessageType('TimeRange', (_message.Message,), dict( 148 | DESCRIPTOR = _TIMERANGE, 149 | __module__ = 'tbmatch.query_pb2' 150 | # @@protoc_insertion_point(class_scope:tbmatch.TimeRange) 151 | )) 152 | _sym_db.RegisterMessage(TimeRange) 153 | 154 | 155 | # @@protoc_insertion_point(module_scope) 156 | -------------------------------------------------------------------------------- /tbmatch/session_pb2.py: -------------------------------------------------------------------------------- 1 | # Generated by the protocol buffer compiler. DO NOT EDIT! 2 | # source: tbmatch/session.proto 3 | 4 | import sys 5 | _b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) 6 | from google.protobuf.internal import enum_type_wrapper 7 | from google.protobuf import descriptor as _descriptor 8 | from google.protobuf import message as _message 9 | from google.protobuf import reflection as _reflection 10 | from google.protobuf import symbol_database as _symbol_database 11 | from google.protobuf import descriptor_pb2 12 | # @@protoc_insertion_point(imports) 13 | 14 | _sym_db = _symbol_database.Default() 15 | 16 | 17 | from tbrpc import tbrpc_pb2 as tbrpc_dot_tbrpc__pb2 18 | 19 | 20 | DESCRIPTOR = _descriptor.FileDescriptor( 21 | name='tbmatch/session.proto', 22 | package='tbmatch', 23 | syntax='proto2', 24 | serialized_pb=_b('\n\x15tbmatch/session.proto\x12\x07tbmatch\x1a\x11tbrpc/tbrpc.proto\"f\n\x0cLoginRequest\x12\r\n\x05login\x18\x01 \x01(\t\x12\x10\n\x08password\x18\x02 \x01(\t\x12\x15\n\rbuild_version\x18\x05 \x01(\t\x12\x1e\n\x16__deprecated_remote_ip\x18\n \x01(\t\"L\n\x1aGetGameSessionTicketResult\x12\x1f\n\x04game\x18\x01 \x01(\x0e\x32\x11.tbmatch.GameType\x12\r\n\x05nonce\x18\x02 \x01(\t\"g\n\x1eRedeemGameSessionTicketRequest\x12\x1f\n\x04game\x18\x01 \x01(\x0e\x32\x11.tbmatch.GameType\x12\r\n\x05nonce\x18\x02 \x01(\t\x12\x15\n\rbuild_version\x18\x05 \x01(\t\"\\\n\x1bGetGameSessionTicketRequest\x12\x1f\n\x04game\x18\x01 \x01(\x0e\x32\x11.tbmatch.GameType\x12\x1c\n\x14\x63lient_build_version\x18\x02 \x01(\t*!\n\x08GameType\x12\x15\n\x11GT_RISING_THUNDER\x10\x64\x32\xb1\x02\n\x0eSessionService\x12\x32\n\x05Login\x12\x15.tbmatch.LoginRequest\x1a\x0c.tbrpc.Empty\"\x04\xc8\xf3\x18\x00\x12g\n\x14GetGameSessionTicket\x12$.tbmatch.GetGameSessionTicketRequest\x1a#.tbmatch.GetGameSessionTicketResult\"\x04\xc8\xf3\x18\x01\x12V\n\x17RedeemGameSessionTicket\x12\'.tbmatch.RedeemGameSessionTicketRequest\x1a\x0c.tbrpc.Empty\"\x04\xc8\xf3\x18\x00\x12*\n\x06Logout\x12\x0c.tbrpc.Empty\x1a\x0c.tbrpc.Empty\"\x04\xc8\xf3\x18\x01') 25 | , 26 | dependencies=[tbrpc_dot_tbrpc__pb2.DESCRIPTOR,]) 27 | 28 | _GAMETYPE = _descriptor.EnumDescriptor( 29 | name='GameType', 30 | full_name='tbmatch.GameType', 31 | filename=None, 32 | file=DESCRIPTOR, 33 | values=[ 34 | _descriptor.EnumValueDescriptor( 35 | name='GT_RISING_THUNDER', index=0, number=100, 36 | options=None, 37 | type=None), 38 | ], 39 | containing_type=None, 40 | options=None, 41 | serialized_start=434, 42 | serialized_end=467, 43 | ) 44 | _sym_db.RegisterEnumDescriptor(_GAMETYPE) 45 | 46 | GameType = enum_type_wrapper.EnumTypeWrapper(_GAMETYPE) 47 | GT_RISING_THUNDER = 100 48 | 49 | 50 | 51 | _LOGINREQUEST = _descriptor.Descriptor( 52 | name='LoginRequest', 53 | full_name='tbmatch.LoginRequest', 54 | filename=None, 55 | file=DESCRIPTOR, 56 | containing_type=None, 57 | fields=[ 58 | _descriptor.FieldDescriptor( 59 | name='login', full_name='tbmatch.LoginRequest.login', index=0, 60 | number=1, type=9, cpp_type=9, label=1, 61 | has_default_value=False, default_value=_b("").decode('utf-8'), 62 | message_type=None, enum_type=None, containing_type=None, 63 | is_extension=False, extension_scope=None, 64 | options=None), 65 | _descriptor.FieldDescriptor( 66 | name='password', full_name='tbmatch.LoginRequest.password', index=1, 67 | number=2, type=9, cpp_type=9, label=1, 68 | has_default_value=False, default_value=_b("").decode('utf-8'), 69 | message_type=None, enum_type=None, containing_type=None, 70 | is_extension=False, extension_scope=None, 71 | options=None), 72 | _descriptor.FieldDescriptor( 73 | name='build_version', full_name='tbmatch.LoginRequest.build_version', index=2, 74 | number=5, type=9, cpp_type=9, label=1, 75 | has_default_value=False, default_value=_b("").decode('utf-8'), 76 | message_type=None, enum_type=None, containing_type=None, 77 | is_extension=False, extension_scope=None, 78 | options=None), 79 | _descriptor.FieldDescriptor( 80 | name='__deprecated_remote_ip', full_name='tbmatch.LoginRequest.__deprecated_remote_ip', index=3, 81 | number=10, type=9, cpp_type=9, label=1, 82 | has_default_value=False, default_value=_b("").decode('utf-8'), 83 | message_type=None, enum_type=None, containing_type=None, 84 | is_extension=False, extension_scope=None, 85 | options=None), 86 | ], 87 | extensions=[ 88 | ], 89 | nested_types=[], 90 | enum_types=[ 91 | ], 92 | options=None, 93 | is_extendable=False, 94 | syntax='proto2', 95 | extension_ranges=[], 96 | oneofs=[ 97 | ], 98 | serialized_start=53, 99 | serialized_end=155, 100 | ) 101 | 102 | 103 | _GETGAMESESSIONTICKETRESULT = _descriptor.Descriptor( 104 | name='GetGameSessionTicketResult', 105 | full_name='tbmatch.GetGameSessionTicketResult', 106 | filename=None, 107 | file=DESCRIPTOR, 108 | containing_type=None, 109 | fields=[ 110 | _descriptor.FieldDescriptor( 111 | name='game', full_name='tbmatch.GetGameSessionTicketResult.game', index=0, 112 | number=1, type=14, cpp_type=8, label=1, 113 | has_default_value=False, default_value=100, 114 | message_type=None, enum_type=None, containing_type=None, 115 | is_extension=False, extension_scope=None, 116 | options=None), 117 | _descriptor.FieldDescriptor( 118 | name='nonce', full_name='tbmatch.GetGameSessionTicketResult.nonce', index=1, 119 | number=2, type=9, cpp_type=9, label=1, 120 | has_default_value=False, default_value=_b("").decode('utf-8'), 121 | message_type=None, enum_type=None, containing_type=None, 122 | is_extension=False, extension_scope=None, 123 | options=None), 124 | ], 125 | extensions=[ 126 | ], 127 | nested_types=[], 128 | enum_types=[ 129 | ], 130 | options=None, 131 | is_extendable=False, 132 | syntax='proto2', 133 | extension_ranges=[], 134 | oneofs=[ 135 | ], 136 | serialized_start=157, 137 | serialized_end=233, 138 | ) 139 | 140 | 141 | _REDEEMGAMESESSIONTICKETREQUEST = _descriptor.Descriptor( 142 | name='RedeemGameSessionTicketRequest', 143 | full_name='tbmatch.RedeemGameSessionTicketRequest', 144 | filename=None, 145 | file=DESCRIPTOR, 146 | containing_type=None, 147 | fields=[ 148 | _descriptor.FieldDescriptor( 149 | name='game', full_name='tbmatch.RedeemGameSessionTicketRequest.game', index=0, 150 | number=1, type=14, cpp_type=8, label=1, 151 | has_default_value=False, default_value=100, 152 | message_type=None, enum_type=None, containing_type=None, 153 | is_extension=False, extension_scope=None, 154 | options=None), 155 | _descriptor.FieldDescriptor( 156 | name='nonce', full_name='tbmatch.RedeemGameSessionTicketRequest.nonce', index=1, 157 | number=2, type=9, cpp_type=9, label=1, 158 | has_default_value=False, default_value=_b("").decode('utf-8'), 159 | message_type=None, enum_type=None, containing_type=None, 160 | is_extension=False, extension_scope=None, 161 | options=None), 162 | _descriptor.FieldDescriptor( 163 | name='build_version', full_name='tbmatch.RedeemGameSessionTicketRequest.build_version', index=2, 164 | number=5, type=9, cpp_type=9, label=1, 165 | has_default_value=False, default_value=_b("").decode('utf-8'), 166 | message_type=None, enum_type=None, containing_type=None, 167 | is_extension=False, extension_scope=None, 168 | options=None), 169 | ], 170 | extensions=[ 171 | ], 172 | nested_types=[], 173 | enum_types=[ 174 | ], 175 | options=None, 176 | is_extendable=False, 177 | syntax='proto2', 178 | extension_ranges=[], 179 | oneofs=[ 180 | ], 181 | serialized_start=235, 182 | serialized_end=338, 183 | ) 184 | 185 | 186 | _GETGAMESESSIONTICKETREQUEST = _descriptor.Descriptor( 187 | name='GetGameSessionTicketRequest', 188 | full_name='tbmatch.GetGameSessionTicketRequest', 189 | filename=None, 190 | file=DESCRIPTOR, 191 | containing_type=None, 192 | fields=[ 193 | _descriptor.FieldDescriptor( 194 | name='game', full_name='tbmatch.GetGameSessionTicketRequest.game', index=0, 195 | number=1, type=14, cpp_type=8, label=1, 196 | has_default_value=False, default_value=100, 197 | message_type=None, enum_type=None, containing_type=None, 198 | is_extension=False, extension_scope=None, 199 | options=None), 200 | _descriptor.FieldDescriptor( 201 | name='client_build_version', full_name='tbmatch.GetGameSessionTicketRequest.client_build_version', index=1, 202 | number=2, type=9, cpp_type=9, label=1, 203 | has_default_value=False, default_value=_b("").decode('utf-8'), 204 | message_type=None, enum_type=None, containing_type=None, 205 | is_extension=False, extension_scope=None, 206 | options=None), 207 | ], 208 | extensions=[ 209 | ], 210 | nested_types=[], 211 | enum_types=[ 212 | ], 213 | options=None, 214 | is_extendable=False, 215 | syntax='proto2', 216 | extension_ranges=[], 217 | oneofs=[ 218 | ], 219 | serialized_start=340, 220 | serialized_end=432, 221 | ) 222 | 223 | _GETGAMESESSIONTICKETRESULT.fields_by_name['game'].enum_type = _GAMETYPE 224 | _REDEEMGAMESESSIONTICKETREQUEST.fields_by_name['game'].enum_type = _GAMETYPE 225 | _GETGAMESESSIONTICKETREQUEST.fields_by_name['game'].enum_type = _GAMETYPE 226 | DESCRIPTOR.message_types_by_name['LoginRequest'] = _LOGINREQUEST 227 | DESCRIPTOR.message_types_by_name['GetGameSessionTicketResult'] = _GETGAMESESSIONTICKETRESULT 228 | DESCRIPTOR.message_types_by_name['RedeemGameSessionTicketRequest'] = _REDEEMGAMESESSIONTICKETREQUEST 229 | DESCRIPTOR.message_types_by_name['GetGameSessionTicketRequest'] = _GETGAMESESSIONTICKETREQUEST 230 | DESCRIPTOR.enum_types_by_name['GameType'] = _GAMETYPE 231 | _sym_db.RegisterFileDescriptor(DESCRIPTOR) 232 | 233 | LoginRequest = _reflection.GeneratedProtocolMessageType('LoginRequest', (_message.Message,), dict( 234 | DESCRIPTOR = _LOGINREQUEST, 235 | __module__ = 'tbmatch.session_pb2' 236 | # @@protoc_insertion_point(class_scope:tbmatch.LoginRequest) 237 | )) 238 | _sym_db.RegisterMessage(LoginRequest) 239 | 240 | GetGameSessionTicketResult = _reflection.GeneratedProtocolMessageType('GetGameSessionTicketResult', (_message.Message,), dict( 241 | DESCRIPTOR = _GETGAMESESSIONTICKETRESULT, 242 | __module__ = 'tbmatch.session_pb2' 243 | # @@protoc_insertion_point(class_scope:tbmatch.GetGameSessionTicketResult) 244 | )) 245 | _sym_db.RegisterMessage(GetGameSessionTicketResult) 246 | 247 | RedeemGameSessionTicketRequest = _reflection.GeneratedProtocolMessageType('RedeemGameSessionTicketRequest', (_message.Message,), dict( 248 | DESCRIPTOR = _REDEEMGAMESESSIONTICKETREQUEST, 249 | __module__ = 'tbmatch.session_pb2' 250 | # @@protoc_insertion_point(class_scope:tbmatch.RedeemGameSessionTicketRequest) 251 | )) 252 | _sym_db.RegisterMessage(RedeemGameSessionTicketRequest) 253 | 254 | GetGameSessionTicketRequest = _reflection.GeneratedProtocolMessageType('GetGameSessionTicketRequest', (_message.Message,), dict( 255 | DESCRIPTOR = _GETGAMESESSIONTICKETREQUEST, 256 | __module__ = 'tbmatch.session_pb2' 257 | # @@protoc_insertion_point(class_scope:tbmatch.GetGameSessionTicketRequest) 258 | )) 259 | _sym_db.RegisterMessage(GetGameSessionTicketRequest) 260 | 261 | 262 | 263 | _SESSIONSERVICE = _descriptor.ServiceDescriptor( 264 | name='SessionService', 265 | full_name='tbmatch.SessionService', 266 | file=DESCRIPTOR, 267 | index=0, 268 | options=None, 269 | serialized_start=470, 270 | serialized_end=775, 271 | methods=[ 272 | _descriptor.MethodDescriptor( 273 | name='Login', 274 | full_name='tbmatch.SessionService.Login', 275 | index=0, 276 | containing_service=None, 277 | input_type=_LOGINREQUEST, 278 | output_type=tbrpc_dot_tbrpc__pb2._EMPTY, 279 | options=_descriptor._ParseOptions(descriptor_pb2.MethodOptions(), _b('\310\363\030\000')), 280 | ), 281 | _descriptor.MethodDescriptor( 282 | name='GetGameSessionTicket', 283 | full_name='tbmatch.SessionService.GetGameSessionTicket', 284 | index=1, 285 | containing_service=None, 286 | input_type=_GETGAMESESSIONTICKETREQUEST, 287 | output_type=_GETGAMESESSIONTICKETRESULT, 288 | options=_descriptor._ParseOptions(descriptor_pb2.MethodOptions(), _b('\310\363\030\001')), 289 | ), 290 | _descriptor.MethodDescriptor( 291 | name='RedeemGameSessionTicket', 292 | full_name='tbmatch.SessionService.RedeemGameSessionTicket', 293 | index=2, 294 | containing_service=None, 295 | input_type=_REDEEMGAMESESSIONTICKETREQUEST, 296 | output_type=tbrpc_dot_tbrpc__pb2._EMPTY, 297 | options=_descriptor._ParseOptions(descriptor_pb2.MethodOptions(), _b('\310\363\030\000')), 298 | ), 299 | _descriptor.MethodDescriptor( 300 | name='Logout', 301 | full_name='tbmatch.SessionService.Logout', 302 | index=3, 303 | containing_service=None, 304 | input_type=tbrpc_dot_tbrpc__pb2._EMPTY, 305 | output_type=tbrpc_dot_tbrpc__pb2._EMPTY, 306 | options=_descriptor._ParseOptions(descriptor_pb2.MethodOptions(), _b('\310\363\030\001')), 307 | ), 308 | ]) 309 | _sym_db.RegisterServiceDescriptor(_SESSIONSERVICE) 310 | 311 | DESCRIPTOR.services_by_name['SessionService'] = _SESSIONSERVICE 312 | 313 | # @@protoc_insertion_point(module_scope) 314 | -------------------------------------------------------------------------------- /tbmatch/user_pb2.py: -------------------------------------------------------------------------------- 1 | # Generated by the protocol buffer compiler. DO NOT EDIT! 2 | # source: tbmatch/user.proto 3 | 4 | import sys 5 | _b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) 6 | from google.protobuf.internal import enum_type_wrapper 7 | from google.protobuf import descriptor as _descriptor 8 | from google.protobuf import message as _message 9 | from google.protobuf import reflection as _reflection 10 | from google.protobuf import symbol_database as _symbol_database 11 | from google.protobuf import descriptor_pb2 12 | # @@protoc_insertion_point(imports) 13 | 14 | _sym_db = _symbol_database.Default() 15 | 16 | 17 | from tbmatch import match_pb2 as tbmatch_dot_match__pb2 18 | 19 | 20 | DESCRIPTOR = _descriptor.FileDescriptor( 21 | name='tbmatch/user.proto', 22 | package='tbmatch', 23 | syntax='proto2', 24 | serialized_pb=_b('\n\x12tbmatch/user.proto\x12\x07tbmatch\x1a\x13tbmatch/match.proto\"\x8c\x04\n\rPlayerSession\x12#\n\x05state\x18\x01 \x01(\x0e\x32\x14.tbmatch.PlayerState\x12\x38\n\x0c\x61\x63tive_match\x18\x02 \x01(\x0b\x32\".tbmatch.PlayerSession.ActiveMatch\x12)\n\x04menu\x18\x65 \x01(\x0b\x32\x1b.tbmatch.PlayerSession.Menu\x12+\n\x05queue\x18\x66 \x01(\x0b\x32\x1c.tbmatch.PlayerSession.Queue\x12+\n\x05lobby\x18g \x01(\x0b\x32\x1c.tbmatch.PlayerSession.Lobby\x1a\x32\n\x0b\x41\x63tiveMatch\x12\x10\n\x08match_id\x18\x01 \x01(\x03\x12\x11\n\tspectator\x18\x02 \x01(\x08\x1a\x82\x01\n\x04Menu\x12\x32\n\x06screen\x18\x01 \x01(\x0e\x32\".tbmatch.PlayerSession.Menu.Screen\"F\n\x06Screen\x12\x0b\n\x07SUMMARY\x10\x01\x12\x10\n\x0cPLAYER_STATS\x10\x02\x12\x12\n\x0ePLAYER_HISTORY\x10\x03\x12\t\n\x05STORE\x10\x04\x1a\x43\n\x05Queue\x12&\n\nmatch_type\x18\x01 \x01(\x0e\x32\x12.tbmatch.MatchType\x12\x12\n\nstart_time\x18\x02 \x01(\x03\x1a\x19\n\x05Lobby\x12\x10\n\x08lobby_id\x18\x01 \x01(\x03*6\n\x0bPlayerState\x12\x0b\n\x07PS_MENU\x10\x65\x12\x0c\n\x08PS_QUEUE\x10\x66\x12\x0c\n\x08PS_LOBBY\x10g') 25 | , 26 | dependencies=[tbmatch_dot_match__pb2.DESCRIPTOR,]) 27 | 28 | _PLAYERSTATE = _descriptor.EnumDescriptor( 29 | name='PlayerState', 30 | full_name='tbmatch.PlayerState', 31 | filename=None, 32 | file=DESCRIPTOR, 33 | values=[ 34 | _descriptor.EnumValueDescriptor( 35 | name='PS_MENU', index=0, number=101, 36 | options=None, 37 | type=None), 38 | _descriptor.EnumValueDescriptor( 39 | name='PS_QUEUE', index=1, number=102, 40 | options=None, 41 | type=None), 42 | _descriptor.EnumValueDescriptor( 43 | name='PS_LOBBY', index=2, number=103, 44 | options=None, 45 | type=None), 46 | ], 47 | containing_type=None, 48 | options=None, 49 | serialized_start=579, 50 | serialized_end=633, 51 | ) 52 | _sym_db.RegisterEnumDescriptor(_PLAYERSTATE) 53 | 54 | PlayerState = enum_type_wrapper.EnumTypeWrapper(_PLAYERSTATE) 55 | PS_MENU = 101 56 | PS_QUEUE = 102 57 | PS_LOBBY = 103 58 | 59 | 60 | _PLAYERSESSION_MENU_SCREEN = _descriptor.EnumDescriptor( 61 | name='Screen', 62 | full_name='tbmatch.PlayerSession.Menu.Screen', 63 | filename=None, 64 | file=DESCRIPTOR, 65 | values=[ 66 | _descriptor.EnumValueDescriptor( 67 | name='SUMMARY', index=0, number=1, 68 | options=None, 69 | type=None), 70 | _descriptor.EnumValueDescriptor( 71 | name='PLAYER_STATS', index=1, number=2, 72 | options=None, 73 | type=None), 74 | _descriptor.EnumValueDescriptor( 75 | name='PLAYER_HISTORY', index=2, number=3, 76 | options=None, 77 | type=None), 78 | _descriptor.EnumValueDescriptor( 79 | name='STORE', index=3, number=4, 80 | options=None, 81 | type=None), 82 | ], 83 | containing_type=None, 84 | options=None, 85 | serialized_start=411, 86 | serialized_end=481, 87 | ) 88 | _sym_db.RegisterEnumDescriptor(_PLAYERSESSION_MENU_SCREEN) 89 | 90 | 91 | _PLAYERSESSION_ACTIVEMATCH = _descriptor.Descriptor( 92 | name='ActiveMatch', 93 | full_name='tbmatch.PlayerSession.ActiveMatch', 94 | filename=None, 95 | file=DESCRIPTOR, 96 | containing_type=None, 97 | fields=[ 98 | _descriptor.FieldDescriptor( 99 | name='match_id', full_name='tbmatch.PlayerSession.ActiveMatch.match_id', index=0, 100 | number=1, type=3, cpp_type=2, label=1, 101 | has_default_value=False, default_value=0, 102 | message_type=None, enum_type=None, containing_type=None, 103 | is_extension=False, extension_scope=None, 104 | options=None), 105 | _descriptor.FieldDescriptor( 106 | name='spectator', full_name='tbmatch.PlayerSession.ActiveMatch.spectator', index=1, 107 | number=2, type=8, cpp_type=7, label=1, 108 | has_default_value=False, default_value=False, 109 | message_type=None, enum_type=None, containing_type=None, 110 | is_extension=False, extension_scope=None, 111 | options=None), 112 | ], 113 | extensions=[ 114 | ], 115 | nested_types=[], 116 | enum_types=[ 117 | ], 118 | options=None, 119 | is_extendable=False, 120 | syntax='proto2', 121 | extension_ranges=[], 122 | oneofs=[ 123 | ], 124 | serialized_start=298, 125 | serialized_end=348, 126 | ) 127 | 128 | _PLAYERSESSION_MENU = _descriptor.Descriptor( 129 | name='Menu', 130 | full_name='tbmatch.PlayerSession.Menu', 131 | filename=None, 132 | file=DESCRIPTOR, 133 | containing_type=None, 134 | fields=[ 135 | _descriptor.FieldDescriptor( 136 | name='screen', full_name='tbmatch.PlayerSession.Menu.screen', index=0, 137 | number=1, type=14, cpp_type=8, label=1, 138 | has_default_value=False, default_value=1, 139 | message_type=None, enum_type=None, containing_type=None, 140 | is_extension=False, extension_scope=None, 141 | options=None), 142 | ], 143 | extensions=[ 144 | ], 145 | nested_types=[], 146 | enum_types=[ 147 | _PLAYERSESSION_MENU_SCREEN, 148 | ], 149 | options=None, 150 | is_extendable=False, 151 | syntax='proto2', 152 | extension_ranges=[], 153 | oneofs=[ 154 | ], 155 | serialized_start=351, 156 | serialized_end=481, 157 | ) 158 | 159 | _PLAYERSESSION_QUEUE = _descriptor.Descriptor( 160 | name='Queue', 161 | full_name='tbmatch.PlayerSession.Queue', 162 | filename=None, 163 | file=DESCRIPTOR, 164 | containing_type=None, 165 | fields=[ 166 | _descriptor.FieldDescriptor( 167 | name='match_type', full_name='tbmatch.PlayerSession.Queue.match_type', index=0, 168 | number=1, type=14, cpp_type=8, label=1, 169 | has_default_value=False, default_value=1, 170 | message_type=None, enum_type=None, containing_type=None, 171 | is_extension=False, extension_scope=None, 172 | options=None), 173 | _descriptor.FieldDescriptor( 174 | name='start_time', full_name='tbmatch.PlayerSession.Queue.start_time', index=1, 175 | number=2, type=3, cpp_type=2, label=1, 176 | has_default_value=False, default_value=0, 177 | message_type=None, enum_type=None, containing_type=None, 178 | is_extension=False, extension_scope=None, 179 | options=None), 180 | ], 181 | extensions=[ 182 | ], 183 | nested_types=[], 184 | enum_types=[ 185 | ], 186 | options=None, 187 | is_extendable=False, 188 | syntax='proto2', 189 | extension_ranges=[], 190 | oneofs=[ 191 | ], 192 | serialized_start=483, 193 | serialized_end=550, 194 | ) 195 | 196 | _PLAYERSESSION_LOBBY = _descriptor.Descriptor( 197 | name='Lobby', 198 | full_name='tbmatch.PlayerSession.Lobby', 199 | filename=None, 200 | file=DESCRIPTOR, 201 | containing_type=None, 202 | fields=[ 203 | _descriptor.FieldDescriptor( 204 | name='lobby_id', full_name='tbmatch.PlayerSession.Lobby.lobby_id', index=0, 205 | number=1, type=3, cpp_type=2, label=1, 206 | has_default_value=False, default_value=0, 207 | message_type=None, enum_type=None, containing_type=None, 208 | is_extension=False, extension_scope=None, 209 | options=None), 210 | ], 211 | extensions=[ 212 | ], 213 | nested_types=[], 214 | enum_types=[ 215 | ], 216 | options=None, 217 | is_extendable=False, 218 | syntax='proto2', 219 | extension_ranges=[], 220 | oneofs=[ 221 | ], 222 | serialized_start=552, 223 | serialized_end=577, 224 | ) 225 | 226 | _PLAYERSESSION = _descriptor.Descriptor( 227 | name='PlayerSession', 228 | full_name='tbmatch.PlayerSession', 229 | filename=None, 230 | file=DESCRIPTOR, 231 | containing_type=None, 232 | fields=[ 233 | _descriptor.FieldDescriptor( 234 | name='state', full_name='tbmatch.PlayerSession.state', index=0, 235 | number=1, type=14, cpp_type=8, label=1, 236 | has_default_value=False, default_value=101, 237 | message_type=None, enum_type=None, containing_type=None, 238 | is_extension=False, extension_scope=None, 239 | options=None), 240 | _descriptor.FieldDescriptor( 241 | name='active_match', full_name='tbmatch.PlayerSession.active_match', index=1, 242 | number=2, type=11, cpp_type=10, label=1, 243 | has_default_value=False, default_value=None, 244 | message_type=None, enum_type=None, containing_type=None, 245 | is_extension=False, extension_scope=None, 246 | options=None), 247 | _descriptor.FieldDescriptor( 248 | name='menu', full_name='tbmatch.PlayerSession.menu', index=2, 249 | number=101, type=11, cpp_type=10, label=1, 250 | has_default_value=False, default_value=None, 251 | message_type=None, enum_type=None, containing_type=None, 252 | is_extension=False, extension_scope=None, 253 | options=None), 254 | _descriptor.FieldDescriptor( 255 | name='queue', full_name='tbmatch.PlayerSession.queue', index=3, 256 | number=102, type=11, cpp_type=10, label=1, 257 | has_default_value=False, default_value=None, 258 | message_type=None, enum_type=None, containing_type=None, 259 | is_extension=False, extension_scope=None, 260 | options=None), 261 | _descriptor.FieldDescriptor( 262 | name='lobby', full_name='tbmatch.PlayerSession.lobby', index=4, 263 | number=103, type=11, cpp_type=10, label=1, 264 | has_default_value=False, default_value=None, 265 | message_type=None, enum_type=None, containing_type=None, 266 | is_extension=False, extension_scope=None, 267 | options=None), 268 | ], 269 | extensions=[ 270 | ], 271 | nested_types=[_PLAYERSESSION_ACTIVEMATCH, _PLAYERSESSION_MENU, _PLAYERSESSION_QUEUE, _PLAYERSESSION_LOBBY, ], 272 | enum_types=[ 273 | ], 274 | options=None, 275 | is_extendable=False, 276 | syntax='proto2', 277 | extension_ranges=[], 278 | oneofs=[ 279 | ], 280 | serialized_start=53, 281 | serialized_end=577, 282 | ) 283 | 284 | _PLAYERSESSION_ACTIVEMATCH.containing_type = _PLAYERSESSION 285 | _PLAYERSESSION_MENU.fields_by_name['screen'].enum_type = _PLAYERSESSION_MENU_SCREEN 286 | _PLAYERSESSION_MENU.containing_type = _PLAYERSESSION 287 | _PLAYERSESSION_MENU_SCREEN.containing_type = _PLAYERSESSION_MENU 288 | _PLAYERSESSION_QUEUE.fields_by_name['match_type'].enum_type = tbmatch_dot_match__pb2._MATCHTYPE 289 | _PLAYERSESSION_QUEUE.containing_type = _PLAYERSESSION 290 | _PLAYERSESSION_LOBBY.containing_type = _PLAYERSESSION 291 | _PLAYERSESSION.fields_by_name['state'].enum_type = _PLAYERSTATE 292 | _PLAYERSESSION.fields_by_name['active_match'].message_type = _PLAYERSESSION_ACTIVEMATCH 293 | _PLAYERSESSION.fields_by_name['menu'].message_type = _PLAYERSESSION_MENU 294 | _PLAYERSESSION.fields_by_name['queue'].message_type = _PLAYERSESSION_QUEUE 295 | _PLAYERSESSION.fields_by_name['lobby'].message_type = _PLAYERSESSION_LOBBY 296 | DESCRIPTOR.message_types_by_name['PlayerSession'] = _PLAYERSESSION 297 | DESCRIPTOR.enum_types_by_name['PlayerState'] = _PLAYERSTATE 298 | _sym_db.RegisterFileDescriptor(DESCRIPTOR) 299 | 300 | PlayerSession = _reflection.GeneratedProtocolMessageType('PlayerSession', (_message.Message,), dict( 301 | 302 | ActiveMatch = _reflection.GeneratedProtocolMessageType('ActiveMatch', (_message.Message,), dict( 303 | DESCRIPTOR = _PLAYERSESSION_ACTIVEMATCH, 304 | __module__ = 'tbmatch.user_pb2' 305 | # @@protoc_insertion_point(class_scope:tbmatch.PlayerSession.ActiveMatch) 306 | )) 307 | , 308 | 309 | Menu = _reflection.GeneratedProtocolMessageType('Menu', (_message.Message,), dict( 310 | DESCRIPTOR = _PLAYERSESSION_MENU, 311 | __module__ = 'tbmatch.user_pb2' 312 | # @@protoc_insertion_point(class_scope:tbmatch.PlayerSession.Menu) 313 | )) 314 | , 315 | 316 | Queue = _reflection.GeneratedProtocolMessageType('Queue', (_message.Message,), dict( 317 | DESCRIPTOR = _PLAYERSESSION_QUEUE, 318 | __module__ = 'tbmatch.user_pb2' 319 | # @@protoc_insertion_point(class_scope:tbmatch.PlayerSession.Queue) 320 | )) 321 | , 322 | 323 | Lobby = _reflection.GeneratedProtocolMessageType('Lobby', (_message.Message,), dict( 324 | DESCRIPTOR = _PLAYERSESSION_LOBBY, 325 | __module__ = 'tbmatch.user_pb2' 326 | # @@protoc_insertion_point(class_scope:tbmatch.PlayerSession.Lobby) 327 | )) 328 | , 329 | DESCRIPTOR = _PLAYERSESSION, 330 | __module__ = 'tbmatch.user_pb2' 331 | # @@protoc_insertion_point(class_scope:tbmatch.PlayerSession) 332 | )) 333 | _sym_db.RegisterMessage(PlayerSession) 334 | _sym_db.RegisterMessage(PlayerSession.ActiveMatch) 335 | _sym_db.RegisterMessage(PlayerSession.Menu) 336 | _sym_db.RegisterMessage(PlayerSession.Queue) 337 | _sym_db.RegisterMessage(PlayerSession.Lobby) 338 | 339 | 340 | # @@protoc_insertion_point(module_scope) 341 | -------------------------------------------------------------------------------- /tbportal/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RiotGames/rtce-server/5d1b9222b7f90db94e1f424986253d6dff103484/tbportal/__init__.py -------------------------------------------------------------------------------- /tbrpc/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RiotGames/rtce-server/5d1b9222b7f90db94e1f424986253d6dff103484/tbrpc/__init__.py -------------------------------------------------------------------------------- /tbui/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RiotGames/rtce-server/5d1b9222b7f90db94e1f424986253d6dff103484/tbui/__init__.py -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | current = os.path.dirname(os.path.abspath(__file__)) 5 | root = os.path.normpath(os.path.join(current, '..')) 6 | sys.path = ['..'] + sys.path 7 | -------------------------------------------------------------------------------- /tests/game_client.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import rpc_client 3 | import tbmatch.session_pb2 4 | import google.protobuf.json_format 5 | 6 | NEXT_USER_ID = 1 7 | 8 | class GameClient(rpc_client.RpcClient): 9 | def __init__(self, username = None): 10 | rpc_client.RpcClient.__init__(self) 11 | 12 | if not username: 13 | global NEXT_USER_ID 14 | username = 'testuser%03d' % NEXT_USER_ID 15 | NEXT_USER_ID += 1 16 | 17 | self.username = username 18 | self.get_event_version = '' 19 | self.DoLogin() 20 | 21 | def DoLogin(self): 22 | request = tbmatch.session_pb2.LoginRequest() 23 | request.login = self.username 24 | self.Login(request) 25 | 26 | request = tbmatch.session_pb2.GetGameSessionTicketRequest() 27 | request.game = tbmatch.session_pb2.GT_RISING_THUNDER 28 | ticket = self.GetGameSessionTicket(request).nonce 29 | 30 | self.Logout() 31 | 32 | request = tbmatch.session_pb2.RedeemGameSessionTicketRequest() 33 | request.nonce = ticket 34 | request.build_version = '1728' 35 | request.game = tbmatch.session_pb2.GT_RISING_THUNDER; 36 | self.RedeemGameSessionTicket(request) 37 | 38 | def DoGetEvents(self): 39 | request = tbmatch.event_pb2.GetEventRequest() 40 | request.version = self.get_event_version 41 | result = self.GetEvent(request) 42 | if len(result.event) > 0: 43 | self.get_event_version = str(result.event[-1].event_id) 44 | 45 | return result.event 46 | -------------------------------------------------------------------------------- /tests/test_home_screen.py: -------------------------------------------------------------------------------- 1 | import game_client 2 | 3 | def test_login(): 4 | game_client.GameClient() 5 | 6 | def test_home(): 7 | c = game_client.GameClient() 8 | profile = c.GetGameProfile() 9 | games = c.GetRecentGames() 10 | 11 | if __name__ == '__main__': 12 | test_login() -------------------------------------------------------------------------------- /tests/test_lobbies.py: -------------------------------------------------------------------------------- 1 | import game_client 2 | import tbmatch.lobby_pb2 3 | 4 | 5 | def test_create_lobby(): 6 | c = game_client.GameClient() 7 | request = tbmatch.lobby_pb2.CreateLobbyRequest() 8 | request.type = tbmatch.lobby_pb2.LT_QUEUED 9 | c.CreateLobby(request) 10 | c.DoGetEvents() 11 | 12 | def test_set_lobby_ready(): 13 | c = game_client.GameClient() 14 | request = tbmatch.lobby_pb2.CreateLobbyRequest() 15 | request.type = tbmatch.lobby_pb2.LT_QUEUED 16 | c.CreateLobby(request) 17 | 18 | request = tbmatch.lobby_pb2.LobbySetReadyRequest() 19 | request.ready = True 20 | c.LobbySetReady(request) 21 | 22 | c.DoGetEvents() 23 | 24 | def test_join_lobby_code(): 25 | c = game_client.GameClient() 26 | request = tbmatch.lobby_pb2.CreateLobbyRequest() 27 | request.type = tbmatch.lobby_pb2.LT_QUEUED 28 | c.CreateLobby(request) 29 | 30 | request = tbmatch.lobby_pb2.JoinLobbyByCodeRequest() 31 | request.code = "test" 32 | c.JoinLobbyByCode(request) 33 | 34 | c.DoGetEvents() -------------------------------------------------------------------------------- /tests/test_matchmaker.py: -------------------------------------------------------------------------------- 1 | import time 2 | import tbmatch.event_pb2 3 | import game_client 4 | 5 | def test_matchmaking_loop(): 6 | c1 = game_client.GameClient() 7 | c2 = game_client.GameClient() 8 | 9 | get_match_request = tbmatch.match_pb2.GetMatchRequest() 10 | c1.GetMatch(get_match_request) 11 | c2.GetMatch(get_match_request) 12 | 13 | # wait for at least 1 poll to happen 14 | time.sleep(6) 15 | 16 | assert check_client_events(c1) and check_client_events(c2) 17 | 18 | def check_client_events(client): 19 | found_waiting_event = False 20 | found_match_event = False 21 | for event in client.DoGetEvents(): 22 | if is_wait_event(event): 23 | found_waiting_event = True 24 | if is_match_event(event): 25 | found_match_event = True 26 | 27 | return found_waiting_event and found_match_event 28 | 29 | def is_wait_event(event): 30 | return event.type == tbmatch.event_pb2.Event.E_WAIT_MATCH_PROGRESS and event.wait_match_progress.status == tbmatch.event_pb2.WaitMatchProgressEvent.WAITING 31 | 32 | def is_match_event(event): 33 | return event.type == tbmatch.event_pb2.Event.E_WAIT_MATCH_PROGRESS and event.wait_match_progress.status == tbmatch.event_pb2.WaitMatchProgressEvent.MATCH 34 | 35 | if __name__ == '__main__': 36 | test_matchmaking_loop() --------------------------------------------------------------------------------