mirror of https://github.com/OpenTTD/OpenTTD
Codechange: [Script] use nlohmann for Squirrel <-> JSON conversion (#11251)
parent
6e8d7964ed
commit
5f9b8aaa95
|
@ -60,7 +60,7 @@ static const uint NETWORK_REVISION_LENGTH = 33; ///< The m
|
||||||
static const uint NETWORK_PASSWORD_LENGTH = 33; ///< The maximum length of the password, in bytes including '\0' (must be >= NETWORK_SERVER_ID_LENGTH)
|
static const uint NETWORK_PASSWORD_LENGTH = 33; ///< The maximum length of the password, in bytes including '\0' (must be >= NETWORK_SERVER_ID_LENGTH)
|
||||||
static const uint NETWORK_CLIENT_NAME_LENGTH = 25; ///< The maximum length of a client's name, in bytes including '\0'
|
static const uint NETWORK_CLIENT_NAME_LENGTH = 25; ///< The maximum length of a client's name, in bytes including '\0'
|
||||||
static const uint NETWORK_RCONCOMMAND_LENGTH = 500; ///< The maximum length of a rconsole command, in bytes including '\0'
|
static const uint NETWORK_RCONCOMMAND_LENGTH = 500; ///< The maximum length of a rconsole command, in bytes including '\0'
|
||||||
static const uint NETWORK_GAMESCRIPT_JSON_LENGTH = COMPAT_MTU - 3; ///< The maximum length of a gamescript json string, in bytes including '\0'. Must not be longer than COMPAT_MTU including header (3 bytes)
|
static const uint NETWORK_GAMESCRIPT_JSON_LENGTH = 9000; ///< The maximum length of a receiving gamescript json string, in bytes including '\0'.
|
||||||
static const uint NETWORK_CHAT_LENGTH = 900; ///< The maximum length of a chat message, in bytes including '\0'
|
static const uint NETWORK_CHAT_LENGTH = 900; ///< The maximum length of a chat message, in bytes including '\0'
|
||||||
static const uint NETWORK_CONTENT_FILENAME_LENGTH = 48; ///< The maximum length of a content's filename, in bytes including '\0'.
|
static const uint NETWORK_CONTENT_FILENAME_LENGTH = 48; ///< The maximum length of a content's filename, in bytes including '\0'.
|
||||||
static const uint NETWORK_CONTENT_NAME_LENGTH = 32; ///< The maximum length of a content's name, in bytes including '\0'.
|
static const uint NETWORK_CONTENT_NAME_LENGTH = 32; ///< The maximum length of a content's name, in bytes including '\0'.
|
||||||
|
|
|
@ -557,11 +557,6 @@ NetworkRecvStatus ServerNetworkAdminSocketHandler::SendConsole(const std::string
|
||||||
*/
|
*/
|
||||||
NetworkRecvStatus ServerNetworkAdminSocketHandler::SendGameScript(const std::string_view json)
|
NetworkRecvStatus ServerNetworkAdminSocketHandler::SendGameScript(const std::string_view json)
|
||||||
{
|
{
|
||||||
/* At the moment we cannot transmit anything larger than MTU. So we limit
|
|
||||||
* the maximum amount of json data that can be sent. Account also for
|
|
||||||
* the trailing \0 of the string */
|
|
||||||
if (json.size() + 1 >= NETWORK_GAMESCRIPT_JSON_LENGTH) return NETWORK_RECV_STATUS_OKAY;
|
|
||||||
|
|
||||||
Packet *p = new Packet(ADMIN_PACKET_SERVER_GAMESCRIPT);
|
Packet *p = new Packet(ADMIN_PACKET_SERVER_GAMESCRIPT);
|
||||||
|
|
||||||
p->Send_string(json);
|
p->Send_string(json);
|
||||||
|
|
|
@ -16,9 +16,9 @@
|
||||||
|
|
||||||
#include "../../safeguards.h"
|
#include "../../safeguards.h"
|
||||||
|
|
||||||
/* static */ bool ScriptAdmin::MakeJSON(HSQUIRRELVM vm, SQInteger index, int max_depth, std::string &data)
|
/* static */ bool ScriptAdmin::MakeJSON(nlohmann::json &json, HSQUIRRELVM vm, SQInteger index, int depth)
|
||||||
{
|
{
|
||||||
if (max_depth == 0) {
|
if (depth == SQUIRREL_MAX_DEPTH) {
|
||||||
ScriptLog::Error("Send parameters can only be nested to 25 deep. No data sent."); // SQUIRREL_MAX_DEPTH = 25
|
ScriptLog::Error("Send parameters can only be nested to 25 deep. No data sent."); // SQUIRREL_MAX_DEPTH = 25
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@
|
||||||
SQInteger res;
|
SQInteger res;
|
||||||
sq_getinteger(vm, index, &res);
|
sq_getinteger(vm, index, &res);
|
||||||
|
|
||||||
data = fmt::format("{}", res);
|
json = res;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,63 +36,52 @@
|
||||||
const SQChar *buf;
|
const SQChar *buf;
|
||||||
sq_getstring(vm, index, &buf);
|
sq_getstring(vm, index, &buf);
|
||||||
|
|
||||||
size_t len = strlen(buf) + 1;
|
json = std::string(buf);
|
||||||
if (len >= 255) {
|
|
||||||
ScriptLog::Error("Maximum string length is 254 chars. No data sent.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
data = fmt::format("\"{}\"", buf);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
case OT_ARRAY: {
|
case OT_ARRAY: {
|
||||||
data = "[ ";
|
json = nlohmann::json::array();
|
||||||
|
|
||||||
bool first = true;
|
|
||||||
sq_pushnull(vm);
|
sq_pushnull(vm);
|
||||||
while (SQ_SUCCEEDED(sq_next(vm, index - 1))) {
|
while (SQ_SUCCEEDED(sq_next(vm, index - 1))) {
|
||||||
if (!first) data += ", ";
|
nlohmann::json tmp;
|
||||||
if (first) first = false;
|
|
||||||
|
|
||||||
std::string tmp;
|
bool res = MakeJSON(tmp, vm, -1, depth + 1);
|
||||||
|
|
||||||
bool res = MakeJSON(vm, -1, max_depth - 1, tmp);
|
|
||||||
sq_pop(vm, 2);
|
sq_pop(vm, 2);
|
||||||
if (!res) {
|
if (!res) {
|
||||||
sq_pop(vm, 1);
|
sq_pop(vm, 1);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
data += tmp;
|
|
||||||
|
json.push_back(tmp);
|
||||||
}
|
}
|
||||||
sq_pop(vm, 1);
|
sq_pop(vm, 1);
|
||||||
data += " ]";
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
case OT_TABLE: {
|
case OT_TABLE: {
|
||||||
data = "{ ";
|
json = nlohmann::json::object();
|
||||||
|
|
||||||
bool first = true;
|
|
||||||
sq_pushnull(vm);
|
sq_pushnull(vm);
|
||||||
while (SQ_SUCCEEDED(sq_next(vm, index - 1))) {
|
while (SQ_SUCCEEDED(sq_next(vm, index - 1))) {
|
||||||
if (!first) data += ", ";
|
/* Squirrel ensure the key is a string. */
|
||||||
if (first) first = false;
|
assert(sq_gettype(vm, -2) == OT_STRING);
|
||||||
|
const SQChar *buf;
|
||||||
|
sq_getstring(vm, -2, &buf);
|
||||||
|
std::string key = std::string(buf);
|
||||||
|
|
||||||
std::string key;
|
nlohmann::json value;
|
||||||
std::string value;
|
bool res = MakeJSON(value, vm, -1, depth + 1);
|
||||||
|
|
||||||
/* Store the key + value */
|
|
||||||
bool res = MakeJSON(vm, -2, max_depth - 1, key) && MakeJSON(vm, -1, max_depth - 1, value);
|
|
||||||
sq_pop(vm, 2);
|
sq_pop(vm, 2);
|
||||||
if (!res) {
|
if (!res) {
|
||||||
sq_pop(vm, 1);
|
sq_pop(vm, 1);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
data += key + ": " + value;
|
|
||||||
|
json[key] = value;
|
||||||
}
|
}
|
||||||
sq_pop(vm, 1);
|
sq_pop(vm, 1);
|
||||||
data += " }";
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,17 +89,12 @@
|
||||||
SQBool res;
|
SQBool res;
|
||||||
sq_getbool(vm, index, &res);
|
sq_getbool(vm, index, &res);
|
||||||
|
|
||||||
if (res) {
|
json = res ? true : false;
|
||||||
data = "true";
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
data = "false";
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
case OT_NULL: {
|
case OT_NULL: {
|
||||||
data = "null";
|
json = nullptr;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,19 +112,13 @@
|
||||||
return sq_throwerror(vm, "ScriptAdmin::Send requires a table as first parameter. No data sent.");
|
return sq_throwerror(vm, "ScriptAdmin::Send requires a table as first parameter. No data sent.");
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string json;
|
nlohmann::json json;
|
||||||
if (!ScriptAdmin::MakeJSON(vm, -1, SQUIRREL_MAX_DEPTH, json)) {
|
if (!ScriptAdmin::MakeJSON(json, vm, -1)) {
|
||||||
sq_pushinteger(vm, 0);
|
sq_pushinteger(vm, 0);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (json.length() > NETWORK_GAMESCRIPT_JSON_LENGTH) {
|
NetworkAdminGameScript(json.dump());
|
||||||
ScriptLog::Error("You are trying to send a table that is too large to the AdminPort. No data sent.");
|
|
||||||
sq_pushinteger(vm, 0);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
NetworkAdminGameScript(json);
|
|
||||||
|
|
||||||
sq_pushinteger(vm, 1);
|
sq_pushinteger(vm, 1);
|
||||||
return 1;
|
return 1;
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#define SCRIPT_ADMIN_HPP
|
#define SCRIPT_ADMIN_HPP
|
||||||
|
|
||||||
#include "script_object.hpp"
|
#include "script_object.hpp"
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class that handles communication with the AdminPort.
|
* Class that handles communication with the AdminPort.
|
||||||
|
@ -38,13 +39,14 @@ public:
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/**
|
/**
|
||||||
* Convert a Squirrel structure into a JSON string.
|
* Convert a Squirrel structure into a JSON object.
|
||||||
|
* @param json The resulting JSON object.
|
||||||
* @param vm The VM to operate on.
|
* @param vm The VM to operate on.
|
||||||
* @param index The index we are currently working for.
|
* @param index The index we are currently working for.
|
||||||
* @param max_depth The maximal depth to follow the squirrel struct.
|
* @param depth The current depth in the squirrel struct.
|
||||||
* @param data The resulting json string.
|
* @return True iff the conversion was successful.
|
||||||
*/
|
*/
|
||||||
static bool MakeJSON(HSQUIRRELVM vm, SQInteger index, int max_depth, std::string &data);
|
static bool MakeJSON(nlohmann::json &data, HSQUIRRELVM vm, SQInteger index, int depth = 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* SCRIPT_ADMIN_HPP */
|
#endif /* SCRIPT_ADMIN_HPP */
|
||||||
|
|
|
@ -128,14 +128,24 @@ ScriptEventAdminPort::ScriptEventAdminPort(const std::string &json) :
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
#define SKIP_EMPTY(p) while (*(p) == ' ' || *(p) == '\n' || *(p) == '\r') (p)++;
|
|
||||||
#define RETURN_ERROR(stack) { ScriptLog::Error("Received invalid JSON data from AdminPort."); if (stack != 0) sq_pop(vm, stack); return nullptr; }
|
|
||||||
|
|
||||||
SQInteger ScriptEventAdminPort::GetObject(HSQUIRRELVM vm)
|
SQInteger ScriptEventAdminPort::GetObject(HSQUIRRELVM vm)
|
||||||
{
|
{
|
||||||
const char *p = this->json.c_str();
|
auto json = nlohmann::json::parse(this->json, nullptr, false);
|
||||||
|
|
||||||
|
if (!json.is_object()) {
|
||||||
|
ScriptLog::Error("The root element in the JSON data from AdminPort has to be an object.");
|
||||||
|
|
||||||
|
sq_pushnull(vm);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto top = sq_gettop(vm);
|
||||||
|
if (!this->ReadValue(vm, json)) {
|
||||||
|
/* Rewind the stack, removing anything that might be left on top. */
|
||||||
|
sq_settop(vm, top);
|
||||||
|
|
||||||
|
ScriptLog::Error("Received invalid JSON data from AdminPort.");
|
||||||
|
|
||||||
if (this->ReadTable(vm, p) == nullptr) {
|
|
||||||
sq_pushnull(vm);
|
sq_pushnull(vm);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -143,177 +153,59 @@ SQInteger ScriptEventAdminPort::GetObject(HSQUIRRELVM vm)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *ScriptEventAdminPort::ReadString(HSQUIRRELVM vm, const char *p)
|
bool ScriptEventAdminPort::ReadValue(HSQUIRRELVM vm, nlohmann::json &json)
|
||||||
{
|
{
|
||||||
const char *value = p;
|
switch (json.type()) {
|
||||||
|
case nlohmann::json::value_t::null:
|
||||||
|
sq_pushnull(vm);
|
||||||
|
break;
|
||||||
|
|
||||||
bool escape = false;
|
case nlohmann::json::value_t::boolean:
|
||||||
for (;;) {
|
sq_pushbool(vm, json.get<bool>() ? 1 : 0);
|
||||||
if (*p == '\\') {
|
break;
|
||||||
escape = true;
|
|
||||||
p++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (*p == '"' && escape) {
|
|
||||||
escape = false;
|
|
||||||
p++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
escape = false;
|
|
||||||
|
|
||||||
if (*p == '"') break;
|
|
||||||
if (*p == '\0') RETURN_ERROR(0);
|
|
||||||
|
|
||||||
p++;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t len = p - value;
|
|
||||||
sq_pushstring(vm, value, len);
|
|
||||||
p++; // Step past the end-of-string marker (")
|
|
||||||
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *ScriptEventAdminPort::ReadTable(HSQUIRRELVM vm, const char *p)
|
|
||||||
{
|
|
||||||
sq_newtable(vm);
|
|
||||||
|
|
||||||
SKIP_EMPTY(p);
|
|
||||||
if (*p++ != '{') RETURN_ERROR(1);
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
SKIP_EMPTY(p);
|
|
||||||
if (*p++ != '"') RETURN_ERROR(1);
|
|
||||||
|
|
||||||
p = ReadString(vm, p);
|
|
||||||
if (p == nullptr) {
|
|
||||||
sq_pop(vm, 1);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
SKIP_EMPTY(p);
|
|
||||||
if (*p++ != ':') RETURN_ERROR(2);
|
|
||||||
|
|
||||||
p = this->ReadValue(vm, p);
|
|
||||||
if (p == nullptr) {
|
|
||||||
sq_pop(vm, 2);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
sq_rawset(vm, -3);
|
|
||||||
/* The key (-2) and value (-1) are popped from the stack by squirrel. */
|
|
||||||
|
|
||||||
SKIP_EMPTY(p);
|
|
||||||
if (*p == ',') {
|
|
||||||
p++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
SKIP_EMPTY(p);
|
|
||||||
if (*p++ != '}') RETURN_ERROR(1);
|
|
||||||
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *ScriptEventAdminPort::ReadValue(HSQUIRRELVM vm, const char *p)
|
|
||||||
{
|
|
||||||
SKIP_EMPTY(p);
|
|
||||||
|
|
||||||
if (strncmp(p, "false", 5) == 0) {
|
|
||||||
sq_pushbool(vm, 0);
|
|
||||||
return p + 5;
|
|
||||||
}
|
|
||||||
if (strncmp(p, "true", 4) == 0) {
|
|
||||||
sq_pushbool(vm, 1);
|
|
||||||
return p + 4;
|
|
||||||
}
|
|
||||||
if (strncmp(p, "null", 4) == 0) {
|
|
||||||
sq_pushnull(vm);
|
|
||||||
return p + 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (*p) {
|
|
||||||
case '"': {
|
|
||||||
/* String */
|
|
||||||
p = ReadString(vm, ++p);
|
|
||||||
if (p == nullptr) return nullptr;
|
|
||||||
|
|
||||||
|
case nlohmann::json::value_t::string: {
|
||||||
|
auto value = json.get<std::string>();
|
||||||
|
sq_pushstring(vm, value.data(), value.size());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case '{': {
|
case nlohmann::json::value_t::number_integer:
|
||||||
/* Table */
|
case nlohmann::json::value_t::number_unsigned:
|
||||||
p = this->ReadTable(vm, p);
|
sq_pushinteger(vm, json.get<int64_t>());
|
||||||
if (p == nullptr) return nullptr;
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
case '[': {
|
case nlohmann::json::value_t::object:
|
||||||
/* Array */
|
sq_newtable(vm);
|
||||||
|
|
||||||
|
for (auto &[key, value] : json.items()) {
|
||||||
|
sq_pushstring(vm, key.data(), key.size());
|
||||||
|
|
||||||
|
if (!this->ReadValue(vm, value)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
sq_rawset(vm, -3);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case nlohmann::json::value_t::array:
|
||||||
sq_newarray(vm, 0);
|
sq_newarray(vm, 0);
|
||||||
|
|
||||||
/* Empty array? */
|
for (auto &value : json) {
|
||||||
const char *p2 = p + 1;
|
if (!this->ReadValue(vm, value)) {
|
||||||
SKIP_EMPTY(p2);
|
return false;
|
||||||
if (*p2 == ']') {
|
|
||||||
p = p2 + 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (*p++ != ']') {
|
|
||||||
p = this->ReadValue(vm, p);
|
|
||||||
if (p == nullptr) {
|
|
||||||
sq_pop(vm, 1);
|
|
||||||
return nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sq_arrayappend(vm, -2);
|
sq_arrayappend(vm, -2);
|
||||||
|
|
||||||
SKIP_EMPTY(p);
|
|
||||||
if (*p == ',') continue;
|
|
||||||
if (*p == ']') break;
|
|
||||||
RETURN_ERROR(1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
p++;
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
case '1': case '2': case '3': case '4': case '5':
|
|
||||||
case '6': case '7': case '8': case '9': case '0':
|
|
||||||
case '-': {
|
|
||||||
/* Integer */
|
|
||||||
|
|
||||||
const char *value = p++;
|
|
||||||
for (;;) {
|
|
||||||
switch (*p++) {
|
|
||||||
case '1': case '2': case '3': case '4': case '5':
|
|
||||||
case '6': case '7': case '8': case '9': case '0':
|
|
||||||
continue;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
p--;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
int res = atoi(value);
|
|
||||||
sq_pushinteger(vm, (SQInteger)res);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/* These types are not supported by Squirrel. */
|
||||||
|
case nlohmann::json::value_t::number_float:
|
||||||
default:
|
default:
|
||||||
RETURN_ERROR(0);
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return p;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef SKIP_EMPTY
|
|
||||||
#undef RETURN_ERROR
|
|
||||||
|
|
|
@ -14,6 +14,8 @@
|
||||||
#include "script_goal.hpp"
|
#include "script_goal.hpp"
|
||||||
#include "script_window.hpp"
|
#include "script_window.hpp"
|
||||||
|
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Event Vehicle Crash, indicating a vehicle of yours is crashed.
|
* Event Vehicle Crash, indicating a vehicle of yours is crashed.
|
||||||
* It contains the crash site, the crashed vehicle and the reason for the crash.
|
* It contains the crash site, the crashed vehicle and the reason for the crash.
|
||||||
|
@ -912,25 +914,11 @@ private:
|
||||||
std::string json; ///< The JSON string.
|
std::string json; ///< The JSON string.
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read a table from a JSON string.
|
* Convert a JSON part fo Squirrel.
|
||||||
* @param vm The VM used.
|
* @param vm The VM used.
|
||||||
* @param p The (part of the) JSON string reading.
|
* @param json The JSON part to convert to Squirrel.
|
||||||
*/
|
*/
|
||||||
const char *ReadTable(HSQUIRRELVM vm, const char *p);
|
bool ReadValue(HSQUIRRELVM vm, nlohmann::json &json);
|
||||||
|
|
||||||
/**
|
|
||||||
* Read a value from a JSON string.
|
|
||||||
* @param vm The VM used.
|
|
||||||
* @param p The (part of the) JSON string reading.
|
|
||||||
*/
|
|
||||||
const char *ReadValue(HSQUIRRELVM vm, const char *p);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read a string from a JSON string.
|
|
||||||
* @param vm The VM used.
|
|
||||||
* @param p The (part of the) JSON string reading.
|
|
||||||
*/
|
|
||||||
const char *ReadString(HSQUIRRELVM vm, const char *p);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -72,14 +72,14 @@ public:
|
||||||
REQUIRE(sq_gettype(vm, -1) == OT_TABLE);
|
REQUIRE(sq_gettype(vm, -1) == OT_TABLE);
|
||||||
|
|
||||||
/* Feed the snippet into the MakeJSON function. */
|
/* Feed the snippet into the MakeJSON function. */
|
||||||
std::string json;
|
nlohmann::json json;
|
||||||
if (!ScriptAdmin::MakeJSON(vm, -1, SQUIRREL_MAX_DEPTH, json)) {
|
if (!ScriptAdmin::MakeJSON(json, vm, -1)) {
|
||||||
sq_close(vm);
|
sq_close(vm);
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
sq_close(vm);
|
sq_close(vm);
|
||||||
return json;
|
return json.dump();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -111,11 +111,11 @@ public:
|
||||||
}
|
}
|
||||||
REQUIRE(sq_gettype(vm, -1) == OT_TABLE);
|
REQUIRE(sq_gettype(vm, -1) == OT_TABLE);
|
||||||
|
|
||||||
std::string squirrel_json;
|
nlohmann::json squirrel_json;
|
||||||
REQUIRE(ScriptAdmin::MakeJSON(vm, -1, SQUIRREL_MAX_DEPTH, squirrel_json) == true);
|
REQUIRE(ScriptAdmin::MakeJSON(squirrel_json, vm, -1) == true);
|
||||||
|
|
||||||
sq_close(vm);
|
sq_close(vm);
|
||||||
return squirrel_json;
|
return squirrel_json.dump();
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -124,18 +124,18 @@ TEST_CASE("Squirrel -> JSON conversion")
|
||||||
{
|
{
|
||||||
TestScriptController controller;
|
TestScriptController controller;
|
||||||
|
|
||||||
CHECK(TestScriptAdmin::MakeJSON(R"sq({ test = null })sq") == R"json({ "test": null })json");
|
CHECK(TestScriptAdmin::MakeJSON(R"sq({ test = null })sq") == R"json({"test":null})json");
|
||||||
CHECK(TestScriptAdmin::MakeJSON(R"sq({ test = 1 })sq") == R"json({ "test": 1 })json");
|
CHECK(TestScriptAdmin::MakeJSON(R"sq({ test = 1 })sq") == R"json({"test":1})json");
|
||||||
CHECK(TestScriptAdmin::MakeJSON(R"sq({ test = -1 })sq") == R"json({ "test": -1 })json");
|
CHECK(TestScriptAdmin::MakeJSON(R"sq({ test = -1 })sq") == R"json({"test":-1})json");
|
||||||
CHECK(TestScriptAdmin::MakeJSON(R"sq({ test = true })sq") == R"json({ "test": true })json");
|
CHECK(TestScriptAdmin::MakeJSON(R"sq({ test = true })sq") == R"json({"test":true})json");
|
||||||
CHECK(TestScriptAdmin::MakeJSON(R"sq({ test = "a" })sq") == R"json({ "test": "a" })json");
|
CHECK(TestScriptAdmin::MakeJSON(R"sq({ test = "a" })sq") == R"json({"test":"a"})json");
|
||||||
CHECK(TestScriptAdmin::MakeJSON(R"sq({ test = [ ] })sq") == R"json({ "test": [ ] })json");
|
CHECK(TestScriptAdmin::MakeJSON(R"sq({ test = [ ] })sq") == R"json({"test":[]})json");
|
||||||
CHECK(TestScriptAdmin::MakeJSON(R"sq({ test = [ 1 ] })sq") == R"json({ "test": [ 1 ] })json");
|
CHECK(TestScriptAdmin::MakeJSON(R"sq({ test = [ 1 ] })sq") == R"json({"test":[1]})json");
|
||||||
CHECK(TestScriptAdmin::MakeJSON(R"sq({ test = [ 1, "a", true, { test = 1 }, [], null ] })sq") == R"json({ "test": [ 1, "a", true, { "test": 1 }, [ ], null ] })json");
|
CHECK(TestScriptAdmin::MakeJSON(R"sq({ test = [ 1, "a", true, { test = 1 }, [], null ] })sq") == R"json({"test":[1,"a",true,{"test":1},[],null]})json");
|
||||||
CHECK(TestScriptAdmin::MakeJSON(R"sq({ test = { } })sq") == R"json({ "test": { } })json");
|
CHECK(TestScriptAdmin::MakeJSON(R"sq({ test = { } })sq") == R"json({"test":{}})json");
|
||||||
CHECK(TestScriptAdmin::MakeJSON(R"sq({ test = { test = 1 } })sq") == R"json({ "test": { "test": 1 } })json");
|
CHECK(TestScriptAdmin::MakeJSON(R"sq({ test = { test = 1 } })sq") == R"json({"test":{"test":1}})json");
|
||||||
CHECK(TestScriptAdmin::MakeJSON(R"sq({ test = { test = 1, test = 2 } })sq") == R"json({ "test": { "test": 2 } })json");
|
CHECK(TestScriptAdmin::MakeJSON(R"sq({ test = { test = 1, test = 2 } })sq") == R"json({"test":{"test":2}})json");
|
||||||
CHECK(TestScriptAdmin::MakeJSON(R"sq({ test = { test = 1, test2 = [ 2 ] } })sq") == R"json({ "test": { "test": 1, "test2": [ 2 ] } })json");
|
CHECK(TestScriptAdmin::MakeJSON(R"sq({ test = { test = 1, test2 = [ 2 ] } })sq") == R"json({"test":{"test":1,"test2":[2]}})json");
|
||||||
|
|
||||||
/* Cases that should fail, as we cannot convert a class to JSON. */
|
/* Cases that should fail, as we cannot convert a class to JSON. */
|
||||||
CHECK(TestScriptAdmin::MakeJSON(R"sq({ test = DummyClass })sq") == std::nullopt);
|
CHECK(TestScriptAdmin::MakeJSON(R"sq({ test = DummyClass })sq") == std::nullopt);
|
||||||
|
@ -147,23 +147,33 @@ TEST_CASE("JSON -> Squirrel conversion")
|
||||||
{
|
{
|
||||||
TestScriptController controller;
|
TestScriptController controller;
|
||||||
|
|
||||||
CHECK(TestScriptAdmin::TestScriptEventAdminPort(R"json({ "test": null })json") == R"json({ "test": null })json");
|
CHECK(TestScriptAdmin::TestScriptEventAdminPort(R"json({ "test": null })json") == R"json({"test":null})json");
|
||||||
CHECK(TestScriptAdmin::TestScriptEventAdminPort(R"json({ "test": 1 })json") == R"json({ "test": 1 })json");
|
CHECK(TestScriptAdmin::TestScriptEventAdminPort(R"json({ "test": 1 })json") == R"json({"test":1})json");
|
||||||
CHECK(TestScriptAdmin::TestScriptEventAdminPort(R"json({ "test": -1 })json") == R"json({ "test": -1 })json");
|
CHECK(TestScriptAdmin::TestScriptEventAdminPort(R"json({ "test": -1 })json") == R"json({"test":-1})json");
|
||||||
CHECK(TestScriptAdmin::TestScriptEventAdminPort(R"json({ "test": true })json") == R"json({ "test": true })json");
|
CHECK(TestScriptAdmin::TestScriptEventAdminPort(R"json({ "test": true })json") == R"json({"test":true})json");
|
||||||
CHECK(TestScriptAdmin::TestScriptEventAdminPort(R"json({ "test": "a" })json") == R"json({ "test": "a" })json");
|
CHECK(TestScriptAdmin::TestScriptEventAdminPort(R"json({ "test": "a" })json") == R"json({"test":"a"})json");
|
||||||
CHECK(TestScriptAdmin::TestScriptEventAdminPort(R"json({ "test": [] })json") == R"json({ "test": [ ] })json");
|
CHECK(TestScriptAdmin::TestScriptEventAdminPort(R"json({ "test": [] })json") == R"json({"test":[]})json");
|
||||||
CHECK(TestScriptAdmin::TestScriptEventAdminPort(R"json({ "test": [ 1 ] })json") == R"json({ "test": [ 1 ] })json");
|
CHECK(TestScriptAdmin::TestScriptEventAdminPort(R"json({ "test": [ 1 ] })json") == R"json({"test":[1]})json");
|
||||||
CHECK(TestScriptAdmin::TestScriptEventAdminPort(R"json({ "test": [ 1, "a", true, { "test": 1 }, [], null ] })json") == R"json({ "test": [ 1, "a", true, { "test": 1 }, [ ], null ] })json");
|
CHECK(TestScriptAdmin::TestScriptEventAdminPort(R"json({ "test": [ 1, "a", true, { "test": 1 }, [], null ] })json") == R"json({"test":[1,"a",true,{"test":1},[],null]})json");
|
||||||
// BUG -- This should work, but doesn't.
|
CHECK(TestScriptAdmin::TestScriptEventAdminPort(R"json({ "test": {} })json") == R"json({"test":{}})json");
|
||||||
// CHECK(TestScriptAdmin::TestScriptEventAdminPort(R"json({ "test": {} })json") == R"json({ "test": { } })json");
|
CHECK(TestScriptAdmin::TestScriptEventAdminPort(R"json({ "test": { "test": 1 } })json") == R"json({"test":{"test":1}})json");
|
||||||
CHECK(TestScriptAdmin::TestScriptEventAdminPort(R"json({ "test": { "test": 1 } })json") == R"json({ "test": { "test": 1 } })json");
|
CHECK(TestScriptAdmin::TestScriptEventAdminPort(R"json({ "test": { "test": 2 } })json") == R"json({"test":{"test":2}})json");
|
||||||
CHECK(TestScriptAdmin::TestScriptEventAdminPort(R"json({ "test": { "test": 2 } })json") == R"json({ "test": { "test": 2 } })json");
|
CHECK(TestScriptAdmin::TestScriptEventAdminPort(R"json({ "test": { "test": 1, "test2": [ 2 ] } })json") == R"json({"test":{"test":1,"test2":[2]}})json");
|
||||||
CHECK(TestScriptAdmin::TestScriptEventAdminPort(R"json({ "test": { "test": 1, "test2": [ 2 ] } })json") == R"json({ "test": { "test": 1, "test2": [ 2 ] } })json");
|
|
||||||
|
|
||||||
/* Check if spaces are properly ignored. */
|
/* Check if spaces are properly ignored. */
|
||||||
CHECK(TestScriptAdmin::TestScriptEventAdminPort(R"json({"test":1})json") == R"json({ "test": 1 })json");
|
CHECK(TestScriptAdmin::TestScriptEventAdminPort(R"json({"test":1})json") == R"json({"test":1})json");
|
||||||
CHECK(TestScriptAdmin::TestScriptEventAdminPort(R"json({"test": 1})json") == R"json({ "test": 1 })json");
|
CHECK(TestScriptAdmin::TestScriptEventAdminPort(R"json({"test": 1})json") == R"json({"test":1})json");
|
||||||
|
|
||||||
|
/* Valid JSON but invalid Squirrel (read: floats). */
|
||||||
|
CHECK(TestScriptAdmin::TestScriptEventAdminPort(R"json({ "test": 1.1 })json") == std::nullopt);
|
||||||
|
CHECK(TestScriptAdmin::TestScriptEventAdminPort(R"json({ "test": [ 1, 3, 1.1 ] })json") == std::nullopt);
|
||||||
|
|
||||||
|
/* Root element has to be an object. */
|
||||||
|
CHECK(TestScriptAdmin::TestScriptEventAdminPort(R"json( 1 )json") == std::nullopt);
|
||||||
|
CHECK(TestScriptAdmin::TestScriptEventAdminPort(R"json( "a" )json") == std::nullopt);
|
||||||
|
CHECK(TestScriptAdmin::TestScriptEventAdminPort(R"json( [ 1 ] )json") == std::nullopt);
|
||||||
|
CHECK(TestScriptAdmin::TestScriptEventAdminPort(R"json( null )json") == std::nullopt);
|
||||||
|
CHECK(TestScriptAdmin::TestScriptEventAdminPort(R"json( true )json") == std::nullopt);
|
||||||
|
|
||||||
/* Cases that should fail, as it is invalid JSON. */
|
/* Cases that should fail, as it is invalid JSON. */
|
||||||
CHECK(TestScriptAdmin::TestScriptEventAdminPort(R"json({"test":test})json") == std::nullopt);
|
CHECK(TestScriptAdmin::TestScriptEventAdminPort(R"json({"test":test})json") == std::nullopt);
|
||||||
|
|
Loading…
Reference in New Issue