UFO: Alien Invasion
Loading...
Searching...
No Matches
cl_irc.cpp
Go to the documentation of this file.
1
5
6/*
7All original material Copyright (C) 2002-2025 UFO: Alien Invasion.
8
9Most of this stuff comes from Warsow
10
11This program is free software; you can redistribute it and/or
12modify it under the terms of the GNU General Public License
13as published by the Free Software Foundation; either version 2
14of the License, or (at your option) any later version.
15
16This program is distributed in the hope that it will be useful,
17but WITHOUT ANY WARRANTY; without even the implied warranty of
18MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
19
20See the GNU General Public License for more details.
21
22You should have received a copy of the GNU General Public License
23along with this program; if not, write to the Free Software
24Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25
26*/
27
28#include "cl_irc.h"
29#include "client.h"
30#include "cl_language.h"
31#include "ui/ui_main.h"
32#include "ui/ui_nodes.h"
33#include "ui/ui_popup.h"
34#include "battlescape/cl_hud.h"
35#include "cgame/cl_game.h"
36
37#ifdef _WIN32
38# include <winerror.h>
39#else
40# include <netinet/in.h>
41# include <arpa/inet.h>
42# include <netdb.h>
43# include <fcntl.h>
44#endif
45
56/* menu cvar */
59
60static bool irc_connected;
61
62#define IRC_SEND_BUF_SIZE 512
63#define IRC_RECV_BUF_SIZE 1024
64
65typedef struct irc_user_s {
66 char key[MAX_VAR];
67 struct irc_user_s* next;
69
70typedef struct irc_channel_s {
72 char topic[256];
73 int users;
76
77/* numeric commands as specified by RFC 1459 - Internet Relay Chat Protocol */
78typedef enum irc_numeric_e {
79 /* command replies */
80 RPL_WELCOME = 1, /* ":Welcome to the Internet Relay Network <nick>!<user>@<host>" */
81 RPL_YOURHOST = 2, /* ":Your host is <servername>, running version <ver>" */
82 RPL_CREATED = 3, /* ":This server was created <date>" */
83 RPL_MYINFO = 4, /* "<servername> <version> <available user modes> <available channel modes>" */
84 RPL_ISUPPORT = 5, /* "<nick> <parameter> * :are supported by this server" */
85 RPL_HELLO = 20, /* ":Please wait while we process your connection" */
86 RPL_NONE = 300,
87 RPL_USERHOST = 302, /* ":[<reply>{<space><reply>}]" */
88 RPL_ISON = 303, /* ":[<nick> {<space><nick>}]" */
89 RPL_AWAY = 301, /* "<nick> :<away message>" */
90 RPL_UNAWAY = 305, /* ":You are no longer marked as being away" */
91 RPL_NOWAWAY = 306, /* ":You have been marked as being away" */
92 RPL_WHOISUSER = 311, /* "<nick> <user> <host> * :<real name>" */
93 RPL_WHOISSERVER = 312, /* "<nick> <server> :<server info>" */
94 RPL_WHOISOPERATOR = 313, /* "<nick> :is an IRC operator" */
95 RPL_WHOISIDLE = 317, /* "<nick> <integer> :seconds idle" */
96 RPL_ENDOFWHOIS = 318, /* "<nick> :End of /WHOIS list" */
97 RPL_WHOISCHANNELS = 319, /* "<nick> :{[@|+]<channel><space>}" */
98 RPL_WHOWASUSER = 314, /* "<nick> <user> <host> * :<real name>" */
99 RPL_ENDOFWHOWAS = 369, /* "<nick> :End of WHOWAS" */
100 RPL_WHOISACCOUNT = 330, /* "<nick> <account> :is logged in as" */
101
102 RPL_LISTSTART = 321, /* "Channel :Users Name" */
103 RPL_LIST = 322, /* "<channel> <# visible> :<topic>" */
104 RPL_LISTEND = 323, /* ":End of /LIST" */
105 RPL_CHANNELMODEIS = 324, /* "<channel> <mode> <mode params>" */
106 RPL_NOTOPIC = 331, /* "<channel> :No topic is set" */
107 RPL_TOPIC = 332, /* "<channel> :<topic>" */
108 RPL_TOPICWHOTIME = 333, /* "<channel> <nick> <time>" */
109 RPL_INVITING = 341, /* "<channel> <nick>" */
110 RPL_SUMMONING = 342, /* "<user> :Summoning user to IRC" */
111 RPL_VERSION = 351, /* "<version>.<debuglevel> <server> :<comments>" */
112 RPL_WHOREPLY = 352, /* "<channel> <user> <host> <server> <nick> <H|G>[*][@|+] :<hopcount> <real name>" */
113 RPL_ENDOFWHO = 315, /* "<name> :End of /WHO list" */
114 RPL_NAMREPLY = 353, /* "<channel> :[[@|+]<nick> [[@|+]<nick> [...]]]" */
115 RPL_ENDOFNAMES = 366, /* "<channel> :End of /NAMES list" */
116 RPL_LINKS = 364, /* "<mask> <server> :<hopcount> <server info>" */
117 RPL_ENDOFLINKS = 365, /* "<mask> :End of /LINKS list" */
118 RPL_BANLIST = 367, /* "<channel> <banid>" */
119 RPL_ENDOFBANLIST = 368, /* "<channel> :End of channel ban list" */
120 RPL_INFO = 371, /* ":<string>" */
121 RPL_ENDOFINFO = 374, /* ":End of /INFO list" */
122 RPL_MOTDSTART = 375, /* ":- <server> Message of the day - " */
123 RPL_MOTD = 372, /* ":- <text>" */
124 RPL_ENDOFMOTD = 376, /* ":End of /MOTD command" */
125 RPL_YOUREOPER = 381, /* ":You are now an IRC operator" */
126 RPL_REHASHING = 382, /* "<config file> :Rehashing" */
127 RPL_TIME = 391, /* "<server> :<string showing server's local time>" */
128 RPL_USERSSTART = 392, /* ":UserID Terminal Host" */
129 RPL_USERS = 393, /* ":%-8s %-9s %-8s" */
130 RPL_ENDOFUSERS = 394, /* ":End of users" */
131 RPL_NOUSERS = 395, /* ":Nobody logged in" */
132 RPL_TRACELINK = 200, /* "Link <version & debug level> <destination> <next server>" */
133 RPL_TRACECONNECTING = 201, /* "Try. <class> <server>" */
134 RPL_TRACEHANDSHAKE = 202, /* "H.S. <class> <server>" */
135 RPL_TRACEUNKNOWN = 203, /* "???? <class> [<client IP address in dot form>]" */
136 RPL_TRACEOPERATOR = 204, /* "Oper <class> <nick>" */
137 RPL_TRACEUSER = 205, /* "User <class> <nick>" */
138 RPL_TRACESERVER = 206, /* "Serv <class> <int>S <int>C <server> <nick!user|*!*>@<host|server>" */
139 RPL_TRACENEWTYPE = 208, /* "<newtype> 0 <client name>" */
140 RPL_TRACELOG = 261, /* "File <logfile> <debug level>" */
141 RPL_STATSLINKINFO = 211, /* "<linkname> <sendq> <sent messages> <sent bytes> <received messages> <received bytes> <time open>" */
142 RPL_STATSCOMMANDS = 212, /* "<command> <count>" */
143 RPL_STATSCLINE = 213, /* "C <host> * <name> <port> <class>" */
144 RPL_STATSNLINE = 214, /* "N <host> * <name> <port> <class>" */
145 RPL_STATSILINE = 215, /* "I <host> * <host> <port> <class>" */
146 RPL_STATSKLINE = 216, /* "K <host> * <username> <port> <class>" */
147 RPL_STATSYLINE = 218, /* "Y <class> <ping frequency> <connectfrequency> <max sendq>" */
148 RPL_ENDOFSTATS = 219, /* "<stats letter> :End of /STATS report" */
149 RPL_STATSLLINE = 241, /* "L <hostmask> * <servername> <maxdepth>" */
150 RPL_STATSUPTIME = 242, /* ":Server Up %d days %d:%02d:%02d" */
151 RPL_STATSOLINE = 243, /* "O <hostmask> * <name>" */
152 RPL_STATSHLINE = 244, /* "H <hostmask> * <servername>" */
153 RPL_UMODEIS = 221, /* "<user mode string>" */
154 RPL_LUSERCLIENT = 251, /* ":There are <integer> users and <integer> invisible on <integer> servers" */
155 RPL_LUSEROP = 252, /* "<integer> :operator(s) online" */
156 RPL_LUSERUNKNOWN = 253, /* "<integer> :unknown connection(s)" */
157 RPL_LUSERCHANNELS = 254, /* "<integer> :channels formed" */
158 RPL_LUSERME = 255, /* ":I have <integer> clients and <integer> servers" */
159 RPL_ADMINME = 256, /* "<server> :Administrative info" */
160 RPL_ADMINLOC1 = 257, /* ":<admin info>" */
161 RPL_ADMINLOC2 = 258, /* ":<admin info>" */
162 RPL_ADMINEMAIL = 259, /* ":<admin info>" */
165
166 /* error codes */
167 ERR_NOSUCHNICK = 401, /* "<nickname> :No such nick/channel" */
168 ERR_NOSUCHSERVER = 402, /* "<server name> :No such server" */
169 ERR_NOSUCHCHANNEL = 403, /* "<channel name> :No such channel" */
170 ERR_CANNOTSENDTOCHAN = 404, /* "<channel name> :Cannot send to channel" */
171 ERR_TOOMANYCHANNELS = 405, /* "<channel name> :You have joined too many channels" */
172 ERR_WASNOSUCHNICK = 406, /* "<nickname> :There was no such nickname" */
173 ERR_TOOMANYTARGETS = 407, /* "<target> :Duplicate recipients. No message delivered" */
174 ERR_NOORIGIN = 409, /* ":No origin specified" */
175 ERR_NORECIPIENT = 411, /* ":No recipient given (<command>)" */
176 ERR_NOTEXTTOSEND = 412, /* ":No text to send" */
177 ERR_NOTOPLEVEL = 413, /* "<mask> :No toplevel domain specified" */
178 ERR_WILDTOPLEVEL = 414, /* "<mask> :Wildcard in toplevel domain" */
179 ERR_UNKNOWNCOMMAND = 421, /* "<command> :Unknown command" */
180 ERR_NOMOTD = 422, /* ":MOTD File is missing" */
181 ERR_NOADMININFO = 423, /* "<server> :No administrative info available" */
182 ERR_FILEERROR = 424, /* ":File error doing <file op> on <file>" */
183 ERR_NONICKNAMEGIVEN = 431, /* ":No nickname given" */
184 ERR_ERRONEUSNICKNAME = 432, /* "<nick> :Erroneus nickname" */
185 ERR_NICKNAMEINUSE = 433, /* "<nick> :Nickname is already in use" */
186 ERR_NICKCOLLISION = 436, /* "<nick> :Nickname collision KILL" */
189 ERR_USERNOTINCHANNEL = 441, /* "<nick> <channel> :They aren't on that channel" */
190 ERR_NOTONCHANNEL = 442, /* "<channel> :You're not on that channel" */
191 ERR_USERONCHANNEL = 443, /* "<user> <channel> :is already on channel" */
192 ERR_NOLOGIN = 444, /* "<user> :User not logged in" */
193 ERR_SUMMONDISABLED = 445, /* ":SUMMON has been disabled" */
194 ERR_USERSDISABLED = 446, /* ":USERS has been disabled" */
195 ERR_NOTREGISTERED = 451, /* ":You have not registered" */
196 ERR_NEEDMOREPARAMS = 461, /* "<command> :Not enough parameters" */
197 ERR_ALREADYREGISTRED = 462, /* ":You may not reregister" */
198 ERR_NOPERMFORHOST = 463, /* ":Your host isn't among the privileged" */
199 ERR_PASSWDMISMATCH = 464, /* ":Password incorrect" */
200 ERR_YOUREBANNEDCREEP = 465, /* ":You are banned from this server" */
201 ERR_BADNAME = 468, /* ":Your username is invalid" */
202 ERR_KEYSET = 467, /* "<channel> :Channel key already set" */
203 ERR_CHANNELISFULL = 471, /* "<channel> :Cannot join channel (+l)" */
204 ERR_UNKNOWNMODE = 472, /* "<char> :is unknown mode char to me" */
205 ERR_INVITEONLYCHAN = 473, /* "<channel> :Cannot join channel (+i)" */
206 ERR_BANNEDFROMCHAN = 474, /* "<channel> :Cannot join channel (+b)" */
207 ERR_BADCHANNELKEY = 475, /* "<channel> :Cannot join channel (+k)" */
208 ERR_NOPRIVILEGES = 481, /* ":Permission Denied- You're not an IRC operator" */
209 ERR_CHANOPRIVSNEEDED = 482, /* "<channel> :You're not channel operator" */
210 ERR_CANTKILLSERVER = 483, /* ":You cant kill a server!" */
211 ERR_NOOPERHOST = 491, /* ":No O-lines for your host" */
212 ERR_UMODEUNKNOWNFLAG = 501, /* ":Unknown MODE flag" */
213 ERR_USERSDONTMATCH = 502, /* ":Cant change mode for other users" */
218/* ERR_TOOMANYWATCH = 512,*/
225
230
231typedef enum irc_nick_prefix_e {
236
237/* commands can be numeric's or string */
238typedef struct irc_command_s {
239 union {
240 /* tagged union */
242 const char* string;
243 } id;
246
247/* server <- client messages */
258
259static struct net_stream* irc_stream;
260
261static const char IRC_INVITE_FOR_A_GAME[] = "UFOAIINVITE;";
262
265
266static char irc_buffer[4096];
267
268static void Irc_Logic_RemoveChannelName(irc_channel_t* channel, const char* nick);
269static void Irc_Logic_AddChannelName(irc_channel_t* channel, irc_nick_prefix_t prefix, const char* nick);
270static void Irc_Client_Names_f(void);
271static bool Irc_Client_Join(const char* channel, const char* password);
272static void Irc_Logic_Disconnect(const char* reason);
273
274static bool Irc_AppendToBuffer(const char* const msg, ...) __attribute__((format(__printf__, 1, 2)));
275static bool Irc_Proto_ParseServerMsg(const char* txt, size_t txt_len, irc_server_msg_t* msg);
276static bool Irc_Proto_Enqueue(const char* msg, size_t msg_len);
277
278static bool Irc_Net_Connect(const char* host, const char* port);
279static bool Irc_Net_Disconnect(void);
280static void Irc_Net_Send(const char* msg, size_t msg_len);
281
282static void Irc_Connect_f(void);
283static void Irc_Disconnect_f(void);
284static void Irc_Input_Deactivate_f(void);
285
286/*
287===============================================================
288Common functions
289===============================================================
290*/
291
292static inline bool Irc_IsChannel (const char* target)
293{
294 assert(target);
295 return (target[0] == '#' || target[0] == '&');
296}
297
298static void Irc_ParseName (const char* mask, char* nick, size_t size, irc_nick_prefix_t* prefix)
299{
300 if (mask[0] == IRC_NICK_PREFIX_OP || mask[0] == IRC_NICK_PREFIX_VOICE) {
301 *prefix = (irc_nick_prefix_t) *mask; /* read prefix */
302 ++mask; /* crop prefix from mask */
303 } else {
304 *prefix = IRC_NICK_PREFIX_NONE;
305 }
306 const char* emph = strchr(mask, '!');
307 if (emph) {
308 size_t length = emph - mask;
309 if (length >= size - 1)
310 length = size - 1;
311 /* complete hostmask, crop anything after ! */
312 memcpy(nick, mask, length);
313 nick[length] = '\0';
314 } else {
315 /* just the nickname, use as is */
316 Q_strncpyz(nick, mask, size);
317 }
318}
319
320/*
321===============================================================
322Protocol functions
323===============================================================
324*/
325
331
332typedef struct irc_bucket_message_s {
333 char* msg;
334 size_t msg_len;
335 struct irc_bucket_message_s* next;
337
338typedef struct irc_bucket_s {
340 unsigned int message_size;
341 unsigned int character_size;
345
347
351static bool Irc_Proto_Connect (const char* host, const char* port)
352{
353 const bool status = Irc_Net_Connect(host, port);
354 if (!status) {
355 irc_bucket.first_msg = nullptr;
356 irc_bucket.message_size = 0;
357 irc_bucket.character_size = 0;
358 irc_bucket.last_refill = CL_Milliseconds();
359 irc_bucket.character_token = (double)irc_characterBucketBurst->value;
360 }
361 return status;
362}
363
367static bool Irc_Proto_Disconnect (void)
368{
369 const bool status = Irc_Net_Disconnect();
370 if (!status) {
371 irc_bucket_message_t* msg = irc_bucket.first_msg;
372 while (msg) {
373 irc_bucket_message_t* prev = msg;
374 msg = msg->next;
375 Mem_Free(prev->msg);
376 Mem_Free(prev);
377 }
378 irc_bucket.first_msg = nullptr;
379 irc_bucket.message_size = 0;
380 irc_bucket.character_size = 0;
381 }
382 return status;
383}
384
388static bool Irc_Proto_Quit (const char* quitmsg)
389{
390 char msg[IRC_SEND_BUF_SIZE];
391 const int msg_len = snprintf(msg, sizeof(msg) - 1, "QUIT %s\r\n", quitmsg);
392 msg[sizeof(msg) - 1] = '\0';
393 Irc_Net_Send(msg, msg_len); /* send immediately */
394 return false;
395}
396
400static bool Irc_Proto_Nick (const char* nick)
401{
402 char msg[IRC_SEND_BUF_SIZE];
403 const int msg_len = snprintf(msg, sizeof(msg) - 1, "NICK %s\r\n", nick);
404 msg[sizeof(msg) - 1] = '\0';
405 return Irc_Proto_Enqueue(msg, msg_len);
406}
407
411static bool Irc_Proto_User (const char* user, bool invisible, const char* name)
412{
413 char msg[IRC_SEND_BUF_SIZE];
414 const int msg_len = snprintf(msg, sizeof(msg) - 1, "USER %s %c * :%s\r\n", user, invisible ? '8' : '0', name);
415 msg[sizeof(msg) - 1] = '\0';
416 return Irc_Proto_Enqueue(msg, msg_len);
417}
418
422static bool Irc_Proto_Password (const char* password)
423{
424 char msg[IRC_SEND_BUF_SIZE];
425 const int msg_len = snprintf(msg, sizeof(msg) - 1, "PASS %s\r\n", password);
426 msg[sizeof(msg) - 1] = '\0';
427 return Irc_Proto_Enqueue(msg, msg_len);
428}
429
433static bool Irc_Proto_Join (const char* channel, const char* password)
434{
435 char msg[IRC_SEND_BUF_SIZE];
436 const int msg_len = password
437 ? snprintf(msg, sizeof(msg) - 1, "JOIN %s %s\r\n", channel, password)
438 : snprintf(msg, sizeof(msg) - 1, "JOIN %s\r\n", channel);
439 msg[sizeof(msg) - 1] = '\0';
440
441 /* only one channel allowed */
442 if (chan) {
443 Com_Printf("Already in a channel\n");
444 return false;
445 }
446
447 chan = &ircChan;
448 OBJZERO(*chan);
449 Q_strncpyz(chan->name, channel, sizeof(chan->name));
450 return Irc_Proto_Enqueue(msg, msg_len);
451}
452
456static bool Irc_Proto_Part (const char* channel)
457{
458 char msg[IRC_SEND_BUF_SIZE];
459 const int msg_len = snprintf(msg, sizeof(msg) - 1, "PART %s\r\n", channel);
460 msg[sizeof(msg) - 1] = '\0';
461 return Irc_Proto_Enqueue(msg, msg_len);
462}
463
467static bool Irc_Proto_Mode (const char* target, const char* modes, const char* params)
468{
469 char msg[IRC_SEND_BUF_SIZE];
470 const int msg_len = params
471 ? snprintf(msg, sizeof(msg) - 1, "MODE %s %s %s\r\n", target, modes, params)
472 : snprintf(msg, sizeof(msg) - 1, "MODE %s %s\r\n", target, modes);
473 msg[sizeof(msg) - 1] = '\0';
474 return Irc_Proto_Enqueue(msg, msg_len);
475}
476
480static bool Irc_Proto_Topic (const char* channel, const char* topic)
481{
482 char msg[IRC_SEND_BUF_SIZE];
483 const int msg_len = topic
484 ? snprintf(msg, sizeof(msg) - 1, "TOPIC %s :%s\r\n", channel, topic)
485 : snprintf(msg, sizeof(msg) - 1, "TOPIC %s\r\n", channel);
486 msg[sizeof(msg) - 1] = '\0';
487 return Irc_Proto_Enqueue(msg, msg_len);
488}
489
495static bool Irc_Proto_Msg (const char* target, const char* text)
496{
497 if (*text == '/') {
498 Com_DPrintf(DEBUG_CLIENT, "Don't send irc commands as PRIVMSG\n");
499 Cbuf_AddText("%s\n", &text[1]);
500 return true;
501 } else {
502 char msg[IRC_SEND_BUF_SIZE];
503 const int msg_len = snprintf(msg, sizeof(msg) - 1, "PRIVMSG %s :%s\r\n", target, text);
504 msg[sizeof(msg) - 1] = '\0';
505 return Irc_Proto_Enqueue(msg, msg_len);
506 }
507}
508
512static bool Irc_Proto_Notice (const char* target, const char* text)
513{
514 char msg[IRC_SEND_BUF_SIZE];
515 const int msg_len = snprintf(msg, sizeof(msg) - 1, "NOTICE %s :%s\r\n", target, text);
516 msg[sizeof(msg) - 1] = '\0';
517 return Irc_Proto_Enqueue(msg, msg_len);
518}
519
523static void Irc_Proto_Pong (const char* nick, const char* server, const char* cookie)
524{
525 char msg[IRC_SEND_BUF_SIZE];
526 const int msg_len = cookie
527 ? snprintf(msg, sizeof(msg) - 1, "PONG %s %s :%s\r\n", nick, server, cookie)
528 : snprintf(msg, sizeof(msg) - 1, "PONG %s %s\r\n", nick, server);
529 msg[sizeof(msg) - 1] = '\0';
530 Irc_Net_Send(msg, msg_len); /* send immediately */
531}
532
536static bool Irc_Proto_Kick (const char* channel, const char* nick, const char* reason)
537{
538 char msg[IRC_SEND_BUF_SIZE];
539 const int msg_len = reason
540 ? snprintf(msg, sizeof(msg) - 1, "KICK %s %s :%s\r\n", channel, nick, reason)
541 : snprintf(msg, sizeof(msg) - 1, "KICK %s %s :%s\r\n", channel, nick, nick);
542 msg[sizeof(msg) - 1] = '\0';
543 return Irc_Proto_Enqueue(msg, msg_len);
544}
545
549static bool Irc_Proto_Who (const char* nick)
550{
551 char msg[IRC_SEND_BUF_SIZE];
552 const int msg_len = snprintf(msg, sizeof(msg) - 1, "WHO %s\r\n", nick);
553 msg[sizeof(msg) - 1] = '\0';
554 return Irc_Proto_Enqueue(msg, msg_len);
555}
556
560static bool Irc_Proto_Whois (const char* nick)
561{
562 char msg[IRC_SEND_BUF_SIZE];
563 const int msg_len = snprintf(msg, sizeof(msg) - 1, "WHOIS %s\r\n", nick);
564 msg[sizeof(msg) - 1] = '\0';
565 return Irc_Proto_Enqueue(msg, msg_len);
566}
567
571static bool Irc_Proto_Whowas (const char* nick)
572{
573 char msg[IRC_SEND_BUF_SIZE];
574 const int msg_len = snprintf(msg, sizeof(msg) - 1, "WHOWAS %s\r\n", nick);
575 msg[sizeof(msg) - 1] = '\0';
576 return Irc_Proto_Enqueue(msg, msg_len);
577}
578
582static bool Irc_Proto_PollServerMsg (irc_server_msg_t* msg, bool* msg_complete)
583{
584 static char buf[IRC_RECV_BUF_SIZE];
585 static char* last = buf;
586 *msg_complete = false;
587 /* recv packet */
588 const int recvd = NET_StreamDequeue(irc_stream, last, sizeof(buf) - (last - buf) - 1);
589 if (recvd < 0)
590 return true;
591
592 /* terminate buf string */
593 const char* const begin = buf;
594 last += recvd;
595 *last = '\0';
596 if (last != begin) {
597 /* buffer not empty; */
598 const char* const end = strstr(begin, "\r\n");
599 if (end) {
600 /* complete command in buffer, parse */
601 const size_t cmd_len = end + 2 - begin;
602 if (!Irc_Proto_ParseServerMsg(begin, cmd_len, msg)) {
603 /* parsing successful */
604 /* move succeeding commands to begin of buffer */
605 memmove(buf, end + 2, sizeof(buf) - cmd_len);
606 last -= cmd_len;
607 *msg_complete = true;
608 } else {
609 /* parsing failure, fatal */
610 Com_Printf("Received invalid packet from server\n");
611 return true;
612 }
613 }
614 } else {
615 *msg_complete = false;
616 }
617 return false;
618}
619
626static bool Irc_AppendToBuffer (const char* const msg, ...)
627{
628 va_list ap;
629 char appendString[2048];
630
631 va_start(ap, msg);
632 Q_vsnprintf(appendString, sizeof(appendString), msg, ap);
633 va_end(ap);
634
635 while (strlen(irc_buffer) + strlen(appendString) + 1 >= sizeof(irc_buffer)) {
636 const char* n;
637 if (!(n = strchr(irc_buffer, '\n'))) {
638 irc_buffer[0] = '\0';
639 break;
640 }
641 memmove(irc_buffer, n + 1, strlen(n));
642 }
643
644 Q_strcat(irc_buffer, sizeof(irc_buffer), "%s\n", appendString);
645 if (irc_logConsole->integer) {
646 char appendStringCut[1000];
647 Q_strncpyz(appendStringCut, appendString, lengthof(appendStringCut));
648 Com_Printf("IRC: %s\n", appendStringCut);
649 }
650
652 UI_TextScrollEnd("irc.chat.mainBody.irc_data");
653
654 if (irc_showIfNotInMenu->integer && !Q_streq(UI_GetActiveWindowName(), "irc")) {
656 GAME_AddChatMessage(appendString);
657 return true;
658 }
659 return false;
660}
661
662static void Irc_Client_CmdRplWhowasuser (const char* params, const char* trailing)
663{
665 const char* nick = "", *user = "", *host = "", *real_name = trailing;
666 unsigned int i = 0;
667
668 /* parse params "<nick> <user> <host> * :<real name>" */
669 Q_strncpyz(buf, params, sizeof(buf));
670 for (char* p = strtok(buf, " "); p; p = strtok(nullptr, " "), ++i) {
671 switch (i) {
672 case 1:
673 nick = p;
674 break;
675 case 2:
676 user = p;
677 break;
678 case 3:
679 host = p;
680 break;
681 }
682 }
683 Irc_AppendToBuffer("^B%s was %s@%s : %s", nick, user, host, real_name);
684}
685
686static inline void Irc_Client_CmdTopic (const char* prefix, const char* trailing)
687{
688 Cvar_ForceSet("irc_topic", trailing);
689}
690
691static void Irc_Client_CmdRplTopic (const char* params, const char* trailing)
692{
693 const char* channel = strchr(params, ' ');
694 if (channel) {
695 ++channel;
696 Irc_Client_CmdTopic(params, trailing);
697 }
698}
699
700static void Irc_Client_CmdRplWhoisuser (const char* params, const char* trailing)
701{
703 const char* nick = "", *user = "", *host = "", *real_name = trailing;
704 unsigned int i = 0;
705
706 /* parse params "<nick> <user> <host> * :<real name>" */
708 for (char* p = strtok(buf, " "); p; p = strtok(nullptr, " "), ++i) {
709 switch (i) {
710 case 1:
711 nick = p;
712 break;
713 case 2:
714 user = p;
715 break;
716 case 3:
717 host = p;
718 break;
719 }
720 }
721 Irc_AppendToBuffer("^B%s is %s@%s : %s", nick, user, host, real_name);
722}
723
724static void Irc_Client_CmdRplWhoisserver (const char* params, const char* trailing)
725{
727 const char* nick = "", *server = "", *server_info = trailing;
728 unsigned int i = 0;
729
730 /* parse params "<nick> <server> :<server info>" */
732 for (char* p = strtok(buf, " "); p; p = strtok(nullptr, " "), ++i) {
733 switch (i) {
734 case 1:
735 nick = p;
736 break;
737 case 2:
738 server = p;
739 break;
740 }
741 }
742 Irc_AppendToBuffer("^B%s using %s : %s", nick, server, server_info);
743}
744
745static void Irc_Client_CmdRplWhoisaccount (const char* params, const char* trailing)
746{
748 const char* nick = "", *account = "";
749 unsigned int i = 0;
750
751 /* parse params "<nick> <account> :is logged in as" */
753 for (char* p = strtok(buf, " "); p; p = strtok(nullptr, " "), ++i) {
754 switch (i) {
755 case 1:
756 nick = p;
757 break;
758 case 2:
759 account = p;
760 break;
761 }
762 }
763 Irc_AppendToBuffer("^B%s %s %s", nick, trailing, account);
764}
765
766static void Irc_Client_CmdRplWhoisidle (const char* params, const char* trailing)
767{
769 const char* nick = "", *idle = "";
770 unsigned int i = 0;
771
772 /* parse params "<nick> <integer> :seconds idle" */
774 for (char* p = strtok(buf, " "); p; p = strtok(nullptr, " "), ++i) {
775 switch (i) {
776 case 1:
777 nick = p;
778 break;
779 case 2:
780 idle = p;
781 break;
782 }
783 }
784 Irc_AppendToBuffer("^B%s is %s %s", nick, idle, trailing);
785}
786
787static void Irc_Client_CmdRplWhoreply (const char* params, const char* trailing)
788{
790 const char* channel = "", *user = "", *host = "", *server = "", *nick = "", *hg = "";
791 unsigned int i = 0;
792
793 /* parse params "<channel> <user> <host> <server> <nick> <H|G>[*][@|+] :<hopcount> <real name>" */
795 for (char* p = strtok(buf, " "); p; p = strtok(nullptr, " "), ++i) {
796 switch (i) {
797 case 0:
798 channel = p;
799 break;
800 case 1:
801 user = p;
802 break;
803 case 2:
804 host = p;
805 break;
806 case 3:
807 server = p;
808 break;
809 case 4:
810 nick = p;
811 break;
812 case 5:
813 hg = p;
814 break;
815 }
816 }
817 Irc_AppendToBuffer("%s %s %s %s %s %s : %s", channel, user, host, server, nick, hg, trailing);
818}
819
820static void Irc_Client_CmdMode (const char* prefix, const char* params, const char* trailing)
821{
822 char nick[MAX_VAR];
824 Irc_ParseName(prefix, nick, sizeof(nick), &p);
825 Irc_AppendToBuffer("^B%s sets mode %s", nick, params);
826}
827
828static void Irc_Client_CmdJoin (const char* prefix, const char* params, const char* trailing)
829{
830 char nick[MAX_VAR];
832 Irc_ParseName(prefix, nick, sizeof(nick), &p);
833 Irc_AppendToBuffer("^BJoined: %s", nick);
835}
836
837static void Irc_Client_CmdPart (const char* prefix, const char* trailing)
838{
839 char nick[MAX_VAR];
841 Irc_ParseName(prefix, nick, sizeof(nick), &p);
842 Irc_AppendToBuffer("^BLeft: %s (%s)", nick, prefix);
844}
845
846static void Irc_Client_CmdQuit (const char* prefix, const char* params, const char* trailing)
847{
848 char nick[MAX_VAR];
850 Irc_ParseName(prefix, nick, sizeof(nick), &p);
851 Irc_AppendToBuffer("^BQuits: %s (%s)", nick, trailing);
853}
854
855static void Irc_Client_CmdKill (const char* prefix, const char* params, const char* trailing)
856{
857 char nick[MAX_VAR];
859 Irc_ParseName(prefix, nick, sizeof(nick), &p);
860 Irc_AppendToBuffer("^BKilled: %s (%s)", nick, trailing);
862}
863
864static void Irc_Client_CmdKick (const char* prefix, const char* params, const char* trailing)
865{
867 char nick[MAX_VAR];
869 Irc_ParseName(prefix, nick, sizeof(nick), &p);
871 const char* channel = strtok(buf, " ");
872 const char* victim = strtok(nullptr, " ");
873 if (Q_streq(victim, irc_nick->string)) {
874 /* we have been kicked */
875 Irc_AppendToBuffer("^BYou were kicked from %s by %s (%s)", channel, nick, trailing);
876 } else {
877 /* someone else was kicked */
878 Irc_AppendToBuffer("^B%s kicked %s (%s)", nick, victim, trailing);
879 }
881}
882
886static void Irc_Client_CmdNick (const char* prefix, const char* params, const char* trailing)
887{
888 char nick[MAX_VAR];
890
891 /* not connected */
892 if (!chan)
893 return;
894
895 Irc_ParseName(prefix, nick, sizeof(nick), &p);
896 if (Q_streq(irc_nick->string, nick))
897 Cvar_ForceSet("cl_name", trailing);
898 Irc_AppendToBuffer("%s is now known as %s", nick, trailing);
900 Irc_Logic_AddChannelName(chan, p, trailing);
901}
902
903#define IRC_CTCP_MARKER_CHR '\001'
904#define IRC_CTCP_MARKER_STR "\001"
905
909static void Irc_Client_CmdPrivmsg (const char* prefix, const char* params, const char* trailing)
910{
911 char nick[MAX_VAR];
912 const char* const emph = strchr(prefix, '!');
913 const char* ctcp = strchr(trailing, IRC_CTCP_MARKER_CHR);
914 OBJZERO(nick);
915 if (emph)
916 memcpy(nick, prefix, emph - prefix);
917 else
918 Q_strncpyz(nick, prefix, MAX_VAR);
919
920 if (ctcp) {
921 if (Q_streq(trailing + 1, "VERSION" IRC_CTCP_MARKER_STR)) {
922 /* response with the game version */
924 /*Irc_Proto_Notice(nick, IRC_CTCP_MARKER_STR "VERSION " UFO_VERSION " " CPUSTRING " " "May 14 2025" " " BUILDSTRING);*/
925 Com_DPrintf(DEBUG_CLIENT, "Irc_Client_CmdPrivmsg: Response to version query\n");
926 } else if (!strncmp(trailing + 1, "PING", 4)) {
927 /* Used to measure the delay of the IRC network between clients. */
928 char response[IRC_SEND_BUF_SIZE];
929 strcpy(response, trailing);
930 response[2] = 'O'; /* PING => PONG */
931 Irc_Proto_Notice(nick, response);
932 } else if (Q_streq(trailing + 1, "TIME" IRC_CTCP_MARKER_STR)) {
933 const time_t t = time(nullptr);
934 char response[IRC_SEND_BUF_SIZE];
935 const size_t response_len = sprintf(response, IRC_CTCP_MARKER_STR "TIME :%s" IRC_CTCP_MARKER_STR, ctime(&t));
936 response[response_len - 1] = '\0'; /* remove trailing \n */
937 Irc_Proto_Notice(nick, response);
938 } else {
939 Com_Printf("Irc_Client_CmdPrivmsg: Unknown ctcp command: '%s'\n", trailing);
940 }
941 } else {
942 if (!strncmp(trailing, IRC_INVITE_FOR_A_GAME, strlen(IRC_INVITE_FOR_A_GAME))) {
943 char serverIPAndPort[128];
944 Q_strncpyz(serverIPAndPort, trailing + strlen(IRC_INVITE_FOR_A_GAME), sizeof(serverIPAndPort));
945 /* values are splitted by ; */
946 char* port = strstr(serverIPAndPort, ";");
947 if (port == nullptr) {
948 Com_DPrintf(DEBUG_CLIENT, "Invalid irc invite message received\n");
949 return;
950 }
951
952 /* split ip and port */
953 *port++ = '\0';
954
955 /* the version is optional */
956 char* version = strstr(port, ";");
957 if (version != nullptr) {
958 /* split port and version */
959 *version++ = '\0';
960 if (!Q_streq(version, UFO_VERSION)) {
961 Com_DPrintf(DEBUG_CLIENT, "irc invite message from different game version received: %s (versus our version: " UFO_VERSION ")\n",
962 version);
963 return;
964 }
965 }
966
968 UI_ExecuteConfunc("multiplayer_invite_server_info %s %s", serverIPAndPort, port);
969
970 UI_PushWindow("multiplayer_invite");
971 } else if (!Irc_AppendToBuffer("<%s> %s", nick, trailing)) {
972 /* check whether this is no message to the channel - but to the user */
973 if (params && !Q_streq(params, irc_defaultChannel->string)) {
974 S_StartLocalSample("misc/lobbyprivmsg", SND_VOLUME_DEFAULT);
975 GAME_AddChatMessage(va("<%s> %s\n", nick, trailing));
976 } else if (strstr(trailing, irc_nick->string)) {
977 S_StartLocalSample("misc/lobbyprivmsg", SND_VOLUME_DEFAULT);
978 GAME_AddChatMessage(va("<%s> %s\n", nick, trailing));
979 }
980 }
981
983 Com_Printf(S_COLOR_GREEN "<%s@lobby> %s\n", nick, trailing);
984 }
985 }
986}
987
988static void Irc_Client_CmdRplNamreply (const char* params, const char* trailing)
989{
990 char* space;
991 char nick[MAX_VAR];
992 const size_t len = strlen(trailing) + 1;
994
995 if (!chan)
996 return;
997
998 char* const parseBuf = Mem_PoolAllocTypeN(char, len, cl_ircSysPool);
999 if (!parseBuf)
1000 return;
1001
1002 Q_strncpyz(parseBuf, trailing, len);
1003 char* pos = parseBuf;
1004
1005 do {
1006 /* names are space separated */
1007 space = strstr(pos, " ");
1008 if (space)
1009 *space = '\0';
1010 Irc_ParseName(pos, nick, sizeof(nick), &p);
1012 if (space)
1013 pos = space + 1;
1014 } while (space && *pos);
1015
1016 Mem_Free(parseBuf);
1017}
1018
1022static void Irc_Client_CmdRplEndofnames (const char* params, const char* trailing)
1023{
1024}
1025
1030{
1031 irc_command_t cmd;
1032 const char* p = nullptr;
1033 cmd.type = msg->type;
1034
1036
1037 switch (cmd.type) {
1039 cmd.id.numeric = msg->id.numeric;
1040 switch (cmd.id.numeric) {
1041 case RPL_HELLO:
1042 case RPL_WELCOME:
1043 case RPL_YOURHOST:
1044 case RPL_CREATED:
1045 case RPL_MYINFO:
1046 case RPL_MOTDSTART:
1047 case RPL_MOTD:
1048 case RPL_LOCALUSERS:
1049 case RPL_GLOBALUSERS:
1050 case RPL_ISUPPORT:
1051 case RPL_LUSEROP:
1052 case RPL_LUSERUNKNOWN:
1053 case RPL_LUSERCHANNELS:
1054 case RPL_LUSERCLIENT:
1055 case RPL_LUSERME:
1056 return true;
1057
1058 /* read our own motd */
1059 case RPL_ENDOFMOTD:
1060 Irc_AppendToBuffer("%s", CL_Translate("*msgid:irc_motd"));
1061 return true;
1062 case RPL_NAMREPLY:
1064 return true;
1065 case RPL_ENDOFNAMES:
1067 return true;
1068 case RPL_TOPIC:
1070 return true;
1071 case RPL_NOTOPIC:
1072 return true;
1073 case RPL_WHOISUSER:
1075 return true;
1076 case RPL_WHOISSERVER:
1078 return true;
1079 case RPL_WHOISIDLE:
1081 return true;
1082 case RPL_WHOISACCOUNT:
1084 return true;
1085 case RPL_WHOREPLY:
1087 return true;
1088 case RPL_WHOWASUSER:
1090 return true;
1091 case RPL_ENDOFWHO:
1092 case RPL_WHOISCHANNELS:
1093 case RPL_WHOISOPERATOR:
1094 case RPL_ENDOFWHOIS:
1095 case RPL_ENDOFWHOWAS:
1096 p = strchr(msg->params, ' ');
1097 if (p) {
1098 ++p;
1099 Irc_AppendToBuffer("%s %s", p, msg->trailing);
1100 }
1101 return true;
1102
1103 case ERR_NICKNAMEINUSE:
1104 case ERR_NOSUCHNICK:
1107 case ERR_NICKCOLLISION:
1108 Irc_AppendToBuffer("%s : %s", msg->params, msg->trailing);
1109 UI_PushWindow("irc_changename");
1110 break;
1111 /* error codes */
1112 case ERR_NOSUCHSERVER:
1113 case ERR_NOSUCHCHANNEL:
1116 case ERR_WASNOSUCHNICK:
1117 case ERR_TOOMANYTARGETS:
1118 case ERR_NOORIGIN:
1119 case ERR_NORECIPIENT:
1120 case ERR_NOTEXTTOSEND:
1121 case ERR_NOTOPLEVEL:
1122 case ERR_WILDTOPLEVEL:
1123 case ERR_UNKNOWNCOMMAND:
1124 case ERR_NOMOTD:
1125 case ERR_NOADMININFO:
1126 case ERR_FILEERROR:
1127 case ERR_BANNICKCHANGE:
1128 case ERR_NCHANGETOOFAST:
1130 case ERR_NOTONCHANNEL:
1131 case ERR_USERONCHANNEL:
1132 case ERR_NOLOGIN:
1133 case ERR_SUMMONDISABLED:
1134 case ERR_USERSDISABLED:
1135 case ERR_NOTREGISTERED:
1136 case ERR_NEEDMOREPARAMS:
1138 case ERR_NOPERMFORHOST:
1139 case ERR_PASSWDMISMATCH:
1141 case ERR_BADNAME:
1142 case ERR_KEYSET:
1143 case ERR_CHANNELISFULL:
1144 case ERR_UNKNOWNMODE:
1145 case ERR_INVITEONLYCHAN:
1146 case ERR_BANNEDFROMCHAN:
1147 case ERR_BADCHANNELKEY:
1148 case ERR_NOPRIVILEGES:
1150 case ERR_CANTKILLSERVER:
1151 case ERR_NOOPERHOST:
1153 case ERR_USERSDONTMATCH:
1154 case ERR_GHOSTEDCLIENT:
1155 case ERR_LAST_ERR_MSG:
1156 case ERR_SILELISTFULL:
1157 case ERR_NOSUCHGLINE:
1158 case ERR_BADPING:
1159 case ERR_TOOMANYDCC:
1160 case ERR_LISTSYNTAX:
1161 case ERR_WHOSYNTAX:
1162 case ERR_WHOLIMEXCEED:
1163 Irc_AppendToBuffer("%s : %s", msg->params, msg->trailing);
1164 return true;
1165 default:
1166 Com_DPrintf(DEBUG_CLIENT, "<%s> [%i] %s : %s\n", msg->prefix, cmd.id.numeric, msg->params, msg->trailing);
1167 return true;
1168 } /* switch */
1169 break;
1170 case IRC_COMMAND_STRING:
1171 cmd.id.string = msg->id.string;
1172#ifdef DEBUG
1173 Com_DPrintf(DEBUG_CLIENT, "<%s> [%s] %s : %s\n", msg->prefix, cmd.id.string, msg->params, msg->trailing);
1174#endif
1175 if (!strncmp(cmd.id.string, "NICK", 4))
1176 Irc_Client_CmdNick(msg->prefix, msg->params, msg->trailing);
1177 else if (!strncmp(cmd.id.string, "QUIT", 4))
1178 Irc_Client_CmdQuit(msg->prefix, msg->params, msg->trailing);
1179 else if (!strncmp(cmd.id.string, "NOTICE", 6)) {
1180 if (irc_logConsole->integer)
1181 Com_Printf("%s\n", msg->trailing);
1182 } else if (!strncmp(cmd.id.string, "PRIVMSG", 7))
1183 Irc_Client_CmdPrivmsg(msg->prefix, msg->params, msg->trailing);
1184 else if (!strncmp(cmd.id.string, "MODE", 4))
1185 Irc_Client_CmdMode(msg->prefix, msg->params, msg->trailing);
1186 else if (!strncmp(cmd.id.string, "JOIN", 4))
1187 Irc_Client_CmdJoin(msg->prefix, msg->params, msg->trailing);
1188 else if (!strncmp(cmd.id.string, "PART", 4))
1190 else if (!strncmp(cmd.id.string, "TOPIC", 5))
1192 else if (!strncmp(cmd.id.string, "KILL", 4))
1193 Irc_Client_CmdKill(msg->prefix, msg->params, msg->trailing);
1194 else if (!strncmp(cmd.id.string, "KICK", 4))
1195 Irc_Client_CmdKick(msg->prefix, msg->params, msg->trailing);
1196 else if (!strncmp(cmd.id.string, "PING", 4))
1197 Irc_Proto_Pong(irc_nick->string, msg->params, msg->trailing[0] ? msg->trailing : nullptr);
1198 else if (!strncmp(cmd.id.string, "ERROR", 5)) {
1200 Q_strncpyz(popupText, msg->trailing, sizeof(popupText));
1201 UI_Popup(_("Error"), popupText);
1202 } else
1203 Irc_AppendToBuffer("%s", msg->trailing);
1204 break;
1205 } /* switch */
1206 return false;
1207}
1208
1209static bool Irc_Proto_ParseServerMsg (const char* txt, size_t txt_len, irc_server_msg_t* msg)
1210{
1211 const char* c = txt;
1212 const char* end = txt + txt_len;
1213 msg->prefix[0] = '\0';
1214 msg->params[0] = '\0';
1215 msg->trailing[0] = '\0';
1216 if (c < end && *c == ':') {
1217 /* parse prefix */
1218 char* prefix = msg->prefix;
1219 int i = 1;
1220 const size_t size = sizeof(msg->prefix);
1221 ++c;
1222 while (c < end && *c != '\r' && *c != ' ') {
1223 if (i++ >= size)
1224 return true;
1225 *prefix++ = *c++;
1226 }
1227 *prefix = '\0';
1228 ++c;
1229 }
1230 if (c < end && *c != '\r') {
1231 /* parse command */
1232 if (c < end && *c >= '0' && *c <= '9') {
1233 /* numeric command */
1234 char command[4];
1235 for (int i = 0; i < 3; ++i) {
1236 if (c < end && *c >= '0' && *c <= '9')
1237 command[i] = *c++;
1238 else
1239 return true;
1240 }
1241 command[3] = '\0';
1243 msg->id.numeric = (irc_numeric_t)atoi(command);
1244 } else if (c < end && *c != '\r') {
1245 /* string command */
1246 int i = 1;
1247 const size_t size = sizeof(msg->id.string);
1248 char* command = msg->id.string;
1249 while (c < end && *c != '\r' && *c != ' ') {
1250 if (i++ >= size)
1251 return true;
1252 *command++ = *c++;
1253 }
1254 *command = '\0';
1255 msg->type = IRC_COMMAND_STRING;
1256 } else
1257 return true;
1258 if (c < end && *c == ' ') {
1259 /* parse params and trailing */
1260 char* params = msg->params;
1261 int i = 1;
1262 const size_t size = sizeof(msg->params);
1263 ++c;
1264
1265 /* parse params */
1266 while (c < end && *c != '\r' && *c != ':') {
1267 /* parse single param */
1268 while (c < end && *c != '\r' && *c != ' ') {
1269 if (i++ >= size)
1270 return true;
1271 *params++ = *c++;
1272 }
1273 /* more params */
1274 if (c + 1 < end && *c == ' ' && *(c + 1) != ':') {
1275 if (i++ >= size)
1276 return true;
1277 *params++ = ' ';
1278 }
1279 if (*c == ' ')
1280 ++c;
1281 }
1282 *params = '\0';
1283 /* parse trailing */
1284 if (c < end && *c == ':') {
1285 char* trailing = msg->trailing;
1286 int i = 1;
1287 const size_t size = sizeof(msg->trailing);
1288 ++c;
1289 while (c < end && *c != '\r') {
1290 if (i++ >= size)
1291 return true;
1292 *trailing++ = *c++;
1293 }
1294 *trailing = '\0';
1295 }
1296 }
1297 }
1298 return false;
1299}
1300
1305static bool Irc_Proto_Enqueue (const char* msg, size_t msg_len)
1306{
1307 const int messageBucketSize = irc_messageBucketSize->integer;
1308 const int characterBucketSize = irc_characterBucketSize->integer;
1309
1310 if (!irc_connected) {
1311 Com_Printf("Irc_Proto_Enqueue: not connected\n");
1312 return true;
1313 }
1314
1315 /* create message node */
1316 if (irc_bucket.message_size + 1 <= messageBucketSize && irc_bucket.character_size + msg_len <= characterBucketSize) {
1319 m->msg = Mem_AllocTypeN(char, msg_len);
1320 memcpy(m->msg, msg, msg_len);
1321 m->msg_len = msg_len;
1322 m->next = nullptr;
1323 /* append message node */
1324 irc_bucket_message_t** anchor = &irc_bucket.first_msg;
1325 while (*anchor) anchor = &(*anchor)->next;
1326 *anchor = m;
1327 /* update bucket sizes */
1328 ++irc_bucket.message_size;
1329 irc_bucket.character_size += msg_len;
1330 return false;
1331 } else {
1332 Com_Printf("Bucket(s) full. Could not enqueue message\n");
1333 return true;
1334 }
1335}
1336
1341static void Irc_Proto_RefillBucket (void)
1342{
1343 /* calculate token refill */
1344 const double characterBucketSize = irc_characterBucketSize->value;
1345 const double characterBucketRate = irc_characterBucketRate->value;
1346 const int ms = CL_Milliseconds();
1347 const int ms_delta = ms - irc_bucket.last_refill;
1348 const double char_delta = (ms_delta * characterBucketRate) / 1000;
1349 const double char_new = irc_bucket.character_token + char_delta;
1350 /* refill token (but do not exceed maximum) */
1351 irc_bucket.character_token = std::min(char_new, characterBucketSize);
1352 /* set timestamp so next refill can calculate delta */
1353 irc_bucket.last_refill = ms;
1354}
1355
1361static void Irc_Proto_DrainBucket (void)
1362{
1363 const double characterBucketBurst = irc_characterBucketBurst->value;
1364
1365 /* remove messages whose size exceed our burst size (we can not send them) */
1366 for (irc_bucket_message_t* msg = irc_bucket.first_msg; msg && msg->msg_len > characterBucketBurst; msg = irc_bucket.first_msg) {
1367 irc_bucket_message_t* const next = msg->next;
1368 /* update bucket sizes */
1369 --irc_bucket.message_size;
1370 irc_bucket.character_size -= msg->msg_len;
1371 /* free message */
1372 Mem_Free(msg->msg);
1373 /* dequeue message */
1374 irc_bucket.first_msg = next;
1375 }
1376 /* send burst of remaining messages */
1377 for (irc_bucket_message_t* msg = irc_bucket.first_msg; msg; msg = irc_bucket.first_msg) {
1378 /* send message */
1379 Irc_Net_Send(msg->msg, msg->msg_len);
1380 irc_bucket.character_token -= msg->msg_len;
1381 /* dequeue message */
1382 irc_bucket.first_msg = msg->next;
1383 /* update bucket sizes */
1384 --irc_bucket.message_size;
1385 irc_bucket.character_size -= msg->msg_len;
1386 /* free message */
1387 Mem_Free(msg->msg);
1388 Mem_Free(msg);
1389 }
1390}
1391
1392/*
1393===============================================================
1394Logic functions
1395===============================================================
1396*/
1397
1401static void Irc_Logic_SendMessages (void)
1402{
1403 Irc_Proto_RefillBucket(); /* first refill token */
1404 Irc_Proto_DrainBucket(); /* then send messages (if allowed) */
1405}
1406
1413static void Irc_Logic_ReadMessages (void)
1414{
1415 bool msg_complete;
1416 do {
1417 irc_server_msg_t msg;
1418 if (!Irc_Proto_PollServerMsg(&msg, &msg_complete)) {
1419 /* success */
1420 if (msg_complete)
1422 } else {
1423 /* failure */
1424 UI_Popup(_("Error"), _("Server closed connection"));
1425 Irc_Logic_Disconnect("Server closed connection");
1426 }
1427 } while (msg_complete);
1428}
1429
1430static void Irc_Logic_Connect (const char* server, const char* port)
1431{
1432 if (!Irc_Proto_Connect(server, port)) {
1433 /* connected to server, send NICK and USER commands */
1434 const char* const pass = irc_password->string;
1435 const char* const user = irc_user->string;
1436 irc_connected = true;
1437 if (pass[0] != '\0')
1438 Irc_Proto_Password(pass);
1439 Irc_Proto_Nick(irc_nick->string);
1440 Irc_Proto_User(user, true, user);
1441 } else {
1442 Com_Printf("Could not connect to the irc server %s:%s\n", server, port);
1443 }
1444}
1445
1446static void Irc_Logic_Disconnect (const char* reason)
1447{
1448 if (!irc_connected) {
1449 Com_Printf("Irc_Disconnect: not connected\n");
1450 return;
1451 }
1452
1453 Com_Printf("Irc_Disconnect: %s\n", reason);
1454 Irc_Proto_Quit(reason);
1456 irc_connected = false;
1457 chan = nullptr;
1458 Cvar_ForceSet("irc_defaultChannel", "");
1459 Cvar_ForceSet("irc_topic", "Connecting (please wait)...");
1462}
1463
1469{
1470 if (irc_connected) {
1471 if (irc_channel->modified) {
1473 Irc_Logic_Disconnect("Switched to another channel");
1474 Irc_Logic_Connect(irc_server->string, irc_port->string);
1475 if (irc_connected)
1476 Irc_Client_Join(irc_channel->string, nullptr);
1477 }
1478 /* could have changed in the meantime */
1479 if (irc_connected) {
1482 }
1483 }
1484 irc_channel->modified = false;
1485}
1486
1487static const char* Irc_Logic_GetChannelTopic (const irc_channel_t* channel)
1488{
1489 assert(channel);
1490 return channel->topic;
1491}
1492
1497static void Irc_Logic_AddChannelName (irc_channel_t* channel, irc_nick_prefix_t prefix, const char* nick)
1498{
1499 /* first one */
1500 irc_user_t* user = channel->user;
1501 for (int i = 0; user && i < channel->users; i++, user = user->next) {
1502 if (!strncmp(&(user->key[1]), nick, MAX_VAR - 1))
1503 return;
1504 }
1506 user->next = channel->user;
1507 channel->user = user;
1508
1509 Com_sprintf(user->key, sizeof(user->key), "%c%s", prefix, nick);
1510 channel->users++;
1511 Com_DPrintf(DEBUG_CLIENT, "Add user '%s' to userlist at pos %i\n", user->key, channel->users);
1513}
1514
1519static void Irc_Logic_RemoveChannelName (irc_channel_t* channel, const char* nick)
1520{
1521 /* first one */
1522 irc_user_t* user = channel->user;
1523 irc_user_t* predecessor = nullptr;
1524 for (int i = 0; user && i < channel->users; i++, user = user->next) {
1525 if (!strncmp(&(user->key[1]), nick, sizeof(user->key))) {
1526 /* delete the first user from the list */
1527 if (!predecessor)
1528 channel->user = user->next;
1529 /* point to the descendant */
1530 else
1531 predecessor->next = user->next;
1532 Mem_Free(user);
1533 chan->users--;
1535 return;
1536 }
1537 predecessor = user;
1538 }
1539}
1540
1541/*
1542===============================================================
1543Network functions
1544===============================================================
1545*/
1546
1547static void Irc_Net_StreamClose (void)
1548{
1549 irc_stream = nullptr;
1550}
1551
1556static bool Irc_Net_Connect (const char* host, const char* port)
1557{
1558 if (irc_stream)
1561 return irc_stream ? false : true;
1562}
1563
1567static bool Irc_Net_Disconnect (void)
1568{
1570 return true;
1571}
1572
1573static void Irc_Net_Send (const char* msg, size_t msg_len)
1574{
1575 assert(msg);
1576 NET_StreamEnqueue(irc_stream, msg, msg_len);
1577}
1578
1579/*
1580===============================================================
1581Bindings
1582===============================================================
1583*/
1584
1585static void Irc_Connect_f (void)
1586{
1587 const int argc = Cmd_Argc();
1588 if (!irc_port || !irc_server)
1589 return;
1590 if (argc >= 2 && argc <= 4) {
1591#if 0
1592 if (irc_connected)
1593 Irc_Logic_Disconnect("reconnect");
1594#endif
1595 if (!irc_connected) {
1596 /* not connected yet */
1597 if (argc >= 2)
1598 Cvar_Set("irc_server", "%s", Cmd_Argv(1));
1599 if (argc >= 3)
1600 Cvar_Set("irc_port", "%s", Cmd_Argv(2));
1601 Com_Printf("Connecting to %s:%s\n", irc_server->string, irc_port->string);
1602 Irc_Logic_Connect(irc_server->string, irc_port->string);
1603 if (irc_connected && argc >= 4)
1604 Irc_Client_Join(Cmd_Argv(3), nullptr);
1605 } else {
1606 Com_Printf("Already connected.\n");
1607 }
1608 } else if (irc_server->string[0] != '\0' && irc_port->integer) {
1609 if (!irc_connected)
1610 Cbuf_AddText("irc_connect %s %i %s\n", irc_server->string, irc_port->integer, irc_channel->string);
1611 else
1612 Com_Printf("Already connected.\n");
1613 } else {
1614 Com_Printf("Usage: %s [<server>] [<port>] [<channel>]\n", Cmd_Argv(0));
1615 }
1616}
1617
1618static void Irc_Disconnect_f (void)
1619{
1620 Irc_Logic_Disconnect("normal exit");
1621}
1622
1623static bool Irc_Client_Join (const char* channel, const char* password)
1624{
1625 if (!Irc_IsChannel(channel)) {
1626 Com_Printf("No valid channel name\n");
1627 return false;
1628 }
1629 /* join desired channel */
1630 if (!Irc_Proto_Join(channel, password)) {
1631 Cvar_ForceSet("irc_defaultChannel", channel);
1632 Com_Printf("Joined channel: '%s'\n", channel);
1633 return true;
1634 } else {
1635 Com_Printf("Could not join channel: '%s'\n", channel);
1636 return false;
1637 }
1638}
1639
1640static void Irc_Client_Join_f (void)
1641{
1642 const int argc = Cmd_Argc();
1643 if (argc >= 2 && argc <= 3) {
1644 const char* const channel = Cmd_Argv(1);
1645 /* password is optional */
1646 const char* const channel_pass = (argc == 3) ? Cmd_Argv(2) : nullptr;
1647 Irc_Client_Join(channel, channel_pass);
1648 } else {
1649 Com_Printf("Usage: %s <channel> [<password>]\n", Cmd_Argv(0));
1650 }
1651}
1652
1653static void Irc_Client_Part_f (void)
1654{
1655 const int argc = Cmd_Argc();
1656 if (argc == 2) {
1657 const char* const channel = Cmd_Argv(1);
1658 Irc_Proto_Part(channel);
1659 } else {
1660 Com_Printf("Usage: %s <channel>\n", Cmd_Argv(0));
1661 }
1662}
1663
1669static void Irc_Client_Msg_f (void)
1670{
1671 char cropped_msg[IRC_SEND_BUF_SIZE];
1672 const char* msg;
1673
1674 if (Cmd_Argc() >= 2)
1675 msg = Cmd_Args();
1676 else if (irc_send_buffer->string[0] != '\0')
1677 msg = irc_send_buffer->string;
1678 else
1679 return;
1680
1681 if (msg[0] != '\0') {
1682 if (irc_defaultChannel->string[0] != '\0') {
1683 if (msg[0] == '"') {
1684 const size_t msg_len = strlen(msg);
1685 memcpy(cropped_msg, msg + 1, msg_len - 2);
1686 cropped_msg[msg_len - 2] = '\0';
1687 msg = cropped_msg;
1688 }
1689 if (!Irc_Proto_Msg(irc_defaultChannel->string, msg)) {
1690 /* local echo */
1691 Irc_AppendToBuffer("<%s> %s", irc_nick->string, msg);
1692 }
1693 Cvar_ForceSet("irc_send_buffer", "");
1694 } else
1695 Com_Printf("Join a channel first.\n");
1696 }
1697}
1698
1699static void Irc_Client_PrivMsg_f (void)
1700{
1701 if (Cmd_Argc() >= 3) {
1702 char cropped_msg[IRC_SEND_BUF_SIZE];
1703 const char* const target = Cmd_Argv(1);
1704 const char* msg = Cmd_Args() + strlen(target) + 1;
1705 if (*msg == '"') {
1706 const size_t msg_len = strlen(msg);
1707 memcpy(cropped_msg, msg + 1, msg_len - 2);
1708 cropped_msg[msg_len - 2] = '\0';
1709 msg = cropped_msg;
1710 }
1711 Irc_Proto_Msg(target, msg);
1712 } else {
1713 Com_Printf("Usage: %s <target> {<msg>}\n", Cmd_Argv(0));
1714 }
1715}
1716
1717static void Irc_Client_Mode_f (void)
1718{
1719 const int argc = Cmd_Argc();
1720 if (argc >= 3) {
1721 const char* const target = Cmd_Argv(1);
1722 const char* const modes = Cmd_Argv(2);
1723 const char* const params = argc >= 4
1724 ? Cmd_Args() + strlen(target) + strlen(modes) + 2
1725 : nullptr;
1726 Irc_Proto_Mode(target, modes, params);
1727 } else
1728 Com_Printf("Usage: %s <target> <modes> {<param>}\n", Cmd_Argv(0));
1729}
1730
1731static void Irc_Client_Topic_f (void)
1732{
1733 const int argc = Cmd_Argc();
1734 if (argc >= 2) {
1735 const char* const channel = Cmd_Argv(1);
1736 if (chan) {
1737 if (argc >= 3) {
1738 char buf[1024];
1739 const char* in = Cmd_Args();
1740 char* out = buf;
1741 if (*in == '"')
1742 in += 2;
1743 in += strlen(channel) + 1;
1744 Q_strncpyz(out, in, sizeof(out));
1745 if (*out == '"') {
1746 ++out;
1747 const size_t out_len = strlen(out);
1748 assert(out_len >= 1);
1749 out[out_len - 1] = '\0';
1750 }
1751 Irc_Proto_Topic(channel, out);
1752 } else {
1753 Com_Printf("%s topic: \"%s\"\n", channel, Irc_Logic_GetChannelTopic(chan));
1754 }
1755 } else {
1756 Com_Printf("Not joined: %s\n", channel);
1757 }
1758 } else {
1759 Com_Printf("Usage: %s <channel> [<topic>]\n", Cmd_Argv(0));
1760 }
1761}
1762
1763#define IRC_MAX_USERLIST 512
1765
1766static void Irc_Client_Names_f (void)
1767{
1768 if (!chan) {
1769 Com_Printf("Not joined\n");
1770 return;
1771 }
1772 linkedList_t* irc_names_buffer = nullptr;
1773 int i;
1774 irc_user_t* user = chan->user;
1775 for (i = 0; i < chan->users; i++) {
1776 if (i >= IRC_MAX_USERLIST)
1777 break;
1779 user = user->next;
1780 }
1781 if (i > 0) {
1782 qsort((void*)irc_userListOrdered, i, MAX_VAR, Q_StringSort);
1783 while (i--)
1784 LIST_AddString(&irc_names_buffer, irc_userListOrdered[i]);
1785 }
1786 UI_RegisterLinkedListText(TEXT_IRCUSERS, irc_names_buffer);
1787}
1788
1789static void Irc_Client_Kick_f (void)
1790{
1791 const int argc = Cmd_Argc();
1792 if (argc < 3) {
1793 Com_Printf("Usage: %s <channel> <nick> [<reason>]\n", Cmd_Argv(0));
1794 return;
1795 }
1796 const char* channel = Cmd_Argv(1);
1797 if (!chan) {
1798 Com_Printf("Not joined: %s.\n", channel);
1799 return;
1800 }
1801 const char* const nick = Cmd_Argv(2);
1802 const char* reason;
1803 if (argc >= 4)
1804 reason = Cmd_Args() + strlen(nick) + strlen(channel) + 2;
1805 else
1806 reason = nullptr;
1807 Irc_Proto_Kick(channel, nick, reason);
1808}
1809
1810static void Irc_GetExternalIP (const char* externalIP, void* userdata)
1811{
1812 char buf[128];
1813
1814 if (!externalIP) {
1815 Com_Printf("Could not query masterserver\n");
1816 return;
1817 }
1818 Com_sprintf(buf, sizeof(buf), "%s%s;%s;%s", IRC_INVITE_FOR_A_GAME, externalIP, port->string, UFO_VERSION);
1819
1820 const irc_user_t* user = chan->user;
1821 while (user) {
1824 /* the first character is a prefix, skip it when checking
1825 * against our own nickname */
1826 const char* name = &(user->key[1]);
1827 if (!Q_streq(name, irc_nick->string))
1829 user = user->next;
1830 }
1831}
1832
1836static void Irc_Client_Invite_f (void)
1837{
1838 if (!CL_OnBattlescape()) {
1839 Com_Printf("You must be connected to a running server to invite others\n");
1840 return;
1841 }
1842
1843 if (!chan) {
1844 UI_PushWindow("irc_popup");
1845 return;
1846 }
1847
1848 HTTP_GetURL(va("%s/ufo/masterserver.php?ip", masterserver_url->string), Irc_GetExternalIP);
1849}
1850
1851static void Irc_Client_Who_f (void)
1852{
1853 if (Cmd_Argc() != 2) {
1854 Com_Printf("Usage: %s <usermask>\n", Cmd_Argv(0));
1855 return;
1856 }
1858}
1859
1860static void Irc_Client_Whois_f (void)
1861{
1862 if (Cmd_Argc() != 2) {
1863 Com_Printf("Usage: %s <nick>\n", Cmd_Argv(0));
1864 return;
1865 }
1867}
1868
1869static void Irc_Client_Whowas_f (void)
1870{
1871 if (Cmd_Argc() != 2) {
1872 Com_Printf("Usage: %s <nick>\n", Cmd_Argv(0));
1873 return;
1874 }
1876}
1877
1878/*
1879===============================================================
1880Menu functions
1881===============================================================
1882*/
1883
1888static void Irc_UserClick_f (void)
1889{
1890 if (Cmd_Argc() != 2)
1891 return;
1892
1893 if (!chan)
1894 return;
1895
1896 const int num = atoi(Cmd_Argv(1));
1897 if (num < 0 || num >= chan->users || num >= IRC_MAX_USERLIST)
1898 return;
1899
1900 const int cnt = std::min(chan->users, IRC_MAX_USERLIST) - (num + 1);
1901
1902 const char* name = irc_userListOrdered[cnt];
1903 Cvar_Set("irc_send_buffer", "%s%s: ", irc_send_buffer->string, &name[1]);
1904}
1905
1910static void Irc_UserRightClick_f (void)
1911{
1912 if (Cmd_Argc() != 2)
1913 return;
1914
1915 if (!chan)
1916 return;
1917
1918 const int num = atoi(Cmd_Argv(1));
1919 if (num < 0 || num >= chan->users || num >= IRC_MAX_USERLIST)
1920 return;
1921
1922 const int cnt = std::min(chan->users, IRC_MAX_USERLIST) - (num + 1);
1923
1924 const char* name = irc_userListOrdered[cnt];
1925 Irc_Proto_Whois(&name[1]);
1926}
1927
1931static void Irc_Input_Activate_f (void)
1932{
1933 /* in case of a failure we need this in UI_PopWindow */
1934 if (irc_connected && irc_defaultChannel->string[0] != '\0') {
1936 } else {
1937 Com_DPrintf(DEBUG_CLIENT, "Irc_Input_Activate: Warning - IRC not connected\n");
1938 UI_PopWindow();
1939 UI_PushWindow("irc_popup");
1940 }
1941}
1942
1946static void Irc_Input_Deactivate_f (void)
1947{
1948 irc_send_buffer->modified = false;
1949
1951}
1952
1953/*
1954===============================================================
1955Init and Shutdown functions
1956===============================================================
1957*/
1958
1959void Irc_Init (void)
1960{
1961 cl_ircSysPool = Mem_CreatePool("Client: IRC system");
1962
1963 /* commands */
1964 Cmd_AddCommand("irc_join", Irc_Client_Join_f, "Join an irc channel");
1965 Cmd_AddCommand("irc_connect", Irc_Connect_f, "Connect to the irc network");
1966 Cmd_AddCommand("irc_disconnect", Irc_Disconnect_f, "Disconnect from the irc network");
1967 Cmd_AddCommand("irc_part", Irc_Client_Part_f);
1968 Cmd_AddCommand("irc_privmsg", Irc_Client_PrivMsg_f);
1969 Cmd_AddCommand("irc_mode", Irc_Client_Mode_f);
1970 Cmd_AddCommand("irc_who", Irc_Client_Who_f);
1971 Cmd_AddCommand("irc_whois", Irc_Client_Whois_f);
1972 Cmd_AddCommand("irc_whowas", Irc_Client_Whowas_f);
1973 Cmd_AddCommand("irc_chanmsg", Irc_Client_Msg_f);
1974 Cmd_AddCommand("irc_topic", Irc_Client_Topic_f);
1975 Cmd_AddCommand("irc_names", Irc_Client_Names_f);
1976 Cmd_AddCommand("irc_kick", Irc_Client_Kick_f);
1977 Cmd_AddCommand("irc_invite", Irc_Client_Invite_f, "Invite other players for a game");
1978
1979 Cmd_AddCommand("irc_userlist_click", Irc_UserClick_f, "Menu function for clicking a user from the list");
1980 Cmd_AddCommand("irc_userlist_rclick", Irc_UserRightClick_f, "Menu function for clicking a user from the list");
1981
1982 Cmd_AddCommand("irc_activate", Irc_Input_Activate_f, "IRC init when entering the menu");
1983 Cmd_AddCommand("irc_deactivate", Irc_Input_Deactivate_f, "IRC deactivate when leaving the irc menu");
1984
1985 /* cvars */
1986 irc_server = Cvar_Get("irc_server", "irc.libera.chat", CVAR_ARCHIVE, "IRC server to connect to");
1987 irc_channel = Cvar_Get("irc_channel", "#ufoai-gamer", CVAR_ARCHIVE, "IRC channel to join into");
1988 irc_channel->modified = false;
1989 irc_port = Cvar_Get("irc_port", "6667", CVAR_ARCHIVE, "IRC port to connect to");
1990 irc_user = Cvar_Get("irc_user", "UFOAIPlayer", CVAR_ARCHIVE);
1991 irc_password = Cvar_Get("irc_password", "", CVAR_ARCHIVE);
1992 irc_topic = Cvar_Get("irc_topic", "Connecting (please wait)...", CVAR_NOSET);
1993 irc_defaultChannel = Cvar_Get("irc_defaultChannel", "", CVAR_NOSET);
1994 irc_logConsole = Cvar_Get("irc_logConsole", "0", CVAR_ARCHIVE, "Log all irc conversations to game console, too");
1995 irc_showIfNotInMenu = Cvar_Get("irc_showIfNotInMenu", "0", CVAR_ARCHIVE, "Show chat messages on top of the menu stack if we are not in the irc menu");
1996 irc_send_buffer = Cvar_Get("irc_send_buffer");
1997 irc_nick = Cvar_Get("cl_name");
1998
1999 irc_messageBucketSize = Cvar_Get("irc_messageBucketSize", "100", CVAR_ARCHIVE, "max 100 messages in bucket");
2000 irc_messageBucketBurst = Cvar_Get("irc_messageBucketBurst", "5", CVAR_ARCHIVE, "max burst size is 5 messages");
2001 irc_characterBucketSize = Cvar_Get("irc_characterBucketSize", "2500", CVAR_ARCHIVE, "max 2,500 characters in bucket");
2002 irc_characterBucketBurst = Cvar_Get("irc_characterBucketBurst", "250", CVAR_ARCHIVE, "max burst size is 250 characters");
2003 irc_characterBucketRate = Cvar_Get("irc_characterBucketRate", "10", CVAR_ARCHIVE, "per second (100 characters in 10 seconds)");
2004}
2005
2006void Irc_Shutdown (void)
2007{
2008 if (irc_connected)
2009 Irc_Logic_Disconnect("shutdown");
2010
2012}
bool CL_OnBattlescape(void)
Check whether we are in a tactical mission as server or as client. But this only means that we are ab...
void GAME_AddChatMessage(const char *format,...)
Definition cl_game.cpp:370
Shared game type headers.
HUD related routines.
static bool Irc_Proto_Nick(const char *nick)
Definition cl_irc.cpp:400
static void Irc_Client_CmdKill(const char *prefix, const char *params, const char *trailing)
Definition cl_irc.cpp:855
static bool Irc_Proto_Mode(const char *target, const char *modes, const char *params)
Definition cl_irc.cpp:467
static void Irc_Client_CmdRplTopic(const char *params, const char *trailing)
Definition cl_irc.cpp:691
static void Irc_Client_Invite_f(void)
Definition cl_irc.cpp:1836
static bool Irc_AppendToBuffer(const char *const msg,...) __attribute__((format(__printf__
Append the irc message to the buffer.
Definition cl_irc.cpp:626
static cvar_t * irc_messageBucketSize
Definition cl_irc.cpp:326
static void Irc_Client_CmdRplWhowasuser(const char *params, const char *trailing)
Definition cl_irc.cpp:662
static void Irc_Logic_Disconnect(const char *reason)
Definition cl_irc.cpp:1446
static cvar_t * irc_channel
Definition cl_irc.cpp:48
#define IRC_SEND_BUF_SIZE
Definition cl_irc.cpp:62
static void Irc_Proto_DrainBucket(void)
Send all enqueued packets.
Definition cl_irc.cpp:1361
static void Irc_Connect_f(void)
Definition cl_irc.cpp:1585
static void Irc_Client_CmdPrivmsg(const char *prefix, const char *params, const char *trailing)
Definition cl_irc.cpp:909
static cvar_t * irc_defaultChannel
Definition cl_irc.cpp:53
static void Irc_GetExternalIP(const char *externalIP, void *userdata)
Definition cl_irc.cpp:1810
static void Irc_Proto_RefillBucket(void)
Definition cl_irc.cpp:1341
static void Irc_Logic_Connect(const char *server, const char *port)
Definition cl_irc.cpp:1430
static void Irc_Net_StreamClose(void)
Definition cl_irc.cpp:1547
static void Irc_Logic_ReadMessages(void)
Definition cl_irc.cpp:1413
static cvar_t * irc_characterBucketSize
Definition cl_irc.cpp:328
static void Irc_UserRightClick_f(void)
Performs a whois query for the username you clicked.
Definition cl_irc.cpp:1910
static cvar_t * irc_password
Definition cl_irc.cpp:51
static bool Irc_Net_Connect(const char *host, const char *port)
Definition cl_irc.cpp:1556
static bool Irc_Proto_Quit(const char *quitmsg)
Definition cl_irc.cpp:388
static bool Irc_Proto_Disconnect(void)
Definition cl_irc.cpp:367
static bool Irc_Proto_ProcessServerMsg(const irc_server_msg_t *msg)
Definition cl_irc.cpp:1029
static void Irc_Client_CmdNick(const char *prefix, const char *params, const char *trailing)
Changes the cvar 'name' with the new name you set.
Definition cl_irc.cpp:886
static void Irc_Client_Whowas_f(void)
Definition cl_irc.cpp:1869
static cvar_t * irc_topic
Definition cl_irc.cpp:52
irc_command_type_t
Definition cl_irc.cpp:226
@ IRC_COMMAND_NUMERIC
Definition cl_irc.cpp:227
@ IRC_COMMAND_STRING
Definition cl_irc.cpp:228
static bool Irc_IsChannel(const char *target)
Definition cl_irc.cpp:292
static void Irc_Client_CmdRplWhoisuser(const char *params, const char *trailing)
Definition cl_irc.cpp:700
static void Irc_Client_CmdRplWhoreply(const char *params, const char *trailing)
Definition cl_irc.cpp:787
static bool irc_connected
Definition cl_irc.cpp:60
static cvar_t * irc_user
Definition cl_irc.cpp:50
static void Irc_Client_Mode_f(void)
Definition cl_irc.cpp:1717
static cvar_t * irc_port
Definition cl_irc.cpp:47
static bool Irc_Proto_Password(const char *password)
Definition cl_irc.cpp:422
static void Irc_Logic_AddChannelName(irc_channel_t *channel, irc_nick_prefix_t prefix, const char *nick)
Adds a new username to the channel username list.
Definition cl_irc.cpp:1497
static const char IRC_INVITE_FOR_A_GAME[]
Definition cl_irc.cpp:261
static irc_bucket_t irc_bucket
Definition cl_irc.cpp:346
static void Irc_Client_CmdRplWhoisidle(const char *params, const char *trailing)
Definition cl_irc.cpp:766
static void Irc_Client_Who_f(void)
Definition cl_irc.cpp:1851
static bool Irc_Proto_Notice(const char *target, const char *text)
Definition cl_irc.cpp:512
static bool Irc_Client_Join(const char *channel, const char *password)
Definition cl_irc.cpp:1623
static void Irc_ParseName(const char *mask, char *nick, size_t size, irc_nick_prefix_t *prefix)
Definition cl_irc.cpp:298
static char irc_userListOrdered[IRC_MAX_USERLIST][MAX_VAR]
Definition cl_irc.cpp:1764
static bool Irc_Proto_Msg(const char *target, const char *text)
Definition cl_irc.cpp:495
#define IRC_CTCP_MARKER_CHR
Definition cl_irc.cpp:903
static irc_channel_t ircChan
Definition cl_irc.cpp:263
static cvar_t * irc_logConsole
Definition cl_irc.cpp:54
static cvar_t * irc_send_buffer
Definition cl_irc.cpp:57
static cvar_t * irc_characterBucketBurst
Definition cl_irc.cpp:329
static void Irc_Proto_Pong(const char *nick, const char *server, const char *cookie)
Definition cl_irc.cpp:523
static cvar_t * irc_showIfNotInMenu
Definition cl_irc.cpp:55
static void Irc_Client_CmdRplWhoisaccount(const char *params, const char *trailing)
Definition cl_irc.cpp:745
void Irc_Shutdown(void)
Definition cl_irc.cpp:2006
static void Irc_Client_Names_f(void)
Definition cl_irc.cpp:1766
static const char * Irc_Logic_GetChannelTopic(const irc_channel_t *channel)
Definition cl_irc.cpp:1487
static bool static bool Irc_Proto_ParseServerMsg(const char *txt, size_t txt_len, irc_server_msg_t *msg)
Definition cl_irc.cpp:1209
static memPool_t * cl_ircSysPool
Definition cl_irc.cpp:58
#define IRC_MAX_USERLIST
Definition cl_irc.cpp:1763
static void Irc_Client_CmdRplEndofnames(const char *params, const char *trailing)
Definition cl_irc.cpp:1022
static irc_channel_t * chan
Definition cl_irc.cpp:264
irc_nick_prefix_t
Definition cl_irc.cpp:231
@ IRC_NICK_PREFIX_VOICE
Definition cl_irc.cpp:234
@ IRC_NICK_PREFIX_OP
Definition cl_irc.cpp:233
@ IRC_NICK_PREFIX_NONE
Definition cl_irc.cpp:232
static void Irc_Client_CmdJoin(const char *prefix, const char *params, const char *trailing)
Definition cl_irc.cpp:828
#define IRC_CTCP_MARKER_STR
Definition cl_irc.cpp:904
#define IRC_RECV_BUF_SIZE
Definition cl_irc.cpp:63
static void Irc_Logic_SendMessages(void)
Definition cl_irc.cpp:1401
static bool Irc_Proto_Join(const char *channel, const char *password)
Definition cl_irc.cpp:433
static cvar_t * irc_messageBucketBurst
Definition cl_irc.cpp:327
static bool Irc_Proto_Kick(const char *channel, const char *nick, const char *reason)
Definition cl_irc.cpp:536
static void Irc_Net_Send(const char *msg, size_t msg_len)
Definition cl_irc.cpp:1573
static bool Irc_Proto_Connect(const char *host, const char *port)
Definition cl_irc.cpp:351
static void Irc_Client_CmdRplWhoisserver(const char *params, const char *trailing)
Definition cl_irc.cpp:724
static bool Irc_Proto_User(const char *user, bool invisible, const char *name)
Definition cl_irc.cpp:411
static bool Irc_Proto_Part(const char *channel)
Definition cl_irc.cpp:456
static struct net_stream * irc_stream
Definition cl_irc.cpp:259
static void Irc_Client_CmdKick(const char *prefix, const char *params, const char *trailing)
Definition cl_irc.cpp:864
static cvar_t * irc_characterBucketRate
Definition cl_irc.cpp:330
static void Irc_Client_CmdPart(const char *prefix, const char *trailing)
Definition cl_irc.cpp:837
void Irc_Init(void)
Definition cl_irc.cpp:1959
static void Irc_Client_Kick_f(void)
Definition cl_irc.cpp:1789
irc_numeric_t
Definition cl_irc.cpp:78
@ RPL_USERS
Definition cl_irc.cpp:129
@ ERR_PASSWDMISMATCH
Definition cl_irc.cpp:199
@ RPL_MOTD
Definition cl_irc.cpp:123
@ RPL_ENDOFUSERS
Definition cl_irc.cpp:130
@ RPL_TRACENEWTYPE
Definition cl_irc.cpp:139
@ RPL_LISTEND
Definition cl_irc.cpp:104
@ ERR_NOSUCHGLINE
Definition cl_irc.cpp:217
@ RPL_TRACECONNECTING
Definition cl_irc.cpp:133
@ RPL_NAMREPLY
Definition cl_irc.cpp:114
@ RPL_CHANNELMODEIS
Definition cl_irc.cpp:105
@ RPL_NOUSERS
Definition cl_irc.cpp:131
@ ERR_BADCHANNELKEY
Definition cl_irc.cpp:207
@ ERR_NCHANGETOOFAST
Definition cl_irc.cpp:188
@ ERR_NOTOPLEVEL
Definition cl_irc.cpp:177
@ RPL_SUMMONING
Definition cl_irc.cpp:110
@ ERR_WHOSYNTAX
Definition cl_irc.cpp:222
@ RPL_TOPIC
Definition cl_irc.cpp:107
@ ERR_WASNOSUCHNICK
Definition cl_irc.cpp:172
@ ERR_CANTKILLSERVER
Definition cl_irc.cpp:210
@ RPL_STATSILINE
Definition cl_irc.cpp:145
@ RPL_STATSCLINE
Definition cl_irc.cpp:143
@ ERR_TOOMANYDCC
Definition cl_irc.cpp:220
@ RPL_MOTDSTART
Definition cl_irc.cpp:122
@ ERR_NOSUCHNICK
Definition cl_irc.cpp:167
@ ERR_USERONCHANNEL
Definition cl_irc.cpp:191
@ RPL_STATSKLINE
Definition cl_irc.cpp:146
@ RPL_WHOISCHANNELS
Definition cl_irc.cpp:97
@ RPL_ENDOFWHO
Definition cl_irc.cpp:113
@ RPL_WHOREPLY
Definition cl_irc.cpp:112
@ RPL_INFO
Definition cl_irc.cpp:120
@ ERR_YOUREBANNEDCREEP
Definition cl_irc.cpp:200
@ RPL_ISUPPORT
Definition cl_irc.cpp:84
@ ERR_SUMMONDISABLED
Definition cl_irc.cpp:193
@ RPL_TRACEOPERATOR
Definition cl_irc.cpp:136
@ ERR_NORECIPIENT
Definition cl_irc.cpp:175
@ RPL_TIME
Definition cl_irc.cpp:127
@ ERR_BANNICKCHANGE
Definition cl_irc.cpp:187
@ RPL_LISTSTART
Definition cl_irc.cpp:102
@ ERR_USERSDISABLED
Definition cl_irc.cpp:194
@ ERR_WHOLIMEXCEED
Definition cl_irc.cpp:223
@ ERR_NICKNAMEINUSE
Definition cl_irc.cpp:185
@ RPL_CREATED
Definition cl_irc.cpp:82
@ ERR_NOPRIVILEGES
Definition cl_irc.cpp:208
@ RPL_ADMINLOC1
Definition cl_irc.cpp:160
@ ERR_UNKNOWNCOMMAND
Definition cl_irc.cpp:179
@ ERR_NOTEXTTOSEND
Definition cl_irc.cpp:176
@ RPL_ENDOFBANLIST
Definition cl_irc.cpp:119
@ ERR_SILELISTFULL
Definition cl_irc.cpp:216
@ ERR_NOLOGIN
Definition cl_irc.cpp:192
@ RPL_YOUREOPER
Definition cl_irc.cpp:125
@ RPL_INVITING
Definition cl_irc.cpp:109
@ ERR_NOSUCHSERVER
Definition cl_irc.cpp:168
@ RPL_ADMINME
Definition cl_irc.cpp:159
@ ERR_LAST_ERR_MSG
Definition cl_irc.cpp:215
@ RPL_UNAWAY
Definition cl_irc.cpp:90
@ RPL_ADMINEMAIL
Definition cl_irc.cpp:162
@ RPL_WHOISOPERATOR
Definition cl_irc.cpp:94
@ RPL_LUSERCHANNELS
Definition cl_irc.cpp:157
@ RPL_HELLO
Definition cl_irc.cpp:85
@ RPL_MYINFO
Definition cl_irc.cpp:83
@ RPL_VERSION
Definition cl_irc.cpp:111
@ RPL_LUSERUNKNOWN
Definition cl_irc.cpp:156
@ ERR_KEYSET
Definition cl_irc.cpp:202
@ ERR_USERSDONTMATCH
Definition cl_irc.cpp:213
@ ERR_ALREADYREGISTRED
Definition cl_irc.cpp:197
@ RPL_WHOWASUSER
Definition cl_irc.cpp:98
@ RPL_STATSYLINE
Definition cl_irc.cpp:147
@ RPL_ENDOFNAMES
Definition cl_irc.cpp:115
@ RPL_ENDOFINFO
Definition cl_irc.cpp:121
@ RPL_GLOBALUSERS
Definition cl_irc.cpp:164
@ RPL_USERHOST
Definition cl_irc.cpp:87
@ RPL_ADMINLOC2
Definition cl_irc.cpp:161
@ RPL_WELCOME
Definition cl_irc.cpp:80
@ RPL_ENDOFMOTD
Definition cl_irc.cpp:124
@ ERR_INVITEONLYCHAN
Definition cl_irc.cpp:205
@ ERR_NOORIGIN
Definition cl_irc.cpp:174
@ ERR_CHANOPRIVSNEEDED
Definition cl_irc.cpp:209
@ ERR_BANNEDFROMCHAN
Definition cl_irc.cpp:206
@ ERR_NOSUCHCHANNEL
Definition cl_irc.cpp:169
@ ERR_USERNOTINCHANNEL
Definition cl_irc.cpp:189
@ RPL_ENDOFWHOWAS
Definition cl_irc.cpp:99
@ RPL_STATSHLINE
Definition cl_irc.cpp:152
@ ERR_NICKCOLLISION
Definition cl_irc.cpp:186
@ ERR_UMODEUNKNOWNFLAG
Definition cl_irc.cpp:212
@ RPL_STATSLLINE
Definition cl_irc.cpp:149
@ ERR_GHOSTEDCLIENT
Definition cl_irc.cpp:214
@ ERR_BADNAME
Definition cl_irc.cpp:201
@ RPL_TRACEUSER
Definition cl_irc.cpp:137
@ ERR_NOPERMFORHOST
Definition cl_irc.cpp:198
@ RPL_LINKS
Definition cl_irc.cpp:116
@ ERR_BADPING
Definition cl_irc.cpp:219
@ ERR_NEEDMOREPARAMS
Definition cl_irc.cpp:196
@ RPL_UMODEIS
Definition cl_irc.cpp:153
@ RPL_LIST
Definition cl_irc.cpp:103
@ RPL_TRACELOG
Definition cl_irc.cpp:140
@ ERR_WILDTOPLEVEL
Definition cl_irc.cpp:178
@ ERR_NOMOTD
Definition cl_irc.cpp:180
@ ERR_NOADMININFO
Definition cl_irc.cpp:181
@ ERR_NOOPERHOST
Definition cl_irc.cpp:211
@ RPL_USERSSTART
Definition cl_irc.cpp:128
@ ERR_ERRONEUSNICKNAME
Definition cl_irc.cpp:184
@ RPL_REHASHING
Definition cl_irc.cpp:126
@ RPL_ENDOFWHOIS
Definition cl_irc.cpp:96
@ RPL_TRACEHANDSHAKE
Definition cl_irc.cpp:134
@ RPL_WHOISACCOUNT
Definition cl_irc.cpp:100
@ RPL_STATSLINKINFO
Definition cl_irc.cpp:141
@ RPL_TRACEUNKNOWN
Definition cl_irc.cpp:135
@ RPL_STATSCOMMANDS
Definition cl_irc.cpp:142
@ RPL_ISON
Definition cl_irc.cpp:88
@ ERR_TOOMANYTARGETS
Definition cl_irc.cpp:173
@ RPL_LUSEROP
Definition cl_irc.cpp:155
@ ERR_UNKNOWNMODE
Definition cl_irc.cpp:204
@ RPL_ENDOFLINKS
Definition cl_irc.cpp:117
@ RPL_BANLIST
Definition cl_irc.cpp:118
@ ERR_LISTSYNTAX
Definition cl_irc.cpp:221
@ RPL_LOCALUSERS
Definition cl_irc.cpp:163
@ RPL_STATSOLINE
Definition cl_irc.cpp:151
@ RPL_LUSERCLIENT
Definition cl_irc.cpp:154
@ RPL_NOTOPIC
Definition cl_irc.cpp:106
@ RPL_TOPICWHOTIME
Definition cl_irc.cpp:108
@ ERR_CANNOTSENDTOCHAN
Definition cl_irc.cpp:170
@ RPL_NOWAWAY
Definition cl_irc.cpp:91
@ ERR_NOTREGISTERED
Definition cl_irc.cpp:195
@ RPL_STATSUPTIME
Definition cl_irc.cpp:150
@ RPL_WHOISIDLE
Definition cl_irc.cpp:95
@ RPL_STATSNLINE
Definition cl_irc.cpp:144
@ RPL_ENDOFSTATS
Definition cl_irc.cpp:148
@ ERR_NONICKNAMEGIVEN
Definition cl_irc.cpp:183
@ RPL_WHOISSERVER
Definition cl_irc.cpp:93
@ ERR_CHANNELISFULL
Definition cl_irc.cpp:203
@ RPL_WHOISUSER
Definition cl_irc.cpp:92
@ ERR_NOTONCHANNEL
Definition cl_irc.cpp:190
@ RPL_AWAY
Definition cl_irc.cpp:89
@ RPL_TRACELINK
Definition cl_irc.cpp:132
@ RPL_NONE
Definition cl_irc.cpp:86
@ ERR_FILEERROR
Definition cl_irc.cpp:182
@ RPL_YOURHOST
Definition cl_irc.cpp:81
@ RPL_LUSERME
Definition cl_irc.cpp:158
@ ERR_TOOMANYCHANNELS
Definition cl_irc.cpp:171
@ RPL_TRACESERVER
Definition cl_irc.cpp:138
static void Irc_Client_PrivMsg_f(void)
Definition cl_irc.cpp:1699
static void Irc_Client_CmdTopic(const char *prefix, const char *trailing)
Definition cl_irc.cpp:686
static bool Irc_Proto_Enqueue(const char *msg, size_t msg_len)
Definition cl_irc.cpp:1305
static void Irc_Client_Topic_f(void)
Definition cl_irc.cpp:1731
static void Irc_Input_Activate_f(void)
Definition cl_irc.cpp:1931
void Irc_Logic_Frame(void)
Definition cl_irc.cpp:1468
static void Irc_Client_Join_f(void)
Definition cl_irc.cpp:1640
static void Irc_UserClick_f(void)
Adds the username you clicked to your input buffer.
Definition cl_irc.cpp:1888
static void Irc_Client_CmdMode(const char *prefix, const char *params, const char *trailing)
Definition cl_irc.cpp:820
static void Irc_Logic_RemoveChannelName(irc_channel_t *channel, const char *nick)
Removes a username from the channel username list.
Definition cl_irc.cpp:1519
static void Irc_Client_Part_f(void)
Definition cl_irc.cpp:1653
static bool Irc_Proto_PollServerMsg(irc_server_msg_t *msg, bool *msg_complete)
Definition cl_irc.cpp:582
static cvar_t * irc_nick
Definition cl_irc.cpp:49
static void Irc_Client_Whois_f(void)
Definition cl_irc.cpp:1860
static bool Irc_Proto_Topic(const char *channel, const char *topic)
Definition cl_irc.cpp:480
static bool Irc_Proto_Who(const char *nick)
Definition cl_irc.cpp:549
static bool Irc_Proto_Whois(const char *nick)
Definition cl_irc.cpp:560
static void Irc_Input_Deactivate_f(void)
Definition cl_irc.cpp:1946
static cvar_t * irc_server
Definition cl_irc.cpp:46
static void Irc_Disconnect_f(void)
Definition cl_irc.cpp:1618
static bool Irc_Proto_Whowas(const char *nick)
Definition cl_irc.cpp:571
static void Irc_Client_Msg_f(void)
Send a message from menu or commandline.
Definition cl_irc.cpp:1669
static bool Irc_Net_Disconnect(void)
Definition cl_irc.cpp:1567
static void Irc_Client_CmdQuit(const char *prefix, const char *params, const char *trailing)
Definition cl_irc.cpp:846
static char irc_buffer[4096]
Definition cl_irc.cpp:266
static void Irc_Client_CmdRplNamreply(const char *params, const char *trailing)
Definition cl_irc.cpp:988
IRC client header for UFO:AI.
const char * CL_Translate(const char *t)
int CL_Milliseconds(void)
Definition cl_main.cpp:1207
#define _(String)
Definition cl_shared.h:44
Primary header for client.
const char * Cmd_Argv(int arg)
Returns a given argument.
Definition cmd.cpp:516
const char * Cmd_Args(void)
Returns a single string containing argv(1) to argv(argc()-1).
Definition cmd.cpp:526
int Cmd_Argc(void)
Return the number of arguments of the current command. "command parameter" will result in a argc of 2...
Definition cmd.cpp:505
void Cmd_AddCommand(const char *cmdName, xcommand_t function, const char *desc)
Add a new command to the script interface.
Definition cmd.cpp:744
void Cbuf_AddText(const char *format,...)
Adds command text at the end of the buffer.
Definition cmd.cpp:126
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
void Com_Printf(const char *const fmt,...)
Definition common.cpp:428
cvar_t * masterserver_url
Definition common.cpp:57
#define S_COLOR_GREEN
Definition common.h:219
#define UFO_VERSION
Definition common.h:36
cvar_t * Cvar_ForceSet(const char *varName, const char *value)
Will set the variable even if NOSET or LATCH.
Definition cvar.cpp:604
cvar_t * Cvar_Set(const char *varName, const char *value,...)
Sets a cvar value.
Definition cvar.cpp:615
cvar_t * Cvar_Get(const char *var_name, const char *var_value, int flags, const char *desc)
Init or return a cvar.
Definition cvar.cpp:342
const char * Cvar_GetString(const char *varName)
Returns the value of cvar as string.
Definition cvar.cpp:210
#define CVAR_ARCHIVE
Definition cvar.h:40
#define CVAR_NOSET
Definition cvar.h:43
#define __attribute__(x)
Definition cxx.h:37
#define DEBUG_CLIENT
Definition defines.h:59
cvar_t * password
Definition g_main.cpp:67
bool HTTP_GetURL(const char *url, http_callback_t callback, void *userdata, const char *postfields)
Downloads the given url and return the data to the callback (optional).
Definition http.cpp:384
voidpf void uLong size
Definition ioapi.h:42
voidpf void * buf
Definition ioapi.h:42
void LIST_AddString(linkedList_t **listDest, const char *data)
Adds an string to a new or to an already existing linked list. The string is copied here.
Definition list.cpp:139
static struct mdfour * m
Definition md4.cpp:35
#define Mem_DeletePool(pool)
Definition mem.h:33
#define Mem_PoolAllocTypeN(type, n, pool)
Definition mem.h:42
#define Mem_Free(ptr)
Definition mem.h:35
#define Mem_CreatePool(name)
Definition mem.h:32
#define Mem_AllocTypeN(type, n)
Definition mem.h:38
#define Mem_AllocType(type)
Definition mem.h:39
#define Mem_PoolAllocType(type, pool)
Definition mem.h:43
void NET_StreamEnqueue(struct net_stream *s, const char *data, int len)
Enqueue a network message into a stream.
Definition net.cpp:719
struct net_stream * NET_Connect(const char *node, const char *service, stream_onclose_func *onclose)
Try to connect to a given host on a given port.
Definition net.cpp:644
int NET_StreamDequeue(struct net_stream *s, char *data, int len)
Definition net.cpp:760
void NET_StreamFree(struct net_stream *s)
Call NET_StreamFree to dump the whole thing right now.
Definition net.cpp:817
void format(__printf__, 1, 2)))
QGL_EXTERN GLuint GLchar GLuint * len
Definition r_gl.h:99
QGL_EXTERN GLuint GLsizei GLsizei * length
Definition r_gl.h:110
QGL_EXTERN GLint i
Definition r_gl.h:113
QGL_EXTERN GLuint GLsizei GLsizei GLint GLenum GLchar * name
Definition r_gl.h:110
QGL_EXTERN GLuint * id
Definition r_gl.h:86
void S_StartLocalSample(const char *s, float volume)
Plays a sample without spatialization.
Definition s_mix.cpp:184
#define SND_VOLUME_DEFAULT
Definition s_main.h:42
#define inline
Definition shared.h:85
#define Q_streq(a, b)
Definition shared.h:136
#define OBJZERO(obj)
Definition shared.h:178
#define MAX_VAR
Definition shared.h:36
#define lengthof(x)
Definition shared.h:105
void Q_strncpyz(char *dest, const char *src, size_t destsize)
Safe strncpy that ensures a trailing zero.
Definition shared.cpp:457
int Q_StringSort(const void *string1, const void *string2)
Compare two strings.
Definition shared.cpp:385
int Q_vsnprintf(char *str, size_t size, const char *format, va_list ap)
Safe (null terminating) vsnprintf implementation.
Definition shared.cpp:535
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
struct irc_bucket_message_s * next
Definition cl_irc.cpp:335
int last_refill
Definition cl_irc.cpp:342
irc_bucket_message_t * first_msg
Definition cl_irc.cpp:339
unsigned int character_size
Definition cl_irc.cpp:341
double character_token
Definition cl_irc.cpp:343
unsigned int message_size
Definition cl_irc.cpp:340
char topic[256]
Definition cl_irc.cpp:72
char name[MAX_VAR]
Definition cl_irc.cpp:71
irc_user_t * user
Definition cl_irc.cpp:74
union irc_command_t::@066033050175334023300264044126211266013240245205 id
const char * string
Definition cl_irc.cpp:242
irc_command_type_t type
Definition cl_irc.cpp:244
irc_numeric_t numeric
Definition cl_irc.cpp:241
union irc_server_msg_t::@212327376166243107002225173133222274261315230343 id
char params[IRC_SEND_BUF_SIZE]
Definition cl_irc.cpp:255
char prefix[IRC_SEND_BUF_SIZE]
Definition cl_irc.cpp:254
irc_command_type_t type
Definition cl_irc.cpp:253
char trailing[IRC_SEND_BUF_SIZE]
Definition cl_irc.cpp:256
irc_numeric_t numeric
Definition cl_irc.cpp:251
char string[IRC_SEND_BUF_SIZE]
Definition cl_irc.cpp:250
struct irc_user_s * next
Definition cl_irc.cpp:67
char key[MAX_VAR]
Definition cl_irc.cpp:66
static const char * user
void UI_ResetData(int dataId)
Reset a shared data. Type became NONE and value became nullptr.
Definition ui_data.cpp:212
void UI_RegisterText(int dataId, const char *text)
share a text with a data id
Definition ui_data.cpp:115
void UI_RegisterLinkedListText(int dataId, linkedList_t *text)
share a linked list of text with a data id
Definition ui_data.cpp:131
@ TEXT_IRCUSERS
Definition ui_dataids.h:67
@ TEXT_IRCCONTENT
Definition ui_dataids.h:66
void UI_ExecuteConfunc(const char *fmt,...)
Executes confunc - just to identify those confuncs in the code - in this frame.
Definition ui_main.cpp:110
void UI_TextScrollEnd(const char *nodePath)
Scroll to the bottom.
void UI_Popup(const char *title, const char *text)
Popup on geoscape.
Definition ui_popup.cpp:47
char popupText[UI_MAX_SMALLTEXTLEN]
strings to be used for popup when text is not static
Definition ui_popup.cpp:37
uiNode_t * UI_PushWindow(const char *name, const char *parentName, linkedList_t *params)
Push a window onto the window stack.
void UI_PopWindow(bool all)
Pops a window from the window stack.
uiNode_t * UI_GetActiveWindow(void)
Returns the current active window from the window stack or nullptr if there is none.
const char * UI_GetActiveWindowName(void)
Returns the name of the current window.