Startup and Initialization¶
Startup and initialization of the server is currently poorly defined. The phases are roughly as follows:
Server Initialization¶
Config loading¶
The config and command-line arguments are loaded first, because they are required to proceed further.
Module initialization¶
The asyncio and twisted reactors are initialized. The files defining the game
logic in piqueserver/
and pyspades/
are imported via
piqueserver.server
.
Script Loading¶
The way the script extension system works means that scripts can only be loaded
before the two main classes, Connection
and Protocol
, are instantiated.
In this phase, the scripts defined in the config are loaded. This consists of four sub-phases:
- All scripts defined under
scripts
in the config are loaded, either from the config directory, or fromsys.path
. apply_script
is called on all scripts, in the order that they are declared in the config.- If the game mode is not
"ctf"
or"tc"
for which the logic is built-in, the game mode is imported as a script. apply_script
is called on the game mode script
Protocol Initialization¶
The Protocol
class created in the previous step is instantiated, calling
it’s __init__
method. This does the following:
- Configure logging
- Read configuration such as map rotation, team names, time limits, win conditions
- Load the ban list
- Import and initialize auxilliary functionality like SSH, status server, banpublish/bansubscribe, command console.
- Initialize user configuration
- Resolve the filenames of the map rotation
- Create the
enet.Host
object, listening on the socket. - Create the
Team
andWorld
objects. - Call
protocol.on_advance(map_name)
with the name of the map that is about to be loaded. - Schedule future tasks such as the update check, ban vaccum, map loading, annoucements, game end, update loop and master server connection.
Config Validation¶
The configuration is validated for any errors or unused keys.
Event Loop Startup¶
Control is passed to the Twisted/AsyncIO event loop, which drives everything from here on out.
Map Loading¶
The map is loaded or generated in a background thread. This is done to prevent connections from timing out on slow hardware.
Once complete, protocol.on_map_change
, and Team.initialize()
are
called.
Map Change¶
Map change is triggered by protocol.advance_rotation
This:
- Calls
protocol.on_advance
with the next map. - If a message was specified, print it and wait 10 seconds.
- Load or generate the next map.
- Call
protocol.on_map_leave
- Call
map_info.on_map_leave
- Replace the loaded map with the new one.
- Call
Team.initialize
Game Mode Initialization¶
Games are defined by the game mode. They usually represent a period of time where two teams compete for points and end with one team winning or losing, or some other condition such as a time limit being hit.
There is currently no hook for when the game starts. However, existing modes
assume it starts with the on_map_change
call.
When a game ends, the game mode will call on_game_end
to notify any other
scripts and possibly trigger a map change.
Player Lifecycle¶
Players join and play the game in a number of phases and states:
Connection Start¶
An ENet connection handshake is performed. protocol.on_connect
is called
once complete.
This creates a new Connection
object, on which connection.on_connect
is
called. This decides if the player should be allowed to join based on
information such as the player count, IP or protocol version.
Map Transfer¶
The map is generated and sent to the player. A snapshot of current player state is saved, on top of which changes occurring during map transfer will be layered. A handshake is started to identify the client version and available protocol extensions.
On map transfer completion, connection.on_join
will be called.
Limbo¶
After map transfer, players are in the “Limbo” state. They are in this state
until the client sends an ExistingPlayer
packet containing the chosen name
and team ID.
Joined¶
Once a team is selected, connection.on_team_join
is called to validate the
team choice. connection.on_login
is then called to validate the chosen
name.
Spawned¶
When a player is about to be spawned, connection.on_spawn_location
is
called to allow overriding the position. connection.on_spawn
is called when
the spawn is performed.
Dead¶
When a player dies, connection.on_kill
is called.