Cuberite
A lightweight, fast and extensible game server for Minecraft
LuaJson.cpp
Go to the documentation of this file.
1 
2 // LuaJson.cpp
3 
4 // Implements the Json exposure bindings to Lua
5 
6 #include "Globals.h"
7 #include <sstream>
8 #include "LuaJson.h"
9 #include "LuaState.h"
10 #include "tolua++/include/tolua++.h"
11 #include "json/json.h"
12 
13 
14 
15 
16 
17 // fwd:
18 
19 static void PushJsonValue(const Json::Value & a_Value, cLuaState & a_LuaState);
20 static Json::Value JsonSerializeValue(cLuaState & a_LuaState);
21 
22 
23 
24 
29  public std::runtime_error
30 {
31  typedef std::runtime_error Super;
32 public:
34  explicit CannotSerializeException(const AString & a_ValueName, const char * a_ErrorMsg):
35  Super(a_ErrorMsg),
36  m_ValueName(a_ValueName)
37  {
38  }
39 
41  explicit CannotSerializeException(int a_ValueIndex, const char * a_ErrorMsg):
42  Super(a_ErrorMsg),
43  m_ValueName(Printf("%d", a_ValueIndex))
44  {
45  }
46 
49  explicit CannotSerializeException(const CannotSerializeException & a_Parent, const AString & a_ValueNamePrefix):
50  Super(a_Parent.what()),
51  m_ValueName(a_ValueNamePrefix + "." + a_Parent.m_ValueName)
52  {
53  }
54 
57  explicit CannotSerializeException(const CannotSerializeException & a_Parent, int a_ValueNamePrefixIndex):
58  Super(a_Parent.what()),
59  m_ValueName(Printf("%d", a_ValueNamePrefixIndex) + "." + a_Parent.m_ValueName)
60  {
61  }
62 
63  const AString & GetValueName() const { return m_ValueName; }
64 
65 protected:
67 };
68 
69 
70 
71 
72 
75 static void PushJsonArray(const Json::Value & a_Value, cLuaState & a_LuaState)
76 {
77  // Create the appropriately-sized Lua table:
78  lua_createtable(a_LuaState, static_cast<int>(a_Value.size()), 0);
79 
80  // Insert each value to the appropriate index (1-based):
81  int idx = 1;
82  for (const auto & v: a_Value)
83  {
84  // Include Json null values in the array - it will have holes, but indices will stay the same
85  PushJsonValue(v, a_LuaState);
86  lua_rawseti(a_LuaState, -2, idx);
87  idx += 1;
88  } // for v: a_Value[]
89 }
90 
91 
92 
93 
94 
97 static void PushJsonObject(const Json::Value & a_Value, cLuaState & a_LuaState)
98 {
99  // Create the appropriately-sized Lua table:
100  lua_createtable(a_LuaState, 0, static_cast<int>(a_Value.size()));
101 
102  // Json::Value has no means of iterating over children with their names included.
103  // We need to iterate over names and "find" them in the object again:
104  auto names = a_Value.getMemberNames();
105  for (const auto & n: names)
106  {
107  auto v = a_Value[n];
108  if (v.isNull())
109  {
110  // Skip null values
111  continue;
112  }
113 
114  // Set the value in Lua's table:
115  a_LuaState.Push(n);
116  PushJsonValue(v, a_LuaState);
117  lua_rawset(a_LuaState, -3);
118  } // for itr - a_Value[]
119 }
120 
121 
122 
123 
124 
126 void PushJsonValue(const Json::Value & a_Value, cLuaState & a_LuaState)
127 {
128  switch (a_Value.type())
129  {
130  case Json::nullValue:
131  {
132  a_LuaState.Push(cLuaState::Nil);
133  break;
134  }
135 
136  case Json::intValue:
137  case Json::uintValue:
138  case Json::realValue:
139  {
140  a_LuaState.Push(static_cast<lua_Number>(a_Value.asDouble()));
141  break;
142  }
143 
144  case Json::booleanValue:
145  {
146  a_LuaState.Push(a_Value.asBool());
147  break;
148  }
149 
150  case Json::stringValue:
151  {
152  a_LuaState.Push(a_Value.asString());
153  break;
154  }
155 
156  case Json::arrayValue:
157  {
158  PushJsonArray(a_Value, a_LuaState);
159  break;
160  }
161 
162  case Json::objectValue:
163  {
164  PushJsonObject(a_Value, a_LuaState);
165  break;
166  }
167  } // switch (v.type())
168 }
169 
170 
171 
172 
173 
176 static Json::Value JsonSerializeTable(cLuaState & a_LuaState)
177 {
178  Json::Value res;
179  lua_pushnil(a_LuaState);
180  while (lua_next(a_LuaState, -2) != 0)
181  {
182  if (lua_type(a_LuaState, -2) == LUA_TNUMBER)
183  {
184  int idx;
185  a_LuaState.GetStackValue(-2, idx);
186  try
187  {
188  res[idx - 1] = JsonSerializeValue(a_LuaState);
189  }
190  catch (const CannotSerializeException & exc)
191  {
192  throw CannotSerializeException(exc, idx);
193  }
194  catch (const std::exception & exc) // Cannot catch Json::Exception, because it's not properly defined
195  {
196  throw CannotSerializeException(idx, exc.what());
197  }
198  }
199  else
200  {
201  AString name;
202  if (a_LuaState.GetStackValue(-2, name))
203  {
204  try
205  {
206  res[name] = JsonSerializeValue(a_LuaState);
207  }
208  catch (const CannotSerializeException & exc)
209  {
210  throw CannotSerializeException(exc, name);
211  }
212  catch (const std::exception & exc) // Cannot catch Json::Exception, because it's not properly defined
213  {
214  throw CannotSerializeException(name, exc.what());
215  }
216  }
217  }
218  lua_pop(a_LuaState, 1);
219  }
220  return res;
221 }
222 
223 
224 
225 
226 
228 static Json::Value JsonSerializeValue(cLuaState & a_LuaState)
229 {
230  switch (lua_type(a_LuaState, -1))
231  {
232  case LUA_TBOOLEAN:
233  {
234  bool v;
235  a_LuaState.GetStackValue(-1, v);
236  return Json::Value(v);
237  }
238  case LUA_TNIL:
239  {
240  return Json::Value(Json::nullValue);
241  }
242  case LUA_TNUMBER:
243  {
244  lua_Number v;
245  a_LuaState.GetStackValue(-1, v);
246  return Json::Value(v);
247  }
248  case LUA_TSTRING:
249  {
250  AString v;
251  a_LuaState.GetStackValue(-1, v);
252  return Json::Value(v);
253  }
254  case LUA_TTABLE:
255  {
256  return JsonSerializeTable(a_LuaState);
257  }
258  default:
259  {
260  LOGD("Attempting to serialize an unhandled Lua value type: %d", lua_type(a_LuaState, -1));
261  return Json::Value(Json::nullValue);
262  }
263  }
264 }
265 
266 
267 
268 
269 
270 static int tolua_cJson_Parse(lua_State * a_LuaState)
271 {
272  // Function signature:
273  // cJson:Parse("string") -> table
274 
275  // Check the param types:
276  cLuaState L(a_LuaState);
277  if (
278  !L.CheckParamUserTable(1, "cJson") ||
279  !L.CheckParamString(2) ||
280  !L.CheckParamEnd(3)
281  )
282  {
283  return 0;
284  }
285 
286  // Get the input string:
287  AString input;
288  if (!L.GetStackValue(2, input))
289  {
290  LOGWARNING("cJson:Parse(): Cannot read input string");
291  L.LogStackTrace();
292  return 0;
293  }
294 
295  // Parse the string:
296  Json::Value root;
297  Json::Reader reader;
298  if (!reader.parse(input, root, false))
299  {
300  L.Push(cLuaState::Nil, Printf("Parsing Json failed: %s", reader.getFormattedErrorMessages().c_str()));
301  return 2;
302  }
303 
304  // Push the Json value onto Lua stack:
305  PushJsonValue(root, L);
306  return 1;
307 }
308 
309 
310 
311 
312 
313 static int tolua_cJson_Serialize(lua_State * a_LuaState)
314 {
315  // Function signature:
316  // cJson:Serialize({table}, [{option1 = value1, option2 = value2}]) -> string
317 
318  // Check the param types:
319  cLuaState L(a_LuaState);
320  if (
321  !L.CheckParamUserTable(1, "cJson") ||
322  !L.CheckParamTable(2) ||
323  !L.CheckParamEnd(4)
324  )
325  {
326  return 0;
327  }
328 
329  // Push the table to the top of the Lua stack, and call the serializing function:
330  lua_pushvalue(L, 2);
331  Json::Value root;
332  try
333  {
334  root = JsonSerializeValue(L);
335  }
336  catch (const CannotSerializeException & exc)
337  {
338  lua_pushnil(L);
339  L.Push(Printf("Cannot serialize into Json, value \"%s\" caused an error \"%s\"", exc.GetValueName().c_str(), exc.what()));
340  return 2;
341  }
342  lua_pop(L, 1);
343 
344  // Create the writer, with all properties (optional param 3) applied to it:
345  Json::StreamWriterBuilder builder;
346  if (lua_istable(L, 3))
347  {
348  lua_pushnil(L);
349  while (lua_next(L, -2) != 0)
350  {
351  if (lua_type(L, -2) == LUA_TSTRING)
352  {
353  AString propName, propValue;
354  if (L.GetStackValues(-2, propName, propValue))
355  {
356  builder[propName] = propValue;
357  }
358  }
359  lua_pop(L, 1);
360  }
361  // Check for invalid settings:
362  Json::Value invalid;
363  if (!builder.validate(&invalid))
364  {
365  LOGINFO("cJson:Serialize(): detected invalid settings:");
366  for (const auto & n: invalid.getMemberNames())
367  {
368  LOGINFO(" \"%s\" (\"%s\")", n.c_str(), invalid[n].asCString());
369  }
370  }
371  }
372  auto writer(builder.newStreamWriter());
373 
374  // Serialize the string and push it as the return value:
375  std::stringstream ss;
376  writer->write(root, &ss);
377  L.Push(ss.str());
378  return 1;
379 }
380 
381 
382 
383 
384 
385 void cLuaJson::Bind(cLuaState & a_LuaState)
386 {
387  tolua_beginmodule(a_LuaState, nullptr);
388 
389  // Create the cJson API class:
390  tolua_usertype(a_LuaState, "cJson");
391  tolua_cclass(a_LuaState, "cJson", "cJson", "", nullptr);
392 
393  // Fill in the functions (alpha-sorted):
394  tolua_beginmodule(a_LuaState, "cJson");
395  tolua_function(a_LuaState, "Parse", tolua_cJson_Parse);
396  tolua_function(a_LuaState, "Serialize", tolua_cJson_Serialize);
397  tolua_endmodule(a_LuaState);
398  tolua_endmodule(a_LuaState);
399 }
400 
401 
402 
403 
bool CheckParamString(int a_StartParam, int a_EndParam=-1)
Returns true if the specified parameters on the stack are strings; also logs warning if not...
Definition: LuaState.cpp:1717
bool CheckParamUserTable(int a_StartParam, const char *a_UserTable, int a_EndParam=-1)
Returns true if the specified parameters on the stack are of the specified usertable type; also logs ...
Definition: LuaState.cpp:1549
std::runtime_error Super
Definition: LuaJson.cpp:31
CannotSerializeException(const CannotSerializeException &a_Parent, const AString &a_ValueNamePrefix)
Constructs a new instance of the exception that takes the error message and value name from the paren...
Definition: LuaJson.cpp:49
Exception thrown when the input cannot be serialized.
Definition: LuaJson.cpp:28
static void PushJsonArray(const Json::Value &a_Value, cLuaState &a_LuaState)
Pushes the specified Json array as a table on top of the specified Lua state.
Definition: LuaJson.cpp:75
static Json::Value JsonSerializeValue(cLuaState &a_LuaState)
Serializes the Lua value at the top of the specified Lua state into a Json value. ...
Definition: LuaJson.cpp:228
static void Bind(cLuaState &a_LuaState)
Registers the Json library in the specified Lua state.
Definition: LuaJson.cpp:385
void LogStackTrace(int a_StartingDepth=0)
Logs all items in the current stack trace to the server console.
Definition: LuaState.cpp:1980
bool GetStackValue(int a_StackPos, AString &a_Value)
Definition: LuaState.cpp:1105
Encapsulates a Lua state and provides some syntactic sugar for common operations. ...
Definition: LuaState.h:57
static const cNil Nil
Definition: LuaState.h:453
static int tolua_cJson_Serialize(lua_State *a_LuaState)
Definition: LuaJson.cpp:313
static int tolua_cJson_Parse(lua_State *a_LuaState)
Definition: LuaJson.cpp:270
bool CheckParamTable(int a_StartParam, int a_EndParam=-1)
Returns true if the specified parameters on the stack are tables; also logs warning if not...
Definition: LuaState.cpp:1615
CannotSerializeException(int a_ValueIndex, const char *a_ErrorMsg)
Constructs a new instance of the exception based on the provided values directly. ...
Definition: LuaJson.cpp:41
bool CheckParamEnd(int a_Param)
Returns true if the specified parameter on the stack is nil (indicating an end-of-parameters) ...
Definition: LuaState.cpp:1860
CannotSerializeException(const AString &a_ValueName, const char *a_ErrorMsg)
Constructs a new instance of the exception based on the provided values directly. ...
Definition: LuaJson.cpp:34
static void PushJsonObject(const Json::Value &a_Value, cLuaState &a_LuaState)
Pushes the specified Json object as a table on top of the specified Lua state.
Definition: LuaJson.cpp:97
void LOGINFO(const char *a_Format, fmt::ArgList a_ArgList)
Definition: Logger.cpp:165
AString & Printf(AString &str, const char *format, fmt::ArgList args)
Output the formatted text into the string.
Definition: StringUtils.cpp:55
void LOGWARNING(const char *a_Format, fmt::ArgList a_ArgList)
Definition: Logger.cpp:174
#define LOGD(...)
Definition: LoggerSimple.h:40
static void PushJsonValue(const Json::Value &a_Value, cLuaState &a_LuaState)
Pushes the specified Json value as an appropriate type on top of the specified Lua state...
Definition: LuaJson.cpp:126
std::string AString
Definition: StringUtils.h:13
static Json::Value JsonSerializeTable(cLuaState &a_LuaState)
Serializes the Lua table at the top of the specified Lua state&#39;s stack into a Json value...
Definition: LuaJson.cpp:176
CannotSerializeException(const CannotSerializeException &a_Parent, int a_ValueNamePrefixIndex)
Constructs a new instance of the exception that takes the error message and value name from the paren...
Definition: LuaJson.cpp:57
const AString & GetValueName() const
Definition: LuaJson.cpp:63
bool GetStackValues(int a_StartStackPos, Arg1 &&a_Arg1, Args &&...args)
Retrieves a list of values from the Lua stack, starting at the specified index.
Definition: LuaState.h:759
void Push(Arg1 &&a_Arg1, Arg2 &&a_Arg2, Args &&...a_Args)
Pushes multiple arguments onto the Lua stack.
Definition: LuaState.h:605