Commented out debug statements again
[oweals/minetest.git] / src / server.cpp
index 798f36ac109f3c2d29b91efc19248668a9c40193..fd93d7523b22f872fdbf8f2b9106c403a6382b21 100644 (file)
@@ -105,10 +105,10 @@ void * EmergeThread::Thread()
 
        DSTACK(__FUNCTION_NAME);
 
-       //bool debug=false;
-       
        BEGIN_DEBUG_EXCEPTION_HANDLER
 
+       bool enable_mapgen_debug_info = g_settings.getBool("enable_mapgen_debug_info");
+       
        /*
                Get block info from queue, emerge them and send them
                to clients.
@@ -155,7 +155,7 @@ void * EmergeThread::Thread()
                        Also decrement the emerge queue count in clients.
                */
 
-               bool optional = true;
+               bool only_from_disk = true;
 
                {
                        core::map<u16, u8>::Iterator i;
@@ -166,14 +166,15 @@ void * EmergeThread::Thread()
                                // Check flags
                                u8 flags = i.getNode()->getValue();
                                if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false)
-                                       optional = false;
+                                       only_from_disk = false;
                                
                        }
                }
-
-               /*dstream<<"EmergeThread: p="
-                               <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
-                               <<"optional="<<optional<<std::endl;*/
+               
+               if(enable_mapgen_debug_info)
+                       dstream<<"EmergeThread: p="
+                                       <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
+                                       <<"only_from_disk="<<only_from_disk<<std::endl;
                
                ServerMap &map = ((ServerMap&)m_server->m_env.getMap());
                        
@@ -184,11 +185,6 @@ void * EmergeThread::Thread()
                bool got_block = true;
                core::map<v3s16, MapBlock*> modified_blocks;
                
-               bool only_from_disk = false;
-               
-               if(optional)
-                       only_from_disk = true;
-
                /*
                        Fetch block from map or generate a single block
                */
@@ -203,6 +199,9 @@ void * EmergeThread::Thread()
                        block = map.getBlockNoCreateNoEx(p);
                        if(!block || block->isDummy() || !block->isGenerated())
                        {
+                               if(enable_mapgen_debug_info)
+                                       dstream<<"EmergeThread: not in memory, loading"<<std::endl;
+
                                // Get, load or create sector
                                /*ServerMapSector *sector =
                                                (ServerMapSector*)map.createSector(p2d);*/
@@ -213,12 +212,20 @@ void * EmergeThread::Thread()
                                                lighting_invalidated_blocks);*/
 
                                block = map.loadBlock(p);
+                               
+                               if(only_from_disk == false)
+                               {
+                                       if(block == NULL || block->isGenerated() == false)
+                                       {
+                                               if(enable_mapgen_debug_info)
+                                                       dstream<<"EmergeThread: generating"<<std::endl;
+                                               block = map.generateBlock(p, modified_blocks);
+                                       }
+                               }
 
-                               if(block == NULL && only_from_disk == false)
-                                       block = map.generateBlock(p, modified_blocks);
-                                       //block = map.generateBlock(p, changed_blocks);
-                                       /*block = map.generateBlock(p, block, sector, changed_blocks,
-                                                       lighting_invalidated_blocks);*/
+                               if(enable_mapgen_debug_info)
+                                       dstream<<"EmergeThread: ended up with: "
+                                                       <<analyze_block(block)<<std::endl;
 
                                if(block == NULL)
                                {
@@ -373,8 +380,7 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
        v3s16 center = getNodeBlockPos(center_nodepos);
        
        // Camera position and direction
-       v3f camera_pos =
-                       playerpos + v3f(0, BS+BS/2, 0);
+       v3f camera_pos = player->getEyePosition();
        v3f camera_dir = v3f(0,0,1);
        camera_dir.rotateYZBy(player->getPitch());
        camera_dir.rotateXZBy(player->getYaw());
@@ -578,7 +584,7 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
                                Don't generate or send if not in sight
                        */
 
-                       if(isBlockInSight(p, camera_pos, camera_dir, 10000*BS) == false)
+                       if(isBlockInSight(p, camera_pos, camera_dir, M_PI, 10000*BS) == false)
                        {
                                continue;
                        }
@@ -768,6 +774,7 @@ void RemoteClient::SendObjectData(
                u16 command
                u16 number of player positions
                for each player:
+                       u16 peer_id
                        v3s32 position*100
                        v3s32 speed*100
                        s32 pitch*100
@@ -1051,17 +1058,20 @@ u32 PIChecksum(core::list<PlayerInfo> &l)
 */
 
 Server::Server(
-               std::string mapsavedir
+               std::string mapsavedir,
+               std::string configpath
        ):
        m_env(new ServerMap(mapsavedir), this),
        m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
        m_authmanager(mapsavedir+"/auth.txt"),
+       m_banmanager(mapsavedir+"/ipban.txt"),
        m_thread(this),
        m_emergethread(this),
        m_time_counter(0),
        m_time_of_day_send_timer(0),
        m_uptime(0),
        m_mapsavedir(mapsavedir),
+       m_configpath(configpath),
        m_shutdown_requested(false),
        m_ignore_map_edit_events(false),
        m_ignore_map_edit_events_peer_id(0)
@@ -1243,6 +1253,20 @@ void Server::AsyncRunStep()
                m_uptime.set(m_uptime.get() + dtime);
        }
        
+       {
+               // Process connection's timeouts
+               JMutexAutoLock lock2(m_con_mutex);
+               ScopeProfiler sp(&g_profiler, "Server: connection timeout processing");
+               m_con.RunTimeouts(dtime);
+       }
+       
+       {
+               // This has to be called so that the client list gets synced
+               // with the peer list of the connection
+               ScopeProfiler sp(&g_profiler, "Server: peer change handling");
+               handlePeerChanges();
+       }
+
        /*
                Update m_time_of_day and overall game time
        */
@@ -1285,20 +1309,6 @@ void Server::AsyncRunStep()
                }
        }
 
-       {
-               // Process connection's timeouts
-               JMutexAutoLock lock2(m_con_mutex);
-               ScopeProfiler sp(&g_profiler, "Server: connection timeout processing");
-               m_con.RunTimeouts(dtime);
-       }
-       
-       {
-               // This has to be called so that the client list gets synced
-               // with the peer list of the connection
-               ScopeProfiler sp(&g_profiler, "Server: peer change handling");
-               handlePeerChanges();
-       }
-
        {
                JMutexAutoLock lock(m_env_mutex);
                // Step environment
@@ -1723,9 +1733,15 @@ void Server::AsyncRunStep()
                        }
                        else if(event->type == MEET_OTHER)
                        {
+                               dstream<<"Server: MEET_OTHER"<<std::endl;
                                prof.add("MEET_OTHER", 1);
-                               dstream<<"WARNING: Server: MEET_OTHER not implemented"
-                                               <<std::endl;
+                               for(core::map<v3s16, bool>::Iterator
+                                               i = event->modified_blocks.getIterator();
+                                               i.atEnd()==false; i++)
+                               {
+                                       v3s16 p = i.getNode()->getKey();
+                                       setBlockNotSent(p);
+                               }
                        }
                        else
                        {
@@ -1739,6 +1755,7 @@ void Server::AsyncRunStep()
                        */
                        if(far_players.size() > 0)
                        {
+                               // Convert list format to that wanted by SetBlocksNotSent
                                core::map<v3s16, MapBlock*> modified_blocks2;
                                for(core::map<v3s16, bool>::Iterator
                                                i = event->modified_blocks.getIterator();
@@ -1748,6 +1765,7 @@ void Server::AsyncRunStep()
                                        modified_blocks2.insert(p,
                                                        m_env.getMap().getBlockNoCreateNoEx(p));
                                }
+                               // Set blocks not sent
                                for(core::list<u16>::Iterator
                                                i = far_players.begin();
                                                i != far_players.end(); i++)
@@ -1824,6 +1842,10 @@ void Server::AsyncRunStep()
                        // Auth stuff
                        if(m_authmanager.isModified())
                                m_authmanager.save();
+
+                       //Bann stuff
+                       if(m_banmanager.isModified())
+                               m_banmanager.save();
                        
                        // Map
                        JMutexAutoLock lock(m_env_mutex);
@@ -1913,6 +1935,16 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                <<peer_id<<" not found"<<std::endl;
                return;
        }
+
+       // drop player if is ip is banned
+       if(m_banmanager.isIpBanned(peer->address.serializeString())){
+               SendAccessDenied(m_con, peer_id,
+                               L"Your ip is banned. Banned name was "
+                               +narrow_to_wide(m_banmanager.getBanName(
+                                       peer->address.serializeString())));
+               m_con.deletePeer(peer_id, false);
+               return;
+       }
        
        u8 peer_ser_ver = getClient(peer->id)->serialization_version;
 
@@ -1949,12 +1981,33 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
 
                //peer->serialization_version = deployed;
                getClient(peer->id)->pending_serialization_version = deployed;
-
+               
                if(deployed == SER_FMT_VER_INVALID)
                {
                        derr_server<<DTIME<<"Server: Cannot negotiate "
                                        "serialization version with peer "
                                        <<peer_id<<std::endl;
+                       SendAccessDenied(m_con, peer_id,
+                                       L"Your client is too old (map format)");
+                       return;
+               }
+               
+               /*
+                       Read and check network protocol version
+               */
+
+               u16 net_proto_version = 0;
+               if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2)
+               {
+                       net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE]);
+               }
+
+               getClient(peer->id)->net_proto_version = net_proto_version;
+
+               if(net_proto_version == 0)
+               {
+                       SendAccessDenied(m_con, peer_id,
+                                       L"Your client is too old. Please upgrade.");
                        return;
                }
 
@@ -1988,7 +2041,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
 
                // Get password
                char password[PASSWORD_SIZE];
-               if(datasize == 2+1+PLAYERNAME_SIZE)
+               if(datasize < 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE)
                {
                        // old version - assume blank password
                        password[0] = 0;
@@ -2012,7 +2065,10 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                        checkpwd = g_settings.get("default_password");
                }
                
-               if(password != checkpwd && checkpwd != "")
+               /*dstream<<"Server: Client gave password '"<<password
+                               <<"', the correct one is '"<<checkpwd<<"'"<<std::endl;*/
+               
+               if(password != checkpwd && m_authmanager.exists(playername))
                {
                        derr_server<<DTIME<<"Server: peer_id="<<peer_id
                                        <<": supplied invalid password for "
@@ -2123,6 +2179,9 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                UpdateCrafting(peer->id);
                SendInventory(peer->id);
 
+               // Send player items to all players
+               SendPlayerItems();
+
                // Send HP
                {
                        Player *player = m_env.getPlayer(peer_id);
@@ -2152,6 +2211,12 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                        message += L" joined game";
                        BroadcastChatMessage(message);
                }
+               
+               // Warnings about protocol version can be issued here
+               /*if(getClient(peer->id)->net_proto_version == 0)
+               {
+                       SendChatMessage(peer_id, L"# Server: NOTE: YOUR CLIENT IS OLD AND DOES NOT WORK PROPERLY WITH THIS SERVER");
+               }*/
 
                return;
        }
@@ -2359,77 +2424,94 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                        return;
                }
 
+               // Skip if object has been removed
+               if(obj->m_removed)
+                       return;
+               
                //TODO: Check that object is reasonably close
                
                // Left click, pick object up (usually)
                if(button == 0)
                {
-                       InventoryList *ilist = player->inventory.getList("main");
-                       if(g_settings.getBool("creative_mode") == false && ilist != NULL)
-                       {
+                       /*
+                               Try creating inventory item
+                       */
+                       InventoryItem *item = obj->createPickedUpItem();
                        
-                               // Skip if inventory has no free space
-                               if(ilist->getUsedSlots() == ilist->getSize())
+                       if(item)
+                       {
+                               InventoryList *ilist = player->inventory.getList("main");
+                               if(ilist != NULL)
                                {
-                                       dout_server<<"Player inventory has no free space"<<std::endl;
-                                       return;
-                               }
+                                       if(g_settings.getBool("creative_mode") == false)
+                                       {
+                                               // Skip if inventory has no free space
+                                               if(ilist->roomForItem(item) == false)
+                                               {
+                                                       dout_server<<"Player inventory has no free space"<<std::endl;
+                                                       return;
+                                               }
 
-                               // Skip if object has been removed
-                               if(obj->m_removed)
-                                       return;
-                               
-                               /*
-                                       Create the inventory item
-                               */
-                               InventoryItem *item = obj->createPickedUpItem();
-                               
-                               if(item)
-                               {
-                                       // Add to inventory and send inventory
-                                       ilist->addItem(item);
-                                       UpdateCrafting(player->peer_id);
-                                       SendInventory(player->peer_id);
+                                               // Add to inventory and send inventory
+                                               ilist->addItem(item);
+                                               UpdateCrafting(player->peer_id);
+                                               SendInventory(player->peer_id);
+                                       }
 
                                        // Remove object from environment
                                        obj->m_removed = true;
                                }
-                               else
-                               {
-                                       /*
-                                               Item cannot be picked up. Punch it instead.
-                                       */
+                       }
+                       else
+                       {
+                               /*
+                                       Item cannot be picked up. Punch it instead.
+                               */
 
-                                       ToolItem *titem = NULL;
-                                       std::string toolname = "";
+                               ToolItem *titem = NULL;
+                               std::string toolname = "";
 
-                                       InventoryList *mlist = player->inventory.getList("main");
-                                       if(mlist != NULL)
+                               InventoryList *mlist = player->inventory.getList("main");
+                               if(mlist != NULL)
+                               {
+                                       InventoryItem *item = mlist->getItem(item_i);
+                                       if(item && (std::string)item->getName() == "ToolItem")
                                        {
-                                               InventoryItem *item = mlist->getItem(item_i);
-                                               if(item && (std::string)item->getName() == "ToolItem")
-                                               {
-                                                       titem = (ToolItem*)item;
-                                                       toolname = titem->getToolName();
-                                               }
+                                               titem = (ToolItem*)item;
+                                               toolname = titem->getToolName();
                                        }
+                               }
 
-                                       v3f playerpos = player->getPosition();
-                                       v3f objpos = obj->getBasePosition();
-                                       v3f dir = (objpos - playerpos).normalize();
-                                       
-                                       u16 wear = obj->punch(toolname, dir);
-                                       
-                                       if(titem)
-                                       {
-                                               bool weared_out = titem->addWear(wear);
-                                               if(weared_out)
-                                                       mlist->deleteItem(item_i);
-                                               SendInventory(player->peer_id);
-                                       }
+                               v3f playerpos = player->getPosition();
+                               v3f objpos = obj->getBasePosition();
+                               v3f dir = (objpos - playerpos).normalize();
+                               
+                               u16 wear = obj->punch(toolname, dir);
+                               
+                               if(titem)
+                               {
+                                       bool weared_out = titem->addWear(wear);
+                                       if(weared_out)
+                                               mlist->deleteItem(item_i);
+                                       SendInventory(player->peer_id);
                                }
                        }
                }
+               // Right click, do something with object
+               if(button == 1)
+               {
+                       // Track hp changes super-crappily
+                       u16 oldhp = player->hp;
+                       
+                       // Do stuff
+                       obj->rightClick(player);
+                       
+                       // Send back stuff
+                       if(player->hp != oldhp)
+                       {
+                               SendPlayerHP(player);
+                       }
+               }
        }
        else if(command == TOSERVER_GROUND_ACTION)
        {
@@ -2492,7 +2574,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                        // Mandatory parameter; actually used for nothing
                        core::map<v3s16, MapBlock*> modified_blocks;
 
-                       u8 material = CONTENT_IGNORE;
+                       content_t material = CONTENT_IGNORE;
                        u8 mineral = MINERAL_NONE;
 
                        bool cannot_remove_node = false;
@@ -2503,7 +2585,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                // Get mineral
                                mineral = n.getMineral();
                                // Get material at position
-                               material = n.d;
+                               material = n.getContent();
                                // If not yet cancelled
                                if(cannot_remove_node == false)
                                {
@@ -2703,7 +2785,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                                        <<" because privileges are "<<getPlayerPrivs(player)
                                                        <<std::endl;
 
-                                       if(content_buildable_to(n2.d) == false
+                                       if(content_features(n2).buildable_to == false
                                                || no_enough_privs)
                                        {
                                                // Client probably has wrong data.
@@ -2734,14 +2816,14 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                // Create node data
                                MaterialItem *mitem = (MaterialItem*)item;
                                MapNode n;
-                               n.d = mitem->getMaterial();
+                               n.setContent(mitem->getMaterial());
 
                                // Calculate direction for wall mounted stuff
-                               if(content_features(n.d).wall_mounted)
-                                       n.dir = packDir(p_under - p_over);
+                               if(content_features(n).wall_mounted)
+                                       n.param2 = packDir(p_under - p_over);
 
                                // Calculate the direction for furnaces and chests and stuff
-                               if(content_features(n.d).param_type == CPT_FACEDIR_SIMPLE)
+                               if(content_features(n).param_type == CPT_FACEDIR_SIMPLE)
                                {
                                        v3f playerpos = player->getPosition();
                                        v3f blockpos = intToFloat(p_over, BS) - playerpos;
@@ -2841,6 +2923,18 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                        return;
                                }
 
+                               /*
+                                       If in creative mode, item dropping is disabled unless
+                                       player has build privileges
+                               */
+                               if(g_settings.getBool("creative_mode") &&
+                                       (getPlayerPrivs(player) & PRIV_BUILD) == 0)
+                               {
+                                       derr_server<<"Not allowing player to drop item: "
+                                                       "creative mode and no build privs"<<std::endl;
+                                       return;
+                               }
+
                                dout_server<<"Placing a miscellaneous item on map"
                                                <<std::endl;
                                
@@ -3104,6 +3198,9 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                                mlist->addItem(item1);
                                        }
                                }
+                               // Disallow moving items if not allowed to build
+                               else if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
+                                       return;
                        }
                        
                        if(disable_action == false)
@@ -3164,23 +3261,34 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                u64 privs = getPlayerPrivs(player);
 
                // Parse commands
-               std::wstring commandprefix = L"/#";
-               if(message.substr(0, commandprefix.size()) == commandprefix)
+               if(message[0] == L'/')
                {
-                       line += L"Server: ";
+                       size_t strip_size = 1;
+                       if (message[1] == L'#') // support old-style commans
+                               ++strip_size;
+                       message = message.substr(strip_size);
 
-                       message = message.substr(commandprefix.size());
+                       WStrfnd f1(message);
+                       f1.next(L" "); // Skip over /#whatever
+                       std::wstring paramstring = f1.next(L"");
 
                        ServerCommandContext *ctx = new ServerCommandContext(
                                str_split(message, L' '),
+                               paramstring,
                                this,
                                &m_env,
                                player,
                                privs);
 
-                       line += processServerCommand(ctx);
-                       send_to_sender = ctx->flags & 1;
-                       send_to_others = ctx->flags & 2;
+                       std::wstring reply(processServerCommand(ctx));
+                       send_to_sender = ctx->flags & SEND_TO_SENDER;
+                       send_to_others = ctx->flags & SEND_TO_OTHERS;
+
+                       if (ctx->flags & SEND_NO_PREFIX)
+                               line += reply;
+                       else
+                               line += L"Server: " + reply;
+
                        delete ctx;
 
                }
@@ -3290,6 +3398,9 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                        newpwd += c;
                }
 
+               dstream<<"Server: Client requests a password change from "
+                               <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
+
                std::string playername = player->getName();
 
                if(m_authmanager.exists(playername) == false)
@@ -3301,7 +3412,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                }
 
                std::string checkpwd = m_authmanager.getPassword(playername);
-               
+
                if(oldpwd != checkpwd)
                {
                        dstream<<"Server: invalid old password"<<std::endl;
@@ -3316,6 +3427,15 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                <<std::endl;
                SendChatMessage(peer_id, L"Password change successful");
        }
+       else if (command == TOSERVER_PLAYERITEM)
+       {
+               if (datasize < 2+2)
+                       return;
+
+               u16 item = readU16(&data[2]);
+               player->wieldItem(item);
+               SendWieldedItem(player);
+       }
        else
        {
                derr_server<<"WARNING: Server::ProcessData(): Ignoring "
@@ -3600,6 +3720,60 @@ void Server::SendInventory(u16 peer_id)
        m_con.Send(peer_id, 0, data, true);
 }
 
+std::string getWieldedItemString(const Player *player)
+{
+       const InventoryItem *item = player->getWieldItem();
+       if (item == NULL)
+               return std::string("");
+       std::ostringstream os(std::ios_base::binary);
+       item->serialize(os);
+       return os.str();
+}
+
+void Server::SendWieldedItem(const Player* player)
+{
+       DSTACK(__FUNCTION_NAME);
+
+       assert(player);
+
+       std::ostringstream os(std::ios_base::binary);
+
+       writeU16(os, TOCLIENT_PLAYERITEM);
+       writeU16(os, 1);
+       writeU16(os, player->peer_id);
+       os<<serializeString(getWieldedItemString(player));
+
+       // Make data buffer
+       std::string s = os.str();
+       SharedBuffer<u8> data((u8*)s.c_str(), s.size());
+
+       m_con.SendToAll(0, data, true);
+}
+
+void Server::SendPlayerItems()
+{
+       DSTACK(__FUNCTION_NAME);
+
+       std::ostringstream os(std::ios_base::binary);
+       core::list<Player *> players = m_env.getPlayers(true);
+
+       writeU16(os, TOCLIENT_PLAYERITEM);
+       writeU16(os, players.size());
+       core::list<Player *>::Iterator i;
+       for(i = players.begin(); i != players.end(); ++i)
+       {
+               Player *p = *i;
+               writeU16(os, p->peer_id);
+               os<<serializeString(getWieldedItemString(p));
+       }
+
+       // Make data buffer
+       std::string s = os.str();
+       SharedBuffer<u8> data((u8*)s.c_str(), s.size());
+
+       m_con.SendToAll(0, data, true);
+}
+
 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
 {
        DSTACK(__FUNCTION_NAME);
@@ -3992,7 +4166,9 @@ std::wstring Server::getStatusString()
        }
        os<<L"}";
        if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
-               os<<" WARNING: Map saving is disabled."<<std::endl;
+               os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
+       if(g_settings.get("motd") != "")
+               os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings.get("motd"));
        return os.str();
 }
 
@@ -4072,6 +4248,11 @@ Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id
                // Reset inventory to creative if in creative mode
                if(g_settings.getBool("creative_mode"))
                {
+                       // Warning: double code below
+                       // Backup actual inventory
+                       player->inventory_backup = new Inventory();
+                       *(player->inventory_backup) = player->inventory;
+                       // Set creative inventory
                        craft_set_creative_inventory(player);
                }
 
@@ -4125,6 +4306,11 @@ Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id
                
                if(g_settings.getBool("creative_mode"))
                {
+                       // Warning: double code above
+                       // Backup actual inventory
+                       player->inventory_backup = new Inventory();
+                       *(player->inventory_backup) = player->inventory;
+                       // Set creative inventory
                        craft_set_creative_inventory(player);
                }
                else if(g_settings.getBool("give_initial_stuff"))