/** Creates a command name that replaces another command. */ static void COM_Alias_f(void) { cmdalias_t *a; char cmd[1024]; size_t i, c; if (COM_Argc() < 3) { CONS_Printf("alias <name> <command>\n"); return; } a = ZZ_Alloc(sizeof *a); a->next = com_alias; com_alias = a; a->name = Z_StrDup(COM_Argv(1)); // copy the rest of the command line cmd[0] = 0; // start out with a null string c = COM_Argc(); for (i = 2; i < c; i++) { strcat(cmd, COM_Argv(i)); if (i != c) strcat(cmd, " "); } strcat(cmd, "\n"); a->value = Z_StrDup(cmd); }
/** Parses a string into command-line tokens. * * \param ptext A null-terminated string. Does not need to be * newline-terminated. */ static void COM_TokenizeString(char *ptext) { size_t i; // Clear the args from the last string. for (i = 0; i < com_argc; i++) Z_Free(com_argv[i]); com_argc = 0; com_args = NULL; while (com_argc < MAX_ARGS) { // Skip whitespace up to a newline. while (*ptext != '\0' && *ptext <= ' ' && *ptext != '\n') ptext++; // A newline means end of command in buffer, // thus end of this command's args too. if (*ptext == '\n' || *ptext == '\0') break; if (com_argc == 1) com_args = ptext; ptext = COM_Parse(ptext); if (ptext == NULL) break; com_argv[com_argc] = Z_StrDup(com_token); com_argc++; } }
// This function decides which global variables you are allowed to set. static int noglobals(lua_State *L) { const char *csname; char *name; lua_remove(L, 1); // we're not gonna be using _G csname = lua_tostring(L, 1); // make an uppercase copy of the name name = Z_StrDup(csname); strupr(name); if (fastncmp(name, "A_", 2) && lua_isfunction(L, 2)) { // Accept new A_Action functions // Add the action to Lua actions refrence table lua_getfield(L, LUA_REGISTRYINDEX, LREG_ACTIONS); lua_pushstring(L, name); // "A_ACTION" lua_pushvalue(L, 2); // function lua_rawset(L, -3); // rawset doesn't trigger this metatable again. // otherwise we would've used setfield, obviously. Z_Free(name); return 0; } Z_Free(name); return luaL_error(L, "Implicit global " LUA_QS " prevented. Create a local variable instead.", csname); }
// Wrapper for COM_AddCommand static int lib_comAddCommand(lua_State *L) { int com_return = -1; const char *luaname = luaL_checkstring(L, 1); // must store in all lowercase char *name = Z_StrDup(luaname); strlwr(name); luaL_checktype(L, 2, LUA_TFUNCTION); NOHUD if (lua_gettop(L) >= 3) { // For the third argument, only take a boolean or a number. lua_settop(L, 3); if (lua_type(L, 3) != LUA_TBOOLEAN) luaL_checktype(L, 3, LUA_TNUMBER); } else { // No third argument? Default to 0. lua_settop(L, 2); lua_pushinteger(L, 0); } lua_getfield(L, LUA_REGISTRYINDEX, "COM_Command"); I_Assert(lua_istable(L, -1)); lua_createtable(L, 2, 0); lua_pushvalue(L, 2); lua_rawseti(L, -2, 1); lua_pushvalue(L, 3); lua_rawseti(L, -2, 2); lua_setfield(L, -2, name); // Try to add the Lua command com_return = COM_AddLuaCommand(name); if (com_return < 0) { // failed to add -- free the lowercased name and return error Z_Free(name); return luaL_error(L, "Couldn't add a new console command \"%s\"", luaname); } else if (com_return == 1) { // command existed already -- free our name as the old string will continue to be used CONS_Printf("Replaced command \"%s\"\n", name); Z_Free(name); } else { // new command was added -- do NOT free the string as it will forever be used by the console CONS_Printf("Added command \"%s\"\n", name); } return 0; }
void QCBUILTIN PF_cl_setwindowcaption(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { const char *newcaption = PR_GetStringOfs(prinst, OFS_PARM0); if (!cl.windowtitle || strcmp(cl.windowtitle, newcaption)) { Z_Free(cl.windowtitle); cl.windowtitle = NULL; if (*newcaption) cl.windowtitle = Z_StrDup(newcaption); CL_UpdateWindowTitle(); } }
boolean LUA_CallAction(const char *csaction, mobj_t *actor) { I_Assert(csaction != NULL); I_Assert(actor != NULL); if (!gL) // Lua isn't loaded, return false; // action not called. if (superstack && fasticmp(csaction, superactions[superstack-1])) // the action is calling itself, return false; // let it call the hardcoded function instead. // grab function by uppercase name. lua_getfield(gL, LUA_REGISTRYINDEX, LREG_ACTIONS); { char *action = Z_StrDup(csaction); strupr(action); lua_getfield(gL, -1, action); Z_Free(action); } lua_remove(gL, -2); // pop LREG_ACTIONS if (lua_isnil(gL, -1)) // no match { lua_pop(gL, 1); // pop nil return false; // action not called. } if (superstack == MAXRECURSION) { CONS_Alert(CONS_WARNING, "Max Lua Action recursion reached! Cool it on the calling A_Action functions from inside A_Action functions!\n"); return true; } // Found a function. // Call it with (actor, var1, var2) I_Assert(lua_isfunction(gL, -1)); LUA_PushUserdata(gL, actor, META_MOBJ); lua_pushinteger(gL, var1); lua_pushinteger(gL, var2); superactions[superstack] = csaction; ++superstack; LUA_Call(gL, 3); --superstack; superactions[superstack] = NULL; return true; // action successfully called. }
// Wrapper for COM_AddCommand commands void COM_Lua_f(void) { char *buf, *p; UINT8 i, flags; UINT16 len; INT32 playernum = consoleplayer; I_Assert(gL != NULL); lua_getfield(gL, LUA_REGISTRYINDEX, "COM_Command"); // push COM_Command I_Assert(lua_istable(gL, -1)); // use buf temporarily -- must use lowercased string buf = Z_StrDup(COM_Argv(0)); strlwr(buf); lua_getfield(gL, -1, buf); // push command info table I_Assert(lua_istable(gL, -1)); lua_remove(gL, -2); // pop COM_Command Z_Free(buf); lua_rawgeti(gL, -1, 2); // push flags from command info table if (lua_isboolean(gL, -1)) flags = (lua_toboolean(gL, -1) ? 1 : 0); else flags = (UINT8)lua_tointeger(gL, -1); lua_pop(gL, 1); // pop flags if (flags & 2) // flag 2: splitscreen player command. { if (!splitscreen) { lua_pop(gL, 1); // pop command info table return; // can't execute splitscreen command without player 2! } playernum = secondarydisplayplayer; } if (netgame) { // Send the command through the network UINT8 argc; lua_pop(gL, 1); // pop command info table if (flags & 1 && !server && !IsPlayerAdmin(playernum)) // flag 1: only server/admin can use this command. { CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n")); return; } if (COM_Argc() > UINT8_MAX) argc = UINT8_MAX; else argc = (UINT8)COM_Argc(); if (argc == UINT8_MAX) len = UINT16_MAX; else len = (argc+1)*256; buf = malloc(len); p = buf; WRITEUINT8(p, argc); for (i = 0; i < argc; i++) WRITESTRINGN(p, COM_Argv(i), 255); if (flags & 2) SendNetXCmd2(XD_LUACMD, buf, p-buf); else SendNetXCmd(XD_LUACMD, buf, p-buf); free(buf); return; } // Do the command locally, NetXCmds don't go through outside of GS_LEVEL || GS_INTERMISSION lua_rawgeti(gL, -1, 1); // push function from command info table I_Assert(lua_isfunction(gL, -1)); lua_remove(gL, -2); // pop command info table LUA_PushUserdata(gL, &players[playernum], META_PLAYER); for (i = 1; i < COM_Argc(); i++) lua_pushstring(gL, COM_Argv(i)); LUA_Call(gL, (int)COM_Argc()); // COM_Argc is 1-based, so this will cover the player we passed too. }
static int lib_cvRegisterVar(lua_State *L) { const char *k; lua_Integer i; consvar_t *cvar; luaL_checktype(L, 1, LUA_TTABLE); lua_settop(L, 1); // Clear out all other possible arguments, leaving only the first one. NOHUD cvar = lua_newuserdata(L, sizeof(consvar_t)); luaL_getmetatable(L, META_CVAR); lua_setmetatable(L, -2); #define FIELDERROR(f, e) luaL_error(L, "bad value for " LUA_QL(f) " in table passed to " LUA_QL("CV_RegisterVar") " (%s)", e); #define TYPEERROR(f, t) FIELDERROR(f, va("%s expected, got %s", lua_typename(L, t), luaL_typename(L, -1))) lua_pushnil(L); while (lua_next(L, 1)) { // stack: cvar table, cvar userdata, key/index, value // 1 2 3 4 i = 0; k = NULL; if (lua_isnumber(L, 3)) i = lua_tointeger(L, 3); else if (lua_isstring(L, 3)) k = lua_tostring(L, 3); if (i == 1 || (k && fasticmp(k, "name"))) { if (!lua_isstring(L, 4)) TYPEERROR("name", LUA_TSTRING) cvar->name = Z_StrDup(lua_tostring(L, 4)); } else if (i == 2 || (k && fasticmp(k, "defaultvalue"))) { if (!lua_isstring(L, 4)) TYPEERROR("defaultvalue", LUA_TSTRING) cvar->defaultvalue = Z_StrDup(lua_tostring(L, 4)); } else if (i == 3 || (k && fasticmp(k, "flags"))) { if (!lua_isnumber(L, 4)) TYPEERROR("flags", LUA_TNUMBER) cvar->flags = (INT32)lua_tointeger(L, 4); } else if (i == 4 || (k && fasticmp(k, "PossibleValue"))) { if (lua_islightuserdata(L, 4)) { CV_PossibleValue_t *pv = lua_touserdata(L, 4); if (pv == CV_OnOff || pv == CV_YesNo || pv == CV_Unsigned || pv == CV_Natural) cvar->PossibleValue = pv; else FIELDERROR("PossibleValue", "CV_PossibleValue_t expected, got unrecognised pointer") } else if (lua_istable(L, 4)) { // Accepts tables in the form of {MIN=0, MAX=9999} or {Red=0, Green=1, Blue=2} // and converts them to CV_PossibleValue_t {{0,"MIN"},{9999,"MAX"}} or {{0,"Red"},{1,"Green"},{2,"Blue"}} // // I don't really like the way this does it because a single PossibleValue table // being used for multiple cvars will be converted and stored multiple times. // So maybe instead it should be a seperate function which must be run beforehand or something. size_t count = 0; CV_PossibleValue_t *cvpv; lua_pushnil(L); while (lua_next(L, 4)) { count++; lua_pop(L, 1); } lua_getfield(L, LUA_REGISTRYINDEX, "CV_PossibleValue"); I_Assert(lua_istable(L, 5)); lua_pushvalue(L, 2); // cvar userdata cvpv = lua_newuserdata(L, sizeof(CV_PossibleValue_t) * (count+1)); lua_rawset(L, 5); lua_pop(L, 1); // pop CV_PossibleValue registry table i = 0; lua_pushnil(L); while (lua_next(L, 4)) { // stack: [...] PossibleValue table, index, value // 4 5 6 if (lua_type(L, 5) != LUA_TSTRING || lua_type(L, 6) != LUA_TNUMBER) FIELDERROR("PossibleValue", "custom PossibleValue table requires a format of string=integer, i.e. {MIN=0, MAX=9999}"); cvpv[i].strvalue = Z_StrDup(lua_tostring(L, 5)); cvpv[i].value = (INT32)lua_tonumber(L, 6); i++; lua_pop(L, 1); } cvpv[i].value = 0; cvpv[i].strvalue = NULL; cvar->PossibleValue = cvpv; } else FIELDERROR("PossibleValue", va("%s or CV_PossibleValue_t expected, got %s", lua_typename(L, LUA_TTABLE), luaL_typename(L, -1))) } else if (cvar->flags & CV_CALL && (i == 5 || (k && fasticmp(k, "func")))) {