1 /**
2 * @file
3 * @brief Server commands.
4 */
5
6 /*
7 All original material Copyright (C) 2002-2013 UFO: Alien Invasion.
8
9 Original file from Quake 2 v3.21: quake2-2.31/game/g_svcmds.c
10 Copyright (C) 1997-2001 Id Software, Inc.
11
12 This program is free software; you can redistribute it and/or
13 modify it under the terms of the GNU General Public License
14 as published by the Free Software Foundation; either version 2
15 of the License, or (at your option) any later version.
16
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
20
21 See the GNU General Public License for more details.
22
23 You should have received a copy of the GNU General Public License
24 along with this program; if not, write to the Free Software
25 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
26
27 */
28
29
30 #include "g_local.h"
31 #include "g_ai.h"
32 #include "g_client.h"
33 #include "g_edicts.h"
34 #include "g_inventory.h"
35 #include "g_match.h"
36 #include "g_vis.h"
37
38 /**
39 * @brief PACKET FILTERING
40 * @note You can add or remove addresses from the filter list with:
41 *
42 * addip IP
43 * removeip IP
44 * The ip address is specified in dot format, and any unspecified digits will match any value, so you can specify an entire class C network with "addip 192.246.40".
45 * Removeip will only remove an address specified exactly the same way. You cannot addip a subnet, then removeip a single host.
46 *
47 * listip
48 * Prints the current list of filters.
49 *
50 * writeip
51 * Dumps "addip IP" commands to listip.cfg so it can be executed at a later date. The filter lists are not saved and restored by default, because I beleive it would cause too much confusion.
52 *
53 * sv_filterban <0 or 1>
54 * If 1 (the default), then ip addresses matching the current list will be prohibited from entering the game. This is the default setting.
55 * If 0, then only addresses matching the list will be allowed. This lets you easily set up a private game, or a game that only allows players from your local network.
56 */
57
58 typedef struct ipfilter_s {
59 unsigned mask;
60 unsigned compare;
61 } ipfilter_t;
62
63 #define MAX_IPFILTERS 1024
64
65 static ipfilter_t ipfilters[MAX_IPFILTERS];
66 static int numipfilters;
67
StringToFilter(const char * s,ipfilter_t * f)68 static bool StringToFilter (const char* s, ipfilter_t* f)
69 {
70 char num[128];
71 int i, j;
72 byte b[4];
73 byte m[4];
74
75 for (i = 0; i < 4; i++) {
76 b[i] = 0;
77 m[i] = 0;
78 }
79
80 for (i = 0; i < 4; i++) {
81 if (*s < '0' || *s > '9') {
82 /** @todo find out if this printf is needed. Passing nullptr as a player would have crashed anyway. */
83 //G_ClientPrintf(nullptr, PRINT_CONSOLE, "Bad filter address: %s\n", s);
84 return false;
85 }
86
87 j = 0;
88 while (isdigit(*s)) {
89 num[j++] = *s++;
90 }
91 num[j] = 0;
92 b[i] = atoi(num);
93 if (b[i] != 0)
94 m[i] = 0xFF;
95
96 if (!*s)
97 break;
98 s++;
99 }
100
101 f->mask = *(unsigned *) m;
102 f->compare = *(unsigned *) b;
103
104 return true;
105 }
106
SV_FilterPacket(const char * from)107 bool SV_FilterPacket (const char* from)
108 {
109 int i;
110 unsigned in;
111 byte m[4];
112 const char* p;
113
114 i = 0;
115 p = from;
116 while (*p && i < 4) {
117 m[i] = 0;
118 while (*p >= '0' && *p <= '9') {
119 m[i] = m[i] * 10 + (*p - '0');
120 p++;
121 }
122 if (!*p || *p == ':')
123 break;
124 i++, p++;
125 }
126
127 in = *(unsigned *) m;
128
129 for (i = 0; i < numipfilters; i++)
130 if ((in & ipfilters[i].mask) == ipfilters[i].compare)
131 return sv_filterban->integer;
132
133 return !sv_filterban->integer;
134 }
135
136
137 /**
138 * @sa SVCmd_RemoveIP_f
139 */
SVCmd_AddIP_f(void)140 static void SVCmd_AddIP_f (void)
141 {
142 int i;
143
144 if (gi.Cmd_Argc() < 3) {
145 gi.DPrintf("Usage: %s <ip-mask>\n", gi.Cmd_Argv(1));
146 return;
147 }
148
149 for (i = 0; i < numipfilters; i++)
150 if (ipfilters[i].compare == ~0x0)
151 break; /* free spot */
152 if (i == numipfilters) {
153 if (numipfilters == MAX_IPFILTERS) {
154 gi.DPrintf("IP filter list is full\n");
155 return;
156 }
157 numipfilters++;
158 }
159
160 if (!StringToFilter(gi.Cmd_Argv(2), &ipfilters[i]))
161 ipfilters[i].compare = ~0x0;
162 }
163
164 /**
165 * @sa SVCmd_AddIP_f
166 */
SVCmd_RemoveIP_f(void)167 static void SVCmd_RemoveIP_f (void)
168 {
169 ipfilter_t f;
170 int i, j;
171
172 if (gi.Cmd_Argc() < 3) {
173 gi.DPrintf("Usage: %s <ip-mask>\n", gi.Cmd_Argv(1));
174 return;
175 }
176
177 if (!StringToFilter(gi.Cmd_Argv(2), &f))
178 return;
179
180 for (i = 0; i < numipfilters; i++)
181 if (ipfilters[i].mask == f.mask && ipfilters[i].compare == f.compare) {
182 for (j = i + 1; j < numipfilters; j++)
183 ipfilters[j - 1] = ipfilters[j];
184 numipfilters--;
185 gi.DPrintf("Removed.\n");
186 return;
187 }
188 gi.DPrintf("Didn't find %s.\n", gi.Cmd_Argv(2));
189 }
190
191 /**
192 * @brief Shows the current ip in the filter list
193 */
SVCmd_ListIP_f(void)194 static void SVCmd_ListIP_f (void)
195 {
196 int i;
197 byte b[4];
198
199 gi.DPrintf("Filter list:\n");
200 for (i = 0; i < numipfilters; i++) {
201 *(unsigned *) b = ipfilters[i].compare;
202 gi.DPrintf("%3i.%3i.%3i.%3i\n", b[0], b[1], b[2], b[3]);
203 }
204 }
205
206 /**
207 * @brief Store all ips in the current filter list in
208 */
SVCmd_WriteIP_f(void)209 static void SVCmd_WriteIP_f (void)
210 {
211 FILE *f;
212 char name[MAX_OSPATH];
213 byte b[4];
214 int i;
215
216 Com_sprintf(name, sizeof(name), "%s/listip.cfg", gi.FS_Gamedir());
217
218 gi.DPrintf("Writing %s.\n", name);
219
220 f = fopen(name, "wb");
221 if (!f) {
222 gi.DPrintf("Couldn't open %s\n", name);
223 return;
224 }
225
226 fprintf(f, "set sv_filterban %d\n", sv_filterban->integer);
227
228 for (i = 0; i < numipfilters; i++) {
229 *(unsigned *) b = ipfilters[i].compare;
230 fprintf(f, "sv addip %i.%i.%i.%i\n", b[0], b[1], b[2], b[3]);
231 }
232
233 fclose(f);
234 }
235
236 /**
237 * @brief Used to add ai opponents to a game
238 * @note civilians can not be added with this function
239 * @sa AI_CreatePlayer
240 */
SVCmd_AI_Add_f(void)241 static void SVCmd_AI_Add_f (void)
242 {
243 int team;
244
245 if (gi.Cmd_Argc() < 3) {
246 gi.DPrintf("Usage: %s <teamnum>\n", gi.Cmd_Argv(1));
247 return;
248 }
249 team = atoi(gi.Cmd_Argv(2));
250 if (team > TEAM_CIVILIAN && team < MAX_TEAMS) {
251 if (!AI_CreatePlayer(team))
252 gi.DPrintf("Couldn't create AI player.\n");
253 } else
254 gi.DPrintf("Bad team number.\n");
255 }
256
257
258 /**
259 * @brief Call the end game function with the given team
260 * used to e.g. abort singleplayer games and let the aliens win
261 * @sa G_MatchEndTrigger
262 */
SVCmd_Win_f(void)263 static void SVCmd_Win_f (void)
264 {
265 int team;
266
267 if (gi.Cmd_Argc() < 3) {
268 gi.DPrintf("Usage: %s <teamnum>\n", gi.Cmd_Argv(1));
269 return;
270 }
271 team = atoi(gi.Cmd_Argv(2));
272 if (team > TEAM_CIVILIAN && team < MAX_TEAMS)
273 G_MatchEndTrigger(team, 0);
274 else
275 gi.DPrintf("Bad team number.\n");
276 }
277
278 #ifdef DEBUG
SVCmd_ShowAll_f(void)279 static void SVCmd_ShowAll_f (void)
280 {
281 Edict* ent = nullptr;
282
283 /* Make everything visible to anyone who can't already see it */
284 while ((ent = G_EdictsGetNextInUse(ent))) {
285 G_AppearPerishEvent(~G_VisToPM(ent->visflags), 1, *ent, nullptr);
286 G_VisFlagsAdd(*ent, ~ent->visflags);
287 }
288 gi.DPrintf("All items and creatures revealed to all sides\n");
289 }
290
SVCmd_AddItem_f(void)291 static void SVCmd_AddItem_f (void)
292 {
293 const int team = TEAM_DEFAULT;
294 Edict* ent = G_EdictsGetNextLivingActorOfTeam(nullptr, team);
295
296 if (gi.Cmd_Argc() < 3) {
297 gi.DPrintf("Usage: %s <item-id>\n", gi.Cmd_Argv(1));
298 return;
299 }
300
301 if (!ent) {
302 gi.DPrintf("Could not add item, no living members of team %i left\n", team);
303 return;
304 }
305
306 G_AddItemToFloor(ent->pos, gi.Cmd_Argv(2));
307 }
308
309 /**
310 * @brief Debug function to show the whole inventory of all connected clients on the server
311 */
SVCmd_ActorInvList_f(void)312 static void SVCmd_ActorInvList_f (void)
313 {
314 Player *player;
315 int i;
316
317 /* show inventory of all players around - include even the ai players */
318 for (i = 0, player = game.players; i < game.sv_maxplayersperteam * 2; i++, player++) {
319 if (!player->isInUse())
320 continue;
321 G_InvList_f(*player);
322 }
323 }
324
SVCmd_ListEdicts_f(void)325 static void SVCmd_ListEdicts_f (void)
326 {
327 Edict* ent = nullptr;
328 int i = 0;
329 Com_Printf("number | entnum | mapnum | type | inuse | pnum | team | size | HP | state | classname | model/ptl | pos\n");
330 while ((ent = G_EdictsGetNext(ent))) {
331 char buf[128];
332 const char* model;
333 if (ent->type == ET_PARTICLE)
334 model = ent->particle;
335 else if (ent->model)
336 model = ent->model;
337 else
338 model = "no mdl";
339 Com_sprintf(buf, sizeof(buf), "#%5i | #%5i | #%5i | %4i | %5i | %4i | %4i | %4i | %3i | %5i | %14s | %21s | %i:%i:%i",
340 i, ent->number, ent->mapNum, ent->type, ent->inuse, ent->pnum, ent->team, ent->fieldSize,
341 ent->HP, ent->state, ent->classname, model, ent->pos[0], ent->pos[1], ent->pos[2]);
342 Com_Printf("%s\n", buf);
343 i++;
344 }
345 }
346 #endif
347
348 /**
349 * @brief ServerCommand will be called when an "sv" command is issued.
350 * The game can issue gi.Cmd_Argc() / gi.Cmd_Argv() commands to get the rest
351 * of the parameters
352 * @sa serverCommandList
353 */
G_ServerCommand(void)354 void G_ServerCommand (void)
355 {
356 const char* cmd;
357
358 cmd = gi.Cmd_Argv(1);
359 if (Q_strcasecmp(cmd, "addip") == 0)
360 SVCmd_AddIP_f();
361 else if (Q_strcasecmp(cmd, "removeip") == 0)
362 SVCmd_RemoveIP_f();
363 else if (Q_strcasecmp(cmd, "listip") == 0)
364 SVCmd_ListIP_f();
365 else if (Q_strcasecmp(cmd, "writeip") == 0)
366 SVCmd_WriteIP_f();
367 else if (Q_strcasecmp(cmd, "ai_add") == 0)
368 SVCmd_AI_Add_f();
369 else if (Q_strcasecmp(cmd, "win") == 0)
370 SVCmd_Win_f();
371 #ifdef DEBUG
372 else if (Q_strcasecmp(cmd, "debug_showall") == 0)
373 SVCmd_ShowAll_f();
374 else if (Q_strcasecmp(cmd, "debug_additem") == 0)
375 SVCmd_AddItem_f();
376 else if (Q_strcasecmp(cmd, "debug_actorinvlist") == 0)
377 SVCmd_ActorInvList_f();
378 else if (Q_strcasecmp(cmd, "debug_listedicts") == 0)
379 SVCmd_ListEdicts_f();
380 #endif
381 else
382 gi.DPrintf("Unknown server command \"%s\"\n", cmd);
383 }
384