1 /* Implementation of the transfer protocol for networked Xconq.
2    Copyright (C) 1996-2000 Stanley T. Shebs.
3    Copyright (C) 2005 Eric A. McDonald.
4 
5 Xconq is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2, or (at your option)
8 any later version.  See the file COPYING. */
9 
10 /* This is the implementation of the high-level protocol. */
11 
12 #include "conq.h"
13 #include "kernel.h"
14 #include "kpublic.h"
15 
16 extern long randstate;
17 
18 /* Iteration over all remote programs, by rid. */
19 
20 #define for_all_remotes(rid) \
21   for ((rid) = 1; (rid) <= numremotes; ++(rid)) \
22     if (online[rid])
23 
24 /* Definitions of special characters in the protocol. */
25 
26 #define STARTPKT '$'
27 #define ENDPKT '^'
28 #define ESCAPEPKT '!'
29 
30 #define STARTPKTESC '%'
31 #define ENDPKTESC '&'
32 #define ESCAPEPKTESC '@'
33 
34 static void broadcast_command_5(char *cmd, int val, int val2, int val3,
35 				int val4, int val5);
36 static void broadcast_side_property(Side *side, char *prop, int val);
37 static void broadcast_side_property_2(Side *side, char *prop, int val,
38 				      int val2);
39 static void broadcast_side_str_property(Side *side, char *prop, char *val);
40 static void broadcast_unit_property(Side *side, Unit *unit, char *prop,
41 				    int val);
42 static void broadcast_unit_property_2(Side *side, Unit *unit, char *prop,
43 				      int val, int val2);
44 static void broadcast_unit_property_5(Side *side, Unit *unit, char *prop,
45 				      int val, int val2, int val3, int val4,
46 				      int val5);
47 static void broadcast_unit_str_property(Side *side, Unit *unit, char *prop,
48 					char *val);
49 static void broadcast_add_task(Unit *unit, int pos, Task *task);
50 static void broadcast_layer_change(char *layername, Side *side, int x, int y,
51 				   int a1, int a2, int a3, int a4);
52 static void broadcast_packet(char *buf);
53 static void save_outgoing_packet(int id, char *inbuf);
54 static void save_incoming_packet(int id, char *inbuf);
55 static void remove_chars(char *buf, int n);
56 static void receive_net_message(char *str);
57 static void receive_command(char *str);
58 static void receive_action(char *str);
59 static void receive_player_prop(char *str);
60 static void receive_quit(char *str);
61 static void receive_bugoff(int rid);
62 static void receive_side_prop(char *str);
63 static void receive_task(char *str);
64 static void receive_unit_prop(char *str);
65 static void receive_world_prop(char *str);
66 static void receive_run_game(char *str);
67 static void receive_game_checksum(char *str);
68 static void receive_error(int id, char *str);
69 static void receive_remote_program(char *str);
70 static void receive_chat(char *str);
71 static void receive_variant_setting(char *str);
72 static void receive_assignment_setting(char *str);
73 static int tohex(int x);
74 static int fromhex(int x);
75 static int flush_incoming_queue(int lim);
76 static void send_game_checksum_error(int rid, int my_csum, int master_csum);
77 
78 static int send_packet(int id, char *buf);
79 static void broadcast_game_checksum(void);
80 static void broadcast_action(Unit *unit, int actee, ActionType atype,
81 			     int arg0, int arg1, int arg2, int arg3);
82 static void receive_packet(int id, char *buf);
83 static void add_remote_program(int rid, char *name);
84 static void download_game_module(int rid);
85 static void send_remote_id(int rid);
86 static void send_bugoff(int rid);
87 
88 static void csum_printf(char *str, ...);
89 
90 static int dumped_checksums = FALSE;
91 static FILE *cfp = NULL;
92 
93 /* True if we are in the process of flushing the incoming queue. */
94 
95 int flushing = FALSE;
96 
97 /* True if we process (and rebroadcast) packets as soon as they are received. */
98 
99 int process_packets = TRUE;
100 
101 /* Turns on output of network traffic through notifications. */
102 
103 int display_traffic = FALSE;
104 
105 /* This is true if the program expects to host the game and also be the
106    master when all the remote programs are hooked up. */
107 
108 int hosting;
109 
110 /* This is the total number of programs in the game. */
111 
112 int numremotes;
113 
114 int numremotewaiting;
115 
116 /* The program to which we are currently downloading. */
117 
118 int tmprid;
119 
120 char *remote_player_specs[MAXSIDES];
121 
122 int online[MAXSIDES];
123 
124 int expecting_ack;
125 
126 int timeout_warnings = TRUE;
127 
128 int sendnow;
129 
130 /* Flag indicating that we're in the middle of downloading. */
131 
132 int downloading;
133 
134 char *downloadbuf;
135 
136 int dlbufend;
137 
138 long new_randstate;
139 
140 void (*update_variant_callback)(int which) = NULL;
141 
142 void (*update_assignment_callback)(int n) = NULL;
143 
144 int quitter;
145 
146 /* Flag indicating that we were bounced from an ongoing game. */
147 
148 int bounced = FALSE;
149 
150 char *default_player_spec = NULL;
151 
152 /* Given a specification for the game to be hosted (such as a port
153    number), set up to host and return success/failure. */
154 
155 int
host_the_game(char * hostport)156 host_the_game(char *hostport)
157 {
158     int i, rslt;
159     Player *player;
160     extern char *remote_player_specs[MAXSIDES];
161     extern int online[MAXSIDES];
162 
163     hosting = TRUE;
164     /* If we call this before a game is loaded, numsides will be zero
165        and this won't do anything, which is what we want. */
166     for (i = 0; i < numsides; ++i) {
167 	if (assignments[i].side && (assignments[i].side)->ingame) {
168 	    player = assignments[i].player;
169 	    if (player->displayname != NULL) {
170 		player->rid = 1;
171 	    } else if (player->name) {
172 		player->remotewanted = player->name;
173 	    }
174 	}
175     }
176     rslt = open_remote_connection(hostport, hosting);
177     if (rslt == 0) {
178 	hosting = FALSE;
179 	return FALSE;
180     }
181     /* As the host, we arbitrarily give ourselves remote id #1. */
182     my_rid = master_rid = 1;
183     /* Compose a plausible default player spec if necessary. */
184     if (default_player_spec == NULL)
185       make_default_player_spec();
186     /* Record our online and active status. */
187     online[my_rid] = TRUE;
188     remote_player_specs[my_rid] = copy_string(default_player_spec);
189     add_remote_locally(my_rid, default_player_spec);
190     numremotes = 1;
191     return TRUE;
192 }
193 
194 /* Try to join a game at the given host and port. */
195 
196 int
try_join_game(char * hostport)197 try_join_game(char *hostport)
198 {
199     int rslt;
200 
201     hosting = FALSE;
202     rslt = open_remote_connection(copy_string(hostport), hosting);
203     /* If failure, return quietly and let caller handle interaction. */
204     if (rslt == 0) {
205 	close_remote_connection(0);
206 	return FALSE;
207     }
208     sendnow = TRUE;
209     /* Compose a player spec if necessary. */
210     if (default_player_spec == NULL)
211       make_default_player_spec();
212 
213 #ifdef MACOSX
214     /* For mysterious reasons, Mac OSX requires a brief pause here, or
215     send_join will fail. Mac OS9 (Classic or Carbon) works fine without
216     this hack. */
217     {
218     	extern int last_ticks;
219 
220     	last_ticks = TickCount();
221     	while(!n_seconds_elapsed(1));
222     }
223 #endif
224 
225     /* Send a join packet and then wait until we're officially added
226        to the game. */
227     rslt = send_join(default_player_spec);
228     sendnow = FALSE;
229     if (rslt) {
230 	master_rid = 1;
231 	while (my_rid == 0) {
232 	    /* (should have a timeout just in case) */
233 	    receive_data(0, MAXPACKETS);
234 	    /* We received a bugoff message. */
235 	    if (bounced) {
236 		close_remote_connection(0);
237 		/* Reset the flag in case we want to try another host. */
238 		bounced = FALSE;
239 		return DONE;
240 	    }
241 	}
242     } else {
243 	close_remote_connection(0);
244 	return FALSE;
245     }
246     /* We've succeeded in getting connected.  Note that this just
247        means that we're in the system of synchronized programs,
248        not that we have a side/player in the game. */
249     return TRUE;
250 }
251 
252 /* If a side is being played from a remote program, we don't need the
253    full user interface structure, just make a placeholder that records
254    the program's id. */
255 
256 void
init_remote_ui(Side * side)257 init_remote_ui(Side *side)
258 {
259     if (side->rui == NULL) {
260 	side->rui = (RUI *) xmalloc(sizeof(RUI));
261     }
262     /* At present, rui->rid is unused. */
263     side->rui->rid = side->player->rid;
264 }
265 
266 /* For a new remote program with given remote id and player spec,
267    record it locally and also broadcast to all remotes currently
268    online. */
269 
270 void
add_remote_program(int rid,char * name)271 add_remote_program(int rid, char *name)
272 {
273     int rid2;
274 
275     numremotes = max(rid, numremotes);
276     online[rid] = TRUE;
277     /* Inform our new program of all the existing programs. */
278     for_all_remotes(rid2) {
279 	if (rid2 != rid) {
280 	    sprintf(spbuf, "p%d %s", rid2, remote_player_specs[rid2]);
281 	    send_packet(rid, spbuf);
282 	}
283     }
284     remote_player_specs[rid] = copy_string(name);
285     add_remote_locally(rid, name);
286     /* Now broadcast info about the new program to everybody. */
287     sprintf(spbuf, "p%d %s", rid, name);
288     broadcast_packet(spbuf);
289 }
290 
291 /* For each remote program (including us), add a player corresponding
292    to the spec supplied by that program. */
293 
294 void
add_remote_players(void)295 add_remote_players(void)
296 {
297     int rid;
298     Player *player;
299 
300     for_all_remotes(rid) {
301 	/* First look for an existing player object that's waiting to
302 	   be associated with a remote program. */
303 	player = NULL;
304 	for_all_players(player) {
305 	    if (!empty_string(player->name)
306 		&& strcmp(player->name, "?") == 0)
307 	      break;
308 	}
309 	/* Otherwise create a new player. */
310 	if (player == NULL)
311 	  player = add_player();
312 	parse_player_spec(player, remote_player_specs[rid]);
313 	canonicalize_player(player);
314 	player->rid = rid;
315     }
316 }
317 
318 /* Send a message asking to join the game.  Return TRUE if the host
319    actually acknowledged us, FALSE if no response. Note: since send_packet
320    waits 30 seconds for each ack, we don't want to get stuck forever here.
321    Instead, we simply return on failure and let the user try to connect once
322    more. */
323 
324 int
send_join(char * str)325 send_join(char *str)
326 {
327     int successful;
328 
329     sprintf(spbuf, "j%s", str);
330     timeout_warnings = FALSE;
331     successful = send_packet(0, spbuf);
332     timeout_warnings = TRUE;
333     return successful;
334 }
335 
336 /* Tell the remote program what its id will be during this game. */
337 
338 void
send_remote_id(int rid)339 send_remote_id(int rid)
340 {
341     sprintf(spbuf, "r%d", rid);
342     send_packet(rid, spbuf);
343 }
344 
345 /* Tell the remote program to bug off. */
346 
347 void
send_bugoff(int rid)348 send_bugoff(int rid)
349 {
350     sprintf(spbuf, "B");
351     send_packet(rid, spbuf);
352     close_remote_connection(rid);
353 }
354 
355 void
net_send_chat(int rid,char * str)356 net_send_chat(int rid, char *str)
357 {
358     if (my_rid == master_rid) {
359 	send_chat(rid, str);
360     }
361     if (numremotes > 0) {
362 	sprintf(spbuf, "c%d %s", rid, str);
363 	broadcast_packet(spbuf);
364     }
365 }
366 
367 void
send_version(int rid)368 send_version(int rid)
369 {
370     /* In order to guarantee that the kernels can actually stay in
371        sync, we require that the program versions match exactly. */
372     sprintf(spbuf, "V%s", version_string());
373     send_packet(rid, spbuf);
374 }
375 
376 void
net_set_variant_value(int which,int v1,int v2,int v3)377 net_set_variant_value(int which, int v1, int v2, int v3)
378 {
379     if (my_rid == master_rid) {
380 	set_variant_value(which, v1, v2, v3);
381     }
382     if (numremotes > 0) {
383 	sprintf(spbuf, "v %d %d %d %d", which, v1, v2, v3);
384 	broadcast_packet(spbuf);
385     }
386 }
387 
388 void
net_set_player_advantage(int n,int newadv)389 net_set_player_advantage(int n, int newadv)
390 {
391     if (my_rid == master_rid) {
392 	set_player_advantage(n, newadv);
393     }
394     if (numremotes > 0) {
395 	sprintf(spbuf, "a%d advantage %d", n, newadv);
396 	broadcast_packet(spbuf);
397     }
398 }
399 
400 int
net_add_side_and_player(void)401 net_add_side_and_player(void)
402 {
403     int rslt = -1;
404 
405     if (my_rid == master_rid) {
406 	rslt = add_side_and_player();
407     }
408     if (numremotes > 0) {
409 	sprintf(spbuf, "a0 add");
410 	broadcast_packet(spbuf);
411     }
412     return rslt;
413 }
414 
415 int
net_remove_side_and_player(int s)416 net_remove_side_and_player(int s)
417 {
418     int rslt = -1;
419 
420     if (my_rid == master_rid) {
421 	rslt = remove_side_and_player(s);
422     }
423     if (numremotes > 0) {
424 	sprintf(spbuf, "a remove %d", s);
425 	broadcast_packet(spbuf);
426     }
427     return rslt;
428 }
429 
430 void
net_rename_side_for_player(int n,int which)431 net_rename_side_for_player(int n, int which)
432 {
433     if (my_rid == master_rid) {
434 	rename_side_for_player(n, which);
435     }
436     if (numremotes > 0) {
437 	sprintf(spbuf, "a%d rename %d", n, which);
438 	broadcast_packet(spbuf);
439     }
440 }
441 
442 void
net_set_ai_for_player(int n,char * aitype)443 net_set_ai_for_player(int n, char *aitype)
444 {
445     if (my_rid == master_rid) {
446 	set_ai_for_player(n, aitype);
447     }
448     if (numremotes > 0) {
449 	sprintf(spbuf, "a%d ai %s", n, (aitype ? aitype : ""));
450 	broadcast_packet(spbuf);
451     }
452 }
453 
454 int
net_exchange_players(int n,int n2)455 net_exchange_players(int n, int n2)
456 {
457     int rslt = -1;
458 
459     if (my_rid == master_rid) {
460 	rslt = exchange_players(n, n2);
461     }
462     if (numremotes > 0) {
463 	sprintf(spbuf, "a%d exchange %d", n, n2);
464 	broadcast_packet(spbuf);
465     }
466     return rslt;
467 }
468 
469 void
net_update_player(Player * player)470 net_update_player(Player *player)
471 {
472     sprintf(spbuf, "a%d update %s", player->id, player_desig(player));
473     broadcast_packet(spbuf);
474 }
475 
476 /* Given the id of a remote program that has just connected,
477    download our own current state, as a module. */
478 
479 void
download_game_module(int rid)480 download_game_module(int rid)
481 {
482     int rslt;
483     Module *module;
484 
485     if (1 /* sending name only */) {
486 	/* If the module has a filename it is a saved game. */
487     	if (!empty_string(mainmodule->filename)) {
488 		sprintf(spbuf, "f %s", mainmodule->filename);
489 	/* else it is a library module. */
490 	} else {
491 		sprintf(spbuf, "g %s", mainmodule->name);
492 	}
493 	send_packet(rid, spbuf);
494 	/* (should send some sort of state checksum also) */
495 	return;
496     }
497     /* The indepside might not have been filled in yet (such as when
498        connecting before sides/players have been set up), but we need
499        it to be correct before going into write_game_module. */
500     if (numtotsides == 0)
501       create_side();
502     /* Record the rid in a global so the module-writing code knows
503        where to send all the packets. */
504     tmprid = rid;
505     send_packet(rid, "gameModule");
506     module = create_game_module("*download*");
507     copy_module(module, mainmodule);
508     /* This module is not associated with a file. */
509     module->filename = NULL;
510     /* The module will sent in its entirety, so suppress any attempt
511        to load a base module. */
512     module->basemodulename = NULL;
513     module->compress_tables = TRUE;
514     module->compress_layers = TRUE;
515     module->def_all = TRUE; /* for now */
516     downloading = TRUE;
517     rslt = write_game_module(module, NULL);
518     downloading = FALSE;
519     send_packet(rid, "\neludoMemag\n");
520     tmprid = 0;
521 }
522 
523 #ifndef MAC
524 #define DOWNLOADPACKETSIZE 200
525 #else
526 #define DOWNLOADPACKETSIZE 40
527 #endif
528 
529 static char *notherbuf;
530 
531 void
add_to_packet(char * str)532 add_to_packet(char *str)
533 {
534     if (notherbuf == NULL)
535       notherbuf = (char *)xmalloc(DOWNLOADPACKETSIZE + 10);
536     if (strlen(str) + strlen(notherbuf) > DOWNLOADPACKETSIZE) {
537 	/* No room in the packet, send it and start on a new one. */
538 	send_packet(tmprid, notherbuf);
539 	notherbuf[0] = '\0';
540 	/* If the string is long, break it into multiple packets. */
541 	while (strlen(str) > DOWNLOADPACKETSIZE) {
542 	    strncpy(notherbuf, str, DOWNLOADPACKETSIZE);
543 	    notherbuf[DOWNLOADPACKETSIZE] = '\0';
544 	    send_packet(tmprid, notherbuf);
545 	    notherbuf[0] = '\0';
546 	    str += DOWNLOADPACKETSIZE;
547 	}
548     }
549     strcat(notherbuf, str);
550 }
551 
552 void
flush_write(void)553 flush_write(void)
554 {
555     if (!empty_string(notherbuf)) {
556 	send_packet(tmprid, notherbuf);
557 	notherbuf[0] = '\0';
558     }
559 }
560 
561 /* Make sure every remote program has the same game that we do. */
562 
563 void
broadcast_game_module(void)564 broadcast_game_module(void)
565 {
566     int rid;
567 
568     for_all_remotes(rid) {
569 	if (rid != my_rid) {
570 	    download_game_module(rid);
571 	}
572     }
573 }
574 
575 void
broadcast_start_game_load(void)576 broadcast_start_game_load(void)
577 {
578     sprintf(spbuf, "s");
579     broadcast_packet(spbuf);
580 }
581 
582 void
broadcast_start_variant_setup(void)583 broadcast_start_variant_setup(void)
584 {
585     sprintf(spbuf, "vSTART");
586     broadcast_packet(spbuf);
587 }
588 
589 void
broadcast_variants_chosen(void)590 broadcast_variants_chosen(void)
591 {
592     /* Once variants have been chosen, programs will advance to making
593        trial assignments immediately, so make sure randstates are in
594        sync first. */
595     sprintf(spbuf, "R%ld", randstate);
596     broadcast_packet(spbuf);
597     sprintf(spbuf, "vOK");
598     broadcast_packet(spbuf);
599 }
600 
601 void
broadcast_start_player_setup(void)602 broadcast_start_player_setup(void)
603 {
604     sprintf(spbuf, "aSTART");
605     broadcast_packet(spbuf);
606 }
607 
608 void
broadcast_players_assigned(void)609 broadcast_players_assigned(void)
610 {
611     sprintf(spbuf, "aOK");
612     broadcast_packet(spbuf);
613 }
614 
615 /* Broadcast the randstate of the peer. */
616 void
broadcast_randstate(void)617 broadcast_randstate(void)
618 {
619     sprintf(spbuf, "R%ld", randstate);
620     broadcast_packet(spbuf);
621 }
622 
623 void
broadcast_game_checksum(void)624 broadcast_game_checksum(void)
625 {
626     sprintf(spbuf, "Z%d %d", my_rid, game_checksum());
627     broadcast_packet(spbuf);
628 }
629 
630 void
send_game_checksum_error(int rid,int my_csum,int master_csum)631 send_game_checksum_error(int rid, int my_csum, int master_csum)
632 {
633     sprintf(spbuf, "Echecksum %d %d %d", my_rid, my_csum, master_csum);
634     send_packet(rid, spbuf);
635 }
636 
637 time_t last_checksum_time;
638 
639 int
net_run_game(int maxactions)640 net_run_game(int maxactions)
641 {
642 	int oldsernum, oldstate, sendcheck, oldcsum, newcsum, numdone = 0;
643     	time_t now;
644 
645 	/* Network clients should get out of here. */
646 	if (my_rid != master_rid)
647     	    return 0;
648 	/* Dont process or rebroadcast any packets for now. */
649     	process_packets = FALSE;
650 	if (numremotes > 0) {
651 	    oldsernum = g_run_serial_number();
652 	    oldstate = randstate;
653 	    /* Send occasional checksums at random times. */
654 	    sendcheck = FALSE;
655 	    if (0 /* for now */) {
656 		time(&now);
657 		sendcheck = (idifftime(now, last_checksum_time) >= 2);
658 		if (sendcheck)
659 		  last_checksum_time = now;
660 	    }
661 	    if (sendcheck)
662 	      broadcast_game_checksum();
663 	    oldcsum = game_checksum();
664 	}
665 	/* This is where the master's real run_game call happens. */
666 	numdone = run_game(maxactions);
667 	if (numremotes > 0) {
668 	    if (numdone > 0) {
669 
670 		/* This checksum broadcast was the source of all the synch
671 		errors. It made it possible for the client to send data while
672 		the host was waiting for an ack that would change the state of
673 		the game (after rebroadcasting by the host). Since we are at a
674 		point where the host but not the client has executed run_game
675 		nothing must be allowed to happen here that will change the
676 		state of the game. */
677 #if 0
678 		sprintf(spbuf, "Za%d %d", my_rid, oldcsum);
679 		broadcast_packet(spbuf);
680 #endif
681 		sprintf(spbuf, "X%d %d %d", maxactions, oldsernum, oldstate);
682 		broadcast_packet(spbuf);
683 		/* This caused the network game to hang on exit. */
684 		if (!endofgame) {
685 		    broadcast_game_checksum();
686 	    	}
687 	    } else {
688 		newcsum = game_checksum();
689 		if (newcsum != oldcsum)
690 		  notify_all("Checksum changed %d -> %d when numdone == 0",
691 			      oldcsum, newcsum);
692 	    }
693 	}
694 	/* Turn on packet processing. */
695 	process_packets = TRUE;
696 	return numdone;
697 }
698 
699 void
net_request_additional_side(char * playerspec)700 net_request_additional_side(char *playerspec)
701 {
702     if (my_rid == master_rid) {
703 	request_additional_side(playerspec);
704     }
705     if (numremotes > 0) {
706 	sprintf(spbuf, "Padd %s", (playerspec ? playerspec : ""));
707 	broadcast_packet(spbuf);
708     }
709 }
710 
711 void
net_send_message(Side * side,SideMask sidemask,char * str)712 net_send_message(Side *side, SideMask sidemask, char *str)
713 {
714     if (my_rid == master_rid) {
715 	send_message(side, sidemask, str);
716     }
717     if (numremotes > 0) {
718 	sprintf(spbuf, "M%d %d %s", side_number(side), sidemask, str);
719 	broadcast_packet(spbuf);
720     }
721 }
722 
723 static void
broadcast_command_5(char * cmd,int a1,int a2,int a3,int a4,int a5)724 broadcast_command_5(char *cmd, int a1, int a2, int a3, int a4, int a5)
725 {
726     sprintf(spbuf, "C%s %d %d %d %d %d", cmd, a1, a2, a3, a4, a5);
727     broadcast_packet(spbuf);
728 }
729 
730 void
net_resign_game(Side * side,Side * side2)731 net_resign_game(Side *side, Side *side2)
732 {
733     if (my_rid == master_rid) {
734 	resign_game(side, side2);
735     }
736     if (numremotes > 0) {
737       broadcast_side_property(side, "resign",
738 			      (side2 ? side_number(side2) : -1));
739     }
740 }
741 
742 void
net_save_game(char * fname)743 net_save_game(char *fname)
744 {
745     char *name;
746 
747      /* First save the game locally wherever the user wanted it. */
748     write_entire_game_state(fname);
749     /* Peel off the path before broadcasting so that standardized
750     locations are used to save the network game. */
751     name = copy_string(find_name(fname));
752     if (my_rid > 0 && my_rid == master_rid) {
753    	save_game(name);
754    }
755     if (numremotes > 0) {
756 	sprintf(spbuf, "w%s", name);
757 	broadcast_packet(spbuf);
758     }
759 }
760 
761 /* Side property tweaking. */
762 
763 void
net_set_side_name(Side * side,Side * side2,char * newname)764 net_set_side_name(Side *side, Side *side2, char *newname)
765 {
766     if (my_rid == master_rid) {
767 	set_side_name(side, side2, newname);
768     }
769     if (numremotes > 0)  {
770 	broadcast_side_str_property(side2, "name", newname);
771     }
772 }
773 
774 void
net_set_side_longname(Side * side,Side * side2,char * newname)775 net_set_side_longname(Side *side, Side *side2, char *newname)
776 {
777     if (my_rid == master_rid) {
778 	set_side_longname(side, side2, newname);
779     }
780     if (numremotes > 0)  {
781 	broadcast_side_str_property(side2, "longname", newname);
782     }
783 }
784 
785 void
net_set_side_shortname(Side * side,Side * side2,char * newname)786 net_set_side_shortname(Side *side, Side *side2, char *newname)
787 {
788     if (my_rid == master_rid) {
789 	set_side_shortname(side, side2, newname);
790     }
791     if (numremotes > 0)  {
792 	broadcast_side_str_property(side2, "shortname", newname);
793     }
794 }
795 
796 void
net_set_side_noun(Side * side,Side * side2,char * newname)797 net_set_side_noun(Side *side, Side *side2, char *newname)
798 {
799     if (my_rid == master_rid) {
800 	set_side_noun(side, side2, newname);
801     }
802     if (numremotes > 0)  {
803 	broadcast_side_str_property(side2, "noun", newname);
804     }
805 }
806 
807 void
net_set_side_pluralnoun(Side * side,Side * side2,char * newname)808 net_set_side_pluralnoun(Side *side, Side *side2, char *newname)
809 {
810     if (my_rid == master_rid) {
811 	set_side_pluralnoun(side, side2, newname);
812     }
813     if (numremotes > 0)  {
814 	broadcast_side_str_property(side2, "pluralnoun", newname);
815     }
816 }
817 
818 void
net_set_side_adjective(Side * side,Side * side2,char * newname)819 net_set_side_adjective(Side *side, Side *side2, char *newname)
820 {
821     if (my_rid == master_rid) {
822 	set_side_adjective(side, side2, newname);
823     }
824     if (numremotes > 0)  {
825 	broadcast_side_str_property(side2, "adjective", newname);
826     }
827 }
828 
829 void
net_set_side_emblemname(Side * side,Side * side2,char * newname)830 net_set_side_emblemname(Side *side, Side *side2, char *newname)
831 {
832     if (my_rid == master_rid) {
833 	set_side_emblemname(side, side2, newname);
834     }
835     if (numremotes > 0)  {
836 	broadcast_side_str_property(side2, "emblemname", newname);
837     }
838 }
839 
840 void
net_set_side_colorscheme(Side * side,Side * side2,char * newname)841 net_set_side_colorscheme(Side *side, Side *side2, char *newname)
842 {
843     if (my_rid == master_rid) {
844 	set_side_colorscheme(side, side2, newname);
845     }
846     if (numremotes > 0)  {
847 	broadcast_side_str_property(side2, "colorscheme", newname);
848     }
849 }
850 
851 void
net_finish_turn(Side * side)852 net_finish_turn(Side *side)
853 {
854     if (my_rid == master_rid) {
855 	finish_turn(side);
856     }
857     if (numremotes > 0)  {
858 	broadcast_side_property(side, "fin", 0);
859     }
860 }
861 
862 void
net_set_trust(Side * side,Side * side2,int val)863 net_set_trust(Side *side, Side *side2, int val)
864 {
865     if (my_rid == master_rid) {
866 	set_trust(side, side2, val);
867     }
868     if (numremotes > 0)  {
869 	broadcast_side_property_2(side, "trust", side_number(side2), val);
870     }
871 }
872 
873 void
net_set_controlled_by(Side * side,Side * side2,int val)874 net_set_controlled_by(Side *side, Side *side2, int val)
875 {
876     if (my_rid == master_rid) {
877 	set_controlled_by(side, side2, val);
878     }
879     if (numremotes > 0)  {
880 	broadcast_side_property_2(side, "controlledby", side_number(side2), val);
881     }
882 }
883 
884 void
net_set_autofinish(Side * side,int value)885 net_set_autofinish(Side *side, int value)
886 {
887     if (my_rid == master_rid) {
888 	set_autofinish(side, value);
889     }
890     if (numremotes > 0)  {
891 	broadcast_side_property(side, "af", value);
892     }
893 }
894 
895 void
net_set_autoresearch(Side * side,int value)896 net_set_autoresearch(Side *side, int value)
897 {
898     if (my_rid == master_rid) {
899 	set_autoresearch(side, value);
900     }
901     if (numremotes > 0)  {
902 	broadcast_side_property(side, "ar", value);
903     }
904 }
905 
906 void
net_set_willing_to_save(Side * side,int flag)907 net_set_willing_to_save(Side *side, int flag)
908 {
909     if (my_rid == master_rid) {
910 	set_willing_to_save(side, flag);
911     }
912     if (numremotes > 0)  {
913 	broadcast_side_property(side, "save", flag);
914     }
915 }
916 
917 void
net_set_willing_to_draw(Side * side,int flag)918 net_set_willing_to_draw(Side *side, int flag)
919 {
920     if (my_rid == master_rid) {
921 	set_willing_to_draw(side, flag);
922     }
923     if (numremotes > 0)  {
924 	broadcast_side_property(side, "draw", flag);
925     }
926 }
927 
928 void
net_set_side_self_unit(Side * side,Unit * unit)929 net_set_side_self_unit(Side *side, Unit *unit)
930 {
931     if (my_rid == master_rid) {
932 	set_side_self_unit(side, unit);
933     }
934     if (numremotes > 0)  {
935 	broadcast_side_property(side, "self", (unit ? unit->id : 0));
936     }
937 }
938 
939 void
net_set_side_ai(Side * side,char * aitype)940 net_set_side_ai(Side *side, char *aitype)
941 {
942     if (my_rid == master_rid) {
943 	set_side_ai(side, aitype);
944     }
945     if (numremotes > 0)  {
946 	if (aitype == NULL)
947 	  aitype = "";
948 	broadcast_side_str_property(side, "ai", aitype);
949     }
950 }
951 
952 void
net_set_doctrine(Side * side,char * spec)953 net_set_doctrine(Side *side, char *spec)
954 {
955     if (my_rid == master_rid) {
956 	set_doctrine(side, spec);
957     }
958     if (numremotes > 0)  {
959 	if (spec == NULL)
960 	  spec = "";
961 	broadcast_side_str_property(side, "doctrine", spec);
962     }
963 }
964 
965 void
net_set_side_research_topic(Side * side,int a)966 net_set_side_research_topic(Side *side, int a)
967 {
968     if (my_rid == master_rid) {
969 	set_side_research_topic(side, a);
970     }
971     if (numremotes > 0)  {
972 	broadcast_side_property(side, "research_topic", a);
973     }
974 }
975 
976 void
net_set_side_research_goal(Side * side,int a)977 net_set_side_research_goal(Side *side, int a)
978 {
979     if (my_rid == master_rid) {
980 	set_side_research_goal(side, a);
981     }
982     if (numremotes > 0)  {
983 	broadcast_side_property(side, "research_goal", a);
984     }
985 }
986 
987 void
net_set_side_startx(Side * side,int x)988 net_set_side_startx(Side *side, int x)
989 {
990     if (my_rid == master_rid) {
991 	set_side_startx(side, x);
992     }
993     if (numremotes > 0)  {
994 	broadcast_side_property(side, "startx", x);
995     }
996 }
997 
998 void
net_set_side_starty(Side * side,int y)999 net_set_side_starty(Side *side, int y)
1000 {
1001     if (my_rid == master_rid) {
1002 	set_side_starty(side, y);
1003     }
1004     if (numremotes > 0)  {
1005 	broadcast_side_property(side, "starty", y);
1006     }
1007 }
1008 
1009 #ifdef DESIGNERS
1010 
1011 void
net_become_designer(Side * side)1012 net_become_designer(Side *side)
1013 {
1014     if (my_rid == master_rid) {
1015 	become_designer(side);
1016     }
1017     if (numremotes > 0)  {
1018 	broadcast_side_property(side, "designer", TRUE);
1019     }
1020 }
1021 
1022 void
net_become_nondesigner(Side * side)1023 net_become_nondesigner(Side *side)
1024 {
1025     if (my_rid == master_rid) {
1026 	become_nondesigner(side);
1027     }
1028     if (numremotes > 0)  {
1029 	broadcast_side_property(side, "designer", FALSE);
1030     }
1031 }
1032 
1033 void
net_paint_view(Side * side,int x,int y,int r,int tview,int uview)1034 net_paint_view(Side *side, int x, int y, int r, int tview, int uview)
1035 {
1036     if (my_rid == master_rid) {
1037 	paint_view(side, x, y, r, tview, uview);
1038     }
1039     if (numremotes > 0)  {
1040 	sprintf(spbuf, "D%d view %d %d %d %d %d %d",
1041 		side_number(side), side_number(side), x, y, r, tview, uview);
1042 	broadcast_packet(spbuf);
1043     }
1044 }
1045 
1046 #endif /* DESIGNERS */
1047 
1048 static void
broadcast_side_property(Side * side,char * prop,int val)1049 broadcast_side_property(Side *side, char *prop, int val)
1050 {
1051     sprintf(spbuf, "S%d %s %d", side_number(side), prop, val);
1052     broadcast_packet(spbuf);
1053 }
1054 
1055 static void
broadcast_side_property_2(Side * side,char * prop,int val,int val2)1056 broadcast_side_property_2(Side *side, char *prop, int val, int val2)
1057 {
1058     sprintf(spbuf, "S%d %s %d %d", side_number(side), prop, val, val2);
1059     broadcast_packet(spbuf);
1060 }
1061 
1062 static void
broadcast_side_str_property(Side * side,char * prop,char * val)1063 broadcast_side_str_property(Side *side, char *prop, char *val)
1064 {
1065     sprintf(spbuf, "S%d %s %s", side_number(side), prop, val);
1066     broadcast_packet(spbuf);
1067 }
1068 
1069 /* Unit property tweaking. */
1070 
1071 void
net_set_unit_name(Side * side,Unit * unit,char * newname)1072 net_set_unit_name(Side *side, Unit *unit, char *newname)
1073 {
1074     if (my_rid == master_rid) {
1075 	set_unit_name(side, unit, newname);
1076     }
1077     if (numremotes > 0) {
1078 	broadcast_unit_str_property(side, unit, "name", newname);
1079     }
1080 }
1081 
1082 void
net_set_unit_plan_type(Side * side,Unit * unit,int type)1083 net_set_unit_plan_type(Side *side, Unit *unit, int type)
1084 {
1085     if (my_rid == master_rid) {
1086 	set_unit_plan_type(side, unit, type);
1087     }
1088     if (numremotes > 0) {
1089 	broadcast_unit_property(side, unit, "plan", type);
1090     }
1091 }
1092 
1093 void
net_set_unit_asleep(Side * side,Unit * unit,int flag,int recurse)1094 net_set_unit_asleep(Side *side, Unit *unit, int flag, int recurse)
1095 {
1096     if (my_rid == master_rid) {
1097 	set_unit_asleep(side, unit, flag, recurse);
1098     }
1099     if (numremotes > 0) {
1100 	broadcast_unit_property_2(side, unit, "sleep", flag, recurse);
1101     }
1102 }
1103 
1104 void
net_set_unit_reserve(Side * side,Unit * unit,int flag,int recurse)1105 net_set_unit_reserve(Side *side, Unit *unit, int flag, int recurse)
1106 {
1107     if (my_rid == master_rid) {
1108 	set_unit_reserve(side, unit, flag, recurse);
1109     }
1110     if (numremotes > 0) {
1111 	broadcast_unit_property_2(side, unit, "resv", flag, recurse);
1112     }
1113 }
1114 
1115 void
net_set_unit_main_goal(Side * side,Unit * unit,Goal * goal)1116 net_set_unit_main_goal(Side *side, Unit *unit, Goal *goal)
1117 {
1118     if (my_rid == master_rid) {
1119 	set_unit_main_goal(side, unit, goal);
1120     }
1121     if (numremotes > 0) {
1122 	broadcast_unit_property_5(side, unit, "maingoal",
1123 				  (goal ? goal->type : 0),
1124 				  (goal ? goal->args[0] : 0),
1125 				  (goal ? goal->args[1] : 0),
1126 				  (goal ? goal->args[2] : 0),
1127 				  (goal ? goal->args[3] : 0));
1128     }
1129 }
1130 
1131 void
net_set_unit_curadvance(Side * side,Unit * unit,int a)1132 net_set_unit_curadvance(Side *side, Unit *unit, int a)
1133 {
1134     if (my_rid == master_rid) {
1135 	set_unit_curadvance(side, unit, a);
1136     }
1137     if (numremotes > 0) {
1138 	broadcast_unit_property(side, unit, "curadvance", a);
1139     }
1140 }
1141 
1142 void
net_set_unit_researchdone(Side * side,Unit * unit,int flag)1143 net_set_unit_researchdone(Side *side, Unit *unit, int flag)
1144 {
1145     if (my_rid == master_rid) {
1146 	set_unit_researchdone(side, unit, flag);
1147     }
1148     if (numremotes > 0) {
1149 	broadcast_unit_property(side, unit, "researchdone", flag);
1150     }
1151 }
1152 
1153 void
net_set_unit_waiting_for_transport(Side * side,Unit * unit,int flag)1154 net_set_unit_waiting_for_transport(Side *side, Unit *unit, int flag)
1155 {
1156     if (my_rid == master_rid) {
1157 	set_unit_waiting_for_transport(side, unit, flag);
1158     }
1159     if (numremotes > 0) {
1160 	broadcast_unit_property(side, unit, "waittrans", flag);
1161     }
1162 }
1163 
1164 void
net_wake_unit(Side * side,Unit * unit,int wakeocc)1165 net_wake_unit(Side *side, Unit *unit, int wakeocc)
1166 {
1167     if (my_rid == master_rid) {
1168 	wake_unit(side, unit, wakeocc);
1169     }
1170     if (numremotes > 0) {
1171 	broadcast_command_5("wake-unit", side_number(side), unit->id, wakeocc, 0, 0);
1172     }
1173 }
1174 
1175 void
net_wake_area(Side * side,int x,int y,int n,int occs)1176 net_wake_area(Side *side, int x, int y, int n, int occs)
1177 {
1178     if (my_rid == master_rid) {
1179 	wake_area(side, x, y, n, occs);
1180     }
1181     if (numremotes > 0) {
1182 	broadcast_command_5("wake-area", side_number(side), x, y, n, occs);
1183     }
1184 }
1185 
1186 void
net_set_unit_ai_control(Side * side,Unit * unit,int flag,int recurse)1187 net_set_unit_ai_control(Side *side, Unit *unit, int flag, int recurse)
1188 {
1189     if (my_rid == master_rid) {
1190 	set_unit_ai_control(side, unit, flag, recurse);
1191     }
1192     if (numremotes > 0) {
1193 	broadcast_unit_property_2(side, unit, "ai", flag, recurse);
1194     }
1195 }
1196 
1197 int
net_clear_task_agenda(Side * side,Unit * unit)1198 net_clear_task_agenda(Side *side, Unit *unit)
1199 {
1200     if (my_rid == master_rid) {
1201 	clear_task_agenda(unit);
1202     }
1203     if (numremotes > 0) {
1204 	broadcast_unit_property(side, unit, "clragenda", 0);
1205     }
1206     return 0;
1207 }
1208 
1209 int
net_clear_task_outcome(Side * side,Unit * unit)1210 net_clear_task_outcome(Side *side, Unit *unit)
1211 {
1212     if (my_rid == master_rid) {
1213 	clear_task_outcome(unit);
1214     }
1215     if (numremotes > 0) {
1216 	broadcast_unit_property(side, unit, "clroutcome", 0);
1217     }
1218     return 0;
1219 }
1220 
1221 void
net_force_replan(Unit * unit)1222 net_force_replan(Unit *unit)
1223 {
1224     if (my_rid == master_rid) {
1225 	force_replan(unit);
1226     }
1227     if (numremotes > 0) {
1228 	broadcast_unit_property(unit->side, unit, "forcereplan", 0);
1229     }
1230 }
1231 
1232 int
net_disband_unit(Side * side,Unit * unit)1233 net_disband_unit(Side *side, Unit *unit)
1234 {
1235     if (my_rid == master_rid) {
1236 	return disband_unit(side, unit);
1237     }
1238     if (numremotes > 0) {
1239 	broadcast_unit_property(side, unit, "disband", 0);
1240     }
1241     return 0;
1242 }
1243 
1244 void
net_set_formation(Unit * unit,Unit * leader,int x,int y,int dist,int flex)1245 net_set_formation(Unit *unit, Unit *leader, int x, int y, int dist, int flex)
1246 {
1247     if (my_rid == master_rid) {
1248 	set_formation(unit, leader, x, y, dist, flex);
1249     }
1250     if (numremotes > 0) {
1251 	broadcast_unit_property_5(unit->side, unit, "formation",
1252 				  (leader ? leader->id : 0), x, y, dist, flex);
1253     }
1254 }
1255 
1256 void
net_delay_unit(Unit * unit,int flag)1257 net_delay_unit(Unit *unit, int flag)
1258 {
1259     if (my_rid == master_rid) {
1260 	delay_unit(unit, flag);
1261     }
1262     if (numremotes > 0) {
1263 	broadcast_unit_property(unit->side, unit, "delay", flag);
1264     }
1265 }
1266 
1267 #ifdef DESIGNERS
1268 
1269 /* A designer can call this to create an arbitrary unit during the game. */
1270 
1271 Unit *
net_designer_create_unit(Side * side,int u,int s,int x,int y)1272 net_designer_create_unit(Side *side, int u, int s, int x, int y)
1273 {
1274     Unit *rslt = NULL;
1275 
1276     if (my_rid == master_rid) {
1277 	rslt = designer_create_unit(side, u, s, x, y);
1278     }
1279     if (numremotes > 0) {
1280 	broadcast_command_5("designer-create-unit", side_number(side),
1281 			    u, s, x, y);
1282     }
1283     return rslt;
1284 }
1285 
1286 /* Move a unit to a given location instantly, with all sides observing.
1287    This is for use by designers only! */
1288 
1289 int
net_designer_teleport(Unit * unit,int x,int y,Unit * other)1290 net_designer_teleport(Unit *unit, int x, int y, Unit *other)
1291 {
1292     int rslt = 0;
1293 
1294     if (my_rid == master_rid) {
1295 	rslt = designer_teleport(unit, x, y, other);
1296     }
1297     if (numremotes > 0) {
1298 	broadcast_command_5("designer-teleport", unit->id, x, y, (other ? other->id : 0), 0);
1299     }
1300     return rslt;
1301 }
1302 
1303 int
net_designer_change_side(Unit * unit,Side * side)1304 net_designer_change_side(Unit *unit, Side *side)
1305 {
1306     int rslt = 0;
1307 
1308     if (my_rid == master_rid) {
1309 	rslt = designer_change_side(unit, side);
1310     }
1311     if (numremotes > 0) {
1312 	broadcast_command_5("designer-change-side", unit->id,
1313 			    side_number(side), 0, 0, 0);
1314     }
1315     return rslt;
1316 }
1317 
1318 int
net_designer_disband(Unit * unit)1319 net_designer_disband(Unit *unit)
1320 {
1321     int rslt = 0;
1322 
1323     if (my_rid == master_rid) {
1324 	rslt = designer_disband(unit);
1325     }
1326     if (numremotes > 0) {
1327 	broadcast_command_5("designer-disband", unit->id, 0, 0, 0, 0);
1328     }
1329     return rslt;
1330 }
1331 
1332 #endif /* DESIGNERS */
1333 
1334 Feature *
net_create_feature(char * feattype,char * name)1335 net_create_feature(char *feattype, char *name)
1336 {
1337     Feature *rslt = NULL;
1338 
1339     if (my_rid == master_rid) {
1340 	rslt = create_feature(feattype, name);
1341     }
1342     if (numremotes > 0) {
1343 	run_error("net");
1344     }
1345     return rslt;
1346 }
1347 
1348 void
net_set_feature_type_name(Feature * feature,char * feattype)1349 net_set_feature_type_name(Feature *feature, char *feattype)
1350 {
1351     if (my_rid == master_rid) {
1352 	set_feature_type_name(feature, feattype);
1353     }
1354     if (numremotes > 0) {
1355 	run_error("net");
1356     }
1357 }
1358 
1359 void
net_set_feature_name(Feature * feature,char * name)1360 net_set_feature_name(Feature *feature, char *name)
1361 {
1362     if (my_rid == master_rid) {
1363 	set_feature_name(feature, name);
1364     }
1365     if (numremotes > 0) {
1366 	run_error("net");
1367     }
1368 }
1369 
1370 void
net_destroy_feature(Feature * feature)1371 net_destroy_feature(Feature *feature)
1372 {
1373     if (my_rid == master_rid) {
1374 	destroy_feature(feature);
1375     }
1376     if (numremotes > 0) {
1377 	run_error("net");
1378     }
1379 }
1380 
1381 void
net_renumber_features(void)1382 net_renumber_features(void)
1383 {
1384     if (my_rid == master_rid) {
1385 	renumber_features();
1386     }
1387     if (numremotes > 0) {
1388 	run_error("net");
1389     }
1390 }
1391 
1392 void
net_toggle_user_at(int u,int x,int y)1393 net_toggle_user_at(int u, int x, int y)
1394 {
1395     if (my_rid == master_rid) {
1396     	toggle_user_at(find_unit(u), x, y);
1397     }
1398     if (numremotes > 0) {
1399 	sprintf(spbuf, "Wuser %d %d %d %d", 0, x, y, u);
1400 	broadcast_packet(spbuf);
1401     }
1402 }
1403 
1404 static void
broadcast_unit_property(Side * side,Unit * unit,char * prop,int val)1405 broadcast_unit_property(Side *side, Unit *unit, char *prop, int val)
1406 {
1407     char buf[BUFSIZE];
1408 
1409     sprintf(buf, "U%d %d %s %d", side_number(side), unit->id, prop, val);
1410     broadcast_packet(buf);
1411 }
1412 
1413 static void
broadcast_unit_property_2(Side * side,Unit * unit,char * prop,int val,int val2)1414 broadcast_unit_property_2(Side *side, Unit *unit, char *prop, int val, int val2)
1415 {
1416     char buf[BUFSIZE];
1417 
1418     sprintf(buf, "U%d %d %s %d %d", side_number(side), unit->id, prop, val, val2);
1419     broadcast_packet(buf);
1420 }
1421 
1422 static void
broadcast_unit_property_5(Side * side,Unit * unit,char * prop,int val,int val2,int val3,int val4,int val5)1423 broadcast_unit_property_5(Side *side, Unit *unit, char *prop,
1424 			  int val, int val2, int val3, int val4, int val5)
1425 {
1426     char buf[BUFSIZE];
1427 
1428     sprintf(buf, "U%d %d %s %d %d %d %d %d", side_number(side), unit->id, prop,
1429 	    val, val2, val3, val4, val5);
1430     broadcast_packet(buf);
1431 }
1432 
1433 static void
broadcast_unit_str_property(Side * side,Unit * unit,char * prop,char * val)1434 broadcast_unit_str_property(Side *side, Unit *unit, char *prop, char *val)
1435 {
1436     char buf[BUFSIZE];
1437 
1438     sprintf(buf, "U%d %d %s %s", side_number(side), unit->id, prop, val);
1439     broadcast_packet(buf);
1440 }
1441 
1442 /* Action networking. */
1443 
1444 static void
broadcast_action(Unit * unit,int actee,ActionType atype,int arg0,int arg1,int arg2,int arg3)1445 broadcast_action(Unit *unit, int actee, ActionType atype,
1446 		 int arg0, int arg1, int arg2, int arg3)
1447 {
1448     int n;
1449     char buf[BUFSIZE];
1450 
1451     sprintf(buf, "A%d", unit->id);
1452     if (unit->id != actee) {
1453 	tprintf(buf, "/%d", actee);
1454     }
1455     tprintf(buf, " %d", atype);
1456     n = strlen(actiondefns[atype].argtypes);
1457     if (n > 0) {
1458 	tprintf(buf, " %d", arg0);
1459     }
1460     if (n > 1) {
1461 	tprintf(buf, " %d", arg1);
1462     }
1463     if (n > 2) {
1464 	tprintf(buf, " %d", arg2);
1465     }
1466     if (n > 3) {
1467 	tprintf(buf, " %d", arg3);
1468     }
1469     if (n > 4) {
1470 	notify_all("internal error: %d args to action", n);
1471     }
1472     broadcast_packet(buf);
1473 }
1474 
1475 /* From actions.c. */
1476 
1477 int
net_prep_none_action(Unit * unit,Unit * unit2)1478 net_prep_none_action(Unit *unit, Unit *unit2)
1479 {
1480     int rslt;
1481 
1482     /* Check the prep action result first and return if it would fail. */
1483     if (unit == NULL || unit->act == NULL || unit2 == NULL) {
1484 	return FALSE;
1485     }
1486     /* Then check the busy flag. */
1487     if (unit->busy) {
1488     	return TRUE;
1489     } else {
1490     	unit->busy = TRUE;
1491     }
1492     if (my_rid == master_rid) {
1493 	rslt = prep_none_action(unit, unit2);
1494     }
1495     if (numremotes > 0) {
1496 	broadcast_action(unit, unit2->id, ACTION_NONE, 0, 0, 0, 0);
1497     }
1498     return rslt;
1499 }
1500 
1501 int
net_prep_produce_action(Unit * unit,Unit * unit2,int m,int n)1502 net_prep_produce_action(Unit *unit, Unit *unit2, int m, int n)
1503 {
1504     int rslt;
1505 
1506     /* Check the prep action result first and return if it would fail. */
1507     if (unit == NULL || unit->act == NULL || unit2 == NULL) {
1508 	return FALSE;
1509     }
1510     /* Then check the busy flag. */
1511     if (unit->busy) {
1512     	return TRUE;
1513     } else {
1514     	unit->busy = TRUE;
1515     }
1516     if (my_rid == master_rid) {
1517 	rslt = prep_produce_action(unit, unit2, m, n);
1518     }
1519     if (numremotes > 0) {
1520 	broadcast_action(unit, unit2->id, ACTION_PRODUCE, m, n, 0, 0);
1521     }
1522     return rslt;
1523 }
1524 
1525 int
net_prep_extract_action(Unit * unit,Unit * unit2,int x,int y,int m,int n)1526 net_prep_extract_action(Unit *unit, Unit *unit2, int x, int y, int m, int n)
1527 {
1528     int rslt;
1529 
1530     /* Check the prep action result first and return if it would fail. */
1531     if (unit == NULL || unit->act == NULL || unit2 == NULL) {
1532 	return FALSE;
1533     }
1534     /* Then check the busy flag. */
1535     if (unit->busy) {
1536     	return TRUE;
1537     } else {
1538     	unit->busy = TRUE;
1539     }
1540     if (my_rid == master_rid) {
1541 	rslt = prep_extract_action(unit, unit2, x, y, m, n);
1542     }
1543     if (numremotes > 0) {
1544 	broadcast_action(unit, unit2->id, ACTION_EXTRACT, x, y, m, n);
1545     }
1546     return rslt;
1547 }
1548 
1549 int
net_prep_develop_action(Unit * unit,Unit * unit2,int u3)1550 net_prep_develop_action(Unit *unit, Unit *unit2, int u3)
1551 {
1552     int rslt;
1553 
1554     /* Check the prep action result first and return if it would fail. */
1555     if (unit == NULL || unit->act == NULL || unit2 == NULL) {
1556 	return FALSE;
1557     }
1558     /* Then check the busy flag. */
1559     if (unit->busy) {
1560     	return TRUE;
1561     } else {
1562     	unit->busy = TRUE;
1563     }
1564     if (my_rid == master_rid) {
1565 	rslt = prep_develop_action(unit, unit2, u3);
1566     }
1567     if (numremotes > 0) {
1568 	broadcast_action(unit, unit2->id, ACTION_DEVELOP, u3, 0, 0, 0);
1569     }
1570     return rslt;
1571 }
1572 
1573 int
net_prep_toolup_action(Unit * unit,Unit * unit2,int u3)1574 net_prep_toolup_action(Unit *unit, Unit *unit2, int u3)
1575 {
1576     int rslt;
1577 
1578     /* Check the prep action result first and return if it would fail. */
1579     if (unit == NULL || unit->act == NULL || unit2 == NULL) {
1580 	return FALSE;
1581     }
1582     /* Then check the busy flag. */
1583     if (unit->busy) {
1584     	return TRUE;
1585     } else {
1586     	unit->busy = TRUE;
1587     }
1588     if (my_rid == master_rid) {
1589 	rslt = prep_toolup_action(unit, unit2, u3);
1590     }
1591     if (numremotes > 0) {
1592 	broadcast_action(unit, unit2->id, ACTION_TOOL_UP, u3, 0, 0, 0);
1593     }
1594     return rslt;
1595 }
1596 
1597 int
net_prep_create_in_action(Unit * unit,Unit * unit2,int u3,Unit * dest)1598 net_prep_create_in_action(Unit *unit, Unit *unit2, int u3, Unit *dest)
1599 {
1600     int rslt;
1601 
1602     /* Check the prep action result first and return if it would fail. */
1603     if (unit == NULL || unit->act == NULL || unit2 == NULL) {
1604 	return FALSE;
1605     }
1606     /* Then check the busy flag. */
1607     if (unit->busy) {
1608     	return TRUE;
1609     } else {
1610     	unit->busy = TRUE;
1611     }
1612     if (my_rid == master_rid) {
1613 	rslt = prep_create_in_action(unit, unit2, u3, dest);
1614     }
1615     if (numremotes > 0) {
1616 	broadcast_action(unit, unit2->id, ACTION_CREATE_IN,
1617 			 u3, dest->id, 0, 0);
1618     }
1619     return rslt;
1620 }
1621 
1622 int
net_prep_create_at_action(Unit * unit,Unit * unit2,int u3,int x,int y,int z)1623 net_prep_create_at_action(Unit *unit, Unit *unit2, int u3, int x, int y, int z)
1624 {
1625     int rslt;
1626 
1627     /* Check the prep action result first and return if it would fail. */
1628     if (unit == NULL || unit->act == NULL || unit2 == NULL) {
1629 	return FALSE;
1630     }
1631     /* Then check the busy flag. */
1632     if (unit->busy) {
1633     	return TRUE;
1634     } else {
1635     	unit->busy = TRUE;
1636     }
1637     if (my_rid == master_rid) {
1638 	rslt = prep_create_at_action(unit, unit2, u3, x, y, z);
1639     }
1640     if (numremotes > 0) {
1641 	broadcast_action(unit, unit2->id, ACTION_CREATE_AT, u3, x, y, z);
1642     }
1643     return rslt;
1644 }
1645 
1646 int
net_prep_build_action(Unit * unit,Unit * unit2,Unit * newunit)1647 net_prep_build_action(Unit *unit, Unit *unit2, Unit *newunit)
1648 {
1649     int rslt;
1650 
1651     /* Check the prep action result first and return if it would fail. */
1652     if (unit == NULL || unit->act == NULL || unit2 == NULL) {
1653 	return FALSE;
1654     }
1655     /* Then check the busy flag. */
1656     if (unit->busy) {
1657     	return TRUE;
1658     } else {
1659     	unit->busy = TRUE;
1660     }
1661     if (my_rid == master_rid) {
1662 	rslt = prep_build_action(unit, unit2, newunit);
1663     }
1664     if (numremotes > 0) {
1665 	broadcast_action(unit, unit2->id, ACTION_BUILD, newunit->id, 0, 0, 0);
1666     }
1667     return rslt;
1668 }
1669 
1670 int
net_prep_repair_action(Unit * unit,Unit * unit2,Unit * unit3)1671 net_prep_repair_action(Unit *unit, Unit *unit2, Unit *unit3)
1672 {
1673     int rslt;
1674 
1675     /* Check the prep action result first and return if it would fail. */
1676     if (unit == NULL || unit->act == NULL || unit2 == NULL) {
1677 	return FALSE;
1678     }
1679     /* Then check the busy flag. */
1680     if (unit->busy) {
1681     	return TRUE;
1682     } else {
1683     	unit->busy = TRUE;
1684     }
1685     if (my_rid == master_rid) {
1686 	rslt = prep_repair_action(unit, unit2, unit3);
1687     }
1688     if (numremotes > 0) {
1689 	broadcast_action(unit, unit2->id, ACTION_REPAIR, unit3->id, 0, 0, 0);
1690     }
1691     return rslt;
1692 }
1693 
1694 int
net_prep_disband_action(Unit * unit,Unit * unit2)1695 net_prep_disband_action(Unit *unit, Unit *unit2)
1696 {
1697     int rslt;
1698 
1699     /* Check the prep action result first and return if it would fail. */
1700     if (unit == NULL || unit->act == NULL || unit2 == NULL) {
1701 	return FALSE;
1702     }
1703     /* Then check the busy flag. */
1704     if (unit->busy) {
1705     	return TRUE;
1706     } else {
1707     	unit->busy = TRUE;
1708     }
1709     if (my_rid == master_rid) {
1710 	rslt = prep_disband_action(unit, unit2);
1711     }
1712     if (numremotes > 0) {
1713 	broadcast_action(unit, unit2->id, ACTION_DISBAND, 0, 0, 0, 0);
1714     }
1715     return rslt;
1716 }
1717 
1718 int
net_prep_transfer_part_action(Unit * unit,Unit * unit2,int parts,Unit * unit3)1719 net_prep_transfer_part_action(Unit *unit, Unit *unit2, int parts, Unit *unit3)
1720 {
1721     int rslt;
1722 
1723     /* Check the prep action result first and return if it would fail. */
1724     if (unit == NULL || unit->act == NULL || unit2 == NULL) {
1725 	return FALSE;
1726     }
1727     /* Then check the busy flag. */
1728     if (unit->busy) {
1729     	return TRUE;
1730     } else {
1731     	unit->busy = TRUE;
1732     }
1733     if (my_rid == master_rid) {
1734 	rslt = prep_transfer_part_action(unit, unit2, parts, unit3);
1735     }
1736     if (numremotes > 0) {
1737 	broadcast_action(unit, unit2->id, ACTION_TRANSFER_PART,
1738 			 parts, (unit3 ? unit3->id : 0), 0, 0);
1739     }
1740     return rslt;
1741 }
1742 
1743 int
net_prep_change_type_action(Unit * unit,Unit * unit2,int u3)1744 net_prep_change_type_action(Unit *unit, Unit *unit2, int u3)
1745 {
1746     int rslt;
1747 
1748     /* Check the prep action result first and return if it would fail. */
1749     if (unit == NULL || unit->act == NULL || unit2 == NULL) {
1750 	return FALSE;
1751     }
1752     /* Then check the busy flag. */
1753     if (unit->busy) {
1754     	return TRUE;
1755     } else {
1756     	unit->busy = TRUE;
1757     }
1758     if (my_rid == master_rid) {
1759 	rslt = prep_change_type_action(unit, unit2, u3);
1760     }
1761     if (numremotes > 0) {
1762 	broadcast_action(unit, unit2->id, ACTION_CHANGE_TYPE, u3, 0, 0, 0);
1763     }
1764     return rslt;
1765 }
1766 
1767 int
net_prep_change_side_action(Unit * unit,Unit * unit2,Side * side)1768 net_prep_change_side_action(Unit *unit, Unit *unit2, Side *side)
1769 {
1770     int rslt;
1771 
1772     /* Check the prep action result first and return if it would fail. */
1773     if (unit == NULL || unit->act == NULL || unit2 == NULL) {
1774 	return FALSE;
1775     }
1776     /* Then check the busy flag. */
1777     if (unit->busy) {
1778     	return TRUE;
1779     } else {
1780     	unit->busy = TRUE;
1781     }
1782     if (my_rid == master_rid) {
1783 	rslt = prep_change_side_action(unit, unit2, side);
1784     }
1785     if (numremotes > 0) {
1786 	broadcast_action(unit, unit2->id, ACTION_CHANGE_SIDE,
1787 			 side_number(side), 0, 0, 0);
1788     }
1789     return rslt;
1790 }
1791 
1792 int
net_prep_alter_cell_action(Unit * unit,Unit * unit2,int x,int y,int t)1793 net_prep_alter_cell_action(Unit *unit, Unit *unit2, int x, int y, int t)
1794 {
1795     int rslt;
1796 
1797     /* Check the prep action result first and return if it would fail. */
1798     if (unit == NULL || unit->act == NULL || unit2 == NULL) {
1799 	return FALSE;
1800     }
1801     /* Then check the busy flag. */
1802     if (unit->busy) {
1803     	return TRUE;
1804     } else {
1805     	unit->busy = TRUE;
1806     }
1807     if (my_rid == master_rid) {
1808 	rslt = prep_alter_cell_action(unit, unit2, x, y, t);
1809     }
1810     if (numremotes > 0) {
1811 	broadcast_action(unit, unit2->id, ACTION_ALTER_TERRAIN, x, y, t, 0);
1812     }
1813     return rslt;
1814 }
1815 
1816 int
net_prep_add_terrain_action(Unit * unit,Unit * unit2,int x,int y,int dir,int t)1817 net_prep_add_terrain_action(Unit *unit, Unit *unit2,
1818 			    int x, int y, int dir, int t)
1819 {
1820     int rslt;
1821 
1822     /* Check the prep action result first and return if it would fail. */
1823     if (unit == NULL || unit->act == NULL || unit2 == NULL) {
1824 	return FALSE;
1825     }
1826     /* Then check the busy flag. */
1827     if (unit->busy) {
1828     	return TRUE;
1829     } else {
1830     	unit->busy = TRUE;
1831     }
1832     if (my_rid == master_rid) {
1833 	rslt = prep_add_terrain_action(unit, unit2, x, y, dir, t);
1834     }
1835     if (numremotes > 0) {
1836 	broadcast_action(unit, unit2->id, ACTION_ADD_TERRAIN, x, y, dir, t);
1837     }
1838     return rslt;
1839 }
1840 
1841 int
net_prep_remove_terrain_action(Unit * unit,Unit * unit2,int x,int y,int dir,int t)1842 net_prep_remove_terrain_action(Unit *unit, Unit *unit2,
1843 			       int x, int y, int dir, int t)
1844 {
1845     int rslt;
1846 
1847     /* Check the prep action result first and return if it would fail. */
1848     if (unit == NULL || unit->act == NULL || unit2 == NULL) {
1849 	return FALSE;
1850     }
1851     /* Then check the busy flag. */
1852     if (unit->busy) {
1853     	return TRUE;
1854     } else {
1855     	unit->busy = TRUE;
1856     }
1857     if (my_rid == master_rid) {
1858 	rslt = prep_remove_terrain_action(unit, unit2, x, y, dir, t);
1859     }
1860     if (numremotes > 0) {
1861 	broadcast_action(unit, unit2->id, ACTION_REMOVE_TERRAIN,
1862 			 x, y, dir, t);
1863     }
1864     return rslt;
1865 }
1866 
1867 /* From combat.c. */
1868 
1869 int
net_prep_attack_action(Unit * unit,Unit * unit2,Unit * defender,int n)1870 net_prep_attack_action(Unit *unit, Unit *unit2, Unit *defender, int n)
1871 {
1872     int rslt;
1873 
1874     /* Check the prep action result first and return if it would fail. */
1875     if (unit == NULL || unit->act == NULL || unit2 == NULL) {
1876 	return FALSE;
1877     }
1878     /* Then check the busy flag. */
1879     if (unit->busy) {
1880     	return TRUE;
1881     } else {
1882     	unit->busy = TRUE;
1883     }
1884     if (my_rid == master_rid) {
1885 	rslt = prep_attack_action(unit, unit2, defender, n);
1886     }
1887     if (numremotes > 0) {
1888 	broadcast_action(unit, unit2->id, ACTION_ATTACK,
1889 			 defender->id, n, 0, 0);
1890     }
1891     return rslt;
1892 }
1893 
1894 int
net_prep_overrun_action(Unit * unit,Unit * unit2,int x,int y,int z,int n)1895 net_prep_overrun_action(Unit *unit, Unit *unit2, int x, int y, int z, int n)
1896 {
1897     int rslt;
1898 
1899     /* Check the prep action result first and return if it would fail. */
1900     if (unit == NULL || unit->act == NULL || unit2 == NULL) {
1901 	return FALSE;
1902     }
1903     /* Then check the busy flag. */
1904     if (unit->busy) {
1905     	return TRUE;
1906     } else {
1907     	unit->busy = TRUE;
1908     }
1909     if (my_rid == master_rid) {
1910 	rslt = prep_overrun_action(unit, unit2, x, y, z, n);
1911     }
1912     if (numremotes > 0) {
1913 	broadcast_action(unit, unit2->id, ACTION_OVERRUN, x, y, z, n);
1914     }
1915     return rslt;
1916 }
1917 
1918 int
net_prep_fire_at_action(Unit * unit,Unit * unit2,Unit * unit3,int m)1919 net_prep_fire_at_action(Unit *unit, Unit *unit2, Unit *unit3, int m)
1920 {
1921     int rslt;
1922 
1923     /* Check the prep action result first and return if it would fail. */
1924     if (unit == NULL || unit->act == NULL || unit2 == NULL) {
1925 	return FALSE;
1926     }
1927     /* Then check the busy flag. */
1928     if (unit->busy) {
1929     	return TRUE;
1930     } else {
1931     	unit->busy = TRUE;
1932     }
1933     if (my_rid == master_rid) {
1934 	rslt = prep_fire_at_action(unit, unit2, unit3, m);
1935     }
1936     if (numremotes > 0) {
1937 	broadcast_action(unit, unit2->id, ACTION_FIRE_AT, unit3->id, m, 0, 0);
1938     }
1939     return rslt;
1940 }
1941 
1942 int
net_prep_fire_into_action(Unit * unit,Unit * unit2,int x,int y,int z,int m)1943 net_prep_fire_into_action(Unit *unit, Unit *unit2, int x, int y, int z, int m)
1944 {
1945     int rslt;
1946 
1947     /* Check the prep action result first and return if it would fail. */
1948     if (unit == NULL || unit->act == NULL || unit2 == NULL) {
1949 	return FALSE;
1950     }
1951     /* Then check the busy flag. */
1952     if (unit->busy) {
1953     	return TRUE;
1954     } else {
1955     	unit->busy = TRUE;
1956     }
1957     if (my_rid == master_rid) {
1958 	rslt = prep_fire_into_action(unit, unit2, x, y, z, m);
1959     }
1960     if (numremotes > 0) {
1961 	broadcast_action(unit, unit2->id, ACTION_FIRE_INTO, x, y, z, m);
1962     }
1963     return rslt;
1964 }
1965 
1966 int
net_prep_capture_action(Unit * unit,Unit * unit2,Unit * unit3)1967 net_prep_capture_action(Unit *unit, Unit *unit2, Unit *unit3)
1968 {
1969     int rslt;
1970 
1971     /* Check the prep action result first and return if it would fail. */
1972     if (unit == NULL || unit->act == NULL || unit2 == NULL) {
1973 	return FALSE;
1974     }
1975     /* Then check the busy flag. */
1976     if (unit->busy) {
1977     	return TRUE;
1978     } else {
1979     	unit->busy = TRUE;
1980     }
1981     if (my_rid == master_rid) {
1982 	rslt = prep_capture_action(unit, unit2, unit3);
1983     }
1984     if (numremotes > 0) {
1985 	broadcast_action(unit, unit2->id, ACTION_CAPTURE,
1986 			 unit3->id, 0, 0, 0);
1987     }
1988     return rslt;
1989 }
1990 
1991 int
net_prep_detonate_action(Unit * unit,Unit * unit2,int x,int y,int z)1992 net_prep_detonate_action(Unit *unit, Unit *unit2, int x, int y, int z)
1993 {
1994     int rslt;
1995 
1996     /* Check the prep action result first and return if it would fail. */
1997     if (unit == NULL || unit->act == NULL || unit2 == NULL) {
1998 	return FALSE;
1999     }
2000     /* Then check the busy flag. */
2001     if (unit->busy) {
2002     	return TRUE;
2003     } else {
2004     	unit->busy = TRUE;
2005     }
2006     if (my_rid == master_rid) {
2007 	rslt = prep_detonate_action(unit, unit2, x, y, z);
2008     }
2009     if (numremotes > 0) {
2010 	broadcast_action(unit, unit2->id, ACTION_DETONATE, x, y, z, 0);
2011     }
2012     return rslt;
2013 }
2014 
2015 /* From move.c. */
2016 
2017 int
net_prep_move_action(Unit * unit,Unit * unit2,int x,int y,int z)2018 net_prep_move_action(Unit *unit, Unit *unit2, int x, int y, int z)
2019 {
2020     int rslt;
2021 
2022     /* Check the prep action result first and return if it would fail. */
2023     if (unit == NULL || unit->act == NULL || unit2 == NULL) {
2024 	return FALSE;
2025     }
2026     /* Then check the busy flag. */
2027     if (unit->busy) {
2028     	return TRUE;
2029     } else {
2030     	unit->busy = TRUE;
2031     }
2032     if (my_rid == master_rid) {
2033 	rslt = prep_move_action(unit, unit2, x, y, z);
2034     }
2035     if (numremotes > 0) {
2036 	broadcast_action(unit, unit2->id, ACTION_MOVE, x, y, z, 0);
2037     }
2038     return rslt;
2039 }
2040 
2041 int
net_prep_enter_action(Unit * unit,Unit * unit2,Unit * unit3)2042 net_prep_enter_action(Unit *unit, Unit *unit2, Unit *unit3)
2043 {
2044     int rslt;
2045 
2046     /* Check the prep action result first and return if it would fail. */
2047     if (unit == NULL || unit->act == NULL || unit2 == NULL) {
2048 	return FALSE;
2049     }
2050     /* Then check the busy flag. */
2051     if (unit->busy) {
2052     	return TRUE;
2053     } else {
2054     	unit->busy = TRUE;
2055     }
2056     if (my_rid == master_rid) {
2057 	rslt = prep_enter_action(unit, unit2, unit3);
2058     }
2059     if (numremotes > 0) {
2060 	broadcast_action(unit, unit2->id, ACTION_ENTER, unit3->id, 0, 0, 0);
2061     }
2062     return rslt;
2063 }
2064 
2065 /* Task networking. */
2066 
2067 static Task *tmptask;
2068 
2069 /* (should remove, never used) */
2070 void
net_add_task(Unit * unit,int pos,Task * task)2071 net_add_task(Unit *unit, int pos, Task *task)
2072 {
2073     if (my_rid == master_rid) {
2074 	add_task(unit, pos, task);
2075     }
2076     if (numremotes > 0) {
2077 	broadcast_add_task(unit, pos, task);
2078     }
2079 }
2080 
2081 void
net_set_capture_task(Unit * unit,int x,int y,int u,int s)2082 net_set_capture_task(Unit *unit, int x, int y, int u, int s)
2083 {
2084     if (my_rid == master_rid) {
2085 	set_capture_task(unit, x, y, u, s);
2086     }
2087     if (numremotes > 0) {
2088 	tmptask = create_capture_task(unit, x, y, u, s);
2089 	broadcast_add_task(unit, CLEAR_AGENDA, tmptask);
2090 	free_task(tmptask);
2091     }
2092 }
2093 
2094 void
net_set_occupy_task(Unit * unit,Unit * transport)2095 net_set_occupy_task(Unit *unit, Unit *transport)
2096 {
2097     if (my_rid == master_rid) {
2098 	set_occupy_task(unit, transport);
2099     }
2100     if (numremotes > 0) {
2101 	tmptask = create_occupy_task(unit, transport);
2102 	broadcast_add_task(unit, CLEAR_AGENDA, tmptask);
2103 	free_task(tmptask);
2104     }
2105 }
2106 
2107 void
net_set_collect_task(Unit * unit,int m,int x,int y)2108 net_set_collect_task(Unit *unit, int m, int x, int y)
2109 {
2110     if (my_rid == master_rid) {
2111 	set_collect_task(unit, m, x, y);
2112     }
2113     if (numremotes > 0) {
2114 	tmptask = create_collect_task(unit, m, x, y);
2115 	broadcast_add_task(unit, CLEAR_AGENDA, tmptask);
2116 	free_task(tmptask);
2117     }
2118 }
2119 
2120 void
net_set_move_to_task(Unit * unit,int x,int y,int dist)2121 net_set_move_to_task(Unit *unit, int x, int y, int dist)
2122 {
2123     if (my_rid == master_rid) {
2124 	set_move_to_task(unit, x, y, dist);
2125     }
2126     if (numremotes > 0) {
2127 	tmptask = create_move_to_task(unit, x, y, dist);
2128 	broadcast_add_task(unit, CLEAR_AGENDA, tmptask);
2129 	free_task(tmptask);
2130     }
2131 }
2132 
2133 void
net_push_move_to_task(Unit * unit,int x,int y,int dist)2134 net_push_move_to_task(Unit *unit, int x, int y, int dist)
2135 {
2136     if (my_rid == master_rid) {
2137 	push_move_to_task(unit, x, y, dist);
2138     }
2139     if (numremotes > 0) {
2140 	tmptask = create_move_to_task(unit, x, y, dist);
2141 	broadcast_add_task(unit, 0, tmptask);
2142 	free_task(tmptask);
2143     }
2144 }
2145 
2146 void
net_set_move_dir_task(Unit * unit,int dir,int n)2147 net_set_move_dir_task(Unit *unit, int dir, int n)
2148 {
2149     if (my_rid == master_rid) {
2150 	set_move_dir_task(unit, dir, n);
2151     }
2152     if (numremotes > 0) {
2153 	tmptask = create_move_dir_task(unit, dir, n);
2154 	broadcast_add_task(unit, CLEAR_AGENDA, tmptask);
2155 	free_task(tmptask);
2156     }
2157 }
2158 
2159 /* Construct Task */
2160 
2161 void
net_set_construct_task(Unit * unit,int u,int run,int transid,int x,int y)2162 net_set_construct_task(Unit *unit, int u, int run, int transid, int x, int y)
2163 {
2164     if (my_rid == master_rid) {
2165 	set_construct_task(unit, u, run, transid, x, y);
2166     }
2167     if (numremotes > 0) {
2168 	tmptask = create_construct_task(unit, u, run, transid, x, y);
2169 	broadcast_add_task(unit, CLEAR_AGENDA, tmptask);
2170 	free_task(tmptask);
2171     }
2172 }
2173 
2174 void
net_push_construct_task(Unit * unit,int u,int run,int transid,int x,int y)2175 net_push_construct_task(Unit *unit, int u, int run, int transid, int x, int y)
2176 {
2177     if (my_rid == master_rid) {
2178 	push_construct_task(unit, u, run, transid, x, y);
2179     }
2180     if (numremotes > 0) {
2181 	tmptask = create_construct_task(unit, u, run, transid, x, y);
2182 	broadcast_add_task(unit, 0, tmptask);
2183 	free_task(tmptask);
2184     }
2185 }
2186 
2187 /* Build Task */
2188 
2189 void
net_set_build_task(Unit * unit,int id,int cp)2190 net_set_build_task(Unit *unit, int id, int cp)
2191 {
2192     if (my_rid == master_rid) {
2193 	set_build_task(unit, id, cp);
2194     }
2195     if (numremotes > 0) {
2196 	tmptask = create_build_task(unit, id, cp);
2197 	broadcast_add_task(unit, CLEAR_AGENDA, tmptask);
2198 	free_task(tmptask);
2199     }
2200 }
2201 
2202 void
net_push_build_task(Unit * unit,int id,int cp)2203 net_push_build_task(Unit *unit, int id, int cp)
2204 {
2205     if (my_rid == master_rid) {
2206 	push_build_task(unit, id, cp);
2207     }
2208     if (numremotes > 0) {
2209 	tmptask = create_build_task(unit, id, cp);
2210 	broadcast_add_task(unit, 0, tmptask);
2211 	free_task(tmptask);
2212     }
2213 }
2214 
2215 /* Repair Task */
2216 
2217 void
net_set_repair_task(Unit * unit,int id,int hp)2218 net_set_repair_task(Unit *unit, int id, int hp)
2219 {
2220     if (my_rid == master_rid) {
2221 	set_repair_task(unit, id, hp);
2222     }
2223     if (numremotes > 0) {
2224 	tmptask = create_repair_task(unit, id, hp);
2225 	broadcast_add_task(unit, CLEAR_AGENDA, tmptask);
2226 	free_task(tmptask);
2227     }
2228 }
2229 
2230 void
net_push_repair_task(Unit * unit,int id,int hp)2231 net_push_repair_task(Unit *unit, int id, int hp)
2232 {
2233     if (my_rid == master_rid) {
2234 	push_repair_task(unit, id, hp);
2235     }
2236     if (numremotes > 0) {
2237 	tmptask = create_repair_task(unit, id, hp);
2238 	broadcast_add_task(unit, 0, tmptask);
2239 	free_task(tmptask);
2240     }
2241 }
2242 
2243 void
net_set_develop_task(Unit * unit,int u2,int techgoal)2244 net_set_develop_task(Unit *unit, int u2, int techgoal)
2245 {
2246     if (my_rid == master_rid) {
2247 	set_develop_task(unit, u2, techgoal);
2248     }
2249     if (numremotes > 0) {
2250 	tmptask = create_develop_task(unit, u2, techgoal);
2251 	broadcast_add_task(unit, CLEAR_AGENDA, tmptask);
2252 	free_task(tmptask);
2253     }
2254 }
2255 
2256 void
net_push_develop_task(Unit * unit,int u2,int n)2257 net_push_develop_task(Unit *unit, int u2, int n)
2258 {
2259     if (my_rid == master_rid) {
2260 	push_develop_task(unit, u2, n);
2261     }
2262     if (numremotes > 0) {
2263 	tmptask = create_develop_task(unit, u2, n);
2264 	broadcast_add_task(unit, 0, tmptask);
2265 	free_task(tmptask);
2266     }
2267 }
2268 
2269 void
net_push_hit_unit_task(Unit * unit,int x,int y,int u,int s)2270 net_push_hit_unit_task(Unit *unit, int x, int y, int u, int s)
2271 {
2272     if (my_rid == master_rid) {
2273 	push_hit_unit_task(unit, x, y, u, s);
2274     }
2275     if (numremotes > 0) {
2276 	tmptask = create_hit_unit_task(unit, x, y, u, s);
2277 	broadcast_add_task(unit, 0, tmptask);
2278 	free_task(tmptask);
2279     }
2280 }
2281 
2282 void
net_set_hit_unit_task(Unit * unit,int x,int y,int u,int s)2283 net_set_hit_unit_task(Unit *unit, int x, int y, int u, int s)
2284 {
2285     if (my_rid == master_rid) {
2286 	set_hit_unit_task(unit, x, y, u, s);
2287     }
2288     if (numremotes > 0) {
2289 	tmptask = create_hit_unit_task(unit, x, y, u, s);
2290 	broadcast_add_task(unit, CLEAR_AGENDA, tmptask);
2291 	free_task(tmptask);
2292     }
2293 }
2294 
2295 void
net_set_disband_task(Unit * unit)2296 net_set_disband_task(Unit *unit)
2297 {
2298     if (my_rid == master_rid) {
2299 	set_disband_task(unit);
2300     }
2301     if (numremotes > 0) {
2302 	tmptask = create_task(TASK_DISBAND);
2303 	broadcast_add_task(unit, CLEAR_AGENDA, tmptask);
2304 	free_task(tmptask);
2305     }
2306 }
2307 
2308 void
net_set_resupply_task(Unit * unit,int m)2309 net_set_resupply_task(Unit *unit, int m)
2310 {
2311     if (my_rid == master_rid) {
2312 	set_resupply_task(unit, m);
2313     }
2314     if (numremotes > 0) {
2315 	tmptask = create_resupply_task(unit, m);
2316 	broadcast_add_task(unit, CLEAR_AGENDA, tmptask);
2317 	free_task(tmptask);
2318     }
2319 }
2320 
2321 void
net_push_occupy_task(Unit * unit,Unit * transport)2322 net_push_occupy_task(Unit *unit, Unit *transport)
2323 {
2324     if (my_rid == master_rid) {
2325 	push_occupy_task(unit, transport);
2326     }
2327     if (numremotes > 0) {
2328 	tmptask = create_occupy_task(unit, transport);
2329 	broadcast_add_task(unit, 0, tmptask);
2330 	free_task(tmptask);
2331     }
2332 }
2333 
2334 void
net_push_pickup_task(Unit * unit,Unit * occ)2335 net_push_pickup_task(Unit *unit, Unit *occ)
2336 {
2337     if (my_rid == master_rid) {
2338 	push_pickup_task(unit, occ);
2339     }
2340     if (numremotes > 0) {
2341 	tmptask = create_pickup_task(unit, occ);
2342 	broadcast_add_task(unit, 0, tmptask);
2343 	free_task(tmptask);
2344     }
2345 }
2346 
2347 void
net_push_produce_task(Unit * unit,int m,int n)2348 net_push_produce_task(Unit *unit, int m, int n)
2349 {
2350     if (my_rid == master_rid) {
2351 	push_produce_task(unit, m, n);
2352     }
2353     if (numremotes > 0) {
2354 	tmptask = create_produce_task(unit, m, n);
2355 	broadcast_add_task(unit, 0, tmptask);
2356 	free_task(tmptask);
2357     }
2358 }
2359 
2360 void
net_set_sentry_task(Unit * unit,int n)2361 net_set_sentry_task(Unit *unit, int n)
2362 {
2363     if (my_rid == master_rid) {
2364 	set_sentry_task(unit, n);
2365     }
2366     if (numremotes > 0) {
2367 	tmptask = create_sentry_task(unit, n);
2368 	broadcast_add_task(unit, CLEAR_AGENDA, tmptask);
2369 	free_task(tmptask);
2370     }
2371 }
2372 
2373 void
net_push_sentry_task(Unit * unit,int n)2374 net_push_sentry_task(Unit *unit, int n)
2375 {
2376     if (my_rid == master_rid) {
2377 	push_sentry_task(unit, n);
2378     }
2379     if (numremotes > 0) {
2380 	tmptask = create_sentry_task(unit, n);
2381 	broadcast_add_task(unit, 0, tmptask);
2382 	free_task(tmptask);
2383     }
2384 }
2385 
2386 static void
broadcast_add_task(Unit * unit,int pos,Task * task)2387 broadcast_add_task(Unit *unit, int pos, Task *task)
2388 {
2389     int numargs, i;
2390     char buf[BUFSIZE], *argtypes;
2391 
2392     sprintf(buf, "T%d %d %d %d %d",
2393 	    unit->id, pos, task->type, task->execnum, task->retrynum);
2394     argtypes = taskdefns[task->type].argtypes;
2395     numargs = strlen(argtypes);
2396     for (i = 0; i < numargs; ++i)
2397       tprintf(buf, " %d", task->args[i]);
2398     broadcast_packet(buf);
2399 }
2400 
2401 /* World tweaking. */
2402 
2403 #ifdef DESIGNERS
2404 
2405 void
net_paint_cell(Side * side,int x,int y,int r,int t)2406 net_paint_cell(Side *side, int x, int y, int r, int t)
2407 {
2408     if (my_rid == master_rid) {
2409 	paint_cell(side, x, y, r, t);
2410     }
2411     if (numremotes > 0) {
2412 	broadcast_layer_change("cell", side, x, y, r, t, 0, 0);
2413     }
2414 }
2415 
2416 void
net_paint_border(Side * side,int x,int y,int dir,int t,int mode)2417 net_paint_border(Side *side, int x, int y, int dir, int t, int mode)
2418 {
2419     if (my_rid == master_rid) {
2420 	paint_border(side, x, y, dir, t, mode);
2421     }
2422     if (numremotes > 0) {
2423 	broadcast_layer_change("bord", side, x, y, dir, t, mode, 0);
2424     }
2425 }
2426 
2427 void
net_paint_connection(Side * side,int x,int y,int dir,int t,int mode)2428 net_paint_connection(Side *side, int x, int y, int dir, int t, int mode)
2429 {
2430     if (my_rid == master_rid) {
2431 	paint_connection(side, x, y, dir, t, mode);
2432     }
2433     if (numremotes > 0) {
2434 	broadcast_layer_change("conn", side, x, y, dir, t, mode, 0);
2435     }
2436 }
2437 
2438 void
net_paint_coating(Side * side,int x,int y,int r,int t,int depth)2439 net_paint_coating(Side *side, int x, int y, int r, int t, int depth)
2440 {
2441     if (my_rid == master_rid) {
2442 	paint_coating(side, x, y, r, t, depth);
2443     }
2444     if (numremotes > 0) {
2445 	broadcast_layer_change("coat", side, x, y, r, t, depth, 0);
2446     }
2447 }
2448 
2449 void
net_paint_people(Side * side,int x,int y,int r,int s)2450 net_paint_people(Side *side, int x, int y, int r, int s)
2451 {
2452     if (my_rid == master_rid) {
2453 	paint_people(side, x, y, r, s);
2454     }
2455     if (numremotes > 0) {
2456 	broadcast_layer_change("peop", side, x, y, r, s, 0, 0);
2457     }
2458 }
2459 
2460 void
net_paint_control(Side * side,int x,int y,int r,int s)2461 net_paint_control(Side *side, int x, int y, int r, int s)
2462 {
2463     if (my_rid == master_rid) {
2464 	paint_control(side, x, y, r, s);
2465     }
2466     if (numremotes > 0) {
2467 	broadcast_layer_change("ctrl", side, x, y, r, s, 0, 0);
2468     }
2469 }
2470 
2471 void
net_paint_feature(Side * side,int x,int y,int r,int f)2472 net_paint_feature(Side *side, int x, int y, int r, int f)
2473 {
2474     if (my_rid == master_rid) {
2475 	paint_feature(side, x, y, r, f);
2476     }
2477     if (numremotes > 0) {
2478 	broadcast_layer_change("feat", side, x, y, r, f, 0, 0);
2479     }
2480 }
2481 
2482 void
net_paint_elevation(Side * side,int x,int y,int r,int code,int elev,int vary)2483 net_paint_elevation(Side *side, int x, int y, int r, int code, int elev,
2484 		    int vary)
2485 {
2486     if (my_rid == master_rid) {
2487 	paint_elevation(side, x, y, r, code, elev, vary);
2488     }
2489     if (numremotes > 0) {
2490 	broadcast_layer_change("elev", side, x, y, r, code, elev, vary);
2491     }
2492 }
2493 
2494 void
net_paint_temperature(Side * side,int x,int y,int r,int temp)2495 net_paint_temperature(Side *side, int x, int y, int r, int temp)
2496 {
2497     if (my_rid == master_rid) {
2498 	paint_temperature(side, x, y, r, temp);
2499     }
2500     if (numremotes > 0) {
2501 	broadcast_layer_change("temp", side, x, y, r, temp, 0, 0);
2502     }
2503 }
2504 
2505 void
net_paint_material(Side * side,int x,int y,int r,int m,int amt)2506 net_paint_material(Side *side, int x, int y, int r, int m, int amt)
2507 {
2508     if (my_rid == master_rid) {
2509 	paint_material(side, x, y, r, m, amt);
2510     }
2511     if (numremotes > 0) {
2512 	broadcast_layer_change("m", side, x, y, r, m, amt, 0);
2513     }
2514 }
2515 
2516 void
net_paint_clouds(Side * side,int x,int y,int r,int cloudtype,int bot,int hgt)2517 net_paint_clouds(Side *side, int x, int y, int r, int cloudtype, int bot,
2518 		 int hgt)
2519 {
2520     if (my_rid == master_rid) {
2521 	paint_clouds(side, x, y, r, cloudtype, bot, hgt);
2522     }
2523     if (numremotes > 0) {
2524 	broadcast_layer_change("clouds", side, x, y, r, cloudtype, 0, 0);
2525 	broadcast_layer_change("cloud-bottoms", side, x, y, r, bot, 0, 0);
2526 	broadcast_layer_change("cloud-heights", side, x, y, r, hgt, 0, 0);
2527     }
2528 }
2529 
2530 void
net_paint_winds(Side * side,int x,int y,int r,int dir,int force)2531 net_paint_winds(Side *side, int x, int y, int r, int dir, int force)
2532 {
2533     if (my_rid == master_rid) {
2534 	paint_winds(side, x, y, r, dir, force);
2535     }
2536     if (numremotes > 0) {
2537 	broadcast_layer_change("wind", side, x, y, r, dir, force, 0);
2538     }
2539 }
2540 
2541 static void
broadcast_layer_change(char * layername,Side * side,int x,int y,int a1,int a2,int a3,int a4)2542 broadcast_layer_change(char *layername, Side *side, int x, int y,
2543 		       int a1, int a2, int a3, int a4)
2544 {
2545     char buf[BUFSIZE];
2546 
2547     sprintf(buf, "W%s %d %d %d %d %d %d %d",
2548 	    layername, side->id, x, y, a1, a2, a3, a4);
2549     broadcast_packet(buf);
2550 }
2551 
2552 #endif /* DESIGNERS */
2553 
2554 void
send_quit(void)2555 send_quit(void)
2556 {
2557     /* (should have more error-handling, since may be called in bad
2558        situations) */
2559     sprintf(spbuf, "Q%d", my_rid);
2560     broadcast_packet(spbuf);
2561     flush_outgoing_queue();
2562 }
2563 
2564 /* Given a buffer with a packet in it, send it all the places that
2565    it should go. */
2566 
2567 static void
broadcast_packet(char * buf)2568 broadcast_packet(char *buf)
2569 {
2570     int rid;
2571     extern int in_run_game;
2572 
2573     if (in_run_game) {
2574 	notify_all("Attempting to send \"%s\" while in run_game", buf);
2575     }
2576     if (my_rid == master_rid) {
2577 	for_all_remotes(rid) {
2578 	    if (rid != my_rid) {
2579 		send_packet(rid, buf);
2580 	    }
2581 	}
2582     } else {
2583 	/* Send to master; master will echo back eventually. */
2584 	send_packet(master_rid, buf);
2585     }
2586 }
2587 
2588 /* Given a packet to be sent, package it up (add header, embed escape
2589    chars, add checksum), send, and maybe wait for acknowledgement. */
2590 
2591 int
send_packet(int id,char * inbuf)2592 send_packet(int id, char *inbuf)
2593 {
2594     int i, j, csum, numtimeouts;
2595     char buf[BUFSIZE];
2596 
2597     if (my_rid != master_rid && !sendnow) {
2598 	/* Add packet to the outgoing queue. */
2599 	save_outgoing_packet(id, inbuf);
2600 	Dprintf("OutQ: %d \"%s\"\n", id, inbuf);
2601 	return TRUE;
2602     }
2603     Dprintf("Send: %d \"%s\"\n", id, inbuf);
2604     j = 0;
2605     csum = 0;
2606     buf[j++] = STARTPKT;
2607     /* Copy the input buffer into the packet, changing any special
2608        characters into escape sequences along the way. */
2609     for (i = 0; inbuf[i] != '\0'; ++i) {
2610 	if (inbuf[i] == STARTPKT) {
2611 	    buf[j++] = ESCAPEPKT;
2612 	    buf[j++] = STARTPKTESC;
2613 	} else if (inbuf[i] == ENDPKT) {
2614 	    buf[j++] = ESCAPEPKT;
2615 	    buf[j++] = ENDPKTESC;
2616 	} else if (inbuf[i] == ESCAPEPKT) {
2617 	    buf[j++] = ESCAPEPKT;
2618 	    buf[j++] = ESCAPEPKTESC;
2619 	} else {
2620 	    buf[j++] = inbuf[i];
2621 	}
2622 	csum += (unsigned char) inbuf[i];
2623     }
2624     buf[j++] = ENDPKT;
2625     /* Add on the checksum, in hex. */
2626     buf[j++] = tohex ((csum >> 4) & 0xf);
2627     buf[j++] = tohex (csum & 0xf);
2628     buf[j++] = '\0';
2629     if (display_traffic)
2630       notify_all("To %d: %s", id, inbuf);
2631     low_send(id, buf);
2632     /* Wait for the packet's receipt to be acknowledged. */
2633     numtimeouts = 0;
2634     expecting_ack = TRUE;
2635     while (expecting_ack) {
2636     	time_t start_time, now;
2637 
2638     	time(&start_time);
2639 	receive_data(30, MAXPACKETS);
2640 	time(&now);
2641 	if (!expecting_ack) {
2642 	    if (display_traffic)
2643 	      notify_all("From %d: ack after %d s", id, now - start_time);
2644 	    break;
2645 	}
2646 	notify_all("Timed out waiting for ack");
2647 	Dprintf("Timed out waiting for ack\n");
2648 	++numtimeouts;
2649 #if (1)
2650 	if (hosting && my_rid == master_rid) {
2651 	    /* If we're the master, we MUST be assured that our
2652 	       packets have been received, since we've already changed
2653 	       our own state and can't undo it. So we will loop here
2654 	       for a long time, can only get out early if run_warning
2655 	       includes a quit option. */
2656 #endif
2657 	    if (numtimeouts > 200)
2658 	      run_error("Timed out %d times", numtimeouts);
2659 #if (1)
2660 	} else {
2661 	    /* If we're not the master, then we haven't actually
2662 	       changed our own state, and so we can continue on
2663 	       safely.  The user will have to retry whatever state
2664 	       change was desired. */
2665 	    expecting_ack = FALSE;
2666 	    return FALSE;
2667 	}
2668 #endif
2669     }
2670     return TRUE;
2671 }
2672 
2673 struct q_entry {
2674   int id;
2675   char *buf;
2676   struct q_entry *next;
2677 } *outgoing, *outgoing_last, *incoming, *incoming_last;
2678 
2679 static void
save_outgoing_packet(int id,char * inbuf)2680 save_outgoing_packet(int id, char *inbuf)
2681 {
2682     struct q_entry *entry;
2683 
2684     entry = (struct q_entry *) xmalloc (sizeof (struct q_entry));
2685     entry->id = id;
2686     entry->buf = copy_string(inbuf);
2687     if (outgoing_last) {
2688 	outgoing_last->next = entry;
2689     } else {
2690 	outgoing = entry;
2691     }
2692     outgoing_last = entry;
2693 }
2694 
2695 void
flush_outgoing_queue(void)2696 flush_outgoing_queue(void)
2697 {
2698     struct q_entry *entry;
2699 
2700     if (my_rid != master_rid) {
2701 	sendnow = TRUE;
2702 	for (entry = outgoing; entry != NULL; entry = entry->next) {
2703 	    send_packet(entry->id, entry->buf);
2704 	}
2705 	outgoing = outgoing_last = NULL;
2706 	sendnow = FALSE;
2707     }
2708 }
2709 
2710 static void
save_incoming_packet(int id,char * inbuf)2711 save_incoming_packet(int id, char *inbuf)
2712 {
2713     struct q_entry *entry;
2714 
2715     entry = (struct q_entry *) xmalloc (sizeof (struct q_entry));
2716     entry->id = id;
2717     entry->buf = copy_string(inbuf);
2718     if (incoming_last) {
2719 	incoming_last->next = entry;
2720     } else {
2721 	incoming = entry;
2722     }
2723     incoming_last = entry;
2724 }
2725 
2726 /* Flush up to lim number of packets.
2727    Return the number of packets actually flushed. */
2728 
2729 int
flush_incoming_queue(int lim)2730 flush_incoming_queue(int lim)
2731 {
2732     struct q_entry *entry;
2733     int	packets = 0;
2734 
2735     if (display_traffic)
2736 	notify_all("Starting to flush the incoming queue.");
2737     Dprintf("Starting to flush the incoming queue.\n");
2738     flushing = TRUE;
2739     for (entry = incoming; entry != NULL && packets < lim;
2740 	 entry = entry->next) {
2741 	receive_packet(entry->id, entry->buf);
2742 	packets++;
2743     }
2744     if (display_traffic)
2745 	notify_all("%d packets flushed from the incoming queue.", packets);
2746     Dprintf("%d packets flushed from the incoming queue.\n", packets);
2747     if (entry == NULL) {
2748     	if (display_traffic)
2749 	    notify_all("The incoming queue is empty.");
2750 	Dprintf("The incoming queue is empty.\n");
2751 	incoming = incoming_last = NULL;
2752     } else {
2753     	if (display_traffic)
2754 	    notify_all("Packets still left in the incoming queue.");
2755 	Dprintf("Packets still left in the incoming queue.\n");
2756     }
2757     flushing = FALSE;
2758     return packets;
2759 }
2760 
2761 static char *packetbuf;
2762 
2763 static char *rsltbuf;
2764 
2765 static int rsltid;
2766 
2767 #define PACKETBUFSIZE 1000
2768 
2769 static time_t nothing_start_time;
2770 
2771 static int nothing_count;
2772 
2773 static int nothing_timeout;
2774 
2775 static void
remove_chars(char * buf,int n)2776 remove_chars(char *buf, int n)
2777 {
2778     int i;
2779 
2780     for (i = 0; i < PACKETBUFSIZE - n; ++i)
2781       buf[i] = buf[i + n];
2782 }
2783 
2784 /* Handle the basic process of receiving up to lim number of packets. */
2785 
2786 void
receive_data(int timeout,int lim)2787 receive_data(int timeout, int lim)
2788 {
2789     char buf[2000], *pktend;
2790     int retry = 10, gotsome, i, j, len, csum, pktcsum, packets = 0;
2791 
2792     /* (should have buffers for each remote prog) */
2793     if (packetbuf == NULL)
2794       packetbuf = (char *)xmalloc(PACKETBUFSIZE);
2795     if (rsltbuf == NULL)
2796       rsltbuf = (char *)xmalloc(BUFSIZE);
2797     if (incoming != NULL && process_packets && !expecting_ack) {
2798 	packets += flush_incoming_queue(lim - packets);
2799 	return;
2800     }
2801     while (retry > 0 && packets < lim) {
2802 	if (packetbuf[0] == STARTPKT) {
2803 	    pktend = strchr(packetbuf, ENDPKT);
2804 	    if (pktend != NULL
2805 		&& strlen(packetbuf) >= (pktend - packetbuf + 3)) {
2806 		/* We have accumulated a whole packet. */
2807 		len = pktend - packetbuf - 1;
2808 		/* Copy out the packet's contents, handling escape
2809 		   chars and computing checksum along the way. */
2810 		j = 0;
2811 		csum = 0;
2812 		for (i = 1; i < len + 1; ++i) {
2813 		    if (packetbuf[i] == ESCAPEPKT
2814 			&& packetbuf[i+1] == STARTPKTESC) {
2815 			rsltbuf[j++] = STARTPKT;
2816 			++i;
2817 		    } else if (packetbuf[i] == ESCAPEPKT
2818 			       && packetbuf[i+1] == ENDPKTESC) {
2819 			rsltbuf[j++] = ENDPKT;
2820 			++i;
2821 		    } else if (packetbuf[i] == ESCAPEPKT
2822 			       && packetbuf[i+1] == ESCAPEPKTESC) {
2823 			rsltbuf[j++] = ESCAPEPKT;
2824 			++i;
2825 		    } else {
2826 			rsltbuf[j++] = packetbuf[i];
2827 		    }
2828 		    csum += (unsigned char) rsltbuf[j-1];
2829 		}
2830 		rsltbuf[j++] = '\0';
2831 		csum &= 0xff;
2832 		/* Extract the checksum that was sent. */
2833 		pktcsum = fromhex(packetbuf[len + 2]) << 4;
2834 		pktcsum |= fromhex(packetbuf[len + 3]);
2835 		/* Remove the packet from the buffer. */
2836 		remove_chars(packetbuf, len + 4);
2837 		if (csum != pktcsum && pktcsum != 0) {
2838 		    run_warning("checksum error, received %x, calced %x",
2839 				pktcsum, csum);
2840 		    /* what to do with the packet? should require resend */
2841 		}
2842 		if (expecting_ack) {
2843 		    Dprintf(
2844 "Packet received while expecting ack, saving \"%s\"\n", rsltbuf);
2845 		    save_incoming_packet(rsltid, rsltbuf);
2846 		    /* (Should we ack an unexpected packet?) */
2847 		    Dprintf("Sending ack to unexpected packet\n");
2848 		    low_send(rsltid, "+");
2849 #if (1)
2850 		    /* Check if the unexpected packet was followed by the ack
2851 		       that we were looking for. This solved all the host
2852 		       timeout problems with the network code. */
2853 		    if (packetbuf[0] == '+') {
2854 		    	expecting_ack = FALSE;
2855 			Dprintf("Trailing ack received\n");
2856 			remove_chars(packetbuf, 1);
2857 			/* Immediately flush the unexpected packet that was
2858 			   saved while we were waiting for the ack.
2859 			   The test for flushing is to avoid infinite
2860 			   recursion due to rebroadcasting by the master of
2861 			   packets that are flushed. */
2862 	    		if (process_packets && flushing == FALSE) {
2863 			    packets += flush_incoming_queue(lim - packets);
2864 			}
2865 			/* Always returning here instead of going to
2866 			  look_for_data solved the problem with
2867 			  occasional 30 sec lockups of the host when
2868 			  playing against a tcltk client. These cases
2869 			  would always trigger the characteristic
2870 			  debug message "Rcvd nothing 0 times (timeout
2871 			  0 secs)" below. */
2872 			return;
2873 		    }
2874 #endif
2875 		    goto look_for_data;
2876 		}
2877 		if (rsltid == 0) {
2878 		    run_warning("Packet from rid == 0, discarded \"%s\"",
2879 				rsltbuf);
2880 		    goto look_for_data;
2881 		}
2882 		Dprintf("Sending ack\n");
2883 		if (display_traffic)
2884 		  notify_all("From %d: %s", rsltid, rsltbuf);
2885 		low_send(rsltid, "+");
2886 		/* The interpretation process needs to be last,
2887 		   because it may in turn cause more packets to be sent. */
2888 		if (process_packets /*&& flushing == FALSE*/ && packets < lim) {
2889 		    receive_packet(rsltid, rsltbuf);
2890 		/* We dont want to process the packet right now. */
2891 		} else {
2892 		    save_incoming_packet(rsltid, rsltbuf);
2893 		}
2894 		retry = 0;
2895 	    } else {
2896 		/* Part of a packet, but not the whole thing -
2897                    continue waiting. */
2898 		--retry;
2899 		goto look_for_data;
2900 	    }
2901 	/* One of our calls to 'send_packet' will be happy if we receive an
2902 	   ack, because it can then go ahead a finish up. */
2903 	} else if (packetbuf[0] == '+') {
2904 	    expecting_ack = FALSE;
2905 	    Dprintf("Ack received\n");
2906 	    remove_chars(packetbuf, 1);
2907 	    /* Immediately flush any packets that accumulated while we were
2908 	       waiting for the ack. The test for flushing is to prevent
2909 	       infinite recursion by the master due to rebroadcasting of
2910 	       packets that are flushed. */
2911 	    if (incoming != NULL && process_packets && flushing == FALSE) {
2912 		packets += flush_incoming_queue(lim - packets);
2913 		return;
2914 	    }
2915 	    /* If packetbuf is empty we are done. */
2916 	    if (packetbuf[0] == '\0') {
2917 		retry = 0;
2918 	    }
2919 	} else if (strlen(packetbuf) > 0) {
2920 	    Dprintf("Removing garbage char '%c' (0x%x)\n",
2921 		    packetbuf[0], packetbuf[0]);
2922 	    remove_chars(packetbuf, 1);
2923 	} else {
2924 	look_for_data:
2925 	    gotsome = low_receive(&rsltid, buf, BUFSIZE, timeout);
2926 	    /* Debugging printout. */
2927 	    if (gotsome) {
2928 		if (nothing_count > 0) {
2929 		    Dprintf("Rcvd nothing %d times (timeout %d secs)\n",
2930 			    nothing_count, nothing_timeout);
2931 		    nothing_count = 0;
2932 		}
2933 		Dprintf("Rcvd: %d \"%s\"\n", rsltid, (buf ? buf : "<null>"));
2934 	    } else {
2935 		time_t now;
2936 
2937 		if (nothing_count == 0)
2938 		  time(&nothing_start_time);
2939 		time(&now);
2940 		if (idifftime(now, nothing_start_time) > 0
2941 		    || timeout != nothing_timeout) {
2942 		    Dprintf("Rcvd nothing %d times (timeout %d secs)\n",
2943 			    nothing_count, nothing_timeout);
2944 		    nothing_count = 0;
2945 		} else {
2946 		    ++nothing_count;
2947 		    nothing_timeout = timeout;
2948 		}
2949 		nothing_timeout = timeout;
2950 	    }
2951 	    if (gotsome) {
2952 		if (strlen(packetbuf) + strlen(buf) > PACKETBUFSIZE) {
2953 		    run_warning("packet buffer overflow");
2954 		    return;
2955 		}
2956 		strcat(packetbuf, buf);
2957 		/* Go around and see if we have something now. */
2958 		--retry;
2959 	    } else {
2960 		retry = 0;
2961 	    }
2962 	}
2963     }
2964 }
2965 
2966 /* Given a received packet, take it apart and do what it says. */
2967 
2968 void
receive_packet(int id,char * buf)2969 receive_packet(int id, char *buf)
2970 {
2971     int should_rebroadcast;
2972 
2973     Dprintf("From %d: \"%s\"\n", id, buf);
2974     if (downloading) {
2975 	if (strcmp(buf, "\neludoMemag\n") == 0) {
2976 	    /* We've seen the end-of-module marker, now interpret. */
2977 	    downloading = FALSE;
2978 	    mainmodule = get_game_module("*download*");
2979 	    mainmodule->contents = downloadbuf;
2980 	    /* The sequence below is similar to that in load_game_module. */
2981 	    open_module(mainmodule, FALSE);
2982 	    read_forms(mainmodule);
2983 	    close_module(mainmodule);
2984 	    check_game_validity();
2985 	    start_variant_setup_stage();
2986 	} else {
2987 	    /* Make sure we have space with which to concatenate. */
2988 	    if (strlen(downloadbuf) + strlen(buf) >= 200000) {
2989 		init_error("Exceeded download buffer size");
2990 		return;
2991 	    }
2992 	    /* Concatenate this packet to the others. */
2993 	    strcat(downloadbuf, buf);
2994 	}
2995     } else {
2996 	should_rebroadcast = TRUE;
2997 	switch (buf[0]) {
2998 	  case 'A':
2999 	    receive_action(buf + 1);
3000 	    break;
3001 	  case 'B':
3002 	    receive_bugoff(id);
3003 	    break;
3004 	  case 'C':
3005 	    receive_command(buf + 1);
3006 	    break;
3007 	  case 'E':
3008 	    receive_error(id, buf + 1);
3009 	    should_rebroadcast = FALSE;
3010 	    break;
3011 	  case 'M':
3012 	    receive_net_message(buf + 1);
3013 	    break;
3014 	  case 'P':
3015 	    receive_player_prop(buf + 1);
3016 	    break;
3017 	  case 'Q':
3018 	    receive_quit(buf + 1);
3019 	    break;
3020 	  case 'R':
3021 	    new_randstate = atoi(buf + 1);
3022 	    if (new_randstate != randstate) {
3023 		Dprintf("Rand state change: %d -> %d\n",
3024 			randstate, new_randstate);
3025 	    } else {
3026 		Dprintf("Rand state matches\n");
3027 	    }
3028 	    randstate = new_randstate;
3029 	    /* This is only issued by the master, no need to rebroadcast. */
3030 	    should_rebroadcast = FALSE;
3031 	    break;
3032 	  case 'S':
3033 	    receive_side_prop(buf + 1);
3034 	    break;
3035 	  case 'T':
3036 	    receive_task(buf + 1);
3037 	    break;
3038 	  case 'U':
3039 	    receive_unit_prop(buf + 1);
3040 	    break;
3041 	  case 'V':
3042 	    if (strcmp(buf + 1, version_string()) != 0) {
3043 		init_warning(
3044 "Xconq versions \"%s\" and \"%s\" should not link up",
3045 			     buf + 1, version_string());
3046 	    }
3047 	    should_rebroadcast = FALSE;
3048 	    break;
3049 	  case 'W':
3050 	    receive_world_prop(buf + 1);
3051 	    break;
3052 	  case 'X':
3053 	    receive_run_game(buf + 1);
3054 	    /* This is only issued by the master, no need to rebroadcast. */
3055 	    should_rebroadcast = FALSE;
3056 	    break;
3057 	  case 'Z':
3058 	    receive_game_checksum(buf + 1);
3059 	    /* Checksums may be broadcast, but not automatically. */
3060 	    should_rebroadcast = FALSE;
3061 	    break;
3062 	  case 'a':
3063 	    if (buf[1] == 'S' && buf[2] == 'T') {
3064 		start_player_setup_stage();
3065 		/* This is only issued by the master, no need to
3066                    rebroadcast. */
3067 		should_rebroadcast = FALSE;
3068 	    } else if (buf[1] == 'O' && buf[2] == 'K') {
3069 		start_game_ready_stage();
3070 		/* This is only issued by the master, no need to
3071                    rebroadcast. */
3072 		should_rebroadcast = FALSE;
3073 	    } else {
3074 		receive_assignment_setting(buf + 1);
3075 	    }
3076 	    break;
3077 	  case 'c':
3078 	    receive_chat(buf + 1);
3079 	    break;
3080 	  case 'f':
3081 	  /* We received the filename of a saved game. */
3082 	    if (buf[1] == ' ') {
3083 		Module *module = create_game_module(NULL);
3084    		module->filename = copy_string(buf + 2);
3085 		mainmodule = module;
3086 		load_game_module(mainmodule, TRUE);
3087 		check_game_validity();
3088 	    } else {
3089 		downloading = TRUE;
3090 		/* (should use obstack here) */
3091 		if (downloadbuf == NULL)
3092 		  downloadbuf = (char *)xmalloc(200000);
3093 		downloadbuf[0] = '\0';
3094 	    }
3095 	    should_rebroadcast = FALSE;
3096 	    break;
3097 	  case 'g':
3098 	  /* We received the name of a library module. */
3099 	    if (buf[1] == ' ') {
3100 		Module *module = get_game_module(buf + 2);
3101 		mainmodule = module;
3102 		load_game_module(mainmodule, TRUE);
3103 		check_game_validity();
3104 	    } else {
3105 		downloading = TRUE;
3106 		/* (should use obstack here) */
3107 		if (downloadbuf == NULL)
3108 		  downloadbuf = (char *)xmalloc(200000);
3109 		downloadbuf[0] = '\0';
3110 	    }
3111 	    should_rebroadcast = FALSE;
3112 	    break;
3113 	  case 'j':
3114 	    /* Only let people join if the game has not yet started. */
3115 	    if (current_stage == initial_stage) {
3116 		    send_remote_id(id);
3117 		    add_remote_program(id, buf + 1);
3118 	    } else {
3119 	    	    send_bugoff(id);
3120 	    }
3121 	    /* Don't rebroadcast, the master will handle specially. */
3122 	    should_rebroadcast = FALSE;
3123 	    break;
3124 	  case 'p':
3125 	    receive_remote_program(buf + 1);
3126 	    /* This is only issued by the master, no need to rebroadcast. */
3127 	    should_rebroadcast = FALSE;
3128 	    break;
3129 	  case 'r':
3130 	    my_rid = atoi(buf + 1);
3131 	    /* This is only issued by the master, no need to rebroadcast. */
3132 	    should_rebroadcast = FALSE;
3133 	    break;
3134 	  case 's':
3135 	    start_game_load_stage();
3136 	    /* This is only issued by the master, no need to rebroadcast. */
3137 	    should_rebroadcast = FALSE;
3138 	    break;
3139 	  case 'v':
3140 	    if (buf[1] == 'S' && buf[2] == 'T') {
3141 		start_variant_setup_stage();
3142 		/* This is only issued by the master, no need to
3143                    rebroadcast. */
3144 		should_rebroadcast = FALSE;
3145 	    } else if (buf[1] == 'O' && buf[2] == 'K') {
3146 		start_player_pre_setup_stage();
3147 		/* This is only issued by the master, no need to
3148                    rebroadcast. */
3149 		should_rebroadcast = FALSE;
3150 	    } else {
3151 		receive_variant_setting(buf + 1);
3152 	    }
3153 	    break;
3154 	  case 'w':
3155 	    save_game(buf + 1);
3156 	    /* This can be issued by any player. */
3157 	    should_rebroadcast = TRUE;
3158 	    break;
3159 	  default:
3160 	    /* Since the protocol's purpose is to keep multiple
3161 	       executables' state in sync, every kind of packet must
3162 	       be understood. */
3163 	    run_warning("Packet not recognized: \"%s\"\n", buf);
3164 	    break;
3165 	}
3166 	/* The master should rebroadcast most types of packets
3167 	   automatically, so all programs stay in sync. */
3168 	if (my_rid != 0 && my_rid == master_rid && should_rebroadcast)
3169 	  broadcast_packet(buf);
3170     }
3171 }
3172 
3173 static void
receive_net_message(char * str)3174 receive_net_message(char *str)
3175 {
3176     int id;
3177     char *nstr;
3178     SideMask sidemask;
3179     Side *side;
3180 
3181     id = strtol(str, &nstr, 10);
3182     side = side_n(id);
3183     str = nstr;
3184     sidemask = strtol(str, &nstr, 10);
3185     str = nstr;
3186     ++str;
3187     /* Note that this is the internal form of "send", which just copies
3188        to all local recipients. */
3189     send_message(side, sidemask, str);
3190 }
3191 
3192 static void
receive_command(char * str)3193 receive_command(char *str)
3194 {
3195     int i, args[5];
3196     char *nstr, *argstr;
3197     Side *side;
3198     Unit *unit, *unit2;
3199 
3200     argstr = strchr(str, ' ');
3201     i = 0;
3202     while (*argstr != '\0' && i < 5) {
3203 	args[i++] = strtol(argstr, &nstr, 10);
3204 	argstr = nstr;
3205     }
3206     if (strncmp(str, "wake-unit ", 9) == 0) {
3207 	side = side_n(args[0]);
3208 	if (side == NULL) {
3209 	    return;
3210 	}
3211 	unit = find_unit(args[1]);
3212 	if (unit == NULL) {
3213 	    return;
3214 	}
3215 	wake_unit(side, unit, args[2]);
3216     } else if (strncmp(str, "wake-area ", 9) == 0) {
3217 	side = side_n(args[0]);
3218 	if (side == NULL) {
3219 	    return;
3220 	}
3221 	wake_area(side, args[1], args[2], args[3], args[4]);
3222     } else if (strncmp(str, "designer-create-unit ", strlen("designer-create-unit ")) == 0) {
3223 	side = side_n(args[0]);
3224 	if (side == NULL) {
3225 	    return;
3226 	}
3227 	designer_create_unit(side, args[1], args[2], args[3], args[4]);
3228     } else if (strncmp(str, "designer-teleport ", strlen("designer-teleport ")) == 0) {
3229 	unit = find_unit(args[0]);
3230 	if (unit == NULL) {
3231 	    return;
3232 	}
3233 	unit2 = NULL;
3234 	if (args[3] > 0) {
3235 	    unit2 = find_unit(args[3]);
3236 	    if (unit2 == NULL) {
3237 		return;
3238 	    }
3239 	}
3240 	designer_teleport(unit, args[1], args[2], unit2);
3241     } else if (strncmp(str, "designer-change-side ", strlen("designer-change-side ")) == 0) {
3242 	unit = find_unit(args[0]);
3243 	if (unit == NULL) {
3244 	    return;
3245 	}
3246 	side = side_n(args[1]);
3247 	designer_change_side(unit, side);
3248     } else if (strncmp(str, "designer-disband ", strlen("designer-disband ")) == 0) {
3249 	unit = find_unit(args[0]);
3250 	if (unit == NULL) {
3251 	    return;
3252 	}
3253 	designer_disband(unit);
3254     } else {
3255 	run_warning("Unknown C packet \"%s\", ignoring", str);
3256     }
3257 }
3258 
3259 static void
receive_action(char * str)3260 receive_action(char *str)
3261 {
3262     int unitid, acteeid, i;
3263     char *nstr;
3264     Unit *unit;
3265     Action tmpaction;
3266 
3267     unitid = strtol(str, &nstr, 10);
3268     str = nstr;
3269     if (*str == '/') {
3270 	++str;
3271 	acteeid = strtol(str, &nstr, 10);
3272 	str = nstr;
3273     } else {
3274 	acteeid = unitid;
3275     }
3276     tmpaction.actee = acteeid;
3277     tmpaction.type = (ActionType)strtol(str, &nstr, 10);
3278     str = nstr;
3279     i = 0;
3280     while (*nstr != '\0' && i < 10) {
3281 	tmpaction.args[i++] = strtol(str, &nstr, 10);
3282 	str = nstr;
3283     }
3284     unit = find_unit(unitid);
3285     if (unit == NULL) {
3286 #if 1
3287 	notify_all("RUN WARNING: Packet A refers to missing unit #%d, ignoring", unitid);
3288 #else
3289 	run_warning("Packet A refers to missing unit #%d, ignoring", unitid);
3290 #endif
3291 	return;
3292     }
3293     if (unit->act == NULL) {
3294 #if 1
3295 	notify_all("RUN WARNING: Packet A refers to non-acting unit %s, ignoring",
3296 		    unit_desig(unit));
3297 #else
3298 	run_warning("Packet A refers to non-acting unit %s, ignoring",
3299 		    unit_desig(unit));
3300 #endif
3301 	return;
3302     }
3303     /* Lay the action over what is already there; the next run_game
3304        should actually do it. */
3305     unit->act->nextaction = tmpaction;
3306 }
3307 
3308 static void
receive_task(char * str)3309 receive_task(char *str)
3310 {
3311     int unitid, pos, tasktype, i;
3312     char *nstr;
3313     Unit *unit;
3314     Task *task;
3315 
3316     unitid = strtol(str, &nstr, 10);
3317     str = nstr;
3318     pos = strtol(str, &nstr, 10);
3319     str = nstr;
3320     tasktype = strtol(str, &nstr, 10);
3321     str = nstr;
3322     task = create_task((TaskType)tasktype);
3323     task->execnum = strtol(str, &nstr, 10);
3324     str = nstr;
3325     task->retrynum = strtol(str, &nstr, 10);
3326     str = nstr;
3327     i = 0;
3328     while (*nstr != '\0' && i < 10) {
3329 	task->args[i++] = strtol(str, &nstr, 10);
3330 	str = nstr;
3331     }
3332     unit = find_unit(unitid);
3333     if (unit == NULL) {
3334 #if 1
3335 	notify_all("RUN WARNING: Packet T refers to missing unit #%d, ignoring", unitid);
3336 #else
3337 	run_warning("Packet T refers to missing unit #%d, ignoring", unitid);
3338 #endif
3339 	return;
3340     }
3341     if (unit->plan == NULL) {
3342 #if 1
3343 	notify_all("RUN WARNING: Packet T refers to non-planning unit %s, ignoring",
3344 		    unit_desig(unit));
3345 #else
3346 	run_warning("Packet T refers to non-planning unit %s, ignoring",
3347 		    unit_desig(unit));
3348 #endif
3349 	return;
3350     }
3351     add_task(unit, pos, task);
3352 }
3353 
3354 static void
receive_player_prop(char * str)3355 receive_player_prop(char *str)
3356 {
3357     int val, val2;
3358     char *nstr;
3359     Side *side;
3360 
3361     if (strncmp(str, "add ", 4) == 0) {
3362 	str += 4;
3363 	request_additional_side(str);
3364     } else if (strncmp(str, "assign ", 7) == 0) {
3365 	str += 7;
3366 	val = strtol(str, &nstr, 10);
3367 	str = nstr;
3368 	val2 = strtol(str, &nstr, 10);
3369 	side = side_n(val);
3370 	if (side != NULL)
3371 	  side->player = find_player(val2);
3372     } else {
3373 	run_warning("Unknown P packet \"%s\", ignoring", str);
3374     }
3375 }
3376 
3377 static void
receive_quit(char * str)3378 receive_quit(char *str)
3379 {
3380     int rid;
3381     char *nstr;
3382 
3383     rid = strtol(str, &nstr, 10);
3384     Dprintf("Received quit from %d\n", rid);
3385     online[rid] = FALSE;
3386     quitter = rid;
3387 }
3388 
3389 static void
receive_bugoff(int rid)3390 receive_bugoff(int rid)
3391 {
3392     Dprintf("Received bug off from host\n");
3393     master_rid = 0;
3394     bounced = TRUE;
3395 }
3396 
3397 static void
receive_side_prop(char * str)3398 receive_side_prop(char *str)
3399 {
3400     int id, sn, val;
3401     char *nstr;
3402     Side *side;
3403     Unit *unit;
3404 
3405     id = strtol(str, &nstr, 10);
3406     side = side_n(id);
3407     if (side == NULL)
3408       return;
3409     str = nstr;
3410     ++str;
3411     if (strncmp(str, "adjective ", 10) == 0) {
3412 	set_side_adjective(side, side, str + 10);
3413     } else if (strncmp(str, "af ", 3) == 0) {
3414 	str += 3;
3415 	val = strtol(str, &nstr, 10);
3416 	set_autofinish(side, val);
3417     } else if (strncmp(str, "ar ", 3) == 0) {
3418 	str += 3;
3419 	val = strtol(str, &nstr, 10);
3420 	set_autoresearch(side, val);
3421     } else if (strncmp(str, "ai ", 3) == 0) {
3422 	str += 3;
3423 	set_side_ai(side, str);
3424     } else if (strncmp(str, "colorscheme ", 12) == 0) {
3425 	set_side_colorscheme(side, side, str + 12);
3426 	} else if (strncmp(str, "controlledby ", 13) == 0) {
3427 	str += 13;
3428 	sn = strtol(str, &nstr, 10);
3429 	str = nstr;
3430 	val = strtol(str, &nstr, 10);
3431 	set_controlled_by(side, side_n(sn), val);
3432     } else if (strncmp(str, "designer ", 9) == 0) {
3433 	str += 9;
3434 	val = strtol(str, &nstr, 10);
3435 	if (val)
3436 	  become_designer(side);
3437 	else
3438 	  become_nondesigner(side);
3439     } else if (strncmp(str, "doctrine ", 9) == 0) {
3440 	str += 9;
3441 	set_doctrine(side, str);
3442     } else if (strncmp(str, "draw ", 5) == 0) {
3443 	str += 5;
3444 	val = strtol(str, &nstr, 10);
3445 	set_willing_to_draw(side, val);
3446     } else if (strncmp(str, "emblemname ", 11) == 0) {
3447 	set_side_emblemname(side, side, str + 11);
3448     } else if (strncmp(str, "fin ", 4) == 0) {
3449 	finish_turn(side);
3450     } else if (strncmp(str, "longname ", 9) == 0) {
3451 	set_side_longname(side, side, str + 9);
3452     } else if (strncmp(str, "name ", 5) == 0) {
3453 	set_side_name(side, side, str + 5);
3454     } else if (strncmp(str, "noun ", 5) == 0) {
3455 	set_side_noun(side, side, str + 5);
3456     } else if (strncmp(str, "pluralnoun ", 11) == 0) {
3457 	set_side_pluralnoun(side, side, str + 11);
3458     } else if (strncmp(str, "resign ", 7) == 0) {
3459 	str += 7;
3460 	val = strtol(str, &nstr, 10);
3461 	resign_game(side, (val < 0 ? NULL : side_n(val)));
3462     } else if (strncmp(str, "save ", 5) == 0) {
3463 	str += 5;
3464 	val = strtol(str, &nstr, 10);
3465 	set_willing_to_save(side, val);
3466     } else if (strncmp(str, "self ", 5) == 0) {
3467 	str += 5;
3468 	val = strtol(str, &nstr, 10);
3469 	unit = find_unit(val);
3470 	set_side_self_unit(side, unit);
3471     } else if (strncmp(str, "research_topic ", 15) == 0) {
3472 	str += 15;
3473 	val = strtol(str, &nstr, 10);
3474 	set_side_research_topic(side, val);
3475     } else if (strncmp(str, "research_goal ", 14) == 0) {
3476 	str += 14;
3477 	val = strtol(str, &nstr, 10);
3478     } else if (strncmp(str, "shortname ", 10) == 0) {
3479 	set_side_shortname(side, side, str + 10);
3480     } else if (strncmp(str, "startx ", 7) == 0) {
3481 	str += 7;
3482 	val = strtol(str, &nstr, 10);
3483 	set_side_startx(side, val);
3484     } else if (strncmp(str, "starty ", 7) == 0) {
3485 	str += 7;
3486 	val = strtol(str, &nstr, 10);
3487 	set_side_starty(side, val);
3488     } else if (strncmp(str, "trust ", 6) == 0) {
3489 	str += 6;
3490 	sn = strtol(str, &nstr, 10);
3491 	str = nstr;
3492 	val = strtol(str, &nstr, 10);
3493 	set_trust(side, side_n(sn), val);
3494     } else {
3495 	run_warning("Unknown S packet \"%s\", ignoring", str);
3496     }
3497 }
3498 
3499 static void
receive_unit_prop(char * str)3500 receive_unit_prop(char *str)
3501 {
3502     int sid, uid, val, val2;
3503     char *nstr;
3504     Unit *unit;
3505     Side *side;
3506 
3507     /* Collect the side. */
3508     sid = strtol(str, &nstr, 10);
3509     side = side_n(sid);
3510     /* Note that the side may be NULL. */
3511     str = nstr;
3512     /* Collect the unit. */
3513     uid = strtol(str, &nstr, 10);
3514     unit = find_unit(uid);
3515     if (unit == NULL) {
3516 #if 1
3517 	notify_all("RUN WARNING: Packet with invalid unit id %d, ignoring packet", uid);
3518 #else
3519 	run_warning("Packet with invalid unit id %d, ignoring packet", uid);
3520 #endif
3521 	return;
3522     }
3523     str = nstr;
3524     ++str;
3525     /* Decode the property being set. */
3526     if (strncmp(str, "ai ", 3) == 0) {
3527 	val = strtol(str + 3, &nstr, 10);
3528 	str = nstr;
3529 	val2 = strtol(str, &nstr, 10);
3530 	set_unit_ai_control(side, unit, val, val2);
3531     } else if (strncmp(str, "clragenda ", 5) == 0) {
3532 	clear_task_agenda(unit);
3533     } else if (strncmp(str, "clroutcome ", 5) == 0) {
3534 	clear_task_outcome(unit);
3535     } else if (strncmp(str, "delay ", 6) == 0) {
3536 	val = strtol(str + 6, &nstr, 10);
3537 	delay_unit(unit, val);
3538     } else if (strncmp(str, "disband ", 8) == 0) {
3539 	disband_unit(side, unit);
3540     } else if (strncmp(str, "forcereplan ", 12) == 0) {
3541 	force_replan(unit);
3542     } else if (strncmp(str, "formation ", 10) == 0) {
3543 	run_warning("need to interp formation packet");
3544     } else if (strncmp(str, "maingoal ", 9) == 0) {
3545 	Goal *goal;
3546 	int i;
3547 
3548 	str += 9;
3549 	val = strtol(str, &nstr, 10);
3550 	if (val > 0) {
3551 	    str = nstr;
3552 	    goal = create_goal((GoalType)val, side, TRUE);
3553 	    for (i = 0; i < 4; ++i) {
3554 		val = strtol(str, &nstr, 10);
3555 		str = nstr;
3556 		goal->args[i] = val;
3557 	    }
3558 	} else {
3559 	    goal = NULL;
3560 	}
3561 	set_unit_main_goal(side, unit, goal);
3562     } else if (strncmp(str, "curadvance ", 11) == 0) {
3563 	val = strtol(str + 11, &nstr, 10);
3564 	set_unit_curadvance(side, unit, val);
3565     } else if (strncmp(str, "researchdone ", 13) == 0) {
3566 	val = strtol(str + 13, &nstr, 10);
3567 	set_unit_researchdone(side, unit, val);
3568     } else if (strncmp(str, "name ", 5) == 0) {
3569 	set_unit_name(side, unit, str + 5);
3570     } else if (strncmp(str, "plan ", 5) == 0) {
3571 	val = strtol(str + 5, &nstr, 10);
3572 	set_unit_plan_type(side, unit, val);
3573     } else if (strncmp(str, "resv ", 5) == 0) {
3574 	str += 5;
3575 	val = strtol(str, &nstr, 10);
3576 	str = nstr;
3577 	val2 = strtol(str, &nstr, 10);
3578 	set_unit_reserve(side, unit, val, val2);
3579     } else if (strncmp(str, "sleep ", 6) == 0) {
3580 	str += 6;
3581 	val = strtol(str, &nstr, 10);
3582 	str = nstr;
3583 	val2 = strtol(str, &nstr, 10);
3584 	set_unit_asleep(side, unit, val, val2);
3585     } else if (strncmp(str, "waittrans ", 10) == 0) {
3586 	str += 10;
3587 	val = strtol(str, &nstr, 10);
3588 	set_unit_waiting_for_transport(side, unit, val);
3589     } else {
3590 	run_warning("Unknown U packet \"%s\", ignoring", str);
3591     }
3592 }
3593 
3594 static void
receive_world_prop(char * str)3595 receive_world_prop(char *str)
3596 {
3597     int id, x, y, a1, a2, a3, a4;
3598     char *str1, *str2, *nstr;
3599     Side *side;
3600 
3601     /* This is necessary since we modify the string below. */
3602     str1 = copy_string(str);
3603     str2 = strchr(str1, ' ');
3604     if (str2 == NULL) {
3605 	return;
3606     }
3607     *str2 = '\0';
3608     ++str2;
3609     id = strtol(str2, &nstr, 10);
3610     str2 = nstr;
3611     x = strtol(str2, &nstr, 10);
3612     str2 = nstr;
3613     y = strtol(str2, &nstr, 10);
3614     str2 = nstr;
3615     a1 = strtol(str2, &nstr, 10);
3616     str2 = nstr;
3617     a2 = strtol(str2, &nstr, 10);
3618     str2 = nstr;
3619     a3 = strtol(str2, &nstr, 10);
3620     str2 = nstr;
3621     a4 = strtol(str2, &nstr, 10);
3622     side = side_n(id);
3623     if (strcmp(str1, "cell") == 0) {
3624 	paint_cell(side, x, y, a1, a2);
3625     } else if (strcmp(str1, "bord") == 0) {
3626 	paint_border(side, x, y, a1, a2, a3);
3627     } else if (strcmp(str1, "conn") == 0) {
3628 	paint_connection(side, x, y, a1, a2, a3);
3629     } else if (strcmp(str1, "coat") == 0) {
3630 	paint_coating(side, x, y, a1, a2, a3);
3631     } else if (strcmp(str1, "peop") == 0) {
3632 	paint_people(side, x, y, a1, a2);
3633     } else if (strcmp(str1, "ctrl") == 0) {
3634 	paint_control(side, x, y, a1, a2);
3635     } else if (strcmp(str1, "feat") == 0) {
3636 	paint_feature(side, x, y, a1, a2);
3637     } else if (strcmp(str1, "elev") == 0) {
3638 	paint_elevation(side, x, y, a1, a2, a3, a4);
3639     } else if (strcmp(str1, "temp") == 0) {
3640 	paint_temperature(side, x, y, a1, a2);
3641     } else if (strcmp(str1, "m") == 0) {
3642 	paint_material(side, x, y, a1, a2, a3);
3643     } else if (strcmp(str1, "wind") == 0) {
3644 	paint_winds(side, x, y, a1, a2, a3);
3645     } else if (strcmp(str1, "user") == 0) {
3646     	toggle_user_at(find_unit(a1), x, y);
3647     } else {
3648 	run_warning("Unknown W packet \"%s\", ignoring", str);
3649     }
3650 }
3651 
3652 static void
receive_run_game(char * str)3653 receive_run_game(char *str)
3654 {
3655     int maxactions, newsernum;
3656     char *reststr, *nreststr;
3657 
3658     maxactions = strtol(str, &reststr, 10);
3659     newsernum = strtol(reststr, &nreststr, 10);
3660     reststr = nreststr;
3661     new_randstate = strtol(reststr, &nreststr, 10);
3662     set_g_run_serial_number(newsernum);
3663     if (new_randstate != randstate) {
3664 	Dprintf("Rand state change: %d -> %d\n", randstate, new_randstate);
3665     } else {
3666 	Dprintf("Rand state matches\n");
3667     }
3668     randstate = new_randstate;
3669     /* This where non-masters actually call run_game. */
3670     run_game(maxactions);
3671 }
3672 
3673 static void
receive_game_checksum(char * str)3674 receive_game_checksum(char *str)
3675 {
3676     int before_run = FALSE, other_rid, other_csum, our_csum;
3677     char *reststr, *nreststr;
3678 
3679     if (*str == 'a') {
3680 	before_run = TRUE;
3681 	++str;
3682     }
3683     other_rid = strtol(str, &reststr, 10);
3684     other_csum = strtol(reststr, &nreststr, 10);
3685     our_csum = game_checksum();
3686     if (our_csum != other_csum) {
3687 	/* If we're about to lose, make sure the master knows about it. */
3688 	if (my_rid != master_rid)
3689 	  send_game_checksum_error(master_rid, our_csum, other_csum);
3690 	/* This would be a warning normally, but being out of sync
3691 	   isn't always a real problem apparently... */
3692 	notify_all("Game is out of sync! (received %d from %d, computed %d%s)",
3693 		    other_csum, other_rid, our_csum,
3694 		    (before_run ? ", before run_game" : ""));
3695 	Dprintf("Game is out of sync! (received %d from %d, computed %d%s)\n",
3696 		other_csum, other_rid, our_csum,
3697 		(before_run ? ", before run_game" : ""));
3698 	if (!dumped_checksums) {
3699 		sprintf(tmpbuf,
3700 			"CLIENT (rid %d) checksums at first sync error:",
3701 			my_rid);
3702 		dump_checksums(tmpbuf);
3703 	    	dumped_checksums = TRUE;
3704     		notify_all("Checksums dumped into Xconq-Client.Csums");
3705 		Dprintf("Checksums dumped into Xconq-Client.Csums\n");
3706 	}
3707     }
3708 }
3709 
3710 /* Compute a single integer derived from the contents of the game state. */
3711 
3712 #define add_int_to_checksum(cs, x) ((cs) += (x));
3713 
3714 int
game_checksum(void)3715 game_checksum(void)
3716 {
3717     int csum, i;
3718     Side *side;
3719     Unit *unit;
3720     Plan *plan;
3721     Task *task;
3722 
3723     csum = 0;
3724     for_all_sides(side) {
3725 	add_int_to_checksum(csum, side->id);
3726 	add_int_to_checksum(csum, side->self_unit_id);
3727 	add_int_to_checksum(csum, side->controlled_by_id);
3728 	add_int_to_checksum(csum, side->ingame);
3729 	add_int_to_checksum(csum, side->everingame);
3730 	add_int_to_checksum(csum, side->status);
3731 	add_int_to_checksum(csum, side->willingtodraw);
3732 	add_int_to_checksum(csum, side->autofinish);
3733 	add_int_to_checksum(csum, side->finishedturn);
3734 	add_int_to_checksum(csum, side->advantage);
3735     }
3736     for_all_units(unit) {
3737 	add_int_to_checksum(csum, unit->id);
3738 	add_int_to_checksum(csum, unit->number);
3739 	add_int_to_checksum(csum, unit->x);
3740 	add_int_to_checksum(csum, unit->y);
3741 	add_int_to_checksum(csum, unit->z);
3742 	add_int_to_checksum(csum, unit->hp);
3743 	if (unit->transport) {
3744 	    add_int_to_checksum(csum, unit->transport->id);
3745 	}
3746         add_int_to_checksum(csum, unit->creation_id);
3747 	plan = unit->plan;
3748 	if (unit->plan) {
3749 	    add_int_to_checksum(csum, plan->type);
3750 	    add_int_to_checksum(csum, plan->asleep);
3751 	    add_int_to_checksum(csum, plan->reserve);
3752 	    add_int_to_checksum(csum, plan->delayed);
3753 	    add_int_to_checksum(csum, plan->waitingfortasks);
3754 	    add_int_to_checksum(csum, plan->aicontrol);
3755 	    for_all_tasks(plan, task) {
3756 		add_int_to_checksum(csum, task->type);
3757 		for (i = 0; i < MAXTASKARGS; ++i)
3758 		  add_int_to_checksum(csum, task->args[i]);
3759 		add_int_to_checksum(csum, task->execnum);
3760 		add_int_to_checksum(csum, task->retrynum);
3761 	    }
3762 	}
3763     }
3764     return csum;
3765 }
3766 
3767 /* Checksums go to a file. */
3768 
3769 static void
csum_printf(char * str,...)3770 csum_printf(char *str, ...)
3771 {
3772 	va_list ap;
3773 
3774 	va_start(ap, str);
3775 	vfprintf(cfp, str, ap);
3776 	va_end(ap);
3777 }
3778 
3779 
3780 void
dump_checksums(char * str)3781 dump_checksums(char *str)
3782 {
3783 	Side *side;
3784 	Unit *unit;
3785 	Plan *plan;
3786 	Task *task;
3787 	int i;
3788 
3789 	if (my_rid == 0) {
3790 		/* Should never happen. */
3791 		cfp = open_file("Xconq.Csums", "a");
3792 	} else if (my_rid == master_rid) {
3793 		cfp = open_file("Xconq-Master.Csums", "a");
3794 	} else {
3795 		cfp = open_file("Xconq-Client.Csums", "a");
3796 	}
3797 	csum_printf("%s\n\n", str);
3798 	csum_printf("GAME  %s\n\n", mainmodule->name);
3799 	csum_printf("TURN %d  RUN #%d\n\n", g_turn(), g_run_serial_number());
3800 	csum_printf("RANDSTATE %d\n\n", randstate);
3801 	for_all_sides(side) {
3802 		csum_printf("SIDE %d %s\n", side->id,
3803 			    shortest_side_title(side, tmpbuf));
3804 		csum_printf("self_unit_id %d\n", side->self_unit_id);
3805 		csum_printf("controlled_by_id %d\n", side->controlled_by_id);
3806 		csum_printf("ingame %d\n", side->ingame);
3807 		csum_printf("everingame %d\n", side->everingame);
3808 		csum_printf("status %d\n", side->status);
3809 		csum_printf("willingtodraw %d\n", side->willingtodraw);
3810 		csum_printf("autofinish %d\n", side->autofinish);
3811 		csum_printf("finishedturn %d\n", side->finishedturn);
3812 		csum_printf("advantage %d\n\n", side->advantage);
3813 	}
3814 	for_all_units(unit) {
3815 		csum_printf("UNIT #%d %s\n", unit->id, short_unit_handle(unit));
3816 		csum_printf("side %d\n", unit->side->id);
3817 		csum_printf("type %s\n", u_type_name(unit->type));
3818 		csum_printf("number %d\n", unit->number);
3819 		csum_printf("x %d\n", unit->x);
3820 		csum_printf("y %d\n", unit->y);
3821 		csum_printf("z %d\n", unit->z);
3822 		csum_printf("hp %d\n", unit->hp);
3823 		if (unit->transport) {
3824 			csum_printf("transport %d\n", unit->transport->id);
3825 		}
3826 		plan = unit->plan;
3827 		if (unit->plan) {
3828 			csum_printf("	PLAN %s\n", capitalize(plantypenames[plan->type]));
3829 			csum_printf("	asleep %d\n", plan->asleep);
3830 			csum_printf("	reserve %d\n", plan->reserve);
3831 			csum_printf("	delayed %d\n", plan->delayed);
3832 			csum_printf("	waitingfortasks %d\n", plan->waitingfortasks);
3833 			csum_printf("	aicontrol %d\n", plan->aicontrol);
3834 			for_all_tasks(plan, task) {
3835 				csum_printf("		TASK %s\n", capitalize(taskdefns[task->type].display_name));
3836 				for (i = 0; i < MAXTASKARGS; ++i) {
3837 					csum_printf("		args %d: %d\n", i, task->args[i]);
3838 				}
3839 				csum_printf("		execnum %d\n", task->execnum);
3840 				csum_printf("		retrynum %d\n", task->retrynum);
3841 			}
3842 		}
3843 		csum_printf("\n");
3844 	}
3845 	fclose(cfp);
3846 }
3847 
3848 static void
receive_error(int id,char * str)3849 receive_error(int id, char *str)
3850 {
3851     /* (should decode specific types of errors?) */
3852     notify_all("Error from #%d, \"%s\"", id, str);
3853     Dprintf("Error from #%d, \"%s\"\n", id, str);
3854 
3855     if (!dumped_checksums) {
3856 	sprintf(tmpbuf, "MASTER (rid %d) checksums at first sync error:", my_rid);
3857 	dump_checksums(tmpbuf);
3858 	dumped_checksums = TRUE;
3859 	notify_all("Checksums dumped into Xconq-Master.Csums");
3860 	Dprintf("Checksums dumped into Xconq-Master.Csums\n");
3861     }
3862 }
3863 
3864 static void
receive_remote_program(char * str)3865 receive_remote_program(char *str)
3866 {
3867     int rid;
3868     char *nstr;
3869 
3870     rid = strtol(str, &nstr, 10);
3871     str = nstr + 1;
3872     numremotes = max(rid, numremotes);
3873     online[rid] = TRUE;
3874     remote_player_specs[rid] = copy_string(str);
3875     /* (should do with a function pointer) */
3876     add_remote_locally(rid, str);
3877 }
3878 
3879 static void
receive_chat(char * str)3880 receive_chat(char *str)
3881 {
3882     int rid;
3883     char *nstr;
3884 
3885     rid = strtol(str, &nstr, 10);
3886     str = nstr + 1;
3887     /* (should do with a function pointer) */
3888     send_chat(rid, str);
3889 }
3890 
3891 static void
receive_variant_setting(char * str)3892 receive_variant_setting(char *str)
3893 {
3894     int which, v1, v2, v3;
3895     char *nstr;
3896 
3897     ++str;
3898     which = strtol(str, &nstr, 10);
3899     str = nstr + 1;
3900     v1 = strtol(str, &nstr, 10);
3901     str = nstr + 1;
3902     v2 = strtol(str, &nstr, 10);
3903     str = nstr + 1;
3904     v3 = strtol(str, &nstr, 10);
3905     set_variant_value(which, v1, v2, v3);
3906     if (update_variant_callback)
3907       (*update_variant_callback)(which);
3908 }
3909 
3910 static void
receive_assignment_setting(char * str)3911 receive_assignment_setting(char *str)
3912 {
3913     int n, n2, val;
3914     char *nstr, *aitype;
3915     Player *player;
3916 
3917     n = strtol(str, &nstr, 10);
3918     str = nstr;
3919     ++str;
3920     if (strncmp(str, "add", 3) == 0) {
3921 	n = add_side_and_player();
3922     } else if (strncmp(str, "remove ", 7) == 0) {
3923 	n = strtol(str + 7, NULL, 10);
3924 	remove_side_and_player(n);
3925   	if (update_assignment_callback) {
3926 		for (n2 = n; n2 <= numsides + 1; n2++) {
3927 			(*update_assignment_callback)(n2);
3928 		}
3929 	}
3930     } else if (strncmp(str, "advantage ", 10) == 0) {
3931 	val = strtol(str + 10, NULL, 10);
3932 	player = assignments[n].player;
3933 	if (player != NULL) {
3934 	    player->advantage = val;
3935 	}
3936     } else if (strncmp(str, "ai ", 3) == 0) {
3937 	aitype = NULL;
3938 	if (str[3] != '\0')
3939 	  aitype = copy_string(str + 3);
3940 	set_ai_for_player(n, aitype);
3941     } else if (strncmp(str, "exchange ", 9) == 0) {
3942 	n2 = strtol(str + 9, NULL, 10);
3943 	n2 = exchange_players(n, n2);
3944 	if (update_assignment_callback)
3945 	  (*update_assignment_callback)(n2);
3946     } else if (strncmp(str, "rename ", 7) == 0) {
3947 	n2 = strtol(str + 7, NULL, 10);
3948 	rename_side_for_player(n, n2);
3949     } else if (strncmp(str, "update ", 7) == 0) {
3950 	nstr = str + 7;
3951 	player = find_player(n);
3952 	if (player == NULL)
3953 	  player = add_player();
3954 	parse_player_spec(player, nstr);
3955 	return; /* a hack, need to handle callback below when windows not up */
3956     } else {
3957 	run_error("assignment packet \"%s\" not recognized", str);
3958     }
3959     if (n >= 0) {
3960 	/* Most settings affect the same assignment, so always update it
3961 	   here. */
3962 	if (update_assignment_callback)
3963 	  (*update_assignment_callback)(n);
3964     }
3965 }
3966 
3967 /* Convert hex digit A to a number.  */
3968 
3969 static int
fromhex(int a)3970 fromhex(int a)
3971 {
3972     if (a >= '0' && a <= '9')
3973       return a - '0';
3974     else if (a >= 'a' && a <= 'f')
3975       return a - 'a' + 10;
3976     else
3977       run_warning ("Reply contains invalid hex digit %d", a);
3978     return 0;
3979 }
3980 
3981 /* Convert number NIB to a hex digit.  */
3982 
3983 static int
tohex(int nib)3984 tohex(int nib)
3985 {
3986     if (nib < 10)
3987       return '0' + nib;
3988     else
3989       return 'a' + nib - 10;
3990 }
3991 
3992 /* (this routine is almost certainly incorrect now - only used on Macs) */
3993 /* Wrong. This code is being used by the x11 interface. */
3994 
3995 static void send_assignment(int id, Side *side, Player *player);
3996 static void send_randstate(int id);
3997 
3998 void
download_to_player(Player * player)3999 download_to_player(Player *player)
4000 {
4001     int i;
4002 
4003     download_game_module(player->rid);
4004     /* Send it the current list of side/player assignments. */
4005     for (i = 0; i < numsides; ++i) {
4006 	send_assignment(player->rid, assignments[i].side,
4007 					assignments[i].player);
4008     }
4009 
4010     /* Make it run everything up to the player setup phase. */
4011     send_randstate(player->rid);
4012     /* If errors, should set player rid back to zero */
4013  }
4014 
4015 void
send_assignment(int rid,Side * side,Player * player)4016 send_assignment(int rid, Side *side, Player *player)
4017 {
4018     sprintf(spbuf, "Passign %d %d", side->id, player->id);
4019     send_packet(rid, spbuf);
4020  }
4021 
4022 void
send_randstate(int rid)4023 send_randstate(int rid)
4024 {
4025     sprintf(spbuf, "R%ld", randstate);
4026     send_packet(rid, spbuf);
4027  }
4028