/* Implementation of the transfer protocol for networked Xconq. Copyright (C) 1996-2000 Stanley T. Shebs. Xconq is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. See the file COPYING. */ /* This is the implementation of the high-level protocol. */ #include "conq.h" #include "kernel.h" #include "kpublic.h" extern long randstate; /* Iteration over all remote programs, by rid. */ #define for_all_remotes(rid) \ for ((rid) = 1; (rid) <= numremotes; ++(rid)) \ if (online[rid]) /* Definitions of special characters in the protocol. */ #define STARTPKT '$' #define ENDPKT '^' #define ESCAPEPKT '!' #define STARTPKTESC '%' #define ENDPKTESC '&' #define ESCAPEPKTESC '@' static void broadcast_command_5(char *cmd, int val, int val2, int val3, int val4, int val5); static void broadcast_side_property(Side *side, char *prop, int val); static void broadcast_side_property_2(Side *side, char *prop, int val, int val2); static void broadcast_side_str_property(Side *side, char *prop, char *val); static void broadcast_unit_property(Side *side, Unit *unit, char *prop, int val); static void broadcast_unit_property_2(Side *side, Unit *unit, char *prop, int val, int val2); static void broadcast_unit_property_5(Side *side, Unit *unit, char *prop, int val, int val2, int val3, int val4, int val5); static void broadcast_unit_str_property(Side *side, Unit *unit, char *prop, char *val); static void broadcast_add_task(Unit *unit, int pos, Task *task); static void broadcast_layer_change(char *layername, Side *side, int x, int y, int a1, int a2, int a3, int a4); static void broadcast_packet(char *buf); static void save_outgoing_packet(int id, char *inbuf); static void save_incoming_packet(int id, char *inbuf); static void remove_chars(char *buf, int n); static void receive_net_message(char *str); static void receive_command(char *str); static void receive_action(char *str); static void receive_player_prop(char *str); static void receive_quit(char *str); static void receive_side_prop(char *str); static void receive_task(char *str); static void receive_unit_prop(char *str); static void receive_world_prop(char *str); static void receive_run_game(char *str); static void receive_game_checksum(char *str); static void receive_error(int id, char *str); static void receive_remote_program(char *str); static void receive_chat(char *str); static void receive_variant_setting(char *str); static void receive_assignment_setting(char *str); static int tohex(int x); static int fromhex(int x); static void flush_incoming_queue(void); static void send_game_checksum_error(int rid, int my_csum, int master_csum); static int send_packet(int id, char *buf); static void broadcast_game_checksum(void); static void send_assignment(int id, Side *side, Player *player); static void send_randstate(int id); static void broadcast_next_action(Unit *unit); static void receive_packet(int id, char *buf); static void add_remote_program(int rid, char *name); static void download_game_module(int rid); static void send_remote_id(int rid); /* This is true if the program expects to host the game and also be the master when all the remote programs are hooked up. */ int hosting; /* This is the total number of programs in the game. */ int numremotes; int numremotewaiting; /* The remote id of this program. */ int my_rid; /* The remote id of the current master program. */ int master_rid; /* The program to which we are currently downloading. */ int tmprid; char *remote_player_specs[100]; int online[100]; int expecting_ack; int timeout_warnings = TRUE; int sendnow; /* Flag indicating that we're in the middle of downloading. */ int downloading; char *downloadbuf; int dlbufend; long new_randstate; void (*update_variant_callback)(int which) = NULL; void (*update_assignment_callback)(int n) = NULL; int quitter; /* If a side is being played from a remote program, we don't need the full user interface structure, just make a placeholder that records the program's id. */ void init_remote_ui(Side *side) { if (side->rui == NULL) { side->rui = (RUI *) xmalloc(sizeof(RUI)); } /* At present, rui->rid is unused. */ side->rui->rid = side->player->rid; } /* For a new remote program with given remote id and player spec, record it locally and also broadcast to all remotes currently online. */ void add_remote_program(int rid, char *name) { int rid2; numremotes = max(rid, numremotes); online[rid] = TRUE; /* Inform our new program of all the existing programs. */ for_all_remotes(rid2) { if (rid2 != rid) { sprintf(spbuf, "p%d %s", rid2, remote_player_specs[rid2]); send_packet(rid, spbuf); } } remote_player_specs[rid] = copy_string(name); add_remote_locally(rid, name); /* Now broadcast info about the new program to everybody. */ sprintf(spbuf, "p%d %s", rid, name); broadcast_packet(spbuf); } /* For each remote program (including us), add a player corresponding to the spec supplied by that program. */ void add_remote_players(void) { int rid; Player *player; for_all_remotes(rid) { /* First look for an existing player object that's waiting to be associated with a remote program. */ player = NULL; for_all_players(player) { if (!empty_string(player->name) && strcmp(player->name, "?") == 0) break; } /* Otherwise create a new player. */ if (player == NULL) player = add_player(); parse_player_spec(player, remote_player_specs[rid]); canonicalize_player(player); player->rid = rid; } } /* Send a message asking to join the game. Return TRUE if the host actually acknowledged us, FALSE if no response. */ int send_join(char *str) { int tries = 100, successful; sprintf(spbuf, "j%s", str); /* Keep trying until host responds. */ while (--tries > 0) { timeout_warnings = FALSE; successful = send_packet(0, spbuf); timeout_warnings = TRUE; if (successful) return TRUE; } init_warning("No response from host program"); return FALSE; } /* Tell the remote program what its id will be during this game. */ void send_remote_id(int rid) { sprintf(spbuf, "r%d", rid); send_packet(rid, spbuf); } void net_send_chat(int rid, char *str) { if (my_rid == master_rid) { send_chat(rid, str); } if (numremotes > 0) { sprintf(spbuf, "c%d %s", rid, str); broadcast_packet(spbuf); } } void send_version(int rid) { /* In order to guarantee that the kernels can actually stay in sync, we require that the program versions match exactly. */ sprintf(spbuf, "V%s", version_string()); send_packet(rid, spbuf); } void net_set_variant_value(int which, int v1, int v2, int v3) { if (my_rid == master_rid) { set_variant_value(which, v1, v2, v3); } if (numremotes > 0) { sprintf(spbuf, "v %d %d %d %d", which, v1, v2, v3); broadcast_packet(spbuf); } } void net_set_player_advantage(int n, int newadv) { if (my_rid == master_rid) { set_player_advantage(n, newadv); } if (numremotes > 0) { sprintf(spbuf, "a%d advantage %d", n, newadv); broadcast_packet(spbuf); } } int net_add_side_and_player(void) { int rslt = -1; if (my_rid == master_rid) { rslt = add_side_and_player(); } if (numremotes > 0) { sprintf(spbuf, "a0 add"); broadcast_packet(spbuf); } return rslt; } void net_rename_side_for_player(int n, int which) { if (my_rid == master_rid) { rename_side_for_player(n, which); } if (numremotes > 0) { sprintf(spbuf, "a%d rename %d", n, which); broadcast_packet(spbuf); } } void net_set_ai_for_player(int n, char *aitype) { if (my_rid == master_rid) { set_ai_for_player(n, aitype); } if (numremotes > 0) { sprintf(spbuf, "a%d ai %s", n, (aitype ? aitype : "")); broadcast_packet(spbuf); } } int net_exchange_players(int n, int n2) { int rslt = -1; if (my_rid == master_rid) { rslt = exchange_players(n, n2); } if (numremotes > 0) { sprintf(spbuf, "a%d exchange %d", n, n2); broadcast_packet(spbuf); } return rslt; } void send_assignment(int rid, Side *side, Player *player) { sprintf(spbuf, "Passign %d %d", side->id, player->id); send_packet(rid, spbuf); } /* (this routine is almost certainly incorrect now - only used on Macs) */ void download_to_player(Player *player) { int i; download_game_module(player->rid); /* Send it the current list of side/player assignments. */ for (i = 0; i < numsides; ++i) { send_assignment(player->rid, assignments[i].side, assignments[i].player); } /* Make it run everything up to the player setup phase. */ send_randstate(player->rid); /* If errors, should set player rid back to zero */ } void net_update_player(Player *player) { sprintf(spbuf, "a%d update %s", player->id, player_desig(player)); broadcast_packet(spbuf); } /* Given the id of a remote program that has just connected, download our own current state, as a module. */ void download_game_module(int rid) { int rslt; Module *module; if (1 /* sending name only */) { sprintf(spbuf, "g %s", mainmodule->name); send_packet(rid, spbuf); /* (should send some sort of state checksum also) */ return; } /* The indepside might not have been filled in yet (such as when connecting before sides/players have been set up), but we need it to be correct before going into write_game_module. */ if (numtotsides == 0) create_side(); /* Record the rid in a global so the module-writing code knows where to send all the packets. */ tmprid = rid; send_packet(rid, "gameModule"); module = create_game_module("*download*"); copy_module(module, mainmodule); /* This module is not associated with a file. */ module->filename = NULL; /* The module will sent in its entirety, so suppress any attempt to load a base module. */ module->basemodulename = NULL; module->compress_tables = TRUE; module->compress_layers = TRUE; module->def_all = TRUE; /* for now */ downloading = TRUE; rslt = write_game_module(module); downloading = FALSE; send_packet(rid, "\neludoMemag\n"); tmprid = 0; } #ifndef MAC #define DOWNLOADPACKETSIZE 200 #else #define DOWNLOADPACKETSIZE 40 #endif static char *notherbuf; void add_to_packet(char *str) { if (notherbuf == NULL) notherbuf = xmalloc(DOWNLOADPACKETSIZE + 10); if (strlen(str) + strlen(notherbuf) > DOWNLOADPACKETSIZE) { /* No room in the packet, send it and start on a new one. */ send_packet(tmprid, notherbuf); notherbuf[0] = '\0'; /* If the string is long, break it into multiple packets. */ while (strlen(str) > DOWNLOADPACKETSIZE) { strncpy(notherbuf, str, DOWNLOADPACKETSIZE); notherbuf[DOWNLOADPACKETSIZE] = '\0'; send_packet(tmprid, notherbuf); notherbuf[0] = '\0'; str += DOWNLOADPACKETSIZE; } } strcat(notherbuf, str); } void flush_write(void) { if (!empty_string(notherbuf)) { send_packet(tmprid, notherbuf); notherbuf[0] = '\0'; } } /* Make sure every remote program has the same game that we do. */ void broadcast_game_module(void) { int rid; for_all_remotes(rid) { if (rid != my_rid) { download_game_module(rid); } } } void broadcast_start_variant_setup(void) { sprintf(spbuf, "vSTART"); broadcast_packet(spbuf); } void broadcast_variants_chosen(void) { /* Once variants have been chosen, programs will advance to making trial assignments immediately, so make sure randstates are in sync first. */ sprintf(spbuf, "R%ld", randstate); broadcast_packet(spbuf); sprintf(spbuf, "vOK"); broadcast_packet(spbuf); } void broadcast_start_player_setup(void) { sprintf(spbuf, "aSTART"); broadcast_packet(spbuf); } void broadcast_players_assigned(void) { sprintf(spbuf, "aOK"); broadcast_packet(spbuf); } void send_randstate(int rid) { sprintf(spbuf, "R%ld", randstate); send_packet(rid, spbuf); } void broadcast_game_checksum(void) { sprintf(spbuf, "Z%d %d", my_rid, game_checksum()); broadcast_packet(spbuf); } void send_game_checksum_error(int rid, int my_csum, int master_csum) { sprintf(spbuf, "Echecksum %d %d %d", my_rid, my_csum, master_csum); send_packet(rid, spbuf); } time_t last_checksum_time; int net_run_game(int maxactions) { int oldsernum, oldstate, sendcheck, oldcsum, newcsum, numdone = 0; time_t now; if (my_rid == master_rid) { if (numremotes > 0) { oldsernum = g_run_serial_number(); oldstate = randstate; /* Send occasional checksums at random times. */ sendcheck = FALSE; if (0 /* for now */) { time(&now); sendcheck = (idifftime(now, last_checksum_time) >= 2); if (sendcheck) last_checksum_time = now; } if (sendcheck) broadcast_game_checksum(); oldcsum = game_checksum(); } /* This is where the master's real run_game call happens. */ numdone = run_game(maxactions); if (numremotes > 0) { if (numdone > 0) { sprintf(spbuf, "Za%d %d", my_rid, oldcsum); broadcast_packet(spbuf); sprintf(spbuf, "X%d %d %d", maxactions, oldsernum, oldstate); broadcast_packet(spbuf); broadcast_game_checksum(); } else { newcsum = game_checksum(); if (newcsum != oldcsum) notify_all("Checksum changed %d -> %d when numdone == 0", oldcsum, newcsum); } } } return numdone; } void net_request_additional_side(char *playerspec) { if (my_rid == master_rid) { request_additional_side(playerspec); } if (numremotes > 0) { sprintf(spbuf, "Padd %s", (playerspec ? playerspec : "")); broadcast_packet(spbuf); } } void net_send_message(Side *side, SideMask sidemask, char *str) { if (my_rid == master_rid) { send_message(side, sidemask, str); } if (numremotes > 0) { sprintf(spbuf, "M%d %d %s", side_number(side), sidemask, str); broadcast_packet(spbuf); } } static void broadcast_command_5(char *cmd, int a1, int a2, int a3, int a4, int a5) { sprintf(spbuf, "C%s %d %d %d %d %d", cmd, a1, a2, a3, a4, a5); broadcast_packet(spbuf); } void net_resign_game(Side *side, Side *side2) { if (my_rid == master_rid) { resign_game(side, side2); } if (numremotes > 0) { broadcast_side_property(side, "resign", (side2 ? side_number(side2) : 0)); } } /* Side property tweaking. */ void net_set_side_name(Side *side, Side *side2, char *newname) { if (my_rid == master_rid) { set_side_name(side, side2, newname); } if (numremotes > 0) { broadcast_side_str_property(side2, "name", newname); } } void net_set_side_longname(Side *side, Side *side2, char *newname) { if (my_rid == master_rid) { set_side_longname(side, side2, newname); } if (numremotes > 0) { broadcast_side_str_property(side2, "longname", newname); } } void net_set_side_shortname(Side *side, Side *side2, char *newname) { if (my_rid == master_rid) { set_side_shortname(side, side2, newname); } if (numremotes > 0) { broadcast_side_str_property(side2, "shortname", newname); } } void net_set_side_noun(Side *side, Side *side2, char *newname) { if (my_rid == master_rid) { set_side_noun(side, side2, newname); } if (numremotes > 0) { broadcast_side_str_property(side2, "noun", newname); } } void net_set_side_pluralnoun(Side *side, Side *side2, char *newname) { if (my_rid == master_rid) { set_side_pluralnoun(side, side2, newname); } if (numremotes > 0) { broadcast_side_str_property(side2, "pluralnoun", newname); } } void net_set_side_adjective(Side *side, Side *side2, char *newname) { if (my_rid == master_rid) { set_side_adjective(side, side2, newname); } if (numremotes > 0) { broadcast_side_str_property(side2, "adjective", newname); } } void net_set_side_emblemname(Side *side, Side *side2, char *newname) { if (my_rid == master_rid) { set_side_emblemname(side, side2, newname); } if (numremotes > 0) { broadcast_side_str_property(side2, "emblemname", newname); } } void net_set_side_colorscheme(Side *side, Side *side2, char *newname) { if (my_rid == master_rid) { set_side_colorscheme(side, side2, newname); } if (numremotes > 0) { broadcast_side_str_property(side2, "colorscheme", newname); } } void net_finish_turn(Side *side) { if (my_rid == master_rid) { finish_turn(side); } if (numremotes > 0) { broadcast_side_property(side, "fin", 0); } } void net_set_trust(Side *side, Side *side2, int val) { if (my_rid == master_rid) { set_trust(side, side2, val); } if (numremotes > 0) { broadcast_side_property_2(side, "trust", side_number(side2), val); } } void net_set_controlled_by(Side *side, Side *side2, int val) { if (my_rid == master_rid) { set_controlled_by(side, side2, val); } if (numremotes > 0) { broadcast_side_property_2(side, "controlledby", side_number(side2), val); } } void net_set_autofinish(Side *side, int value) { if (my_rid == master_rid) { set_autofinish(side, value); } if (numremotes > 0) { broadcast_side_property(side, "af", value); } } void net_set_autoresearch(Side *side, int value) { if (my_rid == master_rid) { set_autoresearch(side, value); } if (numremotes > 0) { broadcast_side_property(side, "ar", value); } } void net_set_willing_to_save(Side *side, int flag) { if (my_rid == master_rid) { set_willing_to_save(side, flag); } if (numremotes > 0) { broadcast_side_property(side, "save", flag); } } void net_set_willing_to_draw(Side *side, int flag) { if (my_rid == master_rid) { set_willing_to_draw(side, flag); } if (numremotes > 0) { broadcast_side_property(side, "draw", flag); } } void net_set_side_self_unit(Side *side, Unit *unit) { if (my_rid == master_rid) { set_side_self_unit(side, unit); } if (numremotes > 0) { broadcast_side_property(side, "self", (unit ? unit->id : 0)); } } void net_set_side_ai(Side *side, char *typename) { if (my_rid == master_rid) { set_side_ai(side, typename); } if (numremotes > 0) { if (typename == NULL) typename = ""; broadcast_side_str_property(side, "ai", typename); } } void net_set_doctrine(Side *side, char *spec) { if (my_rid == master_rid) { set_doctrine(side, spec); } if (numremotes > 0) { if (spec == NULL) spec = ""; broadcast_side_str_property(side, "doctrine", spec); } } void net_set_side_research(Side *side, int a) { if (my_rid == master_rid) { set_side_research(side, a); } if (numremotes > 0) { broadcast_side_property(side, "research", a); } } #ifdef DESIGNERS void net_become_designer(Side *side) { if (my_rid == master_rid) { become_designer(side); } if (numremotes > 0) { broadcast_side_property(side, "designer", TRUE); } } void net_become_nondesigner(Side *side) { if (my_rid == master_rid) { become_nondesigner(side); } if (numremotes > 0) { broadcast_side_property(side, "designer", FALSE); } } void net_paint_view(Side *side, int x, int y, int r, int tview, int uview) { if (my_rid == master_rid) { paint_view(side, x, y, r, tview, uview); } if (numremotes > 0) { sprintf(spbuf, "D%d view %d %d %d %d %d %d", side_number(side), side_number(side), x, y, r, tview, uview); broadcast_packet(spbuf); } } #endif /* DESIGNERS */ static void broadcast_side_property(Side *side, char *prop, int val) { sprintf(spbuf, "S%d %s %d", side_number(side), prop, val); broadcast_packet(spbuf); } static void broadcast_side_property_2(Side *side, char *prop, int val, int val2) { sprintf(spbuf, "S%d %s %d %d", side_number(side), prop, val, val2); broadcast_packet(spbuf); } static void broadcast_side_str_property(Side *side, char *prop, char *val) { sprintf(spbuf, "S%d %s %s", side_number(side), prop, val); broadcast_packet(spbuf); } /* Unit property tweaking. */ void net_set_unit_name(Side *side, Unit *unit, char *newname) { if (my_rid == master_rid) { set_unit_name(side, unit, newname); } if (numremotes > 0) { broadcast_unit_str_property(side, unit, "name", newname); } } void net_set_unit_plan_type(Side *side, Unit *unit, int type) { if (my_rid == master_rid) { set_unit_plan_type(side, unit, type); } if (numremotes > 0) { broadcast_unit_property(side, unit, "plan", type); } } void net_set_unit_asleep(Side *side, Unit *unit, int flag, int recurse) { if (my_rid == master_rid) { set_unit_asleep(side, unit, flag, recurse); } if (numremotes > 0) { broadcast_unit_property_2(side, unit, "sleep", flag, recurse); } } void net_set_unit_reserve(Side *side, Unit *unit, int flag, int recurse) { if (my_rid == master_rid) { set_unit_reserve(side, unit, flag, recurse); } if (numremotes > 0) { broadcast_unit_property_2(side, unit, "resv", flag, recurse); } } void net_set_unit_main_goal(Side *side, Unit *unit, Goal *goal) { if (my_rid == master_rid) { set_unit_main_goal(side, unit, goal); } if (numremotes > 0) { broadcast_unit_property_5(side, unit, "maingoal", (goal ? goal->type : 0), (goal ? goal->args[0] : 0), (goal ? goal->args[1] : 0), (goal ? goal->args[2] : 0), (goal ? goal->args[3] : 0)); } } void net_set_unit_curadvance(Side *side, Unit *unit, int a) { if (my_rid == master_rid) { set_unit_curadvance(side, unit, a); } if (numremotes > 0) { broadcast_unit_property(side, unit, "curadvance", a); } } void net_set_unit_autoplan(Side *side, Unit *unit, int flag) { if (my_rid == master_rid) { set_unit_autoplan(side, unit, flag); } if (numremotes > 0) { broadcast_unit_property(side, unit, "autoplan", flag); } } void net_set_unit_autoresearch(Side *side, Unit *unit, int flag) { if (my_rid == master_rid) { set_unit_autoresearch(side, unit, flag); } if (numremotes > 0) { broadcast_unit_property(side, unit, "autoresearch", flag); } } void net_set_unit_autobuild(Side *side, Unit *unit, int flag) { if (my_rid == master_rid) { set_unit_autobuild(side, unit, flag); } if (numremotes > 0) { broadcast_unit_property(side, unit, "autobuild", flag); } } void net_set_unit_buildingdone(Side *side, Unit *unit, int flag) { if (my_rid == master_rid) { set_unit_buildingdone(side, unit, flag); } if (numremotes > 0) { broadcast_unit_property(side, unit, "buildingdone", flag); } } void net_set_unit_researchdone(Side *side, Unit *unit, int flag) { if (my_rid == master_rid) { set_unit_researchdone(side, unit, flag); } if (numremotes > 0) { broadcast_unit_property(side, unit, "researchdone", flag); } } void net_set_unit_waiting_for_transport(Side *side, Unit *unit, int flag) { if (my_rid == master_rid) { set_unit_waiting_for_transport(side, unit, flag); } if (numremotes > 0) { broadcast_unit_property(side, unit, "waittrans", flag); } } void net_wake_unit(Side *side, Unit *unit, int wakeocc) { if (my_rid == master_rid) { wake_unit(side, unit, wakeocc); } if (numremotes > 0) { broadcast_command_5("wake-unit", side_number(side), unit->id, wakeocc, 0, 0); } } void net_wake_area(Side *side, int x, int y, int n, int occs) { if (my_rid == master_rid) { wake_area(side, x, y, n, occs); } if (numremotes > 0) { broadcast_command_5("wake-area", side_number(side), x, y, n, occs); } } void net_set_unit_ai_control(Side *side, Unit *unit, int flag, int recurse) { if (my_rid == master_rid) { set_unit_ai_control(side, unit, flag, recurse); } if (numremotes > 0) { broadcast_unit_property_2(side, unit, "ai", flag, recurse); } } int net_clear_task_agenda(Side *side, Unit *unit) { if (my_rid == master_rid) { clear_task_agenda(unit->plan); } if (numremotes > 0) { broadcast_unit_property(side, unit, "clra", 0); } return 0; } void net_force_replan(Side *side, Unit *unit, int passive_only) { if (my_rid == master_rid) { force_replan(side, unit, passive_only); } if (numremotes > 0) { broadcast_unit_property(side, unit, "forcereplan", passive_only); } } int net_disband_unit(Side *side, Unit *unit) { if (my_rid == master_rid) { return disband_unit(side, unit); } if (numremotes > 0) { broadcast_unit_property(side, unit, "disband", 0); } return 0; } void net_set_formation(Unit *unit, Unit *leader, int x, int y, int dist, int flex) { if (my_rid == master_rid) { set_formation(unit, leader, x, y, dist, flex); } if (numremotes > 0) { broadcast_unit_property_5(unit->side, unit, "formation", (leader ? leader->id : 0), x, y, dist, flex); } } void net_delay_unit(Unit *unit, int flag) { if (my_rid == master_rid) { delay_unit(unit, flag); } if (numremotes > 0) { broadcast_unit_property(unit->side, unit, "delay", flag); } } #ifdef DESIGNERS /* A designer can call this to create an arbitrary unit during the game. */ Unit * net_designer_create_unit(Side *side, int u, int s, int x, int y) { Unit *rslt = NULL; if (my_rid == master_rid) { rslt = designer_create_unit(side, u, s, x, y); } if (numremotes > 0) { broadcast_command_5("designer-create-unit", side_number(side), u, s, x, y); } return rslt; } /* Move a unit to a given location instantly, with all sides observing. This is for use by designers only! */ int net_designer_teleport(Unit *unit, int x, int y, Unit *other) { int rslt = 0; if (my_rid == master_rid) { rslt = designer_teleport(unit, x, y, other); } if (numremotes > 0) { broadcast_command_5("designer-teleport", unit->id, x, y, (other ? other->id : 0), 0); } return rslt; } int net_designer_change_side(Unit *unit, Side *side) { int rslt = 0; if (my_rid == master_rid) { rslt = designer_change_side(unit, side); } if (numremotes > 0) { broadcast_command_5("designer-change-side", unit->id, side_number(side), 0, 0, 0); } return rslt; } int net_designer_disband(Unit *unit) { int rslt = 0; if (my_rid == master_rid) { rslt = designer_disband(unit); } if (numremotes > 0) { broadcast_command_5("designer-disband", unit->id, 0, 0, 0, 0); } return rslt; } #endif /* DESIGNERS */ Feature * net_create_feature(char *typename, char *name) { Feature *rslt = NULL; if (my_rid == master_rid) { rslt = create_feature(typename, name); } if (numremotes > 0) { run_error("net"); } return rslt; } void net_set_feature_type_name(Feature *feature, char *typename) { if (my_rid == master_rid) { set_feature_type_name(feature, typename); } if (numremotes > 0) { run_error("net"); } } void net_set_feature_name(Feature *feature, char *name) { if (my_rid == master_rid) { set_feature_name(feature, name); } if (numremotes > 0) { run_error("net"); } } void net_destroy_feature(Feature *feature) { if (my_rid == master_rid) { destroy_feature(feature); } if (numremotes > 0) { run_error("net"); } } void net_renumber_features(void) { if (my_rid == master_rid) { renumber_features(); } if (numremotes > 0) { run_error("net"); } } static void broadcast_unit_property(Side *side, Unit *unit, char *prop, int val) { char buf[BUFSIZE]; sprintf(buf, "U%d %d %s %d", side_number(side), unit->id, prop, val); broadcast_packet(buf); } static void broadcast_unit_property_2(Side *side, Unit *unit, char *prop, int val, int val2) { char buf[BUFSIZE]; sprintf(buf, "U%d %d %s %d %d", side_number(side), unit->id, prop, val, val2); broadcast_packet(buf); } static void broadcast_unit_property_5(Side *side, Unit *unit, char *prop, int val, int val2, int val3, int val4, int val5) { char buf[BUFSIZE]; sprintf(buf, "U%d %d %s %d %d %d %d %d", side_number(side), unit->id, prop, val, val2, val3, val4, val5); broadcast_packet(buf); } static void broadcast_unit_str_property(Side *side, Unit *unit, char *prop, char *val) { char buf[BUFSIZE]; sprintf(buf, "U%d %d %s %s", side_number(side), unit->id, prop, val); broadcast_packet(buf); } /* Action networking. */ void broadcast_next_action(Unit *unit) { int j, atype, n; char buf[BUFSIZE]; if (numremotes <= 0) return; atype = unit->act->nextaction.type; sprintf(buf, "A%d", unit->id); if (unit->id != unit->act->nextaction.actee) { tprintf(buf, "/%d", unit->act->nextaction.actee); } tprintf(buf, " %d", unit->act->nextaction.type); n = strlen(actiondefns[atype].argtypes); for (j = 0; j < n; ++j) { tprintf(buf, " %d", unit->act->nextaction.args[j]); } broadcast_packet(buf); if (my_rid != master_rid) { /* Bash the local action, wait for master to send it back in the correct order. */ unit->act->nextaction.type = ACTION_NONE; } } /* From actions.c. */ int net_prep_none_action(Unit *unit, Unit *unit2) { int rslt; rslt = prep_none_action(unit, unit2); broadcast_next_action(unit); return rslt; } int net_prep_produce_action(unit, unit2, m, n) Unit *unit, *unit2; int m, n; { int rslt; rslt = prep_produce_action(unit, unit2, m, n); broadcast_next_action(unit); return rslt; } int net_prep_extract_action(unit, unit2, x, y, m, n) Unit *unit, *unit2; int x, y, m, n; { int rslt; rslt = prep_extract_action(unit, unit2, x, y, m, n); broadcast_next_action(unit); return rslt; } int net_prep_develop_action(Unit *unit, Unit *unit2, int u3) { int rslt; rslt = prep_develop_action(unit, unit2, u3); broadcast_next_action(unit); return rslt; } int net_prep_toolup_action(Unit *unit, Unit *unit2, int u3) { int rslt; rslt = prep_toolup_action(unit, unit2, u3); broadcast_next_action(unit); return rslt; } int net_prep_create_in_action(Unit *unit, Unit *unit2, int u3, Unit *dest) { int rslt; rslt = prep_create_in_action(unit, unit2, u3, dest); broadcast_next_action(unit); return rslt; } int net_prep_create_at_action(Unit *unit, Unit *unit2, int u3, int x, int y, int z) { int rslt; rslt = prep_create_at_action(unit, unit2, u3, x, y, z); broadcast_next_action(unit); return rslt; } int net_prep_build_action(Unit *unit, Unit *unit2, Unit *newunit) { int rslt; rslt = prep_build_action(unit, unit2, newunit); broadcast_next_action(unit); return rslt; } int net_prep_repair_action(unit, unit2, unit3) Unit *unit, *unit2, *unit3; { int rslt; rslt = prep_repair_action(unit, unit2, unit3); broadcast_next_action(unit); return rslt; } int net_prep_disband_action(unit, unit2) Unit *unit, *unit2; { int rslt; rslt = prep_disband_action(unit, unit2); broadcast_next_action(unit); return rslt; } int net_prep_transfer_part_action(unit, unit2, parts, unit3) Unit *unit, *unit2, *unit3; int parts; { int rslt; rslt = prep_transfer_part_action(unit, unit2, parts, unit3); broadcast_next_action(unit); return rslt; } int net_prep_change_type_action(unit, unit2, u3) Unit *unit, *unit2; int u3; { int rslt; rslt = prep_change_type_action(unit, unit2, u3); broadcast_next_action(unit); return rslt; } int net_prep_change_side_action(unit, unit2, side) Unit *unit, *unit2; Side *side; { int rslt; rslt = prep_change_side_action(unit, unit2, side); broadcast_next_action(unit); return rslt; } int net_prep_alter_cell_action(unit, unit2, x, y, t) Unit *unit, *unit2; int x, y, t; { int rslt; rslt = prep_alter_cell_action(unit, unit2, x, y, t); broadcast_next_action(unit); return rslt; } int net_prep_add_terrain_action(unit, unit2, x, y, dir, t) Unit *unit, *unit2; int x, y, dir, t; { int rslt; rslt = prep_add_terrain_action(unit, unit2, x, y, dir, t); broadcast_next_action(unit); return rslt; } int net_prep_remove_terrain_action(unit, unit2, x, y, dir, t) Unit *unit, *unit2; int x, y, dir, t; { int rslt; rslt = prep_remove_terrain_action(unit, unit2, x, y, dir, t); broadcast_next_action(unit); return rslt; } /* From combat.c. */ int net_prep_attack_action(Unit *unit, Unit *unit2, Unit *defender, int n) { int rslt; rslt = prep_attack_action(unit, unit2, defender, n); broadcast_next_action(unit); return rslt; } int net_prep_overrun_action(Unit *unit, Unit *unit2, int x, int y, int z, int n) { int rslt; rslt = prep_overrun_action(unit, unit2, x, y, z, n); broadcast_next_action(unit); return rslt; } int net_prep_fire_at_action(Unit *unit, Unit *unit2, Unit *unit3, int m) { int rslt; rslt = prep_fire_at_action(unit, unit2, unit3, m); broadcast_next_action(unit); return rslt; } int net_prep_fire_into_action(Unit *unit, Unit *unit2, int x, int y, int z, int m) { int rslt; rslt = prep_fire_into_action(unit, unit2, x, y, z, m); broadcast_next_action(unit); return rslt; } int net_prep_capture_action(Unit *unit, Unit *unit2, Unit *unit3) { int rslt; rslt = prep_capture_action(unit, unit2, unit3); broadcast_next_action(unit); return rslt; } int net_prep_detonate_action(Unit *unit, Unit *unit2, int x, int y, int z) { int rslt; rslt = prep_detonate_action(unit, unit2, x, y, z); broadcast_next_action(unit); return rslt; } /* From move.c. */ int net_prep_move_action(Unit *unit, Unit *unit2, int x, int y, int z) { int rslt; rslt = prep_move_action(unit, unit2, x, y, z); broadcast_next_action(unit); return rslt; } int net_prep_enter_action(Unit *unit, Unit *unit2, Unit *unit3) { int rslt; rslt = prep_enter_action(unit, unit2, unit3); broadcast_next_action(unit); return rslt; } /* Task networking. */ #define CLEAR_AGENDA 99 static Task *tmptask; /* (should remove, never used) */ void net_add_task(unit, pos, task) Unit *unit; int pos; Task *task; { if (my_rid == master_rid) { add_task(unit, pos, task); } if (numremotes > 0) { broadcast_add_task(unit, pos, task); } } void net_set_capture_task(Unit *unit, int x, int y, int u, int s) { if (my_rid == master_rid) { set_capture_task(unit, x, y, u, s); } if (numremotes > 0) { tmptask = create_capture_task(unit, x, y, u, s); broadcast_add_task(unit, CLEAR_AGENDA, tmptask); free_task(tmptask); } } void net_set_occupy_task(Unit *unit, Unit *transport) { if (my_rid == master_rid) { set_occupy_task(unit, transport); } if (numremotes > 0) { tmptask = create_occupy_task(unit, transport); broadcast_add_task(unit, CLEAR_AGENDA, tmptask); free_task(tmptask); } } void net_set_collect_task(Unit *unit, int m, int x, int y) { if (my_rid == master_rid) { set_collect_task(unit, m, x, y); } if (numremotes > 0) { tmptask = create_collect_task(unit, m, x, y); broadcast_add_task(unit, CLEAR_AGENDA, tmptask); free_task(tmptask); } } void net_set_move_to_task(Unit *unit, int x, int y, int dist) { if (my_rid == master_rid) { set_move_to_task(unit, x, y, dist); } if (numremotes > 0) { tmptask = create_move_to_task(unit, x, y, dist); broadcast_add_task(unit, CLEAR_AGENDA, tmptask); free_task(tmptask); } } void net_push_move_to_task(Unit *unit, int x, int y, int dist) { if (my_rid == master_rid) { push_move_to_task(unit, x, y, dist); } if (numremotes > 0) { tmptask = create_move_to_task(unit, x, y, dist); broadcast_add_task(unit, 0, tmptask); free_task(tmptask); } } void net_set_move_dir_task(Unit *unit, int dir, int n) { if (my_rid == master_rid) { set_move_dir_task(unit, dir, n); } if (numremotes > 0) { tmptask = create_move_dir_task(unit, dir, n); broadcast_add_task(unit, CLEAR_AGENDA, tmptask); free_task(tmptask); } } void net_set_build_task(Unit *unit, int u2, int run, int x, int y) { if (my_rid == master_rid) { set_build_task(unit, u2, run, x, y); } if (numremotes > 0) { tmptask = create_build_task(unit, u2, run, x, y); broadcast_add_task(unit, CLEAR_AGENDA, tmptask); free_task(tmptask); } } /* Same as above but handles cases where we resume building an incomplete occ. */ void net_resume_build_task(Unit *unit, Unit *unit2, int run, int x, int y) { if (my_rid == master_rid) { resume_build_task(unit, unit2, run, x, y); } if (numremotes > 0) { tmptask = create_build_task(unit, unit2->type, run, x, y); tmptask->args[1] = unit2->id; broadcast_add_task(unit, CLEAR_AGENDA, tmptask); free_task(tmptask); } } void net_push_build_task(Unit *unit, int u2, int run, int x, int y) { if (my_rid == master_rid) { push_build_task(unit, u2, run, x, y); } if (numremotes > 0) { tmptask = create_build_task(unit, u2, run, x, y); broadcast_add_task(unit, 0, tmptask); free_task(tmptask); } } void net_push_develop_task(Unit *unit, int u2, int n) { if (my_rid == master_rid) { push_develop_task(unit, u2, n); } if (numremotes > 0) { tmptask = create_develop_task(unit, u2, n); broadcast_add_task(unit, 0, tmptask); free_task(tmptask); } } void net_push_hit_unit_task(Unit *unit, int x, int y, int u, int s) { if (my_rid == master_rid) { push_hit_unit_task(unit, x, y, u, s); } if (numremotes > 0) { tmptask = create_hit_unit_task(unit, x, y, u, s); broadcast_add_task(unit, 0, tmptask); free_task(tmptask); } } void net_set_hit_unit_task(Unit *unit, int x, int y, int u, int s) { if (my_rid == master_rid) { set_hit_unit_task(unit, x, y, u, s); } if (numremotes > 0) { tmptask = create_hit_unit_task(unit, x, y, u, s); broadcast_add_task(unit, CLEAR_AGENDA, tmptask); free_task(tmptask); } } void net_set_disband_task(Unit *unit) { if (my_rid == master_rid) { set_disband_task(unit); } if (numremotes > 0) { tmptask = create_task(TASK_DISBAND); broadcast_add_task(unit, CLEAR_AGENDA, tmptask); free_task(tmptask); } } void net_set_resupply_task(Unit *unit, int m) { if (my_rid == master_rid) { set_resupply_task(unit, m); } if (numremotes > 0) { tmptask = create_resupply_task(unit, m); broadcast_add_task(unit, CLEAR_AGENDA, tmptask); free_task(tmptask); } } void net_push_occupy_task(Unit *unit, Unit *transport) { if (my_rid == master_rid) { push_occupy_task(unit, transport); } if (numremotes > 0) { tmptask = create_occupy_task(unit, transport); broadcast_add_task(unit, 0, tmptask); free_task(tmptask); } } void net_push_pickup_task(Unit *unit, Unit *occ) { if (my_rid == master_rid) { push_pickup_task(unit, occ); } if (numremotes > 0) { tmptask = create_pickup_task(unit, occ); broadcast_add_task(unit, 0, tmptask); free_task(tmptask); } } void net_push_produce_task(Unit *unit, int m, int n) { if (my_rid == master_rid) { push_produce_task(unit, m, n); } if (numremotes > 0) { tmptask = create_produce_task(unit, m, n); broadcast_add_task(unit, 0, tmptask); free_task(tmptask); } } void net_set_sentry_task(Unit *unit, int n) { if (my_rid == master_rid) { set_sentry_task(unit, n); } if (numremotes > 0) { tmptask = create_sentry_task(unit, n); broadcast_add_task(unit, CLEAR_AGENDA, tmptask); free_task(tmptask); } } void net_push_sentry_task(Unit *unit, int n) { if (my_rid == master_rid) { push_sentry_task(unit, n); } if (numremotes > 0) { tmptask = create_sentry_task(unit, n); broadcast_add_task(unit, 0, tmptask); free_task(tmptask); } } static void broadcast_add_task(Unit *unit, int pos, Task *task) { int numargs, i; char buf[BUFSIZE], *argtypes; sprintf(buf, "T%d %d %d %d %d", unit->id, pos, task->type, task->execnum, task->retrynum); argtypes = taskdefns[task->type].argtypes; numargs = strlen(argtypes); for (i = 0; i < numargs; ++i) tprintf(buf, " %d", task->args[i]); broadcast_packet(buf); } /* World tweaking. */ #ifdef DESIGNERS void net_paint_cell(Side *side, int x, int y, int r, int t) { if (my_rid == master_rid) { paint_cell(side, x, y, r, t); } if (numremotes > 0) { broadcast_layer_change("cell", side, x, y, r, t, 0, 0); } } void net_paint_border(Side *side, int x, int y, int dir, int t, int mode) { if (my_rid == master_rid) { paint_border(side, x, y, dir, t, mode); } if (numremotes > 0) { broadcast_layer_change("bord", side, x, y, dir, t, mode, 0); } } void net_paint_connection(Side *side, int x, int y, int dir, int t, int mode) { if (my_rid == master_rid) { paint_connection(side, x, y, dir, t, mode); } if (numremotes > 0) { broadcast_layer_change("conn", side, x, y, dir, t, mode, 0); } } void net_paint_coating(Side *side, int x, int y, int r, int t, int depth) { if (my_rid == master_rid) { paint_coating(side, x, y, r, t, depth); } if (numremotes > 0) { broadcast_layer_change("coat", side, x, y, r, t, depth, 0); } } void net_paint_people(Side *side, int x, int y, int r, int s) { if (my_rid == master_rid) { paint_people(side, x, y, r, s); } if (numremotes > 0) { broadcast_layer_change("peop", side, x, y, r, s, 0, 0); } } void net_paint_control(Side *side, int x, int y, int r, int s) { if (my_rid == master_rid) { paint_control(side, x, y, r, s); } if (numremotes > 0) { broadcast_layer_change("ctrl", side, x, y, r, s, 0, 0); } } void net_paint_feature(Side *side, int x, int y, int r, int f) { if (my_rid == master_rid) { paint_feature(side, x, y, r, f); } if (numremotes > 0) { broadcast_layer_change("feat", side, x, y, r, f, 0, 0); } } void net_paint_elevation(Side *side, int x, int y, int r, int code, int elev, int vary) { if (my_rid == master_rid) { paint_elevation(side, x, y, r, code, elev, vary); } if (numremotes > 0) { broadcast_layer_change("elev", side, x, y, r, code, elev, vary); } } void net_paint_temperature(Side *side, int x, int y, int r, int temp) { if (my_rid == master_rid) { paint_temperature(side, x, y, r, temp); } if (numremotes > 0) { broadcast_layer_change("temp", side, x, y, r, temp, 0, 0); } } void net_paint_material(Side *side, int x, int y, int r, int m, int amt) { if (my_rid == master_rid) { paint_material(side, x, y, r, m, amt); } if (numremotes > 0) { broadcast_layer_change("m", side, x, y, r, m, amt, 0); } } void net_paint_clouds(Side *side, int x, int y, int r, int cloudtype, int bot, int hgt) { if (my_rid == master_rid) { paint_clouds(side, x, y, r, cloudtype, bot, hgt); } if (numremotes > 0) { broadcast_layer_change("clouds", side, x, y, r, cloudtype, 0, 0); broadcast_layer_change("cloud-bottoms", side, x, y, r, bot, 0, 0); broadcast_layer_change("cloud-heights", side, x, y, r, hgt, 0, 0); } } void net_paint_winds(Side *side, int x, int y, int r, int dir, int force) { if (my_rid == master_rid) { paint_winds(side, x, y, r, dir, force); } if (numremotes > 0) { broadcast_layer_change("wind", side, x, y, r, dir, force, 0); } } static void broadcast_layer_change(char *layername, Side *side, int x, int y, int a1, int a2, int a3, int a4) { char buf[BUFSIZE]; sprintf(buf, "W%s %d %d %d %d %d %d %d", layername, side->id, x, y, a1, a2, a3, a4); broadcast_packet(buf); } #endif /* DESIGNERS */ void send_quit(void) { /* (should have more error-handling, since may be called in bad situations) */ sprintf(spbuf, "Q%d", my_rid); broadcast_packet(spbuf); flush_outgoing_queue(); } /* Given a buffer with a packet in it, send it all the places that it should go. */ static void broadcast_packet(char *buf) { int rid; extern int in_run_game; if (in_run_game) { notify_all("Attempting to send \"%s\" while in run_game", buf); } if (my_rid == master_rid) { for_all_remotes(rid) { if (rid != my_rid) { send_packet(rid, buf); } } } else { /* Send to master; master will echo back eventually. */ send_packet(master_rid, buf); } } /* Given a packet to be sent, package it up (add header, embed escape chars, add checksum), send, and maybe wait for acknowledgement. */ int send_packet(int id, char *inbuf) { int i, j, csum, numtimeouts; char buf[BUFSIZE]; if (my_rid != master_rid && !sendnow) { /* Add packet to the outgoing queue. */ save_outgoing_packet(id, inbuf); Dprintf("OutQ: %d \"%s\"\n", id, inbuf); return TRUE; } Dprintf("Send: %d \"%s\"\n", id, inbuf); j = 0; csum = 0; buf[j++] = STARTPKT; /* Copy the input buffer into the packet, changing any special characters into escape sequences along the way. */ for (i = 0; inbuf[i] != '\0'; ++i) { if (inbuf[i] == STARTPKT) { buf[j++] = ESCAPEPKT; buf[j++] = STARTPKTESC; } else if (inbuf[i] == ENDPKT) { buf[j++] = ESCAPEPKT; buf[j++] = ENDPKTESC; } else if (inbuf[i] == ESCAPEPKT) { buf[j++] = ESCAPEPKT; buf[j++] = ESCAPEPKTESC; } else { buf[j++] = inbuf[i]; } csum += (unsigned char) inbuf[i]; } buf[j++] = ENDPKT; /* Add on the checksum, in hex. */ buf[j++] = tohex ((csum >> 4) & 0xf); buf[j++] = tohex (csum & 0xf); buf[j++] = '\0'; low_send(id, buf); /* Wait for the packet's receipt to be acknowledged. */ numtimeouts = 0; expecting_ack = TRUE; while (expecting_ack) { receive_data(30); if (!expecting_ack) break; if (timeout_warnings) notify_all("Timed out waiting for ack"); else notify_all("Timed out waiting for ack"); ++numtimeouts; if (hosting && my_rid == master_rid) { /* If we're the master, we MUST be assured that our packets have been received, since we've already changed our own state and can't undo it. So we will loop here for a long time, can only get out early if run_warning includes a quit option. */ if (numtimeouts > 200) run_error("Timed out %d times", numtimeouts); } else { /* If we're not the master, then we haven't actually changed our own state, and so we can continue on safely. The user will have to retry whatever state change was desired. */ expecting_ack = FALSE; return FALSE; } } return TRUE; } struct q_entry { int id; char *buf; struct q_entry *next; } *outgoing, *outgoing_last, *incoming, *incoming_last; static void save_outgoing_packet(int id, char *inbuf) { struct q_entry *entry; entry = (struct q_entry *) xmalloc (sizeof (struct q_entry)); entry->id = id; entry->buf = copy_string(inbuf); if (outgoing_last) { outgoing_last->next = entry; } else { outgoing = entry; } outgoing_last = entry; } void flush_outgoing_queue(void) { struct q_entry *entry; if (my_rid != master_rid) { sendnow = TRUE; for (entry = outgoing; entry != NULL; entry = entry->next) { send_packet(entry->id, entry->buf); } outgoing = outgoing_last = NULL; sendnow = FALSE; } } static void save_incoming_packet(int id, char *inbuf) { struct q_entry *entry; entry = (struct q_entry *) xmalloc (sizeof (struct q_entry)); entry->id = id; entry->buf = copy_string(inbuf); if (incoming_last) { incoming_last->next = entry; } else { incoming = entry; } incoming_last = entry; } void flush_incoming_queue(void) { struct q_entry *entry; for (entry = incoming; entry != NULL; entry = entry->next) { receive_packet(entry->id, entry->buf); } incoming = incoming_last = NULL; } static char *packetbuf; static char *rsltbuf; static int rsltid; #define PACKETBUFSIZE 1000 static time_t nothing_start_time; static int nothing_count; static int nothing_timeout; static void remove_chars(char *buf, int n) { int i; for (i = 0; i < PACKETBUFSIZE - n; ++i) buf[i] = buf[i + n]; } /* Handle the basic process of receiving packets. */ void receive_data(int timeout) { char buf[2000], *pktend; int retry = 10, gotsome, i, j, len, csum, pktcsum; /* (should have buffers for each remote prog) */ if (packetbuf == NULL) packetbuf = xmalloc(PACKETBUFSIZE); if (rsltbuf == NULL) rsltbuf = xmalloc(BUFSIZE); if (incoming != NULL && !expecting_ack) { flush_incoming_queue(); return; } while (retry > 0) { if (packetbuf[0] == STARTPKT) { pktend = strchr(packetbuf, ENDPKT); if (pktend != NULL && strlen(packetbuf) >= (pktend - packetbuf + 3) ) { /* We have accumulated a whole packet. */ len = pktend - packetbuf - 1; /* Copy out the packet's contents, handling escape chars and computing checksum along the way. */ j = 0; csum = 0; for (i = 1; i < len + 1; ++i) { if (packetbuf[i] == ESCAPEPKT && packetbuf[i+1] == STARTPKTESC) { rsltbuf[j++] = STARTPKT; ++i; } else if (packetbuf[i] == ESCAPEPKT && packetbuf[i+1] == ENDPKTESC) { rsltbuf[j++] = ENDPKT; ++i; } else if (packetbuf[i] == ESCAPEPKT && packetbuf[i+1] == ESCAPEPKTESC) { rsltbuf[j++] = ESCAPEPKT; ++i; } else { rsltbuf[j++] = packetbuf[i]; } csum += (unsigned char) rsltbuf[j-1]; } rsltbuf[j++] = '\0'; csum &= 0xff; /* Extract the checksum that was sent. */ pktcsum = fromhex(packetbuf[len + 2]) << 4; pktcsum |= fromhex(packetbuf[len + 3]); /* Remove the packet from the buffer. */ remove_chars(packetbuf, len + 4); if (csum != pktcsum && pktcsum != 0) { run_warning("checksum error, received %x, calced %x", pktcsum, csum); /* what to do with the packet? should require resend */ } if (expecting_ack) { Dprintf("Packet received while expecting ack, saving \"%s\"\n", rsltbuf); save_incoming_packet(rsltid, rsltbuf); Dprintf("Sending ack to unexpected packet\n"); low_send(rsltid, "+"); goto foo; } if (rsltid == 0) { run_warning("Packet from rid == 0, discarded \"%s\"", rsltbuf); goto foo; } Dprintf("Sending ack\n"); low_send(rsltid, "+"); /* The interpretation process needs to be last, because it may in turn cause more packets to be sent. */ receive_packet(rsltid, rsltbuf); retry = 0; } else { /* Part of a packet, but not the whole thing - continue waiting. */ --retry; goto foo; } } else if (packetbuf[0] == '+') { expecting_ack = FALSE; Dprintf("Ack received\n"); remove_chars(packetbuf, 1); retry = 0; } else if (strlen(packetbuf) > 0) { Dprintf("Removing garbage char '%c' (0x%x)\n", packetbuf[0], packetbuf[0]); remove_chars(packetbuf, 1); } else { foo: gotsome = low_receive(&rsltid, buf, BUFSIZE, timeout); /* Debugging printout. */ if (gotsome) { if (nothing_count > 0) { Dprintf("Rcvd nothing %d times (timeout %d secs)\n", nothing_count, nothing_timeout); nothing_count = 0; } Dprintf("Rcvd: %d \"%s\"\n", rsltid, (buf ? buf : "")); } else { time_t now; if (nothing_count == 0) time(¬hing_start_time); time(&now); if (idifftime(now, nothing_start_time) > 0 || timeout != nothing_timeout) { Dprintf("Rcvd nothing %d times (timeout %d secs)\n", nothing_count, nothing_timeout); nothing_count = 0; } else { ++nothing_count; nothing_timeout = timeout; } nothing_timeout = timeout; } if (gotsome) { if (strlen(packetbuf) + strlen(buf) > PACKETBUFSIZE) { run_warning("packet buffer overflow"); return; } strcat(packetbuf, buf); /* Go around and see if we have something now. */ --retry; } else { retry = 0; } } } } /* Given a received packet, take it apart and do what it says. */ void receive_packet(int id, char *buf) { int should_rebroadcast; Dprintf("From %d: \"%s\"\n", id, buf); if (downloading) { if (strcmp(buf, "\neludoMemag\n") == 0) { /* We've seen the end-of-module marker, now interpret. */ downloading = FALSE; mainmodule = get_game_module("*download*"); mainmodule->contents = downloadbuf; /* The sequence below is similar to that in load_game_module. */ open_module(mainmodule, FALSE); read_forms(mainmodule); close_module(mainmodule); /* Make all the cross-references right. */ patch_object_references(); check_game_validity(); start_variant_setup_stage(); } else { /* Make sure we have space with which to concatenate. */ if (strlen(downloadbuf) + strlen(buf) >= 200000) { init_error("Exceeded download buffer size"); return; } /* Concatenate this packet to the others. */ strcat(downloadbuf, buf); } } else { should_rebroadcast = TRUE; switch (buf[0]) { case 'A': receive_action(buf + 1); break; case 'C': receive_command(buf + 1); break; case 'E': receive_error(id, buf + 1); should_rebroadcast = FALSE; break; case 'M': receive_net_message(buf + 1); break; case 'P': receive_player_prop(buf + 1); break; case 'Q': receive_quit(buf + 1); break; case 'R': new_randstate = atoi(buf + 1); if (new_randstate != randstate) { Dprintf("Rand state change: %d -> %d\n", randstate, new_randstate); } else { Dprintf("Rand state matches\n"); } randstate = new_randstate; /* This is only issued by the master, no need to rebroadcast. */ should_rebroadcast = FALSE; break; case 'S': receive_side_prop(buf + 1); break; case 'T': receive_task(buf + 1); break; case 'U': receive_unit_prop(buf + 1); break; case 'V': if (strcmp(buf + 1, version_string()) != 0) { init_warning("Xconq versions \"%s\" and \"%s\" should not link up", buf + 1, version_string()); } should_rebroadcast = FALSE; break; case 'W': receive_world_prop(buf + 1); break; case 'X': receive_run_game(buf + 1); /* This is only issued by the master, no need to rebroadcast. */ should_rebroadcast = FALSE; break; case 'Z': receive_game_checksum(buf + 1); /* Checksums may be broadcast, but not automatically. */ should_rebroadcast = FALSE; break; case 'a': if (buf[1] == 'S' && buf[2] == 'T') { start_player_setup_stage(); /* This is only issued by the master, no need to rebroadcast. */ should_rebroadcast = FALSE; } else if (buf[1] == 'O' && buf[2] == 'K') { start_game_ready_stage(); /* This is only issued by the master, no need to rebroadcast. */ should_rebroadcast = FALSE; } else { receive_assignment_setting(buf + 1); } break; case 'c': receive_chat(buf + 1); break; case 'g': if (buf[1] == ' ') { Module *module = get_game_module(buf + 2); mainmodule = module; load_game_module(mainmodule, TRUE); } else { downloading = TRUE; /* (should use obstack here) */ if (downloadbuf == NULL) downloadbuf = xmalloc(200000); downloadbuf[0] = '\0'; } should_rebroadcast = FALSE; break; case 'j': send_remote_id(id); add_remote_program(id, buf + 1); /* Don't rebroadcast, the master will handle specially. */ should_rebroadcast = FALSE; break; case 'p': receive_remote_program(buf + 1); start_game_load_stage(); /* seems wrong? */ /* This is only issued by the master, no need to rebroadcast. */ should_rebroadcast = FALSE; break; case 'r': my_rid = atoi(buf + 1); /* This is only issued by the master, no need to rebroadcast. */ should_rebroadcast = FALSE; break; case 'v': if (buf[1] == 'S' && buf[2] == 'T') { start_variant_setup_stage(); /* This is only issued by the master, no need to rebroadcast. */ should_rebroadcast = FALSE; } else if (buf[1] == 'O' && buf[2] == 'K') { start_player_pre_setup_stage(); /* This is only issued by the master, no need to rebroadcast. */ should_rebroadcast = FALSE; } else { receive_variant_setting(buf + 1); } break; default: /* Since the protocol's purpose is to keep multiple executables' state in sync, every kind of packet must be understood. */ run_warning("Packet not recognized: \"%s\"\n", buf); break; } /* The master should rebroadcast most types of packets automatically, so all programs stay in sync. */ if (my_rid == master_rid && should_rebroadcast) broadcast_packet(buf); } } static void receive_net_message(char *str) { int id; char *nstr; SideMask sidemask; Side *side; id = strtol(str, &nstr, 10); side = side_n(id); str = nstr; sidemask = strtol(str, &nstr, 10); str = nstr; ++str; /* Note that this is the internal form of "send", which just copies to all local recipients. */ send_message(side, sidemask, str); } static void receive_command(char *str) { int i, args[5]; char *nstr, *argstr; Side *side; Unit *unit, *unit2; argstr = strchr(str, ' '); i = 0; while (*argstr != '\0' && i < 5) { args[i++] = strtol(argstr, &nstr, 10); argstr = nstr; } if (strncmp(str, "wake-unit ", 9) == 0) { side = side_n(args[0]); if (side == NULL) { return; } unit = find_unit(args[1]); if (unit == NULL) { return; } wake_unit(side, unit, args[2]); } else if (strncmp(str, "wake-area ", 9) == 0) { side = side_n(args[0]); if (side == NULL) { return; } wake_area(side, args[1], args[2], args[3], args[4]); } else if (strncmp(str, "designer-create-unit ", strlen("designer-create-unit ")) == 0) { side = side_n(args[0]); if (side == NULL) { return; } designer_create_unit(side, args[1], args[2], args[3], args[4]); } else if (strncmp(str, "designer-teleport ", strlen("designer-teleport ")) == 0) { unit = find_unit(args[0]); if (unit == NULL) { return; } unit2 = NULL; if (args[3] > 0) { unit2 = find_unit(args[3]); if (unit2 == NULL) { return; } } designer_teleport(unit, args[1], args[2], unit2); } else if (strncmp(str, "designer-change-side ", strlen("designer-change-side ")) == 0) { unit = find_unit(args[0]); if (unit == NULL) { return; } side = side_n(args[1]); designer_change_side(unit, side); } else if (strncmp(str, "designer-disband ", strlen("designer-disband ")) == 0) { unit = find_unit(args[0]); if (unit == NULL) { return; } designer_disband(unit); } else { run_warning("Unknown C packet \"%s\", ignoring", str); } } static void receive_action(char *str) { int unitid, acteeid, i; char *nstr; Unit *unit; Action tmpaction; unitid = strtol(str, &nstr, 10); str = nstr; if (*str == '/') { ++str; acteeid = strtol(str, &nstr, 10); str = nstr; } else { acteeid = unitid; } tmpaction.actee = acteeid; tmpaction.type = strtol(str, &nstr, 10); str = nstr; i = 0; while (*nstr != '\0' && i < 10) { tmpaction.args[i++] = strtol(str, &nstr, 10); str = nstr; } unit = find_unit(unitid); if (unit == NULL) { run_warning("Packet A refers to missing unit #%d, ignoring", unitid); return; } if (unit->act == NULL) { run_warning("Packet A refers to non-acting unit %s, ignoring", unit_desig(unit)); return; } /* Lay the action over what is already there; the next run_game should actually do it. */ unit->act->nextaction = tmpaction; } static void receive_task(char *str) { int unitid, pos, tasktype, i; char *nstr; Unit *unit; Task *task; unitid = strtol(str, &nstr, 10); str = nstr; pos = strtol(str, &nstr, 10); str = nstr; tasktype = strtol(str, &nstr, 10); str = nstr; task = create_task(tasktype); task->execnum = strtol(str, &nstr, 10); str = nstr; task->retrynum = strtol(str, &nstr, 10); str = nstr; i = 0; while (*nstr != '\0' && i < 10) { task->args[i++] = strtol(str, &nstr, 10); str = nstr; } unit = find_unit(unitid); if (unit == NULL) { run_warning("Packet T refers to missing unit #%d, ignoring", unitid); return; } if (unit->plan == NULL) { run_warning("Packet T refers to non-planning unit %s, ignoring", unit_desig(unit)); return; } add_task(unit, pos, task); } static void receive_player_prop(char *str) { int val, val2; char *nstr; Side *side; if (strncmp(str, "add ", 4) == 0) { str += 4; request_additional_side(str); } else if (strncmp(str, "assign ", 7) == 0) { str += 7; val = strtol(str, &nstr, 10); str = nstr; val2 = strtol(str, &nstr, 10); side = side_n(val); if (side != NULL) side->player = find_player(val2); } else { run_warning("Unknown P packet \"%s\", ignoring", str); } } static void receive_quit(char *str) { int rid; char *nstr; rid = strtol(str, &nstr, 10); Dprintf("Received quit from %d\n", rid); online[rid] = FALSE; quitter = rid; } static void receive_side_prop(char *str) { int id, sn, val; char *nstr; Side *side; Unit *unit; id = strtol(str, &nstr, 10); side = side_n(id); if (side == NULL) return; str = nstr; ++str; if (strncmp(str, "adjective ", 10) == 0) { set_side_adjective(side, side, str + 10); } else if (strncmp(str, "af ", 3) == 0) { str += 3; val = strtol(str, &nstr, 10); set_autofinish(side, val); } else if (strncmp(str, "ar ", 3) == 0) { str += 3; val = strtol(str, &nstr, 10); set_autoresearch(side, val); } else if (strncmp(str, "ai ", 3) == 0) { str += 3; set_side_ai(side, str); } else if (strncmp(str, "colorscheme ", 12) == 0) { set_side_colorscheme(side, side, str + 12); } else if (strncmp(str, "controlledby ", 13) == 0) { str += 13; sn = strtol(str, &nstr, 10); str = nstr; val = strtol(str, &nstr, 10); set_controlled_by(side, side_n(sn), val); } else if (strncmp(str, "designer ", 9) == 0) { str += 9; val = strtol(str, &nstr, 10); if (val) become_designer(side); else become_nondesigner(side); } else if (strncmp(str, "doctrine ", 9) == 0) { str += 9; set_doctrine(side, str); } else if (strncmp(str, "draw ", 5) == 0) { str += 5; val = strtol(str, &nstr, 10); set_willing_to_draw(side, val); } else if (strncmp(str, "emblemname ", 11) == 0) { set_side_emblemname(side, side, str + 11); } else if (strncmp(str, "fin ", 4) == 0) { finish_turn(side); } else if (strncmp(str, "longname ", 9) == 0) { set_side_longname(side, side, str + 9); } else if (strncmp(str, "name ", 5) == 0) { set_side_name(side, side, str + 5); } else if (strncmp(str, "noun ", 5) == 0) { set_side_noun(side, side, str + 5); } else if (strncmp(str, "pluralnoun ", 11) == 0) { set_side_pluralnoun(side, side, str + 11); } else if (strncmp(str, "resign ", 7) == 0) { str += 7; val = strtol(str, &nstr, 10); resign_game(side, (val ? side_n(val) : NULL)); } else if (strncmp(str, "save ", 5) == 0) { str += 5; val = strtol(str, &nstr, 10); set_willing_to_save(side, val); } else if (strncmp(str, "self ", 5) == 0) { str += 5; val = strtol(str, &nstr, 10); unit = find_unit(val); set_side_self_unit(side, unit); } else if (strncmp(str, "research ", 9) == 0) { str += 9; val = strtol(str, &nstr, 10); set_side_research(side, val); } else if (strncmp(str, "shortname ", 10) == 0) { set_side_shortname(side, side, str + 10); } else if (strncmp(str, "trust ", 6) == 0) { str += 6; sn = strtol(str, &nstr, 10); str = nstr; val = strtol(str, &nstr, 10); set_trust(side, side_n(sn), val); } else { run_warning("Unknown S packet \"%s\", ignoring", str); } } static void receive_unit_prop(char *str) { int sid, uid, val, val2; char *nstr; Unit *unit; Side *side; /* Collect the side. */ sid = strtol(str, &nstr, 10); side = side_n(sid); /* Note that the side may be NULL. */ str = nstr; /* Collect the unit. */ uid = strtol(str, &nstr, 10); unit = find_unit(uid); if (unit == NULL) { run_warning("Packet with invalid unit id %d, ignoring packet", uid); return; } str = nstr; ++str; /* Decode the property being set. */ if (strncmp(str, "ai ", 3) == 0) { val = strtol(str + 3, &nstr, 10); str = nstr; val2 = strtol(str, &nstr, 10); set_unit_ai_control(side, unit, val, val2); } else if (strncmp(str, "clra ", 5) == 0) { clear_task_agenda(unit->plan); } else if (strncmp(str, "delay ", 6) == 0) { val = strtol(str + 6, &nstr, 10); delay_unit(unit, val); } else if (strncmp(str, "disband ", 8) == 0) { disband_unit(side, unit); } else if (strncmp(str, "forcereplan ", 12) == 0) { val = strtol(str + 12, &nstr, 10); force_replan(side, unit, val); } else if (strncmp(str, "formation ", 10) == 0) { run_warning("need to interp formation packet"); } else if (strncmp(str, "maingoal ", 9) == 0) { Goal *goal; int i; str += 9; val = strtol(str, &nstr, 10); if (val > 0) { str = nstr; goal = create_goal(val, side, TRUE); for (i = 0; i < 4; ++i) { val = strtol(str, &nstr, 10); str = nstr; goal->args[i] = val; } } else { goal = NULL; } set_unit_main_goal(side, unit, goal); } else if (strncmp(str, "curadvance ", 11) == 0) { val = strtol(str + 11, &nstr, 10); set_unit_curadvance(side, unit, val); } else if (strncmp(str, "autoplan ", 9) == 0) { val = strtol(str + 9, &nstr, 10); set_unit_autoplan(side, unit, val); } else if (strncmp(str, "autoresearch ", 13) == 0) { val = strtol(str + 13, &nstr, 10); set_unit_autoresearch(side, unit, val); } else if (strncmp(str, "autobuild ", 10) == 0) { val = strtol(str + 10, &nstr, 10); set_unit_autobuild(side, unit, val); } else if (strncmp(str, "buildingdone ", 13) == 0) { val = strtol(str + 13, &nstr, 10); set_unit_autobuild(side, unit, val); } else if (strncmp(str, "researchdone ", 13) == 0) { val = strtol(str + 13, &nstr, 10); set_unit_autobuild(side, unit, val); } else if (strncmp(str, "name ", 5) == 0) { set_unit_name(side, unit, str + 5); } else if (strncmp(str, "plan ", 5) == 0) { val = strtol(str + 5, &nstr, 10); set_unit_plan_type(side, unit, val); } else if (strncmp(str, "resv ", 5) == 0) { str += 5; val = strtol(str, &nstr, 10); str = nstr; val2 = strtol(str, &nstr, 10); set_unit_reserve(side, unit, val, val2); } else if (strncmp(str, "sleep ", 6) == 0) { str += 6; val = strtol(str, &nstr, 10); str = nstr; val2 = strtol(str, &nstr, 10); set_unit_asleep(side, unit, val, val2); } else if (strncmp(str, "waittrans ", 10) == 0) { str += 10; val = strtol(str, &nstr, 10); set_unit_waiting_for_transport(side, unit, val); } else { run_warning("Unknown U packet \"%s\", ignoring", str); } } static void receive_world_prop(char *str) { int id, x, y, a1, a2, a3, a4; char *str2, *nstr; Side *side; str2 = strchr(str, ' '); if (str2 == NULL) { return; } *str2 = '\0'; ++str2; id = strtol(str2, &nstr, 10); str2 = nstr; x = strtol(str2, &nstr, 10); str2 = nstr; y = strtol(str2, &nstr, 10); str2 = nstr; a1 = strtol(str2, &nstr, 10); str2 = nstr; a2 = strtol(str2, &nstr, 10); str2 = nstr; a3 = strtol(str2, &nstr, 10); str2 = nstr; a4 = strtol(str2, &nstr, 10); side = side_n(id); if (strcmp(str, "cell") == 0) { paint_cell(side, x, y, a1, a2); } else if (strcmp(str, "bord") == 0) { paint_border(side, x, y, a1, a2, a3); } else if (strcmp(str, "conn") == 0) { paint_connection(side, x, y, a1, a2, a3); } else if (strcmp(str, "coat") == 0) { paint_coating(side, x, y, a1, a2, a3); } else if (strcmp(str, "peop") == 0) { paint_people(side, x, y, a1, a2); } else if (strcmp(str, "ctrl") == 0) { paint_control(side, x, y, a1, a2); } else if (strcmp(str, "feat") == 0) { paint_feature(side, x, y, a1, a2); } else if (strcmp(str, "elev") == 0) { paint_elevation(side, x, y, a1, a2, a3, a4); } else if (strcmp(str, "temp") == 0) { paint_temperature(side, x, y, a1, a2); } else if (strcmp(str, "m") == 0) { paint_material(side, x, y, a1, a2, a3); } else if (strcmp(str, "wind") == 0) { paint_winds(side, x, y, a1, a2, a3); } else { run_warning("Unknown W packet \"%s\", ignoring", str); } } static void receive_run_game(char *str) { int maxactions, newsernum; char *reststr, *nreststr; maxactions = strtol(str, &reststr, 10); newsernum = strtol(reststr, &nreststr, 10); reststr = nreststr; new_randstate = strtol(reststr, &nreststr, 10); set_g_run_serial_number(newsernum); if (new_randstate != randstate) { Dprintf("Rand state change: %d -> %d\n", randstate, new_randstate); } else { Dprintf("Rand state matches\n"); } randstate = new_randstate; /* This where non-masters actually call run_game. */ run_game(maxactions); } static void receive_game_checksum(char *str) { int before_run = FALSE, other_rid, other_csum, our_csum; char *reststr, *nreststr; if (*str == 'a') { before_run = TRUE; ++str; } other_rid = strtol(str, &reststr, 10); other_csum = strtol(reststr, &nreststr, 10); our_csum = game_checksum(); if (our_csum != other_csum) { /* If we're about to lose, make sure the master knows about it. */ if (my_rid != master_rid) send_game_checksum_error(master_rid, our_csum, other_csum); /* This would be a warning normally, but being out of sync isn't always a real problem apparently... */ notify_all("Game is out of sync! (received %d from %d, computed %d%s)", other_csum, other_rid, our_csum, (before_run ? ", before run_game" : "")); } } /* Compute a single integer derived from the contents of the game state. */ #define add_int_to_checksum(cs, x) ((cs) += (x)); int game_checksum(void) { int csum, i; Side *side; Unit *unit; Plan *plan; Task *task; csum = 0; for_all_sides(side) { add_int_to_checksum(csum, side->id); add_int_to_checksum(csum, side->self_unit_id); add_int_to_checksum(csum, side->controlled_by_id); add_int_to_checksum(csum, side->ingame); add_int_to_checksum(csum, side->everingame); add_int_to_checksum(csum, side->status); add_int_to_checksum(csum, side->willingtodraw); add_int_to_checksum(csum, side->autofinish); add_int_to_checksum(csum, side->finishedturn); add_int_to_checksum(csum, side->advantage); } for_all_units(unit) { add_int_to_checksum(csum, unit->id); add_int_to_checksum(csum, unit->number); add_int_to_checksum(csum, unit->x); add_int_to_checksum(csum, unit->y); add_int_to_checksum(csum, unit->z); add_int_to_checksum(csum, unit->hp); if (unit->transport) { add_int_to_checksum(csum, unit->transport->id); } plan = unit->plan; if (unit->plan) { add_int_to_checksum(csum, plan->type); add_int_to_checksum(csum, plan->asleep); add_int_to_checksum(csum, plan->reserve); add_int_to_checksum(csum, plan->delayed); add_int_to_checksum(csum, plan->waitingfortasks); add_int_to_checksum(csum, plan->aicontrol); for_all_tasks(plan, task) { add_int_to_checksum(csum, task->type); for (i = 0; i < MAXTASKARGS; ++i) add_int_to_checksum(csum, task->args[i]); add_int_to_checksum(csum, task->execnum); add_int_to_checksum(csum, task->retrynum); } } } return csum; } static void receive_error(int id, char *str) { /* (should decode specific types of errors?) */ notify_all("Error from #%d, \"%s\"", id, str); Dprintf("Error from #%d, \"%s\"\n", id, str); } static void receive_remote_program(char *str) { int rid; char *nstr; rid = strtol(str, &nstr, 10); str = nstr + 1; numremotes = max(rid, numremotes); online[rid] = TRUE; remote_player_specs[rid] = copy_string(str); /* (should do with a function pointer) */ add_remote_locally(rid, str); } static void receive_chat(char *str) { int rid; char *nstr; rid = strtol(str, &nstr, 10); str = nstr + 1; /* (should do with a function pointer) */ send_chat(rid, str); } static void receive_variant_setting(char *str) { int which, v1, v2, v3; char *nstr; ++str; which = strtol(str, &nstr, 10); str = nstr + 1; v1 = strtol(str, &nstr, 10); str = nstr + 1; v2 = strtol(str, &nstr, 10); str = nstr + 1; v3 = strtol(str, &nstr, 10); set_variant_value(which, v1, v2, v3); if (update_variant_callback) (*update_variant_callback)(which); } static void receive_assignment_setting(char *str) { int n, n2, val; char *nstr, *aitype; Player *player; n = strtol(str, &nstr, 10); str = nstr; ++str; if (strncmp(str, "add", 3) == 0) { n = add_side_and_player(); } else if (strncmp(str, "advantage ", 10) == 0) { val = strtol(str + 10, NULL, 10); player = assignments[n].player; if (player != NULL) { player->advantage = val; } } else if (strncmp(str, "ai ", 3) == 0) { aitype = NULL; if (str[3] != '\0') aitype = copy_string(str + 3); set_ai_for_player(n, aitype); } else if (strncmp(str, "exchange ", 9) == 0) { n2 = strtol(str + 9, NULL, 10); n2 = exchange_players(n, n2); if (update_assignment_callback) (*update_assignment_callback)(n2); } else if (strncmp(str, "rename ", 7) == 0) { n2 = strtol(str + 7, NULL, 10); rename_side_for_player(n, n2); } else if (strncmp(str, "update ", 7) == 0) { nstr = str + 7; player = find_player(n); if (player == NULL) player = add_player(); parse_player_spec(player, nstr); return; /* a hack, need to handle callback below when windows not up */ } else { run_error("assignment packet \"%s\" not recognized", str); } if (n >= 0) { /* Most settings affect the same assignment, so always update it here. */ if (update_assignment_callback) (*update_assignment_callback)(n); } } /* Convert hex digit A to a number. */ static int fromhex(int a) { if (a >= '0' && a <= '9') return a - '0'; else if (a >= 'a' && a <= 'f') return a - 'a' + 10; else run_warning ("Reply contains invalid hex digit %d", a); return 0; } /* Convert number NIB to a hex digit. */ static int tohex(int nib) { if (nib < 10) return '0' + nib; else return 'a' + nib - 10; } /* The following "connection method" is for debugging. */ static char *inport_filename; static char *outport_filename; static FILE *inport; static FILE *outport; static char *sendbuf; void init_file_port(int ishost) { /* Just use wired-in filenames, since only for debugging. */ if (ishost) { inport_filename = "server.in"; outport_filename = "server.out"; } else { inport_filename = "client.in"; outport_filename = "client.out"; } inport = open_file(inport_filename, "r"); outport = open_file(outport_filename, "w"); if (inport == NULL || outport == NULL) run_warning("connection files opening failed"); /* (should be able to get all the rids from file) */ master_rid = 1; if (ishost) { my_rid = 1; } else { my_rid = 2; } numremotes = 2; } void low_file_send(int rid, char *buf) { int i, j, len; if (outport) { if (sendbuf == NULL) sendbuf = xmalloc(1000); j = 0; len = strlen(buf); for (i = 0; i <= len; ++i) { if (buf[i] == '\n') { sendbuf[j++] = '<'; sendbuf[j++] = 'n'; sendbuf[j++] = 'l'; sendbuf[j++] = '>'; } else { sendbuf[j++] = buf[i]; } } fprintf(outport, "%d->%d: %s\n", my_rid, rid, sendbuf); fflush(outport); } else { run_warning("No place to write \"%s\"", (buf ? buf : "")); } } int low_file_receive(int *idp, char *buf, int maxchars, int timeout) { int rslt = FALSE, i, j, len; char readbuf[BUFSIZE], *str; if (inport) { /* Read until we find a message addressed to us. */ while (1) { str = fgets(readbuf, maxchars, inport); if (str == NULL) return FALSE; if (outport) fputs(str, outport); if (str[3] - '0' == my_rid) break; } *idp = str[0] - '0'; strcpy(buf, str + 6); len = strlen(buf); if (buf[len-1] == '\n') { buf[len-1] = '\0'; --len; } if (strstr(buf, "")) { j = 0; for (i = 0; i <= len; ++i) { if (i + 4 <= len && buf[i] == '<' && buf[i+1] == 'n' && buf[i+2] == 'l' && buf[i+3] == '>' ) { buf[j++] = '\n'; i += 3; } else { buf[j++] = buf[i]; } } } rslt = TRUE; } else { run_warning("No place to read"); } return rslt; } void close_file_port(void) { fclose(inport); fclose(outport); }