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(¬hing_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