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(<);
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