Rename packethandler/{client,server}.cpp to {client,server}packethandler.cpp
authorLoic Blot <loic.blot@unix-experience.fr>
Mon, 16 Mar 2015 09:19:13 +0000 (10:19 +0100)
committerLoic Blot <loic.blot@unix-experience.fr>
Mon, 16 Mar 2015 09:19:19 +0000 (10:19 +0100)
* Requested by @Zeno-
* Approved by @nerzhul

build/android/jni/Android.mk
src/network/CMakeLists.txt
src/network/clientpackethandler.cpp [new file with mode: 0644]
src/network/packethandlers/client.cpp [deleted file]
src/network/packethandlers/server.cpp [deleted file]
src/network/serverpackethandler.cpp [new file with mode: 0644]

index a35084433cc7d7b2ecaa2a7d92e5050969828726..24367a2aac892a224076d1e30aff63184e6d6d04 100644 (file)
@@ -221,9 +221,9 @@ LOCAL_SRC_FILES +=                                \
                jni/src/network/connection.cpp            \
                jni/src/network/networkpacket.cpp         \
                jni/src/network/clientopcodes.cpp         \
+               jni/src/network/clientpackethandler.cpp   \
                jni/src/network/serveropcodes.cpp         \
-               jni/src/network/packethandlers/server.cpp \
-               jni/src/network/packethandlers/client.cpp
+               jni/src/network/serverpackethandler.cpp   \
 
 # lua api
 LOCAL_SRC_FILES +=                                \
index 3162469c8f866af97a5e2864e3a359d2c09e3724..4095ace260021fcc4c03d7e9ac8bca64d21331ce 100644 (file)
@@ -1,7 +1,7 @@
 set(common_network_SRCS
        ${CMAKE_CURRENT_SOURCE_DIR}/connection.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/networkpacket.cpp
-       ${CMAKE_CURRENT_SOURCE_DIR}/packethandlers/server.cpp
+       ${CMAKE_CURRENT_SOURCE_DIR}/serverpackethandler.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/serveropcodes.cpp
        PARENT_SCOPE
 )
@@ -9,7 +9,7 @@ set(common_network_SRCS
 if (BUILD_CLIENT)
        set(client_network_SRCS
                ${CMAKE_CURRENT_SOURCE_DIR}/clientopcodes.cpp
-               ${CMAKE_CURRENT_SOURCE_DIR}/packethandlers/client.cpp
+               ${CMAKE_CURRENT_SOURCE_DIR}/clientpackethandler.cpp
                PARENT_SCOPE
        )
-endif(BUILD_CLIENT)
\ No newline at end of file
+endif(BUILD_CLIENT)
diff --git a/src/network/clientpackethandler.cpp b/src/network/clientpackethandler.cpp
new file mode 100644 (file)
index 0000000..5acf04f
--- /dev/null
@@ -0,0 +1,1096 @@
+/*
+Minetest
+Copyright (C) 2015 nerzhul, Loic Blot <loic.blot@unix-experience.fr>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "client.h"
+
+#include "util/base64.h"
+#include "clientmedia.h"
+#include "log.h"
+#include "map.h"
+#include "mapsector.h"
+#include "nodedef.h"
+#include "serialization.h"
+#include "server.h"
+#include "strfnd.h"
+#include "network/clientopcodes.h"
+#include "util/serialize.h"
+
+void Client::handleCommand_Deprecated(NetworkPacket* pkt)
+{
+       infostream << "Got deprecated command "
+                       << toClientCommandTable[pkt->getCommand()].name << " from peer "
+                       << pkt->getPeerId() << "!" << std::endl;
+}
+
+void Client::handleCommand_Hello(NetworkPacket* pkt)
+{
+       if (pkt->getSize() < 1)
+               return;
+
+       u8 deployed;
+       *pkt >> deployed;
+
+       infostream << "Client: TOCLIENT_HELLO received with "
+                       "deployed=" << ((int)deployed & 0xff) << std::endl;
+
+       if (!ser_ver_supported(deployed)) {
+               infostream << "Client: TOCLIENT_HELLO: Server sent "
+                               << "unsupported ser_fmt_ver"<< std::endl;
+               return;
+       }
+
+       m_server_ser_ver = deployed;
+
+       // @ TODO auth to server
+}
+
+void Client::handleCommand_AuthAccept(NetworkPacket* pkt)
+{
+       v3f playerpos;
+       *pkt >> playerpos >> m_map_seed >> m_recommended_send_interval;
+
+       playerpos -= v3f(0, BS / 2, 0);
+
+       // Set player position
+       Player *player = m_env.getLocalPlayer();
+       assert(player != NULL);
+       player->setPosition(playerpos);
+
+       infostream << "Client: received map seed: " << m_map_seed << std::endl;
+       infostream << "Client: received recommended send interval "
+                                       << m_recommended_send_interval<<std::endl;
+
+       // Reply to server
+       NetworkPacket resp_pkt(TOSERVER_INIT2, 0);
+       Send(&resp_pkt);
+
+       m_state = LC_Init;
+}
+
+void Client::handleCommand_InitLegacy(NetworkPacket* pkt)
+{
+       if (pkt->getSize() < 1)
+               return;
+
+       u8 deployed;
+       *pkt >> deployed;
+
+       infostream << "Client: TOCLIENT_INIT_LEGACY received with "
+                       "deployed=" << ((int)deployed & 0xff) << std::endl;
+
+       if (!ser_ver_supported(deployed)) {
+               infostream << "Client: TOCLIENT_INIT_LEGACY: Server sent "
+                               << "unsupported ser_fmt_ver"<< std::endl;
+               return;
+       }
+
+       m_server_ser_ver = deployed;
+
+       // Get player position
+       v3s16 playerpos_s16(0, BS * 2 + BS * 20, 0);
+       if (pkt->getSize() >= 1 + 6) {
+               *pkt >> playerpos_s16;
+       }
+       v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS / 2, 0);
+
+
+       // Set player position
+       Player *player = m_env.getLocalPlayer();
+       assert(player != NULL);
+       player->setPosition(playerpos_f);
+
+       if (pkt->getSize() >= 1 + 6 + 8) {
+               // Get map seed
+               *pkt >> m_map_seed;
+               infostream << "Client: received map seed: " << m_map_seed << std::endl;
+       }
+
+       if (pkt->getSize() >= 1 + 6 + 8 + 4) {
+               *pkt >> m_recommended_send_interval;
+               infostream << "Client: received recommended send interval "
+                               << m_recommended_send_interval<<std::endl;
+       }
+
+       // Reply to server
+       NetworkPacket resp_pkt(TOSERVER_INIT2, 0);
+       Send(&resp_pkt);
+
+       m_state = LC_Init;
+}
+
+void Client::handleCommand_AccessDenied(NetworkPacket* pkt)
+{
+       // The server didn't like our password. Note, this needs
+       // to be processed even if the serialisation format has
+       // not been agreed yet, the same as TOCLIENT_INIT.
+       m_access_denied = true;
+       m_access_denied_reason = L"Unknown";
+
+       if (pkt->getCommand() == TOCLIENT_ACCESS_DENIED) {
+               if (pkt->getSize() < 1)
+                       return;
+
+               u8 denyCode = SERVER_ACCESSDENIED_UNEXPECTED_DATA;
+               *pkt >> denyCode;
+               if (denyCode == SERVER_ACCESSDENIED_CUSTOM_STRING) {
+                       *pkt >> m_access_denied_reason;
+               }
+               else if (denyCode < SERVER_ACCESSDENIED_MAX) {
+                       m_access_denied_reason = accessDeniedStrings[denyCode];
+               }
+       }
+       // 13/03/15 Legacy code from 0.4.12 and lesser. must stay 1 year
+       // for compat with old clients
+       else {
+               if (pkt->getSize() >= 2) {
+                       *pkt >> m_access_denied_reason;
+               }
+       }
+}
+
+void Client::handleCommand_RemoveNode(NetworkPacket* pkt)
+{
+       if (pkt->getSize() < 6)
+               return;
+
+       v3s16 p;
+       *pkt >> p;
+       removeNode(p);
+}
+
+void Client::handleCommand_AddNode(NetworkPacket* pkt)
+{
+       if (pkt->getSize() < 6 + MapNode::serializedLength(m_server_ser_ver))
+               return;
+
+       v3s16 p;
+       *pkt >> p;
+
+       MapNode n;
+       n.deSerialize(pkt->getU8Ptr(6), m_server_ser_ver);
+
+       bool remove_metadata = true;
+       u32 index = 6 + MapNode::serializedLength(m_server_ser_ver);
+       if ((pkt->getSize() >= index + 1) && pkt->getU8(index)) {
+               remove_metadata = false;
+       }
+
+       addNode(p, n, remove_metadata);
+}
+void Client::handleCommand_BlockData(NetworkPacket* pkt)
+{
+       // Ignore too small packet
+       if (pkt->getSize() < 6)
+               return;
+
+       v3s16 p;
+       *pkt >> p;
+
+       std::string datastring(pkt->getString(6), pkt->getSize() - 6);
+       std::istringstream istr(datastring, std::ios_base::binary);
+
+       MapSector *sector;
+       MapBlock *block;
+
+       v2s16 p2d(p.X, p.Z);
+       sector = m_env.getMap().emergeSector(p2d);
+
+       assert(sector->getPos() == p2d);
+
+       block = sector->getBlockNoCreateNoEx(p.Y);
+       if (block) {
+               /*
+                       Update an existing block
+               */
+               block->deSerialize(istr, m_server_ser_ver, false);
+               block->deSerializeNetworkSpecific(istr);
+       }
+       else {
+               /*
+                       Create a new block
+               */
+               block = new MapBlock(&m_env.getMap(), p, this);
+               block->deSerialize(istr, m_server_ser_ver, false);
+               block->deSerializeNetworkSpecific(istr);
+               sector->insertBlock(block);
+       }
+
+       if (m_localdb) {
+               ServerMap::saveBlock(block, m_localdb);
+       }
+
+       /*
+               Add it to mesh update queue and set it to be acknowledged after update.
+       */
+       addUpdateMeshTaskWithEdge(p, true);
+}
+
+void Client::handleCommand_Inventory(NetworkPacket* pkt)
+{
+       if (pkt->getSize() < 1)
+               return;
+
+       std::string datastring(pkt->getString(0), pkt->getSize());
+       std::istringstream is(datastring, std::ios_base::binary);
+
+       Player *player = m_env.getLocalPlayer();
+       assert(player != NULL);
+
+       player->inventory.deSerialize(is);
+
+       m_inventory_updated = true;
+
+       delete m_inventory_from_server;
+       m_inventory_from_server = new Inventory(player->inventory);
+       m_inventory_from_server_age = 0.0;
+}
+
+void Client::handleCommand_TimeOfDay(NetworkPacket* pkt)
+{
+       if (pkt->getSize() < 2)
+               return;
+
+       u16 time_of_day;
+
+       *pkt >> time_of_day;
+
+       time_of_day      = time_of_day % 24000;
+       float time_speed = 0;
+
+       if (pkt->getSize() >= 2 + 4) {
+               *pkt >> time_speed;
+       }
+       else {
+               // Old message; try to approximate speed of time by ourselves
+               float time_of_day_f = (float)time_of_day / 24000.0;
+               float tod_diff_f = 0;
+
+               if (time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
+                       tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0;
+               else
+                       tod_diff_f = time_of_day_f - m_last_time_of_day_f;
+
+               m_last_time_of_day_f       = time_of_day_f;
+               float time_diff            = m_time_of_day_update_timer;
+               m_time_of_day_update_timer = 0;
+
+               if (m_time_of_day_set) {
+                       time_speed = (3600.0 * 24.0) * tod_diff_f / time_diff;
+                       infostream << "Client: Measured time_of_day speed (old format): "
+                                       << time_speed << " tod_diff_f=" << tod_diff_f
+                                       << " time_diff=" << time_diff << std::endl;
+               }
+       }
+
+       // Update environment
+       m_env.setTimeOfDay(time_of_day);
+       m_env.setTimeOfDaySpeed(time_speed);
+       m_time_of_day_set = true;
+
+       u32 dr = m_env.getDayNightRatio();
+       infostream << "Client: time_of_day=" << time_of_day
+                       << " time_speed=" << time_speed
+                       << " dr=" << dr << std::endl;
+}
+
+void Client::handleCommand_ChatMessage(NetworkPacket* pkt)
+{
+       /*
+               u16 command
+               u16 length
+               wstring message
+       */
+       u16 len, read_wchar;
+
+       *pkt >> len;
+
+       std::wstring message;
+       for (u32 i = 0; i < len; i++) {
+               *pkt >> read_wchar;
+               message += (wchar_t)read_wchar;
+       }
+
+       m_chat_queue.push(message);
+}
+
+void Client::handleCommand_ActiveObjectRemoveAdd(NetworkPacket* pkt)
+{
+       /*
+               u16 command
+               u16 count of removed objects
+               for all removed objects {
+                       u16 id
+               }
+               u16 count of added objects
+               for all added objects {
+                       u16 id
+                       u8 type
+                       u32 initialization data length
+                       string initialization data
+               }
+       */
+
+       // Read removed objects
+       u8 type;
+       u16 removed_count, added_count, id;
+
+       *pkt >> removed_count;
+
+       for (u16 i = 0; i < removed_count; i++) {
+               *pkt >> id;
+               m_env.removeActiveObject(id);
+       }
+
+       // Read added objects
+       *pkt >> added_count;
+
+       for (u16 i = 0; i < added_count; i++) {
+               *pkt >> id >> type;
+               m_env.addActiveObject(id, type, pkt->readLongString());
+       }
+}
+
+void Client::handleCommand_ActiveObjectMessages(NetworkPacket* pkt)
+{
+       /*
+               u16 command
+               for all objects
+               {
+                       u16 id
+                       u16 message length
+                       string message
+               }
+       */
+       char buf[6];
+       // Get all data except the command number
+       std::string datastring(pkt->getString(0), pkt->getSize());
+       // Throw them in an istringstream
+       std::istringstream is(datastring, std::ios_base::binary);
+
+       while(is.eof() == false) {
+               is.read(buf, 2);
+               u16 id = readU16((u8*)buf);
+               if (is.eof())
+                       break;
+               is.read(buf, 2);
+               size_t message_size = readU16((u8*)buf);
+               std::string message;
+               message.reserve(message_size);
+               for (u32 i = 0; i < message_size; i++) {
+                       is.read(buf, 1);
+                       message.append(buf, 1);
+               }
+               // Pass on to the environment
+               m_env.processActiveObjectMessage(id, message);
+       }
+}
+
+void Client::handleCommand_Movement(NetworkPacket* pkt)
+{
+       Player *player = m_env.getLocalPlayer();
+       assert(player != NULL);
+
+       float mad, maa, maf, msw, mscr, msf, mscl, msj, lf, lfs, ls, g;
+
+       *pkt >> mad >> maa >> maf >> msw >> mscr >> msf >> mscl >> msj
+               >> lf >> lfs >> ls >> g;
+
+       player->movement_acceleration_default   = mad * BS;
+       player->movement_acceleration_air       = maa * BS;
+       player->movement_acceleration_fast      = maf * BS;
+       player->movement_speed_walk             = msw * BS;
+       player->movement_speed_crouch           = mscr * BS;
+       player->movement_speed_fast             = msf * BS;
+       player->movement_speed_climb            = mscl * BS;
+       player->movement_speed_jump             = msj * BS;
+       player->movement_liquid_fluidity        = lf * BS;
+       player->movement_liquid_fluidity_smooth = lfs * BS;
+       player->movement_liquid_sink            = ls * BS;
+       player->movement_gravity                = g * BS;
+}
+
+void Client::handleCommand_HP(NetworkPacket* pkt)
+{
+
+       Player *player = m_env.getLocalPlayer();
+       assert(player != NULL);
+
+       u8 oldhp   = player->hp;
+
+       u8 hp;
+       *pkt >> hp;
+
+       player->hp = hp;
+
+       if (hp < oldhp) {
+               // Add to ClientEvent queue
+               ClientEvent event;
+               event.type = CE_PLAYER_DAMAGE;
+               event.player_damage.amount = oldhp - hp;
+               m_client_event_queue.push(event);
+       }
+}
+
+void Client::handleCommand_Breath(NetworkPacket* pkt)
+{
+       Player *player = m_env.getLocalPlayer();
+       assert(player != NULL);
+
+       u16 breath;
+
+       *pkt >> breath;
+
+       player->setBreath(breath);
+}
+
+void Client::handleCommand_MovePlayer(NetworkPacket* pkt)
+{
+       Player *player = m_env.getLocalPlayer();
+       assert(player != NULL);
+
+       v3f pos;
+       f32 pitch, yaw;
+
+       *pkt >> pos >> pitch >> yaw;
+
+       player->setPosition(pos);
+
+       infostream << "Client got TOCLIENT_MOVE_PLAYER"
+                       << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
+                       << " pitch=" << pitch
+                       << " yaw=" << yaw
+                       << std::endl;
+
+       /*
+               Add to ClientEvent queue.
+               This has to be sent to the main program because otherwise
+               it would just force the pitch and yaw values to whatever
+               the camera points to.
+       */
+       ClientEvent event;
+       event.type = CE_PLAYER_FORCE_MOVE;
+       event.player_force_move.pitch = pitch;
+       event.player_force_move.yaw = yaw;
+       m_client_event_queue.push(event);
+
+       // Ignore damage for a few seconds, so that the player doesn't
+       // get damage from falling on ground
+       m_ignore_damage_timer = 3.0;
+}
+
+void Client::handleCommand_PlayerItem(NetworkPacket* pkt)
+{
+       infostream << "Client: WARNING: Ignoring TOCLIENT_PLAYERITEM" << std::endl;
+}
+
+void Client::handleCommand_DeathScreen(NetworkPacket* pkt)
+{
+       bool set_camera_point_target;
+       v3f camera_point_target;
+
+       *pkt >> set_camera_point_target;
+       *pkt >> camera_point_target;
+
+       ClientEvent event;
+       event.type                                = CE_DEATHSCREEN;
+       event.deathscreen.set_camera_point_target = set_camera_point_target;
+       event.deathscreen.camera_point_target_x   = camera_point_target.X;
+       event.deathscreen.camera_point_target_y   = camera_point_target.Y;
+       event.deathscreen.camera_point_target_z   = camera_point_target.Z;
+       m_client_event_queue.push(event);
+}
+
+void Client::handleCommand_AnnounceMedia(NetworkPacket* pkt)
+{
+       u16 num_files;
+
+       *pkt >> num_files;
+
+       infostream << "Client: Received media announcement: packet size: "
+                       << pkt->getSize() << std::endl;
+
+       if (m_media_downloader == NULL ||
+                       m_media_downloader->isStarted()) {
+               const char *problem = m_media_downloader ?
+                       "we already saw another announcement" :
+                       "all media has been received already";
+               errorstream << "Client: Received media announcement but "
+                       << problem << "! "
+                       << " files=" << num_files
+                       << " size=" << pkt->getSize() << std::endl;
+               return;
+       }
+
+       // Mesh update thread must be stopped while
+       // updating content definitions
+       sanity_check(!m_mesh_update_thread.IsRunning());
+
+       for (u16 i = 0; i < num_files; i++) {
+               std::string name, sha1_base64;
+
+               *pkt >> name >> sha1_base64;
+
+               std::string sha1_raw = base64_decode(sha1_base64);
+               m_media_downloader->addFile(name, sha1_raw);
+       }
+
+       std::vector<std::string> remote_media;
+       try {
+               std::string str;
+
+               *pkt >> str;
+
+               Strfnd sf(str);
+               while(!sf.atend()) {
+                       std::string baseurl = trim(sf.next(","));
+                       if (baseurl != "")
+                               m_media_downloader->addRemoteServer(baseurl);
+               }
+       }
+       catch(SerializationError& e) {
+               // not supported by server or turned off
+       }
+
+       m_media_downloader->step(this);
+}
+
+void Client::handleCommand_Media(NetworkPacket* pkt)
+{
+       /*
+               u16 command
+               u16 total number of file bunches
+               u16 index of this bunch
+               u32 number of files in this bunch
+               for each file {
+                       u16 length of name
+                       string name
+                       u32 length of data
+                       data
+               }
+       */
+       u16 num_bunches;
+       u16 bunch_i;
+       u32 num_files;
+
+       *pkt >> num_bunches >> bunch_i >> num_files;
+
+       infostream << "Client: Received files: bunch " << bunch_i << "/"
+                       << num_bunches << " files=" << num_files
+                       << " size=" << pkt->getSize() << std::endl;
+
+       if (num_files == 0)
+               return;
+
+       if (m_media_downloader == NULL ||
+                       !m_media_downloader->isStarted()) {
+               const char *problem = m_media_downloader ?
+                       "media has not been requested" :
+                       "all media has been received already";
+               errorstream << "Client: Received media but "
+                       << problem << "! "
+                       << " bunch " << bunch_i << "/" << num_bunches
+                       << " files=" << num_files
+                       << " size=" << pkt->getSize() << std::endl;
+               return;
+       }
+
+       // Mesh update thread must be stopped while
+       // updating content definitions
+       sanity_check(!m_mesh_update_thread.IsRunning());
+
+       for (u32 i=0; i < num_files; i++) {
+               std::string name;
+
+               *pkt >> name;
+
+               std::string data = pkt->readLongString();
+
+               m_media_downloader->conventionalTransferDone(
+                               name, data, this);
+       }
+}
+
+void Client::handleCommand_ToolDef(NetworkPacket* pkt)
+{
+       infostream << "Client: WARNING: Ignoring TOCLIENT_TOOLDEF" << std::endl;
+}
+
+void Client::handleCommand_NodeDef(NetworkPacket* pkt)
+{
+       infostream << "Client: Received node definitions: packet size: "
+                       << pkt->getSize() << std::endl;
+
+       // Mesh update thread must be stopped while
+       // updating content definitions
+       sanity_check(!m_mesh_update_thread.IsRunning());
+
+       // Decompress node definitions
+       std::string datastring(pkt->getString(0), pkt->getSize());
+       std::istringstream is(datastring, std::ios_base::binary);
+       std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
+       std::ostringstream tmp_os;
+       decompressZlib(tmp_is, tmp_os);
+
+       // Deserialize node definitions
+       std::istringstream tmp_is2(tmp_os.str());
+       m_nodedef->deSerialize(tmp_is2);
+       m_nodedef_received = true;
+}
+
+void Client::handleCommand_CraftItemDef(NetworkPacket* pkt)
+{
+       infostream << "Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF" << std::endl;
+}
+
+void Client::handleCommand_ItemDef(NetworkPacket* pkt)
+{
+       infostream << "Client: Received item definitions: packet size: "
+                       << pkt->getSize() << std::endl;
+
+       // Mesh update thread must be stopped while
+       // updating content definitions
+       sanity_check(!m_mesh_update_thread.IsRunning());
+
+       // Decompress item definitions
+       std::string datastring(pkt->getString(0), pkt->getSize());
+       std::istringstream is(datastring, std::ios_base::binary);
+       std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
+       std::ostringstream tmp_os;
+       decompressZlib(tmp_is, tmp_os);
+
+       // Deserialize node definitions
+       std::istringstream tmp_is2(tmp_os.str());
+       m_itemdef->deSerialize(tmp_is2);
+       m_itemdef_received = true;
+}
+
+void Client::handleCommand_PlaySound(NetworkPacket* pkt)
+{
+       s32 server_id;
+       std::string name;
+       float gain;
+       u8 type; // 0=local, 1=positional, 2=object
+       v3f pos;
+       u16 object_id;
+       bool loop;
+
+       *pkt >> server_id >> name >> gain >> type >> pos >> object_id >> loop;
+
+       // Start playing
+       int client_id = -1;
+       switch(type) {
+               case 0: // local
+                       client_id = m_sound->playSound(name, loop, gain);
+                       break;
+               case 1: // positional
+                       client_id = m_sound->playSoundAt(name, loop, gain, pos);
+                       break;
+               case 2:
+               { // object
+                       ClientActiveObject *cao = m_env.getActiveObject(object_id);
+                       if (cao)
+                               pos = cao->getPosition();
+                       client_id = m_sound->playSoundAt(name, loop, gain, pos);
+                       // TODO: Set up sound to move with object
+                       break;
+               }
+               default:
+                       break;
+       }
+
+       if (client_id != -1) {
+               m_sounds_server_to_client[server_id] = client_id;
+               m_sounds_client_to_server[client_id] = server_id;
+               if (object_id != 0)
+                       m_sounds_to_objects[client_id] = object_id;
+       }
+}
+
+void Client::handleCommand_StopSound(NetworkPacket* pkt)
+{
+       s32 server_id;
+
+       *pkt >> server_id;
+
+       std::map<s32, int>::iterator i =
+               m_sounds_server_to_client.find(server_id);
+
+       if (i != m_sounds_server_to_client.end()) {
+               int client_id = i->second;
+               m_sound->stopSound(client_id);
+       }
+}
+
+void Client::handleCommand_Privileges(NetworkPacket* pkt)
+{
+       m_privileges.clear();
+       infostream << "Client: Privileges updated: ";
+       u16 num_privileges;
+
+       *pkt >> num_privileges;
+
+       for (u16 i = 0; i < num_privileges; i++) {
+               std::string priv;
+
+               *pkt >> priv;
+
+               m_privileges.insert(priv);
+               infostream << priv << " ";
+       }
+       infostream << std::endl;
+}
+
+void Client::handleCommand_InventoryFormSpec(NetworkPacket* pkt)
+{
+       Player *player = m_env.getLocalPlayer();
+       assert(player != NULL);
+
+       // Store formspec in LocalPlayer
+       player->inventory_formspec = pkt->readLongString();
+}
+
+void Client::handleCommand_DetachedInventory(NetworkPacket* pkt)
+{
+       std::string datastring(pkt->getString(0), pkt->getSize());
+       std::istringstream is(datastring, std::ios_base::binary);
+
+       std::string name = deSerializeString(is);
+
+       infostream << "Client: Detached inventory update: \"" << name
+                       << "\"" << std::endl;
+
+       Inventory *inv = NULL;
+       if (m_detached_inventories.count(name) > 0)
+               inv = m_detached_inventories[name];
+       else {
+               inv = new Inventory(m_itemdef);
+               m_detached_inventories[name] = inv;
+       }
+       inv->deSerialize(is);
+}
+
+void Client::handleCommand_ShowFormSpec(NetworkPacket* pkt)
+{
+       std::string formspec = pkt->readLongString();
+       std::string formname;
+
+       *pkt >> formname;
+
+       ClientEvent event;
+       event.type = CE_SHOW_FORMSPEC;
+       // pointer is required as event is a struct only!
+       // adding a std:string to a struct isn't possible
+       event.show_formspec.formspec = new std::string(formspec);
+       event.show_formspec.formname = new std::string(formname);
+       m_client_event_queue.push(event);
+}
+
+void Client::handleCommand_SpawnParticle(NetworkPacket* pkt)
+{
+       std::string datastring(pkt->getString(0), pkt->getSize());
+       std::istringstream is(datastring, std::ios_base::binary);
+
+       v3f pos                 = readV3F1000(is);
+       v3f vel                 = readV3F1000(is);
+       v3f acc                 = readV3F1000(is);
+       float expirationtime    = readF1000(is);
+       float size              = readF1000(is);
+       bool collisiondetection = readU8(is);
+       std::string texture     = deSerializeLongString(is);
+       bool vertical           = false;
+       try {
+               vertical = readU8(is);
+       } catch (...) {}
+
+       ClientEvent event;
+       event.type                              = CE_SPAWN_PARTICLE;
+       event.spawn_particle.pos                = new v3f (pos);
+       event.spawn_particle.vel                = new v3f (vel);
+       event.spawn_particle.acc                = new v3f (acc);
+       event.spawn_particle.expirationtime     = expirationtime;
+       event.spawn_particle.size               = size;
+       event.spawn_particle.collisiondetection = collisiondetection;
+       event.spawn_particle.vertical           = vertical;
+       event.spawn_particle.texture            = new std::string(texture);
+
+       m_client_event_queue.push(event);
+}
+
+void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt)
+{
+       u16 amount;
+       float spawntime;
+       v3f minpos;
+       v3f maxpos;
+       v3f minvel;
+       v3f maxvel;
+       v3f minacc;
+       v3f maxacc;
+       float minexptime;
+       float maxexptime;
+       float minsize;
+       float maxsize;
+       bool collisiondetection;
+       u32 id;
+
+       *pkt >> amount >> spawntime >> minpos >> maxpos >> minvel >> maxvel
+               >> minacc >> maxacc >> minexptime >> maxexptime >> minsize
+               >> maxsize >> collisiondetection;
+
+       std::string texture = pkt->readLongString();
+
+       *pkt >> id;
+
+       bool vertical = false;
+       try {
+               *pkt >> vertical;
+       } catch (...) {}
+
+       ClientEvent event;
+       event.type                                   = CE_ADD_PARTICLESPAWNER;
+       event.add_particlespawner.amount             = amount;
+       event.add_particlespawner.spawntime          = spawntime;
+       event.add_particlespawner.minpos             = new v3f (minpos);
+       event.add_particlespawner.maxpos             = new v3f (maxpos);
+       event.add_particlespawner.minvel             = new v3f (minvel);
+       event.add_particlespawner.maxvel             = new v3f (maxvel);
+       event.add_particlespawner.minacc             = new v3f (minacc);
+       event.add_particlespawner.maxacc             = new v3f (maxacc);
+       event.add_particlespawner.minexptime         = minexptime;
+       event.add_particlespawner.maxexptime         = maxexptime;
+       event.add_particlespawner.minsize            = minsize;
+       event.add_particlespawner.maxsize            = maxsize;
+       event.add_particlespawner.collisiondetection = collisiondetection;
+       event.add_particlespawner.vertical           = vertical;
+       event.add_particlespawner.texture            = new std::string(texture);
+       event.add_particlespawner.id                 = id;
+
+       m_client_event_queue.push(event);
+}
+
+
+void Client::handleCommand_DeleteParticleSpawner(NetworkPacket* pkt)
+{
+       u16 legacy_id;
+       u32 id;
+
+       // Modification set 13/03/15, 1 year of compat for protocol v24
+       if (pkt->getCommand() == TOCLIENT_DELETE_PARTICLESPAWNER_LEGACY) {
+               *pkt >> legacy_id;
+       }
+       else {
+               *pkt >> id;
+       }
+
+
+       ClientEvent event;
+       event.type                      = CE_DELETE_PARTICLESPAWNER;
+       event.delete_particlespawner.id =
+                       (pkt->getCommand() == TOCLIENT_DELETE_PARTICLESPAWNER_LEGACY ? (u32) legacy_id : id);
+
+       m_client_event_queue.push(event);
+}
+
+void Client::handleCommand_HudAdd(NetworkPacket* pkt)
+{
+       std::string datastring(pkt->getString(0), pkt->getSize());
+       std::istringstream is(datastring, std::ios_base::binary);
+
+       u32 id;
+       u8 type;
+       v2f pos;
+       std::string name;
+       v2f scale;
+       std::string text;
+       u32 number;
+       u32 item;
+       u32 dir;
+       v2f align;
+       v2f offset;
+       v3f world_pos;
+       v2s32 size;
+
+       *pkt >> id >> type >> pos >> name >> scale >> text >> number >> item
+               >> dir >> align >> offset;
+       try {
+               *pkt >> world_pos;
+       }
+       catch(SerializationError &e) {};
+
+       try {
+               *pkt >> size;
+       } catch(SerializationError &e) {};
+
+       ClientEvent event;
+       event.type             = CE_HUDADD;
+       event.hudadd.id        = id;
+       event.hudadd.type      = type;
+       event.hudadd.pos       = new v2f(pos);
+       event.hudadd.name      = new std::string(name);
+       event.hudadd.scale     = new v2f(scale);
+       event.hudadd.text      = new std::string(text);
+       event.hudadd.number    = number;
+       event.hudadd.item      = item;
+       event.hudadd.dir       = dir;
+       event.hudadd.align     = new v2f(align);
+       event.hudadd.offset    = new v2f(offset);
+       event.hudadd.world_pos = new v3f(world_pos);
+       event.hudadd.size      = new v2s32(size);
+       m_client_event_queue.push(event);
+}
+
+void Client::handleCommand_HudRemove(NetworkPacket* pkt)
+{
+       u32 id;
+
+       *pkt >> id;
+
+       ClientEvent event;
+       event.type     = CE_HUDRM;
+       event.hudrm.id = id;
+       m_client_event_queue.push(event);
+}
+
+void Client::handleCommand_HudChange(NetworkPacket* pkt)
+{
+       std::string sdata;
+       v2f v2fdata;
+       v3f v3fdata;
+       u32 intdata = 0;
+       v2s32 v2s32data;
+       u32 id;
+       u8 stat;
+
+       *pkt >> id >> stat;
+
+       if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
+               stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
+               *pkt >> v2fdata;
+       else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
+               *pkt >> sdata;
+       else if (stat == HUD_STAT_WORLD_POS)
+               *pkt >> v3fdata;
+       else if (stat == HUD_STAT_SIZE )
+               *pkt >> v2s32data;
+       else
+               *pkt >> intdata;
+
+       ClientEvent event;
+       event.type              = CE_HUDCHANGE;
+       event.hudchange.id      = id;
+       event.hudchange.stat    = (HudElementStat)stat;
+       event.hudchange.v2fdata = new v2f(v2fdata);
+       event.hudchange.v3fdata = new v3f(v3fdata);
+       event.hudchange.sdata   = new std::string(sdata);
+       event.hudchange.data    = intdata;
+       event.hudchange.v2s32data = new v2s32(v2s32data);
+       m_client_event_queue.push(event);
+}
+
+void Client::handleCommand_HudSetFlags(NetworkPacket* pkt)
+{
+       u32 flags, mask;
+
+       *pkt >> flags >> mask;
+
+       Player *player = m_env.getLocalPlayer();
+       assert(player != NULL);
+
+       player->hud_flags &= ~mask;
+       player->hud_flags |= flags;
+}
+
+void Client::handleCommand_HudSetParam(NetworkPacket* pkt)
+{
+       u16 param; std::string value;
+
+       *pkt >> param >> value;
+
+       Player *player = m_env.getLocalPlayer();
+       assert(player != NULL);
+
+       if (param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) {
+               s32 hotbar_itemcount = readS32((u8*) value.c_str());
+               if (hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
+                       player->hud_hotbar_itemcount = hotbar_itemcount;
+       }
+       else if (param == HUD_PARAM_HOTBAR_IMAGE) {
+               ((LocalPlayer *) player)->hotbar_image = value;
+       }
+       else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
+               ((LocalPlayer *) player)->hotbar_selected_image = value;
+       }
+}
+
+void Client::handleCommand_HudSetSky(NetworkPacket* pkt)
+{
+       std::string datastring(pkt->getString(0), pkt->getSize());
+       std::istringstream is(datastring, std::ios_base::binary);
+
+       video::SColor *bgcolor           = new video::SColor(readARGB8(is));
+       std::string *type                = new std::string(deSerializeString(is));
+       u16 count                        = readU16(is);
+       std::vector<std::string> *params = new std::vector<std::string>;
+
+       for (size_t i = 0; i < count; i++)
+               params->push_back(deSerializeString(is));
+
+       ClientEvent event;
+       event.type            = CE_SET_SKY;
+       event.set_sky.bgcolor = bgcolor;
+       event.set_sky.type    = type;
+       event.set_sky.params  = params;
+       m_client_event_queue.push(event);
+}
+
+void Client::handleCommand_OverrideDayNightRatio(NetworkPacket* pkt)
+{
+       bool do_override;
+       u16 day_night_ratio_u;
+
+       *pkt >> do_override >> day_night_ratio_u;
+
+       float day_night_ratio_f = (float)day_night_ratio_u / 65536;
+
+       ClientEvent event;
+       event.type                                 = CE_OVERRIDE_DAY_NIGHT_RATIO;
+       event.override_day_night_ratio.do_override = do_override;
+       event.override_day_night_ratio.ratio_f     = day_night_ratio_f;
+       m_client_event_queue.push(event);
+}
+
+void Client::handleCommand_LocalPlayerAnimations(NetworkPacket* pkt)
+{
+       LocalPlayer *player = m_env.getLocalPlayer();
+       assert(player != NULL);
+
+       *pkt >> player->local_animations[0];
+       *pkt >> player->local_animations[1];
+       *pkt >> player->local_animations[2];
+       *pkt >> player->local_animations[3];
+       *pkt >> player->local_animation_speed;
+}
+
+void Client::handleCommand_EyeOffset(NetworkPacket* pkt)
+{
+       LocalPlayer *player = m_env.getLocalPlayer();
+       assert(player != NULL);
+
+       *pkt >> player->eye_offset_first >> player->eye_offset_third;
+}
diff --git a/src/network/packethandlers/client.cpp b/src/network/packethandlers/client.cpp
deleted file mode 100644 (file)
index 5acf04f..0000000
+++ /dev/null
@@ -1,1096 +0,0 @@
-/*
-Minetest
-Copyright (C) 2015 nerzhul, Loic Blot <loic.blot@unix-experience.fr>
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 of the License, or
-(at your option) any later version.
-
-This program 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 Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser General Public License along
-with this program; if not, write to the Free Software Foundation, Inc.,
-51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-*/
-
-#include "client.h"
-
-#include "util/base64.h"
-#include "clientmedia.h"
-#include "log.h"
-#include "map.h"
-#include "mapsector.h"
-#include "nodedef.h"
-#include "serialization.h"
-#include "server.h"
-#include "strfnd.h"
-#include "network/clientopcodes.h"
-#include "util/serialize.h"
-
-void Client::handleCommand_Deprecated(NetworkPacket* pkt)
-{
-       infostream << "Got deprecated command "
-                       << toClientCommandTable[pkt->getCommand()].name << " from peer "
-                       << pkt->getPeerId() << "!" << std::endl;
-}
-
-void Client::handleCommand_Hello(NetworkPacket* pkt)
-{
-       if (pkt->getSize() < 1)
-               return;
-
-       u8 deployed;
-       *pkt >> deployed;
-
-       infostream << "Client: TOCLIENT_HELLO received with "
-                       "deployed=" << ((int)deployed & 0xff) << std::endl;
-
-       if (!ser_ver_supported(deployed)) {
-               infostream << "Client: TOCLIENT_HELLO: Server sent "
-                               << "unsupported ser_fmt_ver"<< std::endl;
-               return;
-       }
-
-       m_server_ser_ver = deployed;
-
-       // @ TODO auth to server
-}
-
-void Client::handleCommand_AuthAccept(NetworkPacket* pkt)
-{
-       v3f playerpos;
-       *pkt >> playerpos >> m_map_seed >> m_recommended_send_interval;
-
-       playerpos -= v3f(0, BS / 2, 0);
-
-       // Set player position
-       Player *player = m_env.getLocalPlayer();
-       assert(player != NULL);
-       player->setPosition(playerpos);
-
-       infostream << "Client: received map seed: " << m_map_seed << std::endl;
-       infostream << "Client: received recommended send interval "
-                                       << m_recommended_send_interval<<std::endl;
-
-       // Reply to server
-       NetworkPacket resp_pkt(TOSERVER_INIT2, 0);
-       Send(&resp_pkt);
-
-       m_state = LC_Init;
-}
-
-void Client::handleCommand_InitLegacy(NetworkPacket* pkt)
-{
-       if (pkt->getSize() < 1)
-               return;
-
-       u8 deployed;
-       *pkt >> deployed;
-
-       infostream << "Client: TOCLIENT_INIT_LEGACY received with "
-                       "deployed=" << ((int)deployed & 0xff) << std::endl;
-
-       if (!ser_ver_supported(deployed)) {
-               infostream << "Client: TOCLIENT_INIT_LEGACY: Server sent "
-                               << "unsupported ser_fmt_ver"<< std::endl;
-               return;
-       }
-
-       m_server_ser_ver = deployed;
-
-       // Get player position
-       v3s16 playerpos_s16(0, BS * 2 + BS * 20, 0);
-       if (pkt->getSize() >= 1 + 6) {
-               *pkt >> playerpos_s16;
-       }
-       v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS / 2, 0);
-
-
-       // Set player position
-       Player *player = m_env.getLocalPlayer();
-       assert(player != NULL);
-       player->setPosition(playerpos_f);
-
-       if (pkt->getSize() >= 1 + 6 + 8) {
-               // Get map seed
-               *pkt >> m_map_seed;
-               infostream << "Client: received map seed: " << m_map_seed << std::endl;
-       }
-
-       if (pkt->getSize() >= 1 + 6 + 8 + 4) {
-               *pkt >> m_recommended_send_interval;
-               infostream << "Client: received recommended send interval "
-                               << m_recommended_send_interval<<std::endl;
-       }
-
-       // Reply to server
-       NetworkPacket resp_pkt(TOSERVER_INIT2, 0);
-       Send(&resp_pkt);
-
-       m_state = LC_Init;
-}
-
-void Client::handleCommand_AccessDenied(NetworkPacket* pkt)
-{
-       // The server didn't like our password. Note, this needs
-       // to be processed even if the serialisation format has
-       // not been agreed yet, the same as TOCLIENT_INIT.
-       m_access_denied = true;
-       m_access_denied_reason = L"Unknown";
-
-       if (pkt->getCommand() == TOCLIENT_ACCESS_DENIED) {
-               if (pkt->getSize() < 1)
-                       return;
-
-               u8 denyCode = SERVER_ACCESSDENIED_UNEXPECTED_DATA;
-               *pkt >> denyCode;
-               if (denyCode == SERVER_ACCESSDENIED_CUSTOM_STRING) {
-                       *pkt >> m_access_denied_reason;
-               }
-               else if (denyCode < SERVER_ACCESSDENIED_MAX) {
-                       m_access_denied_reason = accessDeniedStrings[denyCode];
-               }
-       }
-       // 13/03/15 Legacy code from 0.4.12 and lesser. must stay 1 year
-       // for compat with old clients
-       else {
-               if (pkt->getSize() >= 2) {
-                       *pkt >> m_access_denied_reason;
-               }
-       }
-}
-
-void Client::handleCommand_RemoveNode(NetworkPacket* pkt)
-{
-       if (pkt->getSize() < 6)
-               return;
-
-       v3s16 p;
-       *pkt >> p;
-       removeNode(p);
-}
-
-void Client::handleCommand_AddNode(NetworkPacket* pkt)
-{
-       if (pkt->getSize() < 6 + MapNode::serializedLength(m_server_ser_ver))
-               return;
-
-       v3s16 p;
-       *pkt >> p;
-
-       MapNode n;
-       n.deSerialize(pkt->getU8Ptr(6), m_server_ser_ver);
-
-       bool remove_metadata = true;
-       u32 index = 6 + MapNode::serializedLength(m_server_ser_ver);
-       if ((pkt->getSize() >= index + 1) && pkt->getU8(index)) {
-               remove_metadata = false;
-       }
-
-       addNode(p, n, remove_metadata);
-}
-void Client::handleCommand_BlockData(NetworkPacket* pkt)
-{
-       // Ignore too small packet
-       if (pkt->getSize() < 6)
-               return;
-
-       v3s16 p;
-       *pkt >> p;
-
-       std::string datastring(pkt->getString(6), pkt->getSize() - 6);
-       std::istringstream istr(datastring, std::ios_base::binary);
-
-       MapSector *sector;
-       MapBlock *block;
-
-       v2s16 p2d(p.X, p.Z);
-       sector = m_env.getMap().emergeSector(p2d);
-
-       assert(sector->getPos() == p2d);
-
-       block = sector->getBlockNoCreateNoEx(p.Y);
-       if (block) {
-               /*
-                       Update an existing block
-               */
-               block->deSerialize(istr, m_server_ser_ver, false);
-               block->deSerializeNetworkSpecific(istr);
-       }
-       else {
-               /*
-                       Create a new block
-               */
-               block = new MapBlock(&m_env.getMap(), p, this);
-               block->deSerialize(istr, m_server_ser_ver, false);
-               block->deSerializeNetworkSpecific(istr);
-               sector->insertBlock(block);
-       }
-
-       if (m_localdb) {
-               ServerMap::saveBlock(block, m_localdb);
-       }
-
-       /*
-               Add it to mesh update queue and set it to be acknowledged after update.
-       */
-       addUpdateMeshTaskWithEdge(p, true);
-}
-
-void Client::handleCommand_Inventory(NetworkPacket* pkt)
-{
-       if (pkt->getSize() < 1)
-               return;
-
-       std::string datastring(pkt->getString(0), pkt->getSize());
-       std::istringstream is(datastring, std::ios_base::binary);
-
-       Player *player = m_env.getLocalPlayer();
-       assert(player != NULL);
-
-       player->inventory.deSerialize(is);
-
-       m_inventory_updated = true;
-
-       delete m_inventory_from_server;
-       m_inventory_from_server = new Inventory(player->inventory);
-       m_inventory_from_server_age = 0.0;
-}
-
-void Client::handleCommand_TimeOfDay(NetworkPacket* pkt)
-{
-       if (pkt->getSize() < 2)
-               return;
-
-       u16 time_of_day;
-
-       *pkt >> time_of_day;
-
-       time_of_day      = time_of_day % 24000;
-       float time_speed = 0;
-
-       if (pkt->getSize() >= 2 + 4) {
-               *pkt >> time_speed;
-       }
-       else {
-               // Old message; try to approximate speed of time by ourselves
-               float time_of_day_f = (float)time_of_day / 24000.0;
-               float tod_diff_f = 0;
-
-               if (time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
-                       tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0;
-               else
-                       tod_diff_f = time_of_day_f - m_last_time_of_day_f;
-
-               m_last_time_of_day_f       = time_of_day_f;
-               float time_diff            = m_time_of_day_update_timer;
-               m_time_of_day_update_timer = 0;
-
-               if (m_time_of_day_set) {
-                       time_speed = (3600.0 * 24.0) * tod_diff_f / time_diff;
-                       infostream << "Client: Measured time_of_day speed (old format): "
-                                       << time_speed << " tod_diff_f=" << tod_diff_f
-                                       << " time_diff=" << time_diff << std::endl;
-               }
-       }
-
-       // Update environment
-       m_env.setTimeOfDay(time_of_day);
-       m_env.setTimeOfDaySpeed(time_speed);
-       m_time_of_day_set = true;
-
-       u32 dr = m_env.getDayNightRatio();
-       infostream << "Client: time_of_day=" << time_of_day
-                       << " time_speed=" << time_speed
-                       << " dr=" << dr << std::endl;
-}
-
-void Client::handleCommand_ChatMessage(NetworkPacket* pkt)
-{
-       /*
-               u16 command
-               u16 length
-               wstring message
-       */
-       u16 len, read_wchar;
-
-       *pkt >> len;
-
-       std::wstring message;
-       for (u32 i = 0; i < len; i++) {
-               *pkt >> read_wchar;
-               message += (wchar_t)read_wchar;
-       }
-
-       m_chat_queue.push(message);
-}
-
-void Client::handleCommand_ActiveObjectRemoveAdd(NetworkPacket* pkt)
-{
-       /*
-               u16 command
-               u16 count of removed objects
-               for all removed objects {
-                       u16 id
-               }
-               u16 count of added objects
-               for all added objects {
-                       u16 id
-                       u8 type
-                       u32 initialization data length
-                       string initialization data
-               }
-       */
-
-       // Read removed objects
-       u8 type;
-       u16 removed_count, added_count, id;
-
-       *pkt >> removed_count;
-
-       for (u16 i = 0; i < removed_count; i++) {
-               *pkt >> id;
-               m_env.removeActiveObject(id);
-       }
-
-       // Read added objects
-       *pkt >> added_count;
-
-       for (u16 i = 0; i < added_count; i++) {
-               *pkt >> id >> type;
-               m_env.addActiveObject(id, type, pkt->readLongString());
-       }
-}
-
-void Client::handleCommand_ActiveObjectMessages(NetworkPacket* pkt)
-{
-       /*
-               u16 command
-               for all objects
-               {
-                       u16 id
-                       u16 message length
-                       string message
-               }
-       */
-       char buf[6];
-       // Get all data except the command number
-       std::string datastring(pkt->getString(0), pkt->getSize());
-       // Throw them in an istringstream
-       std::istringstream is(datastring, std::ios_base::binary);
-
-       while(is.eof() == false) {
-               is.read(buf, 2);
-               u16 id = readU16((u8*)buf);
-               if (is.eof())
-                       break;
-               is.read(buf, 2);
-               size_t message_size = readU16((u8*)buf);
-               std::string message;
-               message.reserve(message_size);
-               for (u32 i = 0; i < message_size; i++) {
-                       is.read(buf, 1);
-                       message.append(buf, 1);
-               }
-               // Pass on to the environment
-               m_env.processActiveObjectMessage(id, message);
-       }
-}
-
-void Client::handleCommand_Movement(NetworkPacket* pkt)
-{
-       Player *player = m_env.getLocalPlayer();
-       assert(player != NULL);
-
-       float mad, maa, maf, msw, mscr, msf, mscl, msj, lf, lfs, ls, g;
-
-       *pkt >> mad >> maa >> maf >> msw >> mscr >> msf >> mscl >> msj
-               >> lf >> lfs >> ls >> g;
-
-       player->movement_acceleration_default   = mad * BS;
-       player->movement_acceleration_air       = maa * BS;
-       player->movement_acceleration_fast      = maf * BS;
-       player->movement_speed_walk             = msw * BS;
-       player->movement_speed_crouch           = mscr * BS;
-       player->movement_speed_fast             = msf * BS;
-       player->movement_speed_climb            = mscl * BS;
-       player->movement_speed_jump             = msj * BS;
-       player->movement_liquid_fluidity        = lf * BS;
-       player->movement_liquid_fluidity_smooth = lfs * BS;
-       player->movement_liquid_sink            = ls * BS;
-       player->movement_gravity                = g * BS;
-}
-
-void Client::handleCommand_HP(NetworkPacket* pkt)
-{
-
-       Player *player = m_env.getLocalPlayer();
-       assert(player != NULL);
-
-       u8 oldhp   = player->hp;
-
-       u8 hp;
-       *pkt >> hp;
-
-       player->hp = hp;
-
-       if (hp < oldhp) {
-               // Add to ClientEvent queue
-               ClientEvent event;
-               event.type = CE_PLAYER_DAMAGE;
-               event.player_damage.amount = oldhp - hp;
-               m_client_event_queue.push(event);
-       }
-}
-
-void Client::handleCommand_Breath(NetworkPacket* pkt)
-{
-       Player *player = m_env.getLocalPlayer();
-       assert(player != NULL);
-
-       u16 breath;
-
-       *pkt >> breath;
-
-       player->setBreath(breath);
-}
-
-void Client::handleCommand_MovePlayer(NetworkPacket* pkt)
-{
-       Player *player = m_env.getLocalPlayer();
-       assert(player != NULL);
-
-       v3f pos;
-       f32 pitch, yaw;
-
-       *pkt >> pos >> pitch >> yaw;
-
-       player->setPosition(pos);
-
-       infostream << "Client got TOCLIENT_MOVE_PLAYER"
-                       << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
-                       << " pitch=" << pitch
-                       << " yaw=" << yaw
-                       << std::endl;
-
-       /*
-               Add to ClientEvent queue.
-               This has to be sent to the main program because otherwise
-               it would just force the pitch and yaw values to whatever
-               the camera points to.
-       */
-       ClientEvent event;
-       event.type = CE_PLAYER_FORCE_MOVE;
-       event.player_force_move.pitch = pitch;
-       event.player_force_move.yaw = yaw;
-       m_client_event_queue.push(event);
-
-       // Ignore damage for a few seconds, so that the player doesn't
-       // get damage from falling on ground
-       m_ignore_damage_timer = 3.0;
-}
-
-void Client::handleCommand_PlayerItem(NetworkPacket* pkt)
-{
-       infostream << "Client: WARNING: Ignoring TOCLIENT_PLAYERITEM" << std::endl;
-}
-
-void Client::handleCommand_DeathScreen(NetworkPacket* pkt)
-{
-       bool set_camera_point_target;
-       v3f camera_point_target;
-
-       *pkt >> set_camera_point_target;
-       *pkt >> camera_point_target;
-
-       ClientEvent event;
-       event.type                                = CE_DEATHSCREEN;
-       event.deathscreen.set_camera_point_target = set_camera_point_target;
-       event.deathscreen.camera_point_target_x   = camera_point_target.X;
-       event.deathscreen.camera_point_target_y   = camera_point_target.Y;
-       event.deathscreen.camera_point_target_z   = camera_point_target.Z;
-       m_client_event_queue.push(event);
-}
-
-void Client::handleCommand_AnnounceMedia(NetworkPacket* pkt)
-{
-       u16 num_files;
-
-       *pkt >> num_files;
-
-       infostream << "Client: Received media announcement: packet size: "
-                       << pkt->getSize() << std::endl;
-
-       if (m_media_downloader == NULL ||
-                       m_media_downloader->isStarted()) {
-               const char *problem = m_media_downloader ?
-                       "we already saw another announcement" :
-                       "all media has been received already";
-               errorstream << "Client: Received media announcement but "
-                       << problem << "! "
-                       << " files=" << num_files
-                       << " size=" << pkt->getSize() << std::endl;
-               return;
-       }
-
-       // Mesh update thread must be stopped while
-       // updating content definitions
-       sanity_check(!m_mesh_update_thread.IsRunning());
-
-       for (u16 i = 0; i < num_files; i++) {
-               std::string name, sha1_base64;
-
-               *pkt >> name >> sha1_base64;
-
-               std::string sha1_raw = base64_decode(sha1_base64);
-               m_media_downloader->addFile(name, sha1_raw);
-       }
-
-       std::vector<std::string> remote_media;
-       try {
-               std::string str;
-
-               *pkt >> str;
-
-               Strfnd sf(str);
-               while(!sf.atend()) {
-                       std::string baseurl = trim(sf.next(","));
-                       if (baseurl != "")
-                               m_media_downloader->addRemoteServer(baseurl);
-               }
-       }
-       catch(SerializationError& e) {
-               // not supported by server or turned off
-       }
-
-       m_media_downloader->step(this);
-}
-
-void Client::handleCommand_Media(NetworkPacket* pkt)
-{
-       /*
-               u16 command
-               u16 total number of file bunches
-               u16 index of this bunch
-               u32 number of files in this bunch
-               for each file {
-                       u16 length of name
-                       string name
-                       u32 length of data
-                       data
-               }
-       */
-       u16 num_bunches;
-       u16 bunch_i;
-       u32 num_files;
-
-       *pkt >> num_bunches >> bunch_i >> num_files;
-
-       infostream << "Client: Received files: bunch " << bunch_i << "/"
-                       << num_bunches << " files=" << num_files
-                       << " size=" << pkt->getSize() << std::endl;
-
-       if (num_files == 0)
-               return;
-
-       if (m_media_downloader == NULL ||
-                       !m_media_downloader->isStarted()) {
-               const char *problem = m_media_downloader ?
-                       "media has not been requested" :
-                       "all media has been received already";
-               errorstream << "Client: Received media but "
-                       << problem << "! "
-                       << " bunch " << bunch_i << "/" << num_bunches
-                       << " files=" << num_files
-                       << " size=" << pkt->getSize() << std::endl;
-               return;
-       }
-
-       // Mesh update thread must be stopped while
-       // updating content definitions
-       sanity_check(!m_mesh_update_thread.IsRunning());
-
-       for (u32 i=0; i < num_files; i++) {
-               std::string name;
-
-               *pkt >> name;
-
-               std::string data = pkt->readLongString();
-
-               m_media_downloader->conventionalTransferDone(
-                               name, data, this);
-       }
-}
-
-void Client::handleCommand_ToolDef(NetworkPacket* pkt)
-{
-       infostream << "Client: WARNING: Ignoring TOCLIENT_TOOLDEF" << std::endl;
-}
-
-void Client::handleCommand_NodeDef(NetworkPacket* pkt)
-{
-       infostream << "Client: Received node definitions: packet size: "
-                       << pkt->getSize() << std::endl;
-
-       // Mesh update thread must be stopped while
-       // updating content definitions
-       sanity_check(!m_mesh_update_thread.IsRunning());
-
-       // Decompress node definitions
-       std::string datastring(pkt->getString(0), pkt->getSize());
-       std::istringstream is(datastring, std::ios_base::binary);
-       std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
-       std::ostringstream tmp_os;
-       decompressZlib(tmp_is, tmp_os);
-
-       // Deserialize node definitions
-       std::istringstream tmp_is2(tmp_os.str());
-       m_nodedef->deSerialize(tmp_is2);
-       m_nodedef_received = true;
-}
-
-void Client::handleCommand_CraftItemDef(NetworkPacket* pkt)
-{
-       infostream << "Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF" << std::endl;
-}
-
-void Client::handleCommand_ItemDef(NetworkPacket* pkt)
-{
-       infostream << "Client: Received item definitions: packet size: "
-                       << pkt->getSize() << std::endl;
-
-       // Mesh update thread must be stopped while
-       // updating content definitions
-       sanity_check(!m_mesh_update_thread.IsRunning());
-
-       // Decompress item definitions
-       std::string datastring(pkt->getString(0), pkt->getSize());
-       std::istringstream is(datastring, std::ios_base::binary);
-       std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
-       std::ostringstream tmp_os;
-       decompressZlib(tmp_is, tmp_os);
-
-       // Deserialize node definitions
-       std::istringstream tmp_is2(tmp_os.str());
-       m_itemdef->deSerialize(tmp_is2);
-       m_itemdef_received = true;
-}
-
-void Client::handleCommand_PlaySound(NetworkPacket* pkt)
-{
-       s32 server_id;
-       std::string name;
-       float gain;
-       u8 type; // 0=local, 1=positional, 2=object
-       v3f pos;
-       u16 object_id;
-       bool loop;
-
-       *pkt >> server_id >> name >> gain >> type >> pos >> object_id >> loop;
-
-       // Start playing
-       int client_id = -1;
-       switch(type) {
-               case 0: // local
-                       client_id = m_sound->playSound(name, loop, gain);
-                       break;
-               case 1: // positional
-                       client_id = m_sound->playSoundAt(name, loop, gain, pos);
-                       break;
-               case 2:
-               { // object
-                       ClientActiveObject *cao = m_env.getActiveObject(object_id);
-                       if (cao)
-                               pos = cao->getPosition();
-                       client_id = m_sound->playSoundAt(name, loop, gain, pos);
-                       // TODO: Set up sound to move with object
-                       break;
-               }
-               default:
-                       break;
-       }
-
-       if (client_id != -1) {
-               m_sounds_server_to_client[server_id] = client_id;
-               m_sounds_client_to_server[client_id] = server_id;
-               if (object_id != 0)
-                       m_sounds_to_objects[client_id] = object_id;
-       }
-}
-
-void Client::handleCommand_StopSound(NetworkPacket* pkt)
-{
-       s32 server_id;
-
-       *pkt >> server_id;
-
-       std::map<s32, int>::iterator i =
-               m_sounds_server_to_client.find(server_id);
-
-       if (i != m_sounds_server_to_client.end()) {
-               int client_id = i->second;
-               m_sound->stopSound(client_id);
-       }
-}
-
-void Client::handleCommand_Privileges(NetworkPacket* pkt)
-{
-       m_privileges.clear();
-       infostream << "Client: Privileges updated: ";
-       u16 num_privileges;
-
-       *pkt >> num_privileges;
-
-       for (u16 i = 0; i < num_privileges; i++) {
-               std::string priv;
-
-               *pkt >> priv;
-
-               m_privileges.insert(priv);
-               infostream << priv << " ";
-       }
-       infostream << std::endl;
-}
-
-void Client::handleCommand_InventoryFormSpec(NetworkPacket* pkt)
-{
-       Player *player = m_env.getLocalPlayer();
-       assert(player != NULL);
-
-       // Store formspec in LocalPlayer
-       player->inventory_formspec = pkt->readLongString();
-}
-
-void Client::handleCommand_DetachedInventory(NetworkPacket* pkt)
-{
-       std::string datastring(pkt->getString(0), pkt->getSize());
-       std::istringstream is(datastring, std::ios_base::binary);
-
-       std::string name = deSerializeString(is);
-
-       infostream << "Client: Detached inventory update: \"" << name
-                       << "\"" << std::endl;
-
-       Inventory *inv = NULL;
-       if (m_detached_inventories.count(name) > 0)
-               inv = m_detached_inventories[name];
-       else {
-               inv = new Inventory(m_itemdef);
-               m_detached_inventories[name] = inv;
-       }
-       inv->deSerialize(is);
-}
-
-void Client::handleCommand_ShowFormSpec(NetworkPacket* pkt)
-{
-       std::string formspec = pkt->readLongString();
-       std::string formname;
-
-       *pkt >> formname;
-
-       ClientEvent event;
-       event.type = CE_SHOW_FORMSPEC;
-       // pointer is required as event is a struct only!
-       // adding a std:string to a struct isn't possible
-       event.show_formspec.formspec = new std::string(formspec);
-       event.show_formspec.formname = new std::string(formname);
-       m_client_event_queue.push(event);
-}
-
-void Client::handleCommand_SpawnParticle(NetworkPacket* pkt)
-{
-       std::string datastring(pkt->getString(0), pkt->getSize());
-       std::istringstream is(datastring, std::ios_base::binary);
-
-       v3f pos                 = readV3F1000(is);
-       v3f vel                 = readV3F1000(is);
-       v3f acc                 = readV3F1000(is);
-       float expirationtime    = readF1000(is);
-       float size              = readF1000(is);
-       bool collisiondetection = readU8(is);
-       std::string texture     = deSerializeLongString(is);
-       bool vertical           = false;
-       try {
-               vertical = readU8(is);
-       } catch (...) {}
-
-       ClientEvent event;
-       event.type                              = CE_SPAWN_PARTICLE;
-       event.spawn_particle.pos                = new v3f (pos);
-       event.spawn_particle.vel                = new v3f (vel);
-       event.spawn_particle.acc                = new v3f (acc);
-       event.spawn_particle.expirationtime     = expirationtime;
-       event.spawn_particle.size               = size;
-       event.spawn_particle.collisiondetection = collisiondetection;
-       event.spawn_particle.vertical           = vertical;
-       event.spawn_particle.texture            = new std::string(texture);
-
-       m_client_event_queue.push(event);
-}
-
-void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt)
-{
-       u16 amount;
-       float spawntime;
-       v3f minpos;
-       v3f maxpos;
-       v3f minvel;
-       v3f maxvel;
-       v3f minacc;
-       v3f maxacc;
-       float minexptime;
-       float maxexptime;
-       float minsize;
-       float maxsize;
-       bool collisiondetection;
-       u32 id;
-
-       *pkt >> amount >> spawntime >> minpos >> maxpos >> minvel >> maxvel
-               >> minacc >> maxacc >> minexptime >> maxexptime >> minsize
-               >> maxsize >> collisiondetection;
-
-       std::string texture = pkt->readLongString();
-
-       *pkt >> id;
-
-       bool vertical = false;
-       try {
-               *pkt >> vertical;
-       } catch (...) {}
-
-       ClientEvent event;
-       event.type                                   = CE_ADD_PARTICLESPAWNER;
-       event.add_particlespawner.amount             = amount;
-       event.add_particlespawner.spawntime          = spawntime;
-       event.add_particlespawner.minpos             = new v3f (minpos);
-       event.add_particlespawner.maxpos             = new v3f (maxpos);
-       event.add_particlespawner.minvel             = new v3f (minvel);
-       event.add_particlespawner.maxvel             = new v3f (maxvel);
-       event.add_particlespawner.minacc             = new v3f (minacc);
-       event.add_particlespawner.maxacc             = new v3f (maxacc);
-       event.add_particlespawner.minexptime         = minexptime;
-       event.add_particlespawner.maxexptime         = maxexptime;
-       event.add_particlespawner.minsize            = minsize;
-       event.add_particlespawner.maxsize            = maxsize;
-       event.add_particlespawner.collisiondetection = collisiondetection;
-       event.add_particlespawner.vertical           = vertical;
-       event.add_particlespawner.texture            = new std::string(texture);
-       event.add_particlespawner.id                 = id;
-
-       m_client_event_queue.push(event);
-}
-
-
-void Client::handleCommand_DeleteParticleSpawner(NetworkPacket* pkt)
-{
-       u16 legacy_id;
-       u32 id;
-
-       // Modification set 13/03/15, 1 year of compat for protocol v24
-       if (pkt->getCommand() == TOCLIENT_DELETE_PARTICLESPAWNER_LEGACY) {
-               *pkt >> legacy_id;
-       }
-       else {
-               *pkt >> id;
-       }
-
-
-       ClientEvent event;
-       event.type                      = CE_DELETE_PARTICLESPAWNER;
-       event.delete_particlespawner.id =
-                       (pkt->getCommand() == TOCLIENT_DELETE_PARTICLESPAWNER_LEGACY ? (u32) legacy_id : id);
-
-       m_client_event_queue.push(event);
-}
-
-void Client::handleCommand_HudAdd(NetworkPacket* pkt)
-{
-       std::string datastring(pkt->getString(0), pkt->getSize());
-       std::istringstream is(datastring, std::ios_base::binary);
-
-       u32 id;
-       u8 type;
-       v2f pos;
-       std::string name;
-       v2f scale;
-       std::string text;
-       u32 number;
-       u32 item;
-       u32 dir;
-       v2f align;
-       v2f offset;
-       v3f world_pos;
-       v2s32 size;
-
-       *pkt >> id >> type >> pos >> name >> scale >> text >> number >> item
-               >> dir >> align >> offset;
-       try {
-               *pkt >> world_pos;
-       }
-       catch(SerializationError &e) {};
-
-       try {
-               *pkt >> size;
-       } catch(SerializationError &e) {};
-
-       ClientEvent event;
-       event.type             = CE_HUDADD;
-       event.hudadd.id        = id;
-       event.hudadd.type      = type;
-       event.hudadd.pos       = new v2f(pos);
-       event.hudadd.name      = new std::string(name);
-       event.hudadd.scale     = new v2f(scale);
-       event.hudadd.text      = new std::string(text);
-       event.hudadd.number    = number;
-       event.hudadd.item      = item;
-       event.hudadd.dir       = dir;
-       event.hudadd.align     = new v2f(align);
-       event.hudadd.offset    = new v2f(offset);
-       event.hudadd.world_pos = new v3f(world_pos);
-       event.hudadd.size      = new v2s32(size);
-       m_client_event_queue.push(event);
-}
-
-void Client::handleCommand_HudRemove(NetworkPacket* pkt)
-{
-       u32 id;
-
-       *pkt >> id;
-
-       ClientEvent event;
-       event.type     = CE_HUDRM;
-       event.hudrm.id = id;
-       m_client_event_queue.push(event);
-}
-
-void Client::handleCommand_HudChange(NetworkPacket* pkt)
-{
-       std::string sdata;
-       v2f v2fdata;
-       v3f v3fdata;
-       u32 intdata = 0;
-       v2s32 v2s32data;
-       u32 id;
-       u8 stat;
-
-       *pkt >> id >> stat;
-
-       if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
-               stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
-               *pkt >> v2fdata;
-       else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
-               *pkt >> sdata;
-       else if (stat == HUD_STAT_WORLD_POS)
-               *pkt >> v3fdata;
-       else if (stat == HUD_STAT_SIZE )
-               *pkt >> v2s32data;
-       else
-               *pkt >> intdata;
-
-       ClientEvent event;
-       event.type              = CE_HUDCHANGE;
-       event.hudchange.id      = id;
-       event.hudchange.stat    = (HudElementStat)stat;
-       event.hudchange.v2fdata = new v2f(v2fdata);
-       event.hudchange.v3fdata = new v3f(v3fdata);
-       event.hudchange.sdata   = new std::string(sdata);
-       event.hudchange.data    = intdata;
-       event.hudchange.v2s32data = new v2s32(v2s32data);
-       m_client_event_queue.push(event);
-}
-
-void Client::handleCommand_HudSetFlags(NetworkPacket* pkt)
-{
-       u32 flags, mask;
-
-       *pkt >> flags >> mask;
-
-       Player *player = m_env.getLocalPlayer();
-       assert(player != NULL);
-
-       player->hud_flags &= ~mask;
-       player->hud_flags |= flags;
-}
-
-void Client::handleCommand_HudSetParam(NetworkPacket* pkt)
-{
-       u16 param; std::string value;
-
-       *pkt >> param >> value;
-
-       Player *player = m_env.getLocalPlayer();
-       assert(player != NULL);
-
-       if (param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) {
-               s32 hotbar_itemcount = readS32((u8*) value.c_str());
-               if (hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
-                       player->hud_hotbar_itemcount = hotbar_itemcount;
-       }
-       else if (param == HUD_PARAM_HOTBAR_IMAGE) {
-               ((LocalPlayer *) player)->hotbar_image = value;
-       }
-       else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
-               ((LocalPlayer *) player)->hotbar_selected_image = value;
-       }
-}
-
-void Client::handleCommand_HudSetSky(NetworkPacket* pkt)
-{
-       std::string datastring(pkt->getString(0), pkt->getSize());
-       std::istringstream is(datastring, std::ios_base::binary);
-
-       video::SColor *bgcolor           = new video::SColor(readARGB8(is));
-       std::string *type                = new std::string(deSerializeString(is));
-       u16 count                        = readU16(is);
-       std::vector<std::string> *params = new std::vector<std::string>;
-
-       for (size_t i = 0; i < count; i++)
-               params->push_back(deSerializeString(is));
-
-       ClientEvent event;
-       event.type            = CE_SET_SKY;
-       event.set_sky.bgcolor = bgcolor;
-       event.set_sky.type    = type;
-       event.set_sky.params  = params;
-       m_client_event_queue.push(event);
-}
-
-void Client::handleCommand_OverrideDayNightRatio(NetworkPacket* pkt)
-{
-       bool do_override;
-       u16 day_night_ratio_u;
-
-       *pkt >> do_override >> day_night_ratio_u;
-
-       float day_night_ratio_f = (float)day_night_ratio_u / 65536;
-
-       ClientEvent event;
-       event.type                                 = CE_OVERRIDE_DAY_NIGHT_RATIO;
-       event.override_day_night_ratio.do_override = do_override;
-       event.override_day_night_ratio.ratio_f     = day_night_ratio_f;
-       m_client_event_queue.push(event);
-}
-
-void Client::handleCommand_LocalPlayerAnimations(NetworkPacket* pkt)
-{
-       LocalPlayer *player = m_env.getLocalPlayer();
-       assert(player != NULL);
-
-       *pkt >> player->local_animations[0];
-       *pkt >> player->local_animations[1];
-       *pkt >> player->local_animations[2];
-       *pkt >> player->local_animations[3];
-       *pkt >> player->local_animation_speed;
-}
-
-void Client::handleCommand_EyeOffset(NetworkPacket* pkt)
-{
-       LocalPlayer *player = m_env.getLocalPlayer();
-       assert(player != NULL);
-
-       *pkt >> player->eye_offset_first >> player->eye_offset_third;
-}
diff --git a/src/network/packethandlers/server.cpp b/src/network/packethandlers/server.cpp
deleted file mode 100644 (file)
index bb3a534..0000000
+++ /dev/null
@@ -1,1847 +0,0 @@
-/*
-Minetest
-Copyright (C) 2015 nerzhul, Loic Blot <loic.blot@unix-experience.fr>
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 of the License, or
-(at your option) any later version.
-
-This program 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 Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser General Public License along
-with this program; if not, write to the Free Software Foundation, Inc.,
-51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-*/
-
-#include "server.h"
-#include "log.h"
-
-#include "content_abm.h"
-#include "content_sao.h"
-#include "emerge.h"
-#include "main.h"
-#include "nodedef.h"
-#include "player.h"
-#include "rollback_interface.h"
-#include "scripting_game.h"
-#include "settings.h"
-#include "tool.h"
-#include "version.h"
-#include "network/networkprotocol.h"
-#include "network/serveropcodes.h"
-#include "util/base64.h"
-#include "util/pointedthing.h"
-#include "util/serialize.h"
-
-void Server::handleCommand_Deprecated(NetworkPacket* pkt)
-{
-       infostream << "Server: " << toServerCommandTable[pkt->getCommand()].name
-               << " not supported anymore" << std::endl;
-}
-
-void Server::handleCommand_Init(NetworkPacket* pkt)
-{
-
-       if(pkt->getSize() < 1)
-               return;
-
-       RemoteClient* client = getClient(pkt->getPeerId(), CS_Created);
-
-       std::string addr_s;
-       try {
-               Address address = getPeerAddress(pkt->getPeerId());
-               addr_s = address.serializeString();
-       }
-       catch (con::PeerNotFoundException &e) {
-               /*
-                * no peer for this packet found
-                * most common reason is peer timeout, e.g. peer didn't
-                * respond for some time, your server was overloaded or
-                * things like that.
-                */
-               infostream << "Server::ProcessData(): Canceling: peer "
-                               << pkt->getPeerId() << " not found" << std::endl;
-               return;
-       }
-
-       // If net_proto_version is set, this client has already been handled
-       if (client->getState() > CS_Created) {
-               verbosestream << "Server: Ignoring multiple TOSERVER_INITs from "
-                               << addr_s << " (peer_id=" << pkt->getPeerId() << ")" << std::endl;
-               return;
-       }
-
-       verbosestream << "Server: Got TOSERVER_INIT from " << addr_s << " (peer_id="
-                       << pkt->getPeerId() << ")" << std::endl;
-
-       // Do not allow multiple players in simple singleplayer mode.
-       // This isn't a perfect way to do it, but will suffice for now
-       if (m_simple_singleplayer_mode && m_clients.getClientIDs().size() > 1) {
-               infostream << "Server: Not allowing another client (" << addr_s
-                               << ") to connect in simple singleplayer mode" << std::endl;
-               DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_SINGLEPLAYER);
-               return;
-       }
-
-       // First byte after command is maximum supported
-       // serialization version
-       u8 client_max;
-       u8 compression_modes;
-       u16 min_net_proto_version = 0;
-       u16 max_net_proto_version;
-
-       *pkt >> client_max >> compression_modes >> min_net_proto_version
-                       >> max_net_proto_version;
-
-       u8 our_max = SER_FMT_VER_HIGHEST_READ;
-       // Use the highest version supported by both
-       int deployed = std::min(client_max, our_max);
-       // If it's lower than the lowest supported, give up.
-       if (deployed < SER_FMT_VER_LOWEST)
-               deployed = SER_FMT_VER_INVALID;
-
-       if (deployed == SER_FMT_VER_INVALID) {
-               actionstream << "Server: A mismatched client tried to connect from "
-                               << addr_s << std::endl;
-               infostream<<"Server: Cannot negotiate serialization version with "
-                               << addr_s << std::endl;
-               DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_WRONG_VERSION);
-               return;
-       }
-
-       client->setPendingSerializationVersion(deployed);
-
-       /*
-               Read and check network protocol version
-       */
-
-       u16 net_proto_version = 0;
-
-       // Figure out a working version if it is possible at all
-       if (max_net_proto_version >= SERVER_PROTOCOL_VERSION_MIN ||
-                       min_net_proto_version <= SERVER_PROTOCOL_VERSION_MAX) {
-               // If maximum is larger than our maximum, go with our maximum
-               if (max_net_proto_version > SERVER_PROTOCOL_VERSION_MAX)
-                       net_proto_version = SERVER_PROTOCOL_VERSION_MAX;
-               // Else go with client's maximum
-               else
-                       net_proto_version = max_net_proto_version;
-       }
-
-       verbosestream << "Server: " << addr_s << ": Protocol version: min: "
-                       << min_net_proto_version << ", max: " << max_net_proto_version
-                       << ", chosen: " << net_proto_version << std::endl;
-
-       client->net_proto_version = net_proto_version;
-
-       // On this handler protocol version 25 is required
-       if (net_proto_version < 25 ||
-                       net_proto_version < SERVER_PROTOCOL_VERSION_MIN ||
-                       net_proto_version > SERVER_PROTOCOL_VERSION_MAX) {
-               actionstream << "Server: A mismatched client tried to connect from "
-                               << addr_s << std::endl;
-               DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_WRONG_VERSION);
-               return;
-       }
-
-       if (g_settings->getBool("strict_protocol_version_checking")) {
-               if (net_proto_version != LATEST_PROTOCOL_VERSION) {
-                       actionstream << "Server: A mismatched (strict) client tried to "
-                                       << "connect from " << addr_s << std::endl;
-                       DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_WRONG_VERSION);
-                       return;
-               }
-       }
-
-       // @TODO: check if we support same modes, but not required now
-
-       client->setSupportedCompressionModes(compression_modes);
-
-       m_clients.event(pkt->getPeerId(), CSE_Init);
-}
-
-void Server::handleCommand_Auth(NetworkPacket* pkt)
-{
-       std::string addr_s;
-       try {
-               Address address = getPeerAddress(pkt->getPeerId());
-               addr_s = address.serializeString();
-       }
-       catch (con::PeerNotFoundException &e) {
-               /*
-                * no peer for this packet found
-                * most common reason is peer timeout, e.g. peer didn't
-                * respond for some time, your server was overloaded or
-                * things like that.
-                */
-               infostream << "Server::ProcessData(): Canceling: peer "
-                               << pkt->getPeerId() << " not found" << std::endl;
-               return;
-       }
-
-       std::string playerName, playerPassword;
-
-       *pkt >> playerName >> playerPassword;
-
-       const char* playername = playerName.c_str();
-
-       if (playerName.size() > PLAYERNAME_SIZE) {
-               actionstream << "Server: Player with an too long name "
-                               << "tried to connect from " << addr_s << std::endl;
-               DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_WRONG_NAME);
-               return;
-       }
-
-       if (string_allowed(playerName, PLAYERNAME_ALLOWED_CHARS) == false) {
-               actionstream << "Server: Player with an invalid name "
-                               << "tried to connect from " << addr_s << std::endl;
-               DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_WRONG_CHARS_IN_NAME);
-               return;
-       }
-
-       if (!isSingleplayer() && strcasecmp(playername, "singleplayer") == 0) {
-               actionstream << "Server: Player with the name \"singleplayer\" "
-                               << "tried to connect from " << addr_s << std::endl;
-               DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_WRONG_NAME);
-               return;
-       }
-
-       {
-               std::string reason;
-               if(m_script->on_prejoinplayer(playername, addr_s, reason)) {
-                       actionstream << "Server: Player with the name \"" << playerName << "\" "
-                                       << "tried to connect from " << addr_s << " "
-                                       << "but it was disallowed for the following reason: "
-                                       << reason << std::endl;
-                       DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_CUSTOM_STRING,
-                                       narrow_to_wide(reason.c_str()));
-                       return;
-               }
-       }
-
-       if (playerPassword.size() > PASSWORD_SIZE) {
-               actionstream << "Server: Player with an too long password "
-                               << "tried to connect from " << addr_s << std::endl;
-               DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_WRONG_PASSWORD);
-               return;
-       }
-
-       infostream << "Server: New connection: \"" << playerName << "\" from "
-                       << addr_s << " (peer_id=" << pkt->getPeerId() << ")" << std::endl;
-
-       if(!base64_is_valid(playerPassword)){
-               actionstream << "Server: " << playerName
-                               << " supplied invalid password hash" << std::endl;
-               DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_WRONG_PASSWORD);
-               return;
-       }
-
-       // Enforce user limit.
-       // Don't enforce for users that have some admin right
-       if (m_clients.getClientIDs(CS_Created).size() >= g_settings->getU16("max_users") &&
-                       !checkPriv(playername, "server") &&
-                       !checkPriv(playername, "ban") &&
-                       !checkPriv(playername, "privs") &&
-                       !checkPriv(playername, "password") &&
-                       playername != g_settings->get("name")) {
-               actionstream << "Server: " << playername << " tried to join, but there"
-                               << " are already max_users="
-                               << g_settings->getU16("max_users") << " players." << std::endl;
-               DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_TOO_MANY_USERS);
-               return;
-       }
-
-       std::string checkpwd; // Password hash to check against
-       bool has_auth = m_script->getAuth(playername, &checkpwd, NULL);
-
-       // If no authentication info exists for user, create it
-       if (!has_auth) {
-               if (!isSingleplayer() &&
-                               g_settings->getBool("disallow_empty_password") &&
-                               playerPassword.empty()) {
-                       actionstream << "Server: " << playerName
-                                       << " supplied empty password" << std::endl;
-                       DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_EMPTY_PASSWORD);
-                       return;
-               }
-               std::wstring raw_default_password =
-                       narrow_to_wide(g_settings->get("default_password"));
-               std::string initial_password =
-                       translatePassword(playername, raw_default_password);
-
-               // If default_password is empty, allow any initial password
-               if (raw_default_password.length() == 0)
-                       initial_password = playerPassword.c_str();
-
-               m_script->createAuth(playername, initial_password);
-       }
-
-       has_auth = m_script->getAuth(playername, &checkpwd, NULL);
-
-       if(!has_auth) {
-               actionstream << "Server: " << playerName << " cannot be authenticated"
-                               << " (auth handler does not work?)" << std::endl;
-               DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_SERVER_FAIL);
-               return;
-       }
-
-       if(playerPassword.c_str() != checkpwd) {
-               actionstream << "Server: " << playerName << " supplied wrong password"
-                               << std::endl;
-               DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_WRONG_PASSWORD);
-               return;
-       }
-
-       RemotePlayer *player =
-                       static_cast<RemotePlayer*>(m_env->getPlayer(playername));
-
-       if (player && player->peer_id != 0) {
-               errorstream << "Server: " << playername << ": Failed to emerge player"
-                               << " (player allocated to an another client)" << std::endl;
-               DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_ALREADY_CONNECTED);
-       }
-
-       m_clients.setPlayerName(pkt->getPeerId(), playername);
-
-       /*
-               Answer with a TOCLIENT_INIT
-       */
-
-       NetworkPacket resp_pkt(TOCLIENT_AUTH_ACCEPT, 1 + 6 + 8 + 4, pkt->getPeerId());
-
-       resp_pkt << v3f(0,0,0) << (u64) m_env->getServerMap().getSeed()
-                       << g_settings->getFloat("dedicated_server_step");
-
-       Send(&resp_pkt);
-       m_clients.event(pkt->getPeerId(), CSE_Init);
-}
-
-void Server::handleCommand_Init_Legacy(NetworkPacket* pkt)
-{
-       // [0] u8 SER_FMT_VER_HIGHEST_READ
-       // [1] u8[20] player_name
-       // [21] u8[28] password <--- can be sent without this, from old versions
-
-       if (pkt->getSize() < 1+PLAYERNAME_SIZE)
-               return;
-
-       RemoteClient* client = getClient(pkt->getPeerId(), CS_Created);
-
-       std::string addr_s;
-       try {
-               Address address = getPeerAddress(pkt->getPeerId());
-               addr_s = address.serializeString();
-       }
-       catch (con::PeerNotFoundException &e) {
-               /*
-                * no peer for this packet found
-                * most common reason is peer timeout, e.g. peer didn't
-                * respond for some time, your server was overloaded or
-                * things like that.
-                */
-               infostream << "Server::ProcessData(): Cancelling: peer "
-                               << pkt->getPeerId() << " not found" << std::endl;
-               return;
-       }
-
-       // If net_proto_version is set, this client has already been handled
-       if (client->getState() > CS_Created) {
-               verbosestream << "Server: Ignoring multiple TOSERVER_INITs from "
-                               << addr_s << " (peer_id=" << pkt->getPeerId() << ")" << std::endl;
-               return;
-       }
-
-       verbosestream << "Server: Got TOSERVER_INIT from " << addr_s << " (peer_id="
-                       << pkt->getPeerId() << ")" << std::endl;
-
-       // Do not allow multiple players in simple singleplayer mode.
-       // This isn't a perfect way to do it, but will suffice for now
-       if (m_simple_singleplayer_mode && m_clients.getClientIDs().size() > 1) {
-               infostream << "Server: Not allowing another client (" << addr_s
-                               << ") to connect in simple singleplayer mode" << std::endl;
-               DenyAccess_Legacy(pkt->getPeerId(), L"Running in simple singleplayer mode.");
-               return;
-       }
-
-       // First byte after command is maximum supported
-       // serialization version
-       u8 client_max;
-
-       *pkt >> client_max;
-
-       u8 our_max = SER_FMT_VER_HIGHEST_READ;
-       // Use the highest version supported by both
-       int deployed = std::min(client_max, our_max);
-       // If it's lower than the lowest supported, give up.
-       if (deployed < SER_FMT_VER_LOWEST)
-               deployed = SER_FMT_VER_INVALID;
-
-       if (deployed == SER_FMT_VER_INVALID) {
-               actionstream << "Server: A mismatched client tried to connect from "
-                               << addr_s << std::endl;
-               infostream<<"Server: Cannot negotiate serialization version with "
-                               << addr_s << std::endl;
-               DenyAccess_Legacy(pkt->getPeerId(), std::wstring(
-                               L"Your client's version is not supported.\n"
-                               L"Server version is ")
-                               + narrow_to_wide(minetest_version_simple) + L"."
-               );
-               return;
-       }
-
-       client->setPendingSerializationVersion(deployed);
-
-       /*
-               Read and check network protocol version
-       */
-
-       u16 min_net_proto_version = 0;
-       if (pkt->getSize() >= 1 + PLAYERNAME_SIZE + PASSWORD_SIZE + 2)
-               min_net_proto_version = pkt->getU16(1 + PLAYERNAME_SIZE + PASSWORD_SIZE);
-
-       // Use same version as minimum and maximum if maximum version field
-       // doesn't exist (backwards compatibility)
-       u16 max_net_proto_version = min_net_proto_version;
-       if (pkt->getSize() >= 1 + PLAYERNAME_SIZE + PASSWORD_SIZE + 2 + 2)
-               max_net_proto_version = pkt->getU16(1 + PLAYERNAME_SIZE + PASSWORD_SIZE + 2);
-
-       // Start with client's maximum version
-       u16 net_proto_version = max_net_proto_version;
-
-       // Figure out a working version if it is possible at all
-       if (max_net_proto_version >= SERVER_PROTOCOL_VERSION_MIN ||
-                       min_net_proto_version <= SERVER_PROTOCOL_VERSION_MAX) {
-               // If maximum is larger than our maximum, go with our maximum
-               if (max_net_proto_version > SERVER_PROTOCOL_VERSION_MAX)
-                       net_proto_version = SERVER_PROTOCOL_VERSION_MAX;
-               // Else go with client's maximum
-               else
-                       net_proto_version = max_net_proto_version;
-       }
-
-       verbosestream << "Server: " << addr_s << ": Protocol version: min: "
-                       << min_net_proto_version << ", max: " << max_net_proto_version
-                       << ", chosen: " << net_proto_version << std::endl;
-
-       client->net_proto_version = net_proto_version;
-
-       if (net_proto_version < SERVER_PROTOCOL_VERSION_MIN ||
-                       net_proto_version > SERVER_PROTOCOL_VERSION_MAX) {
-               actionstream << "Server: A mismatched client tried to connect from "
-                               << addr_s << std::endl;
-               DenyAccess_Legacy(pkt->getPeerId(), std::wstring(
-                               L"Your client's version is not supported.\n"
-                               L"Server version is ")
-                               + narrow_to_wide(minetest_version_simple) + L",\n"
-                               + L"server's PROTOCOL_VERSION is "
-                               + narrow_to_wide(itos(SERVER_PROTOCOL_VERSION_MIN))
-                               + L"..."
-                               + narrow_to_wide(itos(SERVER_PROTOCOL_VERSION_MAX))
-                               + L", client's PROTOCOL_VERSION is "
-                               + narrow_to_wide(itos(min_net_proto_version))
-                               + L"..."
-                               + narrow_to_wide(itos(max_net_proto_version))
-               );
-               return;
-       }
-
-       if (g_settings->getBool("strict_protocol_version_checking")) {
-               if (net_proto_version != LATEST_PROTOCOL_VERSION) {
-                       actionstream << "Server: A mismatched (strict) client tried to "
-                                       << "connect from " << addr_s << std::endl;
-                       DenyAccess_Legacy(pkt->getPeerId(), std::wstring(
-                                       L"Your client's version is not supported.\n"
-                                       L"Server version is ")
-                                       + narrow_to_wide(minetest_version_simple) + L",\n"
-                                       + L"server's PROTOCOL_VERSION (strict) is "
-                                       + narrow_to_wide(itos(LATEST_PROTOCOL_VERSION))
-                                       + L", client's PROTOCOL_VERSION is "
-                                       + narrow_to_wide(itos(min_net_proto_version))
-                                       + L"..."
-                                       + narrow_to_wide(itos(max_net_proto_version))
-                       );
-                       return;
-               }
-       }
-
-       /*
-               Set up player
-       */
-       char playername[PLAYERNAME_SIZE];
-       unsigned int playername_length = 0;
-       for (; playername_length < PLAYERNAME_SIZE; playername_length++ ) {
-               playername[playername_length] = pkt->getChar(1+playername_length);
-               if (pkt->getChar(1+playername_length) == 0)
-                       break;
-       }
-
-       if (playername_length == PLAYERNAME_SIZE) {
-               actionstream << "Server: Player with name exceeding max length "
-                               << "tried to connect from " << addr_s << std::endl;
-               DenyAccess_Legacy(pkt->getPeerId(), L"Name too long");
-               return;
-       }
-
-
-       if (playername[0]=='\0') {
-               actionstream << "Server: Player with an empty name "
-                               << "tried to connect from " << addr_s << std::endl;
-               DenyAccess_Legacy(pkt->getPeerId(), L"Empty name");
-               return;
-       }
-
-       if (string_allowed(playername, PLAYERNAME_ALLOWED_CHARS) == false) {
-               actionstream << "Server: Player with an invalid name "
-                               << "tried to connect from " << addr_s << std::endl;
-               DenyAccess_Legacy(pkt->getPeerId(), L"Name contains unallowed characters");
-               return;
-       }
-
-       if (!isSingleplayer() && strcasecmp(playername, "singleplayer") == 0) {
-               actionstream << "Server: Player with the name \"singleplayer\" "
-                               << "tried to connect from " << addr_s << std::endl;
-               DenyAccess_Legacy(pkt->getPeerId(), L"Name is not allowed");
-               return;
-       }
-
-       {
-               std::string reason;
-               if (m_script->on_prejoinplayer(playername, addr_s, reason)) {
-                       actionstream << "Server: Player with the name \"" << playername << "\" "
-                                       << "tried to connect from " << addr_s << " "
-                                       << "but it was disallowed for the following reason: "
-                                       << reason << std::endl;
-                       DenyAccess_Legacy(pkt->getPeerId(), narrow_to_wide(reason.c_str()));
-                       return;
-               }
-       }
-
-       infostream<<"Server: New connection: \""<<playername<<"\" from "
-                       <<addr_s<<" (peer_id="<<pkt->getPeerId()<<")"<<std::endl;
-
-       // Get password
-       char given_password[PASSWORD_SIZE];
-       if (pkt->getSize() < 1 + PLAYERNAME_SIZE + PASSWORD_SIZE) {
-               // old version - assume blank password
-               given_password[0] = 0;
-       }
-       else {
-               for (u16 i = 0; i < PASSWORD_SIZE - 1; i++) {
-                       given_password[i] = pkt->getChar(21 + i);
-               }
-               given_password[PASSWORD_SIZE - 1] = 0;
-       }
-
-       if (!base64_is_valid(given_password)) {
-               actionstream << "Server: " << playername
-                               << " supplied invalid password hash" << std::endl;
-               DenyAccess_Legacy(pkt->getPeerId(), L"Invalid password hash");
-               return;
-       }
-
-       // Enforce user limit.
-       // Don't enforce for users that have some admin right
-       if (m_clients.getClientIDs(CS_Created).size() >= g_settings->getU16("max_users") &&
-                       !checkPriv(playername, "server") &&
-                       !checkPriv(playername, "ban") &&
-                       !checkPriv(playername, "privs") &&
-                       !checkPriv(playername, "password") &&
-                       playername != g_settings->get("name")) {
-               actionstream << "Server: " << playername << " tried to join, but there"
-                               << " are already max_users="
-                               << g_settings->getU16("max_users") << " players." << std::endl;
-               DenyAccess_Legacy(pkt->getPeerId(), L"Too many users.");
-               return;
-       }
-
-       std::string checkpwd; // Password hash to check against
-       bool has_auth = m_script->getAuth(playername, &checkpwd, NULL);
-
-       // If no authentication info exists for user, create it
-       if (!has_auth) {
-               if (!isSingleplayer() &&
-                               g_settings->getBool("disallow_empty_password") &&
-                               std::string(given_password) == "") {
-                       actionstream << "Server: " << playername
-                                       << " supplied empty password" << std::endl;
-                       DenyAccess_Legacy(pkt->getPeerId(), L"Empty passwords are "
-                                       L"disallowed. Set a password and try again.");
-                       return;
-               }
-               std::wstring raw_default_password =
-                       narrow_to_wide(g_settings->get("default_password"));
-               std::string initial_password =
-                       translatePassword(playername, raw_default_password);
-
-               // If default_password is empty, allow any initial password
-               if (raw_default_password.length() == 0)
-                       initial_password = given_password;
-
-               m_script->createAuth(playername, initial_password);
-       }
-
-       has_auth = m_script->getAuth(playername, &checkpwd, NULL);
-
-       if (!has_auth) {
-               actionstream << "Server: " << playername << " cannot be authenticated"
-                               << " (auth handler does not work?)" << std::endl;
-               DenyAccess_Legacy(pkt->getPeerId(), L"Not allowed to login");
-               return;
-       }
-
-       if (given_password != checkpwd) {
-               actionstream << "Server: " << playername << " supplied wrong password"
-                               << std::endl;
-               DenyAccess_Legacy(pkt->getPeerId(), L"Wrong password");
-               return;
-       }
-
-       RemotePlayer *player =
-                       static_cast<RemotePlayer*>(m_env->getPlayer(playername));
-
-       if (player && player->peer_id != 0) {
-               errorstream << "Server: " << playername << ": Failed to emerge player"
-                               << " (player allocated to an another client)" << std::endl;
-               DenyAccess_Legacy(pkt->getPeerId(), L"Another client is connected with this "
-                               L"name. If your client closed unexpectedly, try again in "
-                               L"a minute.");
-       }
-
-       m_clients.setPlayerName(pkt->getPeerId(), playername);
-
-       /*
-               Answer with a TOCLIENT_INIT
-       */
-
-       NetworkPacket resp_pkt(TOCLIENT_INIT_LEGACY, 1 + 6 + 8 + 4,
-                       pkt->getPeerId());
-
-       resp_pkt << (u8) deployed << (v3s16) floatToInt(v3f(0,0,0), BS)
-                       << (u64) m_env->getServerMap().getSeed()
-                       << g_settings->getFloat("dedicated_server_step");
-
-       Send(&resp_pkt);
-       m_clients.event(pkt->getPeerId(), CSE_Init);
-}
-
-void Server::handleCommand_Init2(NetworkPacket* pkt)
-{
-       verbosestream << "Server: Got TOSERVER_INIT2 from "
-                       << pkt->getPeerId() << std::endl;
-
-       m_clients.event(pkt->getPeerId(), CSE_GotInit2);
-       u16 protocol_version = m_clients.getProtocolVersion(pkt->getPeerId());
-
-
-       ///// begin compatibility code
-       PlayerSAO* playersao = NULL;
-       if (protocol_version <= 22) {
-               playersao = StageTwoClientInit(pkt->getPeerId());
-
-               if (playersao == NULL) {
-                       errorstream
-                               << "TOSERVER_INIT2 stage 2 client init failed for peer "
-                               << pkt->getPeerId() << std::endl;
-                       return;
-               }
-       }
-       ///// end compatibility code
-
-       /*
-               Send some initialization data
-       */
-
-       infostream << "Server: Sending content to "
-                       << getPlayerName(pkt->getPeerId()) << std::endl;
-
-       // Send player movement settings
-       SendMovement(pkt->getPeerId());
-
-       // Send item definitions
-       SendItemDef(pkt->getPeerId(), m_itemdef, protocol_version);
-
-       // Send node definitions
-       SendNodeDef(pkt->getPeerId(), m_nodedef, protocol_version);
-
-       m_clients.event(pkt->getPeerId(), CSE_SetDefinitionsSent);
-
-       // Send media announcement
-       sendMediaAnnouncement(pkt->getPeerId());
-
-       // Send detached inventories
-       sendDetachedInventories(pkt->getPeerId());
-
-       // Send time of day
-       u16 time = m_env->getTimeOfDay();
-       float time_speed = g_settings->getFloat("time_speed");
-       SendTimeOfDay(pkt->getPeerId(), time, time_speed);
-
-       ///// begin compatibility code
-       if (protocol_version <= 22) {
-               m_clients.event(pkt->getPeerId(), CSE_SetClientReady);
-               m_script->on_joinplayer(playersao);
-       }
-       ///// end compatibility code
-
-       // Warnings about protocol version can be issued here
-       if (getClient(pkt->getPeerId())->net_proto_version < LATEST_PROTOCOL_VERSION) {
-               SendChatMessage(pkt->getPeerId(), L"# Server: WARNING: YOUR CLIENT'S "
-                               L"VERSION MAY NOT BE FULLY COMPATIBLE WITH THIS SERVER!");
-       }
-}
-
-void Server::handleCommand_RequestMedia(NetworkPacket* pkt)
-{
-       std::vector<std::string> tosend;
-       u16 numfiles;
-
-       *pkt >> numfiles;
-
-       infostream << "Sending " << numfiles << " files to "
-                       << getPlayerName(pkt->getPeerId()) << std::endl;
-       verbosestream << "TOSERVER_REQUEST_MEDIA: " << std::endl;
-
-       for (u16 i = 0; i < numfiles; i++) {
-               std::string name;
-
-               *pkt >> name;
-
-               tosend.push_back(name);
-               verbosestream << "TOSERVER_REQUEST_MEDIA: requested file "
-                               << name << std::endl;
-       }
-
-       sendRequestedMedia(pkt->getPeerId(), tosend);
-}
-
-void Server::handleCommand_ReceivedMedia(NetworkPacket* pkt)
-{
-}
-
-void Server::handleCommand_ClientReady(NetworkPacket* pkt)
-{
-       u16 peer_id = pkt->getPeerId();
-       u16 peer_proto_ver = getClient(peer_id, CS_InitDone)->net_proto_version;
-
-       // clients <= protocol version 22 did not send ready message,
-       // they're already initialized
-       if (peer_proto_ver <= 22) {
-               infostream << "Client sent message not expected by a "
-                       << "client using protocol version <= 22,"
-                       << "disconnecing peer_id: " << peer_id << std::endl;
-               m_con.DisconnectPeer(peer_id);
-               return;
-       }
-
-       PlayerSAO* playersao = StageTwoClientInit(peer_id);
-
-       if (playersao == NULL) {
-               errorstream
-                       << "TOSERVER_CLIENT_READY stage 2 client init failed for peer_id: "
-                       << peer_id << std::endl;
-               m_con.DisconnectPeer(peer_id);
-               return;
-       }
-
-
-       if (pkt->getSize() < 8) {
-               errorstream
-                       << "TOSERVER_CLIENT_READY client sent inconsistent data, disconnecting peer_id: "
-                       << peer_id << std::endl;
-               m_con.DisconnectPeer(peer_id);
-               return;
-       }
-
-       u8 major_ver, minor_ver, patch_ver;
-       *pkt >> major_ver >> minor_ver >> patch_ver;
-
-       m_clients.setClientVersion(
-                       peer_id, major_ver, minor_ver, patch_ver,
-                       std::string(pkt->getString(6),(u16) pkt->getU8(4)));
-
-       m_clients.event(peer_id, CSE_SetClientReady);
-       m_script->on_joinplayer(playersao);
-}
-
-void Server::handleCommand_GotBlocks(NetworkPacket* pkt)
-{
-       if (pkt->getSize() < 1)
-               return;
-
-       /*
-               [0] u16 command
-               [2] u8 count
-               [3] v3s16 pos_0
-               [3+6] v3s16 pos_1
-               ...
-       */
-
-       u8 count;
-       *pkt >> count;
-
-       RemoteClient *client = getClient(pkt->getPeerId());
-
-       for (u16 i = 0; i < count; i++) {
-               if ((s16)pkt->getSize() < 1 + (i + 1) * 6)
-                       throw con::InvalidIncomingDataException
-                               ("GOTBLOCKS length is too short");
-               v3s16 p;
-
-               *pkt >> p;
-
-               client->GotBlock(p);
-       }
-}
-
-void Server::handleCommand_PlayerPos(NetworkPacket* pkt)
-{
-       if (pkt->getSize() < 12 + 12 + 4 + 4)
-               return;
-
-       v3s32 ps, ss;
-       s32 f32pitch, f32yaw;
-
-       *pkt >> ps;
-       *pkt >> ss;
-       *pkt >> f32pitch;
-       *pkt >> f32yaw;
-
-       f32 pitch = (f32)f32pitch / 100.0;
-       f32 yaw = (f32)f32yaw / 100.0;
-       u32 keyPressed = 0;
-
-       if (pkt->getSize() >= 12 + 12 + 4 + 4 + 4)
-               *pkt >> keyPressed;
-
-       v3f position((f32)ps.X / 100.0, (f32)ps.Y / 100.0, (f32)ps.Z / 100.0);
-       v3f speed((f32)ss.X / 100.0, (f32)ss.Y / 100.0, (f32)ss.Z / 100.0);
-
-       pitch = modulo360f(pitch);
-       yaw = modulo360f(yaw);
-
-       Player *player = m_env->getPlayer(pkt->getPeerId());
-       if (player == NULL) {
-               errorstream << "Server::ProcessData(): Cancelling: "
-                               "No player for peer_id=" << pkt->getPeerId()
-                               << " disconnecting peer!" << std::endl;
-               m_con.DisconnectPeer(pkt->getPeerId());
-               return;
-       }
-
-       // If player is dead we don't care of this packet
-       if (player->isDead()) {
-               verbosestream << "TOSERVER_PLAYERPOS: " << player->getName()
-                       << " is dead. Ignoring packet";
-               return;
-       }
-
-       PlayerSAO *playersao = player->getPlayerSAO();
-       if (playersao == NULL) {
-               errorstream << "Server::ProcessData(): Cancelling: "
-                               "No player object for peer_id=" << pkt->getPeerId()
-                               << " disconnecting peer!" << std::endl;
-               m_con.DisconnectPeer(pkt->getPeerId());
-               return;
-       }
-
-       player->setPosition(position);
-       player->setSpeed(speed);
-       player->setPitch(pitch);
-       player->setYaw(yaw);
-       player->keyPressed = keyPressed;
-       player->control.up = (keyPressed & 1);
-       player->control.down = (keyPressed & 2);
-       player->control.left = (keyPressed & 4);
-       player->control.right = (keyPressed & 8);
-       player->control.jump = (keyPressed & 16);
-       player->control.aux1 = (keyPressed & 32);
-       player->control.sneak = (keyPressed & 64);
-       player->control.LMB = (keyPressed & 128);
-       player->control.RMB = (keyPressed & 256);
-
-       if (playersao->checkMovementCheat()) {
-               // Call callbacks
-               m_script->on_cheat(playersao, "moved_too_fast");
-               SendMovePlayer(pkt->getPeerId());
-       }
-}
-
-void Server::handleCommand_DeletedBlocks(NetworkPacket* pkt)
-{
-       if (pkt->getSize() < 1)
-               return;
-
-       /*
-               [0] u16 command
-               [2] u8 count
-               [3] v3s16 pos_0
-               [3+6] v3s16 pos_1
-               ...
-       */
-
-       u8 count;
-       *pkt >> count;
-
-       RemoteClient *client = getClient(pkt->getPeerId());
-
-       for (u16 i = 0; i < count; i++) {
-               if ((s16)pkt->getSize() < 1 + (i + 1) * 6)
-                       throw con::InvalidIncomingDataException
-                               ("DELETEDBLOCKS length is too short");
-               v3s16 p;
-               *pkt >> p;
-
-               client->SetBlockNotSent(p);
-       }
-}
-
-void Server::handleCommand_InventoryAction(NetworkPacket* pkt)
-{
-       Player *player = m_env->getPlayer(pkt->getPeerId());
-       if (player == NULL) {
-               errorstream << "Server::ProcessData(): Cancelling: "
-                               "No player for peer_id=" << pkt->getPeerId()
-                               << " disconnecting peer!" << std::endl;
-               m_con.DisconnectPeer(pkt->getPeerId());
-               return;
-       }
-
-       PlayerSAO *playersao = player->getPlayerSAO();
-       if (playersao == NULL) {
-               errorstream << "Server::ProcessData(): Cancelling: "
-                               "No player object for peer_id=" << pkt->getPeerId()
-                               << " disconnecting peer!" << std::endl;
-               m_con.DisconnectPeer(pkt->getPeerId());
-               return;
-       }
-
-       // Strip command and create a stream
-       std::string datastring(pkt->getString(0), pkt->getSize());
-       verbosestream << "TOSERVER_INVENTORY_ACTION: data=" << datastring
-               << std::endl;
-       std::istringstream is(datastring, std::ios_base::binary);
-       // Create an action
-       InventoryAction *a = InventoryAction::deSerialize(is);
-       if (a == NULL) {
-               infostream << "TOSERVER_INVENTORY_ACTION: "
-                               << "InventoryAction::deSerialize() returned NULL"
-                               << std::endl;
-               return;
-       }
-
-       // If something goes wrong, this player is to blame
-       RollbackScopeActor rollback_scope(m_rollback,
-                       std::string("player:")+player->getName());
-
-       /*
-               Note: Always set inventory not sent, to repair cases
-               where the client made a bad prediction.
-       */
-
-       /*
-               Handle restrictions and special cases of the move action
-       */
-       if (a->getType() == IACTION_MOVE) {
-               IMoveAction *ma = (IMoveAction*)a;
-
-               ma->from_inv.applyCurrentPlayer(player->getName());
-               ma->to_inv.applyCurrentPlayer(player->getName());
-
-               setInventoryModified(ma->from_inv);
-               setInventoryModified(ma->to_inv);
-
-               bool from_inv_is_current_player =
-                       (ma->from_inv.type == InventoryLocation::PLAYER) &&
-                       (ma->from_inv.name == player->getName());
-
-               bool to_inv_is_current_player =
-                       (ma->to_inv.type == InventoryLocation::PLAYER) &&
-                       (ma->to_inv.name == player->getName());
-
-               /*
-                       Disable moving items out of craftpreview
-               */
-               if (ma->from_list == "craftpreview") {
-                       infostream << "Ignoring IMoveAction from "
-                                       << (ma->from_inv.dump()) << ":" << ma->from_list
-                                       << " to " << (ma->to_inv.dump()) << ":" << ma->to_list
-                                       << " because src is " << ma->from_list << std::endl;
-                       delete a;
-                       return;
-               }
-
-               /*
-                       Disable moving items into craftresult and craftpreview
-               */
-               if (ma->to_list == "craftpreview" || ma->to_list == "craftresult") {
-                       infostream << "Ignoring IMoveAction from "
-                                       << (ma->from_inv.dump()) << ":" << ma->from_list
-                                       << " to " << (ma->to_inv.dump()) << ":" << ma->to_list
-                                       << " because dst is " << ma->to_list << std::endl;
-                       delete a;
-                       return;
-               }
-
-               // Disallow moving items in elsewhere than player's inventory
-               // if not allowed to interact
-               if (!checkPriv(player->getName(), "interact") &&
-                               (!from_inv_is_current_player ||
-                               !to_inv_is_current_player)) {
-                       infostream << "Cannot move outside of player's inventory: "
-                                       << "No interact privilege" << std::endl;
-                       delete a;
-                       return;
-               }
-       }
-       /*
-               Handle restrictions and special cases of the drop action
-       */
-       else if (a->getType() == IACTION_DROP) {
-               IDropAction *da = (IDropAction*)a;
-
-               da->from_inv.applyCurrentPlayer(player->getName());
-
-               setInventoryModified(da->from_inv);
-
-               /*
-                       Disable dropping items out of craftpreview
-               */
-               if (da->from_list == "craftpreview") {
-                       infostream << "Ignoring IDropAction from "
-                                       << (da->from_inv.dump()) << ":" << da->from_list
-                                       << " because src is " << da->from_list << std::endl;
-                       delete a;
-                       return;
-               }
-
-               // Disallow dropping items if not allowed to interact
-               if (!checkPriv(player->getName(), "interact")) {
-                       delete a;
-                       return;
-               }
-       }
-       /*
-               Handle restrictions and special cases of the craft action
-       */
-       else if (a->getType() == IACTION_CRAFT) {
-               ICraftAction *ca = (ICraftAction*)a;
-
-               ca->craft_inv.applyCurrentPlayer(player->getName());
-
-               setInventoryModified(ca->craft_inv);
-
-               //bool craft_inv_is_current_player =
-               //      (ca->craft_inv.type == InventoryLocation::PLAYER) &&
-               //      (ca->craft_inv.name == player->getName());
-
-               // Disallow crafting if not allowed to interact
-               if (!checkPriv(player->getName(), "interact")) {
-                       infostream << "Cannot craft: "
-                                       << "No interact privilege" << std::endl;
-                       delete a;
-                       return;
-               }
-       }
-
-       // Do the action
-       a->apply(this, playersao, this);
-       // Eat the action
-       delete a;
-}
-
-void Server::handleCommand_ChatMessage(NetworkPacket* pkt)
-{
-       /*
-               u16 command
-               u16 length
-               wstring message
-       */
-       u16 len;
-       *pkt >> len;
-
-       std::wstring message;
-       for (u16 i = 0; i < len; i++) {
-               u16 tmp_wchar;
-               *pkt >> tmp_wchar;
-
-               message += (wchar_t)tmp_wchar;
-       }
-
-       Player *player = m_env->getPlayer(pkt->getPeerId());
-       if (player == NULL) {
-               errorstream << "Server::ProcessData(): Cancelling: "
-                               "No player for peer_id=" << pkt->getPeerId()
-                               << " disconnecting peer!" << std::endl;
-               m_con.DisconnectPeer(pkt->getPeerId());
-               return;
-       }
-
-       // If something goes wrong, this player is to blame
-       RollbackScopeActor rollback_scope(m_rollback,
-                       std::string("player:")+player->getName());
-
-       // Get player name of this client
-       std::wstring name = narrow_to_wide(player->getName());
-
-       // Run script hook
-       bool ate = m_script->on_chat_message(player->getName(),
-                       wide_to_narrow(message));
-       // If script ate the message, don't proceed
-       if (ate)
-               return;
-
-       // Line to send to players
-       std::wstring line;
-       // Whether to send to the player that sent the line
-       bool send_to_sender_only = false;
-
-       // Commands are implemented in Lua, so only catch invalid
-       // commands that were not "eaten" and send an error back
-       if (message[0] == L'/') {
-               message = message.substr(1);
-               send_to_sender_only = true;
-               if (message.length() == 0)
-                       line += L"-!- Empty command";
-               else
-                       line += L"-!- Invalid command: " + str_split(message, L' ')[0];
-       }
-       else {
-               if (checkPriv(player->getName(), "shout")) {
-                       line += L"<";
-                       line += name;
-                       line += L"> ";
-                       line += message;
-               } else {
-                       line += L"-!- You don't have permission to shout.";
-                       send_to_sender_only = true;
-               }
-       }
-
-       if (line != L"")
-       {
-               /*
-                       Send the message to sender
-               */
-               if (send_to_sender_only) {
-                       SendChatMessage(pkt->getPeerId(), line);
-               }
-               /*
-                       Send the message to others
-               */
-               else {
-                       actionstream << "CHAT: " << wide_to_narrow(line)<<std::endl;
-
-                       std::vector<u16> clients = m_clients.getClientIDs();
-
-                       for (std::vector<u16>::iterator i = clients.begin();
-                               i != clients.end(); ++i) {
-                               if (*i != pkt->getPeerId())
-                                       SendChatMessage(*i, line);
-                       }
-               }
-       }
-}
-
-void Server::handleCommand_Damage(NetworkPacket* pkt)
-{
-       u8 damage;
-
-       *pkt >> damage;
-
-       Player *player = m_env->getPlayer(pkt->getPeerId());
-       if (player == NULL) {
-               errorstream << "Server::ProcessData(): Cancelling: "
-                               "No player for peer_id=" << pkt->getPeerId()
-                               << " disconnecting peer!" << std::endl;
-               m_con.DisconnectPeer(pkt->getPeerId());
-               return;
-       }
-
-       PlayerSAO *playersao = player->getPlayerSAO();
-       if (playersao == NULL) {
-               errorstream << "Server::ProcessData(): Cancelling: "
-                               "No player object for peer_id=" << pkt->getPeerId()
-                               << " disconnecting peer!" << std::endl;
-               m_con.DisconnectPeer(pkt->getPeerId());
-               return;
-       }
-
-       if (g_settings->getBool("enable_damage")) {
-               actionstream << player->getName() << " damaged by "
-                               << (int)damage << " hp at " << PP(player->getPosition() / BS)
-                               << std::endl;
-
-               playersao->setHP(playersao->getHP() - damage);
-               SendPlayerHPOrDie(playersao->getPeerID(), playersao->getHP() == 0);
-       }
-}
-
-void Server::handleCommand_Breath(NetworkPacket* pkt)
-{
-       u16 breath;
-
-       *pkt >> breath;
-
-       Player *player = m_env->getPlayer(pkt->getPeerId());
-       if (player == NULL) {
-               errorstream << "Server::ProcessData(): Cancelling: "
-                               "No player for peer_id=" << pkt->getPeerId()
-                               << " disconnecting peer!" << std::endl;
-               m_con.DisconnectPeer(pkt->getPeerId());
-               return;
-       }
-
-       /*
-        * If player is dead, we don't need to update the breath
-        * He is dead !
-        */
-       if (player->isDead()) {
-               verbosestream << "TOSERVER_BREATH: " << player->getName()
-                       << " is dead. Ignoring packet";
-               return;
-       }
-
-
-       PlayerSAO *playersao = player->getPlayerSAO();
-       if (playersao == NULL) {
-               errorstream << "Server::ProcessData(): Cancelling: "
-                               "No player object for peer_id=" << pkt->getPeerId()
-                               << " disconnecting peer!" << std::endl;
-               m_con.DisconnectPeer(pkt->getPeerId());
-               return;
-       }
-
-       playersao->setBreath(breath);
-       SendPlayerBreath(pkt->getPeerId());
-}
-
-void Server::handleCommand_Password(NetworkPacket* pkt)
-{
-       errorstream << "PAssword packet size: " << pkt->getSize() << " size required: " << PASSWORD_SIZE * 2 << std::endl;
-       if ((pkt->getCommand() == TOSERVER_PASSWORD && pkt->getSize() < 4) ||
-                       pkt->getSize() != PASSWORD_SIZE * 2)
-               return;
-
-       std::string oldpwd;
-       std::string newpwd;
-
-       if (pkt->getCommand() == TOSERVER_PASSWORD) {
-               *pkt >> oldpwd >> newpwd;
-       }
-       // 13/03/15
-       // Protocol v24 compat. Please remove in 1 year after
-       // client convergence to 0.4.13/0.5.x
-       else {
-               for (u16 i = 0; i < PASSWORD_SIZE - 1; i++) {
-                       char c = pkt->getChar(i);
-                       if (c == 0)
-                               break;
-                       oldpwd += c;
-               }
-
-               for (u16 i = 0; i < PASSWORD_SIZE - 1; i++) {
-                       char c = pkt->getChar(PASSWORD_SIZE + i);
-                       if (c == 0)
-                               break;
-                       newpwd += c;
-               }
-       }
-
-       Player *player = m_env->getPlayer(pkt->getPeerId());
-       if (player == NULL) {
-               errorstream << "Server::ProcessData(): Cancelling: "
-                               "No player for peer_id=" << pkt->getPeerId()
-                               << " disconnecting peer!" << std::endl;
-               m_con.DisconnectPeer(pkt->getPeerId());
-               return;
-       }
-
-       if (!base64_is_valid(newpwd)) {
-               infostream<<"Server: " << player->getName() <<
-                               " supplied invalid password hash" << std::endl;
-               // Wrong old password supplied!!
-               SendChatMessage(pkt->getPeerId(), L"Invalid new password hash supplied. Password NOT changed.");
-               return;
-       }
-
-       infostream << "Server: Client requests a password change from "
-                       << "'" << oldpwd << "' to '" << newpwd << "'" << std::endl;
-
-       std::string playername = player->getName();
-
-       std::string checkpwd;
-       m_script->getAuth(playername, &checkpwd, NULL);
-
-       if (oldpwd != checkpwd) {
-               infostream << "Server: invalid old password" << std::endl;
-               // Wrong old password supplied!!
-               SendChatMessage(pkt->getPeerId(), L"Invalid old password supplied. Password NOT changed.");
-               return;
-       }
-
-       bool success = m_script->setPassword(playername, newpwd);
-       if (success) {
-               actionstream << player->getName() << " changes password" << std::endl;
-               SendChatMessage(pkt->getPeerId(), L"Password change successful.");
-       } else {
-               actionstream << player->getName() << " tries to change password but "
-                               << "it fails" << std::endl;
-               SendChatMessage(pkt->getPeerId(), L"Password change failed or inavailable.");
-       }
-}
-
-void Server::handleCommand_PlayerItem(NetworkPacket* pkt)
-{
-       if (pkt->getSize() < 2)
-               return;
-
-       Player *player = m_env->getPlayer(pkt->getPeerId());
-       if (player == NULL) {
-               errorstream << "Server::ProcessData(): Cancelling: "
-                               "No player for peer_id=" << pkt->getPeerId()
-                               << " disconnecting peer!" << std::endl;
-               m_con.DisconnectPeer(pkt->getPeerId());
-               return;
-       }
-
-       PlayerSAO *playersao = player->getPlayerSAO();
-       if (playersao == NULL) {
-               errorstream << "Server::ProcessData(): Cancelling: "
-                               "No player object for peer_id=" << pkt->getPeerId()
-                               << " disconnecting peer!" << std::endl;
-               m_con.DisconnectPeer(pkt->getPeerId());
-               return;
-       }
-
-       u16 item;
-
-       *pkt >> item;
-
-       playersao->setWieldIndex(item);
-}
-
-void Server::handleCommand_Respawn(NetworkPacket* pkt)
-{
-       Player *player = m_env->getPlayer(pkt->getPeerId());
-       if (player == NULL) {
-               errorstream << "Server::ProcessData(): Cancelling: "
-                               "No player for peer_id=" << pkt->getPeerId()
-                               << " disconnecting peer!" << std::endl;
-               m_con.DisconnectPeer(pkt->getPeerId());
-               return;
-       }
-
-       if (!player->isDead())
-               return;
-
-       RespawnPlayer(pkt->getPeerId());
-
-       actionstream << player->getName() << " respawns at "
-                       << PP(player->getPosition()/BS) << std::endl;
-
-       // ActiveObject is added to environment in AsyncRunStep after
-       // the previous addition has been succesfully removed
-}
-
-void Server::handleCommand_Interact(NetworkPacket* pkt)
-{
-       std::string datastring(pkt->getString(0), pkt->getSize());
-       std::istringstream is(datastring, std::ios_base::binary);
-
-       /*
-               [0] u16 command
-               [2] u8 action
-               [3] u16 item
-               [5] u32 length of the next item
-               [9] serialized PointedThing
-               actions:
-               0: start digging (from undersurface) or use
-               1: stop digging (all parameters ignored)
-               2: digging completed
-               3: place block or item (to abovesurface)
-               4: use item
-       */
-       u8 action = readU8(is);
-       u16 item_i = readU16(is);
-       std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
-       PointedThing pointed;
-       pointed.deSerialize(tmp_is);
-
-       verbosestream << "TOSERVER_INTERACT: action=" << (int)action << ", item="
-                       << item_i << ", pointed=" << pointed.dump() << std::endl;
-
-       Player *player = m_env->getPlayer(pkt->getPeerId());
-       if (player == NULL) {
-               errorstream << "Server::ProcessData(): Cancelling: "
-                               "No player for peer_id=" << pkt->getPeerId()
-                               << " disconnecting peer!" << std::endl;
-               m_con.DisconnectPeer(pkt->getPeerId());
-               return;
-       }
-
-       PlayerSAO *playersao = player->getPlayerSAO();
-       if (playersao == NULL) {
-               errorstream << "Server::ProcessData(): Cancelling: "
-                               "No player object for peer_id=" << pkt->getPeerId()
-                               << " disconnecting peer!" << std::endl;
-               m_con.DisconnectPeer(pkt->getPeerId());
-               return;
-       }
-
-       if (player->isDead()) {
-               verbosestream << "TOSERVER_INTERACT: " << player->getName()
-                       << " is dead. Ignoring packet";
-               return;
-       }
-
-       v3f player_pos = playersao->getLastGoodPosition();
-
-       // Update wielded item
-       playersao->setWieldIndex(item_i);
-
-       // Get pointed to node (undefined if not POINTEDTYPE_NODE)
-       v3s16 p_under = pointed.node_undersurface;
-       v3s16 p_above = pointed.node_abovesurface;
-
-       // Get pointed to object (NULL if not POINTEDTYPE_OBJECT)
-       ServerActiveObject *pointed_object = NULL;
-       if (pointed.type == POINTEDTHING_OBJECT) {
-               pointed_object = m_env->getActiveObject(pointed.object_id);
-               if (pointed_object == NULL) {
-                       verbosestream << "TOSERVER_INTERACT: "
-                               "pointed object is NULL" << std::endl;
-                       return;
-               }
-
-       }
-
-       v3f pointed_pos_under = player_pos;
-       v3f pointed_pos_above = player_pos;
-       if (pointed.type == POINTEDTHING_NODE) {
-               pointed_pos_under = intToFloat(p_under, BS);
-               pointed_pos_above = intToFloat(p_above, BS);
-       }
-       else if (pointed.type == POINTEDTHING_OBJECT) {
-               pointed_pos_under = pointed_object->getBasePosition();
-               pointed_pos_above = pointed_pos_under;
-       }
-
-       /*
-               Check that target is reasonably close
-               (only when digging or placing things)
-       */
-       if (action == 0 || action == 2 || action == 3) {
-               float d = player_pos.getDistanceFrom(pointed_pos_under);
-               float max_d = BS * 14; // Just some large enough value
-               if (d > max_d) {
-                       actionstream << "Player " << player->getName()
-                                       << " tried to access " << pointed.dump()
-                                       << " from too far: "
-                                       << "d=" << d <<", max_d=" << max_d
-                                       << ". ignoring." << std::endl;
-                       // Re-send block to revert change on client-side
-                       RemoteClient *client = getClient(pkt->getPeerId());
-                       v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
-                       client->SetBlockNotSent(blockpos);
-                       // Call callbacks
-                       m_script->on_cheat(playersao, "interacted_too_far");
-                       // Do nothing else
-                       return;
-               }
-       }
-
-       /*
-               Make sure the player is allowed to do it
-       */
-       if (!checkPriv(player->getName(), "interact")) {
-               actionstream<<player->getName()<<" attempted to interact with "
-                               <<pointed.dump()<<" without 'interact' privilege"
-                               <<std::endl;
-               // Re-send block to revert change on client-side
-               RemoteClient *client = getClient(pkt->getPeerId());
-               // Digging completed -> under
-               if (action == 2) {
-                       v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
-                       client->SetBlockNotSent(blockpos);
-               }
-               // Placement -> above
-               if (action == 3) {
-                       v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS));
-                       client->SetBlockNotSent(blockpos);
-               }
-               return;
-       }
-
-       /*
-               If something goes wrong, this player is to blame
-       */
-       RollbackScopeActor rollback_scope(m_rollback,
-                       std::string("player:")+player->getName());
-
-       /*
-               0: start digging or punch object
-       */
-       if (action == 0) {
-               if (pointed.type == POINTEDTHING_NODE) {
-                       /*
-                               NOTE: This can be used in the future to check if
-                               somebody is cheating, by checking the timing.
-                       */
-                       MapNode n(CONTENT_IGNORE);
-                       bool pos_ok;
-                       n = m_env->getMap().getNodeNoEx(p_under, &pos_ok);
-                       if (pos_ok)
-                               n = m_env->getMap().getNodeNoEx(p_under, &pos_ok);
-
-                       if (!pos_ok) {
-                               infostream << "Server: Not punching: Node not found."
-                                               << " Adding block to emerge queue."
-                                               << std::endl;
-                               m_emerge->enqueueBlockEmerge(pkt->getPeerId(), getNodeBlockPos(p_above), false);
-                       }
-
-                       if (n.getContent() != CONTENT_IGNORE)
-                               m_script->node_on_punch(p_under, n, playersao, pointed);
-                       // Cheat prevention
-                       playersao->noCheatDigStart(p_under);
-               }
-               else if (pointed.type == POINTEDTHING_OBJECT) {
-                       // Skip if object has been removed
-                       if (pointed_object->m_removed)
-                               return;
-
-                       actionstream<<player->getName()<<" punches object "
-                                       <<pointed.object_id<<": "
-                                       <<pointed_object->getDescription()<<std::endl;
-
-                       ItemStack punchitem = playersao->getWieldedItem();
-                       ToolCapabilities toolcap =
-                                       punchitem.getToolCapabilities(m_itemdef);
-                       v3f dir = (pointed_object->getBasePosition() -
-                                       (player->getPosition() + player->getEyeOffset())
-                                               ).normalize();
-                       float time_from_last_punch =
-                               playersao->resetTimeFromLastPunch();
-
-                       s16 src_original_hp = pointed_object->getHP();
-                       s16 dst_origin_hp = playersao->getHP();
-
-                       pointed_object->punch(dir, &toolcap, playersao,
-                                       time_from_last_punch);
-
-                       // If the object is a player and its HP changed
-                       if (src_original_hp != pointed_object->getHP() &&
-                                       pointed_object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
-                               SendPlayerHPOrDie(((PlayerSAO*)pointed_object)->getPeerID(),
-                                               pointed_object->getHP() == 0);
-                       }
-
-                       // If the puncher is a player and its HP changed
-                       if (dst_origin_hp != playersao->getHP()) {
-                               SendPlayerHPOrDie(playersao->getPeerID(), playersao->getHP() == 0);
-                       }
-               }
-
-       } // action == 0
-
-       /*
-               1: stop digging
-       */
-       else if (action == 1) {
-       } // action == 1
-
-       /*
-               2: Digging completed
-       */
-       else if (action == 2) {
-               // Only digging of nodes
-               if (pointed.type == POINTEDTHING_NODE) {
-                       bool pos_ok;
-                       MapNode n = m_env->getMap().getNodeNoEx(p_under, &pos_ok);
-                       if (!pos_ok) {
-                               infostream << "Server: Not finishing digging: Node not found."
-                                                  << " Adding block to emerge queue."
-                                                  << std::endl;
-                               m_emerge->enqueueBlockEmerge(pkt->getPeerId(), getNodeBlockPos(p_above), false);
-                       }
-
-                       /* Cheat prevention */
-                       bool is_valid_dig = true;
-                       if (!isSingleplayer() && !g_settings->getBool("disable_anticheat")) {
-                               v3s16 nocheat_p = playersao->getNoCheatDigPos();
-                               float nocheat_t = playersao->getNoCheatDigTime();
-                               playersao->noCheatDigEnd();
-                               // If player didn't start digging this, ignore dig
-                               if (nocheat_p != p_under) {
-                                       infostream << "Server: NoCheat: " << player->getName()
-                                                       << " started digging "
-                                                       << PP(nocheat_p) << " and completed digging "
-                                                       << PP(p_under) << "; not digging." << std::endl;
-                                       is_valid_dig = false;
-                                       // Call callbacks
-                                       m_script->on_cheat(playersao, "finished_unknown_dig");
-                               }
-                               // Get player's wielded item
-                               ItemStack playeritem;
-                               InventoryList *mlist = playersao->getInventory()->getList("main");
-                               if (mlist != NULL)
-                                       playeritem = mlist->getItem(playersao->getWieldIndex());
-                               ToolCapabilities playeritem_toolcap =
-                                               playeritem.getToolCapabilities(m_itemdef);
-                               // Get diggability and expected digging time
-                               DigParams params = getDigParams(m_nodedef->get(n).groups,
-                                               &playeritem_toolcap);
-                               // If can't dig, try hand
-                               if (!params.diggable) {
-                                       const ItemDefinition &hand = m_itemdef->get("");
-                                       const ToolCapabilities *tp = hand.tool_capabilities;
-                                       if (tp)
-                                               params = getDigParams(m_nodedef->get(n).groups, tp);
-                               }
-                               // If can't dig, ignore dig
-                               if (!params.diggable) {
-                                       infostream << "Server: NoCheat: " << player->getName()
-                                                       << " completed digging " << PP(p_under)
-                                                       << ", which is not diggable with tool. not digging."
-                                                       << std::endl;
-                                       is_valid_dig = false;
-                                       // Call callbacks
-                                       m_script->on_cheat(playersao, "dug_unbreakable");
-                               }
-                               // Check digging time
-                               // If already invalidated, we don't have to
-                               if (!is_valid_dig) {
-                                       // Well not our problem then
-                               }
-                               // Clean and long dig
-                               else if (params.time > 2.0 && nocheat_t * 1.2 > params.time) {
-                                       // All is good, but grab time from pool; don't care if
-                                       // it's actually available
-                                       playersao->getDigPool().grab(params.time);
-                               }
-                               // Short or laggy dig
-                               // Try getting the time from pool
-                               else if (playersao->getDigPool().grab(params.time)) {
-                                       // All is good
-                               }
-                               // Dig not possible
-                               else {
-                                       infostream << "Server: NoCheat: " << player->getName()
-                                                       << " completed digging " << PP(p_under)
-                                                       << "too fast; not digging." << std::endl;
-                                       is_valid_dig = false;
-                                       // Call callbacks
-                                       m_script->on_cheat(playersao, "dug_too_fast");
-                               }
-                       }
-
-                       /* Actually dig node */
-
-                       if (is_valid_dig && n.getContent() != CONTENT_IGNORE)
-                               m_script->node_on_dig(p_under, n, playersao);
-
-                       v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
-                       RemoteClient *client = getClient(pkt->getPeerId());
-                       // Send unusual result (that is, node not being removed)
-                       if (m_env->getMap().getNodeNoEx(p_under).getContent() != CONTENT_AIR) {
-                               // Re-send block to revert change on client-side
-                               client->SetBlockNotSent(blockpos);
-                       }
-                       else {
-                               client->ResendBlockIfOnWire(blockpos);
-                       }
-               }
-       } // action == 2
-
-       /*
-               3: place block or right-click object
-       */
-       else if (action == 3) {
-               ItemStack item = playersao->getWieldedItem();
-
-               // Reset build time counter
-               if (pointed.type == POINTEDTHING_NODE &&
-                               item.getDefinition(m_itemdef).type == ITEM_NODE)
-                       getClient(pkt->getPeerId())->m_time_from_building = 0.0;
-
-               if (pointed.type == POINTEDTHING_OBJECT) {
-                       // Right click object
-
-                       // Skip if object has been removed
-                       if (pointed_object->m_removed)
-                               return;
-
-                       actionstream << player->getName() << " right-clicks object "
-                                       << pointed.object_id << ": "
-                                       << pointed_object->getDescription() << std::endl;
-
-                       // Do stuff
-                       pointed_object->rightClick(playersao);
-               }
-               else if (m_script->item_OnPlace(
-                               item, playersao, pointed)) {
-                       // Placement was handled in lua
-
-                       // Apply returned ItemStack
-                       if (playersao->setWieldedItem(item)) {
-                               SendInventory(playersao);
-                       }
-               }
-
-               // If item has node placement prediction, always send the
-               // blocks to make sure the client knows what exactly happened
-               RemoteClient *client = getClient(pkt->getPeerId());
-               v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS));
-               v3s16 blockpos2 = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
-               if (item.getDefinition(m_itemdef).node_placement_prediction != "") {
-                       client->SetBlockNotSent(blockpos);
-                       if (blockpos2 != blockpos) {
-                               client->SetBlockNotSent(blockpos2);
-                       }
-               }
-               else {
-                       client->ResendBlockIfOnWire(blockpos);
-                       if (blockpos2 != blockpos) {
-                               client->ResendBlockIfOnWire(blockpos2);
-                       }
-               }
-       } // action == 3
-
-       /*
-               4: use
-       */
-       else if (action == 4) {
-               ItemStack item = playersao->getWieldedItem();
-
-               actionstream << player->getName() << " uses " << item.name
-                               << ", pointing at " << pointed.dump() << std::endl;
-
-               if (m_script->item_OnUse(
-                               item, playersao, pointed)) {
-                       // Apply returned ItemStack
-                       playersao->setWieldedItem(item);
-               }
-
-       } // action == 4
-
-
-       /*
-               Catch invalid actions
-       */
-       else {
-               infostream << "WARNING: Server: Invalid action "
-                               << action << std::endl;
-       }
-}
-
-void Server::handleCommand_RemovedSounds(NetworkPacket* pkt)
-{
-       u16 num;
-       *pkt >> num;
-       for (u16 k = 0; k < num; k++) {
-               s32 id;
-
-               *pkt >> id;
-
-               std::map<s32, ServerPlayingSound>::iterator i =
-                       m_playing_sounds.find(id);
-
-               if (i == m_playing_sounds.end())
-                       continue;
-
-               ServerPlayingSound &psound = i->second;
-               psound.clients.erase(pkt->getPeerId());
-               if (psound.clients.empty())
-                       m_playing_sounds.erase(i++);
-       }
-}
-
-void Server::handleCommand_NodeMetaFields(NetworkPacket* pkt)
-{
-       v3s16 p;
-       std::string formname;
-       u16 num;
-
-       *pkt >> p >> formname >> num;
-
-       std::map<std::string, std::string> fields;
-       for (u16 k = 0; k < num; k++) {
-               std::string fieldname;
-               *pkt >> fieldname;
-               fields[fieldname] = pkt->readLongString();
-       }
-
-       Player *player = m_env->getPlayer(pkt->getPeerId());
-       if (player == NULL) {
-               errorstream << "Server::ProcessData(): Cancelling: "
-                               "No player for peer_id=" << pkt->getPeerId()
-                               << " disconnecting peer!" << std::endl;
-               m_con.DisconnectPeer(pkt->getPeerId());
-               return;
-       }
-
-       PlayerSAO *playersao = player->getPlayerSAO();
-       if (playersao == NULL) {
-               errorstream << "Server::ProcessData(): Cancelling: "
-                               "No player object for peer_id=" << pkt->getPeerId()
-                               << " disconnecting peer!"  << std::endl;
-               m_con.DisconnectPeer(pkt->getPeerId());
-               return;
-       }
-
-       // If something goes wrong, this player is to blame
-       RollbackScopeActor rollback_scope(m_rollback,
-                       std::string("player:")+player->getName());
-
-       // Check the target node for rollback data; leave others unnoticed
-       RollbackNode rn_old(&m_env->getMap(), p, this);
-
-       m_script->node_on_receive_fields(p, formname, fields, playersao);
-
-       // Report rollback data
-       RollbackNode rn_new(&m_env->getMap(), p, this);
-       if (rollback() && rn_new != rn_old) {
-               RollbackAction action;
-               action.setSetNode(p, rn_old, rn_new);
-               rollback()->reportAction(action);
-       }
-}
-
-void Server::handleCommand_InventoryFields(NetworkPacket* pkt)
-{
-       std::string formname;
-       u16 num;
-
-       *pkt >> formname >> num;
-
-       std::map<std::string, std::string> fields;
-       for (u16 k = 0; k < num; k++) {
-               std::string fieldname;
-               *pkt >> fieldname;
-               fields[fieldname] = pkt->readLongString();
-       }
-
-       Player *player = m_env->getPlayer(pkt->getPeerId());
-       if (player == NULL) {
-               errorstream << "Server::ProcessData(): Canceling: "
-                               "No player for peer_id=" << pkt->getPeerId()
-                               << " disconnecting peer!" << std::endl;
-               m_con.DisconnectPeer(pkt->getPeerId());
-               return;
-       }
-
-       PlayerSAO *playersao = player->getPlayerSAO();
-       if (playersao == NULL) {
-               errorstream << "Server::ProcessData(): Canceling: "
-                               "No player object for peer_id=" << pkt->getPeerId()
-                               << " disconnecting peer!" << std::endl;
-               m_con.DisconnectPeer(pkt->getPeerId());
-               return;
-       }
-
-       m_script->on_playerReceiveFields(playersao, formname, fields);
-}
diff --git a/src/network/serverpackethandler.cpp b/src/network/serverpackethandler.cpp
new file mode 100644 (file)
index 0000000..bb3a534
--- /dev/null
@@ -0,0 +1,1847 @@
+/*
+Minetest
+Copyright (C) 2015 nerzhul, Loic Blot <loic.blot@unix-experience.fr>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "server.h"
+#include "log.h"
+
+#include "content_abm.h"
+#include "content_sao.h"
+#include "emerge.h"
+#include "main.h"
+#include "nodedef.h"
+#include "player.h"
+#include "rollback_interface.h"
+#include "scripting_game.h"
+#include "settings.h"
+#include "tool.h"
+#include "version.h"
+#include "network/networkprotocol.h"
+#include "network/serveropcodes.h"
+#include "util/base64.h"
+#include "util/pointedthing.h"
+#include "util/serialize.h"
+
+void Server::handleCommand_Deprecated(NetworkPacket* pkt)
+{
+       infostream << "Server: " << toServerCommandTable[pkt->getCommand()].name
+               << " not supported anymore" << std::endl;
+}
+
+void Server::handleCommand_Init(NetworkPacket* pkt)
+{
+
+       if(pkt->getSize() < 1)
+               return;
+
+       RemoteClient* client = getClient(pkt->getPeerId(), CS_Created);
+
+       std::string addr_s;
+       try {
+               Address address = getPeerAddress(pkt->getPeerId());
+               addr_s = address.serializeString();
+       }
+       catch (con::PeerNotFoundException &e) {
+               /*
+                * no peer for this packet found
+                * most common reason is peer timeout, e.g. peer didn't
+                * respond for some time, your server was overloaded or
+                * things like that.
+                */
+               infostream << "Server::ProcessData(): Canceling: peer "
+                               << pkt->getPeerId() << " not found" << std::endl;
+               return;
+       }
+
+       // If net_proto_version is set, this client has already been handled
+       if (client->getState() > CS_Created) {
+               verbosestream << "Server: Ignoring multiple TOSERVER_INITs from "
+                               << addr_s << " (peer_id=" << pkt->getPeerId() << ")" << std::endl;
+               return;
+       }
+
+       verbosestream << "Server: Got TOSERVER_INIT from " << addr_s << " (peer_id="
+                       << pkt->getPeerId() << ")" << std::endl;
+
+       // Do not allow multiple players in simple singleplayer mode.
+       // This isn't a perfect way to do it, but will suffice for now
+       if (m_simple_singleplayer_mode && m_clients.getClientIDs().size() > 1) {
+               infostream << "Server: Not allowing another client (" << addr_s
+                               << ") to connect in simple singleplayer mode" << std::endl;
+               DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_SINGLEPLAYER);
+               return;
+       }
+
+       // First byte after command is maximum supported
+       // serialization version
+       u8 client_max;
+       u8 compression_modes;
+       u16 min_net_proto_version = 0;
+       u16 max_net_proto_version;
+
+       *pkt >> client_max >> compression_modes >> min_net_proto_version
+                       >> max_net_proto_version;
+
+       u8 our_max = SER_FMT_VER_HIGHEST_READ;
+       // Use the highest version supported by both
+       int deployed = std::min(client_max, our_max);
+       // If it's lower than the lowest supported, give up.
+       if (deployed < SER_FMT_VER_LOWEST)
+               deployed = SER_FMT_VER_INVALID;
+
+       if (deployed == SER_FMT_VER_INVALID) {
+               actionstream << "Server: A mismatched client tried to connect from "
+                               << addr_s << std::endl;
+               infostream<<"Server: Cannot negotiate serialization version with "
+                               << addr_s << std::endl;
+               DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_WRONG_VERSION);
+               return;
+       }
+
+       client->setPendingSerializationVersion(deployed);
+
+       /*
+               Read and check network protocol version
+       */
+
+       u16 net_proto_version = 0;
+
+       // Figure out a working version if it is possible at all
+       if (max_net_proto_version >= SERVER_PROTOCOL_VERSION_MIN ||
+                       min_net_proto_version <= SERVER_PROTOCOL_VERSION_MAX) {
+               // If maximum is larger than our maximum, go with our maximum
+               if (max_net_proto_version > SERVER_PROTOCOL_VERSION_MAX)
+                       net_proto_version = SERVER_PROTOCOL_VERSION_MAX;
+               // Else go with client's maximum
+               else
+                       net_proto_version = max_net_proto_version;
+       }
+
+       verbosestream << "Server: " << addr_s << ": Protocol version: min: "
+                       << min_net_proto_version << ", max: " << max_net_proto_version
+                       << ", chosen: " << net_proto_version << std::endl;
+
+       client->net_proto_version = net_proto_version;
+
+       // On this handler protocol version 25 is required
+       if (net_proto_version < 25 ||
+                       net_proto_version < SERVER_PROTOCOL_VERSION_MIN ||
+                       net_proto_version > SERVER_PROTOCOL_VERSION_MAX) {
+               actionstream << "Server: A mismatched client tried to connect from "
+                               << addr_s << std::endl;
+               DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_WRONG_VERSION);
+               return;
+       }
+
+       if (g_settings->getBool("strict_protocol_version_checking")) {
+               if (net_proto_version != LATEST_PROTOCOL_VERSION) {
+                       actionstream << "Server: A mismatched (strict) client tried to "
+                                       << "connect from " << addr_s << std::endl;
+                       DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_WRONG_VERSION);
+                       return;
+               }
+       }
+
+       // @TODO: check if we support same modes, but not required now
+
+       client->setSupportedCompressionModes(compression_modes);
+
+       m_clients.event(pkt->getPeerId(), CSE_Init);
+}
+
+void Server::handleCommand_Auth(NetworkPacket* pkt)
+{
+       std::string addr_s;
+       try {
+               Address address = getPeerAddress(pkt->getPeerId());
+               addr_s = address.serializeString();
+       }
+       catch (con::PeerNotFoundException &e) {
+               /*
+                * no peer for this packet found
+                * most common reason is peer timeout, e.g. peer didn't
+                * respond for some time, your server was overloaded or
+                * things like that.
+                */
+               infostream << "Server::ProcessData(): Canceling: peer "
+                               << pkt->getPeerId() << " not found" << std::endl;
+               return;
+       }
+
+       std::string playerName, playerPassword;
+
+       *pkt >> playerName >> playerPassword;
+
+       const char* playername = playerName.c_str();
+
+       if (playerName.size() > PLAYERNAME_SIZE) {
+               actionstream << "Server: Player with an too long name "
+                               << "tried to connect from " << addr_s << std::endl;
+               DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_WRONG_NAME);
+               return;
+       }
+
+       if (string_allowed(playerName, PLAYERNAME_ALLOWED_CHARS) == false) {
+               actionstream << "Server: Player with an invalid name "
+                               << "tried to connect from " << addr_s << std::endl;
+               DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_WRONG_CHARS_IN_NAME);
+               return;
+       }
+
+       if (!isSingleplayer() && strcasecmp(playername, "singleplayer") == 0) {
+               actionstream << "Server: Player with the name \"singleplayer\" "
+                               << "tried to connect from " << addr_s << std::endl;
+               DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_WRONG_NAME);
+               return;
+       }
+
+       {
+               std::string reason;
+               if(m_script->on_prejoinplayer(playername, addr_s, reason)) {
+                       actionstream << "Server: Player with the name \"" << playerName << "\" "
+                                       << "tried to connect from " << addr_s << " "
+                                       << "but it was disallowed for the following reason: "
+                                       << reason << std::endl;
+                       DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_CUSTOM_STRING,
+                                       narrow_to_wide(reason.c_str()));
+                       return;
+               }
+       }
+
+       if (playerPassword.size() > PASSWORD_SIZE) {
+               actionstream << "Server: Player with an too long password "
+                               << "tried to connect from " << addr_s << std::endl;
+               DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_WRONG_PASSWORD);
+               return;
+       }
+
+       infostream << "Server: New connection: \"" << playerName << "\" from "
+                       << addr_s << " (peer_id=" << pkt->getPeerId() << ")" << std::endl;
+
+       if(!base64_is_valid(playerPassword)){
+               actionstream << "Server: " << playerName
+                               << " supplied invalid password hash" << std::endl;
+               DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_WRONG_PASSWORD);
+               return;
+       }
+
+       // Enforce user limit.
+       // Don't enforce for users that have some admin right
+       if (m_clients.getClientIDs(CS_Created).size() >= g_settings->getU16("max_users") &&
+                       !checkPriv(playername, "server") &&
+                       !checkPriv(playername, "ban") &&
+                       !checkPriv(playername, "privs") &&
+                       !checkPriv(playername, "password") &&
+                       playername != g_settings->get("name")) {
+               actionstream << "Server: " << playername << " tried to join, but there"
+                               << " are already max_users="
+                               << g_settings->getU16("max_users") << " players." << std::endl;
+               DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_TOO_MANY_USERS);
+               return;
+       }
+
+       std::string checkpwd; // Password hash to check against
+       bool has_auth = m_script->getAuth(playername, &checkpwd, NULL);
+
+       // If no authentication info exists for user, create it
+       if (!has_auth) {
+               if (!isSingleplayer() &&
+                               g_settings->getBool("disallow_empty_password") &&
+                               playerPassword.empty()) {
+                       actionstream << "Server: " << playerName
+                                       << " supplied empty password" << std::endl;
+                       DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_EMPTY_PASSWORD);
+                       return;
+               }
+               std::wstring raw_default_password =
+                       narrow_to_wide(g_settings->get("default_password"));
+               std::string initial_password =
+                       translatePassword(playername, raw_default_password);
+
+               // If default_password is empty, allow any initial password
+               if (raw_default_password.length() == 0)
+                       initial_password = playerPassword.c_str();
+
+               m_script->createAuth(playername, initial_password);
+       }
+
+       has_auth = m_script->getAuth(playername, &checkpwd, NULL);
+
+       if(!has_auth) {
+               actionstream << "Server: " << playerName << " cannot be authenticated"
+                               << " (auth handler does not work?)" << std::endl;
+               DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_SERVER_FAIL);
+               return;
+       }
+
+       if(playerPassword.c_str() != checkpwd) {
+               actionstream << "Server: " << playerName << " supplied wrong password"
+                               << std::endl;
+               DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_WRONG_PASSWORD);
+               return;
+       }
+
+       RemotePlayer *player =
+                       static_cast<RemotePlayer*>(m_env->getPlayer(playername));
+
+       if (player && player->peer_id != 0) {
+               errorstream << "Server: " << playername << ": Failed to emerge player"
+                               << " (player allocated to an another client)" << std::endl;
+               DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_ALREADY_CONNECTED);
+       }
+
+       m_clients.setPlayerName(pkt->getPeerId(), playername);
+
+       /*
+               Answer with a TOCLIENT_INIT
+       */
+
+       NetworkPacket resp_pkt(TOCLIENT_AUTH_ACCEPT, 1 + 6 + 8 + 4, pkt->getPeerId());
+
+       resp_pkt << v3f(0,0,0) << (u64) m_env->getServerMap().getSeed()
+                       << g_settings->getFloat("dedicated_server_step");
+
+       Send(&resp_pkt);
+       m_clients.event(pkt->getPeerId(), CSE_Init);
+}
+
+void Server::handleCommand_Init_Legacy(NetworkPacket* pkt)
+{
+       // [0] u8 SER_FMT_VER_HIGHEST_READ
+       // [1] u8[20] player_name
+       // [21] u8[28] password <--- can be sent without this, from old versions
+
+       if (pkt->getSize() < 1+PLAYERNAME_SIZE)
+               return;
+
+       RemoteClient* client = getClient(pkt->getPeerId(), CS_Created);
+
+       std::string addr_s;
+       try {
+               Address address = getPeerAddress(pkt->getPeerId());
+               addr_s = address.serializeString();
+       }
+       catch (con::PeerNotFoundException &e) {
+               /*
+                * no peer for this packet found
+                * most common reason is peer timeout, e.g. peer didn't
+                * respond for some time, your server was overloaded or
+                * things like that.
+                */
+               infostream << "Server::ProcessData(): Cancelling: peer "
+                               << pkt->getPeerId() << " not found" << std::endl;
+               return;
+       }
+
+       // If net_proto_version is set, this client has already been handled
+       if (client->getState() > CS_Created) {
+               verbosestream << "Server: Ignoring multiple TOSERVER_INITs from "
+                               << addr_s << " (peer_id=" << pkt->getPeerId() << ")" << std::endl;
+               return;
+       }
+
+       verbosestream << "Server: Got TOSERVER_INIT from " << addr_s << " (peer_id="
+                       << pkt->getPeerId() << ")" << std::endl;
+
+       // Do not allow multiple players in simple singleplayer mode.
+       // This isn't a perfect way to do it, but will suffice for now
+       if (m_simple_singleplayer_mode && m_clients.getClientIDs().size() > 1) {
+               infostream << "Server: Not allowing another client (" << addr_s
+                               << ") to connect in simple singleplayer mode" << std::endl;
+               DenyAccess_Legacy(pkt->getPeerId(), L"Running in simple singleplayer mode.");
+               return;
+       }
+
+       // First byte after command is maximum supported
+       // serialization version
+       u8 client_max;
+
+       *pkt >> client_max;
+
+       u8 our_max = SER_FMT_VER_HIGHEST_READ;
+       // Use the highest version supported by both
+       int deployed = std::min(client_max, our_max);
+       // If it's lower than the lowest supported, give up.
+       if (deployed < SER_FMT_VER_LOWEST)
+               deployed = SER_FMT_VER_INVALID;
+
+       if (deployed == SER_FMT_VER_INVALID) {
+               actionstream << "Server: A mismatched client tried to connect from "
+                               << addr_s << std::endl;
+               infostream<<"Server: Cannot negotiate serialization version with "
+                               << addr_s << std::endl;
+               DenyAccess_Legacy(pkt->getPeerId(), std::wstring(
+                               L"Your client's version is not supported.\n"
+                               L"Server version is ")
+                               + narrow_to_wide(minetest_version_simple) + L"."
+               );
+               return;
+       }
+
+       client->setPendingSerializationVersion(deployed);
+
+       /*
+               Read and check network protocol version
+       */
+
+       u16 min_net_proto_version = 0;
+       if (pkt->getSize() >= 1 + PLAYERNAME_SIZE + PASSWORD_SIZE + 2)
+               min_net_proto_version = pkt->getU16(1 + PLAYERNAME_SIZE + PASSWORD_SIZE);
+
+       // Use same version as minimum and maximum if maximum version field
+       // doesn't exist (backwards compatibility)
+       u16 max_net_proto_version = min_net_proto_version;
+       if (pkt->getSize() >= 1 + PLAYERNAME_SIZE + PASSWORD_SIZE + 2 + 2)
+               max_net_proto_version = pkt->getU16(1 + PLAYERNAME_SIZE + PASSWORD_SIZE + 2);
+
+       // Start with client's maximum version
+       u16 net_proto_version = max_net_proto_version;
+
+       // Figure out a working version if it is possible at all
+       if (max_net_proto_version >= SERVER_PROTOCOL_VERSION_MIN ||
+                       min_net_proto_version <= SERVER_PROTOCOL_VERSION_MAX) {
+               // If maximum is larger than our maximum, go with our maximum
+               if (max_net_proto_version > SERVER_PROTOCOL_VERSION_MAX)
+                       net_proto_version = SERVER_PROTOCOL_VERSION_MAX;
+               // Else go with client's maximum
+               else
+                       net_proto_version = max_net_proto_version;
+       }
+
+       verbosestream << "Server: " << addr_s << ": Protocol version: min: "
+                       << min_net_proto_version << ", max: " << max_net_proto_version
+                       << ", chosen: " << net_proto_version << std::endl;
+
+       client->net_proto_version = net_proto_version;
+
+       if (net_proto_version < SERVER_PROTOCOL_VERSION_MIN ||
+                       net_proto_version > SERVER_PROTOCOL_VERSION_MAX) {
+               actionstream << "Server: A mismatched client tried to connect from "
+                               << addr_s << std::endl;
+               DenyAccess_Legacy(pkt->getPeerId(), std::wstring(
+                               L"Your client's version is not supported.\n"
+                               L"Server version is ")
+                               + narrow_to_wide(minetest_version_simple) + L",\n"
+                               + L"server's PROTOCOL_VERSION is "
+                               + narrow_to_wide(itos(SERVER_PROTOCOL_VERSION_MIN))
+                               + L"..."
+                               + narrow_to_wide(itos(SERVER_PROTOCOL_VERSION_MAX))
+                               + L", client's PROTOCOL_VERSION is "
+                               + narrow_to_wide(itos(min_net_proto_version))
+                               + L"..."
+                               + narrow_to_wide(itos(max_net_proto_version))
+               );
+               return;
+       }
+
+       if (g_settings->getBool("strict_protocol_version_checking")) {
+               if (net_proto_version != LATEST_PROTOCOL_VERSION) {
+                       actionstream << "Server: A mismatched (strict) client tried to "
+                                       << "connect from " << addr_s << std::endl;
+                       DenyAccess_Legacy(pkt->getPeerId(), std::wstring(
+                                       L"Your client's version is not supported.\n"
+                                       L"Server version is ")
+                                       + narrow_to_wide(minetest_version_simple) + L",\n"
+                                       + L"server's PROTOCOL_VERSION (strict) is "
+                                       + narrow_to_wide(itos(LATEST_PROTOCOL_VERSION))
+                                       + L", client's PROTOCOL_VERSION is "
+                                       + narrow_to_wide(itos(min_net_proto_version))
+                                       + L"..."
+                                       + narrow_to_wide(itos(max_net_proto_version))
+                       );
+                       return;
+               }
+       }
+
+       /*
+               Set up player
+       */
+       char playername[PLAYERNAME_SIZE];
+       unsigned int playername_length = 0;
+       for (; playername_length < PLAYERNAME_SIZE; playername_length++ ) {
+               playername[playername_length] = pkt->getChar(1+playername_length);
+               if (pkt->getChar(1+playername_length) == 0)
+                       break;
+       }
+
+       if (playername_length == PLAYERNAME_SIZE) {
+               actionstream << "Server: Player with name exceeding max length "
+                               << "tried to connect from " << addr_s << std::endl;
+               DenyAccess_Legacy(pkt->getPeerId(), L"Name too long");
+               return;
+       }
+
+
+       if (playername[0]=='\0') {
+               actionstream << "Server: Player with an empty name "
+                               << "tried to connect from " << addr_s << std::endl;
+               DenyAccess_Legacy(pkt->getPeerId(), L"Empty name");
+               return;
+       }
+
+       if (string_allowed(playername, PLAYERNAME_ALLOWED_CHARS) == false) {
+               actionstream << "Server: Player with an invalid name "
+                               << "tried to connect from " << addr_s << std::endl;
+               DenyAccess_Legacy(pkt->getPeerId(), L"Name contains unallowed characters");
+               return;
+       }
+
+       if (!isSingleplayer() && strcasecmp(playername, "singleplayer") == 0) {
+               actionstream << "Server: Player with the name \"singleplayer\" "
+                               << "tried to connect from " << addr_s << std::endl;
+               DenyAccess_Legacy(pkt->getPeerId(), L"Name is not allowed");
+               return;
+       }
+
+       {
+               std::string reason;
+               if (m_script->on_prejoinplayer(playername, addr_s, reason)) {
+                       actionstream << "Server: Player with the name \"" << playername << "\" "
+                                       << "tried to connect from " << addr_s << " "
+                                       << "but it was disallowed for the following reason: "
+                                       << reason << std::endl;
+                       DenyAccess_Legacy(pkt->getPeerId(), narrow_to_wide(reason.c_str()));
+                       return;
+               }
+       }
+
+       infostream<<"Server: New connection: \""<<playername<<"\" from "
+                       <<addr_s<<" (peer_id="<<pkt->getPeerId()<<")"<<std::endl;
+
+       // Get password
+       char given_password[PASSWORD_SIZE];
+       if (pkt->getSize() < 1 + PLAYERNAME_SIZE + PASSWORD_SIZE) {
+               // old version - assume blank password
+               given_password[0] = 0;
+       }
+       else {
+               for (u16 i = 0; i < PASSWORD_SIZE - 1; i++) {
+                       given_password[i] = pkt->getChar(21 + i);
+               }
+               given_password[PASSWORD_SIZE - 1] = 0;
+       }
+
+       if (!base64_is_valid(given_password)) {
+               actionstream << "Server: " << playername
+                               << " supplied invalid password hash" << std::endl;
+               DenyAccess_Legacy(pkt->getPeerId(), L"Invalid password hash");
+               return;
+       }
+
+       // Enforce user limit.
+       // Don't enforce for users that have some admin right
+       if (m_clients.getClientIDs(CS_Created).size() >= g_settings->getU16("max_users") &&
+                       !checkPriv(playername, "server") &&
+                       !checkPriv(playername, "ban") &&
+                       !checkPriv(playername, "privs") &&
+                       !checkPriv(playername, "password") &&
+                       playername != g_settings->get("name")) {
+               actionstream << "Server: " << playername << " tried to join, but there"
+                               << " are already max_users="
+                               << g_settings->getU16("max_users") << " players." << std::endl;
+               DenyAccess_Legacy(pkt->getPeerId(), L"Too many users.");
+               return;
+       }
+
+       std::string checkpwd; // Password hash to check against
+       bool has_auth = m_script->getAuth(playername, &checkpwd, NULL);
+
+       // If no authentication info exists for user, create it
+       if (!has_auth) {
+               if (!isSingleplayer() &&
+                               g_settings->getBool("disallow_empty_password") &&
+                               std::string(given_password) == "") {
+                       actionstream << "Server: " << playername
+                                       << " supplied empty password" << std::endl;
+                       DenyAccess_Legacy(pkt->getPeerId(), L"Empty passwords are "
+                                       L"disallowed. Set a password and try again.");
+                       return;
+               }
+               std::wstring raw_default_password =
+                       narrow_to_wide(g_settings->get("default_password"));
+               std::string initial_password =
+                       translatePassword(playername, raw_default_password);
+
+               // If default_password is empty, allow any initial password
+               if (raw_default_password.length() == 0)
+                       initial_password = given_password;
+
+               m_script->createAuth(playername, initial_password);
+       }
+
+       has_auth = m_script->getAuth(playername, &checkpwd, NULL);
+
+       if (!has_auth) {
+               actionstream << "Server: " << playername << " cannot be authenticated"
+                               << " (auth handler does not work?)" << std::endl;
+               DenyAccess_Legacy(pkt->getPeerId(), L"Not allowed to login");
+               return;
+       }
+
+       if (given_password != checkpwd) {
+               actionstream << "Server: " << playername << " supplied wrong password"
+                               << std::endl;
+               DenyAccess_Legacy(pkt->getPeerId(), L"Wrong password");
+               return;
+       }
+
+       RemotePlayer *player =
+                       static_cast<RemotePlayer*>(m_env->getPlayer(playername));
+
+       if (player && player->peer_id != 0) {
+               errorstream << "Server: " << playername << ": Failed to emerge player"
+                               << " (player allocated to an another client)" << std::endl;
+               DenyAccess_Legacy(pkt->getPeerId(), L"Another client is connected with this "
+                               L"name. If your client closed unexpectedly, try again in "
+                               L"a minute.");
+       }
+
+       m_clients.setPlayerName(pkt->getPeerId(), playername);
+
+       /*
+               Answer with a TOCLIENT_INIT
+       */
+
+       NetworkPacket resp_pkt(TOCLIENT_INIT_LEGACY, 1 + 6 + 8 + 4,
+                       pkt->getPeerId());
+
+       resp_pkt << (u8) deployed << (v3s16) floatToInt(v3f(0,0,0), BS)
+                       << (u64) m_env->getServerMap().getSeed()
+                       << g_settings->getFloat("dedicated_server_step");
+
+       Send(&resp_pkt);
+       m_clients.event(pkt->getPeerId(), CSE_Init);
+}
+
+void Server::handleCommand_Init2(NetworkPacket* pkt)
+{
+       verbosestream << "Server: Got TOSERVER_INIT2 from "
+                       << pkt->getPeerId() << std::endl;
+
+       m_clients.event(pkt->getPeerId(), CSE_GotInit2);
+       u16 protocol_version = m_clients.getProtocolVersion(pkt->getPeerId());
+
+
+       ///// begin compatibility code
+       PlayerSAO* playersao = NULL;
+       if (protocol_version <= 22) {
+               playersao = StageTwoClientInit(pkt->getPeerId());
+
+               if (playersao == NULL) {
+                       errorstream
+                               << "TOSERVER_INIT2 stage 2 client init failed for peer "
+                               << pkt->getPeerId() << std::endl;
+                       return;
+               }
+       }
+       ///// end compatibility code
+
+       /*
+               Send some initialization data
+       */
+
+       infostream << "Server: Sending content to "
+                       << getPlayerName(pkt->getPeerId()) << std::endl;
+
+       // Send player movement settings
+       SendMovement(pkt->getPeerId());
+
+       // Send item definitions
+       SendItemDef(pkt->getPeerId(), m_itemdef, protocol_version);
+
+       // Send node definitions
+       SendNodeDef(pkt->getPeerId(), m_nodedef, protocol_version);
+
+       m_clients.event(pkt->getPeerId(), CSE_SetDefinitionsSent);
+
+       // Send media announcement
+       sendMediaAnnouncement(pkt->getPeerId());
+
+       // Send detached inventories
+       sendDetachedInventories(pkt->getPeerId());
+
+       // Send time of day
+       u16 time = m_env->getTimeOfDay();
+       float time_speed = g_settings->getFloat("time_speed");
+       SendTimeOfDay(pkt->getPeerId(), time, time_speed);
+
+       ///// begin compatibility code
+       if (protocol_version <= 22) {
+               m_clients.event(pkt->getPeerId(), CSE_SetClientReady);
+               m_script->on_joinplayer(playersao);
+       }
+       ///// end compatibility code
+
+       // Warnings about protocol version can be issued here
+       if (getClient(pkt->getPeerId())->net_proto_version < LATEST_PROTOCOL_VERSION) {
+               SendChatMessage(pkt->getPeerId(), L"# Server: WARNING: YOUR CLIENT'S "
+                               L"VERSION MAY NOT BE FULLY COMPATIBLE WITH THIS SERVER!");
+       }
+}
+
+void Server::handleCommand_RequestMedia(NetworkPacket* pkt)
+{
+       std::vector<std::string> tosend;
+       u16 numfiles;
+
+       *pkt >> numfiles;
+
+       infostream << "Sending " << numfiles << " files to "
+                       << getPlayerName(pkt->getPeerId()) << std::endl;
+       verbosestream << "TOSERVER_REQUEST_MEDIA: " << std::endl;
+
+       for (u16 i = 0; i < numfiles; i++) {
+               std::string name;
+
+               *pkt >> name;
+
+               tosend.push_back(name);
+               verbosestream << "TOSERVER_REQUEST_MEDIA: requested file "
+                               << name << std::endl;
+       }
+
+       sendRequestedMedia(pkt->getPeerId(), tosend);
+}
+
+void Server::handleCommand_ReceivedMedia(NetworkPacket* pkt)
+{
+}
+
+void Server::handleCommand_ClientReady(NetworkPacket* pkt)
+{
+       u16 peer_id = pkt->getPeerId();
+       u16 peer_proto_ver = getClient(peer_id, CS_InitDone)->net_proto_version;
+
+       // clients <= protocol version 22 did not send ready message,
+       // they're already initialized
+       if (peer_proto_ver <= 22) {
+               infostream << "Client sent message not expected by a "
+                       << "client using protocol version <= 22,"
+                       << "disconnecing peer_id: " << peer_id << std::endl;
+               m_con.DisconnectPeer(peer_id);
+               return;
+       }
+
+       PlayerSAO* playersao = StageTwoClientInit(peer_id);
+
+       if (playersao == NULL) {
+               errorstream
+                       << "TOSERVER_CLIENT_READY stage 2 client init failed for peer_id: "
+                       << peer_id << std::endl;
+               m_con.DisconnectPeer(peer_id);
+               return;
+       }
+
+
+       if (pkt->getSize() < 8) {
+               errorstream
+                       << "TOSERVER_CLIENT_READY client sent inconsistent data, disconnecting peer_id: "
+                       << peer_id << std::endl;
+               m_con.DisconnectPeer(peer_id);
+               return;
+       }
+
+       u8 major_ver, minor_ver, patch_ver;
+       *pkt >> major_ver >> minor_ver >> patch_ver;
+
+       m_clients.setClientVersion(
+                       peer_id, major_ver, minor_ver, patch_ver,
+                       std::string(pkt->getString(6),(u16) pkt->getU8(4)));
+
+       m_clients.event(peer_id, CSE_SetClientReady);
+       m_script->on_joinplayer(playersao);
+}
+
+void Server::handleCommand_GotBlocks(NetworkPacket* pkt)
+{
+       if (pkt->getSize() < 1)
+               return;
+
+       /*
+               [0] u16 command
+               [2] u8 count
+               [3] v3s16 pos_0
+               [3+6] v3s16 pos_1
+               ...
+       */
+
+       u8 count;
+       *pkt >> count;
+
+       RemoteClient *client = getClient(pkt->getPeerId());
+
+       for (u16 i = 0; i < count; i++) {
+               if ((s16)pkt->getSize() < 1 + (i + 1) * 6)
+                       throw con::InvalidIncomingDataException
+                               ("GOTBLOCKS length is too short");
+               v3s16 p;
+
+               *pkt >> p;
+
+               client->GotBlock(p);
+       }
+}
+
+void Server::handleCommand_PlayerPos(NetworkPacket* pkt)
+{
+       if (pkt->getSize() < 12 + 12 + 4 + 4)
+               return;
+
+       v3s32 ps, ss;
+       s32 f32pitch, f32yaw;
+
+       *pkt >> ps;
+       *pkt >> ss;
+       *pkt >> f32pitch;
+       *pkt >> f32yaw;
+
+       f32 pitch = (f32)f32pitch / 100.0;
+       f32 yaw = (f32)f32yaw / 100.0;
+       u32 keyPressed = 0;
+
+       if (pkt->getSize() >= 12 + 12 + 4 + 4 + 4)
+               *pkt >> keyPressed;
+
+       v3f position((f32)ps.X / 100.0, (f32)ps.Y / 100.0, (f32)ps.Z / 100.0);
+       v3f speed((f32)ss.X / 100.0, (f32)ss.Y / 100.0, (f32)ss.Z / 100.0);
+
+       pitch = modulo360f(pitch);
+       yaw = modulo360f(yaw);
+
+       Player *player = m_env->getPlayer(pkt->getPeerId());
+       if (player == NULL) {
+               errorstream << "Server::ProcessData(): Cancelling: "
+                               "No player for peer_id=" << pkt->getPeerId()
+                               << " disconnecting peer!" << std::endl;
+               m_con.DisconnectPeer(pkt->getPeerId());
+               return;
+       }
+
+       // If player is dead we don't care of this packet
+       if (player->isDead()) {
+               verbosestream << "TOSERVER_PLAYERPOS: " << player->getName()
+                       << " is dead. Ignoring packet";
+               return;
+       }
+
+       PlayerSAO *playersao = player->getPlayerSAO();
+       if (playersao == NULL) {
+               errorstream << "Server::ProcessData(): Cancelling: "
+                               "No player object for peer_id=" << pkt->getPeerId()
+                               << " disconnecting peer!" << std::endl;
+               m_con.DisconnectPeer(pkt->getPeerId());
+               return;
+       }
+
+       player->setPosition(position);
+       player->setSpeed(speed);
+       player->setPitch(pitch);
+       player->setYaw(yaw);
+       player->keyPressed = keyPressed;
+       player->control.up = (keyPressed & 1);
+       player->control.down = (keyPressed & 2);
+       player->control.left = (keyPressed & 4);
+       player->control.right = (keyPressed & 8);
+       player->control.jump = (keyPressed & 16);
+       player->control.aux1 = (keyPressed & 32);
+       player->control.sneak = (keyPressed & 64);
+       player->control.LMB = (keyPressed & 128);
+       player->control.RMB = (keyPressed & 256);
+
+       if (playersao->checkMovementCheat()) {
+               // Call callbacks
+               m_script->on_cheat(playersao, "moved_too_fast");
+               SendMovePlayer(pkt->getPeerId());
+       }
+}
+
+void Server::handleCommand_DeletedBlocks(NetworkPacket* pkt)
+{
+       if (pkt->getSize() < 1)
+               return;
+
+       /*
+               [0] u16 command
+               [2] u8 count
+               [3] v3s16 pos_0
+               [3+6] v3s16 pos_1
+               ...
+       */
+
+       u8 count;
+       *pkt >> count;
+
+       RemoteClient *client = getClient(pkt->getPeerId());
+
+       for (u16 i = 0; i < count; i++) {
+               if ((s16)pkt->getSize() < 1 + (i + 1) * 6)
+                       throw con::InvalidIncomingDataException
+                               ("DELETEDBLOCKS length is too short");
+               v3s16 p;
+               *pkt >> p;
+
+               client->SetBlockNotSent(p);
+       }
+}
+
+void Server::handleCommand_InventoryAction(NetworkPacket* pkt)
+{
+       Player *player = m_env->getPlayer(pkt->getPeerId());
+       if (player == NULL) {
+               errorstream << "Server::ProcessData(): Cancelling: "
+                               "No player for peer_id=" << pkt->getPeerId()
+                               << " disconnecting peer!" << std::endl;
+               m_con.DisconnectPeer(pkt->getPeerId());
+               return;
+       }
+
+       PlayerSAO *playersao = player->getPlayerSAO();
+       if (playersao == NULL) {
+               errorstream << "Server::ProcessData(): Cancelling: "
+                               "No player object for peer_id=" << pkt->getPeerId()
+                               << " disconnecting peer!" << std::endl;
+               m_con.DisconnectPeer(pkt->getPeerId());
+               return;
+       }
+
+       // Strip command and create a stream
+       std::string datastring(pkt->getString(0), pkt->getSize());
+       verbosestream << "TOSERVER_INVENTORY_ACTION: data=" << datastring
+               << std::endl;
+       std::istringstream is(datastring, std::ios_base::binary);
+       // Create an action
+       InventoryAction *a = InventoryAction::deSerialize(is);
+       if (a == NULL) {
+               infostream << "TOSERVER_INVENTORY_ACTION: "
+                               << "InventoryAction::deSerialize() returned NULL"
+                               << std::endl;
+               return;
+       }
+
+       // If something goes wrong, this player is to blame
+       RollbackScopeActor rollback_scope(m_rollback,
+                       std::string("player:")+player->getName());
+
+       /*
+               Note: Always set inventory not sent, to repair cases
+               where the client made a bad prediction.
+       */
+
+       /*
+               Handle restrictions and special cases of the move action
+       */
+       if (a->getType() == IACTION_MOVE) {
+               IMoveAction *ma = (IMoveAction*)a;
+
+               ma->from_inv.applyCurrentPlayer(player->getName());
+               ma->to_inv.applyCurrentPlayer(player->getName());
+
+               setInventoryModified(ma->from_inv);
+               setInventoryModified(ma->to_inv);
+
+               bool from_inv_is_current_player =
+                       (ma->from_inv.type == InventoryLocation::PLAYER) &&
+                       (ma->from_inv.name == player->getName());
+
+               bool to_inv_is_current_player =
+                       (ma->to_inv.type == InventoryLocation::PLAYER) &&
+                       (ma->to_inv.name == player->getName());
+
+               /*
+                       Disable moving items out of craftpreview
+               */
+               if (ma->from_list == "craftpreview") {
+                       infostream << "Ignoring IMoveAction from "
+                                       << (ma->from_inv.dump()) << ":" << ma->from_list
+                                       << " to " << (ma->to_inv.dump()) << ":" << ma->to_list
+                                       << " because src is " << ma->from_list << std::endl;
+                       delete a;
+                       return;
+               }
+
+               /*
+                       Disable moving items into craftresult and craftpreview
+               */
+               if (ma->to_list == "craftpreview" || ma->to_list == "craftresult") {
+                       infostream << "Ignoring IMoveAction from "
+                                       << (ma->from_inv.dump()) << ":" << ma->from_list
+                                       << " to " << (ma->to_inv.dump()) << ":" << ma->to_list
+                                       << " because dst is " << ma->to_list << std::endl;
+                       delete a;
+                       return;
+               }
+
+               // Disallow moving items in elsewhere than player's inventory
+               // if not allowed to interact
+               if (!checkPriv(player->getName(), "interact") &&
+                               (!from_inv_is_current_player ||
+                               !to_inv_is_current_player)) {
+                       infostream << "Cannot move outside of player's inventory: "
+                                       << "No interact privilege" << std::endl;
+                       delete a;
+                       return;
+               }
+       }
+       /*
+               Handle restrictions and special cases of the drop action
+       */
+       else if (a->getType() == IACTION_DROP) {
+               IDropAction *da = (IDropAction*)a;
+
+               da->from_inv.applyCurrentPlayer(player->getName());
+
+               setInventoryModified(da->from_inv);
+
+               /*
+                       Disable dropping items out of craftpreview
+               */
+               if (da->from_list == "craftpreview") {
+                       infostream << "Ignoring IDropAction from "
+                                       << (da->from_inv.dump()) << ":" << da->from_list
+                                       << " because src is " << da->from_list << std::endl;
+                       delete a;
+                       return;
+               }
+
+               // Disallow dropping items if not allowed to interact
+               if (!checkPriv(player->getName(), "interact")) {
+                       delete a;
+                       return;
+               }
+       }
+       /*
+               Handle restrictions and special cases of the craft action
+       */
+       else if (a->getType() == IACTION_CRAFT) {
+               ICraftAction *ca = (ICraftAction*)a;
+
+               ca->craft_inv.applyCurrentPlayer(player->getName());
+
+               setInventoryModified(ca->craft_inv);
+
+               //bool craft_inv_is_current_player =
+               //      (ca->craft_inv.type == InventoryLocation::PLAYER) &&
+               //      (ca->craft_inv.name == player->getName());
+
+               // Disallow crafting if not allowed to interact
+               if (!checkPriv(player->getName(), "interact")) {
+                       infostream << "Cannot craft: "
+                                       << "No interact privilege" << std::endl;
+                       delete a;
+                       return;
+               }
+       }
+
+       // Do the action
+       a->apply(this, playersao, this);
+       // Eat the action
+       delete a;
+}
+
+void Server::handleCommand_ChatMessage(NetworkPacket* pkt)
+{
+       /*
+               u16 command
+               u16 length
+               wstring message
+       */
+       u16 len;
+       *pkt >> len;
+
+       std::wstring message;
+       for (u16 i = 0; i < len; i++) {
+               u16 tmp_wchar;
+               *pkt >> tmp_wchar;
+
+               message += (wchar_t)tmp_wchar;
+       }
+
+       Player *player = m_env->getPlayer(pkt->getPeerId());
+       if (player == NULL) {
+               errorstream << "Server::ProcessData(): Cancelling: "
+                               "No player for peer_id=" << pkt->getPeerId()
+                               << " disconnecting peer!" << std::endl;
+               m_con.DisconnectPeer(pkt->getPeerId());
+               return;
+       }
+
+       // If something goes wrong, this player is to blame
+       RollbackScopeActor rollback_scope(m_rollback,
+                       std::string("player:")+player->getName());
+
+       // Get player name of this client
+       std::wstring name = narrow_to_wide(player->getName());
+
+       // Run script hook
+       bool ate = m_script->on_chat_message(player->getName(),
+                       wide_to_narrow(message));
+       // If script ate the message, don't proceed
+       if (ate)
+               return;
+
+       // Line to send to players
+       std::wstring line;
+       // Whether to send to the player that sent the line
+       bool send_to_sender_only = false;
+
+       // Commands are implemented in Lua, so only catch invalid
+       // commands that were not "eaten" and send an error back
+       if (message[0] == L'/') {
+               message = message.substr(1);
+               send_to_sender_only = true;
+               if (message.length() == 0)
+                       line += L"-!- Empty command";
+               else
+                       line += L"-!- Invalid command: " + str_split(message, L' ')[0];
+       }
+       else {
+               if (checkPriv(player->getName(), "shout")) {
+                       line += L"<";
+                       line += name;
+                       line += L"> ";
+                       line += message;
+               } else {
+                       line += L"-!- You don't have permission to shout.";
+                       send_to_sender_only = true;
+               }
+       }
+
+       if (line != L"")
+       {
+               /*
+                       Send the message to sender
+               */
+               if (send_to_sender_only) {
+                       SendChatMessage(pkt->getPeerId(), line);
+               }
+               /*
+                       Send the message to others
+               */
+               else {
+                       actionstream << "CHAT: " << wide_to_narrow(line)<<std::endl;
+
+                       std::vector<u16> clients = m_clients.getClientIDs();
+
+                       for (std::vector<u16>::iterator i = clients.begin();
+                               i != clients.end(); ++i) {
+                               if (*i != pkt->getPeerId())
+                                       SendChatMessage(*i, line);
+                       }
+               }
+       }
+}
+
+void Server::handleCommand_Damage(NetworkPacket* pkt)
+{
+       u8 damage;
+
+       *pkt >> damage;
+
+       Player *player = m_env->getPlayer(pkt->getPeerId());
+       if (player == NULL) {
+               errorstream << "Server::ProcessData(): Cancelling: "
+                               "No player for peer_id=" << pkt->getPeerId()
+                               << " disconnecting peer!" << std::endl;
+               m_con.DisconnectPeer(pkt->getPeerId());
+               return;
+       }
+
+       PlayerSAO *playersao = player->getPlayerSAO();
+       if (playersao == NULL) {
+               errorstream << "Server::ProcessData(): Cancelling: "
+                               "No player object for peer_id=" << pkt->getPeerId()
+                               << " disconnecting peer!" << std::endl;
+               m_con.DisconnectPeer(pkt->getPeerId());
+               return;
+       }
+
+       if (g_settings->getBool("enable_damage")) {
+               actionstream << player->getName() << " damaged by "
+                               << (int)damage << " hp at " << PP(player->getPosition() / BS)
+                               << std::endl;
+
+               playersao->setHP(playersao->getHP() - damage);
+               SendPlayerHPOrDie(playersao->getPeerID(), playersao->getHP() == 0);
+       }
+}
+
+void Server::handleCommand_Breath(NetworkPacket* pkt)
+{
+       u16 breath;
+
+       *pkt >> breath;
+
+       Player *player = m_env->getPlayer(pkt->getPeerId());
+       if (player == NULL) {
+               errorstream << "Server::ProcessData(): Cancelling: "
+                               "No player for peer_id=" << pkt->getPeerId()
+                               << " disconnecting peer!" << std::endl;
+               m_con.DisconnectPeer(pkt->getPeerId());
+               return;
+       }
+
+       /*
+        * If player is dead, we don't need to update the breath
+        * He is dead !
+        */
+       if (player->isDead()) {
+               verbosestream << "TOSERVER_BREATH: " << player->getName()
+                       << " is dead. Ignoring packet";
+               return;
+       }
+
+
+       PlayerSAO *playersao = player->getPlayerSAO();
+       if (playersao == NULL) {
+               errorstream << "Server::ProcessData(): Cancelling: "
+                               "No player object for peer_id=" << pkt->getPeerId()
+                               << " disconnecting peer!" << std::endl;
+               m_con.DisconnectPeer(pkt->getPeerId());
+               return;
+       }
+
+       playersao->setBreath(breath);
+       SendPlayerBreath(pkt->getPeerId());
+}
+
+void Server::handleCommand_Password(NetworkPacket* pkt)
+{
+       errorstream << "PAssword packet size: " << pkt->getSize() << " size required: " << PASSWORD_SIZE * 2 << std::endl;
+       if ((pkt->getCommand() == TOSERVER_PASSWORD && pkt->getSize() < 4) ||
+                       pkt->getSize() != PASSWORD_SIZE * 2)
+               return;
+
+       std::string oldpwd;
+       std::string newpwd;
+
+       if (pkt->getCommand() == TOSERVER_PASSWORD) {
+               *pkt >> oldpwd >> newpwd;
+       }
+       // 13/03/15
+       // Protocol v24 compat. Please remove in 1 year after
+       // client convergence to 0.4.13/0.5.x
+       else {
+               for (u16 i = 0; i < PASSWORD_SIZE - 1; i++) {
+                       char c = pkt->getChar(i);
+                       if (c == 0)
+                               break;
+                       oldpwd += c;
+               }
+
+               for (u16 i = 0; i < PASSWORD_SIZE - 1; i++) {
+                       char c = pkt->getChar(PASSWORD_SIZE + i);
+                       if (c == 0)
+                               break;
+                       newpwd += c;
+               }
+       }
+
+       Player *player = m_env->getPlayer(pkt->getPeerId());
+       if (player == NULL) {
+               errorstream << "Server::ProcessData(): Cancelling: "
+                               "No player for peer_id=" << pkt->getPeerId()
+                               << " disconnecting peer!" << std::endl;
+               m_con.DisconnectPeer(pkt->getPeerId());
+               return;
+       }
+
+       if (!base64_is_valid(newpwd)) {
+               infostream<<"Server: " << player->getName() <<
+                               " supplied invalid password hash" << std::endl;
+               // Wrong old password supplied!!
+               SendChatMessage(pkt->getPeerId(), L"Invalid new password hash supplied. Password NOT changed.");
+               return;
+       }
+
+       infostream << "Server: Client requests a password change from "
+                       << "'" << oldpwd << "' to '" << newpwd << "'" << std::endl;
+
+       std::string playername = player->getName();
+
+       std::string checkpwd;
+       m_script->getAuth(playername, &checkpwd, NULL);
+
+       if (oldpwd != checkpwd) {
+               infostream << "Server: invalid old password" << std::endl;
+               // Wrong old password supplied!!
+               SendChatMessage(pkt->getPeerId(), L"Invalid old password supplied. Password NOT changed.");
+               return;
+       }
+
+       bool success = m_script->setPassword(playername, newpwd);
+       if (success) {
+               actionstream << player->getName() << " changes password" << std::endl;
+               SendChatMessage(pkt->getPeerId(), L"Password change successful.");
+       } else {
+               actionstream << player->getName() << " tries to change password but "
+                               << "it fails" << std::endl;
+               SendChatMessage(pkt->getPeerId(), L"Password change failed or inavailable.");
+       }
+}
+
+void Server::handleCommand_PlayerItem(NetworkPacket* pkt)
+{
+       if (pkt->getSize() < 2)
+               return;
+
+       Player *player = m_env->getPlayer(pkt->getPeerId());
+       if (player == NULL) {
+               errorstream << "Server::ProcessData(): Cancelling: "
+                               "No player for peer_id=" << pkt->getPeerId()
+                               << " disconnecting peer!" << std::endl;
+               m_con.DisconnectPeer(pkt->getPeerId());
+               return;
+       }
+
+       PlayerSAO *playersao = player->getPlayerSAO();
+       if (playersao == NULL) {
+               errorstream << "Server::ProcessData(): Cancelling: "
+                               "No player object for peer_id=" << pkt->getPeerId()
+                               << " disconnecting peer!" << std::endl;
+               m_con.DisconnectPeer(pkt->getPeerId());
+               return;
+       }
+
+       u16 item;
+
+       *pkt >> item;
+
+       playersao->setWieldIndex(item);
+}
+
+void Server::handleCommand_Respawn(NetworkPacket* pkt)
+{
+       Player *player = m_env->getPlayer(pkt->getPeerId());
+       if (player == NULL) {
+               errorstream << "Server::ProcessData(): Cancelling: "
+                               "No player for peer_id=" << pkt->getPeerId()
+                               << " disconnecting peer!" << std::endl;
+               m_con.DisconnectPeer(pkt->getPeerId());
+               return;
+       }
+
+       if (!player->isDead())
+               return;
+
+       RespawnPlayer(pkt->getPeerId());
+
+       actionstream << player->getName() << " respawns at "
+                       << PP(player->getPosition()/BS) << std::endl;
+
+       // ActiveObject is added to environment in AsyncRunStep after
+       // the previous addition has been succesfully removed
+}
+
+void Server::handleCommand_Interact(NetworkPacket* pkt)
+{
+       std::string datastring(pkt->getString(0), pkt->getSize());
+       std::istringstream is(datastring, std::ios_base::binary);
+
+       /*
+               [0] u16 command
+               [2] u8 action
+               [3] u16 item
+               [5] u32 length of the next item
+               [9] serialized PointedThing
+               actions:
+               0: start digging (from undersurface) or use
+               1: stop digging (all parameters ignored)
+               2: digging completed
+               3: place block or item (to abovesurface)
+               4: use item
+       */
+       u8 action = readU8(is);
+       u16 item_i = readU16(is);
+       std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
+       PointedThing pointed;
+       pointed.deSerialize(tmp_is);
+
+       verbosestream << "TOSERVER_INTERACT: action=" << (int)action << ", item="
+                       << item_i << ", pointed=" << pointed.dump() << std::endl;
+
+       Player *player = m_env->getPlayer(pkt->getPeerId());
+       if (player == NULL) {
+               errorstream << "Server::ProcessData(): Cancelling: "
+                               "No player for peer_id=" << pkt->getPeerId()
+                               << " disconnecting peer!" << std::endl;
+               m_con.DisconnectPeer(pkt->getPeerId());
+               return;
+       }
+
+       PlayerSAO *playersao = player->getPlayerSAO();
+       if (playersao == NULL) {
+               errorstream << "Server::ProcessData(): Cancelling: "
+                               "No player object for peer_id=" << pkt->getPeerId()
+                               << " disconnecting peer!" << std::endl;
+               m_con.DisconnectPeer(pkt->getPeerId());
+               return;
+       }
+
+       if (player->isDead()) {
+               verbosestream << "TOSERVER_INTERACT: " << player->getName()
+                       << " is dead. Ignoring packet";
+               return;
+       }
+
+       v3f player_pos = playersao->getLastGoodPosition();
+
+       // Update wielded item
+       playersao->setWieldIndex(item_i);
+
+       // Get pointed to node (undefined if not POINTEDTYPE_NODE)
+       v3s16 p_under = pointed.node_undersurface;
+       v3s16 p_above = pointed.node_abovesurface;
+
+       // Get pointed to object (NULL if not POINTEDTYPE_OBJECT)
+       ServerActiveObject *pointed_object = NULL;
+       if (pointed.type == POINTEDTHING_OBJECT) {
+               pointed_object = m_env->getActiveObject(pointed.object_id);
+               if (pointed_object == NULL) {
+                       verbosestream << "TOSERVER_INTERACT: "
+                               "pointed object is NULL" << std::endl;
+                       return;
+               }
+
+       }
+
+       v3f pointed_pos_under = player_pos;
+       v3f pointed_pos_above = player_pos;
+       if (pointed.type == POINTEDTHING_NODE) {
+               pointed_pos_under = intToFloat(p_under, BS);
+               pointed_pos_above = intToFloat(p_above, BS);
+       }
+       else if (pointed.type == POINTEDTHING_OBJECT) {
+               pointed_pos_under = pointed_object->getBasePosition();
+               pointed_pos_above = pointed_pos_under;
+       }
+
+       /*
+               Check that target is reasonably close
+               (only when digging or placing things)
+       */
+       if (action == 0 || action == 2 || action == 3) {
+               float d = player_pos.getDistanceFrom(pointed_pos_under);
+               float max_d = BS * 14; // Just some large enough value
+               if (d > max_d) {
+                       actionstream << "Player " << player->getName()
+                                       << " tried to access " << pointed.dump()
+                                       << " from too far: "
+                                       << "d=" << d <<", max_d=" << max_d
+                                       << ". ignoring." << std::endl;
+                       // Re-send block to revert change on client-side
+                       RemoteClient *client = getClient(pkt->getPeerId());
+                       v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
+                       client->SetBlockNotSent(blockpos);
+                       // Call callbacks
+                       m_script->on_cheat(playersao, "interacted_too_far");
+                       // Do nothing else
+                       return;
+               }
+       }
+
+       /*
+               Make sure the player is allowed to do it
+       */
+       if (!checkPriv(player->getName(), "interact")) {
+               actionstream<<player->getName()<<" attempted to interact with "
+                               <<pointed.dump()<<" without 'interact' privilege"
+                               <<std::endl;
+               // Re-send block to revert change on client-side
+               RemoteClient *client = getClient(pkt->getPeerId());
+               // Digging completed -> under
+               if (action == 2) {
+                       v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
+                       client->SetBlockNotSent(blockpos);
+               }
+               // Placement -> above
+               if (action == 3) {
+                       v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS));
+                       client->SetBlockNotSent(blockpos);
+               }
+               return;
+       }
+
+       /*
+               If something goes wrong, this player is to blame
+       */
+       RollbackScopeActor rollback_scope(m_rollback,
+                       std::string("player:")+player->getName());
+
+       /*
+               0: start digging or punch object
+       */
+       if (action == 0) {
+               if (pointed.type == POINTEDTHING_NODE) {
+                       /*
+                               NOTE: This can be used in the future to check if
+                               somebody is cheating, by checking the timing.
+                       */
+                       MapNode n(CONTENT_IGNORE);
+                       bool pos_ok;
+                       n = m_env->getMap().getNodeNoEx(p_under, &pos_ok);
+                       if (pos_ok)
+                               n = m_env->getMap().getNodeNoEx(p_under, &pos_ok);
+
+                       if (!pos_ok) {
+                               infostream << "Server: Not punching: Node not found."
+                                               << " Adding block to emerge queue."
+                                               << std::endl;
+                               m_emerge->enqueueBlockEmerge(pkt->getPeerId(), getNodeBlockPos(p_above), false);
+                       }
+
+                       if (n.getContent() != CONTENT_IGNORE)
+                               m_script->node_on_punch(p_under, n, playersao, pointed);
+                       // Cheat prevention
+                       playersao->noCheatDigStart(p_under);
+               }
+               else if (pointed.type == POINTEDTHING_OBJECT) {
+                       // Skip if object has been removed
+                       if (pointed_object->m_removed)
+                               return;
+
+                       actionstream<<player->getName()<<" punches object "
+                                       <<pointed.object_id<<": "
+                                       <<pointed_object->getDescription()<<std::endl;
+
+                       ItemStack punchitem = playersao->getWieldedItem();
+                       ToolCapabilities toolcap =
+                                       punchitem.getToolCapabilities(m_itemdef);
+                       v3f dir = (pointed_object->getBasePosition() -
+                                       (player->getPosition() + player->getEyeOffset())
+                                               ).normalize();
+                       float time_from_last_punch =
+                               playersao->resetTimeFromLastPunch();
+
+                       s16 src_original_hp = pointed_object->getHP();
+                       s16 dst_origin_hp = playersao->getHP();
+
+                       pointed_object->punch(dir, &toolcap, playersao,
+                                       time_from_last_punch);
+
+                       // If the object is a player and its HP changed
+                       if (src_original_hp != pointed_object->getHP() &&
+                                       pointed_object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
+                               SendPlayerHPOrDie(((PlayerSAO*)pointed_object)->getPeerID(),
+                                               pointed_object->getHP() == 0);
+                       }
+
+                       // If the puncher is a player and its HP changed
+                       if (dst_origin_hp != playersao->getHP()) {
+                               SendPlayerHPOrDie(playersao->getPeerID(), playersao->getHP() == 0);
+                       }
+               }
+
+       } // action == 0
+
+       /*
+               1: stop digging
+       */
+       else if (action == 1) {
+       } // action == 1
+
+       /*
+               2: Digging completed
+       */
+       else if (action == 2) {
+               // Only digging of nodes
+               if (pointed.type == POINTEDTHING_NODE) {
+                       bool pos_ok;
+                       MapNode n = m_env->getMap().getNodeNoEx(p_under, &pos_ok);
+                       if (!pos_ok) {
+                               infostream << "Server: Not finishing digging: Node not found."
+                                                  << " Adding block to emerge queue."
+                                                  << std::endl;
+                               m_emerge->enqueueBlockEmerge(pkt->getPeerId(), getNodeBlockPos(p_above), false);
+                       }
+
+                       /* Cheat prevention */
+                       bool is_valid_dig = true;
+                       if (!isSingleplayer() && !g_settings->getBool("disable_anticheat")) {
+                               v3s16 nocheat_p = playersao->getNoCheatDigPos();
+                               float nocheat_t = playersao->getNoCheatDigTime();
+                               playersao->noCheatDigEnd();
+                               // If player didn't start digging this, ignore dig
+                               if (nocheat_p != p_under) {
+                                       infostream << "Server: NoCheat: " << player->getName()
+                                                       << " started digging "
+                                                       << PP(nocheat_p) << " and completed digging "
+                                                       << PP(p_under) << "; not digging." << std::endl;
+                                       is_valid_dig = false;
+                                       // Call callbacks
+                                       m_script->on_cheat(playersao, "finished_unknown_dig");
+                               }
+                               // Get player's wielded item
+                               ItemStack playeritem;
+                               InventoryList *mlist = playersao->getInventory()->getList("main");
+                               if (mlist != NULL)
+                                       playeritem = mlist->getItem(playersao->getWieldIndex());
+                               ToolCapabilities playeritem_toolcap =
+                                               playeritem.getToolCapabilities(m_itemdef);
+                               // Get diggability and expected digging time
+                               DigParams params = getDigParams(m_nodedef->get(n).groups,
+                                               &playeritem_toolcap);
+                               // If can't dig, try hand
+                               if (!params.diggable) {
+                                       const ItemDefinition &hand = m_itemdef->get("");
+                                       const ToolCapabilities *tp = hand.tool_capabilities;
+                                       if (tp)
+                                               params = getDigParams(m_nodedef->get(n).groups, tp);
+                               }
+                               // If can't dig, ignore dig
+                               if (!params.diggable) {
+                                       infostream << "Server: NoCheat: " << player->getName()
+                                                       << " completed digging " << PP(p_under)
+                                                       << ", which is not diggable with tool. not digging."
+                                                       << std::endl;
+                                       is_valid_dig = false;
+                                       // Call callbacks
+                                       m_script->on_cheat(playersao, "dug_unbreakable");
+                               }
+                               // Check digging time
+                               // If already invalidated, we don't have to
+                               if (!is_valid_dig) {
+                                       // Well not our problem then
+                               }
+                               // Clean and long dig
+                               else if (params.time > 2.0 && nocheat_t * 1.2 > params.time) {
+                                       // All is good, but grab time from pool; don't care if
+                                       // it's actually available
+                                       playersao->getDigPool().grab(params.time);
+                               }
+                               // Short or laggy dig
+                               // Try getting the time from pool
+                               else if (playersao->getDigPool().grab(params.time)) {
+                                       // All is good
+                               }
+                               // Dig not possible
+                               else {
+                                       infostream << "Server: NoCheat: " << player->getName()
+                                                       << " completed digging " << PP(p_under)
+                                                       << "too fast; not digging." << std::endl;
+                                       is_valid_dig = false;
+                                       // Call callbacks
+                                       m_script->on_cheat(playersao, "dug_too_fast");
+                               }
+                       }
+
+                       /* Actually dig node */
+
+                       if (is_valid_dig && n.getContent() != CONTENT_IGNORE)
+                               m_script->node_on_dig(p_under, n, playersao);
+
+                       v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
+                       RemoteClient *client = getClient(pkt->getPeerId());
+                       // Send unusual result (that is, node not being removed)
+                       if (m_env->getMap().getNodeNoEx(p_under).getContent() != CONTENT_AIR) {
+                               // Re-send block to revert change on client-side
+                               client->SetBlockNotSent(blockpos);
+                       }
+                       else {
+                               client->ResendBlockIfOnWire(blockpos);
+                       }
+               }
+       } // action == 2
+
+       /*
+               3: place block or right-click object
+       */
+       else if (action == 3) {
+               ItemStack item = playersao->getWieldedItem();
+
+               // Reset build time counter
+               if (pointed.type == POINTEDTHING_NODE &&
+                               item.getDefinition(m_itemdef).type == ITEM_NODE)
+                       getClient(pkt->getPeerId())->m_time_from_building = 0.0;
+
+               if (pointed.type == POINTEDTHING_OBJECT) {
+                       // Right click object
+
+                       // Skip if object has been removed
+                       if (pointed_object->m_removed)
+                               return;
+
+                       actionstream << player->getName() << " right-clicks object "
+                                       << pointed.object_id << ": "
+                                       << pointed_object->getDescription() << std::endl;
+
+                       // Do stuff
+                       pointed_object->rightClick(playersao);
+               }
+               else if (m_script->item_OnPlace(
+                               item, playersao, pointed)) {
+                       // Placement was handled in lua
+
+                       // Apply returned ItemStack
+                       if (playersao->setWieldedItem(item)) {
+                               SendInventory(playersao);
+                       }
+               }
+
+               // If item has node placement prediction, always send the
+               // blocks to make sure the client knows what exactly happened
+               RemoteClient *client = getClient(pkt->getPeerId());
+               v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS));
+               v3s16 blockpos2 = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
+               if (item.getDefinition(m_itemdef).node_placement_prediction != "") {
+                       client->SetBlockNotSent(blockpos);
+                       if (blockpos2 != blockpos) {
+                               client->SetBlockNotSent(blockpos2);
+                       }
+               }
+               else {
+                       client->ResendBlockIfOnWire(blockpos);
+                       if (blockpos2 != blockpos) {
+                               client->ResendBlockIfOnWire(blockpos2);
+                       }
+               }
+       } // action == 3
+
+       /*
+               4: use
+       */
+       else if (action == 4) {
+               ItemStack item = playersao->getWieldedItem();
+
+               actionstream << player->getName() << " uses " << item.name
+                               << ", pointing at " << pointed.dump() << std::endl;
+
+               if (m_script->item_OnUse(
+                               item, playersao, pointed)) {
+                       // Apply returned ItemStack
+                       playersao->setWieldedItem(item);
+               }
+
+       } // action == 4
+
+
+       /*
+               Catch invalid actions
+       */
+       else {
+               infostream << "WARNING: Server: Invalid action "
+                               << action << std::endl;
+       }
+}
+
+void Server::handleCommand_RemovedSounds(NetworkPacket* pkt)
+{
+       u16 num;
+       *pkt >> num;
+       for (u16 k = 0; k < num; k++) {
+               s32 id;
+
+               *pkt >> id;
+
+               std::map<s32, ServerPlayingSound>::iterator i =
+                       m_playing_sounds.find(id);
+
+               if (i == m_playing_sounds.end())
+                       continue;
+
+               ServerPlayingSound &psound = i->second;
+               psound.clients.erase(pkt->getPeerId());
+               if (psound.clients.empty())
+                       m_playing_sounds.erase(i++);
+       }
+}
+
+void Server::handleCommand_NodeMetaFields(NetworkPacket* pkt)
+{
+       v3s16 p;
+       std::string formname;
+       u16 num;
+
+       *pkt >> p >> formname >> num;
+
+       std::map<std::string, std::string> fields;
+       for (u16 k = 0; k < num; k++) {
+               std::string fieldname;
+               *pkt >> fieldname;
+               fields[fieldname] = pkt->readLongString();
+       }
+
+       Player *player = m_env->getPlayer(pkt->getPeerId());
+       if (player == NULL) {
+               errorstream << "Server::ProcessData(): Cancelling: "
+                               "No player for peer_id=" << pkt->getPeerId()
+                               << " disconnecting peer!" << std::endl;
+               m_con.DisconnectPeer(pkt->getPeerId());
+               return;
+       }
+
+       PlayerSAO *playersao = player->getPlayerSAO();
+       if (playersao == NULL) {
+               errorstream << "Server::ProcessData(): Cancelling: "
+                               "No player object for peer_id=" << pkt->getPeerId()
+                               << " disconnecting peer!"  << std::endl;
+               m_con.DisconnectPeer(pkt->getPeerId());
+               return;
+       }
+
+       // If something goes wrong, this player is to blame
+       RollbackScopeActor rollback_scope(m_rollback,
+                       std::string("player:")+player->getName());
+
+       // Check the target node for rollback data; leave others unnoticed
+       RollbackNode rn_old(&m_env->getMap(), p, this);
+
+       m_script->node_on_receive_fields(p, formname, fields, playersao);
+
+       // Report rollback data
+       RollbackNode rn_new(&m_env->getMap(), p, this);
+       if (rollback() && rn_new != rn_old) {
+               RollbackAction action;
+               action.setSetNode(p, rn_old, rn_new);
+               rollback()->reportAction(action);
+       }
+}
+
+void Server::handleCommand_InventoryFields(NetworkPacket* pkt)
+{
+       std::string formname;
+       u16 num;
+
+       *pkt >> formname >> num;
+
+       std::map<std::string, std::string> fields;
+       for (u16 k = 0; k < num; k++) {
+               std::string fieldname;
+               *pkt >> fieldname;
+               fields[fieldname] = pkt->readLongString();
+       }
+
+       Player *player = m_env->getPlayer(pkt->getPeerId());
+       if (player == NULL) {
+               errorstream << "Server::ProcessData(): Canceling: "
+                               "No player for peer_id=" << pkt->getPeerId()
+                               << " disconnecting peer!" << std::endl;
+               m_con.DisconnectPeer(pkt->getPeerId());
+               return;
+       }
+
+       PlayerSAO *playersao = player->getPlayerSAO();
+       if (playersao == NULL) {
+               errorstream << "Server::ProcessData(): Canceling: "
+                               "No player object for peer_id=" << pkt->getPeerId()
+                               << " disconnecting peer!" << std::endl;
+               m_con.DisconnectPeer(pkt->getPeerId());
+               return;
+       }
+
+       m_script->on_playerReceiveFields(playersao, formname, fields);
+}