bacommon.servermanager
Functionality related to the server manager script.
1# Released under the MIT License. See LICENSE for details. 2# 3"""Functionality related to the server manager script.""" 4from __future__ import annotations 5 6from enum import Enum 7from dataclasses import field, dataclass 8from typing import TYPE_CHECKING, Any 9 10from efro.dataclassio import ioprepped 11 12if TYPE_CHECKING: 13 pass 14 15 16@ioprepped 17@dataclass 18class ServerConfig: 19 """Configuration for the server manager app (<appname>_server).""" 20 21 # Name of our server in the public parties list. 22 party_name: str = 'FFA' 23 24 # If True, your party will show up in the global public party list 25 # Otherwise it will still be joinable via LAN or connecting by IP address. 26 party_is_public: bool = True 27 28 # If True, all connecting clients will be authenticated through the master 29 # server to screen for fake account info. Generally this should always 30 # be enabled unless you are hosting on a LAN with no internet connection. 31 authenticate_clients: bool = True 32 33 # IDs of server admins. Server admins are not kickable through the default 34 # kick vote system and they are able to kick players without a vote. To get 35 # your account id, enter 'getaccountid' in settings->advanced->enter-code. 36 admins: list[str] = field(default_factory=list) 37 38 # Whether the default kick-voting system is enabled. 39 enable_default_kick_voting: bool = True 40 41 # UDP port to host on. Change this to work around firewalls or run multiple 42 # servers on one machine. 43 # 43210 is the default and the only port that will show up in the LAN 44 # browser tab. 45 port: int = 43210 46 47 # Max devices in the party. Note that this does *NOT* mean max players. 48 # Any device in the party can have more than one player on it if they have 49 # multiple controllers. Also, this number currently includes the server so 50 # generally make it 1 bigger than you need. 51 max_party_size: int = 6 52 53 # Max players that can join a session. If present this will override the 54 # session's preferred max_players. if a value below 0 is given player limit 55 # will be removed. 56 session_max_players_override: int | None = None 57 58 # Options here are 'ffa' (free-for-all), 'teams' and 'coop' (cooperative) 59 # This value is ignored if you supply a playlist_code (see below). 60 session_type: str = 'ffa' 61 62 # Playlist-code for teams or free-for-all mode sessions. 63 # To host your own custom playlists, use the 'share' functionality in the 64 # playlist editor in the regular version of the game. 65 # This will give you a numeric code you can enter here to host that 66 # playlist. 67 playlist_code: int | None = None 68 69 # Alternately, you can embed playlist data here instead of using codes. 70 # Make sure to set session_type to the correct type for the data here. 71 playlist_inline: list[dict[str, Any]] | None = None 72 73 # Whether to shuffle the playlist or play its games in designated order. 74 playlist_shuffle: bool = True 75 76 # If True, keeps team sizes equal by disallowing joining the largest team 77 # (teams mode only). 78 auto_balance_teams: bool = True 79 80 # The campaign used when in co-op session mode. 81 # Do print(ba.app.campaigns) to see available campaign names. 82 coop_campaign: str = 'Easy' 83 84 # The level name within the campaign used in co-op session mode. 85 # For campaign name FOO, do print(ba.app.campaigns['FOO'].levels) to see 86 # available level names. 87 coop_level: str = 'Onslaught Training' 88 89 # Whether to enable telnet access. 90 # IMPORTANT: This option is no longer available, as it was being used 91 # for exploits. Live access to the running server is still possible through 92 # the mgr.cmd() function in the server script. Run your server through 93 # tools such as 'screen' or 'tmux' and you can reconnect to it remotely 94 # over a secure ssh connection. 95 enable_telnet: bool = False 96 97 # Series length in teams mode (7 == 'best-of-7' series; a team must 98 # get 4 wins) 99 teams_series_length: int = 7 100 101 # Points to win in free-for-all mode (Points are awarded per game based on 102 # performance) 103 ffa_series_length: int = 24 104 105 # If you have a custom stats webpage for your server, you can use this 106 # to provide a convenient in-game link to it in the server-browser 107 # alongside the server name. 108 # if ${ACCOUNT} is present in the string, it will be replaced by the 109 # currently-signed-in account's id. To fetch info about an account, 110 # your back-end server can use the following url: 111 # https://legacy.ballistica.net/accountquery?id=ACCOUNT_ID_HERE 112 stats_url: str | None = None 113 114 # If present, the server subprocess will attempt to gracefully exit after 115 # this amount of time. A graceful exit can occur at the end of a series 116 # or other opportune time. Server-managers set to auto-restart (the 117 # default) will then spin up a fresh subprocess. This mechanism can be 118 # useful to clear out any memory leaks or other accumulated bad state 119 # in the server subprocess. 120 clean_exit_minutes: float | None = None 121 122 # If present, the server subprocess will shut down immediately after this 123 # amount of time. This can be useful as a fallback for clean_exit_time. 124 # The server manager will then spin up a fresh server subprocess if 125 # auto-restart is enabled (the default). 126 unclean_exit_minutes: float | None = None 127 128 # If present, the server subprocess will shut down immediately if this 129 # amount of time passes with no activity from any players. The server 130 # manager will then spin up a fresh server subprocess if auto-restart is 131 # enabled (the default). 132 idle_exit_minutes: float | None = None 133 134 # Should the tutorial be shown at the beginning of games? 135 show_tutorial: bool = False 136 137 # Team names (teams mode only). 138 team_names: tuple[str, str] | None = None 139 140 # Team colors (teams mode only). 141 team_colors: ( 142 tuple[tuple[float, float, float], tuple[float, float, float]] | None 143 ) = None 144 145 # Whether to enable the queue where players can line up before entering 146 # your server. Disabling this can be used as a workaround to deal with 147 # queue spamming attacks. 148 enable_queue: bool = True 149 150 # Protocol version we host with. Currently the default is 33 which 151 # still allows older 1.4 game clients to connect. Explicitly setting 152 # to 35 no longer allows those clients but adds/fixes a few things 153 # such as making camera shake properly work in net games. 154 protocol_version: int | None = None 155 156 # (internal) stress-testing mode. 157 stress_test_players: int | None = None 158 159 # How many seconds individual players from a given account must wait 160 # before rejoining the game. This can help suppress exploits 161 # involving leaving and rejoining or switching teams rapidly. 162 player_rejoin_cooldown: float = 10.0 163 164 165# NOTE: as much as possible, communication from the server-manager to the 166# child-process should go through these and not ad-hoc Python string commands 167# since this way is type safe. 168class ServerCommand: 169 """Base class for commands that can be sent to the server.""" 170 171 172@dataclass 173class StartServerModeCommand(ServerCommand): 174 """Tells the app to switch into 'server' mode.""" 175 176 config: ServerConfig 177 178 179class ShutdownReason(Enum): 180 """Reason a server is shutting down.""" 181 182 NONE = 'none' 183 RESTARTING = 'restarting' 184 185 186@dataclass 187class ShutdownCommand(ServerCommand): 188 """Tells the server to shut down.""" 189 190 reason: ShutdownReason 191 immediate: bool 192 193 194@dataclass 195class ChatMessageCommand(ServerCommand): 196 """Chat message from the server.""" 197 198 message: str 199 clients: list[int] | None 200 201 202@dataclass 203class ScreenMessageCommand(ServerCommand): 204 """Screen-message from the server.""" 205 206 message: str 207 color: tuple[float, float, float] | None 208 clients: list[int] | None 209 210 211@dataclass 212class ClientListCommand(ServerCommand): 213 """Print a list of clients.""" 214 215 216@dataclass 217class KickCommand(ServerCommand): 218 """Kick a client.""" 219 220 client_id: int 221 ban_time: int | None
@ioprepped
@dataclass
class
ServerConfig:
17@ioprepped 18@dataclass 19class ServerConfig: 20 """Configuration for the server manager app (<appname>_server).""" 21 22 # Name of our server in the public parties list. 23 party_name: str = 'FFA' 24 25 # If True, your party will show up in the global public party list 26 # Otherwise it will still be joinable via LAN or connecting by IP address. 27 party_is_public: bool = True 28 29 # If True, all connecting clients will be authenticated through the master 30 # server to screen for fake account info. Generally this should always 31 # be enabled unless you are hosting on a LAN with no internet connection. 32 authenticate_clients: bool = True 33 34 # IDs of server admins. Server admins are not kickable through the default 35 # kick vote system and they are able to kick players without a vote. To get 36 # your account id, enter 'getaccountid' in settings->advanced->enter-code. 37 admins: list[str] = field(default_factory=list) 38 39 # Whether the default kick-voting system is enabled. 40 enable_default_kick_voting: bool = True 41 42 # UDP port to host on. Change this to work around firewalls or run multiple 43 # servers on one machine. 44 # 43210 is the default and the only port that will show up in the LAN 45 # browser tab. 46 port: int = 43210 47 48 # Max devices in the party. Note that this does *NOT* mean max players. 49 # Any device in the party can have more than one player on it if they have 50 # multiple controllers. Also, this number currently includes the server so 51 # generally make it 1 bigger than you need. 52 max_party_size: int = 6 53 54 # Max players that can join a session. If present this will override the 55 # session's preferred max_players. if a value below 0 is given player limit 56 # will be removed. 57 session_max_players_override: int | None = None 58 59 # Options here are 'ffa' (free-for-all), 'teams' and 'coop' (cooperative) 60 # This value is ignored if you supply a playlist_code (see below). 61 session_type: str = 'ffa' 62 63 # Playlist-code for teams or free-for-all mode sessions. 64 # To host your own custom playlists, use the 'share' functionality in the 65 # playlist editor in the regular version of the game. 66 # This will give you a numeric code you can enter here to host that 67 # playlist. 68 playlist_code: int | None = None 69 70 # Alternately, you can embed playlist data here instead of using codes. 71 # Make sure to set session_type to the correct type for the data here. 72 playlist_inline: list[dict[str, Any]] | None = None 73 74 # Whether to shuffle the playlist or play its games in designated order. 75 playlist_shuffle: bool = True 76 77 # If True, keeps team sizes equal by disallowing joining the largest team 78 # (teams mode only). 79 auto_balance_teams: bool = True 80 81 # The campaign used when in co-op session mode. 82 # Do print(ba.app.campaigns) to see available campaign names. 83 coop_campaign: str = 'Easy' 84 85 # The level name within the campaign used in co-op session mode. 86 # For campaign name FOO, do print(ba.app.campaigns['FOO'].levels) to see 87 # available level names. 88 coop_level: str = 'Onslaught Training' 89 90 # Whether to enable telnet access. 91 # IMPORTANT: This option is no longer available, as it was being used 92 # for exploits. Live access to the running server is still possible through 93 # the mgr.cmd() function in the server script. Run your server through 94 # tools such as 'screen' or 'tmux' and you can reconnect to it remotely 95 # over a secure ssh connection. 96 enable_telnet: bool = False 97 98 # Series length in teams mode (7 == 'best-of-7' series; a team must 99 # get 4 wins) 100 teams_series_length: int = 7 101 102 # Points to win in free-for-all mode (Points are awarded per game based on 103 # performance) 104 ffa_series_length: int = 24 105 106 # If you have a custom stats webpage for your server, you can use this 107 # to provide a convenient in-game link to it in the server-browser 108 # alongside the server name. 109 # if ${ACCOUNT} is present in the string, it will be replaced by the 110 # currently-signed-in account's id. To fetch info about an account, 111 # your back-end server can use the following url: 112 # https://legacy.ballistica.net/accountquery?id=ACCOUNT_ID_HERE 113 stats_url: str | None = None 114 115 # If present, the server subprocess will attempt to gracefully exit after 116 # this amount of time. A graceful exit can occur at the end of a series 117 # or other opportune time. Server-managers set to auto-restart (the 118 # default) will then spin up a fresh subprocess. This mechanism can be 119 # useful to clear out any memory leaks or other accumulated bad state 120 # in the server subprocess. 121 clean_exit_minutes: float | None = None 122 123 # If present, the server subprocess will shut down immediately after this 124 # amount of time. This can be useful as a fallback for clean_exit_time. 125 # The server manager will then spin up a fresh server subprocess if 126 # auto-restart is enabled (the default). 127 unclean_exit_minutes: float | None = None 128 129 # If present, the server subprocess will shut down immediately if this 130 # amount of time passes with no activity from any players. The server 131 # manager will then spin up a fresh server subprocess if auto-restart is 132 # enabled (the default). 133 idle_exit_minutes: float | None = None 134 135 # Should the tutorial be shown at the beginning of games? 136 show_tutorial: bool = False 137 138 # Team names (teams mode only). 139 team_names: tuple[str, str] | None = None 140 141 # Team colors (teams mode only). 142 team_colors: ( 143 tuple[tuple[float, float, float], tuple[float, float, float]] | None 144 ) = None 145 146 # Whether to enable the queue where players can line up before entering 147 # your server. Disabling this can be used as a workaround to deal with 148 # queue spamming attacks. 149 enable_queue: bool = True 150 151 # Protocol version we host with. Currently the default is 33 which 152 # still allows older 1.4 game clients to connect. Explicitly setting 153 # to 35 no longer allows those clients but adds/fixes a few things 154 # such as making camera shake properly work in net games. 155 protocol_version: int | None = None 156 157 # (internal) stress-testing mode. 158 stress_test_players: int | None = None 159 160 # How many seconds individual players from a given account must wait 161 # before rejoining the game. This can help suppress exploits 162 # involving leaving and rejoining or switching teams rapidly. 163 player_rejoin_cooldown: float = 10.0
Configuration for the server manager app (
ServerConfig( party_name: str = 'FFA', party_is_public: bool = True, authenticate_clients: bool = True, admins: list[str] = <factory>, enable_default_kick_voting: bool = True, port: int = 43210, max_party_size: int = 6, session_max_players_override: int | None = None, session_type: str = 'ffa', playlist_code: int | None = None, playlist_inline: list[dict[str, typing.Any]] | None = None, playlist_shuffle: bool = True, auto_balance_teams: bool = True, coop_campaign: str = 'Easy', coop_level: str = 'Onslaught Training', enable_telnet: bool = False, teams_series_length: int = 7, ffa_series_length: int = 24, stats_url: str | None = None, clean_exit_minutes: float | None = None, unclean_exit_minutes: float | None = None, idle_exit_minutes: float | None = None, show_tutorial: bool = False, team_names: tuple[str, str] | None = None, team_colors: tuple[tuple[float, float, float], tuple[float, float, float]] | None = None, enable_queue: bool = True, protocol_version: int | None = None, stress_test_players: int | None = None, player_rejoin_cooldown: float = 10.0)
class
ServerCommand:
Base class for commands that can be sent to the server.
173@dataclass 174class StartServerModeCommand(ServerCommand): 175 """Tells the app to switch into 'server' mode.""" 176 177 config: ServerConfig
Tells the app to switch into 'server' mode.
StartServerModeCommand(config: ServerConfig)
config: ServerConfig
class
ShutdownReason(enum.Enum):
180class ShutdownReason(Enum): 181 """Reason a server is shutting down.""" 182 183 NONE = 'none' 184 RESTARTING = 'restarting'
Reason a server is shutting down.
NONE =
<ShutdownReason.NONE: 'none'>
RESTARTING =
<ShutdownReason.RESTARTING: 'restarting'>
Inherited Members
- enum.Enum
- name
- value
187@dataclass 188class ShutdownCommand(ServerCommand): 189 """Tells the server to shut down.""" 190 191 reason: ShutdownReason 192 immediate: bool
Tells the server to shut down.
ShutdownCommand(reason: ShutdownReason, immediate: bool)
reason: ShutdownReason
195@dataclass 196class ChatMessageCommand(ServerCommand): 197 """Chat message from the server.""" 198 199 message: str 200 clients: list[int] | None
Chat message from the server.
203@dataclass 204class ScreenMessageCommand(ServerCommand): 205 """Screen-message from the server.""" 206 207 message: str 208 color: tuple[float, float, float] | None 209 clients: list[int] | None
Screen-message from the server.
Print a list of clients.
217@dataclass 218class KickCommand(ServerCommand): 219 """Kick a client.""" 220 221 client_id: int 222 ban_time: int | None
Kick a client.