#include "quakedef.h" #ifdef VM_LUA #include "pr_common.h" #include "hash.h" #define LUA_MALLOC_TAG 0x55780128 #define luagloballist \ globalentity (true, self) \ globalentity (true, other) \ globalentity (true, world) \ globalfloat (true, time) \ globalfloat (true, frametime) \ globalentity (false, newmis) \ globalfloat (false, force_retouch) \ globalstring (true, mapname) \ globalfloat (false, deathmatch) \ globalfloat (false, coop) \ globalfloat (false, teamplay) \ globalfloat (true, serverflags) \ globalfloat (false, dimension_send) \ globalfloat (false, physics_mode) \ globalfloat (true, total_secrets) \ globalfloat (true, total_monsters) \ globalfloat (true, found_secrets) \ globalfloat (true, killed_monsters) \ globalvec (true, v_forward) \ globalvec (true, v_up) \ globalvec (true, v_right) \ globalfloat (true, trace_allsolid) \ globalfloat (true, trace_startsolid) \ globalfloat (true, trace_fraction) \ globalvec (true, trace_endpos) \ globalvec (true, trace_plane_normal) \ globalfloat (true, trace_plane_dist) \ globalentity (true, trace_ent) \ globalfloat (true, trace_inopen) \ globalfloat (true, trace_inwater) \ globalfloat (false, trace_endcontentsf) \ globalint (false, trace_endcontentsi) \ globalfloat (false, trace_surfaceflagsf) \ globalint (false, trace_surfaceflagsi) \ globalfloat (false, cycle_wrapped) \ globalentity (false, msg_entity) \ globalfunc (false, main) \ globalfunc (true, StartFrame) \ globalfunc (true, PlayerPreThink) \ globalfunc (true, PlayerPostThink) \ globalfunc (true, ClientKill) \ globalfunc (true, ClientConnect) \ globalfunc (true, PutClientInServer) \ globalfunc (true, ClientDisconnect) \ globalfunc (false, SetNewParms) \ globalfunc (false, SetChangeParms) \ globalfloat (false, dimension_default) \ globalvec (false, global_gravitydir) //any globals or functions that the server might want access to need to be known also. #define luaextragloballist \ globalstring (true, startspot) \ globalfunc (true, ClientReEnter) typedef struct { #define globalentity(required, name) int name; #define globalint(required, name) int name; #define globalfloat(required, name) float name; #define globalstring(required, name) string_t name; #define globalvec(required, name) vec3_t name; #define globalfunc(required, name) int name; luagloballist luaextragloballist #undef globalentity #undef globalint #undef globalfloat #undef globalstring #undef globalvec #undef globalfunc float parm[NUM_SPAWN_PARMS]; } luaglobalvars_t; typedef struct { int type; qintptr_t offset; char *name; bucket_t buck; } luafld_t; //#define LIBLUA_STATIC #ifdef LIBLUA_STATIC #ifdef _MSC_VER #pragma comment(lib, "liblua.lib") #endif #include //#include #include #else typedef struct lua_State lua_State; typedef void *(QDECL *lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize); typedef const char *(QDECL *lua_Reader)(lua_State *L, void *data, size_t *size); typedef int (QDECL *lua_CFunction) (lua_State *L); typedef double lua_Number; typedef long long lua_Integer; #define lua_pcall(L,n,r,f) lua_pcallk(L, (n), (r), (f), 0, NULL) #define lua_call(L,n,r) lua_callk(L, (n), (r), 0, NULL) #define lua_pop(L,n) lua_settop(L, -(n)-1) #define lua_pushstring(L,s) lua_pushfstring(L,"%s",s) //#define lua_remove(L,idx) (lua_rotate(L, (idx), -1), lua_pop(L, 1)) #define lua_replace(L,idx) (lua_copy(L, -1, (idx)), lua_pop(L, 1)) #define lua_pushglobaltable(L) (lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS)) #define luaL_getmetatable(L,n) (lua_getfield(L, LUA_REGISTRYINDEX, (n))) #define LUA_TNONE (-1) #define LUA_TNIL 0 #define LUA_TBOOLEAN 1 #define LUA_TLIGHTUSERDATA 2 #define LUA_TNUMBER 3 #define LUA_TSTRING 4 #define LUA_TTABLE 5 #define LUA_TFUNCTION 6 #define LUA_TUSERDATA 7 #define LUA_TTHREAD 8 #define LUA_NUMTAGS 9 #define LUA_RIDX_GLOBALS 2 #define LUA_REGISTRYINDEX (-1000000 - 1000) #define lua_newstate lua.newstate #define lua_atpanic lua.atpanic #define lua_close lua.close #define lua_load lua.load #define lua_pcallk lua.pcallk #define lua_callk lua.callk #define lua_getfield lua.getfield #define lua_setfield lua.setfield #define lua_gettable lua.gettable #define lua_settable lua.settable #define lua_getglobal lua.getglobal #define lua_setglobal lua.setglobal #define lua_error lua.error #define lua_type lua.type #define lua_typename lua.typename #define lua_rawget lua.rawget #define lua_rawgeti lua.rawgeti #define lua_rawgetp lua.rawgetp #define lua_rawset lua.rawset #define lua_createtable lua.createtable #define lua_setmetatable lua.setmetatable #define lua_newuserdata lua.newuserdata #define lua_copy lua.copy #define lua_gettop lua.gettop #define lua_settop lua.settop #define lua_pushboolean lua.pushboolean #define lua_pushnil lua.pushnil #define lua_pushnumber lua.pushnumber #define lua_pushinteger lua.pushinteger #define lua_pushvalue lua.pushvalue #define lua_pushcclosure lua.pushcclosure #define lua_pushfstring lua.pushfstring #define lua_pushliteral lua_pushstring #define lua_pushlightuserdata lua.pushlightuserdata #define lua_tolstring lua.tolstring #define lua_toboolean lua.toboolean #define lua_tonumberx lua.tonumberx #define lua_tointegerx lua.tointegerx #define lua_topointer lua.topointer #define lua_touserdata lua.touserdata #define lua_touserdata lua.touserdata #define lua_next lua.next //#define lua_remove lua.remove #define luaL_callmeta lua.Lcallmeta #define luaL_newmetatable lua.Lnewmetatable #define lua_newtable(L) lua_createtable(L,0,0) #define lua_pushcfunction(L,f) lua_pushcclosure(L,f,0) #define lua_isnil(L,i) (lua_type(L,i)==LUA_TNIL) #define lua_tostring(L,i) lua_tolstring(L,i,NULL) #define lua_tonumber(L,i) lua_tonumberx(L,i,NULL) #define lua_tointeger(L,i) lua_tointegerx(L,i,NULL) #endif //I'm using this struct for all the global stuff. static struct { lua_State *ctx; char readbuf[1024]; pubprogfuncs_t progfuncs; progexterns_t progfuncsparms; edict_t **edicttable; unsigned int maxedicts; luaglobalvars_t globals; //internal global structure hashtable_t globalfields; //name->luafld_t luafld_t globflds[1024]; //fld->offset+type hashtable_t entityfields; //name->luafld_t luafld_t entflds[1024]; //fld->offset+type size_t numflds; #ifndef LIBLUA_STATIC qboolean triedlib; dllhandle_t *lib; lua_State * (QDECL *newstate) (lua_Alloc f, void *ud); lua_CFunction (QDECL *atpanic) (lua_State *L, lua_CFunction panicf); void (QDECL *close) (lua_State *L); int (QDECL *load) (lua_State *L, lua_Reader reader, void *dt, const char *chunkname, const char *mode); int (QDECL *pcallk) (lua_State *L, int nargs, int nresults, int errfunc, int ctx, lua_CFunction k); void (QDECL *callk) (lua_State *L, int nargs, int nresults, int ctx, lua_CFunction k); void (QDECL *getfield) (lua_State *L, int idx, const char *k); void (QDECL *setfield) (lua_State *L, int idx, const char *k); void (QDECL *gettable) (lua_State *L, int idx); void (QDECL *settable) (lua_State *L, int idx); void (QDECL *getglobal) (lua_State *L, const char *var); void (QDECL *setglobal) (lua_State *L, const char *var); int (QDECL *error) (lua_State *L); int (QDECL *type) (lua_State *L, int idx); const char *(QDECL *typename) (lua_State *L, int tp); int (QDECL *rawget) (lua_State *L, int idx); int (QDECL *rawgeti) (lua_State *L, int idx, lua_Integer n); int (QDECL *rawgetp) (lua_State *L, int idx, const void *p); void (QDECL *rawset) (lua_State *L, int idx); void (QDECL *createtable) (lua_State *L, int narr, int nrec); int (QDECL *setmetatable) (lua_State *L, int objindex); void *(QDECL *newuserdata) (lua_State *L, size_t usize); void (QDECL *copy) (lua_State *L, int fromidx, int toidx); //added in 5.3 // void (QDECL *replace) (lua_State *L, int idx); //removed in 5.3 int (QDECL *gettop) (lua_State *L); int (QDECL *settop) (lua_State *L, int idx); void (QDECL *pushboolean) (lua_State *L, int b); void (QDECL *pushnil) (lua_State *L); void (QDECL *pushnumber) (lua_State *L, lua_Number n); void (QDECL *pushinteger) (lua_State *L, lua_Integer n); void (QDECL *pushvalue) (lua_State *L, int idx); void (QDECL *pushcclosure) (lua_State *L, lua_CFunction fn, int n); const char * (QDECL *pushfstring) (lua_State *L, const char *fmt, ...); void (QDECL *pushlightuserdata) (lua_State *L, void *p); const char * (QDECL *tolstring) (lua_State *L, int idx, size_t *len); int (QDECL *toboolean) (lua_State *L, int idx); lua_Number (QDECL *tonumberx) (lua_State *L, int idx, int *isnum); lua_Integer (QDECL *tointegerx) (lua_State *L, int idx, int *isnum); const void *(QDECL *topointer) (lua_State *L, int idx); void *(QDECL *touserdata) (lua_State *L, int idx); int (QDECL *next) (lua_State *L, int idx); // void (QDECL *remove) (lua_State *L, int idx); int (QDECL *Lcallmeta) (lua_State *L, int obj, const char *e); int (QDECL *Lnewmetatable) (lua_State *L, const char *tname); #endif } lua; static qboolean init_lua(void) { #ifndef LIBLUA_STATIC if (!lua.triedlib) { dllfunction_t luafuncs[] = { {(void*)&lua.newstate, "lua_newstate"}, {(void*)&lua.atpanic, "lua_atpanic"}, {(void*)&lua.close, "lua_close"}, {(void*)&lua.load, "lua_load"}, {(void*)&lua.pcallk, "lua_pcallk"}, {(void*)&lua.callk, "lua_callk"}, {(void*)&lua.getfield, "lua_getfield"}, {(void*)&lua.setfield, "lua_setfield"}, {(void*)&lua.gettable, "lua_gettable"}, {(void*)&lua.settable, "lua_settable"}, {(void*)&lua.getglobal, "lua_getglobal"}, {(void*)&lua.setglobal, "lua_setglobal"}, {(void*)&lua.error, "lua_error"}, {(void*)&lua.type, "lua_type"}, {(void*)&lua.typename, "lua_typename"}, {(void*)&lua.rawget, "lua_rawget"}, {(void*)&lua.rawgeti, "lua_rawgeti"}, {(void*)&lua.rawgetp, "lua_rawgetp"}, {(void*)&lua.rawset, "lua_rawset"}, {(void*)&lua.createtable, "lua_createtable"}, {(void*)&lua.setmetatable, "lua_setmetatable"}, {(void*)&lua.newuserdata, "lua_newuserdata"}, {(void*)&lua.copy, "lua_copy"}, // {(void*)&lua.replace, "lua_replace"}, {(void*)&lua.gettop, "lua_gettop"}, {(void*)&lua.settop, "lua_settop"}, {(void*)&lua.pushboolean, "lua_pushboolean"}, {(void*)&lua.pushnil, "lua_pushnil"}, {(void*)&lua.pushnumber, "lua_pushnumber"}, {(void*)&lua.pushinteger, "lua_pushinteger"}, {(void*)&lua.pushvalue, "lua_pushvalue"}, {(void*)&lua.pushcclosure, "lua_pushcclosure"}, {(void*)&lua.pushfstring, "lua_pushfstring"}, {(void*)&lua.pushlightuserdata, "lua_pushlightuserdata"}, {(void*)&lua.tolstring, "lua_tolstring"}, {(void*)&lua.toboolean, "lua_toboolean"}, {(void*)&lua.tonumberx, "lua_tonumberx"}, {(void*)&lua.tointegerx, "lua_tointegerx"}, {(void*)&lua.topointer, "lua_topointer"}, {(void*)&lua.touserdata, "lua_touserdata"}, {(void*)&lua.next, "lua_next"}, // {(void*)&lua.remove, "lua_remove"}, {(void*)&lua.Lcallmeta, "luaL_callmeta"}, {(void*)&lua.Lnewmetatable, "luaL_newmetatable"}, {NULL, NULL} }; lua.triedlib = true; lua.lib = Sys_LoadLibrary("lua53", luafuncs); } if (!lua.lib) return false; #endif return true; } char *QDECL Lua_AddString(pubprogfuncs_t *prinst, const char *val, int minlength, pbool demarkup); static void lua_pushedict(lua_State *L, struct edict_s *ent) { lua_rawgetp(L, LUA_REGISTRYINDEX, ent); } /* static void lua_debugstack(lua_State *L) { int idx; int top = lua_gettop(L); for (idx = 1; idx <= top; idx++) { if (luaL_callmeta(L, idx, "__tostring")) { Con_Printf("Stack%i: %s\n", idx, lua_tolstring(L, -1, NULL)); lua_pop(L, 1); } else { int t = lua_type(L, idx); switch (t) { case LUA_TNUMBER: Con_Printf("Stack%i: %g\n", idx, lua_tonumber(L, idx)); break; case LUA_TSTRING: Con_Printf("Stack%i: %s\n", idx, lua_tolstring(L, idx, NULL)); break; case LUA_TBOOLEAN: Con_Printf("Stack%i: %s\n", idx, (lua_toboolean(L, idx) ? "true" : "false")); break; case LUA_TNIL: Con_Printf("Stack%i: %s\n", idx, "nil"); break; case LUA_TTABLE: //special check for things that look like vectors. lua_getfield(L, idx, "x"); lua_getfield(L, idx, "y"); lua_getfield(L, idx, "z"); if (lua_type(L, -3) == LUA_TNUMBER && lua_type(L, -2) == LUA_TNUMBER && lua_type(L, -1) == LUA_TNUMBER) { Con_Printf("Stack%i: '%f %f %f'\n", idx, lua_tonumberx(L, -3, NULL), lua_tonumberx(L, -2, NULL), lua_tonumberx(L, -1, NULL)); lua_pop(L, 3); break; } lua_pop(L, 3); default: Con_Printf("Stack%i: %s: %p\n", idx, lua_typename(L, lua_type(L, idx)), lua_topointer(L, idx)); break; } } } } */ static void *my_lua_alloc (void *ud, void *ptr, size_t osize, size_t nsize) { if (nsize == 0) { free(ptr); return NULL; } else return realloc(ptr, nsize); } const char * my_lua_Reader(lua_State *L, void *data, size_t *size) { vfsfile_t *f = data; *size = VFS_READ(f, lua.readbuf, sizeof(lua.readbuf)); return lua.readbuf; } //replace lua's standard 'print' function to use the console instead of stdout. intended to use the same linebreak rules. static int bi_lua_print(lua_State *L) { //the problem is that we can only really accept strings here. //so lets just use the tostring function to make sure things are actually readable as strings. int args = lua_gettop(L); int i; const char *s; lua_getglobal(L, "tostring"); //args now start at 1 for(i = 1; i <= args; i++) { lua_pushvalue(L, -1); lua_pushvalue(L, i); lua_pcall(L, 1, 1, 0); //pops args+func s = lua_tolstring(L, -1, NULL); if(s == NULL) s = "?"; if(i > 1) Con_Printf("\t"); Con_Printf("%s", s); lua_pop(L, 1); //pop our lstring }; lua_pop(L, 1); //pop the cached tostring. Con_Printf("\n"); return 0; }; //more like quakec's print static int bi_lua_conprint(lua_State *L) { //the problem is that we can only really accept strings here. //so lets just use the tostring function to make sure things are actually readable as strings. int args = lua_gettop(L); int i; const char *s; lua_getglobal(L, "tostring"); //args start at stack index 1 for(i = 1; i <= args; i++) { lua_pushvalue(L, -1); //dupe the tostring lua_pushvalue(L, i); //dupe the argument lua_pcall(L, 1, 1, 0); //pops args+func, pushes the string result s = lua_tolstring(L, -1, NULL); if(s == NULL) s = "?"; Con_Printf("%s", s); lua_pop(L, 1); //pop our lstring }; lua_pop(L, 1); //pop the cached tostring. return 0; }; static int bi_lua_dprint(lua_State *L) { if (!developer.ival) return 0; return bi_lua_conprint(L); } //taken from lua's baselib.c, with dependancies reduced a little. static int bi_lua_tostring(lua_State *L) { // if (lua_type(L, 1) == LUA_TNONE) // luaL_argerror(L, narg, "value expected"); if (luaL_callmeta(L, 1, "__tostring")) return 1; switch (lua_type(L, 1)) { case LUA_TNUMBER: lua_pushfstring(L, lua_tolstring(L, 1, NULL)); break; case LUA_TSTRING: lua_pushvalue(L, 1); break; case LUA_TBOOLEAN: lua_pushstring(L, (lua_toboolean(L, 1) ? "true" : "false")); break; case LUA_TNIL: lua_pushstring(L, "nil"); break; case LUA_TTABLE: //special check for things that look like vectors. lua_getfield(L, 1, "x"); lua_getfield(L, 1, "y"); lua_getfield(L, 1, "z"); if (lua_type(L, -3) == LUA_TNUMBER && lua_type(L, -2) == LUA_TNUMBER && lua_type(L, -1) == LUA_TNUMBER) { lua_pushfstring(L, "'%g %g %g'", lua_tonumberx(L, -3, NULL), lua_tonumberx(L, -2, NULL), lua_tonumberx(L, -1, NULL)); return 1; } //fallthrough default: lua_pushfstring(L, "%s: %p", lua_typename(L, lua_type(L, 1)), lua_topointer(L, 1)); break; } return 1; } #define bi_lua_vtos bi_lua_tostring #define bi_lua_ftos bi_lua_tostring //taken from lua's baselib.c, with dependancies reduced a little. static int bi_lua_tonumber(lua_State *L) { // if (lua_type(L, 1) == LUA_TNONE) // luaL_argerror(L, narg, "value expected"); if (luaL_callmeta(L, 1, "__tonumber")) return 1; switch (lua_type(L, 1)) { case LUA_TSTRING: lua_pushnumber(L, atof(lua_tostring(L, 1))); break; case LUA_TNUMBER: lua_pushvalue(L, 1); break; case LUA_TBOOLEAN: lua_pushnumber(L, lua_toboolean(L, 1)); break; case LUA_TNIL: lua_pushnumber(L, 0); break; case LUA_TTABLE: //special check for things that look like vectors. lua_getfield(L, 1, "x"); lua_getfield(L, 1, "y"); lua_getfield(L, 1, "z"); if (lua_type(L, -3) == LUA_TNUMBER && lua_type(L, -2) == LUA_TNUMBER && lua_type(L, -1) == LUA_TNUMBER) { vec3_t v = {lua_tonumberx(L, -3, NULL), lua_tonumberx(L, -2, NULL), lua_tonumberx(L, -1, NULL)}; lua_pushnumber(L, DotProduct(v,v)); return 1; } lua_getfield(L, 1, "entnum"); if (lua_type(L, -1) == LUA_TNUMBER) return 1; //fallthrough default: lua_pushfstring(L, "%s: %p", lua_typename(L, lua_type(L, 1)), lua_topointer(L, 1)); break; } return 1; } #define bi_lua_stof bi_lua_tonumber static int my_lua_panic(lua_State *L) { const char *s = lua_tolstring(L, -1, NULL); Sys_Error("lua error: %s", s); } static int my_lua_entity_eq(lua_State *L) { //table1=1 //table2=2 unsigned int entnum1, entnum2; lua_getfield(L, 1, "entnum"); entnum1 = lua_tointegerx(L, -1, NULL); lua_getfield(L, 2, "entnum"); entnum2 = lua_tointegerx(L, -1, NULL); lua_pop(L, 2); lua_pushboolean(L, entnum1 == entnum2); return 1; } static int my_lua_entity_tostring(lua_State *L) { //table=1 unsigned int entnum; lua_getfield(L, 1, "entnum"); entnum = lua_tointegerx(L, -1, NULL); lua_pop(L, 1); lua_pushstring(L, va("entity: %u", entnum)); return 1; } static int my_lua_vec3_tostring(lua_State *L) { //table=1 lua_Number x,y,z; lua_getfield(L, 1, "x"); x = lua_tonumber(L, -1); lua_getfield(L, 1, "y"); y = lua_tonumber(L, -1); lua_getfield(L, 1, "z"); z = lua_tonumber(L, -1); lua_pop(L, 3); lua_pushstring(L, va("'%g %g %g'", x, y, z)); return 1; } static qboolean lua_readvector(lua_State *L, int idx, float *result) { switch(lua_type(L, idx)) { case LUA_TSTRING: { //we parse strings primnarily for easy .ent(or bsp) loading support. const char *str = lua_tolstring(L, idx, NULL); str = COM_Parse(str); result[0] = atof(com_token); str = COM_Parse(str); result[1] = atof(com_token); str = COM_Parse(str); result[2] = atof(com_token); } return true; case LUA_TTABLE: lua_getfield(L, idx, "x"); result[0] = lua_tonumberx(L, -1, NULL); lua_getfield(L, idx, "y"); result[1] = lua_tonumberx(L, -1, NULL); lua_getfield(L, idx, "z"); result[2] = lua_tonumberx(L, -1, NULL); lua_pop(L, 3); return true; case LUA_TNUMBER: result[0] = result[1] = result[2] = lua_tonumber(L, idx); break; case LUA_TNIL: result[0] = 0; result[1] = 0; result[2] = 0; break; default: Con_Printf("Expected vector, got %s\n", lua_typename(L, lua_type(L, idx))); result[0] = 0; result[1] = 0; result[2] = 0; break; } return false; } static void lua_pushvector(lua_State *L, vec_t x, vec_t y, vec_t z) { lua_newtable(L); //FIXME: should provide a metatable with a __tostring lua_pushnumber(L, x); lua_setfield (L, -2, "x"); lua_pushnumber(L, y); lua_setfield (L, -2, "y"); lua_pushnumber(L, z); lua_setfield (L, -2, "z"); luaL_getmetatable(L, "vec3_t"); lua_setmetatable(L, -2); } static int my_lua_vec3_eq(lua_State *L) { vec3_t a, b; lua_readvector(L, 1, a); lua_readvector(L, 2, b); lua_pushboolean(L, a[0]==b[0] && a[1]==b[1] && a[2]==b[2]); return 1; } static int my_lua_vec3_sub(lua_State *L) { vec3_t a, b; lua_readvector(L, 1, a); lua_readvector(L, 2, b); lua_pushvector(L, a[0]-b[0], a[1]-b[1], a[2]-b[2]); return 1; } static int my_lua_vec3_mul(lua_State *L) { vec3_t a, b; if (lua_readvector(L, 1, a) + lua_readvector(L, 2, b) == 2) lua_pushnumber(L, DotProduct(a,b)); //vec*vec is a dotproduct else lua_pushvector(L, a[0]*b[0], a[1]*b[1], a[2]*b[2]); //one arg is a scaler. woot. return 1; } static int my_lua_vec3_add(lua_State *L) { vec3_t a, b; lua_readvector(L, 1, a); lua_readvector(L, 2, b); lua_pushvector(L, a[0]+b[0], a[1]+b[1], a[2]+b[2]); return 1; } static int my_lua_vec3_len(lua_State *L) { vec3_t a; lua_readvector(L, 1, a); lua_pushnumber(L, VectorLength(a)); return 1; } static int my_lua_entity_set(lua_State *L) //__newindex { // Con_Printf("lua_entity_set: "); // my_lua_print(L); //table=1 //key=2 //value=3 if (lua_type(L, 2) == LUA_TSTRING) { const char *s = lua_tolstring(L, 2, NULL); luafld_t *fld = Hash_GetInsensitive(&lua.entityfields, s); eval_t *eval; unsigned int entnum; if (fld && fld->offset >= 0) { lua_getfield(L, 1, "entnum"); entnum = lua_tointegerx(L, -1, NULL); lua_pop(L, 1); if (entnum < lua.maxedicts && lua.edicttable[entnum] && !ED_ISFREE(lua.edicttable[entnum])) { eval = (eval_t*)((char*)lua.edicttable[entnum]->v + fld->offset); switch(fld->type) { case ev_float: eval->_float = lua_tonumberx(L, 3, NULL); return 0; case ev_vector: lua_readvector(L, 3, eval->_vector); return 0; case ev_integer: eval->_int = lua_tointegerx(L, 3, NULL); return 0; case ev_function: if (lua_type(L, 3) == LUA_TNIL) eval->function = 0; //so the engine can distinguish between nil and not. else eval->function = fld->offset | ((entnum+1)<<10); lua_pushlightuserdata(L, (void *)(qintptr_t)fld->offset); //execute only knows a function id, so we need to translate the store to match. lua_replace(L, 2); lua_rawset(L, 1); return 0; case ev_string: if (lua_type(L, 3) == LUA_TNIL) eval->string = 0; //so the engine can distinguish between nil and not. else eval->string = fld->offset | ((entnum+1)<<10); lua_pushlightuserdata(L, (void *)(qintptr_t)fld->offset); //execute only knows a string id, so we need to translate the store to match. lua_replace(L, 2); lua_rawset(L, 1); return 0; case ev_entity: //read the table's entnum field so we know which one its meant to be. lua_getfield(L, 3, "entnum"); eval->edict = lua_tointegerx(L, -1, NULL); return 0; } } } } lua_rawset(L, 1); return 0; } static int my_lua_entity_get(lua_State *L) //__index { // Con_Printf("lua_entity_get: "); // my_lua_print(L); //table=1 //key=2 if (lua_type(L, 2) == LUA_TSTRING) { const char *s = lua_tolstring(L, 2, NULL); luafld_t *fld = Hash_GetInsensitive(&lua.entityfields, s); eval_t *eval; int entnum; if (fld) { if (fld->offset < 0) { //negative offsets are not real fields, but are just there to ensure that no nils appear lua_rawget(L, 1); if (lua_isnil(L, -1)) { lua_pop(L, 1); switch(fld->type) { case ev_float: lua_pushnumber(L, 0); return 1; case ev_integer: lua_pushinteger(L, 0); return 1; case ev_vector: lua_pushvector(L, 0, 0, 0); break; case ev_function: default: //no idea lua_pushnil(L); break; case ev_string: lua_pushliteral(L, ""); break; case ev_entity: lua_pushedict(L, lua.edicttable[0]); break; } } return 1; } lua_getfield(L, 1, "entnum"); entnum = lua_tointegerx(L, -1, NULL); lua_pop(L, 1); if (entnum < lua.maxedicts && lua.edicttable[entnum])// && !lua.edicttable[entnum]->isfree) { eval = (eval_t*)((char*)lua.edicttable[entnum]->v + fld->offset); switch(fld->type) { case ev_float: lua_pushnumber(L, eval->_float); return 1; case ev_integer: lua_pushinteger(L, eval->_int); return 1; case ev_vector: lua_pushvector(L, eval->_vector[0], eval->_vector[1], eval->_vector[2]); return 1; case ev_function: case ev_string: lua_pushlightuserdata(L, (void *)(qintptr_t)(eval->function & 1023)); //execute only knows a function id, so we need to translate the store to match. lua_replace(L, 2); lua_rawget(L, 1); return 1; case ev_entity: //return the table for the entity via the lua registry. lua_pushlightuserdata(lua.ctx, lua.edicttable[eval->edict]); lua_gettable(lua.ctx, LUA_REGISTRYINDEX); return 1; } } } } //make sure it exists so we don't get called constantly if code loops through stuff that wasn't set. // lua_pushstring(L, "nil"); lua_rawget(L, 1); return 1; } static int my_lua_global_set(lua_State *L) //__newindex { // Con_Printf("my_lua_global_set: "); // my_lua_print(L); //table=1 //key=2 //value=3 if (lua_type(L, 2) == LUA_TSTRING) { const char *s = lua_tolstring(L, 2, NULL); luafld_t *fld = Hash_GetInsensitive(&lua.globalfields, s); eval_t *eval; if (fld) { eval = (eval_t*)((char*)&lua.globals + fld->offset); switch(fld->type) { case ev_float: eval->_float = lua_tonumberx(L, 3, NULL); return 0; case ev_vector: lua_getfield(L, 3, "x"); eval->_vector[0] = lua_tonumberx(L, -1, NULL); lua_getfield(L, 3, "y"); eval->_vector[1] = lua_tonumberx(L, -1, NULL); lua_getfield(L, 3, "z"); eval->_vector[2] = lua_tonumberx(L, -1, NULL); return 0; case ev_integer: eval->_int = lua_tointegerx(L, 3, NULL); return 0; case ev_function: if (lua_type(L, 3) == LUA_TNIL) eval->function = 0; //so the engine can distinguish between nil and not. else eval->function = fld->offset; lua_pushlightuserdata(L, (void *)fld->offset); //execute only knows a function id, so we need to translate the store to match. lua_replace(L, 2); lua_rawset(L, 1); return 0; case ev_string: if (lua_type(L, 3) == LUA_TNIL) eval->string = 0; //so the engine can distinguish between nil and not. else eval->string = fld->offset; lua_pushlightuserdata(L, (void *)fld->offset); //execute only knows a string id, so we need to translate the store to match. lua_replace(L, 2); lua_rawset(L, 1); return 0; case ev_entity: //read the table's entnum field so we know which one its meant to be. lua_getfield(L, 3, "entnum"); eval->edict = lua_tointegerx(L, -1, NULL); return 0; } } } lua_rawset(L, 1); return 0; } static int my_lua_global_get(lua_State *L) //__index { // Con_Printf("my_lua_global_get: "); // my_lua_print(L); //table=1 //key=2 if (lua_type(L, 2) == LUA_TSTRING) { const char *s = lua_tolstring(L, 2, NULL); luafld_t *fld = Hash_GetInsensitive(&lua.globalfields, s); eval_t *eval; if (fld) { eval = (eval_t*)((char*)&lua.globals + fld->offset); switch(fld->type) { case ev_float: lua_pushnumber(L, eval->_float); return 1; case ev_integer: lua_pushinteger(L, eval->_int); return 1; case ev_vector: lua_pushvector(L, eval->_vector[0], eval->_vector[1], eval->_vector[2]); return 1; case ev_function: lua_pushlightuserdata(L, (void *)(qintptr_t)eval->function); //execute only knows a function id, so we need to translate the store to match. lua_replace(L, 2); lua_rawget(L, 1); return 1; case ev_entity: lua_pushedict(L, lua.edicttable[eval->edict]); return 1; } } } //make sure it exists so we don't get called constantly if code loops through stuff that wasn't set. // lua_pushstring(L, "nil"); lua_rawget(L, 1); return 1; } static int bi_lua_setmodel(lua_State *L) { int entnum; edict_t *e; lua_getfield(L, 1, "entnum"); entnum = lua_tointegerx(L, -1, NULL); e = (entnum>=lua.maxedicts)?NULL:lua.edicttable[entnum]; PF_setmodel_Internal(&lua.progfuncs, e, lua_tolstring(L, 2, NULL)); return 0; } static int bi_lua_precache_model(lua_State *L) { PF_precache_model_Internal(&lua.progfuncs, lua_tolstring(L, 1, NULL), false); return 0; } #define bi_lua_precache_model2 bi_lua_precache_model static int bi_lua_precache_sound(lua_State *L) { PF_precache_sound_Internal(&lua.progfuncs, lua_tolstring(L, 1, NULL)); return 0; } #define bi_lua_precache_sound2 bi_lua_precache_sound static int bi_lua_precache_file(lua_State *L) { return 0; } static int bi_lua_lightstyle(lua_State *L) { vec3_t rgb; if (lua_gettop(L) >= 3) lua_readvector(L, 3, rgb); else VectorSet(rgb, 1, 1, 1); PF_applylightstyle(lua_tointegerx(L, 1, NULL), lua_tolstring(L, 2, NULL), rgb); return 0; } static int bi_lua_spawn(lua_State *L) { edict_t *e = lua.progfuncs.EntAlloc(&lua.progfuncs, false, 0); if (e) lua_pushedict(L, e); else lua_pushnil(L); return 1; } static int bi_lua_remove(lua_State *L) { int entnum; edict_t *e; lua_getfield(L, 1, "entnum"); entnum = lua_tointegerx(L, -1, NULL); e = (entnum>=lua.maxedicts)?NULL:lua.edicttable[entnum]; if (e) lua.progfuncs.EntFree(&lua.progfuncs, e); return 0; } static int bi_lua_setorigin(lua_State *L) { edict_t *e; lua_getfield(L, 1, "entnum"); e = EDICT_NUM_UB((&lua.progfuncs), lua_tointegerx(L, -1, NULL)); lua_readvector(L, 2, e->v->origin); World_LinkEdict (&sv.world, (wedict_t*)e, false); return 0; } static int bi_lua_setsize(lua_State *L) { edict_t *e; lua_getfield(L, 1, "entnum"); e = EDICT_NUM_UB((&lua.progfuncs), lua_tointegerx(L, -1, NULL)); lua_readvector(L, 2, e->v->mins); lua_readvector(L, 3, e->v->maxs); VectorSubtract (e->v->maxs, e->v->mins, e->v->size); World_LinkEdict (&sv.world, (wedict_t*)e, false); return 0; } static int bi_lua_localcmd(lua_State *L) { const char *str = lua_tolstring(lua.ctx, 1, NULL); Cbuf_AddText (str, RESTRICT_INSECURE); return 0; } static int bi_lua_changelevel(lua_State *L) { const char *s, *spot; // make sure we don't issue two changelevels (unless the last one failed) if (sv.mapchangelocked) return 0; sv.mapchangelocked = true; if (lua_type(L, 2) == LUA_TSTRING) //and not nil or none { s = lua_tolstring(lua.ctx, 1, NULL); spot = lua_tolstring(lua.ctx, 2, NULL); Cbuf_AddText (va("\nchangelevel %s %s\n",s, spot), RESTRICT_LOCAL); } else { s = lua_tolstring(lua.ctx, 1, NULL); Cbuf_AddText (va("\nchangelevel %s\n",s), RESTRICT_LOCAL); } return 0; } static int bi_lua_stuffcmd(lua_State *L) { int entnum; const char *str; lua_getfield(L, 1, "entnum"); entnum = lua_tointegerx(L, -1, NULL); str = lua_tolstring(L, 2, NULL); PF_stuffcmd_Internal(entnum, str, 0); return 0; } static int bi_lua_centerprint(lua_State *L) { int entnum; const char *str; lua_getfield(L, 1, "entnum"); entnum = lua_tointegerx(L, -1, NULL); str = lua_tolstring(L, 2, NULL); if (!str) str = ""; PF_centerprint_Internal(entnum, false, str); return 0; } static int bi_lua_getinfokey(lua_State *L) { int entnum; const char *key; lua_getfield(L, 1, "entnum"); entnum = lua_tointegerx(L, -1, NULL); key = lua_tolstring(L, 2, NULL); key = PF_infokey_Internal(entnum, key); lua_pushstring(L, key); return 1; } #define bi_lua_infokey bi_lua_getinfokey static int bi_lua_setinfokey(lua_State *L) { int entnum; const char *key; const char *value; int result; lua_getfield(L, 1, "entnum"); entnum = lua_tointegerx(L, -1, NULL); key = lua_tolstring(L, 2, NULL); value = lua_tolstring(L, 3, NULL); result = PF_ForceInfoKey_Internal(entnum, key, value); lua_pushinteger(L, result); return 1; } static int bi_lua_ambientsound(lua_State *L) { vec3_t pos; const char *samp = lua_tolstring(L, 2, NULL); float vol = lua_tonumberx(L, 3, NULL); float attenuation = lua_tonumberx(L, 4, NULL); lua_readvector(L, 1, pos); PF_ambientsound_Internal(pos, samp, vol, attenuation); return 0; } static int bi_lua_sound(lua_State *L) { int entnum; float channel = lua_tonumberx(L, 2, NULL); const char *samp = lua_tolstring(L, 3, NULL); float volume = lua_tonumberx(L, 4, NULL); float attenuation = lua_tonumberx(L, 5, NULL); lua_getfield(L, 1, "entnum"); entnum = lua_tointegerx(L, -1, NULL); //note: channel & 256 == reliable SVQ1_StartSound (NULL, (wedict_t*)EDICT_NUM_UB((&lua.progfuncs), entnum), channel, samp, volume*255, attenuation, 0, 0, 0); return 0; } static int bi_lua_pointcontents(lua_State *L) { vec3_t pos; lua_readvector(L, 1, pos); lua_pushinteger(L, sv.world.worldmodel->funcs.PointContents(sv.world.worldmodel, NULL, pos)); return 1; } static int bi_lua_setspawnparms(lua_State *L) { globalvars_t pr_globals; lua_getfield(L, 1, "entnum"); pr_globals.param[0].i = lua_tointegerx(L, -1, NULL); PF_setspawnparms(&lua.progfuncs, &pr_globals); return 0; } static int bi_lua_makestatic(lua_State *L) { globalvars_t pr_globals; lua_getfield(L, 1, "entnum"); pr_globals.param[0].i = lua_tointegerx(L, -1, NULL); PF_makestatic(&lua.progfuncs, &pr_globals); return 0; } static int bi_lua_droptofloor(lua_State *L) { extern cvar_t pr_droptofloorunits; wedict_t *ent; vec3_t end; vec3_t start; trace_t trace; const float *gravitydir; static const vec3_t standardgravity = {0,0,-1}; pubprogfuncs_t *prinst = &lua.progfuncs; world_t *world = prinst->parms->user; ent = PROG_TO_WEDICT((&lua.progfuncs), pr_global_struct->self); if (ent->xv->gravitydir[2] || ent->xv->gravitydir[1] || ent->xv->gravitydir[0]) gravitydir = ent->xv->gravitydir; else gravitydir = standardgravity; VectorCopy (ent->v->origin, end); if (pr_droptofloorunits.value > 0) VectorMA(end, pr_droptofloorunits.value, gravitydir, end); else VectorMA(end, 256, gravitydir, end); VectorCopy (ent->v->origin, start); trace = World_Move (world, start, ent->v->mins, ent->v->maxs, end, MOVE_NORMAL, ent); if (trace.fraction == 1 || trace.allsolid) lua_pushboolean(L, false); else { VectorCopy (trace.endpos, ent->v->origin); World_LinkEdict (world, ent, false); ent->v->flags = (int)ent->v->flags | FL_ONGROUND; ent->v->groundentity = EDICT_TO_PROG(prinst, trace.ent); lua_pushboolean(L, true); } return 1; } static int bi_lua_checkbottom(lua_State *L) { qboolean okay; int entnum; vec3_t up = {0,0,1}; lua_getfield(L, 1, "entnum"); entnum = lua_tointegerx(L, -1, NULL); okay = World_CheckBottom(&sv.world, (wedict_t*)EDICT_NUM_UB((&lua.progfuncs), entnum), up); lua_pushboolean(L, okay); return 1; } static int bi_lua_bprint_qw(lua_State *L) { int level = lua_tointegerx(L, 1, NULL); const char *str = lua_tolstring(L, 2, NULL); SV_BroadcastPrintf (level, "%s", str); return 0; } static int bi_lua_bprint_nq(lua_State *L) { int level = PRINT_HIGH; const char *str = lua_tolstring(L, 1, NULL); SV_BroadcastPrintf (level, "%s", str); return 0; } static int bi_lua_sprint_qw(lua_State *L) { int entnum; int level = lua_tointegerx(L, 2, NULL); const char *str = lua_tolstring(L, 3, NULL); lua_getfield(L, 1, "entnum"); entnum = lua_tointegerx(L, -1, NULL); if (entnum < 1 || entnum > sv.allocated_client_slots) { Con_TPrintf ("tried to sprint to a non-client\n"); return 0; } SV_ClientPrintf (&svs.clients[entnum-1], level, "%s", str); return 0; } static int bi_lua_sprint_nq(lua_State *L) { int entnum; int level = PRINT_HIGH; const char *str = lua_tolstring(L, 2, NULL); lua_getfield(L, 1, "entnum"); entnum = lua_tointegerx(L, -1, NULL); if (entnum < 1 || entnum > sv.allocated_client_slots) { Con_TPrintf ("tried to sprint to a non-client\n"); return 0; } SV_ClientPrintf (&svs.clients[entnum-1], level, "%s", str); return 0; } static int bi_lua_cvar_set(lua_State *L) { const char *name = lua_tolstring(L, 1, NULL); const char *str = lua_tolstring(L, 2, NULL); cvar_t *var = Cvar_FindVar(name); if (var) Cvar_Set(var, str); return 0; } static int bi_lua_cvar_get(lua_State *L) { const char *name = lua_tolstring(L, 1, NULL); cvar_t *var = Cvar_FindVar(name); if (var) lua_pushstring(L, var->string); else lua_pushnil(L); return 1; } static int bi_lua_cvar(lua_State *L) //useless get[float] { const char *name = lua_tolstring(L, 1, NULL); cvar_t *var = Cvar_FindVar(name); if (var) lua_pushnumber(L, var->value); else lua_pushnil(L); return 1; } static int pushset_trace_globals(lua_State *L, trace_t *trace) { pr_global_struct->trace_allsolid = trace->allsolid; pr_global_struct->trace_startsolid = trace->startsolid; pr_global_struct->trace_fraction = trace->fraction; pr_global_struct->trace_inwater = trace->inwater; pr_global_struct->trace_inopen = trace->inopen; pr_global_struct->trace_surfaceflagsf = trace->surface?trace->surface->flags:0; pr_global_struct->trace_surfaceflagsi = trace->surface?trace->surface->flags:0; // if (pr_global_struct->trace_surfacename) // prinst->SetStringField(prinst, NULL, &pr_global_struct->trace_surfacename, tr->surface?tr->surface->name:"", true); pr_global_struct->trace_endcontentsf = trace->contents; pr_global_struct->trace_endcontentsi = trace->contents; // if (trace.fraction != 1) // VectorMA (trace->endpos, 4, trace->plane.normal, P_VEC(trace_endpos)); // else VectorCopy (trace->endpos, P_VEC(trace_endpos)); VectorCopy (trace->plane.normal, P_VEC(trace_plane_normal)); pr_global_struct->trace_plane_dist = trace->plane.dist; pr_global_struct->trace_ent = trace->ent?((wedict_t*)trace->ent)->entnum:0; lua_newtable(L); lua_pushnumber(L, trace->fraction); lua_setfield(L, -2, "fraction"); lua_pushvector(L, trace->endpos[0], trace->endpos[1], trace->endpos[2]); lua_setfield(L, -2, "endpos"); if (trace->ent) lua_pushedict(L, trace->ent); else lua_pushedict(L, lua.edicttable[0]); lua_setfield(L, -2, "ent"); lua_pushboolean(L, trace->allsolid); lua_setfield(L, -2, "allsolid"); lua_pushboolean(L, trace->startsolid); lua_setfield(L, -2, "startsolid"); lua_pushboolean(L, trace->inwater); lua_setfield(L, -2, "inwater"); lua_pushboolean(L, trace->inopen); lua_setfield(L, -2, "inopen"); lua_newtable(L); lua_pushnumber(L, trace->plane.dist); lua_setfield(L, -2, "dist"); lua_pushvector(L, trace->plane.normal[0], trace->plane.normal[1], trace->plane.normal[2]); lua_setfield(L, -2, "normal"); lua_setfield(L, -2, "plane"); return 1; } static int bi_lua_traceline(lua_State *L) { vec3_t v1, v2; trace_t trace; int nomonsters; wedict_t *ent; lua_readvector(L, 1, v1); lua_readvector(L, 2, v2); nomonsters = lua_tointegerx(L, 3, NULL); lua_getfield(L, 4, "entnum"); ent = (wedict_t*)EDICT_NUM_UB((&lua.progfuncs), lua_tointegerx(L, -1, NULL)); trace = World_Move (&sv.world, v1, vec3_origin, vec3_origin, v2, nomonsters|MOVE_IGNOREHULL, (wedict_t*)ent); return pushset_trace_globals(L, &trace); } static int bi_lua_tracebox(lua_State *L) { vec3_t v1, v2, mins, maxs; trace_t trace; int nomonsters; wedict_t *ent; lua_readvector(L, 1, v1); lua_readvector(L, 2, v2); lua_readvector(L, 3, mins); lua_readvector(L, 4, maxs); nomonsters = lua_tointegerx(L, 5, NULL); lua_getfield(L, 6, "entnum"); ent = (wedict_t*)EDICT_NUM_UB((&lua.progfuncs), lua_tointegerx(L, -1, NULL)); trace = World_Move (&sv.world, v1, mins, maxs, v2, nomonsters|MOVE_IGNOREHULL, (wedict_t*)ent); return pushset_trace_globals(L, &trace); } static int bi_lua_walkmove(lua_State *L) { pubprogfuncs_t *prinst = &lua.progfuncs; world_t *world = prinst->parms->user; wedict_t *ent; float yaw, dist; vec3_t move; int oldself; vec3_t axis[3]; ent = PROG_TO_WEDICT(prinst, *world->g.self); yaw = lua_tonumberx(L, 1, NULL); dist = lua_tonumberx(L, 2, NULL); if ( !( (int)ent->v->flags & (FL_ONGROUND|FL_FLY|FL_SWIM) ) ) { lua_pushboolean(L, false); return 1; } World_GetEntGravityAxis(ent, axis); yaw = yaw*M_PI*2 / 360; VectorScale(axis[0], cos(yaw)*dist, move); VectorMA(move, sin(yaw)*dist, axis[1], move); // save program state, because World_movestep may call other progs oldself = *world->g.self; lua_pushboolean(L, World_movestep(world, ent, move, axis, true, false, NULL, NULL)); // restore program state *world->g.self = oldself; return 1; } static int bi_lua_movetogoal(lua_State *L) { pubprogfuncs_t *prinst = &lua.progfuncs; world_t *world = prinst->parms->user; wedict_t *ent; float dist; ent = PROG_TO_WEDICT(prinst, *world->g.self); dist = lua_tonumberx(L, 1, NULL); World_MoveToGoal (world, ent, dist); return 0; } static int bi_lua_nextent(lua_State *L) { world_t *world = &sv.world; int i; wedict_t *ent; lua_getfield(L, 1, "entnum"); i = lua_tointegerx(L, -1, NULL); while (1) { i++; if (i == world->num_edicts) { ent = world->edicts; break; } ent = (wedict_t *)lua.edicttable[i]; if (!ED_ISFREE(ent)) { break; } } lua_pushedict(L, (struct edict_s *)ent); return 1; } static int bi_lua_nextclient(lua_State *L) { world_t *world = &sv.world; int i; wedict_t *ent; lua_getfield(L, 1, "entnum"); i = lua_tointegerx(L, -1, NULL); while (1) { i++; if (i == sv.allocated_client_slots) { ent = world->edicts; break; } ent = WEDICT_NUM_UB(world->progs, i); if (!ED_ISFREE(ent)) { break; } } lua_pushlightuserdata(L, ent); lua_gettable(L, LUA_REGISTRYINDEX); return 1; } static int bi_lua_checkclient(lua_State *L) { pubprogfuncs_t *prinst = &lua.progfuncs; wedict_t *ent; ent = WEDICT_NUM_PB(prinst, PF_checkclient_Internal(prinst)); lua_pushlightuserdata(L, ent); lua_gettable(L, LUA_REGISTRYINDEX); return 1; } static int bi_lua_random(lua_State *L) { lua_pushnumber(L, (rand ()&0x7fff) / ((float)0x8000)); return 1; } static int bi_lua_makevectors(lua_State *L) { vec3_t angles; //this is annoying as fuck in lua, what with it writing globals and stuff. //perhaps we should support f,u,l=makevectors(ang)... meh, cba. lua_readvector(L, 1, angles); AngleVectors (angles, pr_global_struct->v_forward, pr_global_struct->v_right, pr_global_struct->v_up); #if 1 //globals suck, and lua allows multi-return. lua_pushvector(L, (pr_global_struct->v_forward)[0], (pr_global_struct->v_forward)[1], (pr_global_struct->v_forward)[2]); lua_pushvector(L, (pr_global_struct->v_right)[0], (pr_global_struct->v_right)[1], (pr_global_struct->v_right)[2]); lua_pushvector(L, (pr_global_struct->v_up)[0], (pr_global_struct->v_up)[1], (pr_global_struct->v_up)[2]); return 3; #else return 0; #endif } static int bi_lua_normalize(lua_State *L) { vec3_t v; lua_readvector(L, 1, v); VectorNormalize(v); lua_pushvector(L, v[0], v[1], v[2]); return 1; } static int bi_lua_vectoangles(lua_State *L) { vec3_t forward; vec3_t up; float *uv = NULL; vec3_t ret; lua_readvector(L, 1, forward); if (lua_type(L, 2) != LUA_TNONE) { lua_readvector(L, 1, up); uv = up; } VectorAngles(forward, uv, ret, true); lua_pushvector(L, ret[0], ret[1], ret[2]); return 1; } static int bi_lua_vectoyaw(lua_State *L) { vec3_t forward; vec3_t up; float *uv = NULL; vec3_t ret; lua_readvector(L, 1, forward); if (lua_type(L, 2) != LUA_TNONE) { lua_readvector(L, 1, up); uv = up; } VectorAngles(forward, uv, ret, true); lua_pushnumber(L, ret[1]); return 1; } void QCBUILTIN PF_aim (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); static int bi_lua_aim(lua_State *L) { //aim builtin is for keyboard users, if they still exist. globalvars_t pr_globals; lua_getfield(L, 1, "entnum"); pr_globals.param[0].i = lua_tointeger(L, -1); pr_globals.param[1].f = lua_tonumber(L, 2); //speed PF_aim(&lua.progfuncs, &pr_globals); lua_pushvector(L, pr_globals.ret.vec[0], pr_globals.ret.vec[1], pr_globals.ret.vec[2]); return 1; } static int bi_lua_tokenize(lua_State *L) { const char *instring = lua_tolstring(L, 1, NULL); int argc = 0; lua_newtable(L); while(NULL != (instring = COM_Parse(instring))) { //lua is traditionally 1-based //for i=1,t.argc do lua_pushinteger(L, ++argc); lua_pushstring(L, com_token); lua_settable(L, -3); if (argc == 1) { while (*instring == ' ' || *instring == '\t') instring++; lua_pushstring(L, instring); lua_setfield(L, -2, "args"); //args is all-but-the-first } } lua_pushinteger(L, argc); lua_setfield(L, -2, "argc"); //argc is the count. return 1; } static int bi_lua_findradiuschain(lua_State *L) { extern cvar_t sv_gameplayfix_blowupfallenzombies; world_t *world = &sv.world; edict_t *ent, *chain; float rad; vec3_t org; vec3_t eorg; int i, j; chain = (edict_t *)world->edicts; lua_readvector(L, 1, org); rad = lua_tonumberx(L, 2, NULL); rad = rad*rad; for (i=1 ; inum_edicts ; i++) { ent = EDICT_NUM_PB(world->progs, i); if (ED_ISFREE(ent)) continue; if (ent->v->solid == SOLID_NOT && (progstype != PROG_QW || !((int)ent->v->flags & FL_FINDABLE_NONSOLID)) && !sv_gameplayfix_blowupfallenzombies.value) continue; for (j=0 ; j<3 ; j++) eorg[j] = org[j] - (ent->v->origin[j] + (ent->v->mins[j] + ent->v->maxs[j])*0.5); if (DotProduct(eorg,eorg) > rad) continue; ent->v->chain = EDICT_TO_PROG(world->progs, chain); chain = ent; } lua_pushlightuserdata(L, chain); lua_gettable(L, LUA_REGISTRYINDEX); return 1; } static int bi_lua_findradiustable(lua_State *L) { extern cvar_t sv_gameplayfix_blowupfallenzombies; world_t *world = &sv.world; edict_t *ent, *chain; float rad; vec3_t org; vec3_t eorg; int i, j; int results = 1; //lua arrays are 1-based chain = (edict_t *)world->edicts; lua_readvector(L, 1, org); rad = lua_tonumberx(L, 2, NULL); rad = rad*rad; lua_newtable(L); //our return value. for (i=1 ; inum_edicts ; i++) { ent = EDICT_NUM_PB(world->progs, i); if (ED_ISFREE(ent)) continue; if (ent->v->solid == SOLID_NOT && (progstype != PROG_QW || !((int)ent->v->flags & FL_FINDABLE_NONSOLID)) && !sv_gameplayfix_blowupfallenzombies.value) continue; for (j=0 ; j<3 ; j++) eorg[j] = org[j] - (ent->v->origin[j] + (ent->v->mins[j] + ent->v->maxs[j])*0.5); if (DotProduct(eorg,eorg) > rad) continue; lua_pushinteger(L, ++results); lua_pushlightuserdata(L, ent); lua_gettable(L, LUA_REGISTRYINDEX); lua_settable(L, -3); } lua_pushlightuserdata(L, chain); lua_gettable(L, LUA_REGISTRYINDEX); return 1; } #define bi_lua_findradius bi_lua_findradiuschain static int bi_lua_find(lua_State *L) { world_t *world = &sv.world; edict_t *ent; size_t i; const char *s; const char *match; lua_getfield(L, 1, "entnum"); i = lua_tointegerx(L, -1, NULL)+1; match = lua_tostring(L, 3); for ( ; inum_edicts ; i++) { ent = EDICT_NUM_PB(world->progs, i); if (ED_ISFREE(ent)) continue; lua_pushedict(L, ent); lua_pushvalue(L, 2); lua_gettable(L, -2); s = lua_tostring(L, -1); if (!s) s = ""; if (!strcmp(s, match)) //should probably do a lua comparison, but nils suck { lua_pop(L, 2); lua_pushedict(L, ent); return 1; } lua_pop(L, 2); } lua_pushedict(L, EDICT_NUM_PB(world->progs, 0)); return 1; } static int bi_lua_multicast(lua_State *L) { int dest; vec3_t org; dest = lua_tointegerx(L, 1, NULL); lua_readvector(L, 2, org); NPP_Flush(); SV_Multicast (org, dest); return 0; } extern sizebuf_t csqcmsgbuffer; static int bi_lua_writechar(lua_State *L) { globalvars_t pr_globals; pr_globals.param[0].f = (csqcmsgbuffer.maxsize?MSG_CSQC:MSG_MULTICAST); pr_globals.param[1].f = lua_tonumberx(L, 1, NULL); PF_WriteChar(&lua.progfuncs, &pr_globals); return 0; } static int bi_lua_writebyte(lua_State *L) { globalvars_t pr_globals; pr_globals.param[0].f = (csqcmsgbuffer.maxsize?MSG_CSQC:MSG_MULTICAST); pr_globals.param[1].f = lua_tonumberx(L, 1, NULL); PF_WriteByte(&lua.progfuncs, &pr_globals); return 0; } static int bi_lua_writeshort(lua_State *L) { globalvars_t pr_globals; pr_globals.param[0].f = (csqcmsgbuffer.maxsize?MSG_CSQC:MSG_MULTICAST); pr_globals.param[1].f = lua_tonumberx(L, 1, NULL); PF_WriteShort(&lua.progfuncs, &pr_globals); return 0; } static int bi_lua_writelong(lua_State *L) { globalvars_t pr_globals; pr_globals.param[0].f = (csqcmsgbuffer.maxsize?MSG_CSQC:MSG_MULTICAST); pr_globals.param[1].f = lua_tonumberx(L, 1, NULL); PF_WriteLong(&lua.progfuncs, &pr_globals); return 0; } static int bi_lua_writeangle(lua_State *L) { globalvars_t pr_globals; pr_globals.param[0].f = (csqcmsgbuffer.maxsize?MSG_CSQC:MSG_MULTICAST); pr_globals.param[1].f = lua_tonumberx(L, 1, NULL); PF_WriteAngle(&lua.progfuncs, &pr_globals); return 0; } static int bi_lua_writecoord(lua_State *L) { globalvars_t pr_globals; pr_globals.param[0].f = (csqcmsgbuffer.maxsize?MSG_CSQC:MSG_MULTICAST); pr_globals.param[1].f = lua_tonumberx(L, 1, NULL); PF_WriteCoord(&lua.progfuncs, &pr_globals); return 0; } static int bi_lua_writestring(lua_State *L) { PF_WriteString_Internal((csqcmsgbuffer.maxsize?MSG_CSQC:MSG_MULTICAST),lua_tolstring(L, 1, NULL)); return 0; } static int bi_lua_writeentity(lua_State *L) { globalvars_t pr_globals; lua_getfield(L, 1, "entnum"); pr_globals.param[0].f = (csqcmsgbuffer.maxsize?MSG_CSQC:MSG_MULTICAST); pr_globals.param[1].i = lua_tointegerx(L, -1, NULL); PF_WriteEntity(&lua.progfuncs, &pr_globals); return 0; } static int bi_lua_WriteChar(lua_State *L) { globalvars_t pr_globals; pr_globals.param[0].f = lua_tonumberx(L, 1, NULL); pr_globals.param[1].f = lua_tonumberx(L, 2, NULL); PF_WriteChar(&lua.progfuncs, &pr_globals); return 0; } static int bi_lua_WriteByte(lua_State *L) { globalvars_t pr_globals; pr_globals.param[0].f = lua_tonumberx(L, 1, NULL); pr_globals.param[1].f = lua_tonumberx(L, 2, NULL); PF_WriteByte(&lua.progfuncs, &pr_globals); return 0; } static int bi_lua_WriteShort(lua_State *L) { globalvars_t pr_globals; pr_globals.param[0].f = lua_tonumberx(L, 1, NULL); pr_globals.param[1].f = lua_tonumberx(L, 2, NULL); PF_WriteShort(&lua.progfuncs, &pr_globals); return 0; } static int bi_lua_WriteLong(lua_State *L) { globalvars_t pr_globals; pr_globals.param[0].f = lua_tonumberx(L, 1, NULL); pr_globals.param[1].f = lua_tonumberx(L, 2, NULL); PF_WriteLong(&lua.progfuncs, &pr_globals); return 0; } static int bi_lua_WriteAngle(lua_State *L) { globalvars_t pr_globals; pr_globals.param[0].f = lua_tonumberx(L, 1, NULL); pr_globals.param[1].f = lua_tonumberx(L, 2, NULL); PF_WriteAngle(&lua.progfuncs, &pr_globals); return 0; } static int bi_lua_WriteCoord(lua_State *L) { globalvars_t pr_globals; pr_globals.param[0].f = lua_tonumberx(L, 1, NULL); pr_globals.param[1].f = lua_tonumberx(L, 2, NULL); PF_WriteCoord(&lua.progfuncs, &pr_globals); return 0; } static int bi_lua_WriteString(lua_State *L) { PF_WriteString_Internal(lua_tonumberx(L, 1, NULL),lua_tolstring(L, 2, NULL)); return 0; } static int bi_lua_WriteEntity(lua_State *L) { globalvars_t pr_globals; lua_getfield(L, 2, "entnum"); pr_globals.param[0].f = lua_tonumberx(L, 1, NULL); pr_globals.param[1].i = lua_tointegerx(L, -1, NULL); PF_WriteEntity(&lua.progfuncs, &pr_globals); return 0; } void QCBUILTIN PF_particle (pubprogfuncs_t *prinst, globalvars_t *pr_globals); static int bi_lua_particle(lua_State *L) { globalvars_t pr_globals; lua_readvector(L, 1, pr_globals.param[0].vec); lua_readvector(L, 2, pr_globals.param[1].vec); pr_globals.param[2].f = lua_tonumberx(L, 3, NULL); pr_globals.param[3].f = lua_tonumberx(L, 4, NULL); PF_particle(&lua.progfuncs, &pr_globals); return 0; } static int bi_lua_ChangeYaw(lua_State *L) { PF_changeyaw(&lua.progfuncs, NULL); return 0; } static int bi_lua_bitnot(lua_State *L) { lua_pushinteger(L, ~lua_tointegerx(L, 1, NULL)); return 1; } static int bi_lua_bitclear(lua_State *L) { lua_pushinteger(L, lua_tointegerx(L, 1, NULL)&~lua_tointegerx(L, 2, NULL)); return 1; } static int bi_lua_bitset(lua_State *L) { lua_pushnumber(L, lua_tointegerx(L, 1, NULL)|lua_tointegerx(L, 2, NULL)); return 1; } #define bi_lua_bitor bi_lua_bitset static int bi_lua_bitand(lua_State *L) { lua_pushnumber(L, lua_tointegerx(L, 1, NULL)&lua_tointegerx(L, 2, NULL)); return 1; } static int bi_lua_bitxor(lua_State *L) { lua_pushnumber(L, lua_tointegerx(L, 1, NULL)^lua_tointegerx(L, 2, NULL)); return 1; } static int bi_lua_sin(lua_State *L) { lua_pushnumber(L, sin(lua_tonumberx(L, 1, NULL))); return 1; } static int bi_lua_cos(lua_State *L) { lua_pushnumber(L, cos(lua_tonumberx(L, 1, NULL))); return 1; } static int bi_lua_tan(lua_State *L) { lua_pushnumber(L, tan(lua_tonumberx(L, 1, NULL))); return 1; } static int bi_lua_atan2(lua_State *L) { lua_pushnumber(L, atan2(lua_tonumberx(L, 1, NULL), lua_tonumberx(L, 2, NULL))); return 1; } static int bi_lua_sqrt(lua_State *L) { lua_pushnumber(L, sin(lua_tonumberx(L, 1, NULL))); return 1; } static int bi_lua_pow(lua_State *L) { lua_pushnumber(L, pow(lua_tonumberx(L, 1, NULL), lua_tonumberx(L, 2, NULL))); return 1; } static int bi_lua_floor(lua_State *L) { lua_pushnumber(L, floor(lua_tonumberx(L, 1, NULL))); return 1; } static int bi_lua_rint(lua_State *L) { //C rounds towards 0, so bias away from 0 by 0.5 and we'll get the right rounded value. lua_Number f = lua_tonumberx(L, 1, NULL); if (f < 0) lua_pushinteger(L, f - 0.5); else lua_pushinteger(L, f + 0.5); return 1; } static int bi_lua_ceil(lua_State *L) { lua_pushnumber(L, ceil(lua_tonumberx(L, 1, NULL))); return 1; } static int bi_lua_fabs(lua_State *L) { lua_pushnumber(L, fabs(lua_tonumberx(L, 1, NULL))); return 1; } static int bi_lua_acos(lua_State *L) { lua_pushnumber(L, acos(lua_tonumberx(L, 1, NULL))); return 1; } static int bi_lua_asin(lua_State *L) { lua_pushnumber(L, asin(lua_tonumberx(L, 1, NULL))); return 1; } static int bi_lua_atan(lua_State *L) { lua_pushnumber(L, atan(lua_tonumberx(L, 1, NULL))); return 1; } typedef struct { lua_State *L; int idx; } luafsenum_t; static int QDECL lua_file_enumerate(const char *fname, qofs_t fsize, time_t mtime, void *param, searchpathfuncs_t *spath) { luafsenum_t *e = param; lua_pushinteger(e->L, e->idx++); lua_pushfstring(e->L, "%s", fname); lua_settable(e->L, -3); return true; } static int bi_lua_getfilelist(lua_State *L) { luafsenum_t e; const char *path = lua_tolstring(L, 1, NULL); e.L = L; e.idx = 1; //lua arrays are 1-based. lua_newtable(L); //our return value. COM_EnumerateFiles(path, lua_file_enumerate, &e); return 1; } static int bi_lua_fclose(lua_State *L) { //both fclose and __gc. //should we use a different function so that we can warn on dupe fcloses without bugging out on fclose+gc? //meh, cba vfsfile_t **f = lua_touserdata(L, 1); if (f && *f != NULL) { VFS_CLOSE(*f); *f = NULL; } return 0; } static int bi_lua_fopen(lua_State *L) { vfsfile_t *f; const char *fname = lua_tolstring(L, 1, NULL); qboolean read = true; vfsfile_t **ud; if (read) f = FS_OpenVFS(fname, "rb", FS_GAME); else f = FS_OpenVFS(fname, "wb", FS_GAMEONLY); if (!f) { lua_pushnil(L); return 1; } ud = lua_newuserdata(L, sizeof(vfsfile_t*)); *ud = f; lua_newtable(L); lua_pushcclosure(L, bi_lua_fclose, 0); lua_setfield(L, -2, "__gc"); lua_setmetatable(L, -2); return 1; } static int bi_lua_fgets(lua_State *L) { vfsfile_t **f = lua_touserdata(L, 1); char line[8192]; char *r = NULL; if (f && *f) r = VFS_GETS(*f, line, sizeof(line)); if (r) lua_pushfstring(L, "%s", r); else lua_pushnil(L); return 1; } static int bi_lua_fputs(lua_State *L) { vfsfile_t **f = lua_touserdata(L, 1); size_t l; const char *str = lua_tolstring(L, 2, &l); if (f && *f != NULL) VFS_WRITE(*f, str, l); return 0; } static int bi_lua_loadlua(lua_State *L) { const char *fname = lua_tolstring(L, 1, NULL); vfsfile_t *sourcefile = FS_OpenVFS(fname, "rb", FS_GAME); if (!sourcefile) { Con_Printf("Error trying to load %s\n", fname); lua_pushnil(L); } else if (0 != lua_load(L, my_lua_Reader, sourcefile, va("@%s", fname), "bt")) //load the file, embed it within a function and push it { Con_Printf("Error trying to parse %s: %s\n", fname, lua_tolstring(L, -1, NULL)); lua_pushnil(L); } VFS_CLOSE(sourcefile); return 1; } static int bi_lua_require(lua_State *L) { const char *fname = lua_tolstring(L, 1, NULL); vfsfile_t *sourcefile; const char *usename; if ((sourcefile = FS_OpenVFS(usename=fname, "rb", FS_GAME))) ; else if ((sourcefile = FS_OpenVFS(usename=va("%s.lua", fname), "rb", FS_GAME))) ; else { Con_Printf("Error trying to load %s\n", fname); return 1; } if (0 != lua_load(lua.ctx, my_lua_Reader, sourcefile, va("@%s", usename), "bt")) //load the file, embed it within a function and push it { //failed - it pushed an error code instead Con_Printf("Error trying to parse %s: %s\n", fname, lua_tolstring(lua.ctx, -1, NULL)); lua_pop(lua.ctx, 1); } else { if (lua_pcall(lua.ctx, 0, 0, 0) != 0) //now call it, so its actually run. { const char *s = lua_tolstring(lua.ctx, -1, NULL); Con_Printf(CON_WARNING "%s\n", s); lua_pop(lua.ctx, 1); } } VFS_CLOSE(sourcefile); return 0; } static int bi_lua_vec3(lua_State *L) { float x = lua_tonumberx(L, 1, NULL); float y = lua_tonumberx(L, 2, NULL); float z = lua_tonumberx(L, 3, NULL); lua_pushvector(L, x, y, z); return 1; } static int bi_lua_field(lua_State *L) { const char *fname = lua_tostring(L, 1); const char *ftype = lua_tostring(L, 2); int t; size_t u; if (!strcmp(ftype, "string")) t = ev_string; else if (!strcmp(ftype, "float")) t = ev_float; else if (!strcmp(ftype, "integer")) t = ev_integer; else if (!strcmp(ftype, "vector")) t = ev_vector; else if (!strcmp(ftype, "function")) t = ev_function; else if (!strcmp(ftype, "entity")) t = ev_entity; else { Con_Printf("Unknown field type\n"); return 0; } if (lua.numflds == countof(lua.entflds)) { Con_Printf("Too many ent fields\n"); return 0; } //no dupes please. for (u = 0; u < lua.numflds; u++) { if (!strcmp(lua.entflds[u].name, fname)) return 0; } lua.entflds[lua.numflds].offset = -1; //if we don't know about it yet, then its an artificial field, and present just so that self.whatever returns something other than nil. lua.entflds[lua.numflds].name = Lua_AddString(NULL, fname, 0, false); lua.entflds[lua.numflds].type = t; Hash_AddInsensitive(&lua.entityfields, lua.entflds[lua.numflds].name, &lua.entflds[lua.numflds], &lua.entflds[lua.numflds].buck); lua.numflds++; return 0; } static int bi_lua_type(lua_State *L) { const char *tn; int t = lua_type(L, 1); // luaL_argcheck(L, t != LUA_TNONE, 1, "value expected"); tn = lua_typename(L, t); lua_pushstring(L, tn); return 1; } static int bi_lua_logfrag(lua_State *L) { //stub... noone cares return 0; } /*static int bi_lua_entities(lua_State *L) { //stub... noone cares return 0; }*/ #ifdef LIBLUA_STATIC static int pairsmeta (lua_State *L, const char *method, int iszero, lua_CFunction iter) { luaL_checkany(L, 1); if (luaL_getmetafield(L, 1, method) == LUA_TNIL) { /* no metamethod? */ lua_pushcfunction(L, iter); /* will return generator, */ lua_pushvalue(L, 1); /* state, */ if (iszero) lua_pushinteger(L, 0); /* and initial value */ else lua_pushnil(L); } else { lua_pushvalue(L, 1); /* argument 'self' to metamethod */ lua_call(L, 1, 3); /* get 3 values from metamethod */ } return 3; } static int luaB_next (lua_State *L) { luaL_checktype(L, 1, LUA_TTABLE); lua_settop(L, 2); /* create a 2nd argument if there isn't one */ if (lua_next(L, 1)) return 2; else { lua_pushnil(L); return 1; } } static int bi_lua_pairs (lua_State *L) { return pairsmeta(L, "__pairs", 0, luaB_next); } #endif static int bi_lua_objerror (lua_State *L) { Con_Printf("\n"); lua_pushedict(L, lua.edicttable[lua.globals.self]); lua_pushnil(L); while (lua_next(L, -2)) { //FIXME: this doesn't find vector fields if (lua_type(L, -2) == LUA_TSTRING) Con_Printf("%21s:", lua_tostring(L, -2)); else if (lua_type(L, -2) == LUA_TLIGHTUSERDATA) { int i; const void *ud = lua_topointer(L, -2); for (i = 0; i < lua.numflds; i++) if (lua.entflds[i].offset == (qintptr_t)ud) break; if (i == lua.numflds) Con_Printf("%21s:", lua_typename(L, lua_type(L, -2))); else Con_Printf("%21s:", lua.entflds[i].name); } else Con_Printf("%21s:", lua_typename(L, lua_type(L, -2))); if (lua_type(L, -1) == LUA_TSTRING) Con_Printf(" \"%s\"\n", lua_tostring(L, -1)); else if (lua_type(L, -1) == LUA_TNUMBER) Con_Printf(" \"%g\"\n", lua_tonumber(L, -1)); #ifdef LIBLUA_STATIC else if (lua_type(L, -1) == LUA_TFUNCTION) { lua_Debug ar = {0}; lua_pushvalue(L, -1); lua_getinfo(L, ">nS", &ar); Con_Printf(" %s %s:%i\n", ar.name, ar.source, ar.linedefined); } #endif else Con_Printf(" %s\n", lua_typename(L, lua_type(L, -1))); lua_pop(L, 1); } lua_pop(L, 1); Con_Printf("objerror: %s\n", lua_tostring(L, 1)); return 0; } static int bi_lua_error (lua_State *L) { SV_Error("Error: %s\n", lua_tostring(L, 1)); return 0; } static int bi_lua_qtrue (lua_State *L) { //the truth is what you make it... const char *s; switch(lua_type(L, 1)) { case LUA_TSTRING: s = lua_tostring(L, 1); lua_pushboolean(L, *s != 0); //empty string is considered false. WARNING: vanilla QC considered empty-but-not-null strings to be true break; default: // case LUA_TUSERDATA: // case LUA_TTHREAD: // case LUA_TLIGHTUSERDATA: // case LUA_TNONE: // case LUA_TNIL: lua_pushboolean(L, false); break; case LUA_TBOOLEAN: lua_pushvalue(L, 1); break; case LUA_TNUMBER: lua_pushboolean(L, lua_tonumber(L, 1) != 0); break; case LUA_TFUNCTION: //functions are always considered true. otherwise they're nil and not functions. lua_pushboolean(L, true); break; case LUA_TTABLE: //might be a vector or an entity. lua_getfield(L, 1, "entnum"); if (!lua_isnil(L, -1)) { //okay, so its a table with a valid entnum field. must be an entity. lua_pushboolean(L, 0!=lua_tointegerx(L, -1, NULL)); //true if its not 0/world } else { //assume that its a vector. //note that this means that any table without x|y|z fields will be considered false. vec3_t v; lua_readvector(L, 1, v); lua_pushboolean(L, v[0] || v[1] || v[2]); } break; } return 1; } #define registerfunc(n) lua_pushcclosure(L, bi_lua_##n, 0); lua_setglobal(L, #n); #define registerfuncn(n) registerfunc(n) //new crap #define registerfuncd(n) registerfunc(n) //deprecated crap static void my_lua_registerbuiltins(lua_State *L) { lua_atpanic (L, my_lua_panic); lua_pushglobaltable(L); lua_setfield(L, -1, "_G"); //standard lua library replacement //this avoids the risk of including any way to access os.execute etc, or other file access. registerfuncn(tostring); //standardish registerfuncn(tonumber); //standardish registerfuncn(type); //standardish registerfuncn(print); //'standard' lua print, except prints to console. WARNING: this adds an implicit \n. Use conprint for the quake-style version. registerfuncn(require); //'standard'ish, except uses quake's filesystem instead of reading random system paths. #ifdef LIBLUA_STATIC registerfuncn(pairs); #endif lua_pushnil(L); lua_setglobal(L, "dofile"); //violates our sandbox lua_pushnil(L); lua_setglobal(L, "loadfile"); //violates our sandbox lua_newtable(L); lua_pushcclosure(L, bi_lua_fabs, 0); lua_setfield(L, -2, "abs"); lua_pushcclosure(L, bi_lua_rint, 0); lua_setfield(L, -2, "rint"); lua_pushcclosure(L, bi_lua_ceil, 0); lua_setfield(L, -2, "ceil"); lua_pushcclosure(L, bi_lua_floor, 0); lua_setfield(L, -2, "floor"); lua_pushcclosure(L, bi_lua_sin, 0); lua_setfield(L, -2, "sin"); lua_pushcclosure(L, bi_lua_cos, 0); lua_setfield(L, -2, "cos"); lua_pushcclosure(L, bi_lua_tan, 0); lua_setfield(L, -2, "tan"); lua_pushcclosure(L, bi_lua_asin, 0); lua_setfield(L, -2, "asin"); lua_pushcclosure(L, bi_lua_acos, 0); lua_setfield(L, -2, "acos"); lua_pushcclosure(L, bi_lua_atan, 0); lua_setfield(L, -2, "atan"); lua_pushcclosure(L, bi_lua_atan2, 0); lua_setfield(L, -2, "atan2"); lua_pushcclosure(L, bi_lua_sqrt, 0); lua_setfield(L, -2, "sqrt"); lua_pushcclosure(L, bi_lua_pow, 0); lua_setfield(L, -2, "pow"); lua_pushnumber (L, M_PI); lua_setfield(L, -2, "pi"); lua_setglobal(L, "math"); // registerfuncd(entities); registerfuncd(loadlua); //should probably use 'require' instead. registerfuncn(vec3); registerfuncn(field); registerfuncn(qtrue); //for auto-converted code that tests for truth amongst a myriad of different custom types... registerfunc(setmodel); registerfunc(precache_model); registerfuncd(precache_model2); //pointless alternative name registerfunc(precache_sound); registerfuncd(precache_sound2); //pointless alternative name registerfuncd(precache_file); //empty pointless function... registerfunc(lightstyle); registerfunc(spawn); registerfunc(remove); registerfunc(nextent); registerfunc(nextclient); registerfunc(makestatic); registerfunc(setorigin); registerfunc(setsize); registerfuncn(conprint); //dprint, without the developer registerfunc(bprint_qw); registerfunc(sprint_qw); if (progstype == PROG_QW) { lua_pushcclosure(L, bi_lua_conprint, 0); lua_setglobal(L, "dprint"); //ignores developer lua_pushcclosure(L, bi_lua_bprint_qw, 0); lua_setglobal(L, "bprint"); //an extra level arg lua_pushcclosure(L, bi_lua_sprint_qw, 0); lua_setglobal(L, "sprint"); //an extra level arg } else { lua_pushcclosure(L, bi_lua_dprint, 0); lua_setglobal(L, "dprint"); //responds to developer lua_pushcclosure(L, bi_lua_bprint_nq, 0); lua_setglobal(L, "bprint"); //no level arg lua_pushcclosure(L, bi_lua_sprint_nq, 0); lua_setglobal(L, "sprint"); //no level arg } registerfunc(centerprint); registerfunc(ambientsound); registerfunc(sound); registerfunc(random); registerfunc(checkclient); registerfunc(stuffcmd); registerfunc(localcmd); registerfuncd(cvar); //gets a float value, never a string. registerfuncn(cvar_get); registerfunc(cvar_set); registerfuncd(findradius); //qc legacy compat. should probably warn when its called or sommit. registerfuncn(findradiuschain); //renamed from qc. because qc's behaviour is annoying enough that we want to discourage its use. registerfuncn(findradiustable); //findradius, but returns an array/table instead. registerfunc(objerror); registerfunc(error); // registerfuncd(break); //won't implement registerfunc(traceline); registerfuncn(tracebox); registerfunc(walkmove); registerfunc(movetogoal); registerfunc(droptofloor); registerfunc(checkbottom); registerfunc(pointcontents); registerfuncd(ChangeYaw); registerfunc(setspawnparms); registerfunc(changelevel); registerfunc(logfrag); registerfuncd(infokey); registerfuncn(getinfokey); registerfuncn(setinfokey); registerfunc(multicast); registerfuncn(writebyte); registerfuncn(writechar); registerfuncn(writeshort); registerfuncn(writelong); registerfuncn(writeangle); registerfuncn(writecoord); registerfuncn(writestring); registerfuncn(writeentity); registerfuncd(WriteByte); registerfuncd(WriteChar); registerfuncd(WriteShort); registerfuncd(WriteLong); registerfuncd(WriteAngle); registerfuncd(WriteCoord); registerfuncd(WriteString); registerfuncd(WriteEntity); registerfuncd(particle); registerfuncn(bitnot); registerfuncn(bitclear); registerfuncn(bitset); registerfuncn(bitor); registerfuncn(bitand); registerfuncn(bitxor); registerfuncn(sin); registerfuncn(cos); registerfuncn(acos); registerfuncn(asin); registerfuncn(atan); registerfuncn(atan2); registerfuncn(sqrt); registerfuncn(pow); registerfunc(floor); registerfunc(ceil); registerfunc(rint); registerfunc(fabs); registerfuncn(fopen); registerfuncn(fclose); registerfuncn(fgets); registerfuncn(fputs); registerfuncn(getfilelist); registerfuncd(find); //registerfunc(strftime); registerfunc(tokenize); registerfunc(makevectors); registerfunc(normalize); registerfunc(vectoangles); registerfunc(vectoyaw); registerfuncd(aim); //original implementation nudges v_forward up or down so that keyboard players can still play. registerfunc(vtos); registerfunc(ftos); registerfunc(stof); //registerfunc(PRECACHE_VWEP_MODEL); //registerfunc(SETPAUSE); //set a metatable on the globals table //this means that we can just directly use self.foo instead of blob.self.foo lua_pushglobaltable(L); if (luaL_newmetatable(L, "globals")) { lua_pushcclosure(L, my_lua_global_set, 0); //for the luls. lua_setfield (L, -2, "__newindex"); lua_pushcclosure(L, my_lua_global_get, 0); //for the luls. lua_setfield (L, -2, "__index"); } lua_setmetatable(L, -2); lua_pop(L, 1); if (luaL_newmetatable(L, "vec3_t")) { // lua_pushcclosure(L, my_lua_vec3_set, 0); //known writes should change the internal info so the engine can use the information. // lua_setfield (L, -2, "__newindex"); // lua_pushcclosure(L, my_lua_vec3_get, 0); //we need to de-translate the engine's fields too. // lua_setfield (L, -2, "__index"); lua_pushcclosure(L, my_lua_vec3_tostring, 0); //cos its prettier than seeing 'table 0x5425729' all over the place lua_setfield (L, -2, "__tostring"); lua_pushcclosure(L, my_lua_vec3_eq, 0); //for comparisons, you know? lua_setfield (L, -2, "__eq"); lua_pushcclosure(L, my_lua_vec3_add, 0); //for comparisons, you know? lua_setfield (L, -2, "__add"); lua_pushcclosure(L, my_lua_vec3_sub, 0); //for comparisons, you know? lua_setfield (L, -2, "__sub"); lua_pushcclosure(L, my_lua_vec3_mul, 0); //for comparisons, you know? lua_setfield (L, -2, "__mul"); lua_pushcclosure(L, my_lua_vec3_len, 0); //for comparisons, you know? lua_setfield (L, -2, "__len"); lua_pop(L, 1); } } static edict_t *QDECL Lua_EdictNum(pubprogfuncs_t *pf, unsigned int num) { int newcount; if (num >= lua.maxedicts) { newcount = num + 64; lua.edicttable = realloc(lua.edicttable, newcount*sizeof(*lua.edicttable)); while(lua.maxedicts < newcount) lua.edicttable[lua.maxedicts++] = NULL; pf->edicttable_length = lua.maxedicts; pf->edicttable = lua.edicttable; } return lua.edicttable[num]; } static unsigned int QDECL Lua_NumForEdict(pubprogfuncs_t *pf, edict_t *e) { return e->entnum; } static int QDECL Lua_EdictToProgs(pubprogfuncs_t *pf, edict_t *e) { return e->entnum; } static edict_t *QDECL Lua_ProgsToEdict(pubprogfuncs_t *pf, int num) { return Lua_EdictNum(pf, num); } void Lua_EntClear (pubprogfuncs_t *pf, edict_t *e) { int num = e->entnum; memset (e->v, 0, sv.world.edict_size); e->ereftype = ER_ENTITY; e->entnum = num; } edict_t *Lua_CreateEdict(unsigned int num) { edict_t *e; e = lua.edicttable[num] = Z_Malloc(sizeof(edict_t) + sv.world.edict_size); e->v = (stdentvars_t*)(e+1); #ifdef VM_Q1 e->xv = (extentvars_t*)(e->v + 1); #endif e->entnum = num; return e; } static void QDECL Lua_EntRemove(pubprogfuncs_t *pf, edict_t *e) { lua_State *L = lua.ctx; if (!ED_CanFree(e)) return; e->ereftype = ER_FREE; e->freetime = sv.time; //clear out the lua version of the entity, so that it can be garbage collected. //should probably clear out its entnum field too, just in case. lua_pushlightuserdata(L, e); lua_pushnil(L); lua_settable(L, LUA_REGISTRYINDEX); } static edict_t *Lua_DoRespawn(pubprogfuncs_t *pf, edict_t *e, int num) { lua_State *L = lua.ctx; if (!e) e = Lua_CreateEdict(num); else Lua_EntClear (pf, e); ED_Spawned((struct edict_s *) e, false); //create a new table for the entity, give it a suitable metatable, and store it into the registry (avoiding GC and allowing us to actually hold on to it). lua_pushlightuserdata(L, lua.edicttable[num]); lua_newtable(L); if (luaL_newmetatable(L, "entity")) { lua_pushcclosure(L, my_lua_entity_set, 0); //known writes should change the internal info so the engine can use the information. lua_setfield (L, -2, "__newindex"); lua_pushcclosure(L, my_lua_entity_get, 0); //we need to de-translate the engine's fields too. lua_setfield (L, -2, "__index"); lua_pushcclosure(L, my_lua_entity_tostring, 0); //cos its prettier than seeing 'table 0x5425729' all over the place lua_setfield (L, -2, "__tostring"); lua_pushcclosure(L, my_lua_entity_eq, 0); //for comparisons, you know? lua_setfield (L, -2, "__eq"); } lua_setmetatable(L, -2); lua_pushinteger(L, num); lua_setfield (L, -2, "entnum"); //so we know which entity it is. lua_settable(L, LUA_REGISTRYINDEX); return e; } static edict_t *QDECL Lua_EntAlloc(pubprogfuncs_t *pf, pbool isobject, size_t extrasize) { int i; edict_t *e; for ( i=0 ; ifreetime < 2 || sv.time - e->freetime > 0.5 ) )) { e = Lua_DoRespawn(pf, e, i); return (struct edict_s *)e; } } if (i >= sv.world.max_edicts-1) //try again, but use timed out ents. { for ( i=0 ; i= sv.world.max_edicts-1) { Sys_Error ("ED_Alloc: no free edicts"); } } sv.world.num_edicts++; e = Lua_EdictNum(pf, i); e = Lua_DoRespawn(pf, e, i); return (struct edict_s *)e; } /*static int QDECL Lua_LoadEnts(pubprogfuncs_t *pf, const char *mapstring, void *ctx, void (PDECL *callback) (pubprogfuncs_t *progfuncs, struct edict_s *ed, void *ctx, const char *entstart, const char *entend)) { lua_State *L = lua.ctx; int i = 0; lua_getglobal(L, "LoadEnts"); lua_newtable(L); while(NULL != (mapstring = COM_Parse(mapstring))) { lua_pushinteger(L, i++); lua_pushstring(L, com_token); lua_settable(L, -3); } // lua_pushinteger(L, spawnflags); if (lua_pcall(L, 2, 0, 0) != 0) { const char *s = lua_tolstring(L, -1, NULL); Con_Printf(CON_WARNING "%s\n", s); lua_pop(L, 1); } return sv.world.edict_size; }*/ static const char *Lua_ParseEdict (pubprogfuncs_t *progfuncs, const char *data, struct edict_s *ent) { lua_State *L = lua.ctx; // fdef_t *key; pbool init; char keyname[256]; int n; int nest = 1; char token[8192]; luafld_t *fld; // eval_t *val; init = false; // go through all the dictionary pairs while (1) { // parse key data = COM_ParseOut(data, token, sizeof(token)); if (token[0] == '}') { if (--nest) continue; break; } if (token[0] == '{' && !token[1]) nest++; if (!data) { Con_Printf ("Lua_ParseEdict: EOF without closing brace\n"); return NULL; } if (nest > 1) continue; strncpy (keyname, token, sizeof(keyname)-1); keyname[sizeof(keyname)-1] = 0; // another hack to fix heynames with trailing spaces n = strlen(keyname); while (n && keyname[n-1] == ' ') { keyname[n-1] = 0; n--; } // parse value data = COM_ParseOut(data, token, sizeof(token)); if (!data) { Con_Printf ("Lua_ParseEdict: EOF without closing brace\n"); return NULL; } if (token[0] == '}') { Con_Printf ("Lua_ParseEdict: closing brace without data\n"); return NULL; } init = true; // keynames with a leading underscore are used for utility comments, // and are immediately discarded by quake if (keyname[0] == '_') continue; if (!strcmp(keyname, "angle")) //Quake anglehack - we've got to leave it in cos it doesn't work for quake otherwise, and this is a QuakeC lib! { Q_snprintfz (keyname, sizeof(keyname), "angles"); //change it from yaw to 3d angle Q_snprintfz (token, sizeof(token), "0 %f 0", atof(token)); //change it from yaw to 3d angle goto cont; } /* key = ED_FindField (progfuncs, keyname); if (!key) { if (!strcmp(keyname, "light")) //Quake lighthack - allows a field name and a classname to go by the same thing in the level editor if ((key = ED_FindField (progfuncs, "light_lev"))) goto cont; if (externs->badfield && externs->badfield(&progfuncs->funcs, (struct edict_s*)ent, keyname, qcc_token)) continue; Con_DPrintf ("'%s' is not a field\n", keyname); continue; } */ cont: fld = Hash_GetInsensitive(&lua.entityfields, keyname); if (fld && fld->type == ev_float) lua_pushnumber(L, atof(token)); else if (fld && fld->type == ev_integer) lua_pushinteger(L, atoi(token)); else if (fld && fld->type == ev_vector) { char *e; float x,y,z; x = strtod(token, &e); if (*e == ' ') e++; y = strtod(e, &e); if (*e == ' ') e++; z = strtod(e, &e); if (*e == ' ') e++; lua_pushvector(L, x, y, z); } else if (fld && fld->type == ev_function) lua_getglobal(L, token); //functions are nameless, except for how they're invoked. so this is only for evil mods... else if (fld && fld->type == ev_entity) { int num = atoi(token); lua_pushedict(L, EDICT_NUM_UB((&lua.progfuncs), num)); } else lua_pushstring(L, token); lua_setfield(L, -2, keyname); /*if (!ED_ParseEpair (progfuncs, (char*)ent->fields - progfuncs->funcs.stringtable, key->ofs, key->type, qcc_token)) { continue; // Sys_Error ("ED_ParseEdict: parse error on entities"); }*/ } if (!init) ent->ereftype = ER_FREE; return data; } static int QDECL Lua_LoadEnts(pubprogfuncs_t *pf, const char *mapstring, void *ctx, void (PDECL *callback) (pubprogfuncs_t *progfuncs, struct edict_s *ed, void *ctx, const char *entstart, const char *entend)) { lua_State *L = lua.ctx; struct edict_s *ed = NULL; const char *datastart = mapstring; lua_pushglobaltable(L); while (1) { datastart = mapstring; mapstring = COM_Parse(mapstring); if (!strcmp(com_token, "{")) { if (!ed) //first entity ed = EDICT_NUM_PB(pf, 0); else ed = ED_Alloc(pf, false, 0); ed->ereftype = ER_ENTITY; if (pf->parms->entspawn) pf->parms->entspawn(ed, true); lua_pushedict(L, ed); mapstring = Lua_ParseEdict(pf, mapstring, ed); if (1) { //we can't call the callback, as it would be unable to represent the function references. int spawnflags, killonspawnflags = *(int*)ctx;//lame. really lame lua_getfield(L, -1, "spawnflags"); //push -1["classname"]... spawnflags = lua_tointeger(L, -1); lua_pop(L, 1); if (spawnflags & killonspawnflags) lua.progfuncs.EntFree(&lua.progfuncs, ed); else { lua_getfield(L, -1, "classname"); //push -1["classname"]... //-3:globaltable, -2:enttable, -1:classname string lua_gettable(L, -3); //pop the classname and look it up inside the global table (to find the function in question) lua.globals.self = ed->entnum; if (lua_pcall(L, 0, 0, 0) != 0) { const char *s = lua_tolstring(L, -1, NULL); lua_getfield(L, -2, "classname"); //push -1["classname"]... Con_Printf(CON_WARNING "spawn func %s: %s\n", lua_tolstring(L, -1, NULL), s); lua_pop(L, 2); } } } else { lua_pop(L, 1); callback(pf, ed, ctx, datastart, mapstring); } lua_pop(L, 1); //pop ent table } else break; //unexpected token... } lua_pop(L, 1); return sv.world.edict_size; } static eval_t *QDECL Lua_GetEdictFieldValue(pubprogfuncs_t *pf, edict_t *e, char *fieldname, etype_t type, evalc_t *cache) { eval_t *val; luafld_t *fld; fld = Hash_GetInsensitive(&lua.entityfields, fieldname); if (fld) { val = (eval_t*)((char*)e->v + fld->offset); return val; } return NULL; } static eval_t *QDECL Lua_FindGlobal (pubprogfuncs_t *prinst, const char *name, progsnum_t num, etype_t *type) { eval_t *val; luafld_t *fld; fld = Hash_GetInsensitive(&lua.globalfields, name); if (fld) { val = (eval_t*)((char*)&lua.globals + fld->offset); return val; } Con_Printf("Lua_FindGlobal: %s\n", name); return NULL; } static func_t QDECL Lua_FindFunction (pubprogfuncs_t *prinst, const char *name, progsnum_t num) { eval_t *val; luafld_t *fld; fld = Hash_GetInsensitive(&lua.globalfields, name); if (fld) { val = (eval_t*)((char*)&lua.globals + fld->offset); return val->function; } Con_Printf("Lua_FindFunction: %s\n", name); return 0; } static globalvars_t *QDECL Lua_Globals(pubprogfuncs_t *prinst, int prnum) { // Con_Printf("Lua_Globals: called\n"); return NULL; } char *QDECL Lua_AddString(pubprogfuncs_t *prinst, const char *val, int minlength, pbool demarkup) { char *ptr; int len = strlen(val)+1; if (len < minlength) len = minlength; ptr = Z_TagMalloc(len, LUA_MALLOC_TAG); strcpy(ptr, val); return ptr; } static string_t QDECL Lua_StringToProgs(pubprogfuncs_t *prinst, const char *str) { Con_Printf("Lua_StringToProgs called instead of Lua_SetStringField\n"); return 0; } //passing NULL for ed means its setting a global. static void QDECL Lua_SetStringField(pubprogfuncs_t *prinst, edict_t *ed, string_t *fld, const char *str, pbool str_is_static) { lua_State *L = lua.ctx; string_t val; string_t base; if (ed) { base = (ed->entnum+1)<<10; val = (char*)fld-(char*)ed->v; lua_pushedict(lua.ctx, lua.edicttable[ed->entnum]); } else { base = 0; val = (char*)fld-(char*)&lua.globals; //push the globals list lua_pushglobaltable(L); } *fld = base | val; //set the engine's value //set the stuff so that lua can read it properly. lua_pushlightuserdata(L, (void *)(qintptr_t)val); lua_pushstring(L, str); lua_rawset(L, -3); //and pop the table lua_pop(L, 1); } static const char *ASMCALL QDECL Lua_StringToNative(pubprogfuncs_t *prinst, string_t str) { const char *ret = ""; unsigned int entnum = str >> 10; if (str) { str &= 1023; if (!entnum) { //okay, its the global table. lua_pushglobaltable(lua.ctx); } else { entnum-=1; if (entnum >= lua.maxedicts) return ret; //erk... //get the entity's table lua_pushlightuserdata(lua.ctx, lua.edicttable[entnum]); lua_gettable(lua.ctx, LUA_REGISTRYINDEX); } //read the function from the table lua_pushlightuserdata(lua.ctx, (void *)(qintptr_t)str); lua_rawget(lua.ctx, -2); ret = lua_tolstring(lua.ctx, -1, NULL); lua_pop(lua.ctx, 2); //pop the table+string. //popping the string is 'safe' on the understanding that the string reference is still held by its containing table, so don't store the string anywhere. } return ret; } static void Lua_Event_Touch(world_t *w, wedict_t *s, wedict_t *o) { int oself = pr_global_struct->self; int oother = pr_global_struct->other; pr_global_struct->self = EDICT_TO_PROG(w->progs, s); pr_global_struct->other = EDICT_TO_PROG(w->progs, o); pr_global_struct->time = w->physicstime; #if 1 PR_ExecuteProgram (w->progs, s->v->touch); #else lua_pushedict(lua.ctx, s); //lua_pushliteral(lua.ctx, "touch"); lua_pushlightuserdata(lua.ctx, (void*)((char*)&s->v->touch-(char*)s->v)); lua_rawget(lua.ctx, -2); lua_replace(lua.ctx, -2); if (lua_pcall(lua.ctx, 0, 0, 0) != 0) { const char *e = lua_tolstring(lua.ctx, -1, NULL); lua_pushedict(lua.ctx, (struct edict_s*)s); lua_getfield(lua.ctx, -1, "classname"); Con_Printf(CON_WARNING "%s touch: %s\n", lua_tostring(lua.ctx, -1), e); lua_pop(lua.ctx, 3); //error, enttable, classname } #endif pr_global_struct->self = oself; pr_global_struct->other = oother; } static void Lua_Event_Think(world_t *w, wedict_t *s) { pr_global_struct->self = EDICT_TO_PROG(w->progs, s); pr_global_struct->other = EDICT_TO_PROG(w->progs, w->edicts); #if 0 PR_ExecuteProgram (w->progs, s->v->think); #else lua_pushedict(lua.ctx, (struct edict_s*)s); // lua_pushliteral(lua.ctx, "think"); lua_pushlightuserdata(lua.ctx, (void*)((char*)&s->v->think-(char*)s->v)); lua_rawget(lua.ctx, -2); lua_replace(lua.ctx, -2); if (lua_pcall(lua.ctx, 0, 0, 0) != 0) { const char *e = lua_tolstring(lua.ctx, -1, NULL); lua_pushedict(lua.ctx, (struct edict_s*)s); lua_getfield(lua.ctx, -1, "classname"); Con_Printf(CON_WARNING "%s think: %s\n", lua_tostring(lua.ctx, -1), e); lua_pop(lua.ctx, 3); //error, enttable, classname } #endif } static qboolean Lua_Event_ContentsTransition(world_t *w, wedict_t *ent, int oldwatertype, int newwatertype) { return false; //always do legacy behaviour, because I cba implementing anything else. } static void Lua_SetupGlobals(world_t *world) { int flds; int bucks; comentvars_t *v = NULL; extentvars_t *xv = (extentvars_t*)(v+1); memset(&lua.globals, 0, sizeof(lua.globals)); lua.globals.physics_mode = 2; lua.globals.dimension_send = 255; lua.globals.dimension_default = 255; lua.globals.global_gravitydir[2] = -1; flds = 0; bucks = 64; Hash_InitTable(&lua.globalfields, bucks, Z_Malloc(Hash_BytesForBuckets(bucks))); //WARNING: global is not remapped yet... //This code is written evilly, but works well enough #define doglobal(n, t) \ pr_global_ptrs->n = &lua.globals.n; \ lua.globflds[flds].offset = (char*)&lua.globals.n - (char*)&lua.globals; \ lua.globflds[flds].name = #n; \ lua.globflds[flds].type = t; \ Hash_AddInsensitive(&lua.globalfields, lua.globflds[flds].name, &lua.globflds[flds], &lua.globflds[flds].buck); \ flds++; #define doglobal_v(o, f, t) \ lua.globflds[flds].offset = (char*)&lua.globals.o - (char*)&lua.globals; \ lua.globflds[flds].name = #f; \ lua.globflds[flds].type = t; \ Hash_AddInsensitive(&lua.globalfields, lua.globflds[flds].name, &lua.globflds[flds], &lua.globflds[flds].buck); \ flds++; #define globalentity(required, name) doglobal(name, ev_entity) #define globalint(required, name) doglobal(name, ev_integer) #define globalfloat(required, name) doglobal(name, ev_float) #define globalstring(required, name) doglobal(name, ev_string) #define globalvec(required, name) doglobal(name, ev_vector) doglobal_v(name[0], name##_x, ev_float) doglobal_v(name[1], name##_y, ev_float) doglobal_v(name[2], name##_z, ev_float) #define globalfunc(required, name) doglobal(name, ev_function) luagloballist #undef doglobal #define doglobal(n, t) doglobal_v(n,n,t) luaextragloballist #define parm(n)\ pr_global_ptrs->spawnparamglobals[n] = &lua.globals.parm[n]; \ lua.globflds[flds].offset = (char*)&lua.globals.parm[n] - (char*)&lua.globals; \ lua.globflds[flds].name = "parm"#n; \ lua.globflds[flds].type = ev_float; \ Hash_AddInsensitive(&lua.globalfields, lua.globflds[flds].name, &lua.globflds[flds], &lua.globflds[flds].buck); \ flds++; parm( 0);parm( 1);parm( 2);parm( 3);parm( 4);parm( 5);parm( 6);parm( 7); parm( 8);parm( 9);parm(10);parm(11);parm(12);parm(13);parm(14);parm(15); #undef parm lua.numflds = 0; bucks = 256; Hash_InitTable(&lua.entityfields, bucks, Z_Malloc(Hash_BytesForBuckets(bucks))); #define doefield(n, t) \ lua.entflds[lua.numflds].offset = (char*)&v->n - (char*)v; \ lua.entflds[lua.numflds].name = #n; \ lua.entflds[lua.numflds].type = t; \ Hash_AddInsensitive(&lua.entityfields, lua.entflds[lua.numflds].name, &lua.entflds[lua.numflds], &lua.entflds[lua.numflds].buck); \ lua.numflds++; #define doefield_v(o, f, t) \ lua.entflds[lua.numflds].offset = (char*)&v->o - (char*)v; \ lua.entflds[lua.numflds].name = #f; \ lua.entflds[lua.numflds].type = t; \ Hash_AddInsensitive(&lua.entityfields, lua.entflds[lua.numflds].name, &lua.entflds[lua.numflds], &lua.entflds[lua.numflds].buck); \ lua.numflds++; #define comfieldentity(name,desc) doefield(name, ev_entity) #define comfieldint(name,desc) doefield(name, ev_integer) #define comfieldfloat(name,desc) doefield(name, ev_float) #define comfieldstring(name,desc) doefield(name, ev_string) #define comfieldvector(name,desc) doefield(name, ev_vector) doefield_v(name[0], name##_x, ev_float) doefield_v(name[1], name##_y, ev_float) doefield_v(name[2], name##_z, ev_float) #define comfieldfunction(name,typestr,desc) doefield(name, ev_function) comqcfields #undef doefield #undef doefield_v #define doefield(n, t) \ lua.entflds[lua.numflds].offset = (char*)&xv->n - (char*)v; \ lua.entflds[lua.numflds].name = #n; \ lua.entflds[lua.numflds].type = t; \ Hash_AddInsensitive(&lua.entityfields, lua.entflds[lua.numflds].name, &lua.entflds[lua.numflds], &lua.entflds[lua.numflds].buck); \ lua.numflds++; #define doefield_v(o, f, t) \ lua.entflds[lua.numflds].offset = (char*)&xv->o - (char*)v; \ lua.entflds[lua.numflds].name = #f; \ lua.entflds[lua.numflds].type = t; \ Hash_AddInsensitive(&lua.entityfields, lua.entflds[lua.numflds].name, &lua.entflds[lua.numflds], &lua.entflds[lua.numflds].buck); \ lua.numflds++; comextqcfields svextqcfields PR_SV_FillWorldGlobals(world); } void QDECL Lua_ExecuteProgram(pubprogfuncs_t *funcs, func_t func) { unsigned int entnum = func >> 10; func &= 1023; if (!entnum) { //okay, its the global table. lua_pushglobaltable(lua.ctx); } else { entnum-=1; if (entnum >= lua.maxedicts) return; //erk... //get the entity's table lua_pushlightuserdata(lua.ctx, lua.edicttable[entnum]); lua_gettable(lua.ctx, LUA_REGISTRYINDEX); } //read the function from the table lua_pushlightuserdata(lua.ctx, (void *)(qintptr_t)func); lua_rawget(lua.ctx, -2); //and now invoke it. if (lua_pcall(lua.ctx, 0, 0, 0) != 0) { const char *s = lua_tolstring(lua.ctx, -1, NULL); Con_Printf(CON_WARNING "%s\n", s); lua_pop(lua.ctx, 1); } } void PDECL Lua_CloseProgs(pubprogfuncs_t *inst) { lua_close(lua.ctx); free(lua.edicttable); lua.edicttable = NULL; lua.maxedicts = 0; memset(&lua, 0, sizeof(lua)); Z_FreeTags(LUA_MALLOC_TAG); } static void QDECL Lua_Get_FrameState(world_t *w, wedict_t *ent, framestate_t *fstate) { memset(fstate, 0, sizeof(*fstate)); fstate->g[FS_REG].frame[0] = ent->v->frame; fstate->g[FS_REG].frametime[0] = ent->xv->frame1time; fstate->g[FS_REG].lerpweight[0] = 1; fstate->g[FS_REG].endbone = 0x7fffffff; fstate->g[FST_BASE].frame[0] = ent->xv->baseframe; fstate->g[FST_BASE].frametime[0] = ent->xv->/*base*/frame1time; fstate->g[FST_BASE].lerpweight[0] = 1; fstate->g[FST_BASE].endbone = ent->xv->basebone; #if defined(SKELETALOBJECTS) || defined(RAGDOLL) if (ent->xv->skeletonindex) skel_lookup(w, ent->xv->skeletonindex, fstate); #endif } qboolean PR_LoadLua(void) { world_t *world = &sv.world; pubprogfuncs_t *pf; vfsfile_t *sourcefile = NULL; if ((sourcefile = FS_OpenVFS("qwprogs.lua", "rb", FS_GAME))) progstype = PROG_QW; else if ((sourcefile = FS_OpenVFS("progs.lua", "rb", FS_GAME))) progstype = PROG_NQ; else return false; if (!init_lua()) { VFS_CLOSE(sourcefile); Con_Printf("WARNING: Found progs.lua, but could load load lua library\n"); return false; } pf = svprogfuncs = &lua.progfuncs; pf->CloseProgs = Lua_CloseProgs; pf->AddString = Lua_AddString; pf->EdictNum = Lua_EdictNum; pf->NumForEdict = Lua_NumForEdict; pf->EdictToProgs = Lua_EdictToProgs; pf->ProgsToEdict = Lua_ProgsToEdict; pf->EntAlloc = Lua_EntAlloc; pf->EntFree = Lua_EntRemove; pf->EntClear = Lua_EntClear; pf->FindGlobal = Lua_FindGlobal; pf->load_ents = Lua_LoadEnts; pf->globals = Lua_Globals; pf->GetEdictFieldValue = Lua_GetEdictFieldValue; pf->SetStringField = Lua_SetStringField; pf->StringToProgs = Lua_StringToProgs; pf->StringToNative = Lua_StringToNative; pf->ExecuteProgram = Lua_ExecuteProgram; pf->FindFunction = Lua_FindFunction; world->Event_Touch = Lua_Event_Touch; world->Event_Think = Lua_Event_Think; world->Event_Sound = SVQ1_StartSound; world->Event_ContentsTransition = Lua_Event_ContentsTransition; world->Get_CModel = SVPR_GetCModel; world->Get_FrameState = Lua_Get_FrameState; world->progs = pf; world->progs->parms = &lua.progfuncsparms; world->progs->parms->user = world; world->progs->parms->Printf = PR_Printf; world->progs->parms->DPrintf = PR_DPrintf; world->usesolidcorpse = true; Lua_SetupGlobals(world); svs.numprogs = 0; //Why is this svs? #ifdef VM_Q1 world->edict_size = sizeof(stdentvars_t) + sizeof(extentvars_t); #else world->edict_size = sizeof(stdentvars_t); #endif //force items2 instead of serverflags sv.haveitems2 = true; //initalise basic lua context lua.ctx = lua_newstate(my_lua_alloc, NULL); //create our lua state // luaL_openlibs(lua.ctx); my_lua_registerbuiltins(lua.ctx); //spawn the world, woo. world->edicts = (wedict_t*)pf->EntAlloc(pf,false,0); //load the gamecode now. it should be safe for it to call various builtins. if (0 != lua_load(lua.ctx, my_lua_Reader, sourcefile, "progs.lua", "bt")) //load the file, embed it within a function and push it { Con_Printf("Error trying to parse %s: %s\n", "progs.lua", lua_tolstring(lua.ctx, -1, NULL)); lua_pop(lua.ctx, 1); } else { if (lua_pcall(lua.ctx, 0, 0, 0) != 0) { const char *s = lua_tolstring(lua.ctx, -1, NULL); Con_Printf(CON_WARNING "%s\n", s); lua_pop(lua.ctx, 1); } } VFS_CLOSE(sourcefile); return true; } #endif //VM_LUA