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.
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"
16 #include "Console.hpp"
17 #include "Console_Lua.hpp"
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()
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 },
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).
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");
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
) : "");
167 case ConVarT::Integer
:
168 ConVar
->SetValue(lua_tointeger(LuaState
, -1));
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);
176 case ConVarT::Double
:
177 ConVar
->SetValue(lua_tonumber(LuaState
, -1));
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);
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");
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
);
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
);
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
];
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
];
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]=='_'))
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());
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());
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");
333 const std::string Key
=lua_tostring(LuaState
, -1);
335 if (Key
.compare(0, PartialTokenLen
, PartialToken
)==0)
336 Completions
.PushBack(Key
);
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());
351 for (unsigned long c
=0; c
<MaxLen
&& CommonPrefix
[c
]==Completions
[CompletionNr
][c
]; c
++)
352 NewCP
+=CommonPrefix
[c
];
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
)
371 static unsigned long ReentrancyCount
=0;
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);
386 assert(ReentrancyCount
>0 || StackHasCafuTable());
393 assert(ReentrancyCount
>0 || StackHasCafuTable());
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;
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
);
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);
435 switch (ConVar
->Type
)
437 case ConVarT::String
:
439 const char* Value
=lua_tostring(LuaState
, 3);
441 ConVar
->SetValue(Value
!=NULL
? std::string(Value
) : "");
445 case ConVarT::Integer
:
446 ConVar
->SetValue(lua_tointeger(LuaState
, 3));
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);
454 case ConVarT::Double
:
455 ConVar
->SetValue(lua_tonumber(LuaState
, 3));
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");
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
);
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");
502 // 2. See if VarName refers to a console variable.
504 ConVarT
* ConVar
=ConsoleInterpreter
->FindVar(VarName
);
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");
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))));
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
];
565 Console
->Print("\nFunctions:\n");
568 Console
->Print(OutCount
% 3==0 ? ",\n" : ", ");
570 Console
->Print(ConFunc
->GetName()+"()");
575 Console
->Print("\n");
577 // 2. List the variables.
578 const ArrayT
<ConVarT
*>& RegConVars
=static_cast<ConsoleInterpreterImplT
*>(ConsoleInterpreter
)->RegisteredConVars
;
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
];
587 Console
->Print("\nVariables:\n");
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;
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
);
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);
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);
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);
644 Console
->Print("\nOther:\n");
647 Console
->Print(OutCount
% 5==0 ? ",\n" : ", ");
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;
658 Console
->Print(" == ...");
662 Console
->Print(cf::va(" [%s]", lua_typename(LuaState
, lua_type(LuaState
, -1))));
665 // Remove the value, but keep the key for the next iteration.
666 lua_pop(LuaState
, 1);
670 Console
->Print("\n");
672 lua_pop(LuaState
, 1);
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.");