Skip to content

Commit 384c1b6

Browse files
committed
Add error string generation
Fix map/vector to conform to existing behavior (keep reading on failure, skipping invalid values) Add value notes to errors
1 parent fa2fd23 commit 384c1b6

File tree

2 files changed

+123
-30
lines changed

2 files changed

+123
-30
lines changed

Shared/mods/deathmatch/logic/lua/CLuaFunctionParser.h

Lines changed: 123 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* Multi Theft Auto is available from http://www.multitheftauto.com/
77
*
88
*****************************************************************************/
9-
#pragma once
9+
#pragma once
1010

1111
#include <optional>
1212
#include <variant>
@@ -23,8 +23,103 @@ template <bool ErrorOnFailure, typename Ret, typename... Args, auto (*Func)(Args
2323
struct CLuaFunctionParser<ErrorOnFailure, Func>
2424
{
2525
std::size_t iIndex = 1;
26-
bool bFailed = false;
27-
std::string strError = "todo fix error message";
26+
std::string strError = "";
27+
std::string strErrorFoundType = "";
28+
29+
template <typename T>
30+
inline void typeToNameVariant(SString& accumulator)
31+
{
32+
using param = typename is_variant<T>::param1_t;
33+
if (accumulator.length() == 0)
34+
accumulator = typeToName<param>();
35+
else
36+
accumulator += "/" + typeToName<param>();
37+
38+
if constexpr (is_variant<T>::count != 1)
39+
return typeToNameVariant<is_variant<T>::rest_t>(accumulator);
40+
}
41+
42+
template <typename T>
43+
inline SString typeToName()
44+
{
45+
if constexpr (std::is_same_v<T, std::string>)
46+
return "string";
47+
else if constexpr (std::is_same_v<T, int> || std::is_same_v<T, float> || std::is_same_v<T, double> || std::is_same_v<T, short> ||
48+
std::is_same_v<T, unsigned int> || std::is_same_v<T, unsigned short>)
49+
return "number";
50+
else if constexpr (std::is_same_v<T, bool>)
51+
return "boolean";
52+
else if constexpr (std::is_enum_v<T>)
53+
return "enum";
54+
else if constexpr (is_specialization<T, std::optional>::value)
55+
{
56+
using param_t = typename is_specialization<T, std::optional>::param_t;
57+
return typeToName<param_t>();
58+
}
59+
else if constexpr (is_2specialization<T, std::vector>::value)
60+
return "table";
61+
else if constexpr (is_5specialization<T, std::unordered_map>::value)
62+
return "table";
63+
else if constexpr (std::is_same_v<T, CLuaFunctionRef>)
64+
return "function";
65+
else if constexpr (std::is_same_v<T, lua_State*>)
66+
return ""; // not reachable
67+
else if constexpr (is_variant<T>::value)
68+
{
69+
SString strTypes;
70+
typeToNameVariant<T>(strTypes);
71+
return strTypes;
72+
}
73+
74+
else if constexpr (std::is_pointer_v<T> && std::is_class_v<std::remove_pointer_t<T>>)
75+
return GetClassTypeName((T)0);
76+
}
77+
78+
static SString ResolveParameter(lua_State* L, std::size_t index)
79+
{
80+
switch (lua_type(L, index))
81+
{
82+
case LUA_TNUMBER:
83+
return SString("number (%d)", lua_tonumber(L, index));
84+
case LUA_TSTRING:
85+
{
86+
std::size_t iLen;
87+
const char* szValue = lua_tolstring(L, index, &iLen);
88+
std::string strValue(szValue, iLen);
89+
if (strValue.length() > 10)
90+
{
91+
strValue.resize(10); // Limit to 10 characters
92+
strValue[9] = '.';
93+
strValue[8] = '.';
94+
strValue[7] = '.';
95+
}
96+
// Avoid printing binary data
97+
if (std::find_if(strValue.begin(), strValue.end(), [](char ch) { return !(std::isprint(ch)); }) != strValue.end())
98+
return "string";
99+
else
100+
{
101+
return SString("string (\"%s\")", strValue.c_str());
102+
}
103+
}
104+
case LUA_TBOOLEAN:
105+
return SString("boolean (%s)", lua_toboolean(L, index) == 1 ? "true" : "false");
106+
case LUA_TNIL:
107+
return "nil";
108+
case LUA_TNONE:
109+
return "none";
110+
case LUA_TTABLE:
111+
return "table";
112+
case LUA_TFUNCTION:
113+
return "function";
114+
case LUA_TTHREAD:
115+
return "coroutine";
116+
case LUA_TUSERDATA:
117+
return GetUserDataClassName(*((void**)lua_touserdata(L, index)), L);
118+
case LUA_TLIGHTUSERDATA:
119+
return GetUserDataClassName(lua_touserdata(L, index), L);
120+
}
121+
return "";
122+
}
28123

29124
// Pop should remove a T from the Lua Stack after verifying that it is a valid type
30125
// Pop may also throw a LuaArgumentError to indicate failure
@@ -33,8 +128,10 @@ struct CLuaFunctionParser<ErrorOnFailure, Func>
33128
{
34129
if (!TypeMatch<T>(L, index))
35130
{
36-
// TODO: resolve error
37-
bFailed = true;
131+
SString strReceived = ResolveParameter(L, index);
132+
SString strExpected = typeToName<T>();
133+
SString strMessage("Expected %s at argument %d, got %s", strExpected.c_str(), index, strReceived.c_str());
134+
strError = strMessage;
38135
return T{};
39136
}
40137
return PopUnsafe<T>(L, index);
@@ -80,7 +177,7 @@ struct CLuaFunctionParser<ErrorOnFailure, Func>
80177
if constexpr (std::is_same_v<T, bool>)
81178
return (iArgument == LUA_TBOOLEAN);
82179

83-
// advanced types
180+
// Advanced types
84181
// Enums are represented as strings to Lua
85182
if constexpr (std::is_enum_v<T>)
86183
return iArgument == LUA_TSTRING;
@@ -162,8 +259,10 @@ struct CLuaFunctionParser<ErrorOnFailure, Func>
162259
return eValue;
163260
else
164261
{
165-
bFailed = true;
166-
// TODO: resolve error
262+
SString strReceived = ResolveParameter(L, index);
263+
SString strExpected = GetEnumTypeName((T)0);
264+
SString strMessage("Expected %s at argument %d, got %s", strExpected.c_str(), index, strReceived.c_str());
265+
strError = strMessage;
167266
return static_cast<T>(0);
168267
}
169268
}
@@ -184,11 +283,11 @@ struct CLuaFunctionParser<ErrorOnFailure, Func>
184283
lua_pushnil(L); /* first key */
185284
while (lua_next(L, index) != 0)
186285
{
187-
if (!TypeMatch<param>(L, -1) || !TypeMatch<int>(L, -2))
286+
if (!TypeMatch<param>(L, -1))
188287
{
189-
bFailed = true;
190-
// TODO: resolve error
191-
return vecData;
288+
// skip
289+
lua_pop(L, 1);
290+
continue;
192291
}
193292

194293
std::size_t i = -1;
@@ -208,9 +307,9 @@ struct CLuaFunctionParser<ErrorOnFailure, Func>
208307
{
209308
if (!TypeMatch<value_t>(L, -1) || !TypeMatch<key_t>(L, -2))
210309
{
211-
bFailed = true;
212-
// TODO: resolve error
213-
return map;
310+
// skip
311+
lua_pop(L, 1);
312+
continue;
214313
}
215314

216315
std::size_t i = -2;
@@ -243,29 +342,24 @@ struct CLuaFunctionParser<ErrorOnFailure, Func>
243342
bool isLightUserData = lua_type(L, index) == LUA_TLIGHTUSERDATA;
244343
void* pValue = lua::PopTrivial<void*>(L, index);
245344
using class_t = std::remove_pointer_t<T>;
246-
T result = nullptr;
247-
if (isLightUserData)
248-
{
249-
result = UserDataCast<class_t>((class_t*)0, pValue, L);
250-
}
251-
else
252-
{
253-
result = UserDataCast<class_t>((class_t*)0, *reinterpret_cast<void**>(pValue), L);
254-
}
345+
auto result =
346+
isLightUserData ? UserDataCast<class_t>((class_t*)0, pValue, L) : UserDataCast<class_t>((class_t*)0, *reinterpret_cast<void**>(pValue), L);
255347
if (result == nullptr)
256348
{
257-
bFailed = true;
258-
// TODO: resolve error
349+
SString strReceived = isLightUserData ? GetUserDataClassName(pValue, L) : GetUserDataClassName(*(void**)pValue, L);
350+
SString strExpected = GetClassTypeName((T)0);
351+
SString strMessage("Expected %s at argument %d, got %s", strExpected.c_str(), index, strReceived.c_str());
352+
strError = strMessage;
259353
return nullptr;
260354
}
261-
return result;
355+
return static_cast<T>(result);
262356
}
263357
}
264358

265359
template <typename... Params>
266360
inline auto Call(lua_State* L, Params&&... ps)
267361
{
268-
if (bFailed)
362+
if (strError.length() != 0)
269363
{
270364
return -1;
271365
}
@@ -298,7 +392,7 @@ struct CLuaFunctionParser<ErrorOnFailure, Func>
298392
{
299393
strError = e.what();
300394
}
301-
if (bFailed)
395+
if (strError.length() != 0)
302396
{
303397
if constexpr (ErrorOnFailure)
304398
{

Shared/mods/deathmatch/logic/luadefs/CLuaCryptDefs.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
#include <SharedUtil.Crypto.h>
1212
#include <lua/CLuaFunctionParser.h>
1313

14-
1514
void CLuaCryptDefs::LoadFunctions()
1615
{
1716
std::map<const char*, lua_CFunction> functions{

0 commit comments

Comments
 (0)