9000f497ec
added //it stuffcmd. now parsing //wps stuffcmds for ezhud use. rework worker threads a little so that the main thread is no longer guaranteed to be stalled at least once. This speeds up load times a little. fix nq server player counts in the server browser. fix switching from/to spectator mode from destroying frag counts. tweak custom bf args a little. fix issue with player names starting with char 0x80. fix menu.dat not loading. attempt to fix flush lag on windows. some fixes for stencil shadows. try to fix public builds not knowing their svn revision. this should fix autoupdates. added global_gravitydir vector, for mods that want to be really weird. doesn't clear onground flags however. reworked the status command a little to attempt to report bursts a little better. fix multicasts missing in mvds issue. fix illegible server message with dpp7 downloads. git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@4992 fc73d0e0-1445-4013-8a0c-d673dee63da5
2159 lines
59 KiB
C
2159 lines
59 KiB
C
#include "quakedef.h"
|
|
|
|
#ifdef VM_LUA
|
|
|
|
#include "pr_common.h"
|
|
#include "hash.h"
|
|
|
|
#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_endcontents) \
|
|
globalfloat (false, trace_surfaceflags) \
|
|
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)
|
|
|
|
//any globals or functions that the server might want access to need to be known also.
|
|
#define luaextragloballist \
|
|
globalstring (true, startspot) \
|
|
globalstring (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
|
|
} luaglobalvars_t;
|
|
|
|
typedef struct
|
|
{
|
|
int type;
|
|
ptrdiff_t offset;
|
|
char *name;
|
|
bucket_t buck;
|
|
} luafld_t;
|
|
|
|
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 int lua_Integer;
|
|
|
|
//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
|
|
|
|
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);
|
|
void (QDECL *rawget) (lua_State *L, int idx);
|
|
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 *replace) (lua_State *L, int idx);
|
|
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 *Lcallmeta) (lua_State *L, int obj, const char *e);
|
|
int (QDECL *Lnewmetatable) (lua_State *L, const char *tname);
|
|
} lua;
|
|
#define pcall(L,n,r,f) pcallk(L, (n), (r), (f), 0, NULL)
|
|
#define call(L,n,r) callk(L, (n), (r), 0, NULL)
|
|
#define pop(L,n) settop(L, -(n)-1)
|
|
#define pushstring(L,s) pushfstring(L,"%s",s)
|
|
|
|
#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_REGISTRYINDEX (-1000000 - 1000)
|
|
|
|
static qboolean init_lua(void)
|
|
{
|
|
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.rawset, "lua_rawset"},
|
|
{(void*)&lua.createtable, "lua_createtable"},
|
|
{(void*)&lua.setmetatable, "lua_setmetatable"},
|
|
{(void*)&lua.newuserdata, "lua_newuserdata"},
|
|
|
|
{(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.Lcallmeta, "luaL_callmeta"},
|
|
{(void*)&lua.Lnewmetatable, "luaL_newmetatable"},
|
|
|
|
{NULL, NULL}
|
|
};
|
|
lua.triedlib = true;
|
|
lua.lib = Sys_LoadLibrary("lua52", luafuncs);
|
|
}
|
|
if (!lua.lib)
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
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 my_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 my_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 my_lua_conprint(L);
|
|
}
|
|
|
|
//taken from lua's baselib.c, with dependancies reduced a little.
|
|
static int my_lua_tostring(lua_State *L)
|
|
{
|
|
// if (lua.type(L, 1) == LUA_TNONE)
|
|
// luaL_argerror(L, narg, "value expected");
|
|
if (lua.Lcallmeta(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;
|
|
}
|
|
|
|
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 void 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);
|
|
}
|
|
break;
|
|
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);
|
|
break;
|
|
case LUA_TNIL:
|
|
result[0] = 0;
|
|
result[1] = 0;
|
|
result[2] = 0;
|
|
break;
|
|
default:
|
|
Con_Printf("Expected vector, got something that wasn't\n");
|
|
}
|
|
}
|
|
|
|
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)
|
|
{
|
|
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:
|
|
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 *)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 *)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)
|
|
{
|
|
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.createtable(L, 0, 0);
|
|
//FIXME: should provide a metatable with a __tostring
|
|
lua.pushnumber(L, eval->_vector[0]);
|
|
lua.setfield (L, -2, "x");
|
|
lua.pushnumber(L, eval->_vector[1]);
|
|
lua.setfield (L, -2, "y");
|
|
lua.pushnumber(L, eval->_vector[2]);
|
|
lua.setfield (L, -2, "z");
|
|
return 1;
|
|
case ev_function:
|
|
case ev_string:
|
|
lua.pushlightuserdata(L, (void *)(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_function:
|
|
lua.pushlightuserdata(L, (void *)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:
|
|
//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 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;
|
|
}
|
|
static int bi_lua_precache_sound(lua_State *L)
|
|
{
|
|
PF_precache_sound_Internal(&lua.progfuncs, lua.tolstring(L, 1, NULL));
|
|
return 0;
|
|
}
|
|
static int bi_lua_lightstyle(lua_State *L)
|
|
{
|
|
vec3_t rgb;
|
|
lua_readvector(L, 3, rgb);
|
|
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);
|
|
if (e)
|
|
{
|
|
lua.pushlightuserdata(L, e);
|
|
lua.gettable(L, LUA_REGISTRYINDEX);
|
|
}
|
|
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((&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((&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("\nmap %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);
|
|
|
|
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;
|
|
}
|
|
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((&lua.progfuncs), entnum), channel, samp, volume*255, attenuation, 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;
|
|
extern const vec3_t standardgravity;
|
|
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;
|
|
lua.getfield(L, 1, "entnum");
|
|
entnum = lua.tointegerx(L, -1, NULL);
|
|
okay = World_CheckBottom(&sv.world, (wedict_t*)EDICT_NUM((&lua.progfuncs), entnum));
|
|
lua.pushboolean(L, okay);
|
|
return 1;
|
|
}
|
|
|
|
static int bi_lua_bprint(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_sprint(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_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 0;
|
|
}
|
|
|
|
static void set_trace_globals(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_surfaceflags = 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_endcontents = 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;
|
|
}
|
|
|
|
static int bi_lua_traceline(lua_State *L)
|
|
{
|
|
vec3_t v1, v2;
|
|
trace_t trace;
|
|
int nomonsters;
|
|
wedict_t *ent;
|
|
int savedhull;
|
|
|
|
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((&lua.progfuncs), lua.tointegerx(L, -1, NULL));
|
|
|
|
savedhull = ent->xv->hull;
|
|
ent->xv->hull = 0;
|
|
trace = World_Move (&sv.world, v1, vec3_origin, vec3_origin, v2, nomonsters, (wedict_t*)ent);
|
|
ent->xv->hull = savedhull;
|
|
|
|
//FIXME: should we just return a table instead, and ignore the globals?
|
|
set_trace_globals(&trace);
|
|
return 0;
|
|
}
|
|
|
|
static int bi_lua_tracebox(lua_State *L)
|
|
{
|
|
vec3_t v1, v2, mins, maxs;
|
|
trace_t trace;
|
|
int nomonsters;
|
|
wedict_t *ent;
|
|
int savedhull;
|
|
|
|
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((&lua.progfuncs), lua.tointegerx(L, -1, NULL));
|
|
|
|
savedhull = ent->xv->hull;
|
|
ent->xv->hull = 0;
|
|
trace = World_Move (&sv.world, v1, mins, maxs, v2, nomonsters, (wedict_t*)ent);
|
|
ent->xv->hull = savedhull;
|
|
|
|
//FIXME: should we just return a table instead, and ignore the globals?
|
|
set_trace_globals(&trace);
|
|
return 0;
|
|
}
|
|
|
|
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;
|
|
|
|
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_NUM(world->progs, i);
|
|
if (!ent->isfree)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
lua.pushlightuserdata(L, ent);
|
|
lua.gettable(L, LUA_REGISTRYINDEX);
|
|
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(world->progs, i);
|
|
if (!ent->isfree)
|
|
{
|
|
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(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);
|
|
return 0;
|
|
}
|
|
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, up, ret);
|
|
|
|
lua.createtable(L, 0, 0);
|
|
//FIXME: should provide a metatable with a __tostring
|
|
lua.pushnumber(L, ret[0]);
|
|
lua.setfield (L, -2, "x");
|
|
lua.pushnumber(L, ret[1]);
|
|
lua.setfield (L, -2, "y");
|
|
lua.pushnumber(L, ret[2]);
|
|
lua.setfield (L, -2, "z");
|
|
return 1;
|
|
}
|
|
|
|
static int bi_lua_tokenize(lua_State *L)
|
|
{
|
|
const char *instring = lua.tolstring(L, 1, NULL);
|
|
int argc = 0;
|
|
lua.createtable(L, 0, 0);
|
|
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 ; i<world->num_edicts ; i++)
|
|
{
|
|
ent = EDICT_NUM(world->progs, i);
|
|
if (ent->isfree)
|
|
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.createtable(L, 0, 0); //our return value.
|
|
|
|
for (i=1 ; i<world->num_edicts ; i++)
|
|
{
|
|
ent = EDICT_NUM(world->progs, i);
|
|
if (ent->isfree)
|
|
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_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_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_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_floor(lua_State *L)
|
|
{
|
|
lua.pushnumber(L, floor(lua.tonumberx(L, 1, NULL)));
|
|
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_acos(lua_State *L)
|
|
{
|
|
lua.pushnumber(L, acos(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, 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.createtable(L, 0, 0); //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.createtable(L, 0, 0);
|
|
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, 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;
|
|
}
|
|
|
|
#define registerfunc(n) lua.pushcclosure(L, bi_lua_##n, 0); lua.setglobal(L, #n);
|
|
static void my_lua_registerbuiltins(lua_State *L)
|
|
{
|
|
lua.atpanic (L, my_lua_panic);
|
|
|
|
//standard lua library replacement
|
|
//this avoids the risk of including any way to access os.execute etc, or other file access.
|
|
lua.pushcclosure(L, my_lua_tostring, 0);
|
|
lua.setglobal(L, "tostring");
|
|
lua.pushcclosure(L, my_lua_print, 0);
|
|
lua.setglobal(L, "print");
|
|
|
|
lua.pushcclosure(L, my_lua_conprint, 0); //for the luls.
|
|
lua.setglobal(L, "conprint");
|
|
|
|
registerfunc(loadlua);
|
|
|
|
registerfunc(setmodel);
|
|
registerfunc(precache_model);
|
|
registerfunc(precache_sound);
|
|
registerfunc(lightstyle);
|
|
registerfunc(spawn);
|
|
registerfunc(remove);
|
|
registerfunc(nextent);
|
|
registerfunc(nextclient);
|
|
//registerfunc(AIM);
|
|
registerfunc(makestatic);
|
|
registerfunc(setorigin);
|
|
registerfunc(setsize);
|
|
|
|
registerfunc(dprint);
|
|
registerfunc(bprint);
|
|
registerfunc(sprint);
|
|
registerfunc(centerprint);
|
|
registerfunc(ambientsound);
|
|
registerfunc(sound);
|
|
registerfunc(random);
|
|
registerfunc(checkclient);
|
|
registerfunc(stuffcmd);
|
|
registerfunc(localcmd);
|
|
registerfunc(cvar_get);
|
|
registerfunc(cvar_set);
|
|
registerfunc(findradius); //qc legacy compat. should probably warn when its called or sommit.
|
|
registerfunc(findradiuschain); //like qc.
|
|
registerfunc(findradiustable); //findradius, but returns an array/table instead.
|
|
|
|
registerfunc(traceline);
|
|
registerfunc(tracebox);
|
|
registerfunc(walkmove);
|
|
registerfunc(movetogoal);
|
|
registerfunc(droptofloor);
|
|
registerfunc(checkbottom);
|
|
registerfunc(pointcontents);
|
|
|
|
registerfunc(setspawnparms);
|
|
registerfunc(changelevel);
|
|
//registerfunc(LOGFRAG);
|
|
registerfunc(getinfokey);
|
|
registerfunc(setinfokey);
|
|
registerfunc(multicast);
|
|
registerfunc(writebyte);
|
|
registerfunc(writechar);
|
|
registerfunc(writeshort);
|
|
registerfunc(writelong);
|
|
registerfunc(writeangle);
|
|
registerfunc(writecoord);
|
|
registerfunc(writestring);
|
|
registerfunc(writeentity);
|
|
registerfunc(bitnot);
|
|
registerfunc(bitclear);
|
|
registerfunc(bitset);
|
|
registerfunc(bitor);
|
|
registerfunc(bitand);
|
|
registerfunc(bitxor);
|
|
registerfunc(sin);
|
|
registerfunc(cos);
|
|
registerfunc(atan2);
|
|
registerfunc(sqrt);
|
|
registerfunc(floor);
|
|
registerfunc(ceil);
|
|
registerfunc(acos);
|
|
registerfunc(fopen);
|
|
registerfunc(fclose);
|
|
registerfunc(fgets);
|
|
registerfunc(fputs);
|
|
registerfunc(getfilelist);
|
|
//registerfunc(Find);
|
|
|
|
//registerfunc(strftime);
|
|
registerfunc(tokenize);
|
|
registerfunc(makevectors);
|
|
registerfunc(vectoangles);
|
|
|
|
//registerfunc(PRECACHE_VWEP_MODEL);
|
|
//registerfunc(SETPAUSE);
|
|
|
|
|
|
|
|
lua.createtable(L, 0, 0);
|
|
if (lua.Lnewmetatable(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.setglobal(L, "glob");
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
|
}
|
|
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->isfree = false;
|
|
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->isfree = true;
|
|
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.createtable(L, 0, 0);
|
|
if (lua.Lnewmetatable(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)
|
|
{
|
|
int i;
|
|
edict_t *e;
|
|
for ( i=0 ; i<sv.world.num_edicts ; i++)
|
|
{
|
|
e = (edict_t*)EDICT_NUM(pf, i);
|
|
// the first couple seconds of server time can involve a lot of
|
|
// freeing and allocating, so relax the replacement policy
|
|
if (!e || (e->isfree && ( e->freetime < 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.num_edicts ; i++)
|
|
{
|
|
e = (edict_t*)EDICT_NUM(pf, i);
|
|
// the first couple seconds of server time can involve a lot of
|
|
// freeing and allocating, so relax the replacement policy
|
|
if (!e || (e->isfree))
|
|
{
|
|
e = Lua_DoRespawn(pf, e, i);
|
|
return (struct edict_s *)e;
|
|
}
|
|
}
|
|
|
|
if (i >= sv.world.max_edicts-1)
|
|
{
|
|
Sys_Error ("ED_Alloc: no free edicts");
|
|
}
|
|
}
|
|
|
|
sv.world.num_edicts++;
|
|
e = (edict_t*)EDICT_NUM(pf, i);
|
|
|
|
e = Lua_DoRespawn(pf, e, i);
|
|
|
|
return (struct edict_s *)e;
|
|
}
|
|
|
|
static int QDECL Lua_LoadEnts(pubprogfuncs_t *pf, char *mapstring, float spawnflags)
|
|
{
|
|
lua_State *L = lua.ctx;
|
|
int i = 0;
|
|
lua.getglobal(L, "LoadEnts");
|
|
lua.createtable(L, 0, 0);
|
|
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 eval_t *QDECL Lua_GetEdictFieldValue(pubprogfuncs_t *pf, edict_t *e, char *fieldname, 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, 0x55780128);
|
|
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;
|
|
|
|
//push the entity table
|
|
lua.pushlightuserdata(lua.ctx, lua.edicttable[ed->entnum]);
|
|
lua.gettable(lua.ctx, LUA_REGISTRYINDEX);
|
|
}
|
|
else
|
|
{
|
|
base = 0;
|
|
val = (char*)fld-(char*)&lua.globals;
|
|
|
|
//push the globals list
|
|
lua.getglobal(lua.ctx, "glob");
|
|
}
|
|
*fld = base | val; //set the engine's value
|
|
|
|
//set the stuff so that lua can read it properly.
|
|
lua.pushlightuserdata(L, (void *)val);
|
|
lua.pushfstring(L, "%s", 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.getglobal(lua.ctx, "glob");
|
|
}
|
|
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 *)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;
|
|
PR_ExecuteProgram (w->progs, s->v->touch);
|
|
|
|
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);
|
|
PR_ExecuteProgram (w->progs, s->v->think);
|
|
}
|
|
|
|
static qboolean Lua_Event_ContentsTransition(world_t *w, wedict_t *ent, int oldwatertype, int newwatertype)
|
|
{
|
|
return false; //always do legacy behaviour
|
|
}
|
|
|
|
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;
|
|
|
|
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
|
|
|
|
flds = 0;
|
|
bucks = 256;
|
|
Hash_InitTable(&lua.entityfields, bucks, Z_Malloc(Hash_BytesForBuckets(bucks)));
|
|
|
|
|
|
#define doefield(n, t) \
|
|
lua.entflds[flds].offset = (char*)&v->n - (char*)v; \
|
|
lua.entflds[flds].name = #n; \
|
|
lua.entflds[flds].type = t; \
|
|
Hash_AddInsensitive(&lua.entityfields, lua.entflds[flds].name, &lua.entflds[flds], &lua.entflds[flds].buck); \
|
|
flds++;
|
|
#define doefield_v(o, f, t) \
|
|
lua.entflds[flds].offset = (char*)&v->o - (char*)v; \
|
|
lua.entflds[flds].name = #f; \
|
|
lua.entflds[flds].type = t; \
|
|
Hash_AddInsensitive(&lua.entityfields, lua.entflds[flds].name, &lua.entflds[flds], &lua.entflds[flds].buck); \
|
|
flds++;
|
|
#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[flds].offset = (char*)&xv->n - (char*)v; \
|
|
lua.entflds[flds].name = #n; \
|
|
lua.entflds[flds].type = t; \
|
|
Hash_AddInsensitive(&lua.entityfields, lua.entflds[flds].name, &lua.entflds[flds], &lua.entflds[flds].buck); \
|
|
flds++;
|
|
#define doefield_v(o, f, t) \
|
|
lua.entflds[flds].offset = (char*)&xv->o - (char*)v; \
|
|
lua.entflds[flds].name = #f; \
|
|
lua.entflds[flds].type = t; \
|
|
Hash_AddInsensitive(&lua.entityfields, lua.entflds[flds].name, &lua.entflds[flds], &lua.entflds[flds].buck); \
|
|
flds++;
|
|
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.getglobal(lua.ctx, "glob");
|
|
}
|
|
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 *)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;
|
|
}
|
|
|
|
qboolean PR_LoadLua(void)
|
|
{
|
|
world_t *world = &sv.world;
|
|
pubprogfuncs_t *pf;
|
|
vfsfile_t *sourcefile = FS_OpenVFS("progs.lua", "rb", FS_GAME);
|
|
if (!sourcefile)
|
|
return false;
|
|
|
|
if (!init_lua())
|
|
{
|
|
VFS_CLOSE(sourcefile);
|
|
Con_Printf("WARNING: Found progs.lua, but could load load lua library\n");
|
|
return false;
|
|
}
|
|
|
|
progstype = PROG_QW;
|
|
|
|
|
|
pf = svprogfuncs = &lua.progfuncs;
|
|
|
|
pf->CloseProgs = Lua_CloseProgs;
|
|
pf->AddString = Lua_AddString;
|
|
pf->EDICT_NUM = Lua_EdictNum;
|
|
pf->NUM_FOR_EDICT = 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->progs = pf;
|
|
world->progs->parms = &lua.progfuncsparms;
|
|
world->progs->parms->user = world;
|
|
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
|
|
my_lua_registerbuiltins(lua.ctx);
|
|
|
|
//spawn the world, woo.
|
|
world->edicts = (wedict_t*)pf->EntAlloc(pf);
|
|
|
|
//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
|