Server mod author guide

Feature flags

TaystJK gates some client-side features on recognising the server as JA+ or jaPRO. taystJKinfo is how any other game module opts in to them individually, without pretending to be either, and taystJKcgflags is how the client tells the server which extended commands it can read.

The problem it solves

Several client features need the server to behave compatibly because they change movement prediction, draw something the server also decides, or extend a server command. Rather than run them everywhere and desync or misread the command, the client checks which mod the server is running and enables them only where it knows they work. The ordering that check uses, and what it costs you, is described on mod compatibility.

If your module supports one of these features but is not JA+ or jaPRO, that check would turn it off. taystJKinfo is the override.

A server flag alone is not enough when a feature changes the layout of a command the server sends. Every client reads serverinfo, including older TaystJK builds and other clients that do not know the flag, and those would misread the new layout. For those features the client also advertises support through taystJKcgflags, and the server sends the extended layout only to clients that advertise it.

Setting it

Put the bitmask in your serverinfo under the key taystJKinfo. The client reads it from the gamestate on connect, and again whenever the serverinfo changes, so you can change flags while the server runs (cg_servercmds.c):

Info_SetValueForKey( info, "taystJKinfo", va( "%i", flags ) );

Set only the bits you have actually implemented. A client that trusts a flag you do not honour can mispredict or parse a server command with the wrong layout, which is worse for the player than the feature being absent.

A worked example against OpenJK’s game module, on the ojk-cgflags branch, shows the interface: declaring every bit, setting taystJKinfo, and reading taystJKcgflags back from each client. It sets every flag to show the declarations and does not implement the features behind them. Your module should set only the bits it implements; scoreboard deaths shows what that one needs.

The bits

Defined in bg_public.h:

Bit Value Constant Means
0 1 TAYSTJK_INFO_RGBSABERS Sabers may use colours beyond the stock set.
1 2 TAYSTJK_INFO_BLACKSABERS The black saber colour is permitted.
2 4 TAYSTJK_INFO_FLIPKICK Flip kicks are supported.
3 8 TAYSTJK_INFO_GRAPPLE The grapple hook behaves as JA+’s (bg_pmove.c). Client builds from before 24 October 2026 ignore it.
4 16 TAYSTJK_INFO_FIXROLL_1 Rolling while using force grip.
5 32 TAYSTJK_INFO_FIXROLL_2 The above, plus chaining rolls.
6 64 TAYSTJK_INFO_FIXROLL_3 JK2-style rolling.
7 128 TAYSTJK_INFO_SCORE_DEATHS The server appends each player’s deaths to the scores command for clients that advertise support. See scoreboard deaths.

The three roll bits are alternatives, not levels to combine. The client tests them from 3 downwards and takes the first that matches (bg_pmove.c).

Client capabilities: taystJKcgflags

The TaystJK cgame registers taystJKcgflags as a read-only userinfo cvar, so it reaches the server in the connect string and in every userinfo update (cg_xcvar.h). The cgame sets it to the capabilities it supports when it loads (cg_cvar.c), and resets it to 0 when it shuts down, so a different cgame loaded later in the same session does not inherit the claim (cg_main.c).

Defined in bg_public.h:

Bit Value Constant Means
0 1 TAYSTJK_CGFLAG_SCORE_DEATHS The cgame reads a deaths field appended to each player record in scores.

Read it in ClientUserinfoChanged, which runs on connect and whenever the client sends new userinfo:

s = Info_ValueForKey( userinfo, "taystJKcgflags" );
client->pers.taystJKcgflags = atoi( s );

A missing key reads as 0, which is the correct answer for every client that is not a current TaystJK build. The value can change after connect: the cgame registers the cvar when it loads, after the initial connect on a fresh start, and the client then sends updated userinfo. Check the stored value each time you build a command; do not cache a decision at connect.

If you maintain a fork of the TaystJK cgame that changes what any of these features parse, do not reuse taystJKcgflags or its bits. Register your own userinfo cvar with its own bit namespace, so servers never mistake your client for one with the stock behaviour.

Scoreboard deaths

TAYSTJK_INFO_SCORE_DEATHS extends the scores command, so it needs both halves:

  1. Set bit 7 in taystJKinfo. A client that sees it reads 15 integers per player instead of the stock 14 (cg_servercmds.c).
  2. In DeathmatchScoreboardMessage, append the deaths only when the client receiving the command advertises TAYSTJK_CGFLAG_SCORE_DEATHS. Every other client gets the stock 14-field record.
qboolean sendDeaths = (ent->client->pers.taystJKcgflags & TAYSTJK_CGFLAG_SCORE_DEATHS) ? qtrue : qfalse;

// after formatting the stock 14 fields into entry
if ( sendDeaths )
	j += Com_sprintf( entry + j, sizeof( entry ) - j, " %i", cl->ps.persistant[PERS_KILLED] );

The deaths field goes last, after captures. Do not send it to a client that has not advertised the capability: older TaystJK builds and other clients read 14 fields per player, take the deaths as the next player’s client number, and misalign every record after it.

With the deaths supplied, the client’s cg_scoreDeaths treats your server as it treats JA+ and jaPRO: 1 and 2 show the server’s count, and 3 still shows the client’s own count (cg_scoreboard.c).

How the client uses a flag

Each feature in the bit table checks for its flag next to the JA+ and jaPRO checks. RGB sabers are typical: they are allowed on JA+ and above, or when your flag is set (cg_players.c).

Flags can also change behaviour on recognised JA+ and jaPRO servers. In the roll selection above, FIXROLL_3 is tested before the server’s roll-2 or roll-1 option, so advertising it takes precedence over either lower mode (bg_pmove.c). Keep the advertised flags consistent with the server’s actual movement rules and options.

Verifying it

Run serverinfo in the server console, or rcon serverinfo from a client, and check the key is there with the value you set. A player can see the same thing with configstrings while connected: the first entry is the serverinfo (cl_main.cpp). serverinfo itself only works where the server is running. If the key is missing, the client never got it and nothing that depends on it will work, so fix that before looking at the feature.

For a capability, run dumpuser <player> on the server console. A current TaystJK client connected to the server lists taystJKcgflags with a non-zero value. A player can check their own with userinfo, but only while connected: the cvar does not exist until the cgame has loaded, and reads 0 again after disconnecting.

Last changed History Edit this page on GitHub