Network Layer 7 rework (Packet handling)
authorLoic Blot <loic.blot@unix-experience.fr>
Mon, 12 Jan 2015 16:01:41 +0000 (17:01 +0100)
committerCraig Robbins <kde.psych@gmail.com>
Tue, 10 Feb 2015 10:04:08 +0000 (20:04 +1000)
* Move networkcode to a dedicated directory
* Rename clientserver.h to network/networkprotocol.h (Better name) and sanitize some includes
* Create object NetworkPacket
  * It stores command (opcode) and data separated
  * It also stores peer_id
  * Data reading can be done by using a streaming interface
* Change packet routing analysis
  * Remove old conditional analysis
  * Now uses function pointed analysis and add connection state ({Client,Server}::handlers)
  * Connection state permit to categorize condition to handle before analyze packets
  * Create a handler for depreciated messages, instead of duplicating code

21 files changed:
src/CMakeLists.txt
src/client.cpp
src/client.h
src/clientmedia.cpp
src/clientserver.h [deleted file]
src/filecache.cpp
src/guiFormSpecMenu.h
src/network/clientopcodes.cpp [new file with mode: 0644]
src/network/clientopcodes.h [new file with mode: 0644]
src/network/networkpacket.cpp [new file with mode: 0644]
src/network/networkpacket.h [new file with mode: 0644]
src/network/networkprotocol.h [new file with mode: 0644]
src/network/serveropcodes.cpp [new file with mode: 0644]
src/network/serveropcodes.h [new file with mode: 0644]
src/network/toclientpacket.cpp [new file with mode: 0644]
src/network/toclientpacket.h [new file with mode: 0644]
src/network/toserverpacket.cpp [new file with mode: 0644]
src/network/toserverpacket.h [new file with mode: 0644]
src/server.cpp
src/server.h
src/test.cpp

index 93083f3694586747e80a1a8c3d9fba5597c3f704..e5245534cd12145cd4dfe319121c0dcb4636d6b3 100644 (file)
@@ -429,6 +429,9 @@ set(common_SRCS
        version.cpp
        voxel.cpp
        voxelalgorithms.cpp
+       network/networkpacket.cpp
+       network/serveropcodes.cpp
+       network/toserverpacket.cpp
        ${JTHREAD_SRCS}
        ${common_SCRIPT_SRCS}
        ${UTIL_SRCS}
@@ -491,6 +494,8 @@ set(minetest_SRCS
        sky.cpp
        tile.cpp
        wieldmesh.cpp
+       network/clientopcodes.cpp
+       network/toclientpacket.cpp
        ${minetest_SCRIPT_SRCS}
 )
 list(SORT minetest_SRCS)
index 2f70d624c4a1f46e1d861507bf94a521a64086bc..b13631f9507d1df0be01c2b5c02b37a0a13d831d 100644 (file)
@@ -28,7 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "util/string.h"
 #include "strfnd.h"
 #include "client.h"
-#include "clientserver.h"
+#include "network/clientopcodes.h"
 #include "main.h"
 #include "filesys.h"
 #include "porting.h"
@@ -1013,1008 +1013,1076 @@ void Client::Receive()
        ProcessData(*data, datasize, sender_peer_id);
 }
 
-/*
-       sender_peer_id given to this shall be quaranteed to be a valid peer
-*/
-void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
+void Client::handleCommand_Deprecated(ToClientPacket* pkt)
 {
-       DSTACK(__FUNCTION_NAME);
+       infostream << "Got deprecated command "
+                       << toClientCommandTable[pkt->getCommand()].name << " from peer "
+                       << pkt->getPeerId() << "!" << std::endl;
+}
 
-       // Ignore packets that don't even fit a command
-       if(datasize < 2)
-       {
-               m_packetcounter.add(60000);
+void Client::handleCommand_Init(ToClientPacket* pkt)
+{
+       if(pkt->getSize() < 1)
                return;
-       }
 
-       ToClientCommand command = (ToClientCommand)readU16(&data[0]);
+       u8 deployed;
+       *pkt >> deployed;
 
-       //infostream<<"Client: received command="<<command<<std::endl;
-       m_packetcounter.add((u16)command);
+       infostream << "Client: TOCLIENT_INIT received with "
+                       "deployed=" << ((int)deployed & 0xff) << std::endl;
 
-       /*
-               If this check is removed, be sure to change the queue
-               system to know the ids
-       */
-       if(sender_peer_id != PEER_ID_SERVER)
-       {
-               infostream<<"Client::ProcessData(): Discarding data not "
-                               "coming from server: peer_id="<<sender_peer_id
-                               <<std::endl;
+       if(!ser_ver_supported(deployed)) {
+               infostream << "Client: TOCLIENT_INIT: Server sent "
+                               << "unsupported ser_fmt_ver"<< std::endl;
                return;
        }
 
-       u8 ser_version = m_server_ser_ver;
+       m_server_ser_ver = deployed;
 
-       if(command == TOCLIENT_INIT)
-       {
-               if(datasize < 3)
-                       return;
+       // 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);
 
-               u8 deployed = data[2];
 
-               infostream<<"Client: TOCLIENT_INIT received with "
-                               "deployed="<<((int)deployed&0xff)<<std::endl;
+       // Set player position
+       Player *player = m_env.getLocalPlayer();
+       assert(player != NULL);
+       player->setPosition(playerpos_f);
 
-               if(!ser_ver_supported(deployed))
-               {
-                       infostream<<"Client: TOCLIENT_INIT: Server sent "
-                                       <<"unsupported ser_fmt_ver"<<std::endl;
-                       return;
-               }
+       if(pkt->getSize() >= 1 + 6 + 8) {
+               // Get map seed
+               *pkt >> m_map_seed;
+               infostream << "Client: received map seed: " << m_map_seed << std::endl;
+       }
 
-               m_server_ser_ver = deployed;
+       if(pkt->getSize() >= 1 + 6 + 8 + 4) {
+               *pkt >> m_recommended_send_interval;
+               infostream << "Client: received recommended send interval "
+                               << m_recommended_send_interval<<std::endl;
+       }
 
-               // Get player position
-               v3s16 playerpos_s16(0, BS*2+BS*20, 0);
-               if(datasize >= 2+1+6)
-                       playerpos_s16 = readV3S16(&data[2+1]);
-               v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
+       // Reply to server
+       u32 replysize = 2;
+       SharedBuffer<u8> reply(replysize);
+       writeU16(&reply[0], TOSERVER_INIT2);
+       // Send as reliable
+       m_con.Send(PEER_ID_SERVER, 1, reply, true);
 
+       m_state = LC_Init;
+}
 
-               // Set player position
-               Player *player = m_env.getLocalPlayer();
-               assert(player != NULL);
-               player->setPosition(playerpos_f);
+void Client::handleCommand_AccessDenied(ToClientPacket* 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->getSize() >= 2) {
+               *pkt >> m_access_denied_reason;
+       }
+}
 
-               if(datasize >= 2+1+6+8)
-               {
-                       // Get map seed
-                       m_map_seed = readU64(&data[2+1+6]);
-                       infostream<<"Client: received map seed: "<<m_map_seed<<std::endl;
-               }
+void Client::handleCommand_RemoveNode(ToClientPacket* pkt)
+{
+       if(pkt->getSize() < 6)
+               return;
 
-               if(datasize >= 2+1+6+8+4)
-               {
-                       // Get map seed
-                       m_recommended_send_interval = readF1000(&data[2+1+6+8]);
-                       infostream<<"Client: received recommended send interval "
-                                       <<m_recommended_send_interval<<std::endl;
-               }
+       v3s16 p;
+       *pkt >> p.X;
+       *pkt >> p.Y;
+       *pkt >> p.Z;
+       removeNode(p);
+}
 
-               // Reply to server
-               u32 replysize = 2;
-               SharedBuffer<u8> reply(replysize);
-               writeU16(&reply[0], TOSERVER_INIT2);
-               // Send as reliable
-               m_con.Send(PEER_ID_SERVER, 1, reply, true);
+void Client::handleCommand_AddNode(ToClientPacket* pkt)
+{
+       if(pkt->getSize() < 6 + MapNode::serializedLength(m_server_ser_ver))
+               return;
 
-               m_state = LC_Init;
+       v3s16 p;
+       *pkt >> p.X;
+       *pkt >> p.Y;
+       *pkt >> p.Z;
 
-               return;
+       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;
        }
 
-       if(command == TOCLIENT_ACCESS_DENIED)
-       {
-               // 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(datasize >= 4)
-               {
-                       std::string datastring((char*)&data[2], datasize-2);
-                       std::istringstream is(datastring, std::ios_base::binary);
-                       m_access_denied_reason = deSerializeWideString(is);
-               }
+       addNode(p, n, remove_metadata);
+}
+void Client::handleCommand_BlockData(ToClientPacket* pkt)
+{
+       // Ignore too small packet
+       if(pkt->getSize() < 6)
                return;
+
+       v3s16 p;
+       *pkt >> p.X;
+       *pkt >> p.Y;
+       *pkt >> p.Z;
+
+       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(ser_version == SER_FMT_VER_INVALID)
-       {
-               infostream<<"Client: Server serialization"
-                               " format invalid or not initialized."
-                               " Skipping incoming command="<<command<<std::endl;
-               return;
+       if (localdb != NULL) {
+               ((ServerMap&) localserver->getMap()).saveBlock(block, localdb);
        }
 
        /*
-         Handle runtime commands
+               Add it to mesh update queue and set it to be acknowledged after update.
        */
-       // there's no sane reason why we shouldn't have a player and
-       // almost everyone needs a player reference
-       Player *player = m_env.getLocalPlayer();
-       assert(player != NULL);
+       addUpdateMeshTaskWithEdge(p, true);
+}
 
-       if(command == TOCLIENT_REMOVENODE)
-       {
-               if(datasize < 8)
-                       return;
-               v3s16 p;
-               p.X = readS16(&data[2]);
-               p.Y = readS16(&data[4]);
-               p.Z = readS16(&data[6]);
-               removeNode(p);
-       }
-       else if(command == TOCLIENT_ADDNODE)
-       {
-               if(datasize < 8 + MapNode::serializedLength(ser_version))
-                       return;
+void Client::handleCommand_Inventory(ToClientPacket* pkt)
+{
+       if(pkt->getSize() < 1)
+               return;
 
-               v3s16 p;
-               p.X = readS16(&data[2]);
-               p.Y = readS16(&data[4]);
-               p.Z = readS16(&data[6]);
+       std::string datastring(pkt->getString(0), pkt->getSize());
+       std::istringstream is(datastring, std::ios_base::binary);
 
-               MapNode n;
-               n.deSerialize(&data[8], ser_version);
+       Player *player = m_env.getLocalPlayer();
+       assert(player != NULL);
 
-               bool remove_metadata = true;
-               u32 index = 8 + MapNode::serializedLength(ser_version);
-               if ((datasize >= index+1) && data[index]){
-                       remove_metadata = false;
-               }
+       player->inventory.deSerialize(is);
 
-               addNode(p, n, remove_metadata);
-       }
-       else if(command == TOCLIENT_BLOCKDATA)
-       {
-               // Ignore too small packet
-               if(datasize < 8)
-                       return;
+       m_inventory_updated = true;
 
-               v3s16 p;
-               p.X = readS16(&data[2]);
-               p.Y = readS16(&data[4]);
-               p.Z = readS16(&data[6]);
+       delete m_inventory_from_server;
+       m_inventory_from_server = new Inventory(player->inventory);
+       m_inventory_from_server_age = 0.0;
+}
+
+void Client::handleCommand_TimeOfDay(ToClientPacket* pkt)
+{
+       if(pkt->getSize() < 2)
+               return;
 
-               std::string datastring((char*)&data[8], datasize-8);
-               std::istringstream istr(datastring, std::ios_base::binary);
+       u16 time_of_day;
 
-               MapSector *sector;
-               MapBlock *block;
+       *pkt >> time_of_day;
 
-               v2s16 p2d(p.X, p.Z);
-               sector = m_env.getMap().emergeSector(p2d);
+       time_of_day      = time_of_day % 24000;
+       float time_speed = 0;
 
-               assert(sector->getPos() == p2d);
+       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;
 
-               block = sector->getBlockNoCreateNoEx(p.Y);
-               if(block)
-               {
-                       /*
-                               Update an existing block
-                       */
-                       block->deSerialize(istr, ser_version, false);
-                       block->deSerializeNetworkSpecific(istr);
-               }
+               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
-               {
-                       /*
-                               Create a new block
-                       */
-                       block = new MapBlock(&m_env.getMap(), p, this);
-                       block->deSerialize(istr, ser_version, false);
-                       block->deSerializeNetworkSpecific(istr);
-                       sector->insertBlock(block);
-               }
+                       tod_diff_f = time_of_day_f - m_last_time_of_day_f;
 
-               if (localdb != NULL) {
-                       ((ServerMap&) localserver->getMap()).saveBlock(block, localdb);
-               }
+               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;
 
-               /*
-                       Add it to mesh update queue and set it to be acknowledged after update.
-               */
-               addUpdateMeshTaskWithEdge(p, true);
+               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;
+               }
        }
-       else if(command == TOCLIENT_INVENTORY)
-       {
-               if(datasize < 3)
-                       return;
 
-               std::string datastring((char*)&data[2], datasize-2);
-               std::istringstream is(datastring, std::ios_base::binary);
+       // Update environment
+       m_env.setTimeOfDay(time_of_day);
+       m_env.setTimeOfDaySpeed(time_speed);
+       m_time_of_day_set = true;
 
-               player->inventory.deSerialize(is);
+       u32 dr = m_env.getDayNightRatio();
+       infostream << "Client: time_of_day=" << time_of_day
+                       << " time_speed=" << time_speed
+                       << " dr=" << dr << std::endl;
+}
 
-               m_inventory_updated = true;
+void Client::handleCommand_ChatMessage(ToClientPacket* pkt)
+{
+       /*
+               u16 command
+               u16 length
+               wstring message
+       */
+       u16 len, read_wchar;
 
-               delete m_inventory_from_server;
-               m_inventory_from_server = new Inventory(player->inventory);
-               m_inventory_from_server_age = 0.0;
+       *pkt >> len;
 
+       std::wstring message;
+       for(unsigned int i=0; i<len; i++) {
+               *pkt >> read_wchar;
+               message += (wchar_t)read_wchar;
        }
-       else if(command == TOCLIENT_TIME_OF_DAY)
-       {
-               if(datasize < 4)
-                       return;
 
-               u16 time_of_day  = readU16(&data[2]);
-               time_of_day      = time_of_day % 24000;
-               float time_speed = 0;
+       m_chat_queue.push_back(message);
+}
 
-               if(datasize >= 2 + 2 + 4)
-               {
-                       time_speed = readF1000(&data[4]);
+void Client::handleCommand_ActiveObjectRemoveAdd(ToClientPacket* pkt)
+{
+       /*
+               u16 command
+               u16 count of removed objects
+               for all removed objects {
+                       u16 id
                }
-               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;
-                       }
+               u16 count of added objects
+               for all added objects {
+                       u16 id
+                       u8 type
+                       u32 initialization data length
+                       string initialization data
                }
+       */
 
-               // Update environment
-               m_env.setTimeOfDay(time_of_day);
-               m_env.setTimeOfDaySpeed(time_speed);
-               m_time_of_day_set = true;
+       // Read removed objects
+       u8 type;
+       u16 removed_count, added_count, id;
 
-               u32 dr = m_env.getDayNightRatio();
-               infostream<<"Client: time_of_day="<<time_of_day
-                               <<" time_speed="<<time_speed
-                               <<" dr="<<dr<<std::endl;
-       }
-       else if(command == TOCLIENT_CHAT_MESSAGE)
-       {
-               /*
-                       u16 command
-                       u16 length
-                       wstring message
-               */
-               u8 buf[6];
-               std::string datastring((char*)&data[2], datasize-2);
-               std::istringstream is(datastring, std::ios_base::binary);
+       *pkt >> removed_count;
 
-               // Read stuff
-               is.read((char*) buf, 2);
-               u16 len = readU16(buf);
+       for(u16 i=0; i<removed_count; i++) {
+               *pkt >> id;
+               m_env.removeActiveObject(id);
+       }
 
-               std::wstring message;
-               for(unsigned int i=0; i<len; i++)
-               {
-                       is.read((char*)buf, 2);
-                       message += (wchar_t)readU16(buf);
-               }
+       // Read added objects
+       *pkt >> added_count;
 
-               m_chat_queue.push_back(message);
+       for(u16 i=0; i<added_count; i++) {
+               *pkt >> id >> type;
+               m_env.addActiveObject(id, type, pkt->readLongString());
        }
-       else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
-       {
-               /*
-                       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
-                       }
-               */
-
-               char buf[6];
-               // Get all data except the command number
-               std::string datastring((char*)&data[2], datasize-2);
-               // Throw them in an istringstream
-               std::istringstream is(datastring, std::ios_base::binary);
+}
 
-               // Read removed objects
-               is.read(buf, 2);
-               u16 removed_count = readU16((u8*)buf);
-               for(unsigned int i=0; i<removed_count; i++)
+void Client::handleCommand_ActiveObjectMessages(ToClientPacket* pkt)
+{
+       /*
+               u16 command
+               for all objects
                {
-                       is.read(buf, 2);
-                       u16 id = readU16((u8*)buf);
-                       m_env.removeActiveObject(id);
+                       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);
 
-               // Read added objects
+       while(is.eof() == false) {
+               is.read(buf, 2);
+               u16 id = readU16((u8*)buf);
+               if(is.eof())
+                       break;
                is.read(buf, 2);
-               u16 added_count = readU16((u8*)buf);
-               for(unsigned int i=0; i<added_count; i++)
+               size_t message_size = readU16((u8*)buf);
+               std::string message;
+               message.reserve(message_size);
+               for(unsigned int i=0; i<message_size; i++)
                {
-                       is.read(buf, 2);
-                       u16 id = readU16((u8*)buf);
                        is.read(buf, 1);
-                       u8 type = readU8((u8*)buf);
-                       std::string data = deSerializeLongString(is);
-                       // Add it
-                       m_env.addActiveObject(id, type, data);
+                       message.append(buf, 1);
                }
+               // Pass on to the environment
+               m_env.processActiveObjectMessage(id, message);
        }
-       else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
-       {
-               /*
-                       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((char*)&data[2], datasize-2);
-               // 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(unsigned int i=0; i<message_size; i++)
-                       {
-                               is.read(buf, 1);
-                               message.append(buf, 1);
-                       }
-                       // Pass on to the environment
-                       m_env.processActiveObjectMessage(id, message);
-               }
-       }
-       else if(command == TOCLIENT_MOVEMENT)
-       {
-               std::string datastring((char*)&data[2], datasize-2);
-               std::istringstream is(datastring, std::ios_base::binary);
-
-               player->movement_acceleration_default   = readF1000(is) * BS;
-               player->movement_acceleration_air       = readF1000(is) * BS;
-               player->movement_acceleration_fast      = readF1000(is) * BS;
-               player->movement_speed_walk             = readF1000(is) * BS;
-               player->movement_speed_crouch           = readF1000(is) * BS;
-               player->movement_speed_fast             = readF1000(is) * BS;
-               player->movement_speed_climb            = readF1000(is) * BS;
-               player->movement_speed_jump             = readF1000(is) * BS;
-               player->movement_liquid_fluidity        = readF1000(is) * BS;
-               player->movement_liquid_fluidity_smooth = readF1000(is) * BS;
-               player->movement_liquid_sink            = readF1000(is) * BS;
-               player->movement_gravity                = readF1000(is) * BS;
-       }
-       else if(command == TOCLIENT_HP)
-       {
-               std::string datastring((char*)&data[2], datasize-2);
-               std::istringstream is(datastring, std::ios_base::binary);
+void Client::handleCommand_Movement(ToClientPacket* pkt)
+{
+       Player *player = m_env.getLocalPlayer();
+       assert(player != NULL);
 
-               u8 oldhp   = player->hp;
-               u8 hp      = readU8(is);
-               player->hp = hp;
+       float mad, maa, maf, msw, mscr, msf, mscl, msj, lf, lfs, ls, g;
 
-               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_back(event);
-               }
-       }
-       else if(command == TOCLIENT_BREATH)
-       {
-               std::string datastring((char*)&data[2], datasize-2);
-               std::istringstream is(datastring, std::ios_base::binary);
+       *pkt >> mad >> maa >> maf >> msw >> mscr >> msf >> mscl >> msj
+               >> lf >> lfs >> ls >> g;
 
-               player->setBreath(readU16(is));
-       }
-       else if(command == TOCLIENT_MOVE_PLAYER)
-       {
-               std::string datastring((char*)&data[2], datasize-2);
-               std::istringstream is(datastring, std::ios_base::binary);
-
-               v3f pos = readV3F1000(is);
-               f32 pitch = readF1000(is);
-               f32 yaw = readF1000(is);
-               player->setPosition(pos);
-
-               infostream<<"Client got TOCLIENT_MOVE_PLAYER"
-                               <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
-                               <<" pitch="<<pitch
-                               <<" yaw="<<yaw
-                               <<std::endl;
+       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;
+}
 
-               /*
-                       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_back(event);
+void Client::handleCommand_HP(ToClientPacket* pkt)
+{
 
-               // Ignore damage for a few seconds, so that the player doesn't
-               // get damage from falling on ground
-               m_ignore_damage_timer = 3.0;
-       }
-       else if(command == TOCLIENT_PLAYERITEM)
-       {
-               infostream<<"Client: WARNING: Ignoring TOCLIENT_PLAYERITEM"<<std::endl;
-       }
-       else if(command == TOCLIENT_DEATHSCREEN)
-       {
-               std::string datastring((char*)&data[2], datasize-2);
-               std::istringstream is(datastring, std::ios_base::binary);
+       Player *player = m_env.getLocalPlayer();
+       assert(player != NULL);
+
+       u8 oldhp   = player->hp;
 
-               bool set_camera_point_target = readU8(is);
-               v3f camera_point_target = readV3F1000(is);
+       u8 hp;
+       *pkt >> hp;
 
+       player->hp = hp;
+
+       if(hp < oldhp) {
+               // Add to ClientEvent queue
                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;
+               event.type = CE_PLAYER_DAMAGE;
+               event.player_damage.amount = oldhp - hp;
                m_client_event_queue.push_back(event);
        }
-       else if(command == TOCLIENT_ANNOUNCE_MEDIA)
-       {
-               std::string datastring((char*)&data[2], datasize-2);
-               std::istringstream is(datastring, std::ios_base::binary);
-
-               int num_files = readU16(is);
-
-               infostream<<"Client: Received media announcement: packet size: "
-                               <<datasize<<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="<<datasize<<std::endl;
-                       return;
-               }
+}
 
-               // Mesh update thread must be stopped while
-               // updating content definitions
-               assert(!m_mesh_update_thread.IsRunning());
+void Client::handleCommand_Breath(ToClientPacket* pkt)
+{
+       Player *player = m_env.getLocalPlayer();
+       assert(player != NULL);
 
-               for(int i=0; i<num_files; i++)
-               {
-                       std::string name = deSerializeString(is);
-                       std::string sha1_base64 = deSerializeString(is);
-                       std::string sha1_raw = base64_decode(sha1_base64);
-                       m_media_downloader->addFile(name, sha1_raw);
-               }
+       u16 breath;
 
-               std::vector<std::string> remote_media;
-               try {
-                       Strfnd sf(deSerializeString(is));
-                       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
-               }
+       *pkt >> breath;
 
-               m_media_downloader->step(this);
+       player->setBreath(breath);
+}
+
+void Client::handleCommand_MovePlayer(ToClientPacket* 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_back(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(ToClientPacket* pkt)
+{
+       infostream << "Client: WARNING: Ignoring TOCLIENT_PLAYERITEM" << std::endl;
+}
+
+void Client::handleCommand_DeathScreen(ToClientPacket* 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_back(event);
+}
+
+void Client::handleCommand_AnnounceMedia(ToClientPacket* 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;
        }
-       else if(command == TOCLIENT_MEDIA)
-       {
-               std::string datastring((char*)&data[2], datasize-2);
-               std::istringstream is(datastring, std::ios_base::binary);
 
-               /*
-                       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
-                       }
-               */
-               int num_bunches = readU16(is);
-               int bunch_i = readU16(is);
-               u32 num_files = readU32(is);
-               infostream<<"Client: Received files: bunch "<<bunch_i<<"/"
-                               <<num_bunches<<" files="<<num_files
-                               <<" size="<<datasize<<std::endl;
-
-               if (num_files == 0)
-                       return;
+       // Mesh update thread must be stopped while
+       // updating content definitions
+       assert(!m_mesh_update_thread.IsRunning());
 
-               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="<<datasize<<std::endl;
-                       return;
-               }
+       for(int i=0; i<num_files; i++) {
+               std::string name, sha1_base64;
 
-               // Mesh update thread must be stopped while
-               // updating content definitions
-               assert(!m_mesh_update_thread.IsRunning());
+               *pkt >> name >> sha1_base64;
+
+               std::string sha1_raw = base64_decode(sha1_base64);
+               m_media_downloader->addFile(name, sha1_raw);
+       }
 
-               for(unsigned int i=0; i<num_files; i++){
-                       std::string name = deSerializeString(is);
-                       std::string data = deSerializeLongString(is);
-                       m_media_downloader->conventionalTransferDone(
-                                       name, data, this);
+       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);
                }
        }
-       else if(command == TOCLIENT_TOOLDEF)
-       {
-               infostream<<"Client: WARNING: Ignoring TOCLIENT_TOOLDEF"<<std::endl;
+       catch(SerializationError& e) {
+               // not supported by server or turned off
        }
-       else if(command == TOCLIENT_NODEDEF)
-       {
-               infostream<<"Client: Received node definitions: packet size: "
-                               <<datasize<<std::endl;
-
-               // Mesh update thread must be stopped while
-               // updating content definitions
-               assert(!m_mesh_update_thread.IsRunning());
-
-               // Decompress node definitions
-               std::string datastring((char*)&data[2], datasize-2);
-               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;
-       }
-       else if(command == TOCLIENT_CRAFTITEMDEF)
-       {
-               infostream<<"Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF"<<std::endl;
+
+       m_media_downloader->step(this);
+}
+
+void Client::handleCommand_Media(ToClientPacket* 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;
        }
-       else if(command == TOCLIENT_ITEMDEF)
-       {
-               infostream<<"Client: Received item definitions: packet size: "
-                               <<datasize<<std::endl;
-
-               // Mesh update thread must be stopped while
-               // updating content definitions
-               assert(!m_mesh_update_thread.IsRunning());
-
-               // Decompress item definitions
-               std::string datastring((char*)&data[2], datasize-2);
-               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;
-       }
-       else if(command == TOCLIENT_PLAY_SOUND)
-       {
-               std::string datastring((char*)&data[2], datasize-2);
-               std::istringstream is(datastring, std::ios_base::binary);
-
-               s32 server_id = readS32(is);
-               std::string name = deSerializeString(is);
-               float gain = readF1000(is);
-               int type = readU8(is); // 0=local, 1=positional, 2=object
-               v3f pos = readV3F1000(is);
-               u16 object_id = readU16(is);
-               bool loop = readU8(is);
-               // Start playing
-               int client_id = -1;
-               switch(type){
+
+       // Mesh update thread must be stopped while
+       // updating content definitions
+       assert(!m_mesh_update_thread.IsRunning());
+
+       for(unsigned int 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(ToClientPacket* pkt)
+{
+       infostream << "Client: WARNING: Ignoring TOCLIENT_TOOLDEF" << std::endl;
+}
+
+void Client::handleCommand_NodeDef(ToClientPacket* pkt)
+{
+       infostream << "Client: Received node definitions: packet size: "
+                       << pkt->getSize() << std::endl;
+
+       // Mesh update thread must be stopped while
+       // updating content definitions
+       assert(!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(ToClientPacket* pkt)
+{
+       infostream << "Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF" << std::endl;
+}
+
+void Client::handleCommand_ItemDef(ToClientPacket* pkt)
+{
+       infostream << "Client: Received item definitions: packet size: "
+                       << pkt->getSize() << std::endl;
+
+       // Mesh update thread must be stopped while
+       // updating content definitions
+       assert(!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(ToClientPacket* 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
+               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;
-               }
+               default:
+                       break;
        }
-       else if(command == TOCLIENT_STOP_SOUND)
-       {
-               std::string datastring((char*)&data[2], datasize-2);
-               std::istringstream is(datastring, std::ios_base::binary);
 
-               s32 server_id = readS32(is);
-               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);
-               }
-       }
-       else if(command == TOCLIENT_PRIVILEGES)
-       {
-               std::string datastring((char*)&data[2], datasize-2);
-               std::istringstream is(datastring, std::ios_base::binary);
-
-               m_privileges.clear();
-               infostream<<"Client: Privileges updated: ";
-               u16 num_privileges = readU16(is);
-               for(unsigned int i=0; i<num_privileges; i++){
-                       std::string priv = deSerializeString(is);
-                       m_privileges.insert(priv);
-                       infostream<<priv<<" ";
-               }
-               infostream<<std::endl;
+       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;
        }
-       else if(command == TOCLIENT_INVENTORY_FORMSPEC)
-       {
-               std::string datastring((char*)&data[2], datasize-2);
-               std::istringstream is(datastring, std::ios_base::binary);
+}
 
-               // Store formspec in LocalPlayer
-               player->inventory_formspec = deSerializeLongString(is);
-       }
-       else if(command == TOCLIENT_DETACHED_INVENTORY)
-       {
-               std::string datastring((char*)&data[2], datasize-2);
-               std::istringstream is(datastring, std::ios_base::binary);
+void Client::handleCommand_StopSound(ToClientPacket* pkt)
+{
+       s32 server_id;
 
-               std::string name = deSerializeString(is);
+       *pkt >> server_id;
 
-               infostream<<"Client: Detached inventory update: \""<<name<<"\""<<std::endl;
+       std::map<s32, int>::iterator i =
+               m_sounds_server_to_client.find(server_id);
 
-               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);
+       if(i != m_sounds_server_to_client.end()) {
+               int client_id = i->second;
+               m_sound->stopSound(client_id);
        }
-       else if(command == TOCLIENT_SHOW_FORMSPEC)
-       {
-               std::string datastring((char*)&data[2], datasize-2);
-               std::istringstream is(datastring, std::ios_base::binary);
+}
 
-               std::string formspec = deSerializeLongString(is);
-               std::string formname = deSerializeString(is);
+void Client::handleCommand_Privileges(ToClientPacket* pkt)
+{
+       m_privileges.clear();
+       infostream << "Client: Privileges updated: ";
+       u16 num_privileges;
 
-               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_back(event);
-       }
-       else if(command == TOCLIENT_SPAWN_PARTICLE)
-       {
-               std::string datastring((char*)&data[2], datasize-2);
-               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 (...) {}
+       *pkt >> num_privileges;
 
-               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);
+       for(unsigned int i=0; i<num_privileges; i++) {
+               std::string priv;
 
-               m_client_event_queue.push_back(event);
+               *pkt >> priv;
+
+               m_privileges.insert(priv);
+               infostream << priv << " ";
        }
-       else if(command == TOCLIENT_ADD_PARTICLESPAWNER)
-       {
-               std::string datastring((char*)&data[2], datasize-2);
-               std::istringstream is(datastring, std::ios_base::binary);
-
-               u16 amount              = readU16(is);
-               float spawntime         = readF1000(is);
-               v3f minpos              = readV3F1000(is);
-               v3f maxpos              = readV3F1000(is);
-               v3f minvel              = readV3F1000(is);
-               v3f maxvel              = readV3F1000(is);
-               v3f minacc              = readV3F1000(is);
-               v3f maxacc              = readV3F1000(is);
-               float minexptime        = readF1000(is);
-               float maxexptime        = readF1000(is);
-               float minsize           = readF1000(is);
-               float maxsize           = readF1000(is);
-               bool collisiondetection = readU8(is);
-               std::string texture     = deSerializeLongString(is);
-               u32 id                  = readU32(is);
-               bool vertical = false;
-               try {
-                       vertical = readU8(is);
-               } catch (...) {}
+       infostream << std::endl;
+}
 
-               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;
+void Client::handleCommand_InventoryFormSpec(ToClientPacket* pkt)
+{
+       Player *player = m_env.getLocalPlayer();
+       assert(player != NULL);
 
-               m_client_event_queue.push_back(event);
-       }
-       else if(command == TOCLIENT_DELETE_PARTICLESPAWNER)
-       {
-               std::string datastring((char*)&data[2], datasize-2);
-               std::istringstream is(datastring, std::ios_base::binary);
+       // Store formspec in LocalPlayer
+       player->inventory_formspec = pkt->readLongString();
+}
 
-               u32 id = readU16(is);
+void Client::handleCommand_DetachedInventory(ToClientPacket* pkt)
+{
+       std::string datastring(pkt->getString(0), pkt->getSize());
+       std::istringstream is(datastring, std::ios_base::binary);
 
-               ClientEvent event;
-               event.type                      = CE_DELETE_PARTICLESPAWNER;
-               event.delete_particlespawner.id = id;
+       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(ToClientPacket* pkt)
+{
+       std::string formspec = pkt->readLongString();
+       std::string formname;
 
-               m_client_event_queue.push_back(event);
-       }
-       else if(command == TOCLIENT_HUDADD)
-       {
-               std::string datastring((char *)&data[2], datasize - 2);
-               std::istringstream is(datastring, std::ios_base::binary);
-
-               u32 id           = readU32(is);
-               u8 type          = readU8(is);
-               v2f pos          = readV2F1000(is);
-               std::string name = deSerializeString(is);
-               v2f scale        = readV2F1000(is);
-               std::string text = deSerializeString(is);
-               u32 number       = readU32(is);
-               u32 item         = readU32(is);
-               u32 dir          = readU32(is);
-               v2f align        = readV2F1000(is);
-               v2f offset       = readV2F1000(is);
-               v3f world_pos;
-               v2s32 size;
-               try{
-                       world_pos    = readV3F1000(is);
-               }catch(SerializationError &e) {};
-               try{
-                       size = readV2S32(is);
-               } catch(SerializationError &e) {};
+       *pkt >> formname;
 
-               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_back(event);
-       }
-       else if(command == TOCLIENT_HUDRM)
-       {
-               std::string datastring((char *)&data[2], datasize - 2);
-               std::istringstream is(datastring, std::ios_base::binary);
+       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_back(event);
+}
 
-               u32 id = readU32(is);
+void Client::handleCommand_SpawnParticle(ToClientPacket* pkt)
+{
+       std::string datastring(pkt->getString(0), pkt->getSize());
+       std::istringstream is(datastring, std::ios_base::binary);
 
-               ClientEvent event;
-               event.type     = CE_HUDRM;
-               event.hudrm.id = id;
-               m_client_event_queue.push_back(event);
-       }
-       else if(command == TOCLIENT_HUDCHANGE)
-       {
-               std::string sdata;
-               v2f v2fdata;
-               v3f v3fdata;
-               u32 intdata = 0;
-               v2s32 v2s32data;
-
-               std::string datastring((char *)&data[2], datasize - 2);
-               std::istringstream is(datastring, std::ios_base::binary);
-
-               u32 id  = readU32(is);
-               u8 stat = (HudElementStat)readU8(is);
-
-               if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
-                       stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
-                       v2fdata = readV2F1000(is);
-               else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
-                       sdata = deSerializeString(is);
-               else if (stat == HUD_STAT_WORLD_POS)
-                       v3fdata = readV3F1000(is);
-               else if (stat == HUD_STAT_SIZE )
-                       v2s32data = readV2S32(is);
-               else
-                       intdata = readU32(is);
+       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_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_back(event);
-       }
-       else if(command == TOCLIENT_HUD_SET_FLAGS)
-       {
-               std::string datastring((char *)&data[2], datasize - 2);
-               std::istringstream is(datastring, std::ios_base::binary);
+       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);
 
-               u32 flags = readU32(is);
-               u32 mask  = readU32(is);
+       m_client_event_queue.push_back(event);
+}
 
-               player->hud_flags &= ~mask;
-               player->hud_flags |= flags;
-       }
-       else if(command == TOCLIENT_HUD_SET_PARAM)
-       {
-               std::string datastring((char *)&data[2], datasize - 2);
-               std::istringstream is(datastring, std::ios_base::binary);
+void Client::handleCommand_AddParticleSpawner(ToClientPacket* 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;
 
-               u16 param         = readU16(is);
-               std::string value = deSerializeString(is);
+       *pkt >> amount >> spawntime >> minpos >> maxpos >> minvel >> maxvel
+               >> minacc >> maxacc >> minexptime >> maxexptime >> minsize
+               >> maxsize >> collisiondetection;
 
-               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;
-               }
-       }
-       else if(command == TOCLIENT_SET_SKY)
-       {
-               std::string datastring((char *)&data[2], datasize - 2);
-               std::istringstream is(datastring, std::ios_base::binary);
+       std::string texture = pkt->readLongString();
 
-               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>;
+       *pkt >> id;
 
-               for(size_t i=0; i<count; i++)
-                       params->push_back(deSerializeString(is));
+       bool vertical = false;
+       try {
+               *pkt >> vertical;
+       } catch (...) {}
 
-               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_back(event);
+       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_back(event);
+}
+
+void Client::handleCommand_DeleteParticleSpawner(ToClientPacket* pkt)
+{
+       u16 id;
+
+       *pkt >> id;
+
+       ClientEvent event;
+       event.type                      = CE_DELETE_PARTICLESPAWNER;
+       event.delete_particlespawner.id = id;
+
+       m_client_event_queue.push_back(event);
+}
+
+void Client::handleCommand_HudAdd(ToClientPacket* 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_back(event);
+}
+
+void Client::handleCommand_HudRemove(ToClientPacket* pkt)
+{
+       u32 id;
+
+       *pkt >> id;
+
+       ClientEvent event;
+       event.type     = CE_HUDRM;
+       event.hudrm.id = id;
+       m_client_event_queue.push_back(event);
+}
+
+void Client::handleCommand_HudChange(ToClientPacket* 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_back(event);
+}
+
+void Client::handleCommand_HudSetFlags(ToClientPacket* 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(ToClientPacket* 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(command == TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO)
-       {
-               std::string datastring((char *)&data[2], datasize - 2);
-               std::istringstream is(datastring, std::ios_base::binary);
+       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;
+       }
+}
 
-               bool do_override        = readU8(is);
-               float day_night_ratio_f = (float)readU16(is) / 65536;
+void Client::handleCommand_HudSetSky(ToClientPacket* pkt)
+{
+       std::string datastring(pkt->getString(0), pkt->getSize());
+       std::istringstream is(datastring, std::ios_base::binary);
 
-               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_back(event);
+       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_back(event);
+}
+
+void Client::handleCommand_OverrideDayNightRatio(ToClientPacket* 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_back(event);
+}
+
+void Client::handleCommand_LocalPlayerAnimations(ToClientPacket* 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(ToClientPacket* pkt)
+{
+       LocalPlayer *player = m_env.getLocalPlayer();
+       assert(player != NULL);
+
+       *pkt >> player->eye_offset_first >> player->eye_offset_third;
+}
+
+inline void Client::handleCommand(ToClientPacket* pkt)
+{
+       const ToClientCommandHandler& opHandle = toClientCommandTable[pkt->getCommand()];
+       (this->*opHandle.handler)(pkt);
+}
+
+/*
+       sender_peer_id given to this shall be quaranteed to be a valid peer
+*/
+void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
+{
+       DSTACK(__FUNCTION_NAME);
+
+       // Ignore packets that don't even fit a command
+       if(datasize < 2) {
+               m_packetcounter.add(60000);
+               return;
        }
-       else if(command == TOCLIENT_LOCAL_PLAYER_ANIMATIONS)
-       {
-               std::string datastring((char *)&data[2], datasize - 2);
-               std::istringstream is(datastring, std::ios_base::binary);
 
-               LocalPlayer *player = m_env.getLocalPlayer();
-               assert(player != NULL);
+       ToClientPacket* pkt = new ToClientPacket(data, datasize, sender_peer_id);
+
+       ToClientCommand command = pkt->getCommand();
 
-               player->local_animations[0] = readV2S32(is);
-               player->local_animations[1] = readV2S32(is);
-               player->local_animations[2] = readV2S32(is);
-               player->local_animations[3] = readV2S32(is);
-               player->local_animation_speed = readF1000(is);
+       //infostream<<"Client: received command="<<command<<std::endl;
+       m_packetcounter.add((u16)command);
+
+       /*
+               If this check is removed, be sure to change the queue
+               system to know the ids
+       */
+       if(sender_peer_id != PEER_ID_SERVER) {
+               infostream << "Client::ProcessData(): Discarding data not "
+                       "coming from server: peer_id=" << sender_peer_id
+                       << std::endl;
+               delete pkt;
+               return;
        }
-       else if(command == TOCLIENT_EYE_OFFSET)
-       {
-               std::string datastring((char *)&data[2], datasize - 2);
-               std::istringstream is(datastring, std::ios_base::binary);
 
-               LocalPlayer *player = m_env.getLocalPlayer();
-               assert(player != NULL);
+       // Command must be handled into ToClientCommandHandler
+       if (command >= TOCLIENT_NUM_MSG_TYPES) {
+               infostream << "Client: Ignoring unknown command "
+                       << command << std::endl;
+       }
 
-               player->eye_offset_first = readV3F1000(is);
-               player->eye_offset_third = readV3F1000(is);
+       /*
+        * Those packets are handled before m_server_ser_ver is set, it's normal
+        * But we must use the new ToClientConnectionState in the future,
+        * as a byte mask
+        */
+       if(toClientCommandTable[command].state == TOCLIENT_STATE_NOT_CONNECTED) {
+               handleCommand(pkt);
+               delete pkt;
+               return;
        }
-       else
-       {
-               infostream<<"Client: Ignoring unknown command "
-                               <<command<<std::endl;
+
+       if(m_server_ser_ver == SER_FMT_VER_INVALID) {
+               infostream << "Client: Server serialization"
+                               " format invalid or not initialized."
+                               " Skipping incoming command=" << command << std::endl;
+               delete pkt;
+               return;
        }
+
+       /*
+         Handle runtime commands
+       */
+
+       handleCommand(pkt);
+       delete pkt;
 }
 
 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
@@ -2900,4 +2968,3 @@ scene::IAnimatedMesh* Client::getMesh(const std::string &filename)
        smgr->getMeshCache()->removeMesh(mesh);
        return mesh;
 }
-
index fd7e5f08d8b8b2c58b82ee53dedbc46ab73b1e20..fd43361b11f68d9c65c22a2d87e45b52283055e8 100644 (file)
@@ -34,6 +34,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "localplayer.h"
 #include "hud.h"
 #include "particles.h"
+#include "network/toclientpacket.h"
 
 struct MeshMakeData;
 class MapBlockMesh;
@@ -341,7 +342,57 @@ public:
        */
        void step(float dtime);
 
+       /*
+        * Command Handlers
+        */
+
+       void handleCommand(ToClientPacket* pkt);
+
+       void handleCommand_Null(ToClientPacket* pkt) {};
+       void handleCommand_Deprecated(ToClientPacket* pkt);
+       void handleCommand_Init(ToClientPacket* pkt);
+       void handleCommand_AccessDenied(ToClientPacket* pkt);
+       void handleCommand_RemoveNode(ToClientPacket* pkt);
+       void handleCommand_AddNode(ToClientPacket* pkt);
+       void handleCommand_BlockData(ToClientPacket* pkt);
+       void handleCommand_Inventory(ToClientPacket* pkt);
+       void handleCommand_TimeOfDay(ToClientPacket* pkt);
+       void handleCommand_ChatMessage(ToClientPacket* pkt);
+       void handleCommand_ActiveObjectRemoveAdd(ToClientPacket* pkt);
+       void handleCommand_ActiveObjectMessages(ToClientPacket* pkt);
+       void handleCommand_Movement(ToClientPacket* pkt);
+       void handleCommand_HP(ToClientPacket* pkt);
+       void handleCommand_Breath(ToClientPacket* pkt);
+       void handleCommand_MovePlayer(ToClientPacket* pkt);
+       void handleCommand_PlayerItem(ToClientPacket* pkt);
+       void handleCommand_DeathScreen(ToClientPacket* pkt);
+       void handleCommand_AnnounceMedia(ToClientPacket* pkt);
+       void handleCommand_Media(ToClientPacket* pkt);
+       void handleCommand_ToolDef(ToClientPacket* pkt);
+       void handleCommand_NodeDef(ToClientPacket* pkt);
+       void handleCommand_CraftItemDef(ToClientPacket* pkt);
+       void handleCommand_ItemDef(ToClientPacket* pkt);
+       void handleCommand_PlaySound(ToClientPacket* pkt);
+       void handleCommand_StopSound(ToClientPacket* pkt);
+       void handleCommand_Privileges(ToClientPacket* pkt);
+       void handleCommand_InventoryFormSpec(ToClientPacket* pkt);
+       void handleCommand_DetachedInventory(ToClientPacket* pkt);
+       void handleCommand_ShowFormSpec(ToClientPacket* pkt);
+       void handleCommand_SpawnParticle(ToClientPacket* pkt);
+       void handleCommand_AddParticleSpawner(ToClientPacket* pkt);
+       void handleCommand_DeleteParticleSpawner(ToClientPacket* pkt);
+       void handleCommand_HudAdd(ToClientPacket* pkt);
+       void handleCommand_HudRemove(ToClientPacket* pkt);
+       void handleCommand_HudChange(ToClientPacket* pkt);
+       void handleCommand_HudSetFlags(ToClientPacket* pkt);
+       void handleCommand_HudSetParam(ToClientPacket* pkt);
+       void handleCommand_HudSetSky(ToClientPacket* pkt);
+       void handleCommand_OverrideDayNightRatio(ToClientPacket* pkt);
+       void handleCommand_LocalPlayerAnimations(ToClientPacket* pkt);
+       void handleCommand_EyeOffset(ToClientPacket* pkt);
+
        void ProcessData(u8 *data, u32 datasize, u16 sender_peer_id);
+
        // Returns true if something was received
        bool AsyncProcessPacket();
        bool AsyncProcessData();
@@ -577,4 +628,3 @@ private:
 };
 
 #endif // !CLIENT_HEADER
-
index 434eeb2488369c0ad6a11ae9bbf7c78533802289..7c24b76d0bb8e95dd1c1e6a782d624c577de4526 100644 (file)
@@ -22,7 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "util/string.h"
 #include "httpfetch.h"
 #include "client.h"
-#include "clientserver.h"
+#include "network/networkprotocol.h"
 #include "filecache.h"
 #include "filesys.h"
 #include "hex.h"
diff --git a/src/clientserver.h b/src/clientserver.h
deleted file mode 100644 (file)
index 5f78647..0000000
+++ /dev/null
@@ -1,801 +0,0 @@
-/*
-Minetest
-Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
-
-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.
-*/
-
-#ifndef CLIENTSERVER_HEADER
-#define CLIENTSERVER_HEADER
-#include "util/string.h"
-
-/*
-       changes by PROTOCOL_VERSION:
-
-       PROTOCOL_VERSION 3:
-               Base for writing changes here
-       PROTOCOL_VERSION 4:
-               Add TOCLIENT_MEDIA
-               Add TOCLIENT_TOOLDEF
-               Add TOCLIENT_NODEDEF
-               Add TOCLIENT_CRAFTITEMDEF
-               Add TOSERVER_INTERACT
-               Obsolete TOSERVER_CLICK_ACTIVEOBJECT
-               Obsolete TOSERVER_GROUND_ACTION
-       PROTOCOL_VERSION 5:
-               Make players to be handled mostly as ActiveObjects
-       PROTOCOL_VERSION 6:
-               Only non-cached textures are sent
-       PROTOCOL_VERSION 7:
-               Add TOCLIENT_ITEMDEF
-               Obsolete TOCLIENT_TOOLDEF
-               Obsolete TOCLIENT_CRAFTITEMDEF
-               Compress the contents of TOCLIENT_ITEMDEF and TOCLIENT_NODEDEF
-       PROTOCOL_VERSION 8:
-               Digging based on item groups
-               Many things
-       PROTOCOL_VERSION 9:
-               ContentFeatures and NodeDefManager use a different serialization
-                   format; better for future version cross-compatibility
-               Many things
-       PROTOCOL_VERSION 10:
-               TOCLIENT_PRIVILEGES
-               Version raised to force 'fly' and 'fast' privileges into effect.
-               Node metadata change (came in later; somewhat incompatible)
-       PROTOCOL_VERSION 11:
-               TileDef in ContentFeatures
-               Nodebox drawtype
-               (some dev snapshot)
-               TOCLIENT_INVENTORY_FORMSPEC
-               (0.4.0, 0.4.1)
-       PROTOCOL_VERSION 12:
-               TOSERVER_INVENTORY_FIELDS
-               16-bit node ids
-               TOCLIENT_DETACHED_INVENTORY
-       PROTOCOL_VERSION 13:
-               InventoryList field "Width" (deserialization fails with old versions)
-       PROTOCOL_VERSION 14:
-               Added transfer of player pressed keys to the server
-               Added new messages for mesh and bone animation, as well as attachments
-               GENERIC_CMD_SET_ANIMATION
-               GENERIC_CMD_SET_BONE_POSITION
-               GENERIC_CMD_SET_ATTACHMENT
-       PROTOCOL_VERSION 15:
-               Serialization format changes
-       PROTOCOL_VERSION 16:
-               TOCLIENT_SHOW_FORMSPEC
-       PROTOCOL_VERSION 17:
-               Serialization format change: include backface_culling flag in TileDef
-               Added rightclickable field in nodedef
-               TOCLIENT_SPAWN_PARTICLE
-               TOCLIENT_ADD_PARTICLESPAWNER
-               TOCLIENT_DELETE_PARTICLESPAWNER
-       PROTOCOL_VERSION 18:
-               damageGroups added to ToolCapabilities
-               sound_place added to ItemDefinition
-       PROTOCOL_VERSION 19:
-               GENERIC_CMD_SET_PHYSICS_OVERRIDE
-       PROTOCOL_VERSION 20:
-               TOCLIENT_HUDADD
-               TOCLIENT_HUDRM
-               TOCLIENT_HUDCHANGE
-               TOCLIENT_HUD_SET_FLAGS
-       PROTOCOL_VERSION 21:
-               TOCLIENT_BREATH
-               TOSERVER_BREATH
-               range added to ItemDefinition
-               drowning, leveled and liquid_range added to ContentFeatures
-               stepheight and collideWithObjects added to object properties
-               version, heat and humidity transfer in MapBock
-               automatic_face_movement_dir and automatic_face_movement_dir_offset
-                       added to object properties
-       PROTOCOL_VERSION 22:
-               add swap_node
-       PROTOCOL_VERSION 23:
-               TOSERVER_CLIENT_READY
-       PROTOCOL_VERSION 24:
-               ContentFeatures version 7
-               ContentFeatures: change number of special tiles to 6 (CF_SPECIAL_COUNT)
-*/
-
-#define LATEST_PROTOCOL_VERSION 24
-
-// Server's supported network protocol range
-#define SERVER_PROTOCOL_VERSION_MIN 13
-#define SERVER_PROTOCOL_VERSION_MAX LATEST_PROTOCOL_VERSION
-
-// Client's supported network protocol range
-#define CLIENT_PROTOCOL_VERSION_MIN 13
-#define CLIENT_PROTOCOL_VERSION_MAX LATEST_PROTOCOL_VERSION
-
-// Constant that differentiates the protocol from random data and other protocols
-#define PROTOCOL_ID 0x4f457403
-
-#define PASSWORD_SIZE 28       // Maximum password length. Allows for
-                               // base64-encoded SHA-1 (27+\0).
-
-#define FORMSPEC_API_VERSION 1
-#define FORMSPEC_VERSION_STRING "formspec_version[" TOSTRING(FORMSPEC_API_VERSION) "]"
-
-#define TEXTURENAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_.-"
-
-enum ToClientCommand
-{
-       TOCLIENT_INIT = 0x10,
-       /*
-               Server's reply to TOSERVER_INIT.
-               Sent second after connected.
-
-               [0] u16 TOSERVER_INIT
-               [2] u8 deployed version
-               [3] v3s16 player's position + v3f(0,BS/2,0) floatToInt'd
-               [12] u64 map seed (new as of 2011-02-27)
-               [20] f1000 recommended send interval (in seconds) (new as of 14)
-
-               NOTE: The position in here is deprecated; position is
-                     explicitly sent afterwards
-       */
-
-       TOCLIENT_BLOCKDATA = 0x20, //TODO: Multiple blocks
-       TOCLIENT_ADDNODE = 0x21,
-       /*
-               u16 command
-               v3s16 position
-               serialized mapnode
-               u8 keep_metadata // Added in protocol version 22
-       */
-       TOCLIENT_REMOVENODE = 0x22,
-
-       TOCLIENT_PLAYERPOS = 0x23, // Obsolete
-       /*
-               [0] u16 command
-               // Followed by an arbitary number of these:
-               // Number is determined from packet length.
-               [N] u16 peer_id
-               [N+2] v3s32 position*100
-               [N+2+12] v3s32 speed*100
-               [N+2+12+12] s32 pitch*100
-               [N+2+12+12+4] s32 yaw*100
-       */
-
-       TOCLIENT_PLAYERINFO = 0x24, // Obsolete
-       /*
-               [0] u16 command
-               // Followed by an arbitary number of these:
-               // Number is determined from packet length.
-               [N] u16 peer_id
-               [N] char[20] name
-       */
-
-       TOCLIENT_OPT_BLOCK_NOT_FOUND = 0x25, // Obsolete
-
-       TOCLIENT_SECTORMETA = 0x26, // Obsolete
-       /*
-               [0] u16 command
-               [2] u8 sector count
-               [3...] v2s16 pos + sector metadata
-       */
-
-       TOCLIENT_INVENTORY = 0x27,
-       /*
-               [0] u16 command
-               [2] serialized inventory
-       */
-
-       TOCLIENT_OBJECTDATA = 0x28, // Obsolete
-       /*
-               Sent as unreliable.
-
-               u16 command
-               u16 number of player positions
-               for each player:
-                       u16 peer_id
-                       v3s32 position*100
-                       v3s32 speed*100
-                       s32 pitch*100
-                       s32 yaw*100
-               u16 count of blocks
-               for each block:
-                       v3s16 blockpos
-                       block objects
-       */
-
-       TOCLIENT_TIME_OF_DAY = 0x29,
-       /*
-               u16 command
-               u16 time (0-23999)
-               Added in a later version:
-               f1000 time_speed
-       */
-
-       // (oops, there is some gap here)
-
-       TOCLIENT_CHAT_MESSAGE = 0x30,
-       /*
-               u16 command
-               u16 length
-               wstring message
-       */
-
-       TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD = 0x31,
-       /*
-               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
-               }
-       */
-
-       TOCLIENT_ACTIVE_OBJECT_MESSAGES = 0x32,
-       /*
-               u16 command
-               for all objects
-               {
-                       u16 id
-                       u16 message length
-                       string message
-               }
-       */
-
-       TOCLIENT_HP = 0x33,
-       /*
-               u16 command
-               u8 hp
-       */
-
-       TOCLIENT_MOVE_PLAYER = 0x34,
-       /*
-               u16 command
-               v3f1000 player position
-               f1000 player pitch
-               f1000 player yaw
-       */
-
-       TOCLIENT_ACCESS_DENIED = 0x35,
-       /*
-               u16 command
-               u16 reason_length
-               wstring reason
-       */
-
-       TOCLIENT_PLAYERITEM = 0x36, // Obsolete
-       /*
-               u16 command
-               u16 count of player items
-               for all player items {
-                       u16 peer id
-                       u16 length of serialized item
-                       string serialized item
-               }
-       */
-
-       TOCLIENT_DEATHSCREEN = 0x37,
-       /*
-               u16 command
-               u8 bool set camera point target
-               v3f1000 camera point target (to point the death cause or whatever)
-       */
-
-       TOCLIENT_MEDIA = 0x38,
-       /*
-               u16 command
-               u16 total number of texture 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 length of remote media server url (if applicable)
-               string url
-       */
-
-       TOCLIENT_TOOLDEF = 0x39,
-       /*
-               u16 command
-               u32 length of the next item
-               serialized ToolDefManager
-       */
-
-       TOCLIENT_NODEDEF = 0x3a,
-       /*
-               u16 command
-               u32 length of the next item
-               serialized NodeDefManager
-       */
-
-       TOCLIENT_CRAFTITEMDEF = 0x3b,
-       /*
-               u16 command
-               u32 length of the next item
-               serialized CraftiItemDefManager
-       */
-
-       TOCLIENT_ANNOUNCE_MEDIA = 0x3c,
-
-       /*
-               u16 command
-               u32 number of files
-               for each texture {
-                       u16 length of name
-                       string name
-                       u16 length of sha1_digest
-                       string sha1_digest
-               }
-       */
-
-       TOCLIENT_ITEMDEF = 0x3d,
-       /*
-               u16 command
-               u32 length of next item
-               serialized ItemDefManager
-       */
-
-       TOCLIENT_PLAY_SOUND = 0x3f,
-       /*
-               u16 command
-               s32 sound_id
-               u16 len
-               u8[len] sound name
-               s32 gain*1000
-               u8 type (0=local, 1=positional, 2=object)
-               s32[3] pos_nodes*10000
-               u16 object_id
-               u8 loop (bool)
-       */
-
-       TOCLIENT_STOP_SOUND = 0x40,
-       /*
-               u16 command
-               s32 sound_id
-       */
-
-       TOCLIENT_PRIVILEGES = 0x41,
-       /*
-               u16 command
-               u16 number of privileges
-               for each privilege
-                       u16 len
-                       u8[len] privilege
-       */
-
-       TOCLIENT_INVENTORY_FORMSPEC = 0x42,
-       /*
-               u16 command
-               u32 len
-               u8[len] formspec
-       */
-
-       TOCLIENT_DETACHED_INVENTORY = 0x43,
-       /*
-               [0] u16 command
-               u16 len
-               u8[len] name
-               [2] serialized inventory
-       */
-
-       TOCLIENT_SHOW_FORMSPEC = 0x44,
-       /*
-               [0] u16 command
-               u32 len
-               u8[len] formspec
-               u16 len
-               u8[len] formname
-       */
-
-       TOCLIENT_MOVEMENT = 0x45,
-       /*
-               u16 command
-               f1000 movement_acceleration_default
-               f1000 movement_acceleration_air
-               f1000 movement_acceleration_fast
-               f1000 movement_speed_walk
-               f1000 movement_speed_crouch
-               f1000 movement_speed_fast
-               f1000 movement_speed_climb
-               f1000 movement_speed_jump
-               f1000 movement_liquid_fluidity
-               f1000 movement_liquid_fluidity_smooth
-               f1000 movement_liquid_sink
-               f1000 movement_gravity
-       */
-
-       TOCLIENT_SPAWN_PARTICLE = 0x46,
-       /*
-               u16 command
-               v3f1000 pos
-               v3f1000 velocity
-               v3f1000 acceleration
-               f1000 expirationtime
-               f1000 size
-               u8 bool collisiondetection
-               u8 bool vertical
-               u32 len
-               u8[len] texture
-       */
-
-       TOCLIENT_ADD_PARTICLESPAWNER = 0x47,
-       /*
-               u16 command
-               u16 amount
-               f1000 spawntime
-               v3f1000 minpos
-               v3f1000 maxpos
-               v3f1000 minvel
-               v3f1000 maxvel
-               v3f1000 minacc
-               v3f1000 maxacc
-               f1000 minexptime
-               f1000 maxexptime
-               f1000 minsize
-               f1000 maxsize
-               u8 bool collisiondetection
-               u8 bool vertical
-               u32 len
-               u8[len] texture
-               u32 id
-       */
-
-       TOCLIENT_DELETE_PARTICLESPAWNER = 0x48,
-       /*
-               u16 command
-               u32 id
-       */
-
-       TOCLIENT_HUDADD = 0x49,
-       /*
-               u16 command
-               u32 id
-               u8 type
-               v2f1000 pos
-               u32 len
-               u8[len] name
-               v2f1000 scale
-               u32 len2
-               u8[len2] text
-               u32 number
-               u32 item
-               u32 dir
-               v2f1000 align
-               v2f1000 offset
-               v3f1000 world_pos
-               v2s32 size
-       */
-
-       TOCLIENT_HUDRM = 0x4a,
-       /*
-               u16 command
-               u32 id
-       */
-
-       TOCLIENT_HUDCHANGE = 0x4b,
-       /*
-               u16 command
-               u32 id
-               u8 stat
-               [v2f1000 data |
-                u32 len
-                u8[len] data |
-                u32 data]
-       */
-
-       TOCLIENT_HUD_SET_FLAGS = 0x4c,
-       /*
-               u16 command
-               u32 flags
-               u32 mask
-       */
-
-       TOCLIENT_HUD_SET_PARAM = 0x4d,
-       /*
-               u16 command
-               u16 param
-               u16 len
-               u8[len] value
-       */
-
-       TOCLIENT_BREATH = 0x4e,
-       /*
-               u16 command
-               u16 breath
-       */
-
-       TOCLIENT_SET_SKY = 0x4f,
-       /*
-               u16 command
-               u8[4] color (ARGB)
-               u8 len
-               u8[len] type
-               u16 count
-               foreach count:
-                       u8 len
-                       u8[len] param
-       */
-
-       TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO = 0x50,
-       /*
-               u16 command
-               u8 do_override (boolean)
-               u16 day-night ratio 0...65535
-       */
-
-       TOCLIENT_LOCAL_PLAYER_ANIMATIONS = 0x51,
-       /*
-               u16 command
-               v2s32 stand/idle
-               v2s32 walk
-               v2s32 dig
-               v2s32 walk+dig
-               f1000 frame_speed
-       */
-
-       TOCLIENT_EYE_OFFSET = 0x52,
-       /*
-               u16 command
-               v3f1000 first
-               v3f1000 third
-       */
-};
-
-enum ToServerCommand
-{
-       TOSERVER_INIT=0x10,
-       /*
-               Sent first after connected.
-
-               [0] u16 TOSERVER_INIT
-               [2] u8 SER_FMT_VER_HIGHEST_READ
-               [3] u8[20] player_name
-               [23] u8[28] password (new in some version)
-               [51] u16 minimum supported network protocol version (added sometime)
-               [53] u16 maximum supported network protocol version (added later than the previous one)
-       */
-
-       TOSERVER_INIT2 = 0x11,
-       /*
-               Sent as an ACK for TOCLIENT_INIT.
-               After this, the server can send data.
-
-               [0] u16 TOSERVER_INIT2
-       */
-
-       TOSERVER_GETBLOCK=0x20, // Obsolete
-       TOSERVER_ADDNODE = 0x21, // Obsolete
-       TOSERVER_REMOVENODE = 0x22, // Obsolete
-
-       TOSERVER_PLAYERPOS = 0x23,
-       /*
-               [0] u16 command
-               [2] v3s32 position*100
-               [2+12] v3s32 speed*100
-               [2+12+12] s32 pitch*100
-               [2+12+12+4] s32 yaw*100
-               [2+12+12+4+4] u32 keyPressed
-       */
-
-       TOSERVER_GOTBLOCKS = 0x24,
-       /*
-               [0] u16 command
-               [2] u8 count
-               [3] v3s16 pos_0
-               [3+6] v3s16 pos_1
-               ...
-       */
-
-       TOSERVER_DELETEDBLOCKS = 0x25,
-       /*
-               [0] u16 command
-               [2] u8 count
-               [3] v3s16 pos_0
-               [3+6] v3s16 pos_1
-               ...
-       */
-
-       TOSERVER_ADDNODE_FROM_INVENTORY = 0x26, // Obsolete
-       /*
-               [0] u16 command
-               [2] v3s16 pos
-               [8] u16 i
-       */
-
-       TOSERVER_CLICK_OBJECT = 0x27, // Obsolete
-       /*
-               length: 13
-               [0] u16 command
-               [2] u8 button (0=left, 1=right)
-               [3] v3s16 blockpos
-               [9] s16 id
-               [11] u16 item
-       */
-
-       TOSERVER_GROUND_ACTION = 0x28, // Obsolete
-       /*
-               length: 17
-               [0] u16 command
-               [2] u8 action
-               [3] v3s16 nodepos_undersurface
-               [9] v3s16 nodepos_abovesurface
-               [15] u16 item
-               actions:
-               0: start digging (from undersurface)
-               1: place block (to abovesurface)
-               2: stop digging (all parameters ignored)
-               3: digging completed
-       */
-
-       TOSERVER_RELEASE = 0x29, // Obsolete
-
-       // (oops, there is some gap here)
-
-       TOSERVER_SIGNTEXT = 0x30, // Old signs, obsolete
-       /*
-               u16 command
-               v3s16 blockpos
-               s16 id
-               u16 textlen
-               textdata
-       */
-
-       TOSERVER_INVENTORY_ACTION = 0x31,
-       /*
-               See InventoryAction in inventory.h
-       */
-
-       TOSERVER_CHAT_MESSAGE = 0x32,
-       /*
-               u16 command
-               u16 length
-               wstring message
-       */
-
-       TOSERVER_SIGNNODETEXT = 0x33, // obsolete
-       /*
-               u16 command
-               v3s16 p
-               u16 textlen
-               textdata
-       */
-
-       TOSERVER_CLICK_ACTIVEOBJECT = 0x34, // Obsolete
-       /*
-               length: 7
-               [0] u16 command
-               [2] u8 button (0=left, 1=right)
-               [3] u16 id
-               [5] u16 item
-       */
-
-       TOSERVER_DAMAGE = 0x35,
-       /*
-               u16 command
-               u8 amount
-       */
-
-       TOSERVER_PASSWORD=0x36,
-       /*
-               Sent to change password.
-
-               [0] u16 TOSERVER_PASSWORD
-               [2] u8[28] old password
-               [30] u8[28] new password
-       */
-
-       TOSERVER_PLAYERITEM=0x37,
-       /*
-               Sent to change selected item.
-
-               [0] u16 TOSERVER_PLAYERITEM
-               [2] u16 item
-       */
-
-       TOSERVER_RESPAWN=0x38,
-       /*
-               u16 TOSERVER_RESPAWN
-       */
-
-       TOSERVER_INTERACT = 0x39,
-       /*
-               [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
-
-               (Obsoletes TOSERVER_GROUND_ACTION and TOSERVER_CLICK_ACTIVEOBJECT.)
-       */
-
-       TOSERVER_REMOVED_SOUNDS = 0x3a,
-       /*
-               u16 command
-               u16 len
-               s32[len] sound_id
-       */
-
-       TOSERVER_NODEMETA_FIELDS = 0x3b,
-       /*
-               u16 command
-               v3s16 p
-               u16 len
-               u8[len] form name (reserved for future use)
-               u16 number of fields
-               for each field:
-                       u16 len
-                       u8[len] field name
-                       u32 len
-                       u8[len] field value
-       */
-
-       TOSERVER_INVENTORY_FIELDS = 0x3c,
-       /*
-               u16 command
-               u16 len
-               u8[len] form name (reserved for future use)
-               u16 number of fields
-               for each field:
-                       u16 len
-                       u8[len] field name
-                       u32 len
-                       u8[len] field value
-       */
-
-       TOSERVER_REQUEST_MEDIA = 0x40,
-       /*
-               u16 command
-               u16 number of files requested
-               for each file {
-                       u16 length of name
-                       string name
-               }
-        */
-
-       TOSERVER_RECEIVED_MEDIA = 0x41,
-       /*
-               u16 command
-       */
-
-       TOSERVER_BREATH = 0x42,
-       /*
-               u16 command
-               u16 breath
-       */
-
-       TOSERVER_CLIENT_READY = 0x43,
-       /*
-               u8 major
-               u8 minor
-               u8 patch
-               u8 reserved
-               u16 len
-               u8[len] full_version_string
-       */
-};
-
-#endif
-
index 33677cc8379a2984312b613f9ec9edeb03da237d..f1694d8d58ac049e69408527af0e1e34dce375ab 100644 (file)
@@ -20,7 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 #include "filecache.h"
 
-#include "clientserver.h"
+#include "network/networkprotocol.h"
 #include "log.h"
 #include "filesys.h"
 #include <string>
index 6d6c07453ed8440c30847aca3821fca07b84b35c..ed0e9637c6cc1354a9e39fc20c0825bcbdd2f4f4 100644 (file)
@@ -28,7 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "inventorymanager.h"
 #include "modalMenu.h"
 #include "guiTable.h"
-#include "clientserver.h"
+#include "network/networkprotocol.h"
 
 class IGameDef;
 class InventoryManager;
diff --git a/src/network/clientopcodes.cpp b/src/network/clientopcodes.cpp
new file mode 100644 (file)
index 0000000..247c671
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+Minetest
+Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
+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 "clientopcodes.h"
+
+const static ToClientCommandHandler null_command_handler = {"TOCLIENT_NULL", TOCLIENT_STATE_ALL, &Client::handleCommand_Null};
+
+const ToClientCommandHandler toClientCommandTable[TOCLIENT_NUM_MSG_TYPES] =
+{
+       null_command_handler,
+       null_command_handler,
+       null_command_handler,
+       null_command_handler,
+       null_command_handler,
+       null_command_handler,
+       null_command_handler,
+       null_command_handler,
+       null_command_handler,
+       null_command_handler,
+       null_command_handler,
+       null_command_handler,
+       null_command_handler,
+       null_command_handler,
+       null_command_handler,
+       null_command_handler,
+       { "TOCLIENT_INIT",                     TOCLIENT_STATE_NOT_CONNECTED, &Client::handleCommand_Init }, // 0x10
+       null_command_handler,
+       null_command_handler,
+       null_command_handler,
+       null_command_handler,
+       null_command_handler,
+       null_command_handler,
+       null_command_handler,
+       null_command_handler,
+       null_command_handler,
+       null_command_handler,
+       null_command_handler,
+       null_command_handler,
+       null_command_handler,
+       null_command_handler,
+       null_command_handler,
+       { "TOCLIENT_BLOCKDATA",                TOCLIENT_STATE_CONNECTED, &Client::handleCommand_BlockData }, // 0x20
+       { "TOCLIENT_ADDNODE",                  TOCLIENT_STATE_CONNECTED, &Client::handleCommand_AddNode }, // 0x21
+       { "TOCLIENT_REMOVENODE",               TOCLIENT_STATE_CONNECTED, &Client::handleCommand_RemoveNode }, // 0x22
+       null_command_handler,
+       null_command_handler,
+       null_command_handler,
+       null_command_handler,
+       { "TOCLIENT_INVENTORY",                TOCLIENT_STATE_CONNECTED, &Client::handleCommand_Inventory }, // 0x27
+       null_command_handler,
+       { "TOCLIENT_TIME_OF_DAY",              TOCLIENT_STATE_CONNECTED, &Client::handleCommand_TimeOfDay }, // 0x29
+       null_command_handler,
+       null_command_handler,
+       null_command_handler,
+       null_command_handler,
+       null_command_handler,
+       null_command_handler,
+       { "TOCLIENT_CHAT_MESSAGE",             TOCLIENT_STATE_CONNECTED, &Client::handleCommand_ChatMessage }, // 0x30
+       { "TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_ActiveObjectRemoveAdd }, // 0x31
+       { "TOCLIENT_ACTIVE_OBJECT_MESSAGES",   TOCLIENT_STATE_CONNECTED, &Client::handleCommand_ActiveObjectMessages }, // 0x32
+       { "TOCLIENT_HP",                       TOCLIENT_STATE_CONNECTED, &Client::handleCommand_HP }, // 0x33
+       { "TOCLIENT_MOVE_PLAYER",              TOCLIENT_STATE_CONNECTED, &Client::handleCommand_MovePlayer }, // 0x34
+       { "TOCLIENT_ACCESS_DENIED",            TOCLIENT_STATE_NOT_CONNECTED, &Client::handleCommand_AccessDenied }, // 0x35
+       { "TOCLIENT_PLAYERITEM",               TOCLIENT_STATE_CONNECTED, &Client::handleCommand_PlayerItem }, // 0x36
+       { "TOCLIENT_DEATHSCREEN",              TOCLIENT_STATE_CONNECTED, &Client::handleCommand_DeathScreen }, // 0x37
+       { "TOCLIENT_MEDIA",                    TOCLIENT_STATE_CONNECTED, &Client::handleCommand_Media }, // 0x38
+       { "TOCLIENT_TOOLDEF",                  TOCLIENT_STATE_CONNECTED, &Client::handleCommand_ToolDef }, // 0x39
+       { "TOCLIENT_NODEDEF",                  TOCLIENT_STATE_CONNECTED, &Client::handleCommand_NodeDef }, // 0x3a
+       { "TOCLIENT_CRAFTITEMDEF",             TOCLIENT_STATE_CONNECTED, &Client::handleCommand_CraftItemDef }, // 0x3b
+       { "TOCLIENT_ANNOUNCE_MEDIA",           TOCLIENT_STATE_CONNECTED, &Client::handleCommand_AnnounceMedia }, // 0x3c
+       { "TOCLIENT_ITEMDEF",                  TOCLIENT_STATE_CONNECTED, &Client::handleCommand_ItemDef }, // 0x3d
+       null_command_handler,
+       { "TOCLIENT_PLAY_SOUND",               TOCLIENT_STATE_CONNECTED, &Client::handleCommand_PlaySound }, // 0x3f
+       { "TOCLIENT_STOP_SOUND",               TOCLIENT_STATE_CONNECTED, &Client::handleCommand_StopSound }, // 0x40
+       { "TOCLIENT_PRIVILEGES",               TOCLIENT_STATE_CONNECTED, &Client::handleCommand_Privileges }, // 0x41
+       { "TOCLIENT_INVENTORY_FORMSPEC",       TOCLIENT_STATE_CONNECTED, &Client::handleCommand_InventoryFormSpec }, // 0x42
+       { "TOCLIENT_DETACHED_INVENTORY",       TOCLIENT_STATE_CONNECTED, &Client::handleCommand_DetachedInventory }, // 0x43
+       { "TOCLIENT_SHOW_FORMSPEC",            TOCLIENT_STATE_CONNECTED, &Client::handleCommand_ShowFormSpec }, // 0x44
+       { "TOCLIENT_MOVEMENT",                 TOCLIENT_STATE_CONNECTED, &Client::handleCommand_Movement }, // 0x45
+       { "TOCLIENT_SPAWN_PARTICLE",           TOCLIENT_STATE_CONNECTED, &Client::handleCommand_SpawnParticle }, // 0x46
+       { "TOCLIENT_ADD_PARTICLESPAWNER",      TOCLIENT_STATE_CONNECTED, &Client::handleCommand_AddParticleSpawner }, // 0x47
+       { "TOCLIENT_DELETE_PARTICLESPAWNER",   TOCLIENT_STATE_CONNECTED, &Client::handleCommand_DeleteParticleSpawner }, // 0x48
+       { "TOCLIENT_HUDADD",                   TOCLIENT_STATE_CONNECTED, &Client::handleCommand_HudAdd }, // 0x49
+       { "TOCLIENT_HUDRM",                    TOCLIENT_STATE_CONNECTED, &Client::handleCommand_HudRemove }, // 0x4a
+       { "TOCLIENT_HUDCHANGE",                TOCLIENT_STATE_CONNECTED, &Client::handleCommand_HudChange }, // 0x4b
+       { "TOCLIENT_HUD_SET_FLAGS",            TOCLIENT_STATE_CONNECTED, &Client::handleCommand_HudSetFlags }, // 0x4c
+       { "TOCLIENT_HUD_SET_PARAM",            TOCLIENT_STATE_CONNECTED, &Client::handleCommand_HudSetParam }, // 0x4d
+       { "TOCLIENT_BREATH",                   TOCLIENT_STATE_CONNECTED, &Client::handleCommand_Breath }, // 0x4e
+       { "TOCLIENT_SET_SKY",                  TOCLIENT_STATE_CONNECTED, &Client::handleCommand_HudSetSky }, // 0x4f
+       { "TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_OverrideDayNightRatio }, // 0x50
+       { "TOCLIENT_LOCAL_PLAYER_ANIMATIONS",  TOCLIENT_STATE_CONNECTED, &Client::handleCommand_LocalPlayerAnimations }, // 0x51
+       { "TOCLIENT_EYE_OFFSET",               TOCLIENT_STATE_CONNECTED, &Client::handleCommand_EyeOffset }, // 0x52
+};
diff --git a/src/network/clientopcodes.h b/src/network/clientopcodes.h
new file mode 100644 (file)
index 0000000..6755342
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+Minetest
+Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
+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.
+*/
+
+#ifndef CLIENTOPCODES_HEADER
+#define CLIENTOPCODES_HEADER
+
+#include "client.h"
+#include "networkprotocol.h"
+#include "toclientpacket.h"
+
+enum ToClientConnectionState {
+       TOCLIENT_STATE_NOT_CONNECTED,
+       TOCLIENT_STATE_CONNECTED,
+       TOCLIENT_STATE_ALL,
+};
+
+struct ToClientCommandHandler
+{
+    char const* name;
+    ToClientConnectionState state;
+    void (Client::*handler)(ToClientPacket* pkt);
+};
+
+extern const ToClientCommandHandler toClientCommandTable[TOCLIENT_NUM_MSG_TYPES];
+
+#endif
diff --git a/src/network/networkpacket.cpp b/src/network/networkpacket.cpp
new file mode 100644 (file)
index 0000000..80ea830
--- /dev/null
@@ -0,0 +1,301 @@
+/*
+Minetest
+Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
+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 "networkpacket.h"
+#include "debug.h"
+#include "util/serialize.h"
+
+NetworkPacket::NetworkPacket(u8 *data, u32 datasize, u16 peer_id):
+m_peer_id(peer_id)
+{
+       m_read_offset = 0;
+       m_datasize = datasize - 2;
+
+       // Copy data packet to remove opcode
+       m_data = new u8[m_datasize];
+
+       memcpy(m_data, &data[2], m_datasize);
+}
+
+NetworkPacket::~NetworkPacket()
+{
+       delete [] m_data;
+}
+
+char* NetworkPacket::getString(u32 from_offset)
+{
+       assert(from_offset < m_datasize);
+
+       return (char*)&m_data[from_offset];
+}
+
+char NetworkPacket::getChar(u32 offset)
+{
+       assert(offset < m_datasize);
+
+       return m_data[offset];
+}
+
+NetworkPacket& NetworkPacket::operator>>(std::string& dst)
+{
+       u16 strLen = readU16(&m_data[m_read_offset]);
+       m_read_offset += sizeof(u16);
+
+       dst.clear();
+
+       if (strLen == 0) {
+               return *this;
+       }
+
+
+       dst.reserve(strLen);
+       dst.append((char*)&m_data[m_read_offset], strLen);
+
+       m_read_offset += strLen*sizeof(char);
+       return *this;
+}
+
+NetworkPacket& NetworkPacket::operator>>(std::wstring& dst)
+{
+       u16 strLen = readU16(&m_data[m_read_offset]);
+       m_read_offset += sizeof(u16);
+
+       dst.clear();
+
+       if (strLen == 0) {
+               return *this;
+       }
+
+       dst.reserve(strLen);
+       for(u16 i=0; i<strLen; i++) {
+               wchar_t c16 = readU16(&m_data[m_read_offset]);
+               dst.append(&c16, 1);
+               m_read_offset += sizeof(u16);
+       }
+
+       return *this;
+}
+
+std::string NetworkPacket::readLongString()
+{
+       u32 strLen = readU32(&m_data[m_read_offset]);
+       m_read_offset += sizeof(u32);
+
+       if (strLen == 0) {
+               return "";
+       }
+
+       std::string dst;
+
+       dst.reserve(strLen);
+       dst.append((char*)&m_data[m_read_offset], strLen);
+
+       m_read_offset += strLen*sizeof(char);
+
+       return dst;
+}
+
+NetworkPacket& NetworkPacket::operator>>(char& dst)
+{
+       dst = getChar(m_read_offset);
+
+       m_read_offset += sizeof(char);
+       return *this;
+}
+
+u8* NetworkPacket::getU8Ptr(u32 from_offset)
+{
+       assert(from_offset < m_datasize);
+
+       return (u8*)&m_data[from_offset];
+}
+
+u8 NetworkPacket::getU8(u32 offset)
+{
+       assert(offset < m_datasize);
+
+       return m_data[offset];
+}
+
+NetworkPacket& NetworkPacket::operator>>(u8& dst)
+{
+       assert(m_read_offset < m_datasize);
+       dst = m_data[m_read_offset];
+
+       m_read_offset += sizeof(u8);
+       return *this;
+}
+
+NetworkPacket& NetworkPacket::operator>>(bool& dst)
+{
+       assert(m_read_offset < m_datasize);
+       dst = m_data[m_read_offset];
+
+       m_read_offset += sizeof(u8);
+       return *this;
+}
+
+u16 NetworkPacket::getU16(u32 from_offset)
+{
+       assert(from_offset < m_datasize);
+
+       return readU16(&m_data[from_offset]);
+}
+
+NetworkPacket& NetworkPacket::operator>>(u16& dst)
+{
+       dst = getU16(m_read_offset);
+
+       m_read_offset += sizeof(u16);
+       return *this;
+}
+
+u32 NetworkPacket::getU32(u32 from_offset)
+{
+       assert(from_offset < m_datasize);
+
+       return readU32(&m_data[from_offset]);
+}
+
+NetworkPacket& NetworkPacket::operator>>(u32& dst)
+{
+       dst = getU32(m_read_offset);
+
+       m_read_offset += sizeof(u32);
+       return *this;
+}
+
+u64 NetworkPacket::getU64(u32 from_offset)
+{
+       assert(from_offset < m_datasize);
+
+       return readU64(&m_data[from_offset]);
+}
+
+NetworkPacket& NetworkPacket::operator>>(u64& dst)
+{
+       dst = getU64(m_read_offset);
+
+       m_read_offset += sizeof(u64);
+       return *this;
+}
+
+float NetworkPacket::getF1000(u32 from_offset)
+{
+       assert(from_offset < m_datasize);
+
+       return readF1000(&m_data[from_offset]);
+}
+
+NetworkPacket& NetworkPacket::operator>>(float& dst)
+{
+       dst = getF1000(m_read_offset);
+
+       m_read_offset += sizeof(float);
+       return *this;
+}
+
+NetworkPacket& NetworkPacket::operator>>(v2f& dst)
+{
+       assert(m_read_offset < m_datasize);
+
+       dst = readV2F1000(&m_data[m_read_offset]);
+
+       m_read_offset += sizeof(v2f);
+       return *this;
+}
+
+NetworkPacket& NetworkPacket::operator>>(v3f& dst)
+{
+       assert(m_read_offset < m_datasize);
+
+       dst = readV3F1000(&m_data[m_read_offset]);
+
+       m_read_offset += sizeof(v3f);
+       return *this;
+}
+
+s16 NetworkPacket::getS16(u32 from_offset)
+{
+       assert(from_offset < m_datasize);
+
+       return readS16(&m_data[from_offset]);
+}
+
+NetworkPacket& NetworkPacket::operator>>(s16& dst)
+{
+       dst = getS16(m_read_offset);
+
+       m_read_offset += sizeof(s16);
+       return *this;
+}
+
+s32 NetworkPacket::getS32(u32 from_offset)
+{
+       assert(from_offset < m_datasize);
+
+       return readS32(&m_data[from_offset]);
+}
+
+NetworkPacket& NetworkPacket::operator>>(s32& dst)
+{
+       dst = getS32(m_read_offset);
+
+       m_read_offset += sizeof(s32);
+       return *this;
+}
+
+NetworkPacket& NetworkPacket::operator>>(v2s32& dst)
+{
+       dst = readV2S32(&m_data[m_read_offset]);
+
+       m_read_offset += sizeof(v2s32);
+       return *this;
+}
+
+v3s16 NetworkPacket::getV3S16(u32 from_offset)
+{
+       assert(from_offset < m_datasize);
+
+       return readV3S16(&m_data[from_offset]);
+}
+
+NetworkPacket& NetworkPacket::operator>>(v3s16& dst)
+{
+       dst = getV3S16(m_read_offset);
+
+       m_read_offset += sizeof(v3s16);
+       return *this;
+}
+
+v3s32 NetworkPacket::getV3S32(u32 from_offset)
+{
+       assert(from_offset < m_datasize);
+
+       return readV3S32(&m_data[from_offset]);
+}
+
+NetworkPacket& NetworkPacket::operator>>(v3s32& dst)
+{
+       dst = getV3S32(m_read_offset);
+
+       m_read_offset += sizeof(v3s32);
+       return *this;
+}
diff --git a/src/network/networkpacket.h b/src/network/networkpacket.h
new file mode 100644 (file)
index 0000000..c9f7e3c
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+Minetest
+Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
+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.
+*/
+
+#ifndef NETWORKPACKET_HEADER
+#define NETWORKPACKET_HEADER
+
+#include "util/numeric.h"
+#include "networkprotocol.h"
+
+class NetworkPacket
+{
+
+public:
+               NetworkPacket(u8 *data, u32 datasize, u16 peer_id);
+               ~NetworkPacket();
+
+               // Getters
+               u32 getSize() { return m_datasize; }
+               u16 getPeerId() { return m_peer_id; }
+
+               // Data extractors
+               char* getString(u32 from_offset);
+               NetworkPacket& operator>>(std::string& dst);
+               NetworkPacket& operator>>(std::wstring& dst);
+               std::string readLongString();
+
+               char getChar(u32 offset);
+               NetworkPacket& operator>>(char& dst);
+
+               NetworkPacket& operator>>(bool& dst);
+
+               u8 getU8(u32 offset);
+               NetworkPacket& operator>>(u8& dst);
+
+               u8* getU8Ptr(u32 offset);
+               u16 getU16(u32 from_offset);
+               NetworkPacket& operator>>(u16& dst);
+               u32 getU32(u32 from_offset);
+               NetworkPacket& operator>>(u32& dst);
+               u64 getU64(u32 from_offset);
+               NetworkPacket& operator>>(u64& dst);
+
+               float getF1000(u32 offset);
+               NetworkPacket& operator>>(float& dst);
+               NetworkPacket& operator>>(v2f& dst);
+               NetworkPacket& operator>>(v3f& dst);
+
+               s16 getS16(u32 from_offset);
+               NetworkPacket& operator>>(s16& dst);
+               s32 getS32(u32 from_offset);
+               NetworkPacket& operator>>(s32& dst);
+
+               NetworkPacket& operator>>(v2s32& dst);
+
+               v3s16 getV3S16(u32 from_offset);
+               NetworkPacket& operator>>(v3s16& dst);
+
+               v3s32 getV3S32(u32 from_offset);
+               NetworkPacket& operator>>(v3s32& dst);
+
+protected:
+               u8 *m_data;
+               u32 m_datasize;
+               u32 m_read_offset;
+private:
+               u16 m_peer_id;
+};
+
+#endif
diff --git a/src/network/networkprotocol.h b/src/network/networkprotocol.h
new file mode 100644 (file)
index 0000000..c774c02
--- /dev/null
@@ -0,0 +1,804 @@
+/*
+Minetest
+Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
+
+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.
+*/
+
+#ifndef NETWORKPROTOCOL_HEADER
+#define NETWORKPROTOCOL_HEADER
+#include "util/string.h"
+
+/*
+       changes by PROTOCOL_VERSION:
+
+       PROTOCOL_VERSION 3:
+               Base for writing changes here
+       PROTOCOL_VERSION 4:
+               Add TOCLIENT_MEDIA
+               Add TOCLIENT_TOOLDEF
+               Add TOCLIENT_NODEDEF
+               Add TOCLIENT_CRAFTITEMDEF
+               Add TOSERVER_INTERACT
+               Obsolete TOSERVER_CLICK_ACTIVEOBJECT
+               Obsolete TOSERVER_GROUND_ACTION
+       PROTOCOL_VERSION 5:
+               Make players to be handled mostly as ActiveObjects
+       PROTOCOL_VERSION 6:
+               Only non-cached textures are sent
+       PROTOCOL_VERSION 7:
+               Add TOCLIENT_ITEMDEF
+               Obsolete TOCLIENT_TOOLDEF
+               Obsolete TOCLIENT_CRAFTITEMDEF
+               Compress the contents of TOCLIENT_ITEMDEF and TOCLIENT_NODEDEF
+       PROTOCOL_VERSION 8:
+               Digging based on item groups
+               Many things
+       PROTOCOL_VERSION 9:
+               ContentFeatures and NodeDefManager use a different serialization
+                   format; better for future version cross-compatibility
+               Many things
+       PROTOCOL_VERSION 10:
+               TOCLIENT_PRIVILEGES
+               Version raised to force 'fly' and 'fast' privileges into effect.
+               Node metadata change (came in later; somewhat incompatible)
+       PROTOCOL_VERSION 11:
+               TileDef in ContentFeatures
+               Nodebox drawtype
+               (some dev snapshot)
+               TOCLIENT_INVENTORY_FORMSPEC
+               (0.4.0, 0.4.1)
+       PROTOCOL_VERSION 12:
+               TOSERVER_INVENTORY_FIELDS
+               16-bit node ids
+               TOCLIENT_DETACHED_INVENTORY
+       PROTOCOL_VERSION 13:
+               InventoryList field "Width" (deserialization fails with old versions)
+       PROTOCOL_VERSION 14:
+               Added transfer of player pressed keys to the server
+               Added new messages for mesh and bone animation, as well as attachments
+               GENERIC_CMD_SET_ANIMATION
+               GENERIC_CMD_SET_BONE_POSITION
+               GENERIC_CMD_SET_ATTACHMENT
+       PROTOCOL_VERSION 15:
+               Serialization format changes
+       PROTOCOL_VERSION 16:
+               TOCLIENT_SHOW_FORMSPEC
+       PROTOCOL_VERSION 17:
+               Serialization format change: include backface_culling flag in TileDef
+               Added rightclickable field in nodedef
+               TOCLIENT_SPAWN_PARTICLE
+               TOCLIENT_ADD_PARTICLESPAWNER
+               TOCLIENT_DELETE_PARTICLESPAWNER
+       PROTOCOL_VERSION 18:
+               damageGroups added to ToolCapabilities
+               sound_place added to ItemDefinition
+       PROTOCOL_VERSION 19:
+               GENERIC_CMD_SET_PHYSICS_OVERRIDE
+       PROTOCOL_VERSION 20:
+               TOCLIENT_HUDADD
+               TOCLIENT_HUDRM
+               TOCLIENT_HUDCHANGE
+               TOCLIENT_HUD_SET_FLAGS
+       PROTOCOL_VERSION 21:
+               TOCLIENT_BREATH
+               TOSERVER_BREATH
+               range added to ItemDefinition
+               drowning, leveled and liquid_range added to ContentFeatures
+               stepheight and collideWithObjects added to object properties
+               version, heat and humidity transfer in MapBock
+               automatic_face_movement_dir and automatic_face_movement_dir_offset
+                       added to object properties
+       PROTOCOL_VERSION 22:
+               add swap_node
+       PROTOCOL_VERSION 23:
+               TOSERVER_CLIENT_READY
+       PROTOCOL_VERSION 24:
+               ContentFeatures version 7
+               ContentFeatures: change number of special tiles to 6 (CF_SPECIAL_COUNT)
+*/
+
+#define LATEST_PROTOCOL_VERSION 24
+
+// Server's supported network protocol range
+#define SERVER_PROTOCOL_VERSION_MIN 13
+#define SERVER_PROTOCOL_VERSION_MAX LATEST_PROTOCOL_VERSION
+
+// Client's supported network protocol range
+#define CLIENT_PROTOCOL_VERSION_MIN 13
+#define CLIENT_PROTOCOL_VERSION_MAX LATEST_PROTOCOL_VERSION
+
+// Constant that differentiates the protocol from random data and other protocols
+#define PROTOCOL_ID 0x4f457403
+
+#define PASSWORD_SIZE 28       // Maximum password length. Allows for
+                               // base64-encoded SHA-1 (27+\0).
+
+#define FORMSPEC_API_VERSION 1
+#define FORMSPEC_VERSION_STRING "formspec_version[" TOSTRING(FORMSPEC_API_VERSION) "]"
+
+#define TEXTURENAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_.-"
+
+enum ToClientCommand
+{
+       TOCLIENT_INIT = 0x10,
+       /*
+               Server's reply to TOSERVER_INIT.
+               Sent second after connected.
+
+               [0] u16 TOSERVER_INIT
+               [2] u8 deployed version
+               [3] v3s16 player's position + v3f(0,BS/2,0) floatToInt'd
+               [12] u64 map seed (new as of 2011-02-27)
+               [20] f1000 recommended send interval (in seconds) (new as of 14)
+
+               NOTE: The position in here is deprecated; position is
+                     explicitly sent afterwards
+       */
+
+       TOCLIENT_BLOCKDATA = 0x20, //TODO: Multiple blocks
+       TOCLIENT_ADDNODE = 0x21,
+       /*
+               u16 command
+               v3s16 position
+               serialized mapnode
+               u8 keep_metadata // Added in protocol version 22
+       */
+       TOCLIENT_REMOVENODE = 0x22,
+
+       TOCLIENT_PLAYERPOS = 0x23, // Obsolete
+       /*
+               [0] u16 command
+               // Followed by an arbitary number of these:
+               // Number is determined from packet length.
+               [N] u16 peer_id
+               [N+2] v3s32 position*100
+               [N+2+12] v3s32 speed*100
+               [N+2+12+12] s32 pitch*100
+               [N+2+12+12+4] s32 yaw*100
+       */
+
+       TOCLIENT_PLAYERINFO = 0x24, // Obsolete
+       /*
+               [0] u16 command
+               // Followed by an arbitary number of these:
+               // Number is determined from packet length.
+               [N] u16 peer_id
+               [N] char[20] name
+       */
+
+       TOCLIENT_OPT_BLOCK_NOT_FOUND = 0x25, // Obsolete
+
+       TOCLIENT_SECTORMETA = 0x26, // Obsolete
+       /*
+               [0] u16 command
+               [2] u8 sector count
+               [3...] v2s16 pos + sector metadata
+       */
+
+       TOCLIENT_INVENTORY = 0x27,
+       /*
+               [0] u16 command
+               [2] serialized inventory
+       */
+
+       TOCLIENT_OBJECTDATA = 0x28, // Obsolete
+       /*
+               Sent as unreliable.
+
+               u16 command
+               u16 number of player positions
+               for each player:
+                       u16 peer_id
+                       v3s32 position*100
+                       v3s32 speed*100
+                       s32 pitch*100
+                       s32 yaw*100
+               u16 count of blocks
+               for each block:
+                       v3s16 blockpos
+                       block objects
+       */
+
+       TOCLIENT_TIME_OF_DAY = 0x29,
+       /*
+               u16 command
+               u16 time (0-23999)
+               Added in a later version:
+               f1000 time_speed
+       */
+
+       // (oops, there is some gap here)
+
+       TOCLIENT_CHAT_MESSAGE = 0x30,
+       /*
+               u16 command
+               u16 length
+               wstring message
+       */
+
+       TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD = 0x31,
+       /*
+               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
+               }
+       */
+
+       TOCLIENT_ACTIVE_OBJECT_MESSAGES = 0x32,
+       /*
+               u16 command
+               for all objects
+               {
+                       u16 id
+                       u16 message length
+                       string message
+               }
+       */
+
+       TOCLIENT_HP = 0x33,
+       /*
+               u16 command
+               u8 hp
+       */
+
+       TOCLIENT_MOVE_PLAYER = 0x34,
+       /*
+               u16 command
+               v3f1000 player position
+               f1000 player pitch
+               f1000 player yaw
+       */
+
+       TOCLIENT_ACCESS_DENIED = 0x35,
+       /*
+               u16 command
+               u16 reason_length
+               wstring reason
+       */
+
+       TOCLIENT_PLAYERITEM = 0x36, // Obsolete
+       /*
+               u16 command
+               u16 count of player items
+               for all player items {
+                       u16 peer id
+                       u16 length of serialized item
+                       string serialized item
+               }
+       */
+
+       TOCLIENT_DEATHSCREEN = 0x37,
+       /*
+               u16 command
+               u8 bool set camera point target
+               v3f1000 camera point target (to point the death cause or whatever)
+       */
+
+       TOCLIENT_MEDIA = 0x38,
+       /*
+               u16 command
+               u16 total number of texture 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 length of remote media server url (if applicable)
+               string url
+       */
+
+       TOCLIENT_TOOLDEF = 0x39,
+       /*
+               u16 command
+               u32 length of the next item
+               serialized ToolDefManager
+       */
+
+       TOCLIENT_NODEDEF = 0x3a,
+       /*
+               u16 command
+               u32 length of the next item
+               serialized NodeDefManager
+       */
+
+       TOCLIENT_CRAFTITEMDEF = 0x3b,
+       /*
+               u16 command
+               u32 length of the next item
+               serialized CraftiItemDefManager
+       */
+
+       TOCLIENT_ANNOUNCE_MEDIA = 0x3c,
+
+       /*
+               u16 command
+               u32 number of files
+               for each texture {
+                       u16 length of name
+                       string name
+                       u16 length of sha1_digest
+                       string sha1_digest
+               }
+       */
+
+       TOCLIENT_ITEMDEF = 0x3d,
+       /*
+               u16 command
+               u32 length of next item
+               serialized ItemDefManager
+       */
+
+       TOCLIENT_PLAY_SOUND = 0x3f,
+       /*
+               u16 command
+               s32 sound_id
+               u16 len
+               u8[len] sound name
+               s32 gain*1000
+               u8 type (0=local, 1=positional, 2=object)
+               s32[3] pos_nodes*10000
+               u16 object_id
+               u8 loop (bool)
+       */
+
+       TOCLIENT_STOP_SOUND = 0x40,
+       /*
+               u16 command
+               s32 sound_id
+       */
+
+       TOCLIENT_PRIVILEGES = 0x41,
+       /*
+               u16 command
+               u16 number of privileges
+               for each privilege
+                       u16 len
+                       u8[len] privilege
+       */
+
+       TOCLIENT_INVENTORY_FORMSPEC = 0x42,
+       /*
+               u16 command
+               u32 len
+               u8[len] formspec
+       */
+
+       TOCLIENT_DETACHED_INVENTORY = 0x43,
+       /*
+               [0] u16 command
+               u16 len
+               u8[len] name
+               [2] serialized inventory
+       */
+
+       TOCLIENT_SHOW_FORMSPEC = 0x44,
+       /*
+               [0] u16 command
+               u32 len
+               u8[len] formspec
+               u16 len
+               u8[len] formname
+       */
+
+       TOCLIENT_MOVEMENT = 0x45,
+       /*
+               u16 command
+               f1000 movement_acceleration_default
+               f1000 movement_acceleration_air
+               f1000 movement_acceleration_fast
+               f1000 movement_speed_walk
+               f1000 movement_speed_crouch
+               f1000 movement_speed_fast
+               f1000 movement_speed_climb
+               f1000 movement_speed_jump
+               f1000 movement_liquid_fluidity
+               f1000 movement_liquid_fluidity_smooth
+               f1000 movement_liquid_sink
+               f1000 movement_gravity
+       */
+
+       TOCLIENT_SPAWN_PARTICLE = 0x46,
+       /*
+               u16 command
+               v3f1000 pos
+               v3f1000 velocity
+               v3f1000 acceleration
+               f1000 expirationtime
+               f1000 size
+               u8 bool collisiondetection
+               u8 bool vertical
+               u32 len
+               u8[len] texture
+       */
+
+       TOCLIENT_ADD_PARTICLESPAWNER = 0x47,
+       /*
+               u16 command
+               u16 amount
+               f1000 spawntime
+               v3f1000 minpos
+               v3f1000 maxpos
+               v3f1000 minvel
+               v3f1000 maxvel
+               v3f1000 minacc
+               v3f1000 maxacc
+               f1000 minexptime
+               f1000 maxexptime
+               f1000 minsize
+               f1000 maxsize
+               u8 bool collisiondetection
+               u8 bool vertical
+               u32 len
+               u8[len] texture
+               u32 id
+       */
+
+       TOCLIENT_DELETE_PARTICLESPAWNER = 0x48,
+       /*
+               u16 command
+               u32 id
+       */
+
+       TOCLIENT_HUDADD = 0x49,
+       /*
+               u16 command
+               u32 id
+               u8 type
+               v2f1000 pos
+               u32 len
+               u8[len] name
+               v2f1000 scale
+               u32 len2
+               u8[len2] text
+               u32 number
+               u32 item
+               u32 dir
+               v2f1000 align
+               v2f1000 offset
+               v3f1000 world_pos
+               v2s32 size
+       */
+
+       TOCLIENT_HUDRM = 0x4a,
+       /*
+               u16 command
+               u32 id
+       */
+
+       TOCLIENT_HUDCHANGE = 0x4b,
+       /*
+               u16 command
+               u32 id
+               u8 stat
+               [v2f1000 data |
+                u32 len
+                u8[len] data |
+                u32 data]
+       */
+
+       TOCLIENT_HUD_SET_FLAGS = 0x4c,
+       /*
+               u16 command
+               u32 flags
+               u32 mask
+       */
+
+       TOCLIENT_HUD_SET_PARAM = 0x4d,
+       /*
+               u16 command
+               u16 param
+               u16 len
+               u8[len] value
+       */
+
+       TOCLIENT_BREATH = 0x4e,
+       /*
+               u16 command
+               u16 breath
+       */
+
+       TOCLIENT_SET_SKY = 0x4f,
+       /*
+               u16 command
+               u8[4] color (ARGB)
+               u8 len
+               u8[len] type
+               u16 count
+               foreach count:
+                       u8 len
+                       u8[len] param
+       */
+
+       TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO = 0x50,
+       /*
+               u16 command
+               u8 do_override (boolean)
+               u16 day-night ratio 0...65535
+       */
+
+       TOCLIENT_LOCAL_PLAYER_ANIMATIONS = 0x51,
+       /*
+               u16 command
+               v2s32 stand/idle
+               v2s32 walk
+               v2s32 dig
+               v2s32 walk+dig
+               f1000 frame_speed
+       */
+
+       TOCLIENT_EYE_OFFSET = 0x52,
+       /*
+               u16 command
+               v3f1000 first
+               v3f1000 third
+       */
+
+       TOCLIENT_NUM_MSG_TYPES = 0x53,
+};
+
+enum ToServerCommand
+{
+       TOSERVER_INIT=0x10,
+       /*
+               Sent first after connected.
+
+               [0] u16 TOSERVER_INIT
+               [2] u8 SER_FMT_VER_HIGHEST_READ
+               [3] u8[20] player_name
+               [23] u8[28] password (new in some version)
+               [51] u16 minimum supported network protocol version (added sometime)
+               [53] u16 maximum supported network protocol version (added later than the previous one)
+       */
+
+       TOSERVER_INIT2 = 0x11,
+       /*
+               Sent as an ACK for TOCLIENT_INIT.
+               After this, the server can send data.
+
+               [0] u16 TOSERVER_INIT2
+       */
+
+       TOSERVER_GETBLOCK=0x20, // Obsolete
+       TOSERVER_ADDNODE = 0x21, // Obsolete
+       TOSERVER_REMOVENODE = 0x22, // Obsolete
+
+       TOSERVER_PLAYERPOS = 0x23,
+       /*
+               [0] u16 command
+               [2] v3s32 position*100
+               [2+12] v3s32 speed*100
+               [2+12+12] s32 pitch*100
+               [2+12+12+4] s32 yaw*100
+               [2+12+12+4+4] u32 keyPressed
+       */
+
+       TOSERVER_GOTBLOCKS = 0x24,
+       /*
+               [0] u16 command
+               [2] u8 count
+               [3] v3s16 pos_0
+               [3+6] v3s16 pos_1
+               ...
+       */
+
+       TOSERVER_DELETEDBLOCKS = 0x25,
+       /*
+               [0] u16 command
+               [2] u8 count
+               [3] v3s16 pos_0
+               [3+6] v3s16 pos_1
+               ...
+       */
+
+       TOSERVER_ADDNODE_FROM_INVENTORY = 0x26, // Obsolete
+       /*
+               [0] u16 command
+               [2] v3s16 pos
+               [8] u16 i
+       */
+
+       TOSERVER_CLICK_OBJECT = 0x27, // Obsolete
+       /*
+               length: 13
+               [0] u16 command
+               [2] u8 button (0=left, 1=right)
+               [3] v3s16 blockpos
+               [9] s16 id
+               [11] u16 item
+       */
+
+       TOSERVER_GROUND_ACTION = 0x28, // Obsolete
+       /*
+               length: 17
+               [0] u16 command
+               [2] u8 action
+               [3] v3s16 nodepos_undersurface
+               [9] v3s16 nodepos_abovesurface
+               [15] u16 item
+               actions:
+               0: start digging (from undersurface)
+               1: place block (to abovesurface)
+               2: stop digging (all parameters ignored)
+               3: digging completed
+       */
+
+       TOSERVER_RELEASE = 0x29, // Obsolete
+
+       // (oops, there is some gap here)
+
+       TOSERVER_SIGNTEXT = 0x30, // Old signs, obsolete
+       /*
+               u16 command
+               v3s16 blockpos
+               s16 id
+               u16 textlen
+               textdata
+       */
+
+       TOSERVER_INVENTORY_ACTION = 0x31,
+       /*
+               See InventoryAction in inventory.h
+       */
+
+       TOSERVER_CHAT_MESSAGE = 0x32,
+       /*
+               u16 command
+               u16 length
+               wstring message
+       */
+
+       TOSERVER_SIGNNODETEXT = 0x33, // obsolete
+       /*
+               u16 command
+               v3s16 p
+               u16 textlen
+               textdata
+       */
+
+       TOSERVER_CLICK_ACTIVEOBJECT = 0x34, // Obsolete
+       /*
+               length: 7
+               [0] u16 command
+               [2] u8 button (0=left, 1=right)
+               [3] u16 id
+               [5] u16 item
+       */
+
+       TOSERVER_DAMAGE = 0x35,
+       /*
+               u16 command
+               u8 amount
+       */
+
+       TOSERVER_PASSWORD=0x36,
+       /*
+               Sent to change password.
+
+               [0] u16 TOSERVER_PASSWORD
+               [2] u8[28] old password
+               [30] u8[28] new password
+       */
+
+       TOSERVER_PLAYERITEM=0x37,
+       /*
+               Sent to change selected item.
+
+               [0] u16 TOSERVER_PLAYERITEM
+               [2] u16 item
+       */
+
+       TOSERVER_RESPAWN=0x38,
+       /*
+               u16 TOSERVER_RESPAWN
+       */
+
+       TOSERVER_INTERACT = 0x39,
+       /*
+               [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
+
+               (Obsoletes TOSERVER_GROUND_ACTION and TOSERVER_CLICK_ACTIVEOBJECT.)
+       */
+
+       TOSERVER_REMOVED_SOUNDS = 0x3a,
+       /*
+               u16 command
+               u16 len
+               s32[len] sound_id
+       */
+
+       TOSERVER_NODEMETA_FIELDS = 0x3b,
+       /*
+               u16 command
+               v3s16 p
+               u16 len
+               u8[len] form name (reserved for future use)
+               u16 number of fields
+               for each field:
+                       u16 len
+                       u8[len] field name
+                       u32 len
+                       u8[len] field value
+       */
+
+       TOSERVER_INVENTORY_FIELDS = 0x3c,
+       /*
+               u16 command
+               u16 len
+               u8[len] form name (reserved for future use)
+               u16 number of fields
+               for each field:
+                       u16 len
+                       u8[len] field name
+                       u32 len
+                       u8[len] field value
+       */
+
+       TOSERVER_REQUEST_MEDIA = 0x40,
+       /*
+               u16 command
+               u16 number of files requested
+               for each file {
+                       u16 length of name
+                       string name
+               }
+        */
+
+       TOSERVER_RECEIVED_MEDIA = 0x41,
+       /*
+               u16 command
+       */
+
+       TOSERVER_BREATH = 0x42,
+       /*
+               u16 command
+               u16 breath
+       */
+
+       TOSERVER_CLIENT_READY = 0x43,
+       /*
+               u8 major
+               u8 minor
+               u8 patch
+               u8 reserved
+               u16 len
+               u8[len] full_version_string
+       */
+
+       TOSERVER_NUM_MSG_TYPES = 0x44,
+};
+
+#endif
diff --git a/src/network/serveropcodes.cpp b/src/network/serveropcodes.cpp
new file mode 100644 (file)
index 0000000..11d2c65
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+Minetest
+Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
+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 "serveropcodes.h"
+
+const static ToServerCommandHandler null_command_handler = { "TOSERVER_NULL", TOSERVER_STATE_ALL, &Server::handleCommand_Null };
+
+const ToServerCommandHandler toServerCommandTable[TOSERVER_NUM_MSG_TYPES] =
+{
+       null_command_handler, // 0x00
+       null_command_handler, // 0x01
+       null_command_handler, // 0x02
+       null_command_handler, // 0x03
+       null_command_handler, // 0x04
+       null_command_handler, // 0x05
+       null_command_handler, // 0x06
+       null_command_handler, // 0x07
+       null_command_handler, // 0x08
+       null_command_handler, // 0x09
+       null_command_handler, // 0x0a
+       null_command_handler, // 0x0b
+       null_command_handler, // 0x0c
+       null_command_handler, // 0x0d
+       null_command_handler, // 0x0e
+       null_command_handler, // 0x0f
+       { "TOSERVER_INIT",                     TOSERVER_STATE_NOT_CONNECTED, &Server::handleCommand_Init }, // 0x10
+       { "TOSERVER_INIT2",                    TOSERVER_STATE_NOT_CONNECTED, &Server::handleCommand_Init2 }, // 0x11
+       null_command_handler, // 0x12
+       null_command_handler, // 0x13
+       null_command_handler, // 0x14
+       null_command_handler, // 0x15
+       null_command_handler, // 0x16
+       null_command_handler, // 0x17
+       null_command_handler, // 0x18
+       null_command_handler, // 0x19
+       null_command_handler, // 0x1a
+       null_command_handler, // 0x1b
+       null_command_handler, // 0x1c
+       null_command_handler, // 0x1d
+       null_command_handler, // 0x1e
+       null_command_handler, // 0x1f
+       null_command_handler, // 0x20
+       null_command_handler, // 0x21
+       null_command_handler, // 0x22
+       { "TOSERVER_PLAYERPOS",                TOSERVER_STATE_INGAME, &Server::handleCommand_PlayerPos }, // 0x23
+       { "TOSERVER_GOTBLOCKS",                TOSERVER_STATE_STARTUP, &Server::handleCommand_GotBlocks }, // 0x24
+       { "TOSERVER_DELETEDBLOCKS",            TOSERVER_STATE_INGAME, &Server::handleCommand_DeletedBlocks }, // 0x25
+       null_command_handler, // 0x26
+       { "TOSERVER_CLICK_OBJECT",             TOSERVER_STATE_INGAME, &Server::handleCommand_Deprecated }, // 0x27
+       { "TOSERVER_GROUND_ACTION",            TOSERVER_STATE_INGAME, &Server::handleCommand_Deprecated }, // 0x28
+       { "TOSERVER_RELEASE",                  TOSERVER_STATE_INGAME, &Server::handleCommand_Deprecated }, // 0x29
+       null_command_handler, // 0x2a
+       null_command_handler, // 0x2b
+       null_command_handler, // 0x2c
+       null_command_handler, // 0x2d
+       null_command_handler, // 0x2e
+       null_command_handler, // 0x2f
+       { "TOSERVER_SIGNTEXT",                 TOSERVER_STATE_INGAME, &Server::handleCommand_Deprecated }, // 0x30
+       { "TOSERVER_INVENTORY_ACTION",         TOSERVER_STATE_INGAME, &Server::handleCommand_InventoryAction }, // 0x31
+       { "TOSERVER_CHAT_MESSAGE",             TOSERVER_STATE_INGAME, &Server::handleCommand_ChatMessage }, // 0x32
+       { "TOSERVER_SIGNNODETEXT",             TOSERVER_STATE_INGAME, &Server::handleCommand_Deprecated }, // 0x33
+       { "TOSERVER_CLICK_ACTIVEOBJECT",       TOSERVER_STATE_INGAME, &Server::handleCommand_Deprecated }, // 0x34
+       { "TOSERVER_DAMAGE",                   TOSERVER_STATE_INGAME, &Server::handleCommand_Damage }, // 0x35
+       { "TOSERVER_PASSWORD",                 TOSERVER_STATE_INGAME, &Server::handleCommand_Password }, // 0x36
+       { "TOSERVER_PLAYERITEM",               TOSERVER_STATE_INGAME, &Server::handleCommand_PlayerItem }, // 0x37
+       { "TOSERVER_RESPAWN",                  TOSERVER_STATE_INGAME, &Server::handleCommand_Respawn }, // 0x38
+       { "TOSERVER_INTERACT",                 TOSERVER_STATE_INGAME, &Server::handleCommand_Interact }, // 0x39
+       { "TOSERVER_REMOVED_SOUNDS",           TOSERVER_STATE_INGAME, &Server::handleCommand_RemovedSounds }, // 0x3a
+       { "TOSERVER_NODEMETA_FIELDS",          TOSERVER_STATE_INGAME, &Server::handleCommand_NodeMetaFields }, // 0x3b
+       { "TOSERVER_INVENTORY_FIELDS",         TOSERVER_STATE_INGAME, &Server::handleCommand_InventoryFields }, // 0x3c
+       null_command_handler, // 0x3d
+       null_command_handler, // 0x3e
+       null_command_handler, // 0x3f
+       { "TOSERVER_REQUEST_MEDIA",            TOSERVER_STATE_STARTUP, &Server::handleCommand_RequestMedia }, // 0x40
+       { "TOSERVER_RECEIVED_MEDIA",           TOSERVER_STATE_STARTUP, &Server::handleCommand_ReceivedMedia }, // 0x41
+       { "TOSERVER_BREATH",                   TOSERVER_STATE_INGAME, &Server::handleCommand_Breath }, // 0x42
+       { "TOSERVER_CLIENT_READY",             TOSERVER_STATE_STARTUP, &Server::handleCommand_ClientReady }, // 0x43
+};
diff --git a/src/network/serveropcodes.h b/src/network/serveropcodes.h
new file mode 100644 (file)
index 0000000..77f39e0
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+Minetest
+Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
+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.
+*/
+
+#ifndef SERVEROPCODES_HEADER
+#define SERVEROPCODES_HEADER
+
+#include "server.h"
+#include "networkprotocol.h"
+#include "toserverpacket.h"
+
+enum ToServerConnectionState {
+       TOSERVER_STATE_NOT_CONNECTED,
+       TOSERVER_STATE_STARTUP,
+       TOSERVER_STATE_INGAME,
+       TOSERVER_STATE_ALL,
+};
+struct ToServerCommandHandler
+{
+    const std::string name;
+    ToServerConnectionState state;
+    void (Server::*handler)(ToServerPacket* pkt);
+};
+
+extern const ToServerCommandHandler toServerCommandTable[TOSERVER_NUM_MSG_TYPES];
+
+#endif
diff --git a/src/network/toclientpacket.cpp b/src/network/toclientpacket.cpp
new file mode 100644 (file)
index 0000000..b51da48
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+Minetest
+Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
+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 "toclientpacket.h"
+#include "util/serialize.h"
+
+ToClientPacket::ToClientPacket(u8 *data, u32 datasize, u16 peer_id):
+NetworkPacket(data, datasize, peer_id)
+{
+       m_command = (ToClientCommand)readU16(&data[0]);
+}
diff --git a/src/network/toclientpacket.h b/src/network/toclientpacket.h
new file mode 100644 (file)
index 0000000..b926514
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+Minetest
+Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
+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.
+*/
+
+#ifndef TOCLIENTPACKET_HEADER
+#define TOCLIENTPACKET_HEADER
+
+#include "util/numeric.h"
+#include "networkprotocol.h"
+#include "networkpacket.h"
+
+class ToClientPacket: public NetworkPacket
+{
+public:
+       ToClientPacket(u8 *data, u32 datasize, u16 peer_id);
+       ToClientCommand getCommand() { return m_command; }
+
+private:
+       ToClientCommand m_command;
+};
+
+#endif
diff --git a/src/network/toserverpacket.cpp b/src/network/toserverpacket.cpp
new file mode 100644 (file)
index 0000000..7b49686
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+Minetest
+Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
+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 "toserverpacket.h"
+#include "util/serialize.h"
+
+ToServerPacket::ToServerPacket(u8 *data, u32 datasize, u16 peer_id):
+NetworkPacket(data, datasize, peer_id)
+{
+       m_command = (ToServerCommand)readU16(&data[0]);
+}
diff --git a/src/network/toserverpacket.h b/src/network/toserverpacket.h
new file mode 100644 (file)
index 0000000..eb8470b
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+Minetest
+Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
+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.
+*/
+
+#ifndef TOSERVERPACKET_HEADER
+#define TOSERVERPACKET_HEADER
+
+#include "util/numeric.h"
+#include "networkprotocol.h"
+#include "networkpacket.h"
+
+class ToServerPacket: public NetworkPacket
+{
+public:
+       ToServerPacket(u8 *data, u32 datasize, u16 peer_id);
+       ToServerCommand getCommand() { return m_command; }
+
+private:
+       ToServerCommand m_command;
+};
+
+#endif
index 399c41b70afe5ac10321e9237ff2c8b6cf462e0a..826350505827bfccb9c79860b3faad9a447b1af6 100644 (file)
@@ -21,7 +21,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include <iostream>
 #include <queue>
 #include <algorithm>
-#include "clientserver.h"
+#include "network/networkprotocol.h"
+#include "network/serveropcodes.h"
 #include "ban.h"
 #include "environment.h"
 #include "map.h"
@@ -1149,12 +1150,11 @@ void Server::Receive()
        SharedBuffer<u8> data;
        u16 peer_id;
        u32 datasize;
-       try{
+       try {
                datasize = m_con.Receive(peer_id,data);
                ProcessData(*data, datasize, peer_id);
        }
-       catch(con::InvalidIncomingDataException &e)
-       {
+       catch(con::InvalidIncomingDataException &e) {
                infostream<<"Server::Receive(): "
                                "InvalidIncomingDataException: what()="
                                <<e.what()<<std::endl;
@@ -1164,14 +1164,12 @@ void Server::Receive()
                                "SerializationError: what()="
                                <<e.what()<<std::endl;
        }
-       catch(ClientStateError &e)
-       {
+       catch(ClientStateError &e) {
                errorstream << "ProcessData: peer=" << peer_id  << e.what() << std::endl;
                DenyAccess(peer_id, L"Your client sent something server didn't expect."
                                L"Try reconnecting or updating your client");
        }
-       catch(con::PeerNotFoundException &e)
-       {
+       catch(con::PeerNotFoundException &e) {
                // Do nothing
        }
 }
@@ -1197,9 +1195,8 @@ PlayerSAO* Server::StageTwoClientInit(u16 peer_id)
                static_cast<RemotePlayer*>(m_env->getPlayer(playername.c_str()));
 
        // If failed, cancel
-       if((playersao == NULL) || (player == NULL))
-       {
-               if(player && player->peer_id != 0){
+       if((playersao == NULL) || (player == NULL)) {
+               if(player && player->peer_id != 0) {
                        errorstream<<"Server: "<<playername<<": Failed to emerge player"
                                        <<" (player allocated to an another client)"<<std::endl;
                        DenyAccess(peer_id, L"Another client is connected with this "
@@ -1240,8 +1237,7 @@ PlayerSAO* Server::StageTwoClientInit(u16 peer_id)
                SendDeathscreen(peer_id, false, v3f(0,0,0));
 
        // Note things in chat if not in simple singleplayer mode
-       if(!m_simple_singleplayer_mode)
-       {
+       if(!m_simple_singleplayer_mode) {
                // Send information about server to player in chat
                SendChatMessage(peer_id, getStatusString());
 
@@ -1271,8 +1267,7 @@ PlayerSAO* Server::StageTwoClientInit(u16 peer_id)
                actionstream<<player->getName() <<" joins game. List of players: ";
 
                for (std::vector<std::string>::iterator i = names.begin();
-                               i != names.end(); i++)
-               {
+                               i != names.end(); i++) {
                        actionstream << *i << " ";
                }
 
@@ -1281,1467 +1276,1605 @@ PlayerSAO* Server::StageTwoClientInit(u16 peer_id)
        return playersao;
 }
 
-void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
+void Server::handleCommand_Deprecated(ToServerPacket* pkt)
 {
-       DSTACK(__FUNCTION_NAME);
-       // Environment is locked first.
-       JMutexAutoLock envlock(m_env_mutex);
+       infostream << "Server: " << toServerCommandTable[pkt->getCommand()].name
+               << " not supported anymore" << std::endl;
+}
 
-       ScopeProfiler sp(g_profiler, "Server::ProcessData");
+void Server::handleCommand_Init(ToServerPacket* pkt)
+{
+       // [0] u16 TOSERVER_INIT
+       // [2] u8 SER_FMT_VER_HIGHEST_READ
+       // [3] u8[20] player_name
+       // [23] 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(peer_id);
+       try {
+               Address address = getPeerAddress(pkt->getPeerId());
                addr_s = address.serializeString();
-
-               // drop player if is ip is banned
-               if(m_banmanager->isIpBanned(addr_s)){
-                       std::string ban_name = m_banmanager->getBanName(addr_s);
-                       infostream<<"Server: A banned client tried to connect from "
-                                       <<addr_s<<"; banned name was "
-                                       <<ban_name<<std::endl;
-                       // This actually doesn't seem to transfer to the client
-                       DenyAccess(peer_id, L"Your ip is banned. Banned name was "
-                                       +narrow_to_wide(ban_name));
-                       return;
-               }
        }
-       catch(con::PeerNotFoundException &e)
-       {
+       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 "
-                               <<peer_id<<" not found"<<std::endl;
+               infostream << "Server::ProcessData(): Cancelling: peer "
+                               << pkt->getPeerId() << " not found" << std::endl;
                return;
        }
 
-       try
-       {
-
-       if(datasize < 2)
+       // 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;
+       }
 
-       ToServerCommand command = (ToServerCommand)readU16(&data[0]);
-
-       if(command == TOSERVER_INIT)
-       {
-               // [0] u16 TOSERVER_INIT
-               // [2] u8 SER_FMT_VER_HIGHEST_READ
-               // [3] u8[20] player_name
-               // [23] u8[28] password <--- can be sent without this, from old versions
+       verbosestream << "Server: Got TOSERVER_INIT from " << addr_s << " (peer_id="
+                       << pkt->getPeerId() << ")" << std::endl;
 
-               if(datasize < 2+1+PLAYERNAME_SIZE)
-                       return;
+       // 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(), L"Running in simple singleplayer mode.");
+               return;
+       }
 
-               RemoteClient* client = getClient(peer_id, CS_Created);
+       // 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_CLIENT_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(), std::wstring(
+                               L"Your client's version is not supported.\n"
+                               L"Server version is ")
+                               + narrow_to_wide(minetest_version_simple) + L"."
+               );
+               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="<<peer_id<<")"<<std::endl;
-                       return;
-               }
+       client->setPendingSerializationVersion(deployed);
 
-               verbosestream<<"Server: Got TOSERVER_INIT from "<<addr_s<<" (peer_id="
-                               <<peer_id<<")"<<std::endl;
+       /*
+               Read and check network protocol version
+       */
 
-               // 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(peer_id, L"Running in simple singleplayer mode.");
-                       return;
-               }
+       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(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;
+       }
 
-               // First byte after command is maximum supported
-               // serialization version
-               u8 client_max = data[2];
-               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_CLIENT_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(peer_id, std::wstring(
+       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(), std::wstring(
                                        L"Your client's version is not supported.\n"
                                        L"Server version is ")
-                                       + narrow_to_wide(minetest_version_simple) + L"."
+                                       + 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;
                }
+       }
 
-               client->setPendingSerializationVersion(deployed);
-
-               /*
-                       Read and check network protocol version
-               */
-
-               u16 min_net_proto_version = 0;
-               if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2)
-                       min_net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE]);
+       /*
+               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;
+       }
 
-               // 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(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2+2)
-                       max_net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2]);
+       if (playername_length == PLAYERNAME_SIZE) {
+               actionstream << "Server: Player with name exceeding max length "
+                               << "tried to connect from " << addr_s << std::endl;
+               DenyAccess(pkt->getPeerId(), L"Name too long");
+               return;
+       }
 
-               // 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;
-               }
+       if(playername[0]=='\0') {
+               actionstream << "Server: Player with an empty name "
+                               << "tried to connect from " << addr_s << std::endl;
+               DenyAccess(pkt->getPeerId(), L"Empty name");
+               return;
+       }
 
-               verbosestream<<"Server: "<<addr_s<<": Protocol version: min: "
-                               <<min_net_proto_version<<", max: "<<max_net_proto_version
-                               <<", chosen: "<<net_proto_version<<std::endl;
+       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(), L"Name contains unallowed characters");
+               return;
+       }
 
-               client->net_proto_version = net_proto_version;
+       if(!isSingleplayer() && strcasecmp(playername, "singleplayer") == 0) {
+               actionstream << "Server: Player with the name \"singleplayer\" "
+                               << "tried to connect from " << addr_s << std::endl;
+               DenyAccess(pkt->getPeerId(), L"Name is not allowed");
+               return;
+       }
 
-               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(peer_id, 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))
-                       );
+       {
+               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(), narrow_to_wide(reason.c_str()));
                        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(peer_id, 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;
-                       }
-               }
+       infostream<<"Server: New connection: \""<<playername<<"\" from "
+                       <<addr_s<<" (peer_id="<<pkt->getPeerId()<<")"<<std::endl;
 
-               /*
-                       Set up player
-               */
-               char playername[PLAYERNAME_SIZE];
-               unsigned int playername_length = 0;
-               for (; playername_length < PLAYERNAME_SIZE; playername_length++ ) {
-                       playername[playername_length] = data[3+playername_length];
-                       if (data[3+playername_length] == 0)
-                               break;
+       // 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(u32 i=0; i<PASSWORD_SIZE - 1; i++) {
+                       given_password[i] = pkt->getChar(21 + i);
                }
+               given_password[PASSWORD_SIZE - 1] = 0;
+       }
 
-               if (playername_length == PLAYERNAME_SIZE) {
-                       actionstream<<"Server: Player with name exceeding max length "
-                                       <<"tried to connect from "<<addr_s<<std::endl;
-                       DenyAccess(peer_id, L"Name too long");
-                       return;
-               }
+       if(!base64_is_valid(given_password)){
+               actionstream << "Server: " << playername
+                               << " supplied invalid password hash" << std::endl;
+               DenyAccess(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(pkt->getPeerId(), L"Too many users.");
+               return;
+       }
 
-               if(playername[0]=='\0')
-               {
-                       actionstream<<"Server: Player with an empty name "
-                                       <<"tried to connect from "<<addr_s<<std::endl;
-                       DenyAccess(peer_id, L"Empty name");
-                       return;
-               }
+       std::string checkpwd; // Password hash to check against
+       bool has_auth = m_script->getAuth(playername, &checkpwd, NULL);
 
-               if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
-               {
-                       actionstream<<"Server: Player with an invalid name "
-                                       <<"tried to connect from "<<addr_s<<std::endl;
-                       DenyAccess(peer_id, L"Name contains unallowed characters");
+       // 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(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(!isSingleplayer() && strcasecmp(playername, "singleplayer") == 0)
-               {
-                       actionstream<<"Server: Player with the name \"singleplayer\" "
-                                       <<"tried to connect from "<<addr_s<<std::endl;
-                       DenyAccess(peer_id, L"Name is not allowed");
-                       return;
-               }
+               // If default_password is empty, allow any initial password
+               if (raw_default_password.length() == 0)
+                       initial_password = given_password;
 
-               {
-                       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(peer_id, narrow_to_wide(reason));
-                               return;
-                       }
-               }
+               m_script->createAuth(playername, initial_password);
+       }
 
-               infostream<<"Server: New connection: \""<<playername<<"\" from "
-                               <<addr_s<<" (peer_id="<<peer_id<<")"<<std::endl;
+       has_auth = m_script->getAuth(playername, &checkpwd, NULL);
 
-               // Get password
-               char given_password[PASSWORD_SIZE];
-               if(datasize < 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE)
-               {
-                       // old version - assume blank password
-                       given_password[0] = 0;
-               }
-               else
-               {
-                       for(u32 i=0; i<PASSWORD_SIZE-1; i++)
-                       {
-                               given_password[i] = data[23+i];
-                       }
-                       given_password[PASSWORD_SIZE-1] = 0;
-               }
+       if(!has_auth){
+               actionstream << "Server: " << playername << " cannot be authenticated"
+                               << " (auth handler does not work?)" << std::endl;
+               DenyAccess(pkt->getPeerId(), L"Not allowed to login");
+               return;
+       }
 
-               if(!base64_is_valid(given_password)){
-                       actionstream<<"Server: "<<playername
-                                       <<" supplied invalid password hash"<<std::endl;
-                       DenyAccess(peer_id, L"Invalid password hash");
-                       return;
-               }
+       if(given_password != checkpwd) {
+               actionstream << "Server: " << playername << " supplied wrong password"
+                               << std::endl;
+               DenyAccess(pkt->getPeerId(), L"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(peer_id, L"Too many users.");
-                       return;
-               }
+       RemotePlayer *player =
+                       static_cast<RemotePlayer*>(m_env->getPlayer(playername));
 
-               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(peer_id, 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(player && player->peer_id != 0) {
+               errorstream << "Server: " << playername << ": Failed to emerge player"
+                               << " (player allocated to an another client)" << std::endl;
+               DenyAccess(pkt->getPeerId(), L"Another client is connected with this "
+                               L"name. If your client closed unexpectedly, try again in "
+                               L"a minute.");
+       }
 
-                       // If default_password is empty, allow any initial password
-                       if (raw_default_password.length() == 0)
-                               initial_password = given_password;
+       m_clients.setPlayerName(pkt->getPeerId(), playername);
 
-                       m_script->createAuth(playername, initial_password);
-               }
+       /*
+               Answer with a TOCLIENT_INIT
+       */
+       {
+               SharedBuffer<u8> reply(2 + 1 + 6 + 8 + 4);
+               writeU16(&reply[0], TOCLIENT_INIT);
+               writeU8(&reply[2], deployed);
+               //send dummy pos for legacy reasons only
+               writeV3S16(&reply[2 + 1], floatToInt(v3f(0,0,0), BS));
+               writeU64(&reply[2 + 1 + 6], m_env->getServerMap().getSeed());
+               writeF1000(&reply[2 + 1 + 6 + 8], g_settings->getFloat("dedicated_server_step"));
 
-               has_auth = m_script->getAuth(playername, &checkpwd, NULL);
+               // Send as reliable
+               m_clients.send(pkt->getPeerId(), 0, reply, true);
+               m_clients.event(pkt->getPeerId(), CSE_Init);
+       }
+}
 
-               if(!has_auth){
-                       actionstream<<"Server: "<<playername<<" cannot be authenticated"
-                                       <<" (auth handler does not work?)"<<std::endl;
-                       DenyAccess(peer_id, L"Not allowed to login");
-                       return;
-               }
+void Server::handleCommand_Init2(ToServerPacket* pkt)
+{
+       verbosestream << "Server: Got TOSERVER_INIT2 from "
+                       << pkt->getPeerId() << std::endl;
 
-               if(given_password != checkpwd){
-                       actionstream<<"Server: "<<playername<<" supplied wrong password"
-                                       <<std::endl;
-                       DenyAccess(peer_id, L"Wrong password");
-                       return;
-               }
+       m_clients.event(pkt->getPeerId(), CSE_GotInit2);
+       u16 protocol_version = m_clients.getProtocolVersion(pkt->getPeerId());
 
-               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(peer_id, L"Another client is connected with this "
-                                       L"name. If your client closed unexpectedly, try again in "
-                                       L"a minute.");
-               }
+       ///// begin compatibility code
+       PlayerSAO* playersao = NULL;
+       if (protocol_version <= 22) {
+               playersao = StageTwoClientInit(pkt->getPeerId());
 
-               m_clients.setPlayerName(peer_id,playername);
+               if (playersao == NULL) {
+                       errorstream
+                               << "TOSERVER_INIT2 stage 2 client init failed for peer "
+                               << pkt->getPeerId() << std::endl;
+                       return;
+               }
+       }
+       ///// end compatibility code
 
-               /*
-                       Answer with a TOCLIENT_INIT
-               */
-               {
-                       SharedBuffer<u8> reply(2+1+6+8+4);
-                       writeU16(&reply[0], TOCLIENT_INIT);
-                       writeU8(&reply[2], deployed);
-                       //send dummy pos for legacy reasons only
-                       writeV3S16(&reply[2+1], floatToInt(v3f(0,0,0), BS));
-                       writeU64(&reply[2+1+6], m_env->getServerMap().getSeed());
-                       writeF1000(&reply[2+1+6+8], g_settings->getFloat("dedicated_server_step"));
+       /*
+               Send some initialization data
+       */
 
-                       // Send as reliable
-                       m_clients.send(peer_id, 0, reply, true);
-                       m_clients.event(peer_id, CSE_Init);
-               }
+       infostream << "Server: Sending content to "
+                       << getPlayerName(pkt->getPeerId()) << std::endl;
 
-               return;
-       }
+       // Send player movement settings
+       SendMovement(pkt->getPeerId());
 
-       if(command == TOSERVER_INIT2)
-       {
+       // Send item definitions
+       SendItemDef(pkt->getPeerId(), m_itemdef, protocol_version);
 
-               verbosestream<<"Server: Got TOSERVER_INIT2 from "
-                               <<peer_id<<std::endl;
+       // Send node definitions
+       SendNodeDef(pkt->getPeerId(), m_nodedef, protocol_version);
 
-               m_clients.event(peer_id, CSE_GotInit2);
-               u16 protocol_version = m_clients.getProtocolVersion(peer_id);
+       m_clients.event(pkt->getPeerId(), CSE_SetDefinitionsSent);
 
+       // Send media announcement
+       sendMediaAnnouncement(pkt->getPeerId());
 
-               ///// begin compatibility code
-               PlayerSAO* playersao = NULL;
-               if (protocol_version <= 22) {
-                       playersao = StageTwoClientInit(peer_id);
+       // Send detached inventories
+       sendDetachedInventories(pkt->getPeerId());
 
-                       if (playersao == NULL) {
-                               errorstream
-                                       << "TOSERVER_INIT2 stage 2 client init failed for peer "
-                                       << peer_id << std::endl;
-                               return;
-                       }
-               }
-               ///// end compatibility code
+       // Send time of day
+       u16 time = m_env->getTimeOfDay();
+       float time_speed = g_settings->getFloat("time_speed");
+       SendTimeOfDay(pkt->getPeerId(), time, time_speed);
 
-               /*
-                       Send some initialization data
-               */
+       ///// begin compatibility code
+       if (protocol_version <= 22) {
+               m_clients.event(pkt->getPeerId(), CSE_SetClientReady);
+               m_script->on_joinplayer(playersao);
+       }
+       ///// end compatibility code
 
-               infostream<<"Server: Sending content to "
-                               <<getPlayerName(peer_id)<<std::endl;
+       // 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!");
+       }
+}
 
-               // Send player movement settings
-               SendMovement(peer_id);
+void Server::handleCommand_RequestMedia(ToServerPacket* pkt)
+{
+       std::list<std::string> tosend;
+       u16 numfiles;
 
-               // Send item definitions
-               SendItemDef(peer_id, m_itemdef, protocol_version);
+       *pkt >> numfiles;
 
-               // Send node definitions
-               SendNodeDef(peer_id, m_nodedef, protocol_version);
+       infostream << "Sending " << numfiles << " files to "
+                       << getPlayerName(pkt->getPeerId()) << std::endl;
+       verbosestream << "TOSERVER_REQUEST_MEDIA: " << std::endl;
 
-               m_clients.event(peer_id, CSE_SetDefinitionsSent);
+       for(int i = 0; i < numfiles; i++) {
+               std::string name;
 
-               // Send media announcement
-               sendMediaAnnouncement(peer_id);
+               *pkt >> name;
 
-               // Send detached inventories
-               sendDetachedInventories(peer_id);
+               tosend.push_back(name);
+               verbosestream << "TOSERVER_REQUEST_MEDIA: requested file "
+                               << name << std::endl;
+       }
 
-               // Send time of day
-               u16 time = m_env->getTimeOfDay();
-               float time_speed = g_settings->getFloat("time_speed");
-               SendTimeOfDay(peer_id, time, time_speed);
+       sendRequestedMedia(pkt->getPeerId(), tosend);
+}
 
-               ///// begin compatibility code
-               if (protocol_version <= 22) {
-                       m_clients.event(peer_id, CSE_SetClientReady);
-                       m_script->on_joinplayer(playersao);
-               }
-               ///// end compatibility code
+void Server::handleCommand_ReceivedMedia(ToServerPacket* pkt)
+{
+}
 
-               // Warnings about protocol version can be issued here
-               if(getClient(peer_id)->net_proto_version < LATEST_PROTOCOL_VERSION)
-               {
-                       SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT'S "
-                                       L"VERSION MAY NOT BE FULLY COMPATIBLE WITH THIS SERVER!");
-               }
+void Server::handleCommand_ClientReady(ToServerPacket* 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;
        }
 
-       u8 peer_ser_ver = getClient(peer_id, CS_InitDone)->serialization_version;
-       u16 peer_proto_ver = getClient(peer_id, CS_InitDone)->net_proto_version;
+       PlayerSAO* playersao = StageTwoClientInit(peer_id);
 
-       if(peer_ser_ver == SER_FMT_VER_INVALID)
-       {
-               errorstream<<"Server::ProcessData(): Cancelling: Peer"
-                               " serialization format invalid or not initialized."
-                               " Skipping incoming command="<<command<<std::endl;
+       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;
        }
 
-       /* Handle commands relate to client startup */
-       if(command == TOSERVER_REQUEST_MEDIA) {
-               std::string datastring((char*)&data[2], datasize-2);
-               std::istringstream is(datastring, std::ios_base::binary);
 
-               std::list<std::string> tosend;
-               u16 numfiles = readU16(is);
+       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;
 
-               infostream<<"Sending "<<numfiles<<" files to "
-                               <<getPlayerName(peer_id)<<std::endl;
-               verbosestream<<"TOSERVER_REQUEST_MEDIA: "<<std::endl;
+       m_clients.setClientVersion(
+                       peer_id, major_ver, minor_ver, patch_ver,
+                       std::string(pkt->getString(6),(u16) pkt->getU8(4)));
 
-               for(int i = 0; i < numfiles; i++) {
-                       std::string name = deSerializeString(is);
-                       tosend.push_back(name);
-                       verbosestream<<"TOSERVER_REQUEST_MEDIA: requested file "
-                                       <<name<<std::endl;
-               }
+       m_clients.event(peer_id, CSE_SetClientReady);
+       m_script->on_joinplayer(playersao);
+}
 
-               sendRequestedMedia(peer_id, tosend);
-               return;
-       }
-       else if(command == TOSERVER_RECEIVED_MEDIA) {
+void Server::handleCommand_GotBlocks(ToServerPacket* 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);
        }
-       else if(command == TOSERVER_CLIENT_READY) {
-               // 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);
+void Server::handleCommand_PlayerPos(ToServerPacket* pkt)
+{
+       if(pkt->getSize() < 12 + 12 + 4 + 4)
+               return;
 
-               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;
-               }
+       v3s32 ps, ss;
+       s32 f32pitch, f32yaw;
 
+       *pkt >> ps;
+       *pkt >> ss;
+       *pkt >> f32pitch;
+       *pkt >> f32yaw;
 
-               if(datasize < 2+8) {
-                       errorstream
-                               << "TOSERVER_CLIENT_READY client sent inconsistent data, disconnecting peer_id: "
-                               << peer_id << std::endl;
-                       m_con.DisconnectPeer(peer_id);
-                       return;
-               }
+       f32 pitch = (f32)f32pitch / 100.0;
+       f32 yaw = (f32)f32yaw / 100.0;
+       u32 keyPressed = 0;
 
-               m_clients.setClientVersion(
-                               peer_id,
-                               data[2], data[3], data[4],
-                               std::string((char*) &data[8],(u16) data[6]));
+       if(pkt->getSize() >= 12 + 12 + 4 + 4 + 4)
+               *pkt >> keyPressed;
 
-               m_clients.event(peer_id, CSE_SetClientReady);
-               m_script->on_joinplayer(playersao);
+       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 = wrapDegrees(pitch);
+       yaw = wrapDegrees(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;
        }
-       else if(command == TOSERVER_GOTBLOCKS)
-       {
-               if(datasize < 2+1)
-                       return;
-
-               /*
-                       [0] u16 command
-                       [2] u8 count
-                       [3] v3s16 pos_0
-                       [3+6] v3s16 pos_1
-                       ...
-               */
 
-               u16 count = data[2];
-               for(u16 i=0; i<count; i++)
-               {
-                       if((s16)datasize < 2+1+(i+1)*6)
-                               throw con::InvalidIncomingDataException
-                                       ("GOTBLOCKS length is too short");
-                       v3s16 p = readV3S16(&data[2+1+i*6]);
-                       /*infostream<<"Server: GOTBLOCKS ("
-                                       <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
-                       RemoteClient *client = getClient(peer_id);
-                       client->GotBlock(p);
-               }
+       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 (m_clients.getClientState(peer_id) < CS_Active)
-       {
-               if (command == TOSERVER_PLAYERPOS) return;
+       player->setPosition(position);
+       player->setSpeed(speed);
+       player->setPitch(pitch);
+       player->setYaw(yaw);
+       player->keyPressed=keyPressed;
+       player->control.up = (bool)(keyPressed & 1);
+       player->control.down = (bool)(keyPressed & 2);
+       player->control.left = (bool)(keyPressed & 4);
+       player->control.right = (bool)(keyPressed & 8);
+       player->control.jump = (bool)(keyPressed & 16);
+       player->control.aux1 = (bool)(keyPressed & 32);
+       player->control.sneak = (bool)(keyPressed & 64);
+       player->control.LMB = (bool)(keyPressed & 128);
+       player->control.RMB = (bool)(keyPressed & 256);
+
+       bool cheated = playersao->checkMovementCheat();
+       if(cheated) {
+               // Call callbacks
+               m_script->on_cheat(playersao, "moved_too_fast");
+       }
+}
 
-               errorstream<<"Got packet command: " << command << " for peer id "
-                               << peer_id << " but client isn't active yet. Dropping packet "
-                               <<std::endl;
+void Server::handleCommand_DeletedBlocks(ToServerPacket* 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);
        }
+}
 
-       Player *player = m_env->getPlayer(peer_id);
+void Server::handleCommand_InventoryAction(ToServerPacket* pkt)
+{
+       Player *player = m_env->getPlayer(pkt->getPeerId());
        if(player == NULL) {
-               errorstream<<"Server::ProcessData(): Cancelling: "
-                               "No player for peer_id="<<peer_id
-                               << " disconnecting peer!" <<std::endl;
-               m_con.DisconnectPeer(peer_id);
+               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="<<peer_id
-                               << " disconnecting peer!" <<std::endl;
-               m_con.DisconnectPeer(peer_id);
+               errorstream << "Server::ProcessData(): Cancelling: "
+                               "No player object for peer_id=" << pkt->getPeerId()
+                               << " disconnecting peer!" << std::endl;
+               m_con.DisconnectPeer(pkt->getPeerId());
                return;
        }
 
-       if(command == TOSERVER_PLAYERPOS)
-       {
-               if(datasize < 2+12+12+4+4)
-                       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;
+       }
 
-               u32 start = 0;
-               v3s32 ps = readV3S32(&data[start+2]);
-               v3s32 ss = readV3S32(&data[start+2+12]);
-               f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
-               f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
-               u32 keyPressed = 0;
-               if(datasize >= 2+12+12+4+4+4)
-                       keyPressed = (u32)readU32(&data[2+12+12+4+4]);
-               v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
-               v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
-               pitch = wrapDegrees(pitch);
-               yaw = wrapDegrees(yaw);
-
-               player->setPosition(position);
-               player->setSpeed(speed);
-               player->setPitch(pitch);
-               player->setYaw(yaw);
-               player->keyPressed=keyPressed;
-               player->control.up = (bool)(keyPressed&1);
-               player->control.down = (bool)(keyPressed&2);
-               player->control.left = (bool)(keyPressed&4);
-               player->control.right = (bool)(keyPressed&8);
-               player->control.jump = (bool)(keyPressed&16);
-               player->control.aux1 = (bool)(keyPressed&32);
-               player->control.sneak = (bool)(keyPressed&64);
-               player->control.LMB = (bool)(keyPressed&128);
-               player->control.RMB = (bool)(keyPressed&256);
-
-               bool cheated = playersao->checkMovementCheat();
-               if(cheated){
-                       // Call callbacks
-                       m_script->on_cheat(playersao, "moved_too_fast");
-               }
+       // If something goes wrong, this player is to blame
+       RollbackScopeActor rollback_scope(m_rollback,
+                       std::string("player:")+player->getName());
 
-               /*infostream<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
-                               <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
-                               <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
-       }
-       else if(command == TOSERVER_DELETEDBLOCKS)
-       {
-               if(datasize < 2+1)
+       /*
+               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;
+               }
 
                /*
-                       [0] u16 command
-                       [2] u8 count
-                       [3] v3s16 pos_0
-                       [3+6] v3s16 pos_1
-                       ...
+                       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;
+               }
 
-               u16 count = data[2];
-               for(u16 i=0; i<count; i++)
-               {
-                       if((s16)datasize < 2+1+(i+1)*6)
-                               throw con::InvalidIncomingDataException
-                                       ("DELETEDBLOCKS length is too short");
-                       v3s16 p = readV3S16(&data[2+1+i*6]);
-                       /*infostream<<"Server: DELETEDBLOCKS ("
-                                       <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
-                       RemoteClient *client = getClient(peer_id);
-                       client->SetBlockNotSent(p);
+               // 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;
                }
        }
-       else if(command == TOSERVER_CLICK_OBJECT)
-       {
-               infostream<<"Server: CLICK_OBJECT not supported anymore"<<std::endl;
-               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;
+               }
        }
-       else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
-       {
-               infostream<<"Server: CLICK_ACTIVEOBJECT not supported anymore"<<std::endl;
+       /*
+               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(ToServerPacket* 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;
        }
-       else if(command == TOSERVER_GROUND_ACTION)
-       {
-               infostream<<"Server: GROUND_ACTION not supported anymore"<<std::endl;
+
+       // 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;
+               }
        }
-       else if(command == TOSERVER_RELEASE)
+
+       if(line != L"")
        {
-               infostream<<"Server: RELEASE not supported anymore"<<std::endl;
+               /*
+                       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::list<u16> clients = m_clients.getClientIDs();
+
+                       for(std::list<u16>::iterator
+                               i = clients.begin();
+                               i != clients.end(); ++i) {
+                               if (*i != pkt->getPeerId())
+                                       SendChatMessage(*i, line);
+                       }
+               }
+       }
+}
+
+void Server::handleCommand_Damage(ToServerPacket* 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;
        }
-       else if(command == TOSERVER_SIGNTEXT)
-       {
-               infostream<<"Server: SIGNTEXT not supported anymore"
-                               <<std::endl;
+
+       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;
        }
-       else if(command == TOSERVER_SIGNNODETEXT)
-       {
-               infostream<<"Server: SIGNNODETEXT not supported anymore"
-                               <<std::endl;
+
+       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);
+
+               if(playersao->getHP() == 0 && playersao->m_hp_not_sent)
+                       DiePlayer(pkt->getPeerId());
+
+               if(playersao->m_hp_not_sent)
+                       SendPlayerHP(pkt->getPeerId());
+       }
+}
+
+void Server::handleCommand_Breath(ToServerPacket* 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;
        }
-       else if(command == TOSERVER_INVENTORY_ACTION)
-       {
-               // Strip command and create a stream
-               std::string datastring((char*)&data[2], datasize-2);
-               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());
+       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;
+       }
 
-               /*
-                       Note: Always set inventory not sent, to repair cases
-                       where the client made a bad prediction.
-               */
+       playersao->setBreath(breath);
+       m_script->player_event(playersao,"breath_changed");
+}
 
-               /*
-                       Handle restrictions and special cases of the move action
-               */
-               if(a->getType() == IACTION_MOVE)
-               {
-                       IMoveAction *ma = (IMoveAction*)a;
+void Server::handleCommand_Password(ToServerPacket* pkt)
+{
+       /*
+               [0] u16 TOSERVER_PASSWORD
+               [2] u8[28] old password
+               [30] u8[28] new password
+       */
 
-                       ma->from_inv.applyCurrentPlayer(player->getName());
-                       ma->to_inv.applyCurrentPlayer(player->getName());
+       if(pkt->getSize() != PASSWORD_SIZE * 2)
+               return;
 
-                       setInventoryModified(ma->from_inv);
-                       setInventoryModified(ma->to_inv);
+       std::string oldpwd;
+       std::string newpwd;
 
-                       bool from_inv_is_current_player =
-                               (ma->from_inv.type == InventoryLocation::PLAYER) &&
-                               (ma->from_inv.name == player->getName());
+       for(u32 i=0; i<PASSWORD_SIZE - 1; i++) {
+               char c = pkt->getChar(i);
+               if(c == 0)
+                       break;
+               oldpwd += c;
+       }
 
-                       bool to_inv_is_current_player =
-                               (ma->to_inv.type == InventoryLocation::PLAYER) &&
-                               (ma->to_inv.name == player->getName());
+       for(u32 i=0; i<PASSWORD_SIZE - 1; i++) {
+               char c = pkt->getChar(PASSWORD_SIZE + i);
+               if(c == 0)
+                       break;
+               newpwd += c;
+       }
 
-                       /*
-                               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;
-                       }
+       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;
+       }
 
-                       /*
-                               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;
-                       }
+       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;
+       }
 
-                       // 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;
+       infostream << "Server: Client requests a password change from "
+                       << "'" << oldpwd << "' to '" << newpwd << "'" << std::endl;
 
-                       da->from_inv.applyCurrentPlayer(player->getName());
+       std::string playername = player->getName();
 
-                       setInventoryModified(da->from_inv);
+       std::string checkpwd;
+       m_script->getAuth(playername, &checkpwd, NULL);
 
-                       /*
-                               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;
-                       }
+       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;
+       }
 
-                       // 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;
+       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.");
+       }
+}
 
-                       ca->craft_inv.applyCurrentPlayer(player->getName());
+void Server::handleCommand_PlayerItem(ToServerPacket* pkt)
+{
+       if (pkt->getSize() < 2)
+               return;
 
-                       setInventoryModified(ca->craft_inv);
+       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;
+       }
 
-                       //bool craft_inv_is_current_player =
-                       //      (ca->craft_inv.type == InventoryLocation::PLAYER) &&
-                       //      (ca->craft_inv.name == player->getName());
+       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;
+       }
 
-                       // Disallow crafting if not allowed to interact
-                       if(!checkPriv(player->getName(), "interact"))
-                       {
-                               infostream<<"Cannot craft: "
-                                               <<"No interact privilege"<<std::endl;
-                               delete a;
-                               return;
-                       }
-               }
+       u16 item;
+
+       *pkt >> item;
 
-               // Do the action
-               a->apply(this, playersao, this);
-               // Eat the action
-               delete a;
+       playersao->setWieldIndex(item);
+}
+
+void Server::handleCommand_Respawn(ToServerPacket* 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;
        }
-       else if(command == TOSERVER_CHAT_MESSAGE)
-       {
-               /*
-                       u16 command
-                       u16 length
-                       wstring message
-               */
-               u8 buf[6];
-               std::string datastring((char*)&data[2], datasize-2);
-               std::istringstream is(datastring, std::ios_base::binary);
 
-               // Read stuff
-               is.read((char*)buf, 2);
-               u16 len = readU16(buf);
+       if(player->hp != 0 || !g_settings->getBool("enable_damage"))
+               return;
 
-               std::wstring message;
-               for(u16 i=0; i<len; i++)
-               {
-                       is.read((char*)buf, 2);
-                       message += (wchar_t)readU16(buf);
-               }
+       RespawnPlayer(pkt->getPeerId());
 
-               // If something goes wrong, this player is to blame
-               RollbackScopeActor rollback_scope(m_rollback,
-                               std::string("player:")+player->getName());
+       actionstream<<player->getName()<<" respawns at "
+                       <<PP(player->getPosition()/BS)<<std::endl;
 
-               // Get player name of this client
-               std::wstring name = narrow_to_wide(player->getName());
+       // ActiveObject is added to environment in AsyncRunStep after
+       // the previous addition has been succesfully removed
+}
 
-               // 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;
+void Server::handleCommand_Interact(ToServerPacket* pkt)
+{
+       std::string datastring(pkt->getString(0), pkt->getSize());
+       std::istringstream is(datastring, std::ios_base::binary);
 
-               // Line to send to players
-               std::wstring line;
-               // Whether to send to the player that sent the line
-               bool send_to_sender_only = false;
+       /*
+               [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);
 
-               // 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;
-                       }
-               }
+       verbosestream << "TOSERVER_INTERACT: action=" << (int)action << ", item="
+                       << item_i << ", pointed=" << pointed.dump() << std::endl;
 
-               if(line != L"")
-               {
-                       /*
-                               Send the message to sender
-                       */
-                       if (send_to_sender_only)
-                       {
-                               SendChatMessage(peer_id, line);
-                       }
-                       /*
-                               Send the message to others
-                       */
-                       else
-                       {
-                               actionstream<<"CHAT: "<<wide_to_narrow(line)<<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;
+       }
 
-                               std::list<u16> clients = m_clients.getClientIDs();
+       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;
+       }
 
-                               for(std::list<u16>::iterator
-                                       i = clients.begin();
-                                       i != clients.end(); ++i)
-                               {
-                                       if (*i != peer_id)
-                                               SendChatMessage(*i, line);
-                               }
-                       }
-               }
+       if(player->hp == 0) {
+               verbosestream << "TOSERVER_INTERACT: " << player->getName()
+                       << " tried to interact, but is dead!" << std::endl;
+               return;
        }
-       else if(command == TOSERVER_DAMAGE)
-       {
-               std::string datastring((char*)&data[2], datasize-2);
-               std::istringstream is(datastring, std::ios_base::binary);
-               u8 damage = readU8(is);
 
-               if(g_settings->getBool("enable_damage"))
-               {
-                       actionstream<<player->getName()<<" damaged by "
-                                       <<(int)damage<<" hp at "<<PP(player->getPosition()/BS)
-                                       <<std::endl;
+       v3f player_pos = playersao->getLastGoodPosition();
 
-                       playersao->setHP(playersao->getHP() - damage);
+       // Update wielded item
+       playersao->setWieldIndex(item_i);
 
-                       if(playersao->getHP() == 0 && playersao->m_hp_not_sent)
-                               DiePlayer(peer_id);
+       // Get pointed to node (undefined if not POINTEDTYPE_NODE)
+       v3s16 p_under = pointed.node_undersurface;
+       v3s16 p_above = pointed.node_abovesurface;
 
-                       if(playersao->m_hp_not_sent)
-                               SendPlayerHP(peer_id);
+       // 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;
                }
+
        }
-       else if(command == TOSERVER_BREATH)
-       {
-               std::string datastring((char*)&data[2], datasize-2);
-               std::istringstream is(datastring, std::ios_base::binary);
-               u16 breath = readU16(is);
-               playersao->setBreath(breath);
-               m_script->player_event(playersao,"breath_changed");
+
+       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;
        }
-       else if(command == TOSERVER_PASSWORD)
-       {
-               /*
-                       [0] u16 TOSERVER_PASSWORD
-                       [2] u8[28] old password
-                       [30] u8[28] new password
-               */
 
-               if(datasize != 2+PASSWORD_SIZE*2)
+       /*
+               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;
-               /*char password[PASSWORD_SIZE];
-               for(u32 i=0; i<PASSWORD_SIZE-1; i++)
-                       password[i] = data[2+i];
-               password[PASSWORD_SIZE-1] = 0;*/
-               std::string oldpwd;
-               for(u32 i=0; i<PASSWORD_SIZE-1; i++)
-               {
-                       char c = data[2+i];
-                       if(c == 0)
-                               break;
-                       oldpwd += c;
-               }
-               std::string newpwd;
-               for(u32 i=0; i<PASSWORD_SIZE-1; i++)
-               {
-                       char c = data[2+PASSWORD_SIZE+i];
-                       if(c == 0)
-                               break;
-                       newpwd += c;
                }
+       }
 
-               if(!base64_is_valid(newpwd)){
-                       infostream<<"Server: "<<player->getName()<<" supplied invalid password hash"<<std::endl;
-                       // Wrong old password supplied!!
-                       SendChatMessage(peer_id, L"Invalid new password hash supplied. Password NOT changed.");
-                       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;
+       }
 
-               infostream<<"Server: Client requests a password change from "
-                               <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
+       /*
+               If something goes wrong, this player is to blame
+       */
+       RollbackScopeActor rollback_scope(m_rollback,
+                       std::string("player:")+player->getName());
 
-               std::string playername = 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);
 
-               std::string checkpwd;
-               m_script->getAuth(playername, &checkpwd, NULL);
+                       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(oldpwd != checkpwd)
-               {
-                       infostream<<"Server: invalid old password"<<std::endl;
-                       // Wrong old password supplied!!
-                       SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
-                       return;
+                       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;
 
-               bool success = m_script->setPassword(playername, newpwd);
-               if(success){
-                       actionstream<<player->getName()<<" changes password"<<std::endl;
-                       SendChatMessage(peer_id, L"Password change successful.");
-               } else {
-                       actionstream<<player->getName()<<" tries to change password but "
-                                       <<"it fails"<<std::endl;
-                       SendChatMessage(peer_id, L"Password change failed or inavailable.");
+                       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();
+                       pointed_object->punch(dir, &toolcap, playersao,
+                                       time_from_last_punch);
                }
-       }
-       else if(command == TOSERVER_PLAYERITEM)
-       {
-               if (datasize < 2+2)
-                       return;
 
-               u16 item = readU16(&data[2]);
-               playersao->setWieldIndex(item);
-       }
-       else if(command == TOSERVER_RESPAWN)
-       {
-               if(player->hp != 0 || !g_settings->getBool("enable_damage"))
-                       return;
+       } // action == 0
 
-               RespawnPlayer(peer_id);
+       /*
+               1: stop digging
+       */
+       else if(action == 1) {
+       } // action == 1
 
-               actionstream<<player->getName()<<" respawns at "
-                               <<PP(player->getPosition()/BS)<<std::endl;
+       /*
+               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);
+                       }
 
-               // ActiveObject is added to environment in AsyncRunStep after
-               // the previous addition has been succesfully removed
-       }
-       else if(command == TOSERVER_INTERACT)
-       {
-               std::string datastring((char*)&data[2], datasize-2);
-               std::istringstream is(datastring, std::ios_base::binary);
+                       /* 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");
+                               }
+                       }
 
-               /*
-                       [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);
+                       /* Actually dig node */
 
-               verbosestream<<"TOSERVER_INTERACT: action="<<(int)action<<", item="
-                               <<item_i<<", pointed="<<pointed.dump()<<std::endl;
+                       if(is_valid_dig && n.getContent() != CONTENT_IGNORE)
+                               m_script->node_on_dig(p_under, n, playersao);
 
-               if(player->hp == 0)
-               {
-                       verbosestream<<"TOSERVER_INTERACT: "<<player->getName()
-                               <<" tried to interact, but is dead!"<<std::endl;
-                       return;
+                       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
 
-               v3f player_pos = playersao->getLastGoodPosition();
+       /*
+               3: place block or right-click object
+       */
+       else if(action == 3) {
+               ItemStack item = playersao->getWieldedItem();
 
-               // Update wielded item
-               playersao->setWieldIndex(item_i);
+               // 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;
 
-               // Get pointed to node (undefined if not POINTEDTYPE_NODE)
-               v3s16 p_under = pointed.node_undersurface;
-               v3s16 p_above = pointed.node_abovesurface;
+               if(pointed.type == POINTEDTHING_OBJECT) {
+                       // Right click object
 
-               // 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;
+                       // 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;
 
-               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;
+                       // Do stuff
+                       pointed_object->rightClick(playersao);
                }
+               else if(m_script->item_OnPlace(
+                               item, playersao, pointed)) {
+                       // Placement was handled in lua
 
-               /*
-                       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(peer_id);
-                               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;
-                       }
+                       // Apply returned ItemStack
+                       playersao->setWieldedItem(item);
                }
 
-               /*
-                       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(peer_id);
-                       // Digging completed -> under
-                       if(action == 2){
-                               v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
-                               client->SetBlockNotSent(blockpos);
+               // 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);
                        }
-                       // Placement -> above
-                       if(action == 3){
-                               v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS));
-                               client->SetBlockNotSent(blockpos);
+               }
+               else {
+                       client->ResendBlockIfOnWire(blockpos);
+                       if(blockpos2 != blockpos) {
+                               client->ResendBlockIfOnWire(blockpos2);
                        }
-                       return;
                }
+       } // action == 3
 
-               /*
-                       If something goes wrong, this player is to blame
-               */
-               RollbackScopeActor rollback_scope(m_rollback,
-                               std::string("player:")+player->getName());
+       /*
+               4: use
+       */
+       else if(action == 4) {
+               ItemStack item = playersao->getWieldedItem();
 
-               /*
-                       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(peer_id, getNodeBlockPos(p_above), false);
-                               }
+               actionstream << player->getName() << " uses " << item.name
+                               << ", pointing at " << pointed.dump() << std::endl;
 
-                               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();
-                               pointed_object->punch(dir, &toolcap, playersao,
-                                               time_from_last_punch);
-                       }
+               if(m_script->item_OnUse(
+                               item, playersao, pointed)) {
+                       // Apply returned ItemStack
+                       playersao->setWieldedItem(item);
+               }
 
-               } // action == 0
+       } // action == 4
 
-               /*
-                       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(peer_id, getNodeBlockPos(p_above), false);
-                               }
+       /*
+               Catch invalid actions
+       */
+       else {
+               infostream << "WARNING: Server: Invalid action "
+                               << action << std::endl;
+       }
+}
 
-                               /* 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");
-                                       }
-                               }
+void Server::handleCommand_RemovedSounds(ToServerPacket* pkt)
+{
+       u16 num;
+       *pkt >> num;
+       for(int k=0; k<num; k++) {
+               s32 id;
 
-                               /* Actually dig node */
+               *pkt >> id;
 
-                               if(is_valid_dig && n.getContent() != CONTENT_IGNORE)
-                                       m_script->node_on_dig(p_under, n, playersao);
+               std::map<s32, ServerPlayingSound>::iterator i =
+                       m_playing_sounds.find(id);
 
-                               v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
-                               RemoteClient *client = getClient(peer_id);
-                               // 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
+               if(i == m_playing_sounds.end())
+                       continue;
 
-               /*
-                       3: place block or right-click object
-               */
-               else if(action == 3)
-               {
-                       ItemStack item = playersao->getWieldedItem();
+               ServerPlayingSound &psound = i->second;
+               psound.clients.erase(pkt->getPeerId());
+               if(psound.clients.empty())
+                       m_playing_sounds.erase(i++);
+       }
+}
 
-                       // Reset build time counter
-                       if(pointed.type == POINTEDTHING_NODE &&
-                                       item.getDefinition(m_itemdef).type == ITEM_NODE)
-                               getClient(peer_id)->m_time_from_building = 0.0;
+void Server::handleCommand_NodeMetaFields(ToServerPacket* pkt)
+{
+       v3s16 p;
+       std::string formname;
+       u16 num;
 
-                       if(pointed.type == POINTEDTHING_OBJECT)
-                       {
-                               // Right click object
+       *pkt >> p >> formname >> num;
 
-                               // Skip if object has been removed
-                               if(pointed_object->m_removed)
-                                       return;
+       std::map<std::string, std::string> fields;
+       for(int k=0; k<num; k++) {
+               std::string fieldname;
+               *pkt >> fieldname;
+               fields[fieldname] = pkt->readLongString();
+       }
 
-                               actionstream<<player->getName()<<" right-clicks object "
-                                               <<pointed.object_id<<": "
-                                               <<pointed_object->getDescription()<<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;
+       }
 
-                               // Do stuff
-                               pointed_object->rightClick(playersao);
-                       }
-                       else if(m_script->item_OnPlace(
-                                       item, playersao, pointed))
-                       {
-                               // Placement was handled in lua
+       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;
+       }
 
-                               // Apply returned ItemStack
-                               playersao->setWieldedItem(item);
-                       }
+       // If something goes wrong, this player is to blame
+       RollbackScopeActor rollback_scope(m_rollback,
+                       std::string("player:")+player->getName());
 
-                       // If item has node placement prediction, always send the
-                       // blocks to make sure the client knows what exactly happened
-                       RemoteClient *client = getClient(peer_id);
-                       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
+       // Check the target node for rollback data; leave others unnoticed
+       RollbackNode rn_old(&m_env->getMap(), p, this);
 
-               /*
-                       4: use
-               */
-               else if(action == 4)
-               {
-                       ItemStack item = playersao->getWieldedItem();
+       m_script->node_on_receive_fields(p, formname, fields, playersao);
 
-                       actionstream<<player->getName()<<" uses "<<item.name
-                                       <<", pointing at "<<pointed.dump()<<std::endl;
+       // 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);
+       }
+}
 
-                       if(m_script->item_OnUse(
-                                       item, playersao, pointed))
-                       {
-                               // Apply returned ItemStack
-                               playersao->setWieldedItem(item);
-                       }
+void Server::handleCommand_InventoryFields(ToServerPacket* pkt)
+{
+       std::string formname;
+       u16 num;
 
-               } // action == 4
+       *pkt >> formname >> num;
 
+       std::map<std::string, std::string> fields;
+       for(int k=0; k<num; k++) {
+               std::string fieldname;
+               *pkt >> fieldname;
+               fields[fieldname] = pkt->readLongString();
+       }
 
-               /*
-                       Catch invalid actions
-               */
-               else
-               {
-                       infostream<<"WARNING: Server: Invalid action "
-                                       <<action<<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;
        }
-       else if(command == TOSERVER_REMOVED_SOUNDS)
-       {
-               std::string datastring((char*)&data[2], datasize-2);
-               std::istringstream is(datastring, std::ios_base::binary);
-
-               int num = readU16(is);
-               for(int k=0; k<num; k++){
-                       s32 id = readS32(is);
-                       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(peer_id);
-                       if(psound.clients.empty())
-                               m_playing_sounds.erase(i++);
+
+       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;
+       }
+
+       m_script->on_playerReceiveFields(playersao, formname, fields);
+}
+
+inline void Server::handleCommand(ToServerPacket* pkt)
+{
+       const ToServerCommandHandler& opHandle = toServerCommandTable[pkt->getCommand()];
+       (this->*opHandle.handler)(pkt);
+}
+
+void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
+{
+       DSTACK(__FUNCTION_NAME);
+       // Environment is locked first.
+       JMutexAutoLock envlock(m_env_mutex);
+
+       ScopeProfiler sp(g_profiler, "Server::ProcessData");
+
+       try {
+               Address address = getPeerAddress(peer_id);
+               std::string addr_s = address.serializeString();
+
+               if(m_banmanager->isIpBanned(addr_s)) {
+                       std::string ban_name = m_banmanager->getBanName(addr_s);
+                       infostream << "Server: A banned client tried to connect from "
+                                       << addr_s << "; banned name was "
+                                       << ban_name << std::endl;
+                       // This actually doesn't seem to transfer to the client
+                       DenyAccess(peer_id, L"Your ip is banned. Banned name was "
+                                       + narrow_to_wide(ban_name));
+                       return;
                }
        }
-       else if(command == TOSERVER_NODEMETA_FIELDS)
-       {
-               std::string datastring((char*)&data[2], datasize-2);
-               std::istringstream is(datastring, std::ios_base::binary);
-
-               v3s16 p = readV3S16(is);
-               std::string formname = deSerializeString(is);
-               int num = readU16(is);
-               std::map<std::string, std::string> fields;
-               for(int k=0; k<num; k++){
-                       std::string fieldname = deSerializeString(is);
-                       std::string fieldvalue = deSerializeLongString(is);
-                       fields[fieldname] = fieldvalue;
+       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 "
+                               << peer_id << " not found" << std::endl;
+               return;
+       }
+
+       try {
+               if(datasize < 2)
+                       return;
+
+               ToServerPacket* pkt = new ToServerPacket(data, datasize, peer_id);
+
+               ToServerCommand command = pkt->getCommand();
+
+               // Command must be handled into ToServerCommandHandler
+               if (command >= TOSERVER_NUM_MSG_TYPES) {
+                       infostream << "Server: Ignoring unknown command "
+                                        << command << std::endl;
                }
 
-               // If something goes wrong, this player is to blame
-               RollbackScopeActor rollback_scope(m_rollback,
-                               std::string("player:")+player->getName());
+               if (toServerCommandTable[command].state == TOSERVER_STATE_NOT_CONNECTED) {
+                       handleCommand(pkt);
+                       delete pkt;
+                       return;
+               }
 
-               // Check the target node for rollback data; leave others unnoticed
-               RollbackNode rn_old(&m_env->getMap(), p, this);
+               u8 peer_ser_ver = getClient(peer_id, CS_InitDone)->serialization_version;
 
-               m_script->node_on_receive_fields(p, formname, fields,playersao);
+               if(peer_ser_ver == SER_FMT_VER_INVALID) {
+                       errorstream << "Server::ProcessData(): Cancelling: Peer"
+                                       " serialization format invalid or not initialized."
+                                       " Skipping incoming command=" << command << std::endl;
 
-               // 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);
+                       delete pkt;
+                       return;
                }
-       }
-       else if(command == TOSERVER_INVENTORY_FIELDS)
-       {
-               std::string datastring((char*)&data[2], datasize-2);
-               std::istringstream is(datastring, std::ios_base::binary);
-
-               std::string formname = deSerializeString(is);
-               int num = readU16(is);
-               std::map<std::string, std::string> fields;
-               for(int k=0; k<num; k++){
-                       std::string fieldname = deSerializeString(is);
-                       std::string fieldvalue = deSerializeLongString(is);
-                       fields[fieldname] = fieldvalue;
+
+               /* Handle commands related to client startup */
+               if (toServerCommandTable[command].state == TOSERVER_STATE_STARTUP) {
+                       handleCommand(pkt);
+                       delete pkt;
+                       return;
                }
 
-               m_script->on_playerReceiveFields(playersao, formname, fields);
-       }
-       else
-       {
-               infostream<<"Server::ProcessData(): Ignoring "
-                               "unknown command "<<command<<std::endl;
-       }
+               if (m_clients.getClientState(peer_id) < CS_Active) {
+                       if (command == TOSERVER_PLAYERPOS) return;
+
+                       errorstream << "Got packet command: " << command << " for peer id "
+                                       << peer_id << " but client isn't active yet. Dropping packet "
+                                       << std::endl;
+
+                       delete pkt;
+                       return;
+               }
+
+               handleCommand(pkt);
+               delete pkt;
 
-       } //try
-       catch(SendFailedException &e)
-       {
-               errorstream<<"Server::ProcessData(): SendFailedException: "
-                               <<"what="<<e.what()
-                               <<std::endl;
+       }
+       catch(SendFailedException &e) {
+               errorstream << "Server::ProcessData(): SendFailedException: "
+                               << "what=" << e.what()
+                               << std::endl;
        }
 }
 
@@ -2753,7 +2886,6 @@ void Server::setTimeOfDay(u32 time)
 
 void Server::onMapEditEvent(MapEditEvent *event)
 {
-       //infostream<<"Server::onMapEditEvent()"<<std::endl;
        if(m_ignore_map_edit_events)
                return;
        if(m_ignore_map_edit_events_area.contains(event->getArea()))
@@ -2764,13 +2896,10 @@ void Server::onMapEditEvent(MapEditEvent *event)
 
 Inventory* Server::getInventory(const InventoryLocation &loc)
 {
-       switch(loc.type){
+       switch (loc.type) {
        case InventoryLocation::UNDEFINED:
-       {}
-       break;
        case InventoryLocation::CURRENT_PLAYER:
-       {}
-       break;
+               break;
        case InventoryLocation::PLAYER:
        {
                Player *player = m_env->getPlayer(loc.name.c_str());
@@ -2781,7 +2910,7 @@ Inventory* Server::getInventory(const InventoryLocation &loc)
                        return NULL;
                return playersao->getInventory();
        }
-       break;
+               break;
        case InventoryLocation::NODEMETA:
        {
                NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p);
@@ -2789,14 +2918,14 @@ Inventory* Server::getInventory(const InventoryLocation &loc)
                        return NULL;
                return meta->getInventory();
        }
-       break;
+               break;
        case InventoryLocation::DETACHED:
        {
                if(m_detached_inventories.count(loc.name) == 0)
                        return NULL;
                return m_detached_inventories[loc.name];
        }
-       break;
+               break;
        default:
                assert(0);
        }
@@ -2806,8 +2935,7 @@ void Server::setInventoryModified(const InventoryLocation &loc)
 {
        switch(loc.type){
        case InventoryLocation::UNDEFINED:
-       {}
-       break;
+               break;
        case InventoryLocation::PLAYER:
        {
                Player *player = m_env->getPlayer(loc.name.c_str());
@@ -2819,7 +2947,7 @@ void Server::setInventoryModified(const InventoryLocation &loc)
                playersao->m_inventory_not_sent = true;
                playersao->m_wielded_item_not_sent = true;
        }
-       break;
+               break;
        case InventoryLocation::NODEMETA:
        {
                v3s16 blockpos = getNodeBlockPos(loc.p);
@@ -2830,12 +2958,12 @@ void Server::setInventoryModified(const InventoryLocation &loc)
 
                setBlockNotSent(blockpos);
        }
-       break;
+               break;
        case InventoryLocation::DETACHED:
        {
                sendDetachedInventory(loc.name,PEER_ID_INEXISTENT);
        }
-       break;
+               break;
        default:
                assert(0);
        }
index 3d6b00d99f65073c843f5561777cbb7b046a3a66..a61b70ec069a3c3d5f27ba31e8c775df9cc4c597 100644 (file)
@@ -33,6 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "util/thread.h"
 #include "environment.h"
 #include "clientiface.h"
+#include "network/toserverpacket.h"
 #include <string>
 #include <list>
 #include <map>
@@ -187,6 +188,35 @@ public:
        void AsyncRunStep(bool initial_step=false);
        void Receive();
        PlayerSAO* StageTwoClientInit(u16 peer_id);
+
+       /*
+        * Command Handlers
+        */
+
+       void handleCommand(ToServerPacket* pkt);
+
+       void handleCommand_Null(ToServerPacket* pkt) {};
+       void handleCommand_Deprecated(ToServerPacket* pkt);
+       void handleCommand_Init(ToServerPacket* pkt);
+       void handleCommand_Init2(ToServerPacket* pkt);
+       void handleCommand_RequestMedia(ToServerPacket* pkt);
+       void handleCommand_ReceivedMedia(ToServerPacket* pkt);
+       void handleCommand_ClientReady(ToServerPacket* pkt);
+       void handleCommand_GotBlocks(ToServerPacket* pkt);
+       void handleCommand_PlayerPos(ToServerPacket* pkt);
+       void handleCommand_DeletedBlocks(ToServerPacket* pkt);
+       void handleCommand_InventoryAction(ToServerPacket* pkt);
+       void handleCommand_ChatMessage(ToServerPacket* pkt);
+       void handleCommand_Damage(ToServerPacket* pkt);
+       void handleCommand_Breath(ToServerPacket* pkt);
+       void handleCommand_Password(ToServerPacket* pkt);
+       void handleCommand_PlayerItem(ToServerPacket* pkt);
+       void handleCommand_Respawn(ToServerPacket* pkt);
+       void handleCommand_Interact(ToServerPacket* pkt);
+       void handleCommand_RemovedSounds(ToServerPacket* pkt);
+       void handleCommand_NodeMetaFields(ToServerPacket* pkt);
+       void handleCommand_InventoryFields(ToServerPacket* pkt);
+
        void ProcessData(u8 *data, u32 datasize, u16 peer_id);
 
        // Environment must be locked when called
@@ -309,7 +339,7 @@ public:
        bool showFormspec(const char *name, const std::string &formspec, const std::string &formname);
        Map & getMap() { return m_env->getMap(); }
        ServerEnvironment & getEnv() { return *m_env; }
-       
+
        u32 hudAdd(Player *player, HudElement *element);
        bool hudRemove(Player *player, u32 id);
        bool hudChange(Player *player, u32 id, HudElementStat stat, void *value);
@@ -320,13 +350,13 @@ public:
 
        inline Address getPeerAddress(u16 peer_id)
                        { return m_con.GetPeerAddress(peer_id); }
-                       
+
        bool setLocalPlayerAnimations(Player *player, v2s32 animation_frames[4], f32 frame_speed);
        bool setPlayerEyeOffset(Player *player, v3f first, v3f third);
 
        bool setSky(Player *player, const video::SColor &bgcolor,
                        const std::string &type, const std::vector<std::string> &params);
-       
+
        bool overrideDayNightRatio(Player *player, bool do_override,
                        float brightness);
 
@@ -379,7 +409,7 @@ private:
        void SendSetSky(u16 peer_id, const video::SColor &bgcolor,
                        const std::string &type, const std::vector<std::string> &params);
        void SendOverrideDayNightRatio(u16 peer_id, bool do_override, float ratio);
-       
+
        /*
                Send a node removal/addition event to all clients except ignore_id.
                Additionally, if far_players!=NULL, players further away than
index 80494e07a4351bfd42295572ed843ede9c2ef486..b4158f241f15af041f3a51f2d86e3436d42604f0 100644 (file)
@@ -42,7 +42,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "util/numeric.h"
 #include "util/serialize.h"
 #include "noise.h" // PseudoRandom used for random data for compression
-#include "clientserver.h" // LATEST_PROTOCOL_VERSION
+#include "network/networkprotocol.h" // LATEST_PROTOCOL_VERSION
 #include <algorithm>
 
 /*