Getting Started with Trosnoth Development
The purpose of this page is to help new Trosnoth coders begin to understand the inner workings of Trosnoth.
Trosnoth currently uses Twisted to provide its main loop. You don't need to understand everything about Twisted to work on Trosnoth, but there are a few things worth noting:
- Twisted is an asynchronous framework. This means that you can start a task and say, "When this finishes, call this function." To represent long-running tasks, Twisted uses
twisted.internet.defer. To indicate what should happen when a task completes or fails, you use
- Twisted doesn't use threading for its multitasking, but instead uses cooperative multitasking. The up-side of this approach is that you don't have to worry about locks and semaphores: you're always guaranteed that your code will run atomically and uninterrupted. The down-side is that chunks of code that take a long time will block the whole process. Therefore never use
time.sleep(), and avoid blocking I/O calls (try to use Twisted's asynchronous alternative). If you have to perform complex calculations, try to find ways to break them up.pause
- Twisted provides time-related functions like
twisted.internet.task.LoopingCall(). These are useful if you want your timing to be based on real time (e.g. for animations in the UI). But what you care about is game time, please use
- Throughout the code, you might see functions decorated with
@defer.inlineCallbacks. When reading these functions, please note that:
defer.returnValue(...)will return from the function
yield dwill interrupt the function and return control to the Twisted reactor until the
- calling a function decorated as
inlineCallbackswill return a
Here are some terms you may come across in the Trosnoth code.
- world or universe: this encapsulates the state of an entire Trosnoth game, including details of the current map, and locations of all units
- unit: anything that moves, including players, shots, grenades and stars
If you're planning to delve deep into the Trosnoth internals, you may come across some of the following interfaces. Here's a quick explanation.
Because Trosnoth operates over the network, information about player actions and changes in universe state need to be able to be serialised. To do this, we use
Message classes (defined in
trosnoth/messages/*.py). Messages typically subclass one of:
AgentRequestif it represents a request being sent from a client to the server
ServerCommandif it represents a command being sent from the server to one or all clients
ClientCommandif the same message class will be used for both the client to server request, and the subsequent command that informs all clients that this event has happened
ServerResponseif it represents a response that is sent back to the client who send an
AgentRequest, usually to inform them why their request was denied
If you create a new
Message class, it is important to add it to
trosnoth.network.client.clientMsg if it's ever going to be received by a client, and to
trosnoth.network.server.serverMsg if it's ever going to be received by a server. This tells the client and server how to decode this message kind.
Game / Agents
Game represents a Trosnoth game. It might be a
LocalGame, which is being controlled within this process, or a
RemoteGame, which is talking over the network to get its instructions.
Game may have a number of
Agents connected to it. An
Agent represents something or someone who wants to receive game events, and who may want to control a player. For instance, the Trosnoth user interface is represented by a subclass of
Agent, which sends requests to the game whenever the user presses a key, clicks to shoot, etc.. Trosnoth bots communicate with the the game using the
AIAgent class, which is also a subclass of
Agent. Within a Trosnoth server process, an
Agent subclass is also used to represent players over the network.
Regardless of the class of
Agent, the same interface is used to send messages between them. This interface is defined in the base