UFO: Alien Invasion
Loading...
Searching...
No Matches
mp_serverlist.cpp
Go to the documentation of this file.
1
5
6/*
7Copyright (C) 2002-2025 UFO: Alien Invasion.
8
9This program is free software; you can redistribute it and/or
10modify it under the terms of the GNU General Public License
11as published by the Free Software Foundation; either version 2
12of the License, or (at your option) any later version.
13
14This program is distributed in the hope that it will be useful,
15but WITHOUT ANY WARRANTY; without even the implied warranty of
16MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17
18See the GNU General Public License for more details.
19
20You should have received a copy of the GNU General Public License
21along with this program; if not, write to the Free Software
22Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23
24*/
25
26#include "../../cl_shared.h"
27#include "../cl_game.h"
29#include "../../ui/ui_data.h"
31#include "mp_serverlist.h"
32#include "mp_callbacks.h"
33
34#define MAX_SERVERLIST 128
35
36static const cgame_import_t* cgi;
37
40static char serverText[1024];
42static int serverListPos;
45
55static bool GAME_MP_ProcessPingReply (serverList_t* server, const char* msg)
56{
57 if (!msg)
58 return false;
59
60 if (PROTOCOL_VERSION != Info_IntegerForKey(msg, "sv_protocol")) {
61 Com_DPrintf(DEBUG_CLIENT, "CL_ProcessPingReply: Protocol mismatch\n");
62 return false;
63 }
64 if (!Q_streq(UFO_VERSION, Info_ValueForKey(msg, "sv_version"))) {
65 Com_DPrintf(DEBUG_CLIENT, "CL_ProcessPingReply: Version mismatch\n");
66 }
67
68 if (server->pinged)
69 return false;
70
71 server->pinged = true;
72 Q_strncpyz(server->sv_hostname, Info_ValueForKey(msg, "sv_hostname"),
73 sizeof(server->sv_hostname));
74 Q_strncpyz(server->version, Info_ValueForKey(msg, "sv_version"),
75 sizeof(server->version));
76 Q_strncpyz(server->mapname, Info_ValueForKey(msg, "sv_mapname"),
77 sizeof(server->mapname));
78 Q_strncpyz(server->gametype, Info_ValueForKey(msg, "sv_gametype"),
79 sizeof(server->gametype));
80 server->clients = Info_IntegerForKey(msg, "clients");
81 server->sv_dedicated = Info_IntegerForKey(msg, "sv_dedicated");
82 server->sv_maxclients = Info_IntegerForKey(msg, "sv_maxclients");
83 return true;
84}
85
91
97static inline bool GAME_MP_ShowServer (const serverList_t* server)
98{
99 if (cl_serverlist->integer == SERVERLIST_SHOWALL)
100 return true;
101 if (cl_serverlist->integer == SERVERLIST_HIDEFULL && server->clients < server->sv_maxclients)
102 return true;
103 if (cl_serverlist->integer == SERVERLIST_HIDEEMPTY && server->clients > 0)
104 return true;
105
106 return false;
107}
108
110{
111 AutoPtr<dbuffer> buf(cgi->NET_ReadMsg(s));
112 if (!buf) {
113 cgi->NET_StreamFree(s);
114 return;
115 }
116 serverList_t* server = (serverList_t*)cgi->NET_StreamGetData(s);
117 const int cmd = cgi->NET_ReadByte(buf);
118 if (cmd != svc_oob) {
119 cgi->NET_StreamFree(s);
120 return;
121 }
122
123 char str[512];
124 cgi->NET_ReadStringLine(buf, str, sizeof(str));
125
126 if (strncmp(str, "info", 4) == 0) {
127 cgi->NET_ReadString(buf, str, sizeof(str));
128 if (GAME_MP_ProcessPingReply(server, str)) {
129 if (GAME_MP_ShowServer(server)) {
132 Q_strcat(serverText, sizeof(serverText), "%s\t\t\t%s\t\t\t%s\t\t%i/%i\n",
133 server->sv_hostname,
134 server->mapname,
135 server->gametype,
136 server->clients,
137 server->sv_maxclients);
138 }
139 }
140 } else if (strncmp(str, "print", 5) == 0) {
141 char paramBuf[2048];
142 cgi->NET_ReadString(buf, paramBuf, sizeof(paramBuf));
143 cgi->Com_DPrintf(DEBUG_CLIENT, "%s", paramBuf);
144 }
145 cgi->NET_StreamFree(s);
146}
147
153static void GAME_MP_PingServer (serverList_t* server)
154{
155 struct net_stream* s = cgi->NET_Connect(server->node, server->service, nullptr);
156 if (s == nullptr) {
157 cgi->Com_Printf("pinging failed [%s]:%s...\n", server->node, server->service);
158 return;
159 }
160 cgi->Com_DPrintf(DEBUG_CLIENT, "pinging [%s]:%s...\n", server->node, server->service);
161 cgi->NET_OOB_Printf(s, SV_CMD_INFO " %i", PROTOCOL_VERSION);
162 cgi->NET_StreamSetData(s, server);
163 cgi->NET_StreamSetCallback(s, &GAME_MP_PingServerCallback);
164}
165
170{
171 cgi->Com_Printf("%i servers on the list\n", serverListLength);
172
173 for (int i = 0; i < serverListLength; i++) {
174 const serverList_t* list = &serverList[i];
175 cgi->Com_Printf("%02i: [%s]:%s (pinged: %i)\n", i, list->node, list->service, list->pinged);
176 }
177}
178
184static void GAME_MP_AddServerToList (const char* node, const char* service)
185{
187 return;
188
189 for (int i = 0; i < serverListLength; i++)
190 if (Q_streq(serverList[i].node, node) && Q_streq(serverList[i].service, service))
191 return;
192
194 serverList[serverListLength].node = cgi->GAME_StrDup(node);
195 serverList[serverListLength].service = cgi->GAME_StrDup(service);
198}
199
209{
210 char str[4096];
211 if (cgi->NET_ReadString(msg, str, sizeof(str)) == 0) {
212 cgi->UI_ResetData(TEXT_MULTIPLAYER_USERLIST);
213 cgi->UI_ResetData(TEXT_MULTIPLAYER_USERTEAM);
214 cgi->UI_ExecuteConfunc("multiplayer_playerNumber 0");
215 cgi->Com_DPrintf(DEBUG_CLIENT, "GAME_MP_ParseTeamInfoMessage: No teaminfo string\n");
216 return;
217 }
218
220
221 teamData.maxteams = Info_IntegerForKey(str, "sv_maxteams");
222 teamData.maxPlayersPerTeam = Info_IntegerForKey(str, "sv_maxplayersperteam");
223
224 int cnt = 0;
225 linkedList_t* userList = nullptr;
226 linkedList_t* userTeam = nullptr;
227
228 /* for each lines */
229 while (cgi->NET_ReadString(msg, str, sizeof(str)) > 0) {
230 const int team = Info_IntegerForKey(str, "cl_team");
231 const int isReady = Info_IntegerForKey(str, "cl_ready");
232 const char* user = Info_ValueForKey(str, "cl_name");
233
234 if (team > 0 && team < MAX_TEAMS)
235 teamData.teamCount[team]++;
236
237 /* store data */
238 cgi->LIST_AddString(&userList, user);
239 if (team != TEAM_NO_ACTIVE)
240 cgi->LIST_AddString(&userTeam, va(_("Team %d"), team));
241 else
242 cgi->LIST_AddString(&userTeam, _("No team"));
243
244 cgi->UI_ExecuteConfunc("multiplayer_playerIsReady %i %i", cnt, isReady);
245
246 cnt++;
247 }
248
249 cgi->UI_RegisterLinkedListText(TEXT_MULTIPLAYER_USERLIST, userList);
250 cgi->UI_RegisterLinkedListText(TEXT_MULTIPLAYER_USERTEAM, userTeam);
251 cgi->UI_ExecuteConfunc("multiplayer_playerNumber %i", cnt);
252
253 /* no players are connected ATM */
254 if (!cnt) {
257 /* Q_strcat(teamData.teamInfoText, sizeof(teamData.teamInfoText), _("No player connected\n")); */
258 }
259
260 cgi->Cvar_SetValue("mn_maxteams", teamData.maxteams);
261 cgi->Cvar_SetValue("mn_maxplayersperteam", teamData.maxPlayersPerTeam);
262}
263
264static char serverInfoText[1024];
265static char userInfoText[256];
275static void GAME_MP_ParseServerInfoMessage (dbuffer* msg, const char* hostname)
276{
277 char str[MAX_INFO_STRING];
278
279 cgi->NET_ReadString(msg, str, sizeof(str));
280
281 /* check for server status response message */
282 const char* value = Info_ValueForKey(str, "sv_dedicated");
283 if (Q_strnull(value)) {
284 cgi->Com_Printf(S_COLOR_GREEN "%s", str);
285 return;
286 }
287
288 /* server info cvars and users are seperated via newline */
289 const char* users = strstr(str, "\n");
290 if (users == nullptr) {
291 cgi->Com_Printf(S_COLOR_GREEN "%s\n", str);
292 return;
293 }
294 cgi->Com_DPrintf(DEBUG_CLIENT, "%s\n", str); /* status string */
295
296 cgi->Cvar_Set("mn_mappic", "maps/shots/default");
297 if (*Info_ValueForKey(str, "sv_needpass") == '1')
298 cgi->Cvar_Set("mn_server_need_password", "1");
299 else
300 cgi->Cvar_Set("mn_server_need_password", "0");
301
302 Com_sprintf(serverInfoText, sizeof(serverInfoText), _("IP\t%s\n\n"), hostname);
303 cgi->Cvar_Set("mn_server_ip", "%s", hostname);
304 value = Info_ValueForKey(str, "sv_mapname");
305 assert(value);
306 cgi->Cvar_Set("mn_svmapname", "%s", value);
307 char buf[256];
308 Q_strncpyz(buf, value, sizeof(buf));
309 const char* token = buf;
310 /* skip random map char. */
311 if (token[0] == '+')
312 token++;
313
314 Q_strcat(serverInfoText, sizeof(serverInfoText), _("Map:\t%s\n"), value);
315 if (!cgi->R_ImageExists("pics/maps/shots/%s", token)) {
316 /* store it relative to pics/ dir - not relative to game dir */
317 cgi->Cvar_Set("mn_mappic", "maps/shots/%s", token);
318 }
319 Q_strcat(serverInfoText, sizeof(serverInfoText), _("Servername:\t%s\n"), Info_ValueForKey(str, "sv_hostname"));
320 Q_strcat(serverInfoText, sizeof(serverInfoText), _("Moralestates:\t%s\n"), _(Info_BoolForKey(str, "sv_enablemorale")));
321 Q_strcat(serverInfoText, sizeof(serverInfoText), _("Gametype:\t%s\n"), Info_ValueForKey(str, "sv_gametype"));
322 Q_strcat(serverInfoText, sizeof(serverInfoText), _("Gameversion:\t%s\n"), Info_ValueForKey(str, "ver"));
323 Q_strcat(serverInfoText, sizeof(serverInfoText), _("Dedicated server:\t%s\n"), _(Info_BoolForKey(str, "sv_dedicated")));
324 Q_strcat(serverInfoText, sizeof(serverInfoText), _("Operating system:\t%s\n"), Info_ValueForKey(str, "sys_os"));
325 Q_strcat(serverInfoText, sizeof(serverInfoText), _("Network protocol:\t%s\n"), Info_ValueForKey(str, "sv_protocol"));
326 Q_strcat(serverInfoText, sizeof(serverInfoText), _("Roundtime:\t%s\n"), Info_ValueForKey(str, "sv_roundtimelimit"));
327 Q_strcat(serverInfoText, sizeof(serverInfoText), _("Teamplay:\t%s\n"), _(Info_BoolForKey(str, "sv_teamplay")));
328 Q_strcat(serverInfoText, sizeof(serverInfoText), _("Max. players per team:\t%s\n"), Info_ValueForKey(str, "sv_maxplayersperteam"));
329 Q_strcat(serverInfoText, sizeof(serverInfoText), _("Max. teams allowed in this map:\t%s\n"), Info_ValueForKey(str, "sv_maxteams"));
330 Q_strcat(serverInfoText, sizeof(serverInfoText), _("Max. clients:\t%s\n"), Info_ValueForKey(str, "sv_maxclients"));
331 Q_strcat(serverInfoText, sizeof(serverInfoText), _("Max. soldiers per player:\t%s\n"), Info_ValueForKey(str, "sv_maxsoldiersperplayer"));
332 Q_strcat(serverInfoText, sizeof(serverInfoText), _("Max. soldiers per team:\t%s\n"), Info_ValueForKey(str, "sv_maxsoldiersperteam"));
333 Q_strcat(serverInfoText, sizeof(serverInfoText), _("Password protected:\t%s\n"), _(Info_BoolForKey(str, "sv_needpass")));
334 cgi->UI_RegisterText(TEXT_STANDARD, serverInfoText);
335 userInfoText[0] = '\0';
336 for (;;) {
337 token = Com_Parse(&users);
338 if (users == nullptr)
339 break;
340 const int team = atoi(token);
341 token = Com_Parse(&users);
342 if (users == nullptr)
343 break;
344 Q_strcat(userInfoText, sizeof(userInfoText), "%s\t%i\n", token, team);
345 }
346 cgi->UI_RegisterText(TEXT_LIST, userInfoText);
347 cgi->UI_PushWindow("serverinfo");
348}
349
355{
356 AutoPtr<dbuffer> buf(cgi->NET_ReadMsg(s));
357 if (!buf) {
358 cgi->NET_StreamFree(s);
359 return;
360 }
361 const int cmd = cgi->NET_ReadByte(buf);
362 if (cmd != svc_oob) {
363 cgi->NET_StreamFree(s);
364 return;
365 }
366 char str[8];
367 cgi->NET_ReadStringLine(buf, str, sizeof(str));
368 if (Q_streq(str, "print")) {
369 char hostname[256];
370 cgi->NET_StreamPeerToName(s, hostname, sizeof(hostname), true);
372 }
373 cgi->NET_StreamFree(s);
374}
375
376static void GAME_MP_QueryMasterServerThread (const char* responseBuf, void* userdata)
377{
378 if (!responseBuf) {
379 cgi->Com_Printf("Could not query masterserver\n");
380 return;
381 }
382
383 const char* serverListBuf = responseBuf;
384
385 Com_DPrintf(DEBUG_CLIENT, "masterserver response: %s\n", serverListBuf);
386 const char* token = Com_Parse(&serverListBuf);
387
388 int num = atoi(token);
389 if (num >= MAX_SERVERLIST) {
390 cgi->Com_DPrintf(DEBUG_CLIENT, "Too many servers: %i\n", num);
391 num = MAX_SERVERLIST;
392 }
393 for (int i = 0; i < num; i++) {
394 /* host */
395 token = Com_Parse(&serverListBuf);
396 if (!*token || !serverListBuf) {
397 cgi->Com_Printf("Could not finish the masterserver response parsing\n");
398 break;
399 }
400 char node[MAX_VAR];
401 Q_strncpyz(node, token, sizeof(node));
402 /* port */
403 token = Com_Parse(&serverListBuf);
404 if (token[0] == '\0' || !serverListBuf) {
405 cgi->Com_Printf("Could not finish the masterserver response parsing\n");
406 break;
407 }
408 char service[MAX_VAR];
409 Q_strncpyz(service, token, sizeof(service));
410 GAME_MP_AddServerToList(node, service);
411 }
412}
413
417static void GAME_MP_ServerListDiscoveryCallback (struct datagram_socket* s, const char* buf, int len, struct sockaddr* from)
418{
419 const char match[] = "discovered";
420 if (len == sizeof(match) && memcmp(buf, match, len) == 0) {
421 char node[MAX_VAR];
422 char service[MAX_VAR];
423 cgi->NET_SockaddrToStrings(s, from, node, sizeof(node), service, sizeof(service));
424 GAME_MP_AddServerToList(node, service);
425 }
426}
427
433static void GAME_MP_BookmarkAdd_f (void)
434{
435 const char* newBookmark;
436
437 if (cgi->Cmd_Argc() < 2) {
438 newBookmark = cgi->Cvar_GetString("mn_server_ip");
439 if (!newBookmark) {
440 cgi->Com_Printf("Usage: %s <ip>\n", cgi->Cmd_Argv(0));
441 return;
442 }
443 } else {
444 newBookmark = cgi->Cmd_Argv(1);
445 }
446
447 for (int i = 0; i < MAX_BOOKMARKS; i++) {
448 const char* bookmark = cgi->Cvar_GetString(va("adr%i", i));
449 if (bookmark[0] == '\0') {
450 cgi->Cvar_Set(va("adr%i", i), "%s", newBookmark);
451 return;
452 }
453 }
454 /* bookmarks are full */
455 cgi->UI_Popup(_("Notice"), "%s", _("All bookmark slots are used - please removed unused entries and repeat this step"));
456}
457
461static void GAME_MP_ServerInfo_f (void)
462{
463 const char* host;
464 const char* port;
465
466 switch (cgi->Cmd_Argc()) {
467 case 2:
468 host = cgi->Cmd_Argv(1);
470 break;
471 case 3:
472 host = cgi->Cmd_Argv(1);
473 port = cgi->Cmd_Argv(2);
474 break;
475 default:
476 if (selectedServer) {
477 host = selectedServer->node;
478 port = selectedServer->service;
479 } else {
480 host = cgi->Cvar_GetString("mn_server_ip");
482 }
483 break;
484 }
485 struct net_stream* s = cgi->NET_Connect(host, port, nullptr);
486 if (s != nullptr) {
487 cgi->NET_OOB_Printf(s, SV_CMD_STATUS " %i", PROTOCOL_VERSION);
488 cgi->NET_StreamSetCallback(s, &GAME_MP_ServerInfoCallback);
489 } else {
490 cgi->Com_Printf("Could not connect to %s %s\n", host, port);
491 }
492}
493
499{
500 if (cgi->Cmd_Argc() < 2) {
501 cgi->Com_Printf("Usage: %s <num>\n", cgi->Cmd_Argv(0));
502 return;
503 }
504 const int num = atoi(cgi->Cmd_Argv(1));
505
506 if (num < 0 || num >= serverListLength)
507 return;
508
509 cgi->UI_RegisterText(TEXT_STANDARD, serverInfoText);
510 for (int i = 0; i < serverListLength; i++) {
511 if (!serverList[i].pinged || serverList[i].serverListPos != num)
512 continue;
513 /* found the server - grab the infos for this server */
515 cgi->Cbuf_AddText("server_info %s %s\n", serverList[i].node, serverList[i].service);
516 return;
517 }
518}
519
521static bool serversAlreadyQueried = false;
522static int lastServerQuery = 0;
524#define SERVERQUERYTIMEOUT 40000
525
532{
533 selectedServer = nullptr;
534
535 /* refresh the list */
536 if (cgi->Cmd_Argc() == 2) {
537 /* reset current list */
538 serverText[0] = 0;
539 serversAlreadyQueried = false;
540 for (int i = 0; i < serverListLength; i++) {
541 cgi->Free(serverList[i].node);
542 cgi->Free(serverList[i].service);
543 }
544 serverListPos = 0;
547 } else {
548 cgi->UI_RegisterText(TEXT_LIST, serverText);
549 return;
550 }
551
554
555 /* broadcast search for all the servers int the local network */
556 if (netDatagramSocket) {
557 const char buf[] = "discover";
558 cgi->NET_DatagramBroadcast(netDatagramSocket, buf, sizeof(buf), PORT_SERVER);
559 }
560 cgi->UI_RegisterText(TEXT_LIST, serverText);
561
562 /* don't query the masterservers with every call */
564 if (lastServerQuery + SERVERQUERYTIMEOUT > cgi->CL_Milliseconds())
565 return;
566 } else
568
569 lastServerQuery = cgi->CL_Milliseconds();
570
571 /* query master server? */
572 if (cgi->Cmd_Argc() == 2 && !Q_streq(cgi->Cmd_Argv(1), "local")) {
573 cgi->Com_DPrintf(DEBUG_CLIENT, "Query masterserver\n");
574 cgi->CL_QueryMasterServer("query", GAME_MP_QueryMasterServerThread);
575 }
576}
577
578static const cmdList_t serverListCmds[] = {
579 {"bookmark_add", GAME_MP_BookmarkAdd_f, "Add a new bookmark - see adrX cvars"},
580 {"server_info", GAME_MP_ServerInfo_f, nullptr},
581 {"serverlist", GAME_MP_PrintServerList_f, nullptr},
582 /* text id is servers in menu_multiplayer.ufo */
583 {"servers_click", GAME_MP_ServerListClick_f, nullptr},
584 {nullptr, nullptr, nullptr}
585};
587{
588 cgi = import;
589 /* register our variables */
590 for (int i = 0; i < MAX_BOOKMARKS; i++)
591 cgi->Cvar_Get(va("adr%i", i), "", CVAR_ARCHIVE, "Bookmark for network ip");
592 cl_serverlist = cgi->Cvar_Get("cl_serverlist", "0", CVAR_ARCHIVE, "0=show all, 1=hide full - servers on the serverlist");
593
594 cgi->Cmd_TableAddList(serverListCmds);
595}
596
598{
599 cgi->Cmd_TableRemoveList(serverListCmds);
600
601 cgi->NET_DatagramSocketClose(netDatagramSocket);
602 netDatagramSocket = nullptr;
603}
Shared game type headers.
Share stuff between the different cgame implementations.
#define _(String)
Definition cl_shared.h:44
cvar_t * port
Definition common.cpp:58
void Com_DPrintf(int level, const char *fmt,...)
A Com_Printf that only shows up if the "developer" cvar is set.
Definition common.cpp:440
#define PROTOCOL_VERSION
Definition common.h:134
#define PORT_SERVER
Definition common.h:137
#define S_COLOR_GREEN
Definition common.h:219
@ svc_oob
Definition common.h:156
#define UFO_VERSION
Definition common.h:36
#define PORT_CLIENT
Definition common.h:136
const cgame_import_t * cgi
#define CVAR_ARCHIVE
Definition cvar.h:40
#define MAX_TEAMS
Definition defines.h:98
#define DEBUG_CLIENT
Definition defines.h:59
const char * Info_BoolForKey(const char *s, const char *key)
const char * Info_ValueForKey(const char *s, const char *key)
Searches the string for the given key and returns the associated value, or an empty string.
int Info_IntegerForKey(const char *s, const char *key)
Info string handling.
#define MAX_INFO_STRING
Definition infostring.h:36
voidpf void * buf
Definition ioapi.h:42
teamData_t teamData
Serverlist menu callbacks headers for multiplayer.
#define SERVERQUERYTIMEOUT
static serverList_t serverList[MAX_SERVERLIST]
static int serverListPos
static void GAME_MP_AddServerToList(const char *node, const char *service)
Adds a server to the serverlist cache.
void GAME_MP_ServerListShutdown(void)
void GAME_MP_PingServers_f(void)
The first function called when entering the multiplayer menu, then CL_Frame takes over.
static char serverInfoText[1024]
static void GAME_MP_ServerListDiscoveryCallback(struct datagram_socket *s, const char *buf, int len, struct sockaddr *from)
static void GAME_MP_BookmarkAdd_f(void)
Add a new bookmark.
serverList_t * selectedServer
static bool GAME_MP_ProcessPingReply(serverList_t *server, const char *msg)
Parsed the server ping response.
static const cmdList_t serverListCmds[]
serverListStatus_t
@ SERVERLIST_HIDEEMPTY
@ SERVERLIST_HIDEFULL
@ SERVERLIST_SHOWALL
static void GAME_MP_ServerListClick_f(void)
Callback for bookmark nodes in multiplayer menu (mp_bookmarks).
static void GAME_MP_QueryMasterServerThread(const char *responseBuf, void *userdata)
static void GAME_MP_ServerInfoCallback(struct net_stream *s)
static void GAME_MP_PingServerCallback(struct net_stream *s)
static char userInfoText[256]
static void GAME_MP_PingServer(serverList_t *server)
Pings all servers in serverList.
static int serverListLength
static cvar_t * cl_serverlist
static void GAME_MP_ServerInfo_f(void)
#define MAX_SERVERLIST
static int lastServerQuery
static struct datagram_socket * netDatagramSocket
static char serverText[1024]
static bool serversAlreadyQueried
static void GAME_MP_PrintServerList_f(void)
Prints all the servers on the list to game console.
static bool GAME_MP_ShowServer(const serverList_t *server)
Perform the server filtering.
static void GAME_MP_ParseServerInfoMessage(dbuffer *msg, const char *hostname)
Serverbrowser text.
void GAME_MP_ParseTeamInfoMessage(dbuffer *msg)
Team selection text.
void GAME_MP_ServerListInit(const cgame_import_t *import)
Serverlist management headers for multiplayer.
#define MAX_BOOKMARKS
const char * Com_Parse(const char *data_p[], char *target, size_t size, bool replaceWhitespaces)
Parse a token out of a string.
Definition parse.cpp:107
Shared parsing functions.
#define SV_CMD_INFO
Definition q_shared.h:592
#define SV_CMD_STATUS
Definition q_shared.h:590
#define TEAM_NO_ACTIVE
Definition q_shared.h:60
QGL_EXTERN GLuint GLchar GLuint * len
Definition r_gl.h:99
QGL_EXTERN GLint i
Definition r_gl.h:113
#define Q_streq(a, b)
Definition shared.h:136
bool Q_strnull(const char *string)
Definition shared.h:138
#define DOUBLEQUOTE(x)
Definition shared.h:90
#define OBJZERO(obj)
Definition shared.h:178
#define MAX_VAR
Definition shared.h:36
void Q_strncpyz(char *dest, const char *src, size_t destsize)
Safe strncpy that ensures a trailing zero.
Definition shared.cpp:457
void Q_strcat(char *dest, size_t destsize, const char *format,...)
Safely (without overflowing the destination buffer) concatenates two strings.
Definition shared.cpp:475
bool Com_sprintf(char *dest, size_t size, const char *fmt,...)
copies formatted string with buffer-size checking
Definition shared.cpp:494
const char * va(const char *format,...)
does a varargs printf into a temp buffer, so I don't need to have varargs versions of all text functi...
Definition shared.cpp:410
This is a cvar definition. Cvars can be user modified and used in our menus e.g.
Definition cvar.h:71
char gametype[8]
char mapname[16]
char version[8]
char sv_hostname[MAX_OSPATH]
static const char * user
Data and interface to share data.
@ TEXT_LIST
Definition ui_dataids.h:31
@ TEXT_STANDARD
Definition ui_dataids.h:30
@ TEXT_MULTIPLAYER_USERTEAM
Definition ui_dataids.h:69
@ TEXT_MULTIPLAYER_USERLIST
Definition ui_dataids.h:68