This is a mod for teeworlds-0.5.x. It doesn't support the latest teeworlds version, 0.6.0, and I'm not sure if it ever will. I don't have the inspiration to work on this project right now.
Teeworlds is an awesome 2D multiplayer shooter. It features funny cartoon-like graphics, sophisticated movement dynamics, and wide community one can find interesting to participate in. Teeworlds has three standard game types similar to those present in any first-person multiplayer shooter: deathmatch (dm), team deathmatch (tdm) and capture the flag (ctf). There also are plenty of modifications (mods), some of which change the gameplay quite dramatically. This site is devoted to one of such mods.
The situation with modifications in teeworlds world is rather stressed. There is no mod support in the code, so teeworlds design does not provide simple API to create mods. Most of mods has to significantly change the code, and hence every single mod is a standalone full-blown fork. This makes it difficult to combine mods and often the same features are implemented separately by different people in different ways for different mods. The mod described below is not an exception. Fortunately, there was a quite successful attempt to combine several mods into one made by Tom. The result features standard gametypes as well as instagib deathmatch (idm), team deathmatch (itdm), capture the flag (ictf), freeze (ifreeze), freeze capture the flag (ifrzctf) and flagball (ifb). Since this mod had most of my favourite gametypes, I decided to take it as a start and develop it further to implement what I wanted.
The idea for the mod arised when I tried to make the server more configurable.
I wanted to make a random maprotation, but couldn't even make a map to appear in
rotation twice. While this particular task could easily be solved with a small
patch, I thought that it would be a nice idea to have a full-scaled programming
language for configuration files. I didn't want to reinvent the bicycle trying
to extend the available teeworlds parser or make my own language even if it
would be backward-compatible with existing server configurations. Instead, I've
chosen Perl, mainly because I wanted to study its internals and figure out how
to embed it in programs. Of course, there were a lot of other choices. I've
heard that Lua will be used for the same or similar task in the upcoming
teeworlds 0.6. Indeed, it seems to be the most suitable language despite that I
don't like some of its features: it's small, fast, and easy to embed. Among
other choices were Python, Tcl and Scheme. Technically, it doesn't really matter
which language to use for this task as long as it is dynamic, has some form of
eval
function and can call C functions (and if I wouldn't want to have
REPL, only the latter requirement would matter). In any case it is a
bad idea to often call functions from embedded language because of all the
penalties foreign function calls imply, so the speed of the embedded language
doesn't really matter. Other requirements, such as memory usage and few
dependencies, exclude several major languages, but still plenty of scripting
languages are left. The choice between them is more likely a matter of desire.
As long as you have a sane API, to implement support for another language is
quite easy. Bad thing is that Teeworlds doesn't have one, so I had to simulate
it with some hacks.
Anyway, the major points of this mod are:
do
function. Also, remember that Perl is quite good for
one-liners.However, there also are major drawbacks. First, the server administrator is better to know Perl well. If you want something similar to my configuration, you can use examples provided below, but you should understand what you are doing. Second, the server is unlikely to compile under non-POSIX environment, notably in Windows. I haven't tried though. Third, current approach to the definition of command sets for user groups requires writing quite a lot of code. Finally, consider carefully security issues described in chapter REPL.
The mod is accessible via the following link:
You may get the current development version from git with the following command:
git clone git://jini-zh.org/teeworlds-perl
Follow these rules to compile the server. In addition to programs
listed there, you will need perl of version not older than 5.10.1 compiled with
shared libraries (libperl.so). Note that you need bam-0.2.0, not the latest
version of bam. As is with Tom's sources, the client cannot compile in this mod.
Issue bam server_release
in the final stage of the compilation.
An example configuration is provided in doc/example.pl.
In addition to providing programming language for configuration files, I decided to implement a REPL (read-evaluate-print loop) for rcon. It makes some form of terminal emulation in rcon and gives the administrator complete control over the server (well, the level of control the server API provides). Note that it is easy to extend this control to get access to things unrelated to teeworlds, for example to get a shell on the machine running teeworlds with the rights of the user running teeworlds. It is easy enough to be done manually, right in the rcon command line, so be sure to pick strong root password if you choose to activate it. Of course, there still exists the possibily of root password to be compromised. To minimize possible damage, you may choose between two security measures.
The best way to protect your system is to run the server in chroot jail
under unprivileged user. Make a directory, for example /srv/teeworlds/
, and put
there files you want to be available to the server. In particular, make a maps
directory inside /srv/teeworlds
and put maps there. Then write a call to
init
function in the end of your configuration file:
init '/srv/teeworlds', 1000, 1001;
When init
function is executed, the server chroots to directory
passed as the first parameter, /srv/teeworlds
in this example, and changes its
group identity and user identity to those specified by the
second (1000) and the third (1001) parameters correspondingly. init
also copies
system files neccessary for teeworlds to work properly. These are
/etc/resolv.conf
, and libraries /lib/libnss_dns.so.2
,
/lib/libnss_files.so.2
, /lib/libresolv.so.2
. Teeworlds uses these files to
establish connection to master servers. Since chroot
call
can be made only by superuser, the server has to be run as root.
If you don't want to bother root every time you want to run the server, you may consider schroot approach.
Often it is desirable to have several types of users administering the server. For example, there could be administrators with full access to the server commands, and referees who should be able to kick any user by rcon, but should not be able to shutdown the server. With REPL this desire becomes even stronger, because anyone who has access to the REPL, has access to the shell on the server host system and can do a lot of nasty things. In this mod a group concept is introduced to provide control for who can execute which functions.
Available groups are described by a global variable %groups
. It is a hash
which keys are group names and values are references to hashes with the
following keys:
Available functions are described by a global variable %commands
. It also is a
hash which keys are command names and values are references to hashes with the
following keys:
Whenever a user enters a password in order to authenticate in rcon, the server searches for a group with the corresponding password field and assignes its group id to the user. The user can execute only those functions whose group ids match their group id. Two group ids match if their binary and is not zero:
$gid1 & $gid2 != 0
Let's consider a simple example. Suppose that there are two groups:
%groups = (
admin => {
gid => 2,
password => 'admin password'
},
user => {
gid => 1,
password => 'user password'
}
);
and three functions:
%commands = (
kick => {
gid => 2,
sub => sub { ... },
},
vote => {
gid => 1 | 2,
sub => sub { ... },
},
complain => {
gid => 1,
sub => sub { ... },
}
);
In this example kick
is executable only by administrator, complain
—only by
user and vote
by both.
Users can belong to several groups at once either if the gid of the group they
are logged into matches gids of other groups or if they log into several groups
by means of the su
command.
There are two special group ids. Group id equal to zero designates an
unregistered player. Obviously such players cannot execute rcon commands, but
they can call predefined votes, so you may have to take them into account. Group
id equal to $root_id
which is ~0
—the exact opposite of zero—is an id for
the root group. Root has direct access to REPL and executes commands in Perl
syntax. Note that you have to define the root group in order to turn it on.
Be careful when assigning group numbers. For the simplest case of not-intersecting groups these should be powers of two (or, strictly speaking, a set of orthogonal binary vectors). In general the more permissions the group has, the bigger should be its group id.
For a command to be votable, it has to have a vgid
entry in the %commands
hash. Naturally, only users whose gid match vgid
have permissions to run the
vote. However, vgid
is not checked when a predefined vote is called, that is a
vote added by addvote
to the list of votes the client can call without
authorizing in rcon.
Often it is desirable to check command parameters to prevent absurd or
undesirable votes. This can be done with entry vcheck
which should be a
reference to a function that is passed command parameters and dies if it doesn't
like them. Here is an example:
%commands = (
sv_map => {
gid => 0, # this command cannot be executed
sub => \&sv_map,
vgid => 1,
vcheck => sub {
# $_[0] is voting client id
# $_[1] is voting client group id
# $_[2] is the command parameters
die "ctf4 is not allowed\n" if $_[2] =~ /^ *ctf4 *$/
}
}
)
Unfortunately, such an approach encourages duplication of the code that parses and checks command parameters. Perhaps later it will be changed.
more
is a simple text viewer that allows the player to read a big bunch
of text in the limited space of rcon. Being fed a pack of strings, more
splits
them so that they look nice and prints a screenful of text at once until all are
done. To proceed to the next screen, user has to send any command except those
predefined:
b
: back one screen..
: redisplay the current screen./regular expression
: search for a pattern.?regular expression
: search for a pattern in backwards direction.n
: repeat last search.N
: repeat last search in the other direction.g number
: go to a given line.q
: quit.h
: print help message.Unfortunately, teeworlds rcon was not supposed to be used as a terminal, so it
lacks some features. Terminals use mono-spaced font and there is a way to find
out the width and the height of the terminal at runtime. None of these is true
for teeworlds, so to control the output two variables, $more_rows
and
$more_cols
, were defined. The former is the number of lines more
prints per
screen. It seems that rcon height is fixed in the code to be 25. $more_cols
is a maximum number of characters to print per line. Its value is hand-picked
so that the common line of English text looks fine. Obviously it will fail for
a line like "WWWWW..." because "W" characters are rather wide, but for common
text it should do. Feel free to complain.
Teeworlds uses one huge struct
to hold all configuration variables.
Values of these variables are used throughout the code everywhere, so change
of the variable value produces immediate effect. Only two types are supported:
int
and char*
(string). In the configuration file variables can be accessed
via %config
hash. Keys of the hash are variable names and values are
references to Perl objects implementing three methods:
get
: returns the value of the variable.set
: sets the value of the variable.help
: returns a string describing the variable.In addition integer variables have the following methods:
min
: minimum allowed value.max
: maximum allowed value.An example:
# Change gametype to ctf
$config{'sv_gametype'}->set('ctf');
# Get the scorelimit
$scorelimit = $config{'sv_scorelimit'}->get();
For convenience, functions with the same names as variables are defined. They
check if they are being passed a value and execute set
or get
method
correspondingly. The example can be written as follows:
sv_gametype('ctf');
$scorelimit = sv_scorelimit();
Feel free to redefine these functions in the configuration file.
Following is a list of configuration variables.
Variable | Default | Minimum | Maximum | Description |
---|---|---|---|---|
sv_all_respawn_after_score |
0 | 0 | 1 | Respawn unfrozen tees after one of the teams has been completely frozen (1) |
sv_anticamper |
1 | 0 | 1 | Anticamper protection (1) |
sv_anticamper_range |
100 | 0 | 500 | Camp radius |
sv_anticamper_time |
10 | 0 | 30 | Acceptable camp time in seconds |
sv_automelt_display |
1 | 0 | 1 | Display melting progress as decreasing armor (1) |
sv_automelt_time |
30 | 0 | 120 | Default tee melting time |
sv_booster_usetiles |
0 | 0 | 1 | Activate boost tiles (1) |
sv_chatloop_interval |
0 | 0 | 10000000 | Send sv_chatloop_message to chat every this number of milliseconds (2) |
sv_external_port |
0 | 0 | — | External port to report to master servers |
sv_fb_ball_damage_lose |
1 | 0 | 1 | Turns on/off turnover on any damage (1) |
sv_fb_ball_deathtile_idle |
3 | 0 | 60 | Number of seconds needed to reset a ball after hitting a death tile (2) |
sv_fb_Ball_direct_pass |
1 | 0 | 1 | Turns on/off direct-passing of ball with hook-key (1) |
sv_fb_ball_direct_pass_distance |
380 | 32 | 3200 | Maximum direct-passing distance |
sv_fb_ball_offset_x |
0 | 0 | 1000 | X offset of the hookable center of the ball with respect to the lower left corner of the image |
sv_fb_ball_offset_y |
38 | 0 | 1000 | Y offset of the hookable center of the ball with respect to the lower left corner of the image |
sv_fb_ball_radius |
40 | 0 | 1000 | Ball hookable radius |
sv_fb_ball_reset_time |
30 | 1 | 500 | Number of seconds needed to reset an unused ball |
sv_fb_ball_velocity |
207 | 10 | 3000 | Velocity of the ball after it got shot |
sv_fb_goalcamp |
1 | 0 | 1 | Turns on/off goal camp protection (1) |
sv_fb_goalcamp_factor |
40 | 10 | 100 | Goal camp protection radius (value/10.0*sv_fb_goalsize ) |
sv_fb_goalcamp_time |
3 | 1 | 600 | Number of seconds allowed lingering near a goal |
sv_fb_goalsize |
64 | 32 | 500 | Radius defining goal |
sv_fb_hook_ball |
1 | 0 | 1 | Turns on/off the possibily to capture the ball by hook (1) |
sv_fb_laser_momentum |
207 | -3000 | 3000 | Momentum passed to a flag when it is hit with laser |
sv_fb_owngoal |
0 | 0 | 1 | Turns on/off own goals (1) |
sv_fb_owngoal_kick |
0 | 0 | 1 | Number of own goals allowed before kicking the player (2) |
sv_fb_owngoal_penalty |
30 | 0 | 999 | Negative score for own goal player |
sv_fb_owngoal_warn |
1 | 0 | 1 | Turns on/off warning when player with ball is near own goal (1) |
sv_fb_score_player_ball |
5 | 0 | 100 | Player score on ball-goal |
sv_fb_score_player_stand |
1 | 0 | 100 | Player score for taking ball from stand |
sv_fb_score_player_tee |
10 | 0 | 100 | Player-score on tee-goal |
sv_fb_score_team_ball |
50 | 1 | 100 | Team-score on ball-goal |
sv_fb_score_team_tee |
100 | 1 | 100 | Team-score on tee-goal |
sv_freeze_usetiles |
0 | 0 | 1 | Activate freeze tiles (1) |
sv_frozen_tags |
1 | 0 | 1 | Insert [F] in front of frozen tee's name (1) |
sv_grenade_reload |
500 | 0 | 10000 | Grenade launcher reload time in milliseconds |
sv_grenade_selfdamage |
1 | 0 | 1 | Grenades damage the shooter (1) |
sv_grenade_split |
0 | 0 | 10 | Number of grenades to fire per grenade launcher shot. Set to 1 to get inifinite normal grenades (2) |
sv_high_bandwidth |
0 | 0 | 1 | Use high bandwidth mode. Doubles the bandwidth required for the server. LAN use only (1) |
sv_inactivity_kick |
120 | 0 | 10000 | Kick players not active for that number of seconds (2) |
sv_inactivity_kick_spect |
0 | 0 | 1 | Force inactive players to spectate rather than kick (1) |
sv_join_as_frozen |
0 | 0 | 1 | Freeze newcomers (1) |
sv_killingspree |
1 | 0 | 1 | Activate killing spree messages (1) |
sv_killingspree_award |
0 | 0 | 1 | Provide awards for killing sprees (1) |
sv_killingspree_award_lasers |
3 | 0 | 1 | For awarded tees, use this instead of sv_lasers |
sv_killingspree_award_lasers_split |
1 | 0 | 10 | For awarded tees, use this instead of sv_lasers_split |
sv_killingspree_award_reload |
75 | 1 | 100 | For awarded tees weapon reload times become this number of percents of default |
sv_killingspree_kills |
5 | 1 | 1000 | Number of kills per killing spree |
sv_killingspree_tag |
1 | 0 | 1 | Insert [SPREE] in front of killingspreeing tee's name (1) |
sv_laser_gothroughfrozens |
0 | 0 | 1 | Let laser penetrate frozen tees (1) |
sv_laser_gothroughplayers |
0 | 0 | 1 | Let laser penetrate players (1) |
sv_laserjumps |
0 | 0 | 1 | Activate laser jumps (1) |
sv_laser_reload |
800 | 0 | 10000 | Laser reload time in milliseconds |
sv_lasers |
1 | 1 | 10 | Number of laser beams per laser rifle shot |
sv_lasers_split |
1 | 0 | 10 | Angle between laser beams if sv_lasers is more than 1. Measured in `pi / 100` |
sv_map_reload |
0 | 0 | 1 | Reload the current map (1) |
sv_maxclients |
8 | 1 | 16 | Maximum number of clients that are allowed on a server |
sv_melt_health |
10 | 1 | 10 | Health of melted tee |
sv_melt_range |
100 | 0 | 1000 | Tee heat radius |
sv_melt_respawn |
1 | 0 | 1 | Respawn melted tees (1) |
sv_melt_tiles_degeneration |
1 | 0 | 10 | Amount of damage per second a tee standing on ice tile receives |
sv_melt_tiles_noshoot |
0 | 0 | 1 | Allow shooting on hot tiles (1) |
sv_melt_time |
1 | 0 | 5 | Melting time in seconds for a tee being thawed |
sv_melt_usetiles |
0 | 0 | 1 | Activate melting tiles (1) |
sv_pause_warmup |
0 | 0 | — | Number of seconds to do warmup before unpausing the game |
sv_port |
8303 | 0 | — | Port to use for the server |
sv_powerups |
1 | 0 | 1 | Allow powerups like ninja (1) |
sv_register |
1 | 0 | 1 | Register server with master server for public listing (1) |
sv_reserved_slots |
0 | 0 | 16 | Keep that many slots reserved to whoever knows the password |
sv_rounds_per_map |
1 | 1 | 100 | Number of rounds on each map before rotating (3) |
sv_scorelimit |
20 | 0 | — | Score limit (2) |
sv_spamprotection |
1 | 0 | 1 | Spam protection (1) |
sv_spawnprotect_time |
1000 | 0 | 10000 | Spawnprotection time in milliseconds |
sv_spectator_slots |
0 | 0 | 16 | Number of slots to reserve for spectators |
sv_teambalance |
0 | 0 | 1 | Activate team auto-balancing |
sv_teambalance_time |
1 | 0 | 1000 | How many minutes to wait before autobalancing teams |
sv_teamdamage |
0 | 0 | 1 | Team damage (1) |
sv_teleport_usetiles |
0 | 0 | 1 | Activate teleport tiles (1) |
sv_timelimit |
0 | 0 | — | Time limit in minutes (2) |
sv_tournament_mode |
0 | 0 | 1 | Tournament mode. When enabled, players joins the server as spectators (1) |
sv_void_frozen |
0 | 0 | 1 | Freeze tees respawned after being killed by death tile (1) |
sv_vote_kick |
1 | 0 | 1 | Allow voting to kick players (1) |
sv_vote_kick_bantime |
5 | 0 | 1440 | The time to ban a player if kicked by vote. 0 makes it just use kick |
sv_warmup |
0 | 0 | — | Number of seconds to do warmup before round starts |
Variable | Description |
---|---|
sv_binaddr |
Address to bind the server to |
sv_chatloop_message |
A message to send to chat every sv_chatloop_interval milliseconds |
sv_gametype |
Game type (4) |
sv_map |
Map to use on the erver |
sv_maprotation |
Maps to rotate between (3) |
sv_motd |
Message of the day to display for the clients |
sv_name |
Server name |
sv_reserved_slots_pass |
Reserved slots password |
(1) This is a boolean value. 0 means disabled, 1—enabled.
(2) Zero value disables this option.
(3) on_cyclemap
can be used
to implement more complex maprotation logic
(4) Available gametypes are:
ctf
, tdm
, dm
, ictf
, itdm
, idm
, ifrzctf
, ifreeze
, ifb
.
Functions providing teeworlds API for the configuration script are described in this section. Description of function signatures uses the following conventions:
$MAX_CLIENTS
which is equal to 16. -1 is often used to designate the
server itself.The functions:
config::int::get
=> integer: returns value of the configuration
variable.config::int::set
integer: sets the value of the
configuration variable.config::int::min
=> integer: returns the minimum allowed value of
the configuration variable.config::int::max
=> integer: returns the maximum allowed value of
the configuration variable.config::int::help
=> string: returns description of the
configuration variable.config::str::get
=> string: returns value of the configuration
variable.config::str::set
string: sets the value of the
configuration variable.config::str::help
=> string: returns description of the configuration
variable.say_rcon
cid, string: sends string to rcon of client cidclient_name
cid: returns player name.client_active
cid: tests whether there is a client with a given
id.client_addr
cid => integer: returns client IP.server_kick
cid , [string reason]: kicks player
cid with reason reason. Default reason is "kicked by console".server_ban
cid, time: bans player cid for time seconds.server_ban_ip
ip, time: bans ip for time seconds.server_unban
ip: unbans ip.server_ban_num
=> integer: returns the number of active bans.server_ban_get
integer index => integer ip, integer
time: returns ip and its expiration time time for ban index.record
string filename: starts recording a demo in file filename.stop_record
: stops demo recording.server_auth
cid, gid: sets client cid's
group id to gid.client_gid
cid => integer: returns client cid's
group id.server_start_vote
cid, string chat_message,
string description, code command: starts a vote.
Here cid is id of the client started the vote (use -1 for anonymous votes),
chat_message is the line to display in chat, description is the line to
display in vote status window, command is the Perl function to execute if
vote passes.vote_active
=> integer: tests whether a vote is running.vote_enforce
integer: force vote result.send_chat
cid, integer target, string text:
send chat message text from client cid to target. When cid is -1,
the message is sent on behalf of the server. Set target to -4 to send the
message to all players, -3 to spectators, -2 to the red team, -1 to
the blue team or to cid of the desired client.send_broadcast
cid, string text: send text to
client cid as broadcast (huge letters in the center of the screen). Set
cid to -1 to send text to all players.addvote
string command: add vote command to the list of votes.
command must be a predefined command.delvote
string command: delete vote command from the list of
votes.restart
[integer warmup]: restart the game. Do warmup for warmup
seconds, 0 by default.server_pause
[integer]: pause or unpause game. If no argument
provided, toggle.endround
: force end of the round.set_team
cid, integer team: move player cid to
team team. set team to -1 for spectators, 0 for red, 1 for blue.set_datadir
path: make engine to look in path for game files.These functions are not supposed to be defined in the configuration script. This documentation is provided for those who would like to extend them.
execute_line
cid, string command: process the string received by
rcon of client cid.auth
cid, string password: called when a player enters some
password into rcon prompt.client_start_vote
cid, string command: called when a player starts
a predefined vote.on_event
string event, args: dispatching function for hooks to
server events. See events.These are hooks to tune the server reaction to events in game. Unless stated explicitly, these functions are not defined by default.
on_cyclemap
: called at the end of the round.server_send_chat
cid, integer target, string_ message,
integer bind: called when someone produces a chat message. cid equal
to -1 means the server. target is the chat target as for send_chat
, bind
is true if the message is suspected to be bind (I think it may get false
positive in case of bad network connection, but I could not make it
happen). Note that the function return value is discarded. The function is
supposed to call send_chat
by itself. The default is to allow all non-binded
messages.server_set_team
cid, integer team: executed when a player wants to
change team. team is -1 for spectators, 0 for read and 1 for blue. The
function is supposed to check whether changing teams is allowed and do the
change via call to set_team
.sv_playerlimit
integer: limit the number of non-spectating players.
Changes sv_spectator_slots
accordingly.find
code test, array => element: returns the first
element in array that matches test.position
code test, array => index: returns the index of
the first element in array that matches testreduce
code, array => scalar: uses a binary operation to
combine the elements of the array. The operation should be defined in terms of
variables $a
and $b
, like the sort
builtin.max
array => number: returns the maximum number in array.min
array => number: returns the minimum number in array.init
string path, integer system_gid,
integer system_uid: chroots to path and sets the server
group id and user id to system_gid and system_uid. Do not confuse
system_gid and system_uid with gid and cid used elsewhere in
documentation! See REPL for details.reply
string message: sends message to the current client defined
by $RCON_CID
global variable. Teeworlds doesn't support tab characters
("\t"
) and it doesn't like newlines ("\n"
), so the former are replaced
with eight spaces, and the latter are handled by several calls to say_rcon
.
If you need to send large amount of text, see more
.quoted_string
string quote: returns a regular expression that
matches a string quoted with quote with possibly escaped quotes inside.
For example, quoted_string('"')
will match to the whole string "abc\"def"
.player_cid
player => cid: returns cid of a player corresponding to
player. The latter can be a number, a string or a regular expression. The
number is returned immidiately as long as it is a valid cid of active
client. The string is used to find a player whose name is equal to that
string. The regular expression is used to find a player whose name matches the
expression. If several names match, the function dies.to_seconds
string time => integer time: given a string
representing some time, returns its value in seconds. The string should be
made of sequences number
time_unit
, possibly divided by spaces. For
example, "4d 3h 15" means four days, three hours and fifteen seconds. Known
units are: s
—second, m
—minute, h
—hour, d
—day,
w
—week, M
—month, y
—year, i
—infinity.from_seconds
integer time => string time: given a time in
seconds, produces human-readable string describing this time. This function is
the reverse of to_seconds
.ip
string ip => integer ip: converts an ip represented
by a string with four decimal numbers ("127.0.0.1" for example) to 32-bit
integer.sprint_ip
integer ip => string ip: the reverse of ip
.sprint_group
integer gid => string group_names:
concatenates names of groups the given gid belongs to.status
: prints the list of players to the active rcon.kick
player: kicks player player.ban
player, time: bans player player for time. unban
string ip: unbans ip.bans
: prints current ban list in rcon.su
password: adds user to the group with the
corresponding password field.start_vote
[cid, ] string description, code action: starts
a vote on behalf of the client cid. If cid is not provided, $RCON_CID
is
used. description is the vote description, action is the code to execute
if vote succeeds.start_cmd_vote
[cid, ] string command: starts vote for a predefined
command. If cid is not provided, $RCON_CID
is used. Dies if user has no
permission to run the vote.vote
result: force a vote. Use yes
or 1 to force yes, no
or 0 to force
no.call_interactive
code: call an interactive function.splitn
string, n [, fuzz] => substring1, substring2: splits the
string near the character number n. substring1 length is no more than n.
If there is a punctuation character among the last fuzz characters, the
split will occur after that character. By default fuzz is n / 5.split_long_string
string, limit => array: splits a long string with
splitn
so that the resulting strings will be of no more than limit
characters in length.more
array text: implements a simple text viewer like more.help
[string command]: prints help for the predefined command. With
no parameters prints the list of available commands.chat
[[cid, ] target] text: send text to target's chat on behalf
of the client cid. By default cid is -1 which means the server. target
can be all
for everyone, spec
for spectators, blue
for the blue team,
red
for the red team or player id. Default is all
.broadcast
[cid, ] text: broadcasts text to client cid. Default is -1
which means everyone.team
player, team: join player player to team team. team must be
one of spec
, red
, blue
or -1, 0, 1.pause
[on]: pause the game, if on is false, unpause; if it is not
specified, toggle.free_gid
=> gid: returns the next free gid. The function assumes that
gid basis are powers of two and computes the next free orthogonal gid number.$MAX_CLIENTS
: the maximum number of clients the server can hold. This
variable is not supposed to be changed. In the current version it is equal to
16.$root_gid
: root group id. It is set to ~0
, which means "every possible
group". This variable is not supposed to be changed.$RCON_CID
: id of the current client. It is set to -1 when the code is being
executed by the server request.%groups
: a hash of groups. Keys are group names, values are hashes with keys
gid
and password
.%commands
: a hash of predefined commands. Keys are command names, values are
hashes with keys gid
—binary or of ids of groups allowed to run the
command, sub
—the code to execute, help
—an optional help string,
vgid
—an optional binary or of ids of groups allowed to vote for this
command, vcheck
—an optional function to run before starting the vote.
vcheck
should check the correctness of the parameters passed. Its arguments
are: client id, client group id, arguments string.@interactive
: when a player executes an interactive command like more
,
this array keeps the code interpreting player responces.$more_cols
: the average number of characters fit on the screen for the
more
function.$more_rows
: the number of rows fit on the screen for the more
function.%hooks
: a hash of hooks to server events. See events.An interactive function is a function that may ask the user for additional input
while being executed. Since teeworlds is a single-threaded program, it is
impossible to implement it in a straightforward way, because the server will hang
while the player is talking to the function. Although the multi-threaded
approach still can be implemented, I decided to do it in a simpler way. To
create an interactive function, define the function that interpretes the user
answer and unshift
it into the @{$interactive[$RCON_CID]}
array. You may use
call_interactive
for this purpose. You are free to modify
$interactive[$RCON_CID][0]
during execution. Don't forget to shift
the array
back when the function returns (with call_interactive
you may call $quit->()
closure variable). Here is a simple example:
sub ask_me {
reply 'To be or not to be?';
call_interactive {
given ($_[0]) {
when ('to be') {
reply 'Ok, to be';
$quit->();
};
when ('not to be') {
reply 'Are you sure?';
my $quit_top = $quit;
call_interactive {
given ($_[0]) {
when ('yes') {
reply 'If you say so';
$quit->();
$quit_top->();
};
when ('no') {
reply 'So, to be or not to be?';
$quit->();
};
default {
reply 'What?';
}
}
}
};
default {
reply 'What??';
}
}
};
return undef;
}
The $quit
variable in the example contains the reference to the code that
clears the function out of the @{interactive[$RCON_CID]}
.
The concept of interactive functions is not well-thought yet. If you really need
such function, perhaps you better read the sources to understand what kind of
magic is going on there. Currently only more
function uses call_interactive
,
maybe later if I need other functions, I'll make a saner interface.
Configuration script may customize server behaviour in case of different events.
For example, it may define actions that should be done when the game is over or,
in case of player leave, release the memory that is no longer needed. That is
achieved by means of hooks to server events. Hooks are subroutines stored in
%hooks
hash. They are called when the corresponding event happens with some
parameters describing the event.
In order to ease configuration and let modules to use hooks without clashing
with other modules, the %hooks
hash stores subroutines in the following way:
each entry in %hooks
is a hash, elements of which are subroutines and keys are
some names assigned to these subroutines, for example
$hooks{'leave'}{'log'} = sub {
printf LOG '%s has left the game', client_name($_[0])
};
defines a function named log
that will print a string to the LOG
stream when
someone leaves the game. When an event happen, all subroutines in the hash stored
under the corresponding event name are executed (in this case these are
values %{$hooks{'leave'}}
). Hence modules can define their functions under
module-specific names and avoid the clash with other modules.
Defined events are:
enter
cid: player with client id cid has entered the game.leave
cid: player with client id cid has left the game.start round
: Warmup (if any) has done and the new game started.end round
: The game is over, declaring the winner.cyclemap
: end of the round, when it is the appropriate time to change the
map.In order to simplify configuration for several servers, most useful features are
provided in form of modules or packages in Perl terminology. To include a module
in your script, just use
it, for example:
use Teeworlds::Common;
The @INC
array that holds paths used by Perl to find module files is modified
to include ~/.teeworlds
and src/config
directories. If you want to run the
server from the place other than teeworlds repository directory, either copy
src/config/Teeworlds
to ~/.teeworlds
or modify @INC
appropriately.
Teeworlds::Common
contains a set of the most common features: rcon commands
required to effectively control the server and a few useful functions. It is
expected to be included in every not-very-unusual configuration script, so it
fills the main
namespace with its symbols. Other modules may depend on it.
Teeworlds::Common
provides the following functions:
parse_player_name
string => id,
rest extracts player id from string. id is the value that can be
passed to player_cid
to get the player client id. The function
uses the first non-whitespace character to determine the type of the result:
Jini, blah
results in (qr/Jini/i, 'blah')
/Jini/, blah
results in (qr/Jini/, 'blah')
'Jini', blah
results in ('Jini', 'blah')
"Jini", blah
results in ('Jini', 'blah')
'"a\"b", blah'
results in ('a"b',
'blah')
. In every case blah
is optional. If the function
is evaluated in scalar context, it returns only the player id.inferior_player_cid
caller_cid, args =>
id, rest: same as parse_player_name
args, but
it also checks that the specified player exists and its gid is lower than
caller_cid. The function dies if the check fails.man
[topic]: prints to rcon commands descriptions or
other information stored in the %man
hash. When invoked without
arguments, prints keys of %man
. When argument matches the key of
%man
, prints its data. When the argument matches command name,
prints contents of field man
of the corresponding
%commands
entry.mute
target: mutes the chat. target may be one
of: team, public, all, or player name suitable for player_cid
.
unmute
target: unmutes the chat.set_map
map: changes current map to map. This
function may be invoked with the map
command and is supposed to be
redefined if more complex logic but call to sv_map
is desired.list_maps
=> array maps: returns
the list of available maps. This function is invoked with the map
command and is supposed to be redefined if more complex logic but return the
contents of @maps
array is desired.Teeworlds::Common
provides the following groups: root, admin, and user, with
gids $g_root
, $g_admin
, $g_user
. By default no password is defined, so
groups are inaccessible.
Teeworlds::Common
provides the following commands:
ban
player: bans the player.bans
: prints the list of active bans.broadcast
message: sends broadcast message to all players.broadcastp
player message: sends broadcast message to a specific player.help
: provides short commands description.kick
player: kicks the player from the server.man
[topic]: provides additional information.map
[map]: change the map or list available maps.mute
target: mute target (see mute
function).pause
: pause or unpause the game.status
: print the list of players together with their client ids, IPs and groups.restart
[warmup]: restart the game.start_vote
command: start a vote for command.su
password: grant the rights of the group with matching password to the
player.sv_playerlimit
integer: limit the number of playing players.team
player, team: move player to team.unban
ip: remove the ban from ip.unmute
target: unmute target (see unmute
function).vote
result: force the vote.
See the code for the details.Teeworlds::Captains
provides support for the captain games. The captain game
starts with two players being elected as captains. They join opposing teams and
start choose other players in turns. The player chosen joins the team of the
corresponding captain. When both teams are built, the actual fight starts as
usual.
To start the game, two captains must be elected by means of elect
command. By
default it can only be voted, so the majority of players have to want to play
the captain game and agree on who they want to be their captain. When both
captains are elected, everyone is forced to spectate and captains are joined
opposing teams. At this stage captains may choose their teammates by means of
the choose
command or pass their turn to the other team via the skip
command. Red team chooses first. By default team balance is not set, so it is
possible to play unbalanced games. When team building is done, both captains
must invoke ready
command and the game starts after a warmup. While playing,
the game can be paused via stop
command and unpaused when both captains
invoke ready
command. When someone leaves, the game pauses and it must be
unpaused in the same way. If both captains leave, the game is dropped.
To stop the captain game, vote for the drop_game
command. The captain may be
de-elected via drop_captain
command.
Teeworlds::Captains
uses a lot of features from Teeworlds::Common
and
expects it to be included first.
Teeworlds::Captains
provides the group captain
with gid $g_captain
.
Teeworlds::Captains
provides the following commands:
elect
player [, team]: elect a captain for team. Free team is used by
default. If there already is a captain for the given team, he will be
superceded.choose
player: joins the player to the team of the captain who has invoked
the command.skip
: pass the turn to choose to the other team.ready
: tell the world that your team is ready to start or continue the game.drop_game
: drop the captain game.drop_captain
: de-elect the captain.spec
: the captain may use this command to force a teammate to join the
spectators. Teambalance is updated accordingly so he can choose someone else then.stop
: pause the captain game.Teeworlds::Random_maps
sets random maprotation on the server. To define the
available maps, use %maps
hash with keys being map names and values being
hashes with keys probability
setting the probability for a map to appear. The
probabilities don't have to be normed to one; when all maps have been defined,
call normalize_maps
function to compute real probabilities. They will be
stored under the p
fields in values of %maps
hash. Here is a short usage
example:
%maps = (
ctf2 => { probability => 0.5 },
ctf4 => { probability => 0.5 },
ctf5 => { probability => 0.3 },
);
normalize_maps;
After the call to normalize_maps
$maps{'ctf2'}{'p'}
and $maps{'ctf4'}{'p'}
are equal to 0.38, and $maps{'ctf5'}{'p'}
is equal to 0.23.
Teeworlds::Random_maps
provides the following functions:
normalize_maps
: recomputes the probability for maps to appear using values
stored in probability
fields of values in %maps
hash. See above for
example.get_next_map
=> map: returns the next random map.next_map
: changes the current map to the next random one.The next_map
function is also available as a command.