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;
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();
}
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
/*
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
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;
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);
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: "
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);
// 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);
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;
};
PROTOCOL_VERSION 3:
Base for writing changes here
PROTOCOL_VERSION 4:
- Add TOCLIENT_TOOLDEF
Add TOCLIENT_TEXTURES
+ Add TOCLIENT_TOOLDEF
Add TOCLIENT_NODEDEF
*/
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
}
*/
- 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
*/
};
/*
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.
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())
//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
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();
/* 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++){
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()){
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);
}
/*