Improve loading screen and protocol
authorPerttu Ahola <celeron55@gmail.com>
Tue, 15 Nov 2011 21:58:56 +0000 (23:58 +0200)
committerPerttu Ahola <celeron55@gmail.com>
Tue, 29 Nov 2011 17:13:45 +0000 (19:13 +0200)
src/client.cpp
src/client.h
src/clientserver.h
src/game.cpp
src/server.cpp

index 4d9233d66d6590c760b596ccc8e79527b9da4445..9752ec5e2d287bfd3cb2b4e00988dd947c621166 100644 (file)
@@ -211,7 +211,11 @@ Client::Client(
        m_time_of_day(0),
        m_map_seed(0),
        m_password(password),
-       m_access_denied(false)
+       m_access_denied(false),
+       m_texture_receive_progress(0),
+       m_textures_received(false),
+       m_tooldef_received(false),
+       m_nodedef_received(false)
 {
        m_packetcounter_timer = 0.0;
        //m_delete_unused_sectors_timer = 0.0;
@@ -661,8 +665,14 @@ void Client::deletingPeer(con::Peer *peer, bool timeout)
 void Client::ReceiveAll()
 {
        DSTACK(__FUNCTION_NAME);
+       u32 start_ms = porting::getTimeMs();
        for(;;)
        {
+               // Limit time even if there would be huge amounts of data to
+               // process
+               if(porting::getTimeMs() > start_ms + 100)
+                       break;
+               
                try{
                        Receive();
                }
@@ -1505,24 +1515,6 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
                event.deathscreen.camera_point_target_z = camera_point_target.Z;
                m_client_event_queue.push_back(event);
        }
-       else if(command == TOCLIENT_TOOLDEF)
-       {
-               infostream<<"Client: Received tool definitions: packet size: "
-                               <<datasize<<std::endl;
-
-               std::string datastring((char*)&data[2], datasize-2);
-               std::istringstream is(datastring, std::ios_base::binary);
-
-               // Stop threads while updating content definitions
-               m_mesh_update_thread.stop();
-
-               std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
-               m_tooldef->deSerialize(tmp_is);
-               
-               // Resume threads
-               m_mesh_update_thread.setRun(true);
-               m_mesh_update_thread.Start();
-       }
        else if(command == TOCLIENT_TEXTURES)
        {
                infostream<<"Client: Received textures: packet size: "<<datasize
@@ -1539,7 +1531,9 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
                
                /*
                        u16 command
-                       u32 number of textures
+                       u16 total number of texture bunches
+                       u16 index of this bunch
+                       u32 number of textures in this bunch
                        for each texture {
                                u16 length of name
                                string name
@@ -1547,6 +1541,11 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
                                data
                        }
                */
+               int num_bunches = readU16(is);
+               int bunch_i = readU16(is);
+               m_texture_receive_progress = (float)bunch_i / (float)(num_bunches - 1);
+               if(bunch_i == num_bunches - 1)
+                       m_textures_received = true;
                int num_textures = readU32(is);
                infostream<<"Client: Received textures: count: "<<num_textures
                                <<std::endl;
@@ -1572,15 +1571,17 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
                        rfile->drop();
                }
                
-               // Rebuild inherited images and recreate textures
-               m_tsrc->rebuildImagesAndTextures();
+               if(m_nodedef_received && m_textures_received){
+                       // Rebuild inherited images and recreate textures
+                       m_tsrc->rebuildImagesAndTextures();
 
-               // Update texture atlas
-               if(g_settings->getBool("enable_texture_atlas"))
-                       m_tsrc->buildMainAtlas(this);
-               
-               // Update node textures
-               m_nodedef->updateTextures(m_tsrc);
+                       // Update texture atlas
+                       if(g_settings->getBool("enable_texture_atlas"))
+                               m_tsrc->buildMainAtlas(this);
+                       
+                       // Update node textures
+                       m_nodedef->updateTextures(m_tsrc);
+               }
 
                // Resume threads
                m_mesh_update_thread.setRun(true);
@@ -1590,6 +1591,26 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
                event.type = CE_TEXTURES_UPDATED;
                m_client_event_queue.push_back(event);
        }
+       else if(command == TOCLIENT_TOOLDEF)
+       {
+               infostream<<"Client: Received tool definitions: packet size: "
+                               <<datasize<<std::endl;
+
+               std::string datastring((char*)&data[2], datasize-2);
+               std::istringstream is(datastring, std::ios_base::binary);
+
+               m_tooldef_received = true;
+
+               // Stop threads while updating content definitions
+               m_mesh_update_thread.stop();
+
+               std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
+               m_tooldef->deSerialize(tmp_is);
+               
+               // Resume threads
+               m_mesh_update_thread.setRun(true);
+               m_mesh_update_thread.Start();
+       }
        else if(command == TOCLIENT_NODEDEF)
        {
                infostream<<"Client: Received node definitions: packet size: "
@@ -1598,18 +1619,22 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
                std::string datastring((char*)&data[2], datasize-2);
                std::istringstream is(datastring, std::ios_base::binary);
 
+               m_nodedef_received = true;
+
                // Stop threads while updating content definitions
                m_mesh_update_thread.stop();
 
                std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
                m_nodedef->deSerialize(tmp_is, this);
                
-               // Update texture atlas
-               if(g_settings->getBool("enable_texture_atlas"))
-                       m_tsrc->buildMainAtlas(this);
-               
-               // Update node textures
-               m_nodedef->updateTextures(m_tsrc);
+               if(m_textures_received){
+                       // Update texture atlas
+                       if(g_settings->getBool("enable_texture_atlas"))
+                               m_tsrc->buildMainAtlas(this);
+                       
+                       // Update node textures
+                       m_nodedef->updateTextures(m_tsrc);
+               }
 
                // Resume threads
                m_mesh_update_thread.setRun(true);
index b160a3bc914a1520169138bb4ef993f8f4dfc3ce..625170b174011382c5c099ad1a75a1ac4c682c4e 100644 (file)
@@ -305,11 +305,21 @@ public:
        // Get event from queue. CE_NONE is returned if queue is empty.
        ClientEvent getClientEvent();
        
-       inline bool accessDenied()
+       bool accessDenied()
        { return m_access_denied; }
 
-       inline std::wstring accessDeniedReason()
+       std::wstring accessDeniedReason()
        { return m_access_denied_reason; }
+
+       float textureReceiveProgress()
+       { return m_texture_receive_progress; }
+
+       bool texturesReceived()
+       { return m_textures_received; }
+       bool tooldefReceived()
+       { return m_tooldef_received; }
+       bool nodedefReceived()
+       { return m_nodedef_received; }
        
        float getRTT(void);
 
@@ -367,6 +377,10 @@ private:
        std::wstring m_access_denied_reason;
        InventoryContext m_inventory_context;
        Queue<ClientEvent> m_client_event_queue;
+       float m_texture_receive_progress;
+       bool m_textures_received;
+       bool m_tooldef_received;
+       bool m_nodedef_received;
        friend class FarMesh;
 };
 
index cd54fe239749a9836641f9a975d2148ad3923b21..148f99cc37f98121ccd21324bea6766c5fe60e09 100644 (file)
@@ -28,8 +28,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
        PROTOCOL_VERSION 3:
                Base for writing changes here
        PROTOCOL_VERSION 4:
-               Add TOCLIENT_TOOLDEF
                Add TOCLIENT_TEXTURES
+               Add TOCLIENT_TOOLDEF
                Add TOCLIENT_NODEDEF
 */
 
@@ -195,17 +195,12 @@ enum ToClientCommand
                v3f1000 camera point target (to point the death cause or whatever)
        */
 
-       TOCLIENT_TOOLDEF = 0x38,
-       /*
-               u16 command
-               u32 length of the next item
-               serialized ToolDefManager
-       */
-       
-       TOCLIENT_TEXTURES = 0x39,
+       TOCLIENT_TEXTURES = 0x38,
        /*
                u16 command
-               u32 number of textures
+               u16 total number of texture bunches
+               u16 index of this bunch
+               u32 number of textures in this bunch
                for each texture {
                        u16 length of name
                        string name
@@ -214,17 +209,18 @@ enum ToClientCommand
                }
        */
        
-       TOCLIENT_NODEDEF = 0x3a,
+       TOCLIENT_TOOLDEF = 0x39,
        /*
                u16 command
                u32 length of the next item
-               serialized NodeDefManager
+               serialized ToolDefManager
        */
        
-       //TOCLIENT_CONTENT_SENDING_MODE = 0x38,
+       TOCLIENT_NODEDEF = 0x3a,
        /*
                u16 command
-               u8 mode (0 = off, 1 = on)
+               u32 length of the next item
+               serialized NodeDefManager
        */
 };
 
index bb1998066b5e9bdd0c246767a2e28a996c524ed2..925dead7c6943681106cd4c40af3b1d69df9f70c 100644 (file)
@@ -632,21 +632,18 @@ void the_game(
        /*
                Draw "Loading" screen
        */
-       /*gui::IGUIStaticText *gui_loadingtext = */
-       //draw_load_screen(L"Loading and connecting...", driver, font);
 
        draw_load_screen(L"Loading...", driver, font);
        
-       // Create tool definition manager
-       IWritableToolDefManager *tooldef = createToolDefManager();
        // Create texture source
        IWritableTextureSource *tsrc = createTextureSource(device);
+       
+       // These will be filled by data received from the server
+       // Create tool definition manager
+       IWritableToolDefManager *tooldef = createToolDefManager();
        // Create node definition manager
        IWritableNodeDefManager *nodedef = createNodeDefManager();
 
-       // Fill node feature table with default definitions
-       //content_mapnode_init(nodedef);
-
        /*
                Create server.
                SharedPtr will delete it when it goes out of scope.
@@ -702,54 +699,51 @@ void the_game(
        connect_address.print(&infostream);
        infostream<<std::endl;
        client.connect(connect_address);
-
-       bool could_connect = false;
        
+       /*
+               Wait for server to accept connection
+       */
+       bool could_connect = false;
        try{
+               float frametime = 0.033;
+               const float timeout = 10.0;
                float time_counter = 0.0;
                for(;;)
                {
-                       if(client.connectedAndInitialized())
-                       {
+                       // Update client and server
+                       client.step(frametime);
+                       if(server != NULL)
+                               server->step(frametime);
+                       
+                       // End condition
+                       if(client.connectedAndInitialized()){
                                could_connect = true;
                                break;
                        }
+                       // Break conditions
                        if(client.accessDenied())
-                       {
                                break;
-                       }
-                       // Wait for 10 seconds
-                       if(time_counter >= 10.0)
-                       {
+                       if(time_counter >= timeout)
                                break;
-                       }
                        
+                       // Display status
                        std::wostringstream ss;
                        ss<<L"Connecting to server... (timeout in ";
-                       ss<<(int)(10.0 - time_counter + 1.0);
+                       ss<<(int)(timeout - time_counter + 1.0);
                        ss<<L" seconds)";
                        draw_load_screen(ss.str(), driver, font);
-
-                       /*// Update screen
-                       driver->beginScene(true, true, video::SColor(255,0,0,0));
-                       guienv->drawAll();
-                       driver->endScene();*/
-
-                       // Update client and server
-
-                       client.step(0.1);
-
-                       if(server != NULL)
-                               server->step(0.1);
                        
                        // Delay a bit
-                       sleep_ms(100);
-                       time_counter += 0.1;
+                       sleep_ms(1000*frametime);
+                       time_counter += frametime;
                }
        }
        catch(con::PeerNotFoundException &e)
        {}
-
+       
+       /*
+               Handle failure to connect
+       */
        if(could_connect == false)
        {
                if(client.accessDenied())
@@ -766,6 +760,56 @@ void the_game(
                //gui_loadingtext->remove();
                return;
        }
+       
+       /*
+               Wait until content has been received
+       */
+       bool got_content = false;
+       {
+               float frametime = 0.033;
+               const float timeout = 5.0;
+               float time_counter = 0.0;
+               for(;;)
+               {
+                       // Update client and server
+                       client.step(frametime);
+                       if(server != NULL)
+                               server->step(frametime);
+                       
+                       // End condition
+                       if(client.texturesReceived() &&
+                                       client.tooldefReceived() &&
+                                       client.nodedefReceived()){
+                               got_content = true;
+                               break;
+                       }
+                       // Break conditions
+                       if(!client.connectedAndInitialized())
+                               break;
+                       if(time_counter >= timeout)
+                               break;
+                       
+                       // Display status
+                       std::wostringstream ss;
+                       ss<<L"Waiting content... (continuing anyway in ";
+                       ss<<(int)(timeout - time_counter + 1.0);
+                       ss<<L" seconds)\n";
+
+                       ss<<(client.tooldefReceived()?L"[X]":L"[  ]");
+                       ss<<L" Tool definitions\n";
+                       ss<<(client.nodedefReceived()?L"[X]":L"[  ]");
+                       ss<<L" Node definitions\n";
+                       //ss<<(client.texturesReceived()?L"[X]":L"[  ]");
+                       ss<<L"["<<(int)(client.textureReceiveProgress()*100+0.5)<<L"%] ";
+                       ss<<L" Textures\n";
+
+                       draw_load_screen(ss.str(), driver, font);
+                       
+                       // Delay a bit
+                       sleep_ms(1000*frametime);
+                       time_counter += frametime;
+               }
+       }
 
        /*
                Create skybox
index 45630d3012ba9de9bc8c087ab8dd326059a0c0c6..2a9aac32beccc5e376746fddbac28da963a37756 100644 (file)
@@ -2139,15 +2139,15 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                        Send some initialization data
                */
 
-               // Send textures
-               SendTextures(peer_id);
-               
                // Send tool definitions
                SendToolDef(m_con, peer_id, m_toolmgr);
                
                // Send node definitions
                SendNodeDef(m_con, peer_id, m_nodemgr);
                
+               // Send textures
+               SendTextures(peer_id);
+               
                // Send player info to all players
                SendPlayerInfos();
 
@@ -4160,7 +4160,13 @@ void Server::SendTextures(u16 peer_id)
        
        /* Read textures */
        
-       core::list<SendableTexture> textures;
+       // Put 5kB in one bunch (this is not accurate)
+       u32 bytes_per_bunch = 5000;
+       
+       core::array< core::list<SendableTexture> > texture_bunches;
+       texture_bunches.push_back(core::list<SendableTexture>());
+       
+       u32 texture_size_bunch_total = 0;
        core::list<ModSpec> mods = getMods(m_modspaths);
        for(core::list<ModSpec>::Iterator i = mods.begin();
                        i != mods.end(); i++){
@@ -4186,6 +4192,7 @@ void Server::SendTextures(u16 peer_id)
                                fis.read(buf, 1024);
                                std::streamsize len = fis.gcount();
                                tmp_os.write(buf, len);
+                               texture_size_bunch_total += len;
                                if(fis.eof())
                                        break;
                                if(!fis.good()){
@@ -4201,40 +4208,57 @@ void Server::SendTextures(u16 peer_id)
                        errorstream<<"Server::SendTextures(): Loaded \""
                                        <<tname<<"\""<<std::endl;
                        // Put in list
-                       textures.push_back(SendableTexture(tname, tpath, tmp_os.str()));
+                       texture_bunches[texture_bunches.size()-1].push_back(
+                                       SendableTexture(tname, tpath, tmp_os.str()));
+                       
+                       // Start next bunch if got enough data
+                       if(texture_size_bunch_total >= bytes_per_bunch){
+                               texture_bunches.push_back(core::list<SendableTexture>());
+                               texture_size_bunch_total = 0;
+                       }
                }
        }
 
-       /* Create and send packet */
+       /* Create and send packets */
+       
+       u32 num_bunches = texture_bunches.size();
+       for(u32 i=0; i<num_bunches; i++)
+       {
+               /*
+                       u16 command
+                       u16 total number of texture bunches
+                       u16 index of this bunch
+                       u32 number of textures in this bunch
+                       for each texture {
+                               u16 length of name
+                               string name
+                               u32 length of data
+                               data
+                       }
+               */
+               std::ostringstream os(std::ios_base::binary);
 
-       /*
-               u16 command
-               u32 number of textures
-               for each texture {
-                       u16 length of name
-                       string name
-                       u32 length of data
-                       data
+               writeU16(os, TOCLIENT_TEXTURES);
+               writeU16(os, num_bunches);
+               writeU16(os, i);
+               writeU32(os, texture_bunches[i].size());
+               
+               for(core::list<SendableTexture>::Iterator
+                               j = texture_bunches[i].begin();
+                               j != texture_bunches[i].end(); j++){
+                       os<<serializeString(j->name);
+                       os<<serializeLongString(j->data);
                }
-       */
-       std::ostringstream os(std::ios_base::binary);
-
-       writeU16(os, TOCLIENT_TEXTURES);
-       writeU32(os, textures.size());
-       
-       for(core::list<SendableTexture>::Iterator i = textures.begin();
-                       i != textures.end(); i++){
-               os<<serializeString(i->name);
-               os<<serializeLongString(i->data);
+               
+               // Make data buffer
+               std::string s = os.str();
+               infostream<<"Server::SendTextures(): number of textures in bunch["
+                               <<i<<"]: "<<texture_bunches[i].size()
+                               <<", size: "<<s.size()<<std::endl;
+               SharedBuffer<u8> data((u8*)s.c_str(), s.size());
+               // Send as reliable
+               m_con.Send(peer_id, 0, data, true);
        }
-       
-       // Make data buffer
-       std::string s = os.str();
-       infostream<<"Server::SendTextures(): number of textures: "
-                       <<textures.size()<<", data size: "<<s.size()<<std::endl;
-       SharedBuffer<u8> data((u8*)s.c_str(), s.size());
-       // Send as reliable
-       m_con.Send(peer_id, 0, data, true);
 }
 
 /*