Fix issue in Rocket.lua script.
[Cafu-Engine.git] / Libs / ConsoleCommands / ConsoleInterpreterImpl.cpp
blob609e00e623ac4f659a08b3783409dd9d4dc2079e
1 /*
2 Cafu Engine, http://www.cafu.de/
3 Copyright (c) Carsten Fuchs and other contributors.
4 This project is licensed under the terms of the MIT license.
5 */
7 ////////////////////////////////////////////////////////////////////////////////////////////
8 // This compilation unit (source code file) must *only* be linked to the main executable, //
9 // *not* to any (subsequently loaded) dll! This is because dlls don't have their own //
10 // ConsoleInterpreterImplT instance, but rather get a pointer to that of the executable! //
11 ////////////////////////////////////////////////////////////////////////////////////////////
13 #include "ConsoleInterpreterImpl.hpp"
14 #include "ConFunc.hpp"
15 #include "ConVar.hpp"
16 #include "Console.hpp"
17 #include "Console_Lua.hpp"
19 extern "C"
21 #include <lua.h>
22 #include <lualib.h>
23 #include <lauxlib.h>
26 #include <algorithm>
27 #include <sstream>
28 #include <cassert>
31 // The name of the Lua table we register all our ConVarTs and ConFuncTs in.
32 // This is usually "c" (short for "Cafu" or "console") or "_G" (the table of global variables).
33 // "_G" is less orderly, but a lot more convenient for users of course. :-)
34 static const char* CAFU_TABLE="_G";
36 // This implementation of the ConsoleInterpreterImplT class maintains the invariant that
37 // at the end of each method, the CAFU_TABLE is left as the only element on the Lua stack.
38 // This in turn makes the implementation of the methods a lot easier and more convenient,
39 // because it makes the ongoing getting and verification of the table unnecessary.
40 #define StackHasCafuTable() (lua_gettop(LuaState)==1 && lua_istable(LuaState, 1))
43 ConsoleInterpreterImplT::ConsoleInterpreterImplT()
44 : LuaState(NULL)
46 // Initialize Lua.
47 LuaState = luaL_newstate();
49 luaL_requiref(LuaState, "_G", luaopen_base, 1); lua_pop(LuaState, 1);
50 luaL_requiref(LuaState, LUA_LOADLIBNAME, luaopen_package, 1); lua_pop(LuaState, 1);
51 luaL_requiref(LuaState, LUA_COLIBNAME, luaopen_coroutine, 1); lua_pop(LuaState, 1);
52 luaL_requiref(LuaState, LUA_TABLIBNAME, luaopen_table, 1); lua_pop(LuaState, 1);
53 luaL_requiref(LuaState, LUA_IOLIBNAME, luaopen_io, 1); lua_pop(LuaState, 1);
54 luaL_requiref(LuaState, LUA_OSLIBNAME, luaopen_os, 1); lua_pop(LuaState, 1);
55 luaL_requiref(LuaState, LUA_STRLIBNAME, luaopen_string, 1); lua_pop(LuaState, 1);
56 luaL_requiref(LuaState, LUA_BITLIBNAME, luaopen_bit32, 1); lua_pop(LuaState, 1);
57 luaL_requiref(LuaState, LUA_MATHLIBNAME, luaopen_math, 1); lua_pop(LuaState, 1);
59 // Load the console library. (Adds a global table with name "Console" to the LuaState with the functions of the ConsoleI interface.)
60 cf::Console_RegisterLua(LuaState);
63 // Make sure that the table with name CAFU_TABLE exists.
64 // This table will be the "proxy" table for getting and setting the console variables,
65 // see PiL2 chapter 13.4 for details about table access metamethods.
66 lua_getglobal(LuaState, CAFU_TABLE);
68 if (!lua_istable(LuaState, -1))
70 if (!lua_isnil(LuaState, -1))
72 // No table at name CAFU_TABLE, but something else??
73 Console->Warning(cf::va("Global variable \"%s\" is not a table or nil, but a \"%s\" - overwriting with table.\n",
74 CAFU_TABLE, lua_typename(LuaState, lua_type(LuaState, -1))));
77 lua_pop(LuaState, 1); // Pop whatever it was.
78 lua_newtable(LuaState); // Create a new table.
79 lua_pushvalue(LuaState, -1); // Pushes/duplicates the new table on the stack.
80 lua_setglobal(LuaState, CAFU_TABLE); // Pop the copy and set it as a global variable with name CAFU_TABLE.
83 // This leaves the CAFU_TABLE on the stack.
84 assert(StackHasCafuTable());
87 // Create a new table T and add it into the registry table with "ConVars_Mediator" as the key and T as the value.
88 // This also leaves T on top of the stack. See PiL2 chapter 28.2 for more details.
89 // T will be used as the metatable for the CAFU_TABLE.
90 luaL_newmetatable(LuaState, "ConVars_Mediator");
92 static const luaL_Reg MediatorMethods[]=
94 { "__index", Lua_get_Callback },
95 { "__newindex", Lua_set_Callback },
96 // { "__tostring", toString },
97 { NULL, NULL }
100 // Insert the functions listed in MediatorMethods into T (the table on top of the stack).
101 luaL_setfuncs(LuaState, MediatorMethods, 0);
103 // Now set T as the metatable of CAFU_TABLE, "CAFU_TABLE.__metatable=T;".
104 // This removes T from the stack, leaving only the CAFU_TABLE.
105 lua_setmetatable(LuaState, -2);
107 // Note that "our" table is intentionally left on the stack, for convenient access by other methods below.
108 // See the comment for the StackHasCafuTable() macro for more details.
109 assert(StackHasCafuTable());
113 ConsoleInterpreterImplT::~ConsoleInterpreterImplT()
115 // Unfortunately we cannot assert(RegisteredConVars.Size()==0); here, because some
116 // static ConVarTs might be destructed after us (if this ConsoleInterpreterImplT was static, too).
118 // Unfortunately we cannot assert(RegisteredConFuncs.Size()==0); here, because some
119 // static ConFuncTs might be destructed after us (if this ConsoleInterpreterImplT was static, too).
121 // Close Lua.
122 lua_close(LuaState);
126 void ConsoleInterpreterImplT::Register(ConVarT* ConVar)
128 // Make sure that we have no console variable with the same name in our list yet.
129 for (unsigned long ConVarNr=0; ConVarNr<RegisteredConVars.Size(); ConVarNr++)
131 // If already registered, don't register again.
132 if (RegisteredConVars[ConVarNr]==ConVar) return;
134 if (RegisteredConVars[ConVarNr]->Name==ConVar->Name)
136 Console->Warning(std::string("Duplicate definition attempt:\nConsole variable with name \"")+ConVar->Name+"\" already defined!\n");
137 return;
141 RegisteredConVars.PushBack(ConVar);
144 // Note that "our" CAFU_TABLE should always be on the stack.
145 assert(StackHasCafuTable());
147 // See if there is already a value with this name in the table (raw access, no metamethod), that is, see if CAFU_TABLE[ConVar->Name]!=nil.
148 // If so, consider this value as a default value, set it as the value of ConVar, then raw-set CAFU_TABLE[ConVar->Name]=nil so that future
149 // accesses of CAFU_TABLE[ConVar->Name] occur via the metamethods.
150 // In short, "move" a possibly preexisting value in CAFU_TABLE from there into the ConVarT.
151 lua_pushstring(LuaState, ConVar->Name.c_str());
152 lua_rawget(LuaState, -2);
154 if (!lua_isnil(LuaState, -1))
156 switch (ConVar->GetType())
158 case ConVarT::String:
160 const char* Value=lua_tostring(LuaState, -1);
162 if (Value==NULL) Console->Warning(std::string("Cannot convert the default value for ")+ConVar->Name+" to type \"string\".\n");
163 ConVar->SetValue(Value!=NULL ? std::string(Value) : "");
164 break;
167 case ConVarT::Integer:
168 ConVar->SetValue(lua_tointeger(LuaState, -1));
169 break;
171 case ConVarT::Bool:
172 // I also want to treat the number 0 as false, not just "false" and "nil".
173 ConVar->SetValue(lua_isnumber(LuaState, -1) ? lua_tointeger(LuaState, -1)!=0 : lua_toboolean(LuaState, -1)!=0);
174 break;
176 case ConVarT::Double:
177 ConVar->SetValue(lua_tonumber(LuaState, -1));
178 break;
181 // Pop the old value of CAFU_TABLE[ConVar->Name] from the stack. (This leaves only the CAFU_TABLE.)
182 lua_pop(LuaState, 1);
183 assert(lua_gettop(LuaState)==1);
185 // "Delete" the ConVar->Name entry from the CAFU_TABLE, that is, raw-set CAFU_TABLE[ConVar->Name] to nil.
186 lua_pushstring(LuaState, ConVar->Name.c_str());
187 lua_pushnil(LuaState);
188 lua_rawset(LuaState, -3);
190 else
192 // There was no entry in CAFU_TABLE for ConVar->Name, that is, the raw value of cv[ConVar->Name] was nil.
193 // So just pop the nil from the stack and be done.
194 lua_pop(LuaState, 1);
197 assert(StackHasCafuTable());
201 void ConsoleInterpreterImplT::Register(ConFuncT* ConFunc)
203 // Make sure that we have no console function with the same name in our list yet.
204 for (unsigned long ConFuncNr=0; ConFuncNr<RegisteredConFuncs.Size(); ConFuncNr++)
206 // If already registered, don't register again.
207 if (RegisteredConFuncs[ConFuncNr]==ConFunc) return;
209 if (RegisteredConFuncs[ConFuncNr]->Name==ConFunc->Name)
211 Console->Warning(std::string("Duplicate definition attempt:\nConsole function with name \"")+ConFunc->Name+"\" already defined!\n");
212 return;
216 RegisteredConFuncs.PushBack(ConFunc);
219 // Register the function with Lua in the CAFU_TABLE.
220 // Using luaL_setfuncs() is not possible here, because it uses metamethods (is this still true with Lua 5.2?).
221 lua_pushstring(LuaState, ConFunc->GetName().c_str());
222 lua_pushcfunction(LuaState, ConFunc->LuaCFunction);
223 lua_rawset(LuaState, -3);
225 assert(StackHasCafuTable());
229 void ConsoleInterpreterImplT::Unregister(ConVarT* ConVar)
231 for (unsigned long ConVarNr=0; ConVarNr<RegisteredConVars.Size(); ConVarNr++)
232 if (RegisteredConVars[ConVarNr]==ConVar)
234 RegisteredConVars.RemoveAt(ConVarNr);
235 break;
239 // Save the value of ConVar by storing it in the CAFU_TABLE, using raw-set.
240 // This keeps the variable available to the script even though the ConVarT has been unregistered.
241 // Even better, if ConVar is registered again later (see Register() method), the value will then
242 // be "moved back" from the CAFU_TABLE to the ConVarT as the default value!
243 lua_pushstring(LuaState, ConVar->Name.c_str());
245 switch (ConVar->Type)
247 case ConVarT::String: lua_pushstring (LuaState, ConVar->GetValueString().c_str()); break;
248 case ConVarT::Integer: lua_pushinteger(LuaState, ConVar->GetValueInt() ); break;
249 case ConVarT::Bool: lua_pushboolean(LuaState, ConVar->GetValueBool() ); break;
250 case ConVarT::Double: lua_pushnumber (LuaState, ConVar->GetValueDouble() ); break;
253 lua_rawset(LuaState, -3);
255 assert(StackHasCafuTable());
259 void ConsoleInterpreterImplT::Unregister(ConFuncT* ConFunc)
261 for (unsigned long ConFuncNr=0; ConFuncNr<RegisteredConFuncs.Size(); ConFuncNr++)
262 if (RegisteredConFuncs[ConFuncNr]==ConFunc)
264 RegisteredConFuncs.RemoveAt(ConFuncNr);
265 break;
269 // Remove the console function from the CAFU_TABLE by setting CAFU_TABLE.FuncName to nil.
270 lua_pushstring(LuaState, ConFunc->GetName().c_str());
271 lua_pushnil(LuaState);
272 lua_rawset(LuaState, -3);
274 assert(StackHasCafuTable());
278 ConFuncT* ConsoleInterpreterImplT::FindFunc(const std::string& Name)
280 for (unsigned long ConFuncNr=0; ConFuncNr<RegisteredConFuncs.Size(); ConFuncNr++)
281 if (RegisteredConFuncs[ConFuncNr]->GetName()==Name)
282 return RegisteredConFuncs[ConFuncNr];
284 return NULL;
288 ConVarT* ConsoleInterpreterImplT::FindVar(const std::string& Name)
290 for (unsigned long ConVarNr=0; ConVarNr<RegisteredConVars.Size(); ConVarNr++)
291 if (RegisteredConVars[ConVarNr]->GetName()==Name)
292 return RegisteredConVars[ConVarNr];
294 return NULL;
298 std::string ConsoleInterpreterImplT::LineCompletion(const std::string& LineBegin, ArrayT<std::string>& Completions)
300 std::string::size_type PartialToken1st=LineBegin.length();
302 while (PartialToken1st>0 && (isalnum(LineBegin[PartialToken1st-1]) || LineBegin[PartialToken1st-1]=='_'))
304 PartialToken1st--;
307 const std::string PartialToken =std::string(LineBegin, PartialToken1st);
308 const std::string::size_type PartialTokenLen=PartialToken.length();
310 for (unsigned long ConVarNr=0; ConVarNr<RegisteredConVars.Size(); ConVarNr++)
311 if (RegisteredConVars[ConVarNr]->GetName().compare(0, PartialTokenLen, PartialToken)==0)
312 Completions.PushBack(RegisteredConVars[ConVarNr]->GetName());
314 #if 0
315 for (unsigned long ConFuncNr=0; ConFuncNr<RegisteredConFuncs.Size(); ConFuncNr++)
316 if (RegisteredConFuncs[ConFuncNr]->GetName().compare(0, PartialTokenLen, PartialToken)==0)
317 Completions.PushBack(RegisteredConFuncs[ConFuncNr]->GetName());
318 #else
319 lua_pushnil(LuaState); // Push the initial key.
320 while (lua_next(LuaState, -2)!=0)
322 // The key is now at stack index -2, the value is at index -1.
323 // Remove the value right now, because we never need it in this loop.
324 // Note that this moves the key to stack index -1.
325 lua_pop(LuaState, 1);
327 if (!lua_isstring(LuaState, -1))
329 Console->DevWarning(std::string("Skipped unexpected key type \"")+lua_typename(LuaState, lua_type(LuaState, -1))+"\" while traversing table \""+CAFU_TABLE+"\".\n");
330 continue;
333 const std::string Key=lua_tostring(LuaState, -1);
335 if (Key.compare(0, PartialTokenLen, PartialToken)==0)
336 Completions.PushBack(Key);
338 #endif
341 // Determine the prefix that is common to all completions.
342 if (Completions.Size()==0) return "";
344 std::string CommonPrefix=Completions[0];
346 for (unsigned long CompletionNr=1; CompletionNr<Completions.Size(); CompletionNr++)
348 const std::string::size_type MaxLen=std::min(CommonPrefix.length(), Completions[CompletionNr].length());
349 std::string NewCP;
351 for (unsigned long c=0; c<MaxLen && CommonPrefix[c]==Completions[CompletionNr][c]; c++)
352 NewCP+=CommonPrefix[c];
354 CommonPrefix=NewCP;
357 assert(StackHasCafuTable());
359 // Of the CommonPrefix, return only the part that is right of the PartialToken.
360 return std::string(CommonPrefix, PartialTokenLen);
364 // Note that this function can be reentrant (call itself recursively), e.g. when the Main Menu GUI
365 // calls RunCommand("changeLevel()"), the changeLevel() implementation in turn calls back into the
366 // Main Menu GUI (letting it know the new server state), and from there the Main Menu GUI in turn
367 // calls e.g. RunCommand("MusicLoad()").
368 bool ConsoleInterpreterImplT::RunCommand(const std::string& Input)
370 #ifndef NDEBUG
371 static unsigned long ReentrancyCount=0;
372 ReentrancyCount++;
373 #endif
375 if (luaL_loadstring(LuaState, Input.c_str())!=0 || lua_pcall(LuaState, 0, 0, 0)!=0)
377 const char* ErrorMsg=lua_tostring(LuaState, -1);
379 // Note that we need an extra newline here, because (all?) the Lua error messages don't have one.
380 Console->Print(std::string(ErrorMsg!=NULL ? ErrorMsg : "Unknown error.")+"\n");
382 lua_pop(LuaState, 1);
383 #ifndef NDEBUG
384 ReentrancyCount--;
385 #endif
386 assert(ReentrancyCount>0 || StackHasCafuTable());
387 return false;
390 #ifdef DEBUG
391 ReentrancyCount--;
392 #endif
393 assert(ReentrancyCount>0 || StackHasCafuTable());
394 return true;
398 int ConsoleInterpreterImplT::Lua_get_Callback(lua_State* LuaState)
400 // This function serves as the __index metamethod of the CAFU_TABLE.
401 // We are given the CAFU_TABLE instance as the first and the key (name of the ConVar) as the second parameter.
402 const char* ConVarName=luaL_checkstring(LuaState, 2);
403 ConVarT* ConVar =ConsoleInterpreter->FindVar(ConVarName);
405 if (ConVar==NULL) return luaL_error(LuaState, "Unknown identifier \"%s\".\n", ConVarName);
407 switch (ConVar->Type)
409 case ConVarT::String: lua_pushstring (LuaState, ConVar->GetValueString().c_str()); break;
410 case ConVarT::Integer: lua_pushinteger(LuaState, ConVar->GetValueInt() ); break;
411 case ConVarT::Bool: lua_pushboolean(LuaState, ConVar->GetValueBool() ); break;
412 case ConVarT::Double: lua_pushnumber (LuaState, ConVar->GetValueDouble() ); break;
415 return 1;
419 int ConsoleInterpreterImplT::Lua_set_Callback(lua_State* LuaState)
421 // This function serves as the __newindex metamethod of the CAFU_TABLE.
422 // We are given the CAFU_TABLE instance as the first, the key (name of the ConVar) as the second, and the new value as the third parameter.
423 const char* ConVarName=luaL_checkstring(LuaState, 2);
424 ConVarT* ConVar =ConsoleInterpreter->FindVar(ConVarName);
426 if (ConVar==NULL)
428 // Okay, we have no registered ConVarT with this name,
429 // so simply raw-write the value into the CAFU_TABLE instead!
430 // This is the key step that allows the user to work quasi normally with the CAFU_TABLE.
431 lua_rawset(LuaState, -3);
432 return 0;
435 switch (ConVar->Type)
437 case ConVarT::String:
439 const char* Value=lua_tostring(LuaState, 3);
441 ConVar->SetValue(Value!=NULL ? std::string(Value) : "");
442 break;
445 case ConVarT::Integer:
446 ConVar->SetValue(lua_tointeger(LuaState, 3));
447 break;
449 case ConVarT::Bool:
450 // I also want to treat the number 0 as false, not just "false" and "nil".
451 ConVar->SetValue(lua_isnumber(LuaState, 3) ? lua_tointeger(LuaState, 3)!=0 : lua_toboolean(LuaState, 3)!=0);
452 break;
454 case ConVarT::Double:
455 ConVar->SetValue(lua_tonumber(LuaState, 3));
456 break;
459 return 0;
463 int ConsoleInterpreterImplT::ConFunc_Help_Callback(lua_State* LuaState)
465 const std::string str_CAFU_TABLE=CAFU_TABLE;
467 if (lua_gettop(LuaState)==0)
469 Console->Print("The help(param) function provides help on the specified console function or variable.\n");
470 Console->Print("\"param\" must be a string with the name of a console function or variable to provide help for.\n");
471 Console->Print("For example, help(\"quit\") provides help on the console variable \"quit\".\n");
472 Console->Print("\n");
473 Console->Print("The list() function shows a list of all available console functions and variables.\n");
474 Console->Print("Passing an optional string parameter to list() reduces the output to functions and variables\n");
475 Console->Print("whose name begins with the given string.\n");
476 Console->Print("\n");
477 return 0;
480 if (!lua_isstring(LuaState, 1))
482 return luaL_error(LuaState, "The parameter is not a \"string\". Did you write help(x) when you intended to write help(\"x\")?\n");
485 const char* VarName=luaL_checkstring(LuaState, 1);
487 // 1. See if VarName refers to a console function.
489 ConFuncT* ConFunc=ConsoleInterpreter->FindFunc(VarName);
491 if (ConFunc!=NULL)
493 if (str_CAFU_TABLE!="_G") Console->Print(str_CAFU_TABLE+".");
495 Console->Print(ConFunc->GetName()+"() is a console function.\n");
496 Console->Print(cf::va("Flags: 0x%X\n", ConFunc->GetFlags()));
497 Console->Print("Description: "+ConFunc->GetDescription()+"\n");
498 return 0;
502 // 2. See if VarName refers to a console variable.
504 ConVarT* ConVar=ConsoleInterpreter->FindVar(VarName);
506 if (ConVar!=NULL)
508 if (str_CAFU_TABLE!="_G") Console->Print(str_CAFU_TABLE+".");
510 Console->Print(ConVar->GetName()+" is a console variable.\n");
512 switch (ConVar->GetType())
514 case ConVarT::String: Console->Print(cf::va("Value: \"%s\"\n", ConVar->GetValueString().c_str() )); Console->Print("Type: string\n"); break;
515 case ConVarT::Integer: Console->Print(cf::va("Value: %i\n", ConVar->GetValueInt() )); Console->Print("Type: int\n"); break;
516 case ConVarT::Bool: Console->Print(cf::va("Value: %s\n", ConVar->GetValueBool() ? "true" : "false")); Console->Print("Type: bool\n"); break;
517 case ConVarT::Double: Console->Print(cf::va("Value: %f\n", ConVar->GetValueDouble() )); Console->Print("Type: double\n"); break;
520 Console->Print(cf::va("Flags: 0x%X\n", ConVar->GetFlags()));
521 Console->Print("Description: "+ConVar->GetDescription()+"\n");
522 return 0;
526 // 3. VarName was neither a registered ConVarT nor a ConFuncT, now let's see if we find something in the CAFU_TABLE.
527 lua_getglobal(LuaState, CAFU_TABLE);
528 lua_pushstring(LuaState, VarName);
529 lua_rawget(LuaState, -2);
531 if (!lua_isnil(LuaState, -1))
533 // Note that the value cannot be one of our C functions of the ConFuncTs,
534 // because we already checked them above.
535 if (str_CAFU_TABLE!="_G") Console->Print(str_CAFU_TABLE+".");
537 Console->Print(cf::va("%s is a native Lua value of type \"%s\".\n", VarName, lua_typename(LuaState, lua_type(LuaState, -1))));
538 return 0;
541 // 4. Found nothing for VarName, give up.
542 return luaL_error(LuaState, "Unknown identifier \"%s\". Did you write help(x) when you intended to write help(\"x\")?\n", VarName);
545 static ConFuncT ConFunc_Help("help", ConsoleInterpreterImplT::ConFunc_Help_Callback, ConFuncT::FLAG_MAIN_EXE, "Provides help on the given function or variable.");
548 /// If no parameter is given, this function lists all known console functions and variables.
549 /// Otherwise, the output is limited to matches with the given prefix.
550 int ConsoleInterpreterImplT::ConFunc_List_Callback(lua_State* LuaState)
552 const std::string Prefix =lua_gettop(LuaState)==0 ? "" : luaL_checkstring(LuaState, 1);
553 const std::string::size_type PrefixLen=Prefix.length();
555 // 1. List the functions.
556 const ArrayT<ConFuncT*>& RegConFuncs=static_cast<ConsoleInterpreterImplT*>(ConsoleInterpreter)->RegisteredConFuncs;
557 unsigned long OutCount=0;
559 for (unsigned long ConFuncNr=0; ConFuncNr<RegConFuncs.Size(); ConFuncNr++)
560 if (RegConFuncs[ConFuncNr]->GetName().compare(0, PrefixLen, Prefix)==0)
562 const ConFuncT* ConFunc=RegConFuncs[ConFuncNr];
564 if (OutCount==0)
565 Console->Print("\nFunctions:\n");
567 if (OutCount>0)
568 Console->Print(OutCount % 3==0 ? ",\n" : ", ");
570 Console->Print(ConFunc->GetName()+"()");
571 OutCount++;
574 if (OutCount>0)
575 Console->Print("\n");
577 // 2. List the variables.
578 const ArrayT<ConVarT*>& RegConVars=static_cast<ConsoleInterpreterImplT*>(ConsoleInterpreter)->RegisteredConVars;
579 OutCount=0;
581 for (unsigned long ConVarNr=0; ConVarNr<RegConVars.Size(); ConVarNr++)
582 if (RegConVars[ConVarNr]->GetName().compare(0, PrefixLen, Prefix)==0)
584 const ConVarT* ConVar=RegConVars[ConVarNr];
586 if (OutCount==0)
587 Console->Print("\nVariables:\n");
589 if (OutCount>0)
590 Console->Print(OutCount % 2==0 ? ",\n" : ", ");
592 Console->Print(ConVar->GetName());
594 switch (ConVar->GetType())
596 case ConVarT::String: Console->Print(cf::va(" == \"%s\" [string]", ConVar->GetValueString().c_str() )); break;
597 case ConVarT::Integer: Console->Print(cf::va(" == %i [int]", ConVar->GetValueInt() )); break;
598 case ConVarT::Bool: Console->Print(cf::va(" == %s [bool]", ConVar->GetValueBool() ? "true" : "false")); break;
599 case ConVarT::Double: Console->Print(cf::va(" == %f [double]", ConVar->GetValueDouble() )); break;
602 OutCount++;
605 if (OutCount>0)
606 Console->Print("\n");
608 // 3. List the variables that are true (raw) members of the CAFU_TABLE (no ConVarT is registered for them, they are not in the RegisteredConVars).
609 lua_getglobal(LuaState, CAFU_TABLE);
610 OutCount=0;
612 lua_pushnil(LuaState); // Push the initial key.
613 while (lua_next(LuaState, -2)!=0)
615 // The key is now at stack index -2, the value is at index -1.
616 if (!lua_isstring(LuaState, -2))
618 Console->Warning(std::string("Skipped unexpected key type \"")+lua_typename(LuaState, lua_type(LuaState, -2))+"\" while traversing table \""+CAFU_TABLE+"\".\n");
620 // Remove the value, but keep the key for the next iteration.
621 lua_pop(LuaState, 1);
622 continue;
625 const std::string Key=lua_tostring(LuaState, -2);
627 // Skip all prefix mismatches.
628 if (Key.compare(0, PrefixLen, Prefix)!=0)
630 // Remove the value, but keep the key for the next iteration.
631 lua_pop(LuaState, 1);
632 continue;
635 // Skip the registered ConFuncTs.
636 if (ConsoleInterpreter->FindFunc(Key)!=NULL)
638 // Remove the value, but keep the key for the next iteration.
639 lua_pop(LuaState, 1);
640 continue;
643 if (OutCount==0)
644 Console->Print("\nOther:\n");
646 if (OutCount>0)
647 Console->Print(OutCount % 5==0 ? ",\n" : ", ");
649 Console->Print(Key);
651 switch (lua_type(LuaState, -1))
653 case LUA_TBOOLEAN: Console->Print(cf::va(" == %s", lua_toboolean(LuaState, -1)!=0 ? "true" : "false")); break;
654 case LUA_TSTRING: Console->Print(cf::va(" == \"%s\"", lua_tostring(LuaState, -1))); break;
655 case LUA_TNUMBER: Console->Print(cf::va(" == %f", lua_tonumber(LuaState, -1))); break;
657 default:
658 Console->Print(" == ...");
659 break;
662 Console->Print(cf::va(" [%s]", lua_typename(LuaState, lua_type(LuaState, -1))));
663 OutCount++;
665 // Remove the value, but keep the key for the next iteration.
666 lua_pop(LuaState, 1);
669 if (OutCount>0)
670 Console->Print("\n");
672 lua_pop(LuaState, 1);
674 // 4. Done.
675 return 0;
678 static ConFuncT ConFunc_List("list", ConsoleInterpreterImplT::ConFunc_List_Callback, ConFuncT::FLAG_MAIN_EXE, "Lists all console functions and variables, or only those matching a given prefix.");