Skip to content
This repository has been archived by the owner on Jun 23, 2023. It is now read-only.

Architecture

infogulch edited this page Nov 3, 2012 · 1 revision

Internal architecture overview

Layout

At this time there are three interesting directories with respect to the main server code:

/pyspades contains the protocol, server, and format implementations for AoS.

/feature_server adds user-facing features and UI to load maps, perform higher-level gameplay tasks, and administrate.

/feature_server/scripts contains additional commands and functionality.

Where possible new features should be in scripts, then in feature_server.

/pyspades/server.py

This is the core server implementation. It is based around Twisted. Look here for:

  • The "player connection", which is associated with players and how they spawn, move, fire, die, etc.
  • The basic definitions for teams and flags.
  • Some anti-speedhacking code
  • Stub functions exposed to feature_server and scripts

/feature_server/run.py

This is the feature_server implementation, and is what end-users actually run. It inherits from server.py, and adds:

  • Map loading and configuration files
  • The concept of an admin and administration commands
  • Hooks for remote admin access(irc, ssh, etc.)
  • Stubs and variables needed for additional gameplay features(e.g. god mode, killstreak)
  • A loader for additional scripts

/feature_server/scripts

This directory exposes modding to users. It is scanned at runtime, and each script inherits the previous one's classes in a chain to composite the final server class.

Because of this inheritance-heavy behavior, trying to use a script to overwrite or add to functions defined in server.py or run.py is likely to destroy the original function. Scripts are best used to add completely original commands and configuration options.

Maps can also have a apply_script function defined.

Connection/Protocol

In pyspades, the server protocol is basically the base server: it handles UDP sending/receiving, players, configuration, etc. Usually, if you, for example, want to send a chat message or a packet to all the players, you refer to the protocol's methods.

The connection instances represent individual players. It has position/orientation/id/etc. attributes and methods, specific to every player.

Writing your own script

Scripts currently follow this conventional format:

  1. import necessary definitions
  2. define any new commands
  3. subclass Connection and Protocol in apply_script to change the behavior of these, and incorporate any new configuration options

A simple welcome-message script can look like this (taken from scripts/welcome.py):

def apply_script(protocol, connection, config):
    welcomes = config.get('welcomes', {})
    class EnterConnection(connection):
        def on_login(self, name):
            if name in welcomes:
                self.protocol.send_chat(welcomes[name])
            connection.on_login(self, name)
    return protocol, EnterConnection

API

General

The Connection and Protocol classes have event handler methods that are called every time the user does something or when another player is doing something to it. If you want to change or add the behavior to an event, you do your own actions in the handler (and if you want to block the original event, return False). Not all events can be blocked.

Connection

The Connection class event handlers are defined here: http://code.google.com/p/pyspades/source/browse/pyspades/server.py#952

Protocol

The Protocol class event handlers are defined here: http://code.google.com/p/pyspades/source/browse/pyspades/server.py#1537

Dealing with the protocol

The Ace of Spades protocol has a lot of limitations, and some not-so-nice hacks are done to implement some features. Here, the protocol and its implementation is explained.

There are 2 types of packets (called 'loaders' in pyspades):

  • Non-container packets (used for simple stuff like pinging, ACK, etc.)
  • Contained packets (used for pretty much all game-related stuff)

The contained packet loaders can be found here:

Client-to-server: http://code.google.com/p/pyspades/source/browse/pyspades/clientloaders.pyx

Server-to-client: http://code.google.com/p/pyspades/source/browse/pyspades/serverloaders.pyx

protocol.py/server.py has singleton instances of these loaders, and they should be used unless inappropriate.

There are 2 important type of contained packets:

  • Orientation-related contained packets
  • General contained packets

This distinction is made because orientation packets aren't guaranteed to arrive, but still have an additional sequence value attached (to prevent out-of-order packets).

Contained packets are sent using the send_contained method of the Connection class (to send contained packets to individual players) and Protocol class (to send contained packets to a lot of players).