/* Program Primary Routines Functions: void SWTHandleSignal(int s) void SWTResetTimmers(void) void SWTReclaimGlobalMemory(bool_t print_response) void SWTDoChangeBackgroundMusic(void) int SWTDoConnect(char *arg) void SWTDoDisconnect(void) void SWTDoRefresh(void) void SWTCManage(void) int SWTInit(int argc, char *argv[]) void SWTManage(void) void SWTShutdown(void) int main(int argc, char *argv[]) --- */ #include <stdio.h> #include <stdlib.h> #include <malloc.h> #include <string.h> #include <time.h> #include <unistd.h> #include <signal.h> #include "../include/string.h" #include "../include/disk.h" #include "../include/unvmain.h" #include "../include/swsoundcodes.h" #include "net.h" #include "swterm.h" pid_t root_pid; int runlevel; long cur_millitime; time_t cur_systime; long lapsed_millitime; double time_compensation; xsw_debug_struct debug; xsw_options_struct option; CDisplay **curses_display; int total_curses_displays; xsw_sound_struct sound; xsw_dname_struct dname; xsw_fname_struct fname; xsw_next_struct next; xsw_local_control_struct local_control; xsw_gctl_struct gctl[1]; sw_units_struct sw_units; xsw_sector_legend_struct sector_legend; scanner_contacts_struct **scanner_contact; int total_scanner_contacts; ocsn_struct **ocsn; int total_ocsns; xsw_loadstat_struct loadstat; xsw_auto_interval_tune_struct auto_interval_tune; xsw_font_struct xsw_font; xsw_color_struct xsw_color; xsw_bridge_win_struct bridge_win; eco_win_struct eco_win; dialog_win_struct err_dw; dialog_win_struct info_dw; xsw_object_struct **xsw_object; int total_objects; xsw_object_struct **inrange_xsw_object; int total_inrange_objects; xsw_net_parms_struct net_parms; ss_item_struct **ss_item; int total_ss_items; /* * Segfault counter: */ static int segfault_count; /* * Signal handler. */ void SWTHandleSignal(int s) { switch(s) { case SIGSEGV: segfault_count++; if(segfault_count >= 2) exit(0); break; case SIGINT: case SIGTERM: runlevel = 1; break; case SIGPIPE: signal(SIGPIPE, SWTHandleSignal); break; default: break; } return; } /* * Resets global timmers. */ void SWTResetTimmers(void) { int i, n; xsw_object_struct **obj_ptr; /* Global event and task timmers. */ next.update_check = 0; next.viewscreen = 0; next.bridge_std_redraw = 0; next.memory_clean = 0; next.lplayer_pos_send = 0; /* Auto interval tunning. */ auto_interval_tune.next = 0; /* Timmers on XSW Objects. */ for(i = 0, obj_ptr = xsw_object; i < total_objects; i++, obj_ptr++ ) { if(*obj_ptr == NULL) continue; if((*obj_ptr)->type <= XSW_OBJ_TYPE_GARBAGE) continue; /* Last network update. */ (*obj_ptr)->last_updated = 0; /* Birth time, since this is in milliseconds midnight * we need this to be reset too. But it may cause certain * objects to live a bit longer. */ (*obj_ptr)->birth_time = 0; /* Last animation frame. */ (*obj_ptr)->animation.last_interval = 0; /* Weapon last use. */ for(n = 0; n < (*obj_ptr)->total_weapons; n++) { if((*obj_ptr)->weapons[n] == NULL) continue; (*obj_ptr)->weapons[n]->last_used = 0; } } return; } /* * Reclaims global memory. */ void SWTReclaimGlobalMemory(bool_t print_response) { if(print_response) MesgAdd("Reclaiming memory...", 0); DBReclaim(); /* No isrefs reclaim. */ OCSReclaimMemory(); /* No vslabels reclaim. */ DBInRangeUpdate(net_parms.player_obj_num); if(print_response) MesgAdd("Memory reclaimed.", 0); return; } /* * Procedure to update background music, this should be called * whenever the background music *may* need changing. */ void SWTDoChangeBackgroundMusic(void) { int code = SOUND_CODE_BKG_STANDARD; xsw_object_struct *obj_ptr, *tar_obj_ptr; /* Check if main menu on bridge window is mapped. */ if(bridge_win.screen_type == BRIDGE_SCREEN_TYPE_MAIN_MENU) { /* Main menu on bridge window is mapped. */ code = SOUND_CODE_BKG_MAINMENU; } else { /* View screen is mapped. */ code = SOUND_CODE_BKG_STANDARD; if(net_parms.player_obj_ptr == NULL) { /* Player object not valid, could be still logging in. */ } else { /* Player object is valid. */ /* Get pointer to player object. */ obj_ptr = net_parms.player_obj_ptr; /* Check if in nebula. */ if(obj_ptr->loc_type == XSW_LOC_TYPE_NEBULA) code = SOUND_CODE_BKG_MYSTY; /* Check if locked onto something. */ if(DBIsObjectGarbage(obj_ptr->locked_on)) { /* Player object is not locked on anything. */ } else { /* Player object is locked on something. */ tar_obj_ptr = xsw_object[obj_ptr->locked_on]; /* Check if locked on target is controlled, * weapon, or player. */ if((tar_obj_ptr->type == XSW_OBJ_TYPE_CONTROLLED) || (tar_obj_ptr->type == XSW_OBJ_TYPE_PLAYER) || (tar_obj_ptr->type == XSW_OBJ_TYPE_WEAPON) || (tar_obj_ptr->type == XSW_OBJ_TYPE_STREAMWEAPON) || (tar_obj_ptr->type == XSW_OBJ_TYPE_SPHEREWEAPON) ) code = SOUND_CODE_BKG_FIGHTING; } } } /* Change background music (as needed). */ if(code != sound.bkg_mood_code) SoundChangeBackgroundMusic(code, 0, 0); return; } /* * Connect procedure. Connects to the universe specified * in the given arg, which should be a URL containing * the protocol, address, port, login name and login password. */ int SWTDoConnect(char *arg) { int status; char *strptr; char url[MAX_URL_LEN]; char text[MAX_URL_LEN + 256]; if(arg == NULL) return(-1); /* Copy argument to url string for parsing. */ strncpy(url, arg, MAX_URL_LEN); url[MAX_URL_LEN - 1] = '\0'; StringStripSpaces(url); /* Get address (mandatory). */ strptr = StringParseAddress(url); if(strptr == NULL) { sprintf( text, "Cannot parse address" ); MesgAdd(text, 0); return(-1); } else { strncpy(net_parms.address, strptr, MAX_URL_LEN); net_parms.address[MAX_URL_LEN - 1] = '\0'; } /* Get port. */ net_parms.port = StringParsePort(url); if(net_parms.port < 0) net_parms.port = DEF_SWSERV_PORT; /* Get login name. */ strptr = StringParseName(url); if(strptr != NULL) { strncpy( net_parms.login_name, strptr, XSW_OBJ_NAME_MAX ); net_parms.login_name[XSW_OBJ_NAME_MAX - 1] = '\0'; } /* Get login password. */ strptr = StringParsePassword(url); if(strptr != NULL) { strncpy( net_parms.login_password, strptr, XSW_OBJ_PASSWORD_MAX ); net_parms.login_password[XSW_OBJ_PASSWORD_MAX - 1] = '\0'; } /* Disconnect if already connected. */ if(net_parms.socket > -1) { NetSendDisconnect(); DBDeleteAllObjects(); NetResetParms(); /* Clear economy window entry. */ /* EcoWinDoDeleteInventory(); */ } /* NetOpenConnection will print all errors and set up * net_parm variables. */ status = NetOpenConnection(net_parms.address, net_parms.port); if(status < 0) { return(-1); } else { sprintf(text, "Connecting to %s...", net_parms.address ); MesgAdd(text, 0); } /* Completely redraw bridge window. */ BridgeDraw(0); return(0); } /* * Disconnect procedure. */ void SWTDoDisconnect(void) { int deallocate_resources = 0; char text[256]; /* Check if currently connected. */ if(net_parms.socket > -1) { /* Currently connected, send disconnect. */ NetSendDisconnect(); /* Increment disconnect send count. */ net_parms.disconnect_send_count++; /* Force disconnect if disconnect send count was sent more * than 3 times. */ if(net_parms.disconnect_send_count > 3) deallocate_resources = 1; } else { /* Not connected, reset all network resources and deallocate * other resources. */ deallocate_resources = 1; } /* Deallocate all network related resources? */ if(deallocate_resources) { /* Delete all objects. This will also reset the player * object referance, delete all scanner contacts, and * reset inrange objects list. */ DBDeleteAllObjects(); /* Reset network parameters. */ NetResetParms(); /* Change bridge window title. */ /* sprintf(text, "%s: Untitled", PROG_NAME); OSWSetWindowTitle(bridge_win.toplevel, text); */ /* Clear economy window entries. */ /* EcoWinDoDeleteInventory(); */ /* Reset game controller positions. */ gctl[0].turn = 0; gctl[0].throttle = 0; gctl[0].thrust_dir = 0; } /* Completely redraw bridge window. */ BridgeDraw(0); return; } /* * Does a network refresh sequence. */ void SWTDoRefresh(void) { /* Delete object labels. */ /* VSLabelDeleteAll(); */ /* Delete scanner contacts. */ ScDeleteAll(); /* Update inrange objects list. */ DBInRangeUpdate(net_parms.player_obj_num); /* Check if currently connected. */ if(net_parms.socket > -1) { /* Send refresh to server. */ NetSendRefresh(); } /* Completely redraw bridge window. */ BridgeDraw(0); return; } /* * Manages curses stuff. */ void SWTCManage(void) { int i, events_handled; CDisplay *display; CEvent event; char text[256]; for(i = 0; i < total_curses_displays; i++) { display = curses_display[i]; if(display == NULL) continue; /* Reset events handled counter. */ events_handled = 0; /* Get next event if any. */ if(CGetNextEvent( display, &event ) <= 0) continue; /* Begin calling event managers. */ /* Bridge window. */ events_handled += BridgeManage(&event); } return; } /* * ShipWars Terminal initialziation. * * Call this function only once at startup. */ int SWTInit(int argc, char *argv[]) { int i, status; char *strptr; char cwd[PATH_MAX]; char home[PATH_MAX]; char *startup_url = NULL; /* First thing to do on initialization is to reset the * segfault counter. */ segfault_count = 0; /* Initialize time zone. */ tzset(); /* Reset/fetch global variables to their default values. */ root_pid = getpid(); debug.level = DEBUG_LEVEL_NONE; debug.val = 0; cur_millitime = MilliTime(); cur_systime = time(NULL); lapsed_millitime = 0; time_compensation = 1.0; next.update_check = cur_millitime; next.viewscreen = cur_millitime; next.bridge_std_redraw = cur_millitime; next.memory_clean = cur_millitime + MEMORY_CLEAN_INTERVAL; next.lplayer_pos_send = cur_millitime; strncpy(dname.startup, cwd, PATH_MAX); dname.startup[PATH_MAX - 1] = '\0'; strncpy(dname.toplevel, DEF_XSW_TOPLEVEL_DIR, PATH_MAX); dname.toplevel[PATH_MAX - 1] = '\0'; strncpy(dname.etc, DEF_XSW_ETC_DIR, PATH_MAX); dname.etc[PATH_MAX - 1] = '\0'; strncpy(dname.images, DEF_XSW_IMAGES_DIR, PATH_MAX); dname.images[PATH_MAX - 1] = '\0'; strncpy(dname.sounds, DEF_XSW_SOUNDS_DIR, PATH_MAX); dname.sounds[PATH_MAX - 1] = '\0'; strptr = getenv("HOME"); strncpy(dname.downloads, ((strptr == NULL) ? "/" : strptr), PATH_MAX); dname.downloads[PATH_MAX - 1] = '\0'; xsw_font.std = NULL; xsw_font.std_bold = NULL; xsw_font.console_heading = NULL; xsw_font.console_standard = NULL; xsw_font.console_message = NULL; option.rc_version_major = PROG_VERSION_MAJOR; option.rc_version_minor = PROG_VERSION_MINOR; option.units = XSW_UNITS_XSW; option.def_scanner_orient = SCANNER_ORIENT_LOCAL; option.log_client = 0; option.log_net = 0; option.log_errors = 0; option.energy_saver_mode = 0; option.show_formal_label = 1; option.show_net_errors = 0; option.show_server_errors = 1; option.local_updates = 1; option.sounds = XSW_SOUNDS_NONE; option.music = 0; option.scanner_limiting = 1; option.notify_scanner_contacts = 0; /* Default to no for swterm. */ curses_display = NULL; total_curses_displays = 0; sound.server_type = SNDSERV_TYPE_NONE; strncpy( sound.start_cmd, DEF_SOUND_SERVER_START_CMD, PATH_MAX + NAME_MAX ); sound.start_cmd[PATH_MAX + NAME_MAX - 1] = '\0'; strncpy( sound.con_arg, DEF_SOUND_SERVER_CONNECT_ARG, MAX_URL_LEN ); sound.con_arg[MAX_URL_LEN - 1] = '\0'; sound.con_data = NULL; sw_units.ru_to_au = DEF_UNITCONV_RU_TO_AU; sector_legend.x_max = SECTOR_SIZE_X_MAX; sector_legend.x_min = SECTOR_SIZE_X_MIN; sector_legend.y_max = SECTOR_SIZE_Y_MAX; sector_legend.y_min = SECTOR_SIZE_Y_MIN; sector_legend.z_max = SECTOR_SIZE_Z_MAX; sector_legend.z_min = SECTOR_SIZE_Z_MIN; sector_legend.x_len = sector_legend.x_max - sector_legend.x_min; sector_legend.y_len = sector_legend.y_max - sector_legend.y_min; sector_legend.z_len = sector_legend.z_max - sector_legend.z_min; local_control.weapon_freq = 180.25; local_control.weapons_online = 1; scanner_contact = NULL; total_scanner_contacts = 0; ocsn = NULL; total_ocsns = 0; xsw_object = NULL; total_objects = 0; inrange_xsw_object = NULL; total_inrange_objects = 0; ss_item = NULL; total_ss_items = 0; /* Reset player object to none (-1). */ DBSetPlayerObject(-1); /* Set default fname. */ strncpy( fname.rc, PrefixPaths(cwd, DEF_XSW_RCFILE), PATH_MAX + NAME_MAX ); fname.rc[PATH_MAX + NAME_MAX - 1] = '\0'; strncpy( fname.sound_scheme, PrefixPaths(cwd, DEF_XSW_SOUND_SCHEME_FILE), PATH_MAX + NAME_MAX ); fname.sound_scheme[PATH_MAX + NAME_MAX - 1] = '\0'; strncpy( fname.ocsn, PrefixPaths(cwd, DEF_XSW_OCSN_FILE), PATH_MAX + NAME_MAX ); fname.ocsn[PATH_MAX + NAME_MAX - 1] = '\0'; strncpy( fname.log, PrefixPaths(cwd, DEF_XSW_LOG_FILE), PATH_MAX + NAME_MAX ); fname.log[PATH_MAX + NAME_MAX - 1] = '\0'; net_parms.connection_state = CON_STATE_NOT_CONNECTED; net_parms.socket = -1; strptr = getenv("LOGNAME"); if(strptr != NULL) strncpy(net_parms.login_name, strptr, XSW_OBJ_NAME_MAX); else memset(net_parms.login_name, '\0', XSW_OBJ_NAME_MAX); net_parms.login_name[XSW_OBJ_NAME_MAX - 1] = '\0'; memset(net_parms.login_password, '\0', XSW_OBJ_PASSWORD_MAX); net_parms.is_address_set = 0; net_parms.con_start = 0; net_parms.warn15 = 0; net_parms.warn30 = 0; net_parms.warn45 = 0; net_parms.login_got_lplayer = 0; net_parms.login_got_position = 0; net_parms.login_got_sector = 0; strncpy(net_parms.address, DEF_SWSERV_ADDRESS, MAX_URL_LEN); net_parms.address[MAX_URL_LEN - 1] = '\0'; net_parms.port = DEF_SWSERV_PORT; net_parms.net_int = SERVER_DEF_INT; net_parms.disconnect_send_count = 0; net_parms.bad_send_count = 0; memset(net_parms.co_data, 0x00, CS_DATA_MAX_LEN); net_parms.co_data_len = 0; loadstat.net_load_max = DEF_NET_LOAD_MAX; loadstat.rx_interval = 0; loadstat.sx_interval = 0; loadstat.rx_ilast = 0; loadstat.sx_ilast = 0; auto_interval_tune.state = 1; auto_interval_tune.interval = AINT_DEF_TUNE_INT; auto_interval_tune.next = cur_millitime + auto_interval_tune.interval; /* Initialize universe object management. */ UNVInit(argc, argv); /* Handle command line parameters. */ for(i = 1; i < argc; i++) { if(argv[i] == NULL) continue; /* Help. */ if(strcasepfx(argv[i], "--h") || strcasepfx(argv[i], "-h") || strcasepfx(argv[i], "-?") ) { printf(PROG_USAGE_MESG); return(-4); } /* Version. */ else if(strcasepfx(argv[i], "--v") || strcasepfx(argv[i], "-v") ) { printf( "%s Version %s\n", PROG_NAME, PROG_VERSION ); return(-4); } /* Data toplevel directory. */ else if(strcasepfx(argv[i], "-d")) { i++; if(i < argc) { strncpy( dname.toplevel, argv[i], PATH_MAX ); dname.toplevel[PATH_MAX - 1] = '\0'; strptr = PrefixPaths(dname.toplevel, "etc"); strncpy( dname.etc, ((strptr == NULL) ? "/" : strptr), PATH_MAX ); dname.etc[PATH_MAX - 1] = '\0'; strptr = PrefixPaths(dname.toplevel, "images"); strncpy( dname.images, ((strptr == NULL) ? "/" : strptr), PATH_MAX ); dname.images[PATH_MAX - 1] = '\0'; strptr = PrefixPaths(dname.toplevel, "sounds"); strncpy( dname.sounds, ((strptr == NULL) ? "/" : strptr), PATH_MAX ); dname.sounds[PATH_MAX - 1] = '\0'; } else { fprintf(stderr, "%s: Requires argument.\n", argv[i - 1] ); } } else if((argv[i][0] != '-') && (argv[i][0] != '+') ) { startup_url = argv[i]; } } /* Connect to curses library. */ total_curses_displays = 1; curses_display = (CDisplay **)malloc( total_curses_displays * sizeof(CDisplay *) ); if(curses_display == NULL) { total_curses_displays = 0; return(-1); } curses_display[0] = CWInit(argc, argv); if(curses_display[0] == NULL) return(-1); /* Initialize bridge window. */ if(BridgeInit(argc, argv)) return(-1); BridgeMap(); BridgeSetFocus(1); /* Initialize reality engine. */ REngInit(); /* ********************************************************* */ /* Select signals to watch for. */ signal(SIGHUP, SWTHandleSignal); signal(SIGINT, SWTHandleSignal); signal(SIGQUIT, SWTHandleSignal); signal(SIGABRT, SWTHandleSignal); signal(SIGKILL, SWTHandleSignal); signal(SIGSEGV, SWTHandleSignal); signal(SIGPIPE, SWTHandleSignal); signal(SIGTERM, SWTHandleSignal); signal(SIGCONT, SWTHandleSignal); signal(SIGSTOP, SWTHandleSignal); /* Connect at startup? */ if(startup_url != NULL) { SWTDoConnect(startup_url); } else { MesgAdd("ShipWars Terminal is ready, press F9 to connect.", 0); } return(0); } /* * ShipWars Terminal management procedure. * * This function needs to be called once per loop. */ void SWTManage(void) { int i, player_obj_num, tar_obj_num; long new_millitime; /* Update timmings. */ cur_systime = time(NULL); new_millitime = MilliTime(); /* Reset timmers if new millitime is less than * cur_millitime. */ if(new_millitime < cur_millitime) { /* Reset global timmers. */ SWTResetTimmers(); lapsed_millitime = 0; time_compensation = 1.0; } else { lapsed_millitime = new_millitime - cur_millitime; time_compensation = (double)( (double)lapsed_millitime / (double)CYCLE_LAPSE_MS ); if(time_compensation < 1) time_compensation = 1; } /* Now set global variable cur_millitime. */ cur_millitime = new_millitime; /* *********************************************************** */ /* Manage curses input and events. */ SWTCManage(); /* Manage sound. */ /* Manage network. */ switch(net_parms.connection_state) { case CON_STATE_CONNECTED: /* Connected. */ /* Handle any incoming data. */ NetHandleRecv(); /* Adjust automatic interval tunning. */ if(auto_interval_tune.next <= cur_millitime) { AIntvTuneHandleAdjust(); /* Schedual next auto interval tune. */ auto_interval_tune.next = cur_millitime + auto_interval_tune.interval; } /* Send out local player object position to server. */ if(next.lplayer_pos_send <= cur_millitime) { /* Get referance to player object number. */ player_obj_num = net_parms.player_obj_num; /* Send sector position. */ NetSendObjectSect(player_obj_num); /* Pose object (coordinates). */ NetSendPoseObj(player_obj_num); /* Throttle position. */ NetSendObjectThrottle(player_obj_num); /* Schedual next player position send. */ next.lplayer_pos_send = cur_millitime + net_parms.net_int; } break; case CON_STATE_NEGOTIATING: /* Logging in. */ /* Warn if logging in is taking longer than it should. */ if(((net_parms.con_start + 15) <= cur_systime) && !net_parms.warn15 ) { MesgAdd( "Waited 15 seconds for server response, still waiting...", xsw_color.standard_text ); net_parms.warn15 = 1; } else if(((net_parms.con_start + 30) <= cur_systime) && !net_parms.warn30 ) { MesgAdd( "Waited 30 seconds for server response, still waiting...", xsw_color.standard_text ); net_parms.warn30 = 1; } else if(((net_parms.con_start + 45) <= cur_systime) && !net_parms.warn45 ) { MesgAdd( "Waited 45 seconds (press ??? a few times to give up)...", xsw_color.standard_text ); net_parms.warn45 = 1; } /* Handle incoming data (just checking for login or * or disconnect). */ NetHandleRecv(); break; /* Not connected. */ default: /* CON_STATE_NOT_CONNECTED */ break; } /* Manage XSW objects. */ REngManage(); /* Redraw graphics. */ /* Redraw standard bridge graphics. */ if(next.bridge_std_redraw <= cur_millitime) { /* Redraw bridge things that need to be redrawn often. */ BridgeDrawSchedualed(); /* Schedual next time to draw consoles. */ next.bridge_std_redraw = cur_millitime + BRIDGE_STANDARD_REDRAW_INTERVAL; } /* ************************************************* */ /* Clean/refresh memory. */ if(next.memory_clean <= cur_millitime) { /* Clean, reclaim, memory and schedual next. */ SWTReclaimGlobalMemory(False); /* XSWReclaimGlobalMemory() already scheduals next. */ } return; } /* * ShipWars Terminal shutdown procedure. * * All resources will be shutdown and global memory deallocated * here. Call this function once at shutdown. */ void SWTShutdown(void) { int i; /* Close connection properly. */ /* SWTDoDisconnect(); */ /* Bridge window. */ BridgeDestroy(); /* Curses library resources. */ for(i = 0; i < total_curses_displays; i++) { CWShutdown(curses_display[i]); } free(curses_display); curses_display = NULL; total_curses_displays = 0; /* Reality engine shutdown. */ REngShutdown(); /* Shutdown sound server and free sound schemes. */ SoundShutdown(); /* Delete all object create script names. */ OCSDeleteAll(); /* Delete all scanner contacts. */ ScDeleteAll(); /* Delete all objects, this has probably already been * done with a call to SWTDoDisconnect(). But we delete * them all here just incase. */ DBDeleteAllObjects(); /* Shutdown universe object management. */ UNVShutdown(); return; } int main(int argc, char *argv[]) { int status; /* Runlevel 1: Starting. */ runlevel = 1; /* Initialize. */ status = SWTInit(argc, argv); switch(status) { /* Success. */ case 0: break; /* Just print help or version. */ case -4: SWTShutdown(); return(0); break; /* Error. */ default: SWTShutdown(); return(1); break; } /* Runlevel 2: Normal running. */ runlevel = 2; while(runlevel >= 2) { /* Sleep. */ usleep(CYCLE_LAPSE_US); /* Standard program management. */ SWTManage(); } /* Runlevel 1: Begin shutdown sequence. */ /* Save configuration. */ /* if(option.save_on_exit) RCSaveToFile(fname.rc); */ SWTShutdown(); /* Runlevel 0: Finished */ runlevel = 0; return(0); }