#include "os.h" #include <setjmp.h> #include "config.h" #include "lnode.h" #include "object.h" #include "interpret.h" #include "wiz_list.h" /* comm1.c */ extern int num_player; extern void print_prompt(void); extern int call_function_interactive(struct interactive *i, char *str); extern int get_message(char *buff, int size); extern void add_message(char *fmt, ...); extern void prepare_ipc(void); /* main.c */ extern int t_flag; extern int e_flag; extern int d_flag; extern void debug_message(char *a, ...); /* simulate.c */ extern struct object *obj_list_destruct; extern struct object *load_object(char *name); extern void destruct2(struct object *ob); extern void player_parser(char *buff); extern void fatal(char *fmt, ...); extern void shutdowngame(int sig); /* ed.c */ extern void ed_cmd (char *str); /* interpret.c */ extern void free_all_values(void); extern struct value *apply(char *fun, struct object *ob, struct value *arg); extern void call_function(struct lnode_def *pr); /* swap.c */ extern int swap(struct object *ob); /* backend.c */ jmp_buf error_recovery_context; int error_recovery_context_exists = 0; struct object *current_heart_beat; struct object *current_reset; int time_to_call_heart_beat; int eval_cost; #ifdef TRACE extern int trace_depth; #endif void logon(struct object *ob); void parse_command(char *str, struct object *ob); void backend(void); void catch_alarm(int sig); #ifdef WIN32 void alarm_run (void * ignored); #endif static void call_heart_beat(void); static void load_first_objects(void); static void move_current_reset(void); static void remove_destructed_objects(void); void logon (struct object *ob) { struct value *ret; ret = apply ("logon", ob, 0); if (ret == 0) { add_message ("prog %s:\n", ob->name); fatal ("Could not find logon on the player %s\n", ob->name); } } /* * Take a player command and parse it. * The command can also come from a NPC. * Beware that 'str' can be modified and extended ! */ void parse_command (char *str, struct object *ob) { struct object *save = command_giver; command_giver = ob; /* search_for_it(str); */ player_parser (str); command_giver = save; } /* * This is the backend. We will stay here for ever (almost). */ void backend (void) { char buff[2000]; int worst_eval_cost = MAX_COST / 5; free_all_values (); printf ("Loading init file %s\n", INIT_FILE); load_first_objects (); /* This is a major kludge. For some reason the environment of the last castle preloaded doesn't get its long description! */ load_object ("room/rum2.c"); printf ("Setting up ipc.\n"); prepare_ipc (); signal (SIGINT, shutdowngame); signal (SIGTERM, shutdowngame); #ifndef WIN32 signal (SIGHUP, shutdowngame); #endif if (!t_flag) call_heart_beat (); if (setjmp (error_recovery_context)) add_message ("Anomaly in the fabric of world space.\n"); error_recovery_context_exists = 1; #ifdef TRACE trace_depth = 0; #endif while (1) { eval_cost = 0; remove_destructed_objects (); if (get_message (buff, sizeof buff)) { /* * Now we have a string from the player. This string can go to * one of several places. If it is prepended with a '!', then * it is an escape from the 'ed' editor, so we send it * as a command to the parser. * If any object function is waiting for an input string, then * send it there. * Otherwise, send the string to the parser. */ if (!command_giver->interactive) fatal ("Non interactive player in main loop !\n"); if (command_giver->reset > 1) command_giver->reset = 1; if (call_function_interactive (command_giver->interactive, buff)); /* Do nothing else ! */ else if (buff[0] == '!') parse_command (buff + 1, command_giver); else if (command_giver->ed_buffer) ed_cmd (buff); else parse_command (buff, command_giver); /* * Print a prompt if player is still here. */ if (command_giver->interactive) print_prompt (); } command_giver = 0; if (time_to_call_heart_beat) call_heart_beat (); free_all_values (); if (eval_cost > worst_eval_cost) { worst_eval_cost = eval_cost; fprintf (stderr, "New worst eval cost: %d\n", eval_cost); } } } #ifdef WIN32 void alarm_run (void * ignored) { while (1) { Sleep(2000); time_to_call_heart_beat = 1; } } #else void catch_alarm (int sig) { time_to_call_heart_beat = 1; } #endif /* * Call all heart_beat() functions in all objects. */ static void call_heart_beat (void) { struct object *ob, *hide_current = current_object, *hide_command = command_giver; #ifdef WIN32 static unsigned long alarm_thread = 0; #endif if (num_player > 0) { for (ob = obj_list; ob; ob = ob->next_all) { if (ob->heart_beat == 0 || ob->reset == 0 || ob->enable_heart_beat == 0) continue; if (ob->swapped) fatal ("Heart beat in swapped object.\n"); current_object = ob; current_heart_beat = ob; if (ob->enable_commands) command_giver = ob; else command_giver = 0; if (ob->wl) ob->wl->heart_beats++; if (d_flag) debug_message ("Heartbeat function %s for object %s called.\n", ((struct lnode_def *) ob->heart_beat)->name, current_object->name); call_function ((struct lnode_def *) ob->heart_beat); } command_giver = hide_command; current_object = hide_current; current_heart_beat = 0; move_current_reset (); } time_to_call_heart_beat = 0; #ifdef WIN32 if (!alarm_thread) { alarm_thread = _beginthread(alarm_run, 256, 0); } #else signal (SIGALRM, catch_alarm); alarm (2); #endif } /* * There is a file with a list of objects to be initialized at * start up. */ static void load_first_objects (void) { FILE *f; char buff[1000]; char *p; if (!load_object ("room/void")) fatal ("Could not load the void.\n"); if (e_flag) return; f = fopen (INIT_FILE, "r"); if (f == 0) return; if (setjmp (error_recovery_context)) add_message ("Anomaly in the fabric of world space.\n"); error_recovery_context_exists = 1; while (1) { if (fgets (buff, sizeof buff, f) == NULL) break; if (buff[0] == '#') continue; p = strchr (buff, '\n'); if (p != 0) *p = 0; if (buff[0] == '\0') continue; (void) printf ("Preloading: %s\n", buff); (void) load_object (buff); } error_recovery_context_exists = 0; fclose (f); } /* * Move reset to next object, one step for every call of this * routine. * Beware that the current_reset object might have been destructed * by destruct_object(), but then, it was moved to the next object. */ static void move_current_reset (void) { static long delay = 0; struct value arg; jmp_buf save_error_recovery_context; int save_rec_exists; delay--; if (delay > time (0)) return; arg.type = T_NUMBER; arg.u.number = 1; if (current_reset == 0) current_reset = obj_list; if (current_reset == 0) { return; } memcpy ((char *) save_error_recovery_context, (char *) error_recovery_context, sizeof error_recovery_context); save_rec_exists = error_recovery_context_exists; error_recovery_context_exists = 1; /* We have to catch an error here, locally. * It is not good if the error is catched globally, as the current * reset wouldn't be moved forward. */ if (setjmp (error_recovery_context)) { if (!current_reset) fatal ("Error in reset, but no object was called with reset!\n"); current_reset->reset = 0; debug_message ("Error in reset.\n"); } else { int save_not_touched = current_reset->not_touched; if (current_reset->reset && current_reset->not_touched == 0 && current_reset->swapped == 0) { (void) apply ("reset", current_reset, &arg); } current_reset->not_touched = save_not_touched + 1; if (current_reset->not_touched >= NUM_RESET_TO_SWAP && !current_reset->swapped) swap (current_reset); } memcpy ((char *) error_recovery_context, (char *) save_error_recovery_context, sizeof error_recovery_context); error_recovery_context_exists = save_rec_exists; current_reset = current_reset->next_all; if (!current_reset) { delay = 15 - num_player; if (delay < 0) delay = 0; delay = time (0) + 60 * delay; wiz_decay (); save_wiz_file (); } } /* * All destructed objects are moved int a sperate linked list, * and deallocated after program execution. */ static void remove_destructed_objects (void) { struct object *ob, *next; for (ob = obj_list_destruct; ob; ob = next) { next = ob->next_all; destruct2 (ob); } obj_list_destruct = 0; }