diff --git a/src/eressea/Jamfile b/src/eressea/Jamfile index cc6bfd495..a20d8583a 100644 --- a/src/eressea/Jamfile +++ b/src/eressea/Jamfile @@ -22,6 +22,7 @@ LUASERVER_SOURCES = unit.cpp server.cpp korrektur.c + console.c ; LinkLibraries $(SERVER) : diff --git a/src/eressea/console.c b/src/eressea/console.c new file mode 100644 index 000000000..081ba45f3 --- /dev/null +++ b/src/eressea/console.c @@ -0,0 +1,180 @@ +#include "console.h" + +/* lua includes */ +#include +#include + +/* libc includes */ +#include +#include +#include +#include +#include + +/*** Lua Console ***/ +/* +** this macro defines a function to show the prompt and reads the +** next line for manual input +*/ +#ifndef lua_readline +#define lua_readline(L,prompt) readline(L,prompt) + +/* maximum length of an input line */ +#ifndef MAXINPUT +#define MAXINPUT 512 +#endif + +static int +readline(lua_State *l, const char *prompt) +{ + static char buffer[MAXINPUT]; + if (prompt) { + fputs(prompt, stdout); + fflush(stdout); + } + if (fgets(buffer, sizeof(buffer), stdin) == NULL) + return 0; /* read fails */ + else { + lua_pushstring(l, buffer); + return 1; + } +} +#endif + +#ifndef PROMPT +#define PROMPT "E> " +#endif + +#ifndef PROMPT2 +#define PROMPT2 "E>> " +#endif + +static const char * +get_prompt(lua_State * L, int firstline) +{ + const char *p = NULL; + lua_pushstring(L, firstline ? "_PROMPT" : "_PROMPT2"); + lua_rawget(L, LUA_GLOBALSINDEX); + p = lua_tostring(L, -1); + if (p == NULL) p = (firstline ? PROMPT : PROMPT2); + lua_pop(L, 1); /* remove global */ + return p; +} + +static int +incomplete(lua_State * L, int status) +{ + if (status!=LUA_ERRSYNTAX) return 0; + if (strstr(lua_tostring(L, -1), "near `'") == NULL) return 0; + + lua_pop(L, 1); + return 1; +} + +static void +l_message(const char *pname, const char *msg) +{ + if (pname) fprintf(stderr, "%s: ", pname); + fprintf(stderr, "%s\n", msg); +} + +static int +l_report(lua_State * L, int status) +{ + const char *msg; + if (status) { + msg = lua_tostring(L, -1); + if (msg == NULL) msg = "(error with no message)"; + l_message(NULL, msg); + lua_pop(L, 1); + } + return status; +} + +/* +** this macro can be used by some `history' system to save lines +** read in manual input +*/ +#ifndef lua_saveline +#define lua_saveline(L,line) /* empty */ +#endif + +static int +load_string(lua_State * L) +{ + int status; + lua_settop(L, 0); + if (lua_readline(L, get_prompt(L, 1)) == 0) /* no input? */ + return -1; + if (lua_tostring(L, -1)[0] == '=') { /* line starts with `=' ? */ + lua_pushfstring(L, "return %s", lua_tostring(L, -1)+1);/* `=' -> `return' */ + lua_remove(L, -2); /* remove original line */ + } + for (;;) { /* repeat until gets a complete line */ + status = luaL_loadbuffer(L, lua_tostring(L, 1), lua_strlen(L, 1), "=stdin"); + if (!incomplete(L, status)) break; /* cannot try to add lines? */ + if (lua_readline(L, get_prompt(L, 0)) == 0) /* no more input? */ + return -1; + lua_concat(L, lua_gettop(L)); /* join lines */ + } + lua_saveline(L, lua_tostring(L, 1)); + lua_remove(L, 1); /* remove line */ + return status; +} + +static void +lstop(lua_State *l, lua_Debug *ar) +{ + (void)ar; /* unused arg. */ + lua_sethook(l, NULL, 0, 0); + luaL_error(l, "interrupted!"); +} + +static lua_State * global_state = NULL; +static void +laction(int i) +{ + signal(i, SIG_DFL); /* if another SIGINT happens before lstop, + terminate process (default action) */ + assert(global_state!=NULL); + lua_sethook(global_state, lstop, LUA_MASKCALL | LUA_MASKRET | LUA_MASKCOUNT, 1); +} + +static int +lcall(lua_State * L, int narg, int clear) +{ + int status; + int base = lua_gettop(L) - narg; /* function index */ + lua_pushliteral(L, "_TRACEBACK"); + lua_rawget(L, LUA_GLOBALSINDEX); /* get traceback function */ + lua_insert(L, base); /* put it under chunk and args */ + assert(global_state==NULL); + global_state = L; /* baaaad hack */ + signal(SIGINT, laction); + status = lua_pcall(L, narg, (clear ? 0 : LUA_MULTRET), base); + signal(SIGINT, SIG_DFL); + assert(global_state==L); + global_state=NULL; + lua_remove(L, base); /* remove traceback function */ + return status; +} + +int +lua_console(lua_State * L) +{ + int status; + while ((status = load_string(L)) != -1) { + if (status == 0) status = lcall(L, 0, 0); + l_report(L, status); + if (status == 0 && lua_gettop(L) > 0) { /* any result to print? */ + lua_getglobal(L, "print"); + lua_insert(L, 1); + if (lua_pcall(L, lua_gettop(L)-1, 0, 0) != 0) + l_message(NULL, lua_pushfstring(L, "error calling `print' (%s)", + lua_tostring(L, -1))); + } + } + lua_settop(L, 0); /* clear stack */ + fputs("\n", stdout); + return 0; +} diff --git a/src/eressea/console.h b/src/eressea/console.h new file mode 100644 index 000000000..b0f101d74 --- /dev/null +++ b/src/eressea/console.h @@ -0,0 +1,20 @@ +/* vi: set ts=2: + * Eressea PB(E)M host Christian Schlittchen (corwin@amber.kn-bremen.de) + * (C) 1998-2004 Katja Zedel (katze@felidae.kn-bremen.de) + * Enno Rehling (enno@eressea-pbem.de) + * + * This program may not be used, modified or distributed without + * prior permission by the authors of Eressea. + **/ + +#ifdef __cplusplus +extern "C" { +#endif +#include + + extern int lua_console(lua_State * L); + +#ifdef __cplusplus +} +#endif + diff --git a/src/eressea/eressea-lua.vcproj b/src/eressea/eressea-lua.vcproj index a4fa815b2..4d32a3c5d 100644 --- a/src/eressea/eressea-lua.vcproj +++ b/src/eressea/eressea-lua.vcproj @@ -339,6 +339,12 @@ + + + + #include "korrektur.h" +#include "console.h" /* initialization - TODO: init in separate module */ #include @@ -97,44 +98,43 @@ #include #include #include - /** - ** global variables we are importing from other modules - **/ +** global variables we are importing from other modules +**/ extern "C" { -extern char * g_reportdir; -extern char * g_datadir; -extern char * g_basedir; -extern char * g_resourcedir; -extern item_type * i_silver; + extern char * g_reportdir; + extern char * g_datadir; + extern char * g_basedir; + extern char * g_resourcedir; + extern item_type * i_silver; -extern boolean nonr; -extern boolean nocr; -extern boolean noreports; -extern boolean nomer; -extern boolean nomsg; -extern boolean nobattle; -extern boolean nomonsters; -extern boolean nobattledebug; -extern boolean dirtyload; + extern boolean nonr; + extern boolean nocr; + extern boolean noreports; + extern boolean nomer; + extern boolean nomsg; + extern boolean nobattle; + extern boolean nomonsters; + extern boolean nobattledebug; + extern boolean dirtyload; -extern int demonfix; -extern int loadplane; + extern int demonfix; + extern int loadplane; -extern void debug_messagetypes(FILE * out); -extern void free_region(region * r); -extern void render_init(void); -extern void free_borders(void); -extern boolean opt_cr_absolute_coords; + extern void debug_messagetypes(FILE * out); + extern void free_region(region * r); + extern void render_init(void); + extern void free_borders(void); + extern boolean opt_cr_absolute_coords; #ifdef FUZZY_BASE36 -extern int fuzzy_hits; + extern int fuzzy_hits; #endif /* FUZZY_BASE36 */ } /** - ** global variables that we are exporting - **/ +** global variables that we are exporting +**/ static char * orders = NULL; static int nowrite = 0; static boolean g_writemap = false; @@ -142,27 +142,11 @@ static boolean opt_reportonly = false; static const char * luafile = "default.lua"; struct settings global = { - "Eressea", /* gamename */ - "eressea", /* resourcepath */ - 1000, /* maxunits */ + "Eressea", /* gamename */ + "eressea", /* resourcepath */ + 1000, /* maxunits */ }; -#if 0 -static int -crwritemap(void) -{ - FILE * F = fopen("world.cr", "w+"); - region * r; - for (r=regions;r;r=r->next) { - plane * p = rplane(r); - fprintf(F, "REGION %d %d %d\n", r->x, r->y, p?p->id:0); - fprintf(F, "\"%s\";Name\n\"%s\";Terrain\n", rname(r, default_locale), LOC(default_locale, terrain[rterrain(r)].name)); - } - fclose(F); - return 0; -} -#endif - static void game_init(void) { @@ -195,7 +179,7 @@ game_init(void) init_data(xmlfile); init_locales(); -/* init_resources(); must be done inside the xml-read, because requirements use items */ + /* init_resources(); must be done inside the xml-read, because requirements use items */ init_attributes(); init_races(); @@ -223,24 +207,24 @@ game_init(void) static void getgarbage(void) { - faction *f; + faction *f; - /* Get rid of stuff that was only relevant last turn */ + /* Get rid of stuff that was only relevant last turn */ - for (f = factions; f; f = f->next) { -/* memset(f->showdata, 0, sizeof f->showdata); */ + for (f = factions; f; f = f->next) { + /* memset(f->showdata, 0, sizeof f->showdata); */ - freestrlist(f->mistakes); - f->mistakes = 0; - /* TODO: free msgs */ - } + freestrlist(f->mistakes); + f->mistakes = 0; + /* TODO: free msgs */ + } #if 0 - for (r = regions; r; r = r->next) { - freestrlist(r->comments); - r->comments = 0; - freestrlist(r->botschaften); - r->botschaften = 0; - } + for (r = regions; r; r = r->next) { + freestrlist(r->comments); + r->comments = 0; + freestrlist(r->botschaften); + r->botschaften = 0; + } #endif } @@ -340,27 +324,27 @@ update_subscriptions(void) int process_orders() { - struct summary * begin, * end; + struct summary * begin, * end; #ifdef SHORTPWDS readshortpwds("passwords"); #endif - begin = make_summary(false); - printf(" - Korrekturen Runde %d\n", turn); - korrektur(); - turn++; - puts(" - entferne Texte der letzten Runde"); - getgarbage(); - puts(" - Nehme Korrekturen am Datenbestand vor"); + begin = make_summary(false); + printf(" - Korrekturen Runde %d\n", turn); + korrektur(); + turn++; + puts(" - entferne Texte der letzten Runde"); + getgarbage(); + puts(" - Nehme Korrekturen am Datenbestand vor"); #if BENCHMARK - exit(0); + exit(0); #endif - processorders(); - score(); + processorders(); + score(); #ifdef WACH_WAFF - remove_unequipped_guarded(); + remove_unequipped_guarded(); #endif - korrektur_end(); + korrektur_end(); end = make_summary(true); report_summary(end, begin, false); @@ -376,65 +360,65 @@ process_orders() static void game_done(void) { - /* Diese Routine enfernt allen allokierten Speicher wieder. Das ist nur - * zum Debugging interessant, wenn man Leak Detection hat, und nach - * nicht freigegebenem Speicher sucht, der nicht bis zum Ende benötigt - * wird (temporäre Hilsstrukturen) */ - unit *u, *u2; - region *r, *r2; - building *b, *b2; - faction *f, *f2; - ship *s, *s2; + /* Diese Routine enfernt allen allokierten Speicher wieder. Das ist nur + * zum Debugging interessant, wenn man Leak Detection hat, und nach + * nicht freigegebenem Speicher sucht, der nicht bis zum Ende benötigt + * wird (temporäre Hilsstrukturen) */ + unit *u, *u2; + region *r, *r2; + building *b, *b2; + faction *f, *f2; + ship *s, *s2; - free(used_faction_ids); - for (r = regions; r; r = r2) { + free(used_faction_ids); + for (r = regions; r; r = r2) { #if 0 - msg * m = r->msgs; - while (m) { - msg * x = m; - m = m->next; - if (x->type->finalize) x->type->finalize(x); - free(x); - } - rm = rm->next; - } + msg * m = r->msgs; + while (m) { + msg * x = m; + m = m->next; + if (x->type->finalize) x->type->finalize(x); + free(x); + } + rm = rm->next; + } #endif - for (u = r->units; u; u = u2) { - u2 = u->next; - stripunit(u); - uunhash(u); - free(u); - } - for (b = r->buildings; b; b = b2) { - free(b->name); - free(b->display); - b2 = b->next; - free(b); - } - for (s = r->ships; s; s = s2) { - free(s->name); - free(s->display); - s2 = s->next; - free(s); - } - r2 = r->next; - free_region(r); - } - for (f = factions; f; f = f2) { - stripfaction(f); - f2 = f->next; - free(f); - } - while (planes) { - plane * pl = planes; - planes = planes->next; - free(pl); - } + for (u = r->units; u; u = u2) { + u2 = u->next; + stripunit(u); + uunhash(u); + free(u); + } + for (b = r->buildings; b; b = b2) { + free(b->name); + free(b->display); + b2 = b->next; + free(b); + } + for (s = r->ships; s; s = s2) { + free(s->name); + free(s->display); + s2 = s->next; + free(s); + } + r2 = r->next; + free_region(r); +} +for (f = factions; f; f = f2) { + stripfaction(f); + f2 = f->next; + free(f); +} +while (planes) { + plane * pl = planes; + planes = planes->next; + free(pl); +} #ifdef LEAK_DETECT - leak_report(stderr); +leak_report(stderr); #endif - creport_cleanup(); - report_cleanup(); +creport_cleanup(); +report_cleanup(); } #endif @@ -459,30 +443,30 @@ init_malloc_debug(void) static int usage(const char * prog, const char * arg) { - if (arg) { - fprintf(stderr, "unknown argument: %s\n\n", arg); - } - fprintf(stderr, "Usage: %s [options]\n" - "-x n : Lädt nur die ersten n regionen\n" - "-f x y : Lädt nur die regionen ab (x,y)\n" - "-v befehlsdatei : verarbeitet automatisch die angegebene Befehlsdatei\n" - "-d datadir : gibt das datenverzeichnis an\n" - "-b basedir : gibt das basisverzeichnis an\n" - "-r resdir : gibt das resourceverzeichnis an\n" - "-t turn : read this datafile, not the most current one\n" - "-o reportdir : gibt das reportverzeichnis an\n" - "-l logfile : specify an alternative logfile\n" - "-R : erstellt nur die Reports neu\n" - "--nomsg : keine Messages (RAM sparen)\n" - "--nobattle : keine Kämpfe\n" - "--nomonsters : keine monster KI\n" - "--nodebug : keine Logfiles für Kämpfe\n" - "--debug : schreibt Debug-Ausgaben in die Datei debug\n" - "--nocr : keine CRs\n" - "--nonr : keine Reports\n" - "--crabsolute : absolute Koordinaten im CR\n" - "--help : help\n", prog); - return -1; + if (arg) { + fprintf(stderr, "unknown argument: %s\n\n", arg); + } + fprintf(stderr, "Usage: %s [options]\n" + "-x n : Lädt nur die ersten n regionen\n" + "-f x y : Lädt nur die regionen ab (x,y)\n" + "-v befehlsdatei : verarbeitet automatisch die angegebene Befehlsdatei\n" + "-d datadir : gibt das datenverzeichnis an\n" + "-b basedir : gibt das basisverzeichnis an\n" + "-r resdir : gibt das resourceverzeichnis an\n" + "-t turn : read this datafile, not the most current one\n" + "-o reportdir : gibt das reportverzeichnis an\n" + "-l logfile : specify an alternative logfile\n" + "-R : erstellt nur die Reports neu\n" + "--nomsg : keine Messages (RAM sparen)\n" + "--nobattle : keine Kämpfe\n" + "--nomonsters : keine monster KI\n" + "--nodebug : keine Logfiles für Kämpfe\n" + "--debug : schreibt Debug-Ausgaben in die Datei debug\n" + "--nocr : keine CRs\n" + "--nonr : keine Reports\n" + "--crabsolute : absolute Koordinaten im CR\n" + "--help : help\n", prog); + return -1; } static void @@ -502,80 +486,84 @@ setLuaNumber(lua_State * luaState, const char * name, double value) static int read_args(int argc, char **argv, lua_State * luaState) { - int i; + int i; char * c; - for (i=1;i!=argc;++i) { - if (argv[i][0]!='-') { - return usage(argv[0], argv[i]); - } else if (argv[i][1]=='-') { /* long format */ - if (strcmp(argv[i]+2, "nocr")==0) nocr = true; - else if (strcmp(argv[i]+2, "nosave")==0) nowrite = true; - else if (strcmp(argv[i]+2, "noreports")==0) { - noreports = true; - nocr = true; - nocr = true; - } - else if (strcmp(argv[i]+2, "xml")==0) xmlfile = argv[++i]; - else if (strcmp(argv[i]+2, "dirtyload")==0) dirtyload = true; - else if (strcmp(argv[i]+2, "nonr")==0) nonr = true; - else if (strcmp(argv[i]+2, "nomsg")==0) nomsg = true; - else if (strcmp(argv[i]+2, "nobattle")==0) nobattle = true; - else if (strcmp(argv[i]+2, "nomonsters")==0) nomonsters = true; - else if (strcmp(argv[i]+2, "nodebug")==0) nobattledebug = true; - else if (strcmp(argv[i]+2, "crabsolute")==0) opt_cr_absolute_coords = true; - else if (strcmp(argv[i]+2, "help")==0) - return usage(argv[0], NULL); - else - return usage(argv[0], argv[i]); - } else switch(argv[i][1]) { - case 'o': - g_reportdir = argv[++i]; - break; + for (i=1;i!=argc;++i) { + if (argv[i][0]!='-') { + return usage(argv[0], argv[i]); + } else if (argv[i][1]=='-') { /* long format */ + if (strcmp(argv[i]+2, "nocr")==0) nocr = true; + else if (strcmp(argv[i]+2, "nosave")==0) nowrite = true; + else if (strcmp(argv[i]+2, "noreports")==0) { + noreports = true; + nocr = true; + nocr = true; + } + else if (strcmp(argv[i]+2, "xml")==0) xmlfile = argv[++i]; + else if (strcmp(argv[i]+2, "dirtyload")==0) dirtyload = true; + else if (strcmp(argv[i]+2, "nonr")==0) nonr = true; + else if (strcmp(argv[i]+2, "nomsg")==0) nomsg = true; + else if (strcmp(argv[i]+2, "nobattle")==0) nobattle = true; + else if (strcmp(argv[i]+2, "nomonsters")==0) nomonsters = true; + else if (strcmp(argv[i]+2, "nodebug")==0) nobattledebug = true; + else if (strcmp(argv[i]+2, "console")==0) luafile=NULL; + else if (strcmp(argv[i]+2, "crabsolute")==0) opt_cr_absolute_coords = true; + else if (strcmp(argv[i]+2, "help")==0) + return usage(argv[0], NULL); + else + return usage(argv[0], argv[i]); + } else switch(argv[i][1]) { + case 'C': + luafile=NULL; + break; + case 'o': + g_reportdir = argv[++i]; + break; case 'e': luafile = argv[++i]; break; - case 'D': /* DEBUG */ - demonfix = atoi(argv[++i]); - break; - case 'd': - g_datadir = argv[++i]; - break; - case 'r': - g_resourcedir = argv[++i]; - break; - case 'b': - g_basedir = argv[++i]; - break; - case 'i': - xmlfile = argv[++i]; - break; - case 't': - turn = atoi(argv[++i]); - break; - case 'f': - firstx = atoi(argv[++i]); - firsty = atoi(argv[++i]); - break; - case 'q': - quiet = 1; - break; - case 'v': + case 'D': /* DEBUG */ + demonfix = atoi(argv[++i]); + break; + case 'd': + g_datadir = argv[++i]; + break; + case 'r': + g_resourcedir = argv[++i]; + break; + case 'b': + g_basedir = argv[++i]; + break; + case 'i': + xmlfile = argv[++i]; + break; + case 't': + turn = atoi(argv[++i]); + break; + case 'f': + firstx = atoi(argv[++i]); + firsty = atoi(argv[++i]); + break; + case 'q': + quiet = 1; + break; + case 'v': if (i