Merge branch 'master' into jgrpp
# Conflicts: # cmake/SourceList.cmake # src/build_vehicle_gui.cpp # src/company_gui.cpp # src/console_cmds.cpp # src/depot_base.h # src/elrail.cpp # src/network/core/udp.cpp # src/network/network_admin.cpp # src/network/network_chat_gui.cpp # src/network/network_gui.cpp # src/network/network_server.cpp # src/newgrf.cpp # src/newgrf_engine.cpp # src/newgrf_railtype.cpp # src/newgrf_railtype.h # src/newgrf_storage.h # src/os/unix/crashlog_unix.cpp # src/rail.h # src/rail_cmd.cpp # src/rail_gui.cpp # src/road_cmd.cpp # src/road_map.h # src/saveload/labelmaps_sl.cpp # src/settings_gui.cpp # src/settings_type.h # src/sl/oldloader_sl.cpp # src/station_cmd.cpp # src/station_gui.cpp # src/table/settings/world_settings.ini # src/tests/test_script_admin.cpp # src/textfile_gui.cpp # src/toolbar_gui.cpp # src/train_cmd.cpp # src/tunnelbridge_cmd.cpp # src/vehicle_gui.cpp # src/widget.cpp # src/window.cpp # src/window_gui.h # src/window_type.h
This commit is contained in:
@@ -14,12 +14,24 @@
|
||||
#include "../script_instance.hpp"
|
||||
#include "../../string_func.h"
|
||||
#include "../../core/format.hpp"
|
||||
#include "../../3rdparty/nlohmann/json.hpp"
|
||||
|
||||
#include "../../safeguards.h"
|
||||
|
||||
/* static */ bool ScriptAdmin::MakeJSON(HSQUIRRELVM vm, SQInteger index, int max_depth, std::string &data)
|
||||
/**
|
||||
* Convert a Squirrel structure into a JSON object.
|
||||
*
|
||||
* This function is not "static", so it can be tested in unittests.
|
||||
*
|
||||
* @param json The resulting JSON object.
|
||||
* @param vm The VM to operate on.
|
||||
* @param index The index we are currently working for.
|
||||
* @param depth The current depth in the squirrel struct.
|
||||
* @return True iff the conversion was successful.
|
||||
*/
|
||||
bool ScriptAdminMakeJSON(nlohmann::json &json, HSQUIRRELVM vm, SQInteger index, int depth = 0)
|
||||
{
|
||||
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
|
||||
return false;
|
||||
}
|
||||
@@ -29,7 +41,7 @@
|
||||
SQInteger res;
|
||||
sq_getinteger(vm, index, &res);
|
||||
|
||||
data = fmt::format("{}", res);
|
||||
json = res;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -37,63 +49,52 @@
|
||||
const SQChar *buf;
|
||||
sq_getstring(vm, index, &buf);
|
||||
|
||||
size_t len = strlen(buf) + 1;
|
||||
if (len >= 255) {
|
||||
ScriptLog::Error("Maximum string length is 254 chars. No data sent.");
|
||||
return false;
|
||||
}
|
||||
|
||||
data = fmt::format("\"{}\"", buf);
|
||||
json = std::string(buf);
|
||||
return true;
|
||||
}
|
||||
|
||||
case OT_ARRAY: {
|
||||
data = "[ ";
|
||||
json = nlohmann::json::array();
|
||||
|
||||
bool first = true;
|
||||
sq_pushnull(vm);
|
||||
while (SQ_SUCCEEDED(sq_next(vm, index - 1))) {
|
||||
if (!first) data += ", ";
|
||||
if (first) first = false;
|
||||
nlohmann::json tmp;
|
||||
|
||||
std::string tmp;
|
||||
|
||||
bool res = MakeJSON(vm, -1, max_depth - 1, tmp);
|
||||
bool res = ScriptAdminMakeJSON(tmp, vm, -1, depth + 1);
|
||||
sq_pop(vm, 2);
|
||||
if (!res) {
|
||||
sq_pop(vm, 1);
|
||||
return false;
|
||||
}
|
||||
data += tmp;
|
||||
|
||||
json.push_back(tmp);
|
||||
}
|
||||
sq_pop(vm, 1);
|
||||
data += " ]";
|
||||
return true;
|
||||
}
|
||||
|
||||
case OT_TABLE: {
|
||||
data = "{ ";
|
||||
json = nlohmann::json::object();
|
||||
|
||||
bool first = true;
|
||||
sq_pushnull(vm);
|
||||
while (SQ_SUCCEEDED(sq_next(vm, index - 1))) {
|
||||
if (!first) data += ", ";
|
||||
if (first) first = false;
|
||||
/* Squirrel ensure the key is a string. */
|
||||
assert(sq_gettype(vm, -2) == OT_STRING);
|
||||
const SQChar *buf;
|
||||
sq_getstring(vm, -2, &buf);
|
||||
std::string key = std::string(buf);
|
||||
|
||||
std::string key;
|
||||
std::string value;
|
||||
|
||||
/* Store the key + value */
|
||||
bool res = MakeJSON(vm, -2, max_depth - 1, key) && MakeJSON(vm, -1, max_depth - 1, value);
|
||||
nlohmann::json value;
|
||||
bool res = ScriptAdminMakeJSON(value, vm, -1, depth + 1);
|
||||
sq_pop(vm, 2);
|
||||
if (!res) {
|
||||
sq_pop(vm, 1);
|
||||
return false;
|
||||
}
|
||||
data += key + ": " + value;
|
||||
|
||||
json[key] = value;
|
||||
}
|
||||
sq_pop(vm, 1);
|
||||
data += " }";
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -101,17 +102,12 @@
|
||||
SQBool res;
|
||||
sq_getbool(vm, index, &res);
|
||||
|
||||
if (res) {
|
||||
data = "true";
|
||||
return true;
|
||||
}
|
||||
|
||||
data = "false";
|
||||
json = res ? true : false;
|
||||
return true;
|
||||
}
|
||||
|
||||
case OT_NULL: {
|
||||
data = "null";
|
||||
json = nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -129,19 +125,13 @@
|
||||
return sq_throwerror(vm, "ScriptAdmin::Send requires a table as first parameter. No data sent.");
|
||||
}
|
||||
|
||||
std::string json;
|
||||
if (!ScriptAdmin::MakeJSON(vm, -1, SQUIRREL_MAX_DEPTH, json)) {
|
||||
nlohmann::json json;
|
||||
if (!ScriptAdminMakeJSON(json, vm, -1)) {
|
||||
sq_pushinteger(vm, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (json.length() > NETWORK_GAMESCRIPT_JSON_LENGTH) {
|
||||
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);
|
||||
NetworkAdminGameScript(json.dump());
|
||||
|
||||
sq_pushinteger(vm, 1);
|
||||
return 1;
|
||||
|
@@ -36,16 +36,6 @@ public:
|
||||
*/
|
||||
static bool Send(void *table);
|
||||
#endif /* DOXYGEN_API */
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Convert a Squirrel structure into a JSON string.
|
||||
* @param vm The VM to operate on.
|
||||
* @param index The index we are currently working for.
|
||||
* @param max_depth The maximal depth to follow the squirrel struct.
|
||||
* @param data The resulting json string.
|
||||
*/
|
||||
static bool MakeJSON(HSQUIRRELVM vm, SQInteger index, int max_depth, std::string &data);
|
||||
};
|
||||
|
||||
#endif /* SCRIPT_ADMIN_HPP */
|
||||
|
@@ -126,193 +126,89 @@ ScriptEventAdminPort::ScriptEventAdminPort(const std::string &json) :
|
||||
json(json)
|
||||
{
|
||||
}
|
||||
/**
|
||||
* Convert a JSON part fo Squirrel.
|
||||
* @param vm The VM used.
|
||||
* @param json The JSON part to convert to Squirrel.
|
||||
*/
|
||||
static bool ScriptEventAdminPortReadValue(HSQUIRRELVM vm, nlohmann::json &json)
|
||||
{
|
||||
switch (json.type()) {
|
||||
case nlohmann::json::value_t::null:
|
||||
sq_pushnull(vm);
|
||||
break;
|
||||
|
||||
#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; }
|
||||
case nlohmann::json::value_t::boolean:
|
||||
sq_pushbool(vm, json.get<bool>() ? 1 : 0);
|
||||
break;
|
||||
|
||||
case nlohmann::json::value_t::string: {
|
||||
auto value = json.get<std::string>();
|
||||
sq_pushstring(vm, value.data(), value.size());
|
||||
break;
|
||||
}
|
||||
|
||||
case nlohmann::json::value_t::number_integer:
|
||||
case nlohmann::json::value_t::number_unsigned:
|
||||
sq_pushinteger(vm, json.get<int64_t>());
|
||||
break;
|
||||
|
||||
case nlohmann::json::value_t::object:
|
||||
sq_newtable(vm);
|
||||
|
||||
for (auto &[key, value] : json.items()) {
|
||||
sq_pushstring(vm, key.data(), key.size());
|
||||
|
||||
if (!ScriptEventAdminPortReadValue(vm, value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
sq_rawset(vm, -3);
|
||||
}
|
||||
break;
|
||||
|
||||
case nlohmann::json::value_t::array:
|
||||
sq_newarray(vm, 0);
|
||||
|
||||
for (auto &value : json) {
|
||||
if (!ScriptEventAdminPortReadValue(vm, value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
sq_arrayappend(vm, -2);
|
||||
}
|
||||
break;
|
||||
|
||||
/* These types are not supported by Squirrel. */
|
||||
case nlohmann::json::value_t::number_float:
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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 (!ScriptEventAdminPortReadValue(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);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char *ScriptEventAdminPort::ReadString(HSQUIRRELVM vm, const char *p)
|
||||
{
|
||||
const char *value = p;
|
||||
|
||||
bool escape = false;
|
||||
for (;;) {
|
||||
if (*p == '\\') {
|
||||
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;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case '{': {
|
||||
/* Table */
|
||||
p = this->ReadTable(vm, p);
|
||||
if (p == nullptr) return nullptr;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case '[': {
|
||||
/* Array */
|
||||
sq_newarray(vm, 0);
|
||||
|
||||
/* Empty array? */
|
||||
const char *p2 = p + 1;
|
||||
SKIP_EMPTY(p2);
|
||||
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);
|
||||
|
||||
SKIP_EMPTY(p);
|
||||
if (*p == ',') continue;
|
||||
if (*p == ']') break;
|
||||
RETURN_ERROR(1);
|
||||
}
|
||||
|
||||
p++;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
default:
|
||||
RETURN_ERROR(0);
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
#undef SKIP_EMPTY
|
||||
#undef RETURN_ERROR
|
||||
|
@@ -911,27 +911,6 @@ public:
|
||||
|
||||
private:
|
||||
std::string json; ///< The JSON string.
|
||||
|
||||
/**
|
||||
* Read a table from a JSON string.
|
||||
* @param vm The VM used.
|
||||
* @param p The (part of the) JSON string reading.
|
||||
*/
|
||||
const char *ReadTable(HSQUIRRELVM vm, const char *p);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@@ -69,7 +69,7 @@
|
||||
EnforceDeityOrCompanyModeValid(false);
|
||||
if ((::RailType)rail_type >= RAILTYPE_END) return false;
|
||||
|
||||
return ScriptCompanyMode::IsDeity() || ::HasRailtypeAvail(ScriptObject::GetCompany(), (::RailType)rail_type);
|
||||
return ScriptCompanyMode::IsDeity() || ::HasRailTypeAvail(ScriptObject::GetCompany(), (::RailType)rail_type);
|
||||
}
|
||||
|
||||
/* static */ ScriptRail::RailType ScriptRail::GetCurrentRailType()
|
||||
|
@@ -18,6 +18,6 @@ ScriptRailTypeList::ScriptRailTypeList()
|
||||
{
|
||||
EnforceDeityOrCompanyModeValid_Void();
|
||||
for (RailType rt = RAILTYPE_BEGIN; rt != RAILTYPE_END; rt++) {
|
||||
if (ScriptCompanyMode::IsDeity() || ::HasRailtypeAvail(ScriptObject::GetCompany(), rt)) this->AddItem(rt);
|
||||
if (ScriptCompanyMode::IsDeity() || ::HasRailTypeAvail(ScriptObject::GetCompany(), rt)) this->AddItem(rt);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user