Developer Guide
Table of Contents
Architecture
Jetrix is designed around the concept of independant threads communicating asynchronously with messages. The three main entities are :- client - it listens for messages sent by the TetriNET client, it's coupled to a protocol that will translate incomming messages into server messages. The messages are then forwarded to the channel occupied by the client. There is one client thread for every client connected to the server.
- channel - this is a game channel accepting playing and non-playing (spectators) clients. Messages go through a set of filters that can transform, delete or add messages to customize the behaviour of the channel. After a processing the game message are sent back to the clients. Non game related messages are forwarded to the main server thread.
- server - it handles system commands such as shutdown and slash commands typed by clients (/who, /list, /join...)

Protocols
TetriNET 1.13
External references:TetriNET 1.14
The version 1.14 of the TetriNET protocol is an extension introduced by Olivier Vidal in August 2003 allowing players to get the same sequence of blocks. This protocol was first integrated to a server in Jetrix 0.1.3. It works by adding an extra parameter at the end of the newgame command:TetriNET Pseudo Random Number Generator
The original TetriNET client designed by St0rmCat uses the standard random function available in Delphi. It's basically a linear congruential generator, the recursive sequence is defined by:
with the following parameters:
The seed s0 is determined by the last parameter of the newgame message. This sequence gives pseudo random integers between 0 and 232 - 1, but an integer in the [0, 100[ range is required for selecting the block, and an integer in the [0, 4[ range for the orientation of the block. So the result is multiplied by the length of the range and divided by 232:
Where L=100 to select the block and L=4 to select its orientation. The sequence sn is evaluated twice to determine each block, first for the block and then for its orientation.
The following table defines the orientations for each type of block :
Orientation | 0 | 1 | 2 | 3 |
---|---|---|---|---|
Line | ![]() |
![]() |
![]() |
![]() |
Square | ![]() |
![]() |
![]() |
![]() |
Left L | ![]() |
![]() |
![]() |
![]() |
Right L | ![]() |
![]() |
![]() |
![]() |
Left Z | ![]() |
![]() |
![]() |
![]() |
Right Z | ![]() |
![]() |
![]() |
![]() |
Half Cross | ![]() |
![]() |
![]() |
![]() |
Examples
Here are some examples of block sequences for different seed values. This might help client programmers to validate their random number generator. The frequencies for these examples are :
Block | Frequency | |
---|---|---|
1 | ![]() |
15 % |
2 | ![]() |
15 % |
3 | ![]() |
14 % |
4 | ![]() |
14 % |
5 | ![]() |
14 % |
6 | ![]() |
14 % |
7 | ![]() |
14 % |
The corresponding frequency table is :
For this example the value of the seed is 0.
Blocks | Sequence | Index | Result |
---|---|---|---|
Block 1 | s1 = 0x00000001 | 0 | ![]() |
s2 = 0x08088406 | 0 | ||
Block 2 | s3 = 0xDC6DAC1F | 86 | ![]() |
s4 = 0x33DC589C | 0 | ||
Block 3 | s5 = 0x45DE2B0D | 27 | ![]() |
s6 = 0xABF18B42 | 2 | ||
Block 4 | s7 = 0x5195C04B | 31 | ![]() |
s8 = 0x296B6D78 | 0 |
Here are the 10 first blocks for different seed values:
Seed | Sequence |
---|---|
0x00000000 |
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() |
0xAABBCCDD |
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() |
0x12345678 |
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() |
TetriFast
TetriFast is a modified TetriNET client that removes the delay between the block fall and the showing of next block. The TetriFast protocol is quite similar to the TetriNET protocol, it has just been slightly modified to prevent TetriFast clients to connect and cheat on regular TetriNET servers.Login
TetriFast clients connect on the same port as TetriNET client, that's 31457. The only difference is the initialization string, a TetriFast client will use tetrifaster instead of tetrisstart.Messages
The TetriFast protocol use two different messages :- the message playernum has been changed to )#)(!@(*3
- the message newgame has been changed to *******
TSpec
Query
Tetrix first introduced a query protocol to get easily a list of players and channels on TetriNET servers. This protocol consists in 4 commands : playerquery, listchan, listuser and version. These commands are sent through the standard tetrinet port 31457 (TCP) and must be terminated by the 0xFF character. The line terminator for the response is a line feed 0x0A.name the name of the channel (without the leading #) description the description or topic of the channel playernum the number of players in this channel playermax the maximum number of players priority the priority of the channel status the game state (1: stopped, 2: started, 3: paused)The response is terminated by the string "+OK".
nick the name of the player team the team name of the player version the version of the client used, usually "1.13" slot the slot used in the channel (1-6) state the state of the player (0: not playing, 1: playing, 2: lost) auth the authentication level (1: normal, 2: channel operator, 3: operator) channel the name of the channelThe response is terminated by the string "+OK".
Customization
Commands
Jetrix has been designed to allow the addition of new commands like /who or /list easily. This section will show you the steps to follow in order to create a simple /hello command that will just display the message "Hello World!".Write the command
Every command is represented by a Java class implementing the Command interface. Let's create our class, HelloCommand, it'll be based on the AbstractCommand class implementing part of the Command interface methods :Compile the command
Save the code above in a HelloCommand.java file and copy the jetrix.jar file in the same directory (this jar is in the jetrix/lib directory of the jetrix distribution). Then compile the command with :Deploy the command
To make your class available to Jetrix just copy it into the jetrix/lib directory. Starting with Jetrix 0.1.1 any .class or .jar file in this directory is automatically loaded at startup. Then you need to declare your command by editing the config.xml file, under the <commands> element just add this :Test the command !
Now we are ready to try the command ! Start Jetrix and log into the server. On typing /help you'll notice that the new command is automatically listed :

Filters
One feature of Jetrix is to make it easy to change the behaviour of the server by modifying its response to the messages sent by the clients. This is done by implementing filters. A filter is a small class that can watch and modify all messages sent to a channel. It can serve various purposes, like modifying the game, displaying additional informations at the end of the game, blocking a flood of messages from a player, or implementing a bot responding to the players.
This guide will show you how to create a simple game modification using a filter. In this mod the first player to complete 7 tetris win.
Write the filter
Filters extend the base class MessageFilter, this class defines a process(Message m, List out) method that must be overridden to implement the behaviour of the filter. A higher level filter GenericFilter with process methods for all message types is provided, we will use it for our filter.
Let's create our class :
Every channel has a chain of filters, the messages go through the filters before reaching the channel. A filter has the responsability to decide what messages the next filter in the chain will get. The filter could simply eat the message and forward nothing to the next filter, pass the message unchanged, transform the message or generate several messages. This is determined by what is put in the out list of the process method.
Our first onMessage method just put the message in the out list with out.add(m), this is mandatory or the game would never start.
The next onMessage method will watch FourLinesAddedMessage messages, this is the message sent when a player performs a tetris. This method will increase the tetris count of the player and test if the limit of 7 tetris has been reached. If so the game is stopped and the winner is announced.
Our filter is complete, now we have to compile and deploy it in the server.
Compile the filter
Save the code above in a TetrisFilter.java file and copy the jetrix.jar file in the same directory (this jar is in the jetrix/lib directory of the jetrix distribution). Then compile the command with :
Deploy the filter
To make the filter available to Jetrix just copy the TetrisFilter.class file into the jetrix/lib directory. Then you need to declare your filter by editing the config.xml file, under the <filter-definitions> element add this line:
Then the filter has to be associated to a channel, we will create a new channel dedicated to this mod :
You can also declare a global filter under the default-filters element that will be applied to all channels.