1 /*
2  * XPilot NG, a multiplayer space war game.
3  *
4  * Copyright (C) 2000-2004 by
5  *
6  *      Uoti Urpala          <uau@users.sourceforge.net>
7  *      Kristian S�derblom   <kps@users.sourceforge.net>
8  *
9  * Copyright (C) 1991-2001 by
10  *
11  *      Bj�rn Stabell        <bjoern@xpilot.org>
12  *      Ken Ronny Schouten   <ken@xpilot.org>
13  *      Bert Gijsbers        <bert@xpilot.org>
14  *      Dick Balaska         <dick@xpilot.org>
15  *
16  * This program is free software; you can redistribute it and/or modify
17  * it under the terms of the GNU General Public License as published by
18  * the Free Software Foundation; either version 2 of the License, or
19  * (at your option) any later version.
20  *
21  * This program is distributed in the hope that it will be useful,
22  * but WITHOUT ANY WARRANTY; without even the implied warranty of
23  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24  * GNU General Public License for more details.
25  *
26  * You should have received a copy of the GNU General Public License
27  * along with this program; if not, write to the Free Software
28  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
29  */
30 
31 #include "xpserver.h"
32 
33 /*
34  * Global variables
35  */
36 int			NumPlayers = 0;
37 int			NumAlliances = 0;
38 int			NumSpectators = 0;
39 int			NumOperators = 0;
40 int			spectatorStart;
41 server_t		Server;
42 char			*serverAddr;
43 int			ShutdownServer = -1;
44 int			ShutdownDelay = 1000;
45 char			ShutdownReason[MAX_CHARS];
46 long			main_loops = 0;		/* needed in events.c */
47 bool			is_server = true;	/* used in common code */
48 
49 static bool		NoPlayersEnteredYet = true;
50 bool			game_lock = false;
51 bool			mute_baseless = false;
52 
53 time_t			gameOverTime = 0;
54 time_t			serverStartTime = 0;
55 
56 #if !defined(_WINDOWS)
57 
58 /* The terminating signal, or zero if no signal. */
59 static volatile int termsig = 0;
60 
Handle_signal(int sig_no)61 static void Handle_signal(int sig_no)
62 {
63     termsig = sig_no;
64     stop_sched();
65 }
66 
67 #endif /* !defined(_WINDOWS) */
68 
main(int argc,char ** argv)69 int main(int argc, char **argv)
70 {
71     int timer_tick_rate;
72     char *addr;
73 
74     /* world is a global now */
75     world = &World;
76 
77     if (sock_startup() < 0) {
78     	warn("Error initializing sockets\n");
79 	return 1;
80     }
81 
82     if (World_init() < 0) {
83 	warn("Error initializing world\n");
84 	return 1;
85     }
86 
87     /*
88      * Make output always linebuffered.  By default pipes
89      * and remote shells cause stdout to be fully buffered.
90      */
91     setvbuf(stdout, NULL, _IOLBF, BUFSIZ);
92     setvbuf(stderr, NULL, _IOLBF, BUFSIZ);
93 
94     /*
95      * --- Output copyright notice ---
96      */
97 
98     xpprintf("  " COPYRIGHT ".\n"
99 	   "  " TITLE " comes with ABSOLUTELY NO WARRANTY; "
100 	      "for details see the\n"
101 	   "  provided COPYING file.\n\n");
102 
103     init_error(argv[0]);
104 
105     /*seedMT((unsigned)time(NULL) * Get_process_id());*/
106     /* Removed seeding random number generator because of server recordings. */
107 
108     Groups_init();
109 
110     /* Make trigonometric tables */
111     Make_table();
112 
113     if (!Parser(argc, argv))
114 	exit(1);
115 
116     Init_recording();
117     /* Lock the server into memory */
118     plock_server(options.pLockServer);
119 
120     Asteroid_line_init();
121     Wormhole_line_init();
122     Walls_init();
123 
124     /* Allocate memory for players, shots and messages */
125     Alloc_players(Num_bases() + MAX_PSEUDO_PLAYERS + MAX_SPECTATORS);
126     spectatorStart = Num_bases() + MAX_PSEUDO_PLAYERS;
127     Alloc_shots(MAX_TOTAL_SHOTS);
128     Alloc_cells();
129 
130     Move_init();
131     Robot_init();
132     Treasure_init();
133     Hitmasks_init();
134 
135     Rank_init_saved_scores();
136 
137     /*
138      * Get server's official name.
139      */
140     if (options.serverHost) {
141 	addr = sock_get_addr_by_name(options.serverHost);
142 	if (addr == NULL) {
143 	    warn("Failed name lookup on: %s", options.serverHost);
144 	    exit(1);
145 	}
146 	serverAddr = xp_strdup(addr);
147 	strlcpy(Server.host, options.serverHost, sizeof(Server.host));
148     } else
149 	sock_get_local_hostname(Server.host, sizeof Server.host,
150 				(options.reportToMetaServer != 0 &&
151 				 options.searchDomainForXPilot != 0));
152 
153     Get_login_name(Server.owner, sizeof Server.owner);
154 
155     /* Log, if enabled. */
156     Log_game("START");
157 
158     if (!Contact_init())
159 	End_game();
160 
161     Meta_init();
162 
163     Timing_setup();
164     Check_playerlimit();
165 
166     if (Setup_net_server() == -1)
167 	End_game();
168 
169 #ifndef _WINDOWS
170     if (options.NoQuit)
171 	signal(SIGHUP, SIG_IGN);
172     else
173 	signal(SIGHUP, Handle_signal);
174     signal(SIGTERM, Handle_signal);
175     signal(SIGINT, Handle_signal);
176     signal(SIGPIPE, SIG_IGN);
177 #ifdef IGNORE_FPE
178     signal(SIGFPE, SIG_IGN);
179 #endif
180 #endif	/* _WINDOWS */
181 
182     /*
183      * Set the time the server started
184      */
185     serverStartTime = time(NULL);
186 
187     xpprintf("%s Server runs at %d frames per second\n",
188 	     showtime(), options.framesPerSecond);
189 
190     teamcup_init();
191 
192 #ifdef SELECT_SCHED
193     install_timer_tick(Main_loop, FPS);
194 #else
195     if (options.timerResolution > 0)
196 	timer_tick_rate = options.timerResolution;
197     else
198 	timer_tick_rate = FPS;
199 
200 # ifdef _WINDOWS
201     /* Windows returns here, we let the worker thread call sched() */
202     install_timer_tick(ServerThreadTimerProc, timer_tick_rate);
203 # else
204     install_timer_tick(Main_loop, timer_tick_rate);
205 
206 # endif
207 #endif
208 
209     sched();
210     End_game();
211 
212     /* NOT REACHED */
213     abort();
214 }
215 
Main_loop(void)216 void Main_loop(void)
217 {
218     struct timeval tv1, tv2;
219     double t1, t2;
220 
221     gettimeofday(&tv1, NULL);
222 
223     main_loops++;
224 
225     if ((main_loops & 0x3F) == 0)
226 	Meta_update(false);
227 
228     /*
229      * Check for possible shutdown, the server will
230      * shutdown when ShutdownServer (a counter) reaches 0.
231      * If the counter is < 0 then no shutdown is in progress.
232      */
233     if (ShutdownServer >= 0) {
234 	if (ShutdownServer == 0)
235 	    End_game();
236 	else
237 	    ShutdownServer--;
238     }
239 
240     Input();
241 
242     if (NumPlayers > NumRobots + NumPseudoPlayers || options.RawMode) {
243 
244 	if (NoPlayersEnteredYet) {
245 	    if (NumPlayers > NumRobots + NumPseudoPlayers) {
246 		NoPlayersEnteredYet = false;
247 		if (options.gameDuration > 0.0) {
248 		    xpprintf("%s Server will stop in %g minutes.\n",
249 			     showtime(), options.gameDuration);
250 		    gameOverTime
251 			= (time_t)(options.gameDuration * 60) + time(NULL);
252 		}
253 	    }
254 	}
255 
256 	Update_objects();
257 
258 	if ((main_loops % CONF_UPDATES_PR_FRAME) == 0)
259 	    Frame_update();
260     }
261 
262     if (!options.NoQuit
263 	&& NumPlayers == NumRobots + NumPseudoPlayers
264 	&& !login_in_progress
265 	&& !NumQueuedPlayers) {
266 
267 	if (!NoPlayersEnteredYet)
268 	    End_game();
269 
270 	if (serverStartTime + 5*60 < time(NULL)) {
271 	    error("First player has yet to show his butt, I'm bored... Bye!");
272 	    Log_game("NOSHOW");
273 	    End_game();
274 	}
275     }
276 
277     playback = record = 0;
278     Queue_loop();
279     playback = rplayback;
280     record = rrecord;
281 
282     if (playback && (*playback_ei == main_loops)) {
283 	char *a, *b, *c, *d, *e;
284 	int i;
285 	unsigned j;
286 	a = playback_es;
287 	while (*playback_es++);
288 	b = playback_es;
289 	while (*playback_es++);
290 	c = playback_es;
291 	while (*playback_es++);
292 	d = playback_es;
293 	while (*playback_es++);
294 	e = playback_es;
295 	while (*playback_es++);
296 	playback_ei++;
297 	i = *playback_ei++;
298 	j = *playback_ei++;
299 	Setup_connection(a, b, c, i, d, e, j);
300     }
301 
302     gettimeofday(&tv2, NULL);
303     t1 = timeval_to_seconds(&tv1);
304     t2 = timeval_to_seconds(&tv2);
305     options.mainLoopTime = (t2 - t1) * 1e3;
306 }
307 
308 
309 /*
310  *  Last function, exit with grace.
311  */
End_game(void)312 void End_game(void)
313 {
314     player_t *pl;
315     char msg[MSG_LEN];
316 
317 #if !defined(_WINDOWS)
318     if (termsig != 0)
319 	warn("Terminating on signal %d", termsig);
320 #endif
321 
322     record = rrecord;
323     playback = rplayback; /* Could be called from signal handler */
324     if (ShutdownServer == 0) {
325 	warn("Shutting down...");
326 	snprintf(msg, sizeof(msg), "shutting down: %s", ShutdownReason);
327     } else
328 	snprintf(msg, sizeof(msg), "server exiting");
329 
330     teamcup_game_over();
331 
332     while (NumPlayers > 0) {	/* Kick out all remaining players */
333 	pl = Player_by_index(NumPlayers - 1);
334 	if (pl->conn == NULL)
335 	    Delete_player(pl);
336 	else
337 	    Destroy_connection(pl->conn, msg);
338     }
339 
340     record = playback = 0;
341     while (NumSpectators > 0) {
342 	pl = Player_by_index(spectatorStart + NumSpectators - 1);
343 	Destroy_connection(pl->conn, msg);
344     }
345     record = rrecord;
346     playback = rplayback;
347 
348     if (options.recordMode != 0) {
349 	options.recordMode = 0;
350 	Init_recording();
351     }
352 
353     /* Tell meta server that we are gone. */
354     Meta_gone();
355 
356     Contact_cleanup();
357 
358     /* Ranking. */
359     Rank_write_webpage();
360     Rank_write_rankfile();
361 
362     Free_players();
363     Free_shots();
364     World_free();
365     Free_cells();
366     Free_options();
367     Log_game("END");
368 
369     sock_cleanup();
370 
371 #if !defined(_WINDOWS)
372     if (termsig != 0) {
373 	signal(termsig, SIG_DFL);
374 	raise(termsig);
375     }
376 #endif
377 
378     exit(0);
379 }
380 
381 /*
382  * Return a good team number for a player.
383  *
384  * If the team is not specified, the player is assigned
385  * to a non-empty team which has space.
386  *
387  * If there is none or only one team with playing (i.e. non-paused)
388  * players the player will be assigned to a randomly chosen empty team.
389  *
390  * If there is more than one team with playing players,
391  * the player will be assigned randomly to a team which
392  * has the least number of playing players.
393  *
394  * If all non-empty teams are full, the player is assigned
395  * to a randomly chosen available team.
396  *
397  * Prefer not to place players in the options.robotTeam if possible.
398  */
Pick_team(int pick_for_type)399 int Pick_team(int pick_for_type)
400 {
401     int i, least_players, num_available_teams = 0, playing_teams = 0;
402     int losing_team;
403     player_t *pl;
404     int playing[MAX_TEAMS], free_bases[MAX_TEAMS], available_teams[MAX_TEAMS];
405     double team_score[MAX_TEAMS], losing_score;
406 
407     /* If game_lock is on, can't join playing teams (might be able to join
408      * paused). */
409     if (game_lock && pick_for_type == PL_TYPE_HUMAN)
410 	return TEAM_NOT_SET;
411 
412     for (i = 0; i < MAX_TEAMS; i++) {
413 	free_bases[i] = world->teams[i].NumBases - world->teams[i].NumMembers;
414 	playing[i] = 0;
415 	team_score[i] = 0;
416 	available_teams[i] = 0;
417     }
418     if (options.restrictRobots) {
419 	if (pick_for_type == PL_TYPE_ROBOT) {
420 	    if (free_bases[options.robotTeam] > 0)
421 		return options.robotTeam;
422 	    else
423 		return TEAM_NOT_SET;
424 	}
425     }
426     if (options.reserveRobotTeam) {
427 	if (pick_for_type != PL_TYPE_ROBOT)
428 	    free_bases[options.robotTeam] = 0;
429     }
430 
431     /*
432      * Find out which teams have actively playing members.
433      * Exclude paused players and tanks.
434      * And calculate the score for each team.
435      */
436     for (i = 0; i < NumPlayers; i++) {
437 	pl = Player_by_index(i);
438 	if (Player_is_tank(pl))
439 	    continue;
440 	if (Player_is_paused(pl))
441 	    continue;
442 	if (!playing[pl->team]++)
443 	    playing_teams++;
444 	if (Player_is_human(pl) || Player_is_robot(pl))
445 	    team_score[pl->team] += Get_Score(pl);
446     }
447     if (playing_teams <= 1) {
448 	for (i = 0; i < MAX_TEAMS; i++) {
449 	    if (!playing[i] && free_bases[i] > 0)
450 		available_teams[num_available_teams++] = i;
451 	}
452     } else {
453 	least_players = NumPlayers;
454 	for (i = 0; i < MAX_TEAMS; i++) {
455 	    /* We fill teams with players first. */
456 	    if (playing[i] > 0 && free_bases[i] > 0) {
457 		if (playing[i] < least_players)
458 		    least_players = playing[i];
459 	    }
460 	}
461 
462 	for (i = 0; i < MAX_TEAMS; i++) {
463 	    if (free_bases[i] > 0) {
464 		if (least_players == NumPlayers
465 			    || playing[i] == least_players)
466 		    available_teams[num_available_teams++] = i;
467 	    }
468 	}
469     }
470 
471     if (!num_available_teams) {
472 	for (i = 0; i < MAX_TEAMS; i++) {
473 	    if (free_bases[i] > 0)
474 		available_teams[num_available_teams++] = i;
475 	}
476     }
477 
478     if (num_available_teams == 1)
479 	return available_teams[0];
480 
481     if (num_available_teams > 1) {
482 	losing_team = -1;
483 	losing_score = LONG_MAX;
484 	for (i = 0; i < num_available_teams; i++) {
485 	    if (team_score[available_teams[i]] < losing_score
486 		&& available_teams[i] != options.robotTeam) {
487 		losing_team = available_teams[i];
488 		losing_score = team_score[losing_team];
489 	    }
490 	}
491 	return losing_team;
492     }
493 
494     return TEAM_NOT_SET;
495 }
496 
Describe_game_status(void)497 const char *Describe_game_status(void)
498 {
499     return (game_lock && ShutdownServer == -1) ? "locked"
500 	: (!game_lock && ShutdownServer != -1) ? "shutting down"
501 	: (game_lock && ShutdownServer != -1) ? "locked and shutting down"
502 	: "ok";
503 }
504 
505 /*
506  * Return status for server
507  *
508  * TODO
509  */
Server_info(char * str,size_t max_size)510 void Server_info(char *str, size_t max_size)
511 {
512     int i, j, k;
513     player_t *pl, **order;
514     char name[MAX_CHARS], lblstr[MAX_CHARS], msg[MSG_LEN];
515 
516     snprintf(str, max_size,
517 	     "SERVER VERSION..: %s\n"
518 	     "STATUS..........: %s\n"
519 	     "CURRENT FPS.....: %d\n"
520 	     "WORLD...........: %s\n"
521 	     "      AUTHOR....: %s\n"
522 	     "      SIZE......: %dx%d pixels\n"
523 	     "PLAYERS.........: %2d/%2d\n"
524 	     "\n"
525 	     "XPILOT NG SERVER, see\n"
526 	     "http://xpilot.sourceforge.net/\n"
527 	     "\n",
528 	     VERSION,
529 	     Describe_game_status(),
530 	     FPS,
531 	     world->name, world->author, world->width, world->height,
532 	     NumPlayers, Num_bases());
533 
534     assert(strlen(str) < max_size);
535 
536     if (NumPlayers <= 0)
537 	return;
538 
539     strlcpy(msg, "\n"
540 	    "NO:  TM: NAME:             LIFE:   SC:    PLAYER:\n"
541 	    "-------------------------------------------------\n",
542 	    sizeof(msg));
543 
544     if (strlen(msg) + strlen(str) >= max_size)
545 	return;
546 
547     strlcat(str, msg, max_size);
548 
549     if ((order = (player_t **) malloc(NumPlayers * sizeof(player_t *)))
550 	== NULL) {
551 	error("No memory for order");
552 	return;
553     }
554     for (i = 0; i < NumPlayers; i++) {
555 	pl = Player_by_index(i);
556 
557 	for (j = 0; j < i; j++) {
558 	    if (Get_Score(order[j]) < Get_Score(pl)) {
559 		for (k = i; k > j; k--)
560 		    order[k] = order[k - 1];
561 		break;
562 	    }
563 	}
564 	order[j] = pl;
565     }
566     for (i = 0; i < NumPlayers; i++) {
567 	pl = order[i];
568 	strlcpy(name, pl->name, MAX_CHARS);
569 	snprintf(lblstr, sizeof(lblstr), "%c%c %-19s%03d%6.0f",
570 		 pl->mychar, pl->team == TEAM_NOT_SET ? ' ' : (pl->team + '0'),
571 		 name, pl->pl_life, Get_Score(pl));
572 	snprintf(msg, sizeof(msg), "%2d... %-36s%s@%s\n",
573 		 i + 1, lblstr, pl->username, pl->hostname);
574 	if (strlen(msg) + strlen(str) >= max_size)
575 	    break;
576 	strlcat(str, msg, max_size);
577     }
578     free(order);
579 }
580 
581 /* kps - is this useful??? */
Log_game(const char * heading)582 void Log_game(const char *heading)
583 {
584     char str[1024];
585     FILE *fp;
586     char timenow[81];
587     struct tm *ptr;
588     time_t lt;
589 
590     if (!options.Log)
591 	return;
592 
593     lt = time(NULL);
594     ptr = localtime(&lt);
595     strftime(timenow, 79, "%I:%M:%S %p %Z %A, %B %d, %Y", ptr);
596 
597     snprintf(str, sizeof(str),
598 	     "%-50.50s\t%10.10s@%-15.15s\tWorld: %-25.25s\t%10.10s\n",
599 	     timenow, Server.owner, Server.host, world->name, heading);
600 
601     if ((fp = fopen(Conf_logfile(), "a")) == NULL) {
602 	error("Couldn't open log file, contact %s", Conf_localguru());
603 	return;
604     }
605 
606     fputs(str, fp);
607     fclose(fp);
608 }
609 
Game_Over(void)610 void Game_Over(void)
611 {
612     double maxsc, minsc;
613     int i, win_team = TEAM_NOT_SET, lose_team = TEAM_NOT_SET;
614     char msg[MSG_LEN];
615     player_t *win_pl = NULL, *lose_pl = NULL;
616 
617     Set_message("Game over...");
618 
619     teamcup_game_over();
620 
621     /*
622      * Hack to prevent Compute_Game_Status from starting over again...
623      */
624     options.gameDuration = -1.0;
625 
626     if (BIT(world->rules->mode, TEAM_PLAY)) {
627 	double teamscore[MAX_TEAMS];
628 
629 	for (i = 0; i < MAX_TEAMS; i++)
630 	    teamscore[i] = FLT_MAX; /* These teams are not used... */
631 
632 	for (i = 0; i < NumPlayers; i++) {
633 	    player_t *pl = Player_by_index(i);
634 	    int team;
635 
636 	    if (Player_is_paused(pl))
637 		continue;
638 
639 	    if (Player_is_human(pl)
640 		|| Player_is_robot(pl)) {
641 		team = pl->team;
642 		if (teamscore[team] == FLT_MAX)
643 		    teamscore[team] = 0;
644 		teamscore[team] += Get_Score(pl);
645 	    }
646 	}
647 
648 	maxsc = -FLT_MAX;
649 	minsc = FLT_MAX;
650 
651 	for (i = 0; i < MAX_TEAMS; i++) {
652 	    if (teamscore[i] != FLT_MAX) {
653 		if (teamscore[i] > maxsc) {
654 		    maxsc = teamscore[i];
655 		    win_team = i;
656 		}
657 		if (teamscore[i] < minsc) {
658 		    minsc = teamscore[i];
659 		    lose_team = i;
660 		}
661 	    }
662 	}
663 
664 	if (win_team != TEAM_NOT_SET) {
665 	    snprintf(msg, sizeof(msg), "Best team (%.2f Pts): Team %d",
666 		     maxsc, win_team);
667 	    Set_message(msg);
668 	    xpprintf("%s\n", msg);
669 	}
670 
671 	if (lose_team != TEAM_NOT_SET && lose_team != win_team) {
672 	    snprintf(msg, sizeof(msg), "Worst team (%.2f Pts): Team %d",
673 		     minsc, lose_team);
674 	    Set_message(msg);
675 	    xpprintf("%s\n", msg);
676 	}
677     }
678 
679     maxsc = -FLT_MAX;
680     minsc = FLT_MAX;
681 
682     for (i = 0; i < NumPlayers; i++) {
683 	player_t *pl_i = Player_by_index(i);
684 
685 	if (Player_is_paused(pl_i))
686 	    continue;
687 
688 	Player_set_state(pl_i, PL_STATE_DEAD);
689 	if (Player_is_human(pl_i)) {
690 	    if (Get_Score(pl_i) > maxsc) {
691 		maxsc = Get_Score(pl_i);
692 		win_pl = pl_i;
693 	    }
694 	    if (Get_Score(pl_i) < minsc) {
695 		minsc = Get_Score(pl_i);
696 		lose_pl = pl_i;
697 	    }
698 	}
699     }
700     if (win_pl) {
701 	snprintf(msg, sizeof(msg), "Best human player: %s", win_pl->name);
702 	Set_message(msg);
703 	xpprintf("%s\n", msg);
704     }
705     if (lose_pl && lose_pl != win_pl) {
706 	snprintf(msg, sizeof(msg), "Worst human player: %s", lose_pl->name);
707 	Set_message(msg);
708 	xpprintf("%s\n", msg);
709     }
710 }
711 
Server_shutdown(const char * user_name,int delay,const char * reason)712 void Server_shutdown(const char *user_name, int delay, const char *reason)
713 {
714     Set_message_f("|*******| %s (%s) |*******| \"%s\" [*Server notice*]",
715 		  (delay > 0) ? "SHUTTING DOWN" : "SHUTDOWN STOPPED",
716 		  user_name, reason);
717     strlcpy(ShutdownReason, reason, sizeof(ShutdownReason));
718     if (delay > 0) {
719 	/* delay is in seconds */;
720 	ShutdownServer = delay * FPS;
721 	ShutdownDelay = ShutdownServer;
722     } else
723 	ShutdownServer = -1;
724 }
725 
Server_log_admin_message(player_t * pl,const char * str)726 void Server_log_admin_message(player_t *pl, const char *str)
727 {
728     /*
729      * Only log the message if logfile already exists,
730      * is writable and less than some KBs in size.
731      */
732     const char *logfilename = options.adminMessageFileName;
733     const int logfile_size_limit = options.adminMessageFileSizeLimit;
734     FILE *fp;
735     struct stat st;
736     char msg[MSG_LEN * 2];
737 
738     if ((logfilename != NULL) &&
739 	(logfilename[0] != '\0') &&
740 	(logfile_size_limit > 0) &&
741 	(access(logfilename, 2) == 0) &&
742 	(stat(logfilename, &st) == 0) &&
743 	(st.st_size + 80 < logfile_size_limit) &&
744 	((size_t)(logfile_size_limit - st.st_size - 80) > strlen(str)) &&
745 	((fp = fopen(logfilename, "a")) != NULL))
746     {
747 	fprintf(fp,
748 		"%s[%s]{%s@%s(%s)|%s}:\n"
749 		"\t%s\n",
750 		showtime(),
751 		pl->name,
752 		pl->username, pl->hostname,
753 		Player_get_addr(pl),
754 		Player_get_dpy(pl),
755 		str);
756 	fclose(fp);
757 	snprintf(msg, sizeof(msg), "%s [%s]:[%s]", str, pl->name, "GOD");
758 	Set_player_message(pl, msg);
759     }
760     else
761 	Set_player_message(pl, " < GOD doesn't seem to be listening>");
762 }
763 
764 #if defined(PLOCKSERVER) && defined(__linux__)
765 /*
766  * Patches for Linux plock support by Steve Payne <srp20@cam.ac.uk>
767  * also added the -options.pLockServer command line option.
768  * All messed up by BG again, with thanks and apologies to Steve.
769  */
770 /* Linux doesn't seem to have plock(2).  *sigh* (BG) */
771 #if !defined(PROCLOCK) || !defined(UNLOCK)
772 #define PROCLOCK	0x01
773 #define UNLOCK		0x00
774 #endif
plock(int op)775 static int plock(int op)
776 {
777 #if defined(MCL_CURRENT) && defined(MCL_FUTURE)
778     return op ? mlockall(MCL_CURRENT | MCL_FUTURE) : munlockall();
779 #else
780     return -1;
781 #endif
782 }
783 #endif
784 
785 /*
786  * Lock the server process data and code segments into memory
787  * if this program has been compiled with the PLOCKSERVER flag.
788  * Or unlock the server process if the argument is false.
789  */
plock_server(bool on)790 int plock_server(bool on)
791 {
792 #ifdef PLOCKSERVER
793     int			op;
794 
795     if (on)
796 	op = PROCLOCK;
797     else
798 	op = UNLOCK;
799 
800     if (plock(op) == -1) {
801 	static int num_plock_errors;
802 	if (++num_plock_errors <= 3)
803 	    error("Can't plock(%d)", op);
804 	return -1;
805     }
806     return on ? 1 : 0;
807 #else
808     if (on)
809 	xpprintf("Can't plock: Server was not compiled with plock support\n");
810     return 0;
811 #endif
812 }
813 
814 
815 /* kps - this is really ugly */
816 extern bool in_move_player;
817 
Friction_area_hitfunc(group_t * groupptr,const move_t * move)818 bool Friction_area_hitfunc(group_t *groupptr, const move_t *move)
819 {
820     UNUSED_PARAM(groupptr); UNUSED_PARAM(move);
821 
822     if (in_move_player)
823 	return true;
824     return false;
825 }
826 
827 /*
828  * Handling of group properties
829  */
Team_immunity_init(void)830 void Team_immunity_init(void)
831 {
832     int group;
833 
834     for (group = 0; group < num_groups; group++) {
835 	group_t *gp = groupptr_by_id(group);
836 
837 	if (gp->type == CANNON) {
838 	    cannon_t *cannon = Cannon_by_index(gp->mapobj_ind);
839 
840 	    assert(cannon->group == group);
841 	    Cannon_set_hitmask(group, cannon);
842 	}
843     }
844 
845 #if 0
846     /* change hitmask of all cannons */
847     P_grouphack(CANNON, Cannon_set_hitmask);
848 #endif
849 }
850 
851 /* kps - called at server startup to initialize hit masks */
Hitmasks_init(void)852 void Hitmasks_init(void)
853 {
854     Target_init();
855     Team_immunity_init();
856 }
857 
858