changeset 18775:26ffd3067724 draft

(svn r23623) -Add: allow bi-directional communication with the AdminPort and GameScript
author truebrain <truebrain@openttd.org>
date Mon, 19 Dec 2011 21:00:32 +0000
parents 1fa19d70aea9
children 3fdd053baa35
files projects/openttd_vs100.vcxproj projects/openttd_vs100.vcxproj.filters projects/openttd_vs80.vcproj projects/openttd_vs90.vcproj source.list src/game/game_instance.cpp src/network/core/config.h src/network/core/tcp_admin.cpp src/network/core/tcp_admin.h src/network/network_admin.cpp src/network/network_admin.h src/script/api/ai/ai_event.hpp.sq src/script/api/game/game_admin.hpp.sq src/script/api/game/game_event.hpp.sq src/script/api/game/game_event_types.hpp.sq src/script/api/script_admin.cpp src/script/api/script_admin.hpp src/script/api/script_event.hpp src/script/api/script_event_types.cpp src/script/api/script_event_types.hpp src/script/api/squirrel_export.awk src/script/api/template/template_admin.hpp.sq src/script/api/template/template_event_types.hpp.sq src/script/script_instance.cpp src/script/script_instance.hpp src/script/squirrel_class.hpp src/script/squirrel_helper.hpp
diffstat 27 files changed, 656 insertions(+), 7 deletions(-) [+]
line wrap: on
line diff
--- a/projects/openttd_vs100.vcxproj
+++ b/projects/openttd_vs100.vcxproj
@@ -926,6 +926,7 @@
     <ClCompile Include="..\src\game\game_scanner.cpp" />
     <ClInclude Include="..\src\game\game_scanner.hpp" />
     <ClInclude Include="..\src\script\api\script_accounting.hpp" />
+    <ClInclude Include="..\src\script\api\script_admin.hpp" />
     <ClInclude Include="..\src\script\api\script_airport.hpp" />
     <ClInclude Include="..\src\script\api\script_base.hpp" />
     <ClInclude Include="..\src\script\api\script_basestation.hpp" />
@@ -980,6 +981,7 @@
     <ClInclude Include="..\src\script\api\script_waypoint.hpp" />
     <ClInclude Include="..\src\script\api\script_waypointlist.hpp" />
     <ClCompile Include="..\src\script\api\script_accounting.cpp" />
+    <ClCompile Include="..\src\script\api\script_admin.cpp" />
     <ClCompile Include="..\src\script\api\script_airport.cpp" />
     <ClCompile Include="..\src\script\api\script_base.cpp" />
     <ClCompile Include="..\src\script\api\script_basestation.cpp" />
--- a/projects/openttd_vs100.vcxproj.filters
+++ b/projects/openttd_vs100.vcxproj.filters
@@ -2007,6 +2007,9 @@
     <ClInclude Include="..\src\script\api\script_accounting.hpp">
       <Filter>Script API</Filter>
     </ClInclude>
+    <ClInclude Include="..\src\script\api\script_admin.hpp">
+      <Filter>Script API</Filter>
+    </ClInclude>
     <ClInclude Include="..\src\script\api\script_airport.hpp">
       <Filter>Script API</Filter>
     </ClInclude>
@@ -2169,6 +2172,9 @@
     <ClCompile Include="..\src\script\api\script_accounting.cpp">
       <Filter>Script API Implementation</Filter>
     </ClCompile>
+    <ClCompile Include="..\src\script\api\script_admin.cpp">
+      <Filter>Script API Implementation</Filter>
+    </ClCompile>
     <ClCompile Include="..\src\script\api\script_airport.cpp">
       <Filter>Script API Implementation</Filter>
     </ClCompile>
--- a/projects/openttd_vs80.vcproj
+++ b/projects/openttd_vs80.vcproj
@@ -3039,6 +3039,10 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\script\api\script_admin.hpp"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\script\api\script_airport.hpp"
 				>
 			</File>
@@ -3259,6 +3263,10 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\script\api\script_admin.cpp"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\script\api\script_airport.cpp"
 				>
 			</File>
--- a/projects/openttd_vs90.vcproj
+++ b/projects/openttd_vs90.vcproj
@@ -3036,6 +3036,10 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\script\api\script_admin.hpp"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\script\api\script_airport.hpp"
 				>
 			</File>
@@ -3256,6 +3260,10 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\script\api\script_admin.cpp"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\script\api\script_airport.cpp"
 				>
 			</File>
--- a/source.list
+++ b/source.list
@@ -704,6 +704,7 @@
 
 # Script API
 script/api/script_accounting.hpp
+script/api/script_admin.hpp
 script/api/script_airport.hpp
 script/api/script_base.hpp
 script/api/script_basestation.hpp
@@ -760,6 +761,7 @@
 
 # Script API Implementation
 script/api/script_accounting.cpp
+script/api/script_admin.cpp
 script/api/script_airport.cpp
 script/api/script_base.cpp
 script/api/script_basestation.cpp
--- a/src/game/game_instance.cpp
+++ b/src/game/game_instance.cpp
@@ -24,6 +24,7 @@
 /* Convert all Game related classes to Squirrel data.
  * Note: this line is a marker in squirrel_export.sh. Do not change! */
 #include "../script/api/game/game_accounting.hpp.sq"
+#include "../script/api/game/game_admin.hpp.sq"
 #include "../script/api/game/game_airport.hpp.sq"
 #include "../script/api/game/game_base.hpp.sq"
 #include "../script/api/game/game_basestation.hpp.sq"
@@ -92,6 +93,7 @@
 /* Register all classes */
 	SQGSList_Register(this->engine);
 	SQGSAccounting_Register(this->engine);
+	SQGSAdmin_Register(this->engine);
 	SQGSAirport_Register(this->engine);
 	SQGSBase_Register(this->engine);
 	SQGSBaseStation_Register(this->engine);
@@ -110,6 +112,7 @@
 	SQGSEngineList_Register(this->engine);
 	SQGSError_Register(this->engine);
 	SQGSEvent_Register(this->engine);
+	SQGSEventAdminPort_Register(this->engine);
 	SQGSEventCompanyBankrupt_Register(this->engine);
 	SQGSEventCompanyInTrouble_Register(this->engine);
 	SQGSEventCompanyMerger_Register(this->engine);
--- a/src/network/core/config.h
+++ b/src/network/core/config.h
@@ -48,6 +48,7 @@
 static const uint NETWORK_CLIENTS_LENGTH          =  200; ///< The maximum length for the list of clients that controls a company, 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_GAMESCRIPT_JSON_LENGTH  = 1450; ///< The maximum length of a 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_GRF_NAME_LENGTH         =   80; ///< Maximum length of the name of a GRF
--- a/src/network/core/tcp_admin.cpp
+++ b/src/network/core/tcp_admin.cpp
@@ -60,6 +60,7 @@
 		case ADMIN_PACKET_ADMIN_POLL:             return this->Receive_ADMIN_POLL(p);
 		case ADMIN_PACKET_ADMIN_CHAT:             return this->Receive_ADMIN_CHAT(p);
 		case ADMIN_PACKET_ADMIN_RCON:             return this->Receive_ADMIN_RCON(p);
+		case ADMIN_PACKET_ADMIN_GAMESCRIPT:       return this->Receive_ADMIN_GAMESCRIPT(p);
 
 		case ADMIN_PACKET_SERVER_FULL:            return this->Receive_SERVER_FULL(p);
 		case ADMIN_PACKET_SERVER_BANNED:          return this->Receive_SERVER_BANNED(p);
@@ -134,6 +135,7 @@
 NetworkRecvStatus NetworkAdminSocketHandler::Receive_ADMIN_POLL(Packet *p) { return this->ReceiveInvalidPacket(ADMIN_PACKET_ADMIN_POLL); }
 NetworkRecvStatus NetworkAdminSocketHandler::Receive_ADMIN_CHAT(Packet *p) { return this->ReceiveInvalidPacket(ADMIN_PACKET_ADMIN_CHAT); }
 NetworkRecvStatus NetworkAdminSocketHandler::Receive_ADMIN_RCON(Packet *p) { return this->ReceiveInvalidPacket(ADMIN_PACKET_ADMIN_RCON); }
+NetworkRecvStatus NetworkAdminSocketHandler::Receive_ADMIN_GAMESCRIPT(Packet *p) { return this->ReceiveInvalidPacket(ADMIN_PACKET_ADMIN_GAMESCRIPT); }
 
 NetworkRecvStatus NetworkAdminSocketHandler::Receive_SERVER_FULL(Packet *p) { return this->ReceiveInvalidPacket(ADMIN_PACKET_SERVER_FULL); }
 NetworkRecvStatus NetworkAdminSocketHandler::Receive_SERVER_BANNED(Packet *p) { return this->ReceiveInvalidPacket(ADMIN_PACKET_SERVER_BANNED); }
--- a/src/network/core/tcp_admin.h
+++ b/src/network/core/tcp_admin.h
@@ -32,6 +32,7 @@
 	ADMIN_PACKET_ADMIN_POLL,             ///< The admin explicitly polls for a piece of information.
 	ADMIN_PACKET_ADMIN_CHAT,             ///< The admin sends a chat message to be distributed.
 	ADMIN_PACKET_ADMIN_RCON,             ///< The admin sends a remote console command.
+	ADMIN_PACKET_ADMIN_GAMESCRIPT,       ///< The admin sends a JSON string for the GameScript.
 
 	ADMIN_PACKET_SERVER_FULL = 100,      ///< The server tells the admin it cannot accept the admin.
 	ADMIN_PACKET_SERVER_BANNED,          ///< The server tells the admin it is banned.
@@ -58,6 +59,7 @@
 	ADMIN_PACKET_SERVER_CONSOLE,         ///< The server gives the admin the data that got printed to its console.
 	ADMIN_PACKET_SERVER_CMD_NAMES,       ///< The server sends out the names of the DoCommands to the admins.
 	ADMIN_PACKET_SERVER_CMD_LOGGING,     ///< The server gives the admin copies of incoming command packets.
+	ADMIN_PACKET_SERVER_GAMESCRIPT,      ///< The server gives the admin information from the GameScript in JSON.
 
 	INVALID_ADMIN_PACKET = 0xFF,         ///< An invalid marker for admin packets.
 };
@@ -80,6 +82,7 @@
 	ADMIN_UPDATE_CONSOLE,         ///< The admin would like to have console messages.
 	ADMIN_UPDATE_CMD_NAMES,       ///< The admin would like a list of all DoCommand names.
 	ADMIN_UPDATE_CMD_LOGGING,     ///< The admin would like to have DoCommand information.
+	ADMIN_UPDATE_GAMESCRIPT,      ///< The admin would like to have gamescript messages.
 	ADMIN_UPDATE_END,             ///< Must ALWAYS be on the end of this list!! (period)
 };
 
@@ -170,6 +173,14 @@
 	virtual NetworkRecvStatus Receive_ADMIN_RCON(Packet *p);
 
 	/**
+	 * Send a JSON string to the current active GameScript.
+	 * json  JSON string for the GameScript.
+	 * @param p The packet that was just received.
+	 * @return The state the network should have.
+	 */
+	virtual NetworkRecvStatus Receive_ADMIN_GAMESCRIPT(Packet *p);
+
+	/**
 	 * The server is full (connection gets closed).
 	 * @param p The packet that was just received.
 	 * @return The state the network should have.
--- a/src/network/network_admin.cpp
+++ b/src/network/network_admin.cpp
@@ -23,6 +23,7 @@
 #include "../core/pool_func.hpp"
 #include "../map_func.h"
 #include "../rev.h"
+#include "../game/game.hpp"
 
 
 /* This file handles all the admin network commands. */
@@ -52,6 +53,7 @@
 	                       ADMIN_FREQUENCY_AUTOMATIC,                                                                                                      ///< ADMIN_UPDATE_CONSOLE
 	ADMIN_FREQUENCY_POLL,                                                                                                                                  ///< ADMIN_UPDATE_CMD_NAMES
 	                       ADMIN_FREQUENCY_AUTOMATIC,                                                                                                      ///< ADMIN_UPDATE_CMD_LOGGING
+	                       ADMIN_FREQUENCY_AUTOMATIC,                                                                                                      ///< ADMIN_UPDATE_GAMESCRIPT
 };
 /** Sanity check. */
 assert_compile(lengthof(_admin_update_type_frequencies) == ADMIN_UPDATE_END);
@@ -510,6 +512,20 @@
 	return NETWORK_RECV_STATUS_OKAY;
 }
 
+NetworkRecvStatus ServerNetworkAdminSocketHandler::Receive_ADMIN_GAMESCRIPT(Packet *p)
+{
+	if (this->status == ADMIN_STATUS_INACTIVE) return this->SendError(NETWORK_ERROR_NOT_EXPECTED);
+
+	char json[NETWORK_GAMESCRIPT_JSON_LENGTH];
+
+	p->Recv_string(json, sizeof(json));
+
+	DEBUG(net, 2, "[admin] GameScript JSON from '%s' (%s): '%s'", this->admin_name, this->admin_version, json);
+
+	Game::NewEvent(new ScriptEventAdminPort(json));
+	return NETWORK_RECV_STATUS_OKAY;
+}
+
 /**
  * Send console output of other clients.
  * @param origin The origin of the string.
@@ -532,6 +548,25 @@
 	return NETWORK_RECV_STATUS_OKAY;
 }
 
+/**
+ * Send GameScript JSON output.
+ * @param json The JSON string.
+ */
+NetworkRecvStatus ServerNetworkAdminSocketHandler::SendGameScript(const char *json)
+{
+	/* At the moment we cannot transmit anything larger than MTU. So the string
+	 *  has to be no longer than the length of the json + '\0' + 3 bytes of the
+	 *  packet header. */
+	if (strlen(json) + 1 + 3 >= SEND_MTU) return NETWORK_RECV_STATUS_OKAY;
+
+	Packet *p = new Packet(ADMIN_PACKET_SERVER_GAMESCRIPT);
+
+	p->Send_string(json);
+	this->SendPacket(p);
+
+	return NETWORK_RECV_STATUS_OKAY;
+}
+
 /** Send the names of the commands. */
 NetworkRecvStatus ServerNetworkAdminSocketHandler::SendCmdNames()
 {
@@ -896,6 +931,20 @@
 }
 
 /**
+ * Send GameScript JSON to the admin network (if they did opt in for the respective update).
+ * @param json The JSON data as received from the GameScript.
+ */
+void NetworkAdminGameScript(const char *json)
+{
+	ServerNetworkAdminSocketHandler *as;
+	FOR_ALL_ACTIVE_ADMIN_SOCKETS(as) {
+		if (as->update_frequency[ADMIN_UPDATE_GAMESCRIPT] & ADMIN_FREQUENCY_AUTOMATIC) {
+			as->SendGameScript(json);
+		}
+	}
+}
+
+/**
  * Distribute CommandPacket details over the admin network for logging purposes.
  * @param owner The owner of the CommandPacket (who sent us the CommandPacket).
  * @param cp    The CommandPacket to be distributed.
--- a/src/network/network_admin.h
+++ b/src/network/network_admin.h
@@ -34,6 +34,7 @@
 	virtual NetworkRecvStatus Receive_ADMIN_POLL(Packet *p);
 	virtual NetworkRecvStatus Receive_ADMIN_CHAT(Packet *p);
 	virtual NetworkRecvStatus Receive_ADMIN_RCON(Packet *p);
+	virtual NetworkRecvStatus Receive_ADMIN_GAMESCRIPT(Packet *p);
 
 	NetworkRecvStatus SendProtocol();
 public:
@@ -65,6 +66,7 @@
 	NetworkRecvStatus SendChat(NetworkAction action, DestType desttype, ClientID client_id, const char *msg, int64 data);
 	NetworkRecvStatus SendRcon(uint16 colour, const char *command);
 	NetworkRecvStatus SendConsole(const char *origin, const char *command);
+	NetworkRecvStatus SendGameScript(const char *json);
 	NetworkRecvStatus SendCmdNames();
 	NetworkRecvStatus SendCmdLogging(ClientID client_id, const CommandPacket *cp);
 
@@ -116,6 +118,7 @@
 void NetworkAdminUpdate(AdminUpdateFrequency freq);
 void NetworkServerSendAdminRcon(AdminIndex admin_index, TextColour colour_code, const char *string);
 void NetworkAdminConsole(const char *origin, const char *string);
+void NetworkAdminGameScript(const char *json);
 void NetworkAdminCmdLogging(const NetworkClientSocket *owner, const CommandPacket *cp);
 
 #endif /* ENABLE_NETWORK */
--- a/src/script/api/ai/ai_event.hpp.sq
+++ b/src/script/api/ai/ai_event.hpp.sq
@@ -45,6 +45,7 @@
 	SQAIEvent.DefSQConst(engine, ScriptEvent::ET_DISASTER_ZEPPELINER_CLEARED, "ET_DISASTER_ZEPPELINER_CLEARED");
 	SQAIEvent.DefSQConst(engine, ScriptEvent::ET_TOWN_FOUNDED,                "ET_TOWN_FOUNDED");
 	SQAIEvent.DefSQConst(engine, ScriptEvent::ET_AIRCRAFT_DEST_TOO_FAR,       "ET_AIRCRAFT_DEST_TOO_FAR");
+	SQAIEvent.DefSQConst(engine, ScriptEvent::ET_ADMIN_PORT,                  "ET_ADMIN_PORT");
 
 	SQAIEvent.DefSQMethod(engine, &ScriptEvent::GetEventType, "GetEventType", 1, "x");
 
new file mode 100644
--- /dev/null
+++ b/src/script/api/game/game_admin.hpp.sq
@@ -0,0 +1,27 @@
+/* $Id$ */
+
+/*
+ * This file is part of OpenTTD.
+ * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
+ * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */
+
+#include "../script_admin.hpp"
+#include "../template/template_admin.hpp.sq"
+
+
+template <> const char *GetClassName<ScriptAdmin, ST_GS>() { return "GSAdmin"; }
+
+void SQGSAdmin_Register(Squirrel *engine)
+{
+	DefSQClass<ScriptAdmin, ST_GS> SQGSAdmin("GSAdmin");
+	SQGSAdmin.PreRegister(engine);
+	SQGSAdmin.AddConstructor<void (ScriptAdmin::*)(), 1>(engine, "x");
+
+	SQGSAdmin.DefSQAdvancedStaticMethod(engine, &ScriptAdmin::Send, "Send");
+
+	SQGSAdmin.PostRegister(engine);
+}
--- a/src/script/api/game/game_event.hpp.sq
+++ b/src/script/api/game/game_event.hpp.sq
@@ -45,6 +45,7 @@
 	SQGSEvent.DefSQConst(engine, ScriptEvent::ET_DISASTER_ZEPPELINER_CLEARED, "ET_DISASTER_ZEPPELINER_CLEARED");
 	SQGSEvent.DefSQConst(engine, ScriptEvent::ET_TOWN_FOUNDED,                "ET_TOWN_FOUNDED");
 	SQGSEvent.DefSQConst(engine, ScriptEvent::ET_AIRCRAFT_DEST_TOO_FAR,       "ET_AIRCRAFT_DEST_TOO_FAR");
+	SQGSEvent.DefSQConst(engine, ScriptEvent::ET_ADMIN_PORT,                  "ET_ADMIN_PORT");
 
 	SQGSEvent.DefSQMethod(engine, &ScriptEvent::GetEventType, "GetEventType", 1, "x");
 
--- a/src/script/api/game/game_event_types.hpp.sq
+++ b/src/script/api/game/game_event_types.hpp.sq
@@ -217,3 +217,18 @@
 
 	SQGSEventTownFounded.PostRegister(engine);
 }
+
+
+template <> const char *GetClassName<ScriptEventAdminPort, ST_GS>() { return "GSEventAdminPort"; }
+
+void SQGSEventAdminPort_Register(Squirrel *engine)
+{
+	DefSQClass<ScriptEventAdminPort, ST_GS> SQGSEventAdminPort("GSEventAdminPort");
+	SQGSEventAdminPort.PreRegister(engine, "GSEvent");
+
+	SQGSEventAdminPort.DefSQStaticMethod(engine, &ScriptEventAdminPort::Convert, "Convert", 2, ".x");
+
+	SQGSEventAdminPort.DefSQAdvancedMethod(engine, &ScriptEventAdminPort::GetObject, "GetObject");
+
+	SQGSEventAdminPort.PostRegister(engine);
+}
new file mode 100644
--- /dev/null
+++ b/src/script/api/script_admin.cpp
@@ -0,0 +1,151 @@
+/* $Id$ */
+
+/*
+ * This file is part of OpenTTD.
+ * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
+ * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** @file script_admin.cpp Implementation of ScriptAdmin. */
+
+#include "../../stdafx.h"
+#include "script_admin.hpp"
+#include "script_log.hpp"
+#include "../../network/network_admin.h"
+#include "../script_instance.hpp"
+#include "../../game/game.hpp"
+
+/* static */ bool ScriptAdmin::MakeJSON(HSQUIRRELVM vm, SQInteger index, int max_depth, std::string &data)
+{
+	if (max_depth == 0) {
+		ScriptLog::Error("Send parameters can only be nested to 25 deep. No data sent."); // SQUIRREL_MAX_DEPTH = 25
+		return false;
+	}
+
+	switch (sq_gettype(vm, index)) {
+		case OT_INTEGER: {
+			SQInteger res;
+			sq_getinteger(vm, index, &res);
+
+			char buf[10];
+			snprintf(buf, sizeof(buf), "%d", (int32)res);
+			data = buf;
+			return true;
+		}
+
+		case OT_STRING: {
+			const SQChar *res;
+			sq_getstring(vm, index, &res);
+
+			/* @bug if a string longer than 512 characters is given to SQ2OTTD, the
+			 *  internal buffer overflows. */
+			const char *buf = SQ2OTTD(res);
+			size_t len = strlen(buf) + 1;
+			if (len >= 255) {
+				ScriptLog::Error("Maximum string length is 254 chars. No data sent.");
+				return false;
+			}
+
+			data = std::string("\"") + buf + "\"";
+			return true;
+		}
+
+		case OT_ARRAY: {
+			data = "[ ";
+
+			bool first = true;
+			sq_pushnull(vm);
+			while (SQ_SUCCEEDED(sq_next(vm, index - 1))) {
+				if (!first) data += ", ";
+				if (first) first = false;
+
+				std::string tmp;
+
+				bool res = MakeJSON(vm, -1, max_depth - 1, tmp);
+				sq_pop(vm, 2);
+				if (!res) {
+					sq_pop(vm, 1);
+					return false;
+				}
+				data += tmp;
+			}
+			sq_pop(vm, 1);
+			data += " ]";
+			return true;
+		}
+
+		case OT_TABLE: {
+			data = "{ ";
+
+			bool first = true;
+			sq_pushnull(vm);
+			while (SQ_SUCCEEDED(sq_next(vm, index - 1))) {
+				if (!first) data += ", ";
+				if (first) first = false;
+
+				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);
+				sq_pop(vm, 2);
+				if (!res) {
+					sq_pop(vm, 1);
+					return false;
+				}
+				data += key + ": " + value;
+			}
+			sq_pop(vm, 1);
+			data += " }";
+			return true;
+		}
+
+		case OT_BOOL: {
+			SQBool res;
+			sq_getbool(vm, index, &res);
+
+			if (res) {
+				data = "true";
+				return true;
+			}
+
+			data = "false";
+			return true;
+		}
+
+		case OT_NULL: {
+			data = "null";
+			return true;
+		}
+
+		default:
+			ScriptLog::Error("You tried to send an unsupported type. No data sent.");
+			return false;
+	}
+}
+
+/* static */ SQInteger ScriptAdmin::Send(HSQUIRRELVM vm)
+{
+	if (sq_gettop(vm) - 1 != 1) return sq_throwerror(vm, _SC("wrong number of parameters"));
+
+	if (sq_gettype(vm, 2) != OT_TABLE) {
+		return sq_throwerror(vm, _SC("ScriptAdmin::Send requires a table as first parameter. No data sent."));
+	}
+
+	std::string json;
+	ScriptAdmin::MakeJSON(vm, -1, SQUIRREL_MAX_DEPTH, json);
+
+#ifdef ENABLE_NETWORK
+	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.c_str());
+#endif /* ENABLE_NETWORK */
+
+	sq_pushinteger(vm, 1);
+	return 1;
+}
new file mode 100644
--- /dev/null
+++ b/src/script/api/script_admin.hpp
@@ -0,0 +1,53 @@
+/* $Id$ */
+
+/*
+ * This file is part of OpenTTD.
+ * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
+ * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** @file script_admin.hpp Everything to communicate with the AdminPort. */
+
+#ifndef SCRIPT_ADMIN_HPP
+#define SCRIPT_ADMIN_HPP
+
+#include <string>
+#include "script_object.hpp"
+
+/**
+ * Class that handles communication with the AdminPort.
+ * @api game
+ */
+class ScriptAdmin : public ScriptObject {
+public:
+#ifndef DOXYGEN_API
+	/**
+	 * Internal representation of the Send function.
+	 */
+	static SQInteger Send(HSQUIRRELVM vm);
+#else
+	/**
+	 * Send information to the AdminPort. The information can be anything
+	 *  as long as it isn't a class or instance thereof.
+	 * @param table The information to send, in a table. For example: { param = "param" }.
+	 * @return True if and only if the data was successfully converted to JSON
+	 *  and send to the AdminPort.
+	 * @note If the resulting JSON of your table is larger than 1450 bytes,
+	 *   nothing will be sent (and false will be returned).
+	 */
+	static bool Send(table);
+#endif /* DOXYGEN_API */
+
+private:
+	/**
+	 * 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 */
--- a/src/script/api/script_event.hpp
+++ b/src/script/api/script_event.hpp
@@ -50,6 +50,7 @@
 		ET_DISASTER_ZEPPELINER_CLEARED,
 		ET_TOWN_FOUNDED,
 		ET_AIRCRAFT_DEST_TOO_FAR,
+		ET_ADMIN_PORT,
 	};
 
 	/**
--- a/src/script/api/script_event_types.cpp
+++ b/src/script/api/script_event_types.cpp
@@ -12,6 +12,7 @@
 #include "../../stdafx.h"
 #include "script_event_types.hpp"
 #include "script_vehicle.hpp"
+#include "script_log.hpp"
 #include "../../command_type.h"
 #include "../../strings_func.h"
 #include "../../settings_type.h"
@@ -119,3 +120,185 @@
 {
 	return ScriptObject::DoCommand(0, this->owner, 0, CMD_BUY_COMPANY);
 }
+
+#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 NULL; }
+
+SQInteger ScriptEventAdminPort::GetObject(HSQUIRRELVM vm)
+{
+	char *p = this->json;
+
+	if (this->ReadTable(vm, p) == NULL) {
+		sq_pushnull(vm);
+		return 1;
+	}
+
+	return 1;
+}
+
+char *ScriptEventAdminPort::ReadString(HSQUIRRELVM vm, char *p)
+{
+	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++;
+	}
+
+	*p = '\0';
+	sq_pushstring(vm, OTTD2SQ(value), -1);
+	*p++ = '"';
+
+	return p;
+}
+
+char *ScriptEventAdminPort::ReadTable(HSQUIRRELVM vm, 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 == NULL) {
+			sq_pop(vm, 1);
+			return NULL;
+		}
+
+		SKIP_EMPTY(p);
+		if (*p++ != ':') RETURN_ERROR(2);
+
+		p = this->ReadValue(vm, p);
+		if (p == NULL) {
+			sq_pop(vm, 2);
+			return NULL;
+		}
+
+		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;
+}
+
+char *ScriptEventAdminPort::ReadValue(HSQUIRRELVM vm, char *p)
+{
+	SKIP_EMPTY(p);
+
+	if (strncmp(p, "false", 5) == 0) {
+		sq_pushinteger(vm, 0);
+		return p + 5;
+	}
+	if (strncmp(p, "true", 4) == 0) {
+		sq_pushinteger(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 == NULL) return NULL;
+
+			break;
+		}
+
+		case '{': {
+			/* Table */
+			p = this->ReadTable(vm, p);
+			if (p == NULL) return NULL;
+
+			break;
+		}
+
+		case '[': {
+			/* Array */
+			sq_newarray(vm, 0);
+
+			while (*p++ != ']') {
+				p = this->ReadValue(vm, p);
+				if (p == NULL) {
+					sq_pop(vm, 1);
+					return NULL;
+				}
+				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
--- a/src/script/api/script_event_types.hpp
+++ b/src/script/api/script_event_types.hpp
@@ -829,4 +829,60 @@
 	VehicleID vehicle_id; ///< The vehicle aircraft whose destination is too far away.
 };
 
+/**
+ * Event Admin Port, indicating the admin port is sending you information.
+ * @api game
+ */
+class ScriptEventAdminPort : public ScriptEvent {
+public:
+	/**
+	 * @param json The JSON string which got sent.
+	 */
+	ScriptEventAdminPort(const char *json) :
+		ScriptEvent(ET_ADMIN_PORT),
+		json(strdup(json))
+	{}
+
+	~ScriptEventAdminPort()
+	{
+		free(this->json);
+	}
+
+	/**
+	 * Convert an ScriptEvent to the real instance.
+	 * @param instance The instance to convert.
+	 * @return The converted instance.
+	 */
+	static ScriptEventAdminPort *Convert(ScriptEvent *instance) { return (ScriptEventAdminPort *)instance; }
+
+	/**
+	 * Get the information that was sent to you back as Squirrel object.
+	 */
+	SQInteger GetObject(HSQUIRRELVM vm);
+
+private:
+	char *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.
+	 */
+	char *ReadTable(HSQUIRRELVM vm, char *p);
+
+	/**
+	 * Read a value from a JSON string.
+	 * @param vm The VM used.
+	 * @param p The (part of the) JSON string reading.
+	 */
+	char *ReadValue(HSQUIRRELVM vm, char *p);
+
+	/**
+	 * Read a string from a JSON string.
+	 * @param vm The VM used.
+	 * @param p The (part of the) JSON string reading.
+	 */
+	char *ReadString(HSQUIRRELVM vm, char *p);
+};
+
 #endif /* SCRIPT_EVENT_TYPES_HPP */
--- a/src/script/api/squirrel_export.awk
+++ b/src/script/api/squirrel_export.awk
@@ -385,7 +385,11 @@
 		if (mlen <= length(static_methods[i, 0])) mlen = length(static_methods[i, 0])
 	}
 	for (i = 1; i <= static_method_size; i++) {
-		print "	SQ" api_cls ".DefSQStaticMethod(engine, &" cls "::" static_methods[i, 0] ", " substr(spaces, 1, mlen - length(static_methods[i, 0])) "\""  static_methods[i, 0] "\", " substr(spaces, 1, mlen - length(static_methods[i, 0])) "" static_methods[i, 1] ", \"" static_methods[i, 2] "\");"
+		if (static_methods[i, 2] == "v") {
+			print "	SQ" api_cls ".DefSQAdvancedStaticMethod(engine, &" cls "::" static_methods[i, 0] ", " substr(spaces, 1, mlen - length(static_methods[i, 0]) - 8) "\""  static_methods[i, 0] "\");"
+		} else {
+			print "	SQ" api_cls ".DefSQStaticMethod(engine, &" cls "::" static_methods[i, 0] ", " substr(spaces, 1, mlen - length(static_methods[i, 0])) "\""  static_methods[i, 0] "\", " substr(spaces, 1, mlen - length(static_methods[i, 0])) "" static_methods[i, 1] ", \"" static_methods[i, 2] "\");"
+		}
 		delete static_methods[i]
 	}
 	if (static_method_size != 0) print ""
new file mode 100644
--- /dev/null
+++ b/src/script/api/template/template_admin.hpp.sq
@@ -0,0 +1,21 @@
+/* $Id$ */
+
+/*
+ * This file is part of OpenTTD.
+ * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
+ * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */
+
+#include "../script_admin.hpp"
+
+namespace SQConvert {
+	/* Allow ScriptAdmin to be used as Squirrel parameter */
+	template <> inline ScriptAdmin *GetParam(ForceType<ScriptAdmin *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return  (ScriptAdmin *)instance; }
+	template <> inline ScriptAdmin &GetParam(ForceType<ScriptAdmin &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(ScriptAdmin *)instance; }
+	template <> inline const ScriptAdmin *GetParam(ForceType<const ScriptAdmin *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return  (ScriptAdmin *)instance; }
+	template <> inline const ScriptAdmin &GetParam(ForceType<const ScriptAdmin &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(ScriptAdmin *)instance; }
+	template <> inline int Return<ScriptAdmin *>(HSQUIRRELVM vm, ScriptAdmin *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "Admin", res, NULL, DefSQDestructorCallback<ScriptAdmin>, true); return 1; }
+} // namespace SQConvert
--- a/src/script/api/template/template_event_types.hpp.sq
+++ b/src/script/api/template/template_event_types.hpp.sq
@@ -212,3 +212,12 @@
 	template <> inline const ScriptEventAircraftDestTooFar &GetParam(ForceType<const ScriptEventAircraftDestTooFar &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(ScriptEventAircraftDestTooFar *)instance; }
 	template <> inline int Return<ScriptEventAircraftDestTooFar *>(HSQUIRRELVM vm, ScriptEventAircraftDestTooFar *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "EventAircraftDestTooFar", res, NULL, DefSQDestructorCallback<ScriptEventAircraftDestTooFar>, true); return 1; }
 } // namespace SQConvert
+
+namespace SQConvert {
+	/* Allow ScriptEventAdminPort to be used as Squirrel parameter */
+	template <> inline ScriptEventAdminPort *GetParam(ForceType<ScriptEventAdminPort *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return  (ScriptEventAdminPort *)instance; }
+	template <> inline ScriptEventAdminPort &GetParam(ForceType<ScriptEventAdminPort &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(ScriptEventAdminPort *)instance; }
+	template <> inline const ScriptEventAdminPort *GetParam(ForceType<const ScriptEventAdminPort *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return  (ScriptEventAdminPort *)instance; }
+	template <> inline const ScriptEventAdminPort &GetParam(ForceType<const ScriptEventAdminPort &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(ScriptEventAdminPort *)instance; }
+	template <> inline int Return<ScriptEventAdminPort *>(HSQUIRRELVM vm, ScriptEventAdminPort *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "EventAdminPort", res, NULL, DefSQDestructorCallback<ScriptEventAdminPort>, true); return 1; }
+} // namespace SQConvert
--- a/src/script/script_instance.cpp
+++ b/src/script/script_instance.cpp
@@ -297,12 +297,10 @@
 	SLE_END()
 };
 
-static const uint SCRIPTSAVE_MAX_DEPTH = 25; ///< The maximum recursive depth for items stored in the savegame.
-
 /* static */ bool ScriptInstance::SaveObject(HSQUIRRELVM vm, SQInteger index, int max_depth, bool test)
 {
 	if (max_depth == 0) {
-		ScriptLog::Error("Savedata can only be nested to 25 deep. No data saved.");
+		ScriptLog::Error("Savedata can only be nested to 25 deep. No data saved."); // SQUIRREL_MAX_DEPTH = 25
 		return false;
 	}
 
@@ -439,7 +437,7 @@
 		_script_sl_byte = 1;
 		SlObject(NULL, _script_byte);
 		/* Save the data that was just loaded. */
-		SaveObject(vm, -1, SCRIPTSAVE_MAX_DEPTH, false);
+		SaveObject(vm, -1, SQUIRREL_MAX_DEPTH, false);
 	} else if (!this->is_started) {
 		SaveEmpty();
 		return;
@@ -478,10 +476,10 @@
 			return;
 		}
 		sq_pushobject(vm, savedata);
-		if (SaveObject(vm, -1, SCRIPTSAVE_MAX_DEPTH, true)) {
+		if (SaveObject(vm, -1, SQUIRREL_MAX_DEPTH, true)) {
 			_script_sl_byte = 1;
 			SlObject(NULL, _script_byte);
-			SaveObject(vm, -1, SCRIPTSAVE_MAX_DEPTH, false);
+			SaveObject(vm, -1, SQUIRREL_MAX_DEPTH, false);
 			this->is_save_data_on_stack = true;
 		} else {
 			SaveEmpty();
--- a/src/script/script_instance.hpp
+++ b/src/script/script_instance.hpp
@@ -17,6 +17,8 @@
 
 #include "../command_type.h"
 
+static const uint SQUIRREL_MAX_DEPTH = 25; ///< The maximum recursive depth for items stored in the savegame.
+
 /** Runtime information about a script like a pointer to the squirrel vm and the current state. */
 class ScriptInstance {
 public:
--- a/src/script/squirrel_class.hpp
+++ b/src/script/squirrel_class.hpp
@@ -72,6 +72,16 @@
 	}
 
 	/**
+	 * This defines a static method inside a class for Squirrel, which has access to the 'engine' (experts only!).
+	 */
+	template <typename Func>
+	void DefSQAdvancedStaticMethod(Squirrel *engine, Func function_proc, const char *function_name)
+	{
+		using namespace SQConvert;
+		engine->AddMethod(function_name, DefSQAdvancedStaticCallback<CL, Func>, 0, NULL, &function_proc, sizeof(function_proc));
+	}
+
+	/**
 	 * This defines a static method inside a class for Squirrel with defined params.
 	 * @note If you define nparam, make sure that he first param is always 'x',
 	 *  which is the 'this' inside the function. This is hidden from the rest
--- a/src/script/squirrel_helper.hpp
+++ b/src/script/squirrel_helper.hpp
@@ -831,6 +831,28 @@
 		}
 	}
 
+
+	/**
+	 * A general template for all static advanced method callbacks from Squirrel.
+	 *  In here the function_proc is recovered, and the SQCall is called that
+	 *  can handle this exact amount of params.
+	 */
+	template <typename Tcls, typename Tmethod>
+	inline SQInteger DefSQAdvancedStaticCallback(HSQUIRRELVM vm)
+	{
+		/* Find the amount of params we got */
+		int nparam = sq_gettop(vm);
+		SQUserPointer ptr = NULL;
+
+		/* Get the real function pointer */
+		sq_getuserdata(vm, nparam, &ptr, 0);
+		/* Remove the userdata from the stack */
+		sq_pop(vm, 1);
+
+		/* Call the function, which its only param is always the VM */
+		return (SQInteger)(*(*(Tmethod *)ptr))(vm);
+	}
+
 	/**
 	 * A general template for the destructor of SQ instances. This is needed
 	 *  here as it has to be in the same scope as DefSQConstructorCallback.