Improve loading screen and protocol

This commit is contained in:
Perttu Ahola 2011-11-15 23:58:56 +02:00
parent cde35d1606
commit 7696a38543
5 changed files with 220 additions and 117 deletions

@ -211,7 +211,11 @@ Client::Client(
m_time_of_day(0), m_time_of_day(0),
m_map_seed(0), m_map_seed(0),
m_password(password), 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_packetcounter_timer = 0.0;
//m_delete_unused_sectors_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() void Client::ReceiveAll()
{ {
DSTACK(__FUNCTION_NAME); DSTACK(__FUNCTION_NAME);
u32 start_ms = porting::getTimeMs();
for(;;) for(;;)
{ {
// Limit time even if there would be huge amounts of data to
// process
if(porting::getTimeMs() > start_ms + 100)
break;
try{ try{
Receive(); 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; event.deathscreen.camera_point_target_z = camera_point_target.Z;
m_client_event_queue.push_back(event); 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) else if(command == TOCLIENT_TEXTURES)
{ {
infostream<<"Client: Received textures: packet size: "<<datasize infostream<<"Client: Received textures: packet size: "<<datasize
@ -1539,7 +1531,9 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
/* /*
u16 command 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 { for each texture {
u16 length of name u16 length of name
string name string name
@ -1547,6 +1541,11 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
data 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); int num_textures = readU32(is);
infostream<<"Client: Received textures: count: "<<num_textures infostream<<"Client: Received textures: count: "<<num_textures
<<std::endl; <<std::endl;
@ -1572,6 +1571,7 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
rfile->drop(); rfile->drop();
} }
if(m_nodedef_received && m_textures_received){
// Rebuild inherited images and recreate textures // Rebuild inherited images and recreate textures
m_tsrc->rebuildImagesAndTextures(); m_tsrc->rebuildImagesAndTextures();
@ -1581,6 +1581,7 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
// Update node textures // Update node textures
m_nodedef->updateTextures(m_tsrc); m_nodedef->updateTextures(m_tsrc);
}
// Resume threads // Resume threads
m_mesh_update_thread.setRun(true); 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; event.type = CE_TEXTURES_UPDATED;
m_client_event_queue.push_back(event); 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) else if(command == TOCLIENT_NODEDEF)
{ {
infostream<<"Client: Received node definitions: packet size: " 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::string datastring((char*)&data[2], datasize-2);
std::istringstream is(datastring, std::ios_base::binary); std::istringstream is(datastring, std::ios_base::binary);
m_nodedef_received = true;
// Stop threads while updating content definitions // Stop threads while updating content definitions
m_mesh_update_thread.stop(); m_mesh_update_thread.stop();
std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary); std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
m_nodedef->deSerialize(tmp_is, this); m_nodedef->deSerialize(tmp_is, this);
if(m_textures_received){
// Update texture atlas // Update texture atlas
if(g_settings->getBool("enable_texture_atlas")) if(g_settings->getBool("enable_texture_atlas"))
m_tsrc->buildMainAtlas(this); m_tsrc->buildMainAtlas(this);
// Update node textures // Update node textures
m_nodedef->updateTextures(m_tsrc); m_nodedef->updateTextures(m_tsrc);
}
// Resume threads // Resume threads
m_mesh_update_thread.setRun(true); m_mesh_update_thread.setRun(true);

@ -305,12 +305,22 @@ public:
// Get event from queue. CE_NONE is returned if queue is empty. // Get event from queue. CE_NONE is returned if queue is empty.
ClientEvent getClientEvent(); ClientEvent getClientEvent();
inline bool accessDenied() bool accessDenied()
{ return m_access_denied; } { return m_access_denied; }
inline std::wstring accessDeniedReason() std::wstring accessDeniedReason()
{ return m_access_denied_reason; } { 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); float getRTT(void);
// IGameDef interface // IGameDef interface
@ -367,6 +377,10 @@ private:
std::wstring m_access_denied_reason; std::wstring m_access_denied_reason;
InventoryContext m_inventory_context; InventoryContext m_inventory_context;
Queue<ClientEvent> m_client_event_queue; 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; friend class FarMesh;
}; };

@ -28,8 +28,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
PROTOCOL_VERSION 3: PROTOCOL_VERSION 3:
Base for writing changes here Base for writing changes here
PROTOCOL_VERSION 4: PROTOCOL_VERSION 4:
Add TOCLIENT_TOOLDEF
Add TOCLIENT_TEXTURES Add TOCLIENT_TEXTURES
Add TOCLIENT_TOOLDEF
Add TOCLIENT_NODEDEF Add TOCLIENT_NODEDEF
*/ */
@ -195,17 +195,12 @@ enum ToClientCommand
v3f1000 camera point target (to point the death cause or whatever) v3f1000 camera point target (to point the death cause or whatever)
*/ */
TOCLIENT_TOOLDEF = 0x38, TOCLIENT_TEXTURES = 0x38,
/* /*
u16 command u16 command
u32 length of the next item u16 total number of texture bunches
serialized ToolDefManager u16 index of this bunch
*/ u32 number of textures in this bunch
TOCLIENT_TEXTURES = 0x39,
/*
u16 command
u32 number of textures
for each texture { for each texture {
u16 length of name u16 length of name
string name string name
@ -214,18 +209,19 @@ enum ToClientCommand
} }
*/ */
TOCLIENT_TOOLDEF = 0x39,
/*
u16 command
u32 length of the next item
serialized ToolDefManager
*/
TOCLIENT_NODEDEF = 0x3a, TOCLIENT_NODEDEF = 0x3a,
/* /*
u16 command u16 command
u32 length of the next item u32 length of the next item
serialized NodeDefManager serialized NodeDefManager
*/ */
//TOCLIENT_CONTENT_SENDING_MODE = 0x38,
/*
u16 command
u8 mode (0 = off, 1 = on)
*/
}; };
enum ToServerCommand enum ToServerCommand

@ -632,21 +632,18 @@ void the_game(
/* /*
Draw "Loading" screen Draw "Loading" screen
*/ */
/*gui::IGUIStaticText *gui_loadingtext = */
//draw_load_screen(L"Loading and connecting...", driver, font);
draw_load_screen(L"Loading...", driver, font); draw_load_screen(L"Loading...", driver, font);
// Create tool definition manager
IWritableToolDefManager *tooldef = createToolDefManager();
// Create texture source // Create texture source
IWritableTextureSource *tsrc = createTextureSource(device); 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 // Create node definition manager
IWritableNodeDefManager *nodedef = createNodeDefManager(); IWritableNodeDefManager *nodedef = createNodeDefManager();
// Fill node feature table with default definitions
//content_mapnode_init(nodedef);
/* /*
Create server. Create server.
SharedPtr will delete it when it goes out of scope. SharedPtr will delete it when it goes out of scope.
@ -703,53 +700,50 @@ void the_game(
infostream<<std::endl; infostream<<std::endl;
client.connect(connect_address); client.connect(connect_address);
/*
Wait for server to accept connection
*/
bool could_connect = false; bool could_connect = false;
try{ try{
float frametime = 0.033;
const float timeout = 10.0;
float time_counter = 0.0; float time_counter = 0.0;
for(;;) 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; could_connect = true;
break; break;
} }
// Break conditions
if(client.accessDenied()) if(client.accessDenied())
{
break; break;
} if(time_counter >= timeout)
// Wait for 10 seconds
if(time_counter >= 10.0)
{
break; break;
}
// Display status
std::wostringstream ss; std::wostringstream ss;
ss<<L"Connecting to server... (timeout in "; 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)"; ss<<L" seconds)";
draw_load_screen(ss.str(), driver, font); 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 // Delay a bit
sleep_ms(100); sleep_ms(1000*frametime);
time_counter += 0.1; time_counter += frametime;
} }
} }
catch(con::PeerNotFoundException &e) catch(con::PeerNotFoundException &e)
{} {}
/*
Handle failure to connect
*/
if(could_connect == false) if(could_connect == false)
{ {
if(client.accessDenied()) if(client.accessDenied())
@ -767,6 +761,56 @@ void the_game(
return; 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 Create skybox
*/ */

@ -2139,15 +2139,15 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
Send some initialization data Send some initialization data
*/ */
// Send textures
SendTextures(peer_id);
// Send tool definitions // Send tool definitions
SendToolDef(m_con, peer_id, m_toolmgr); SendToolDef(m_con, peer_id, m_toolmgr);
// Send node definitions // Send node definitions
SendNodeDef(m_con, peer_id, m_nodemgr); SendNodeDef(m_con, peer_id, m_nodemgr);
// Send textures
SendTextures(peer_id);
// Send player info to all players // Send player info to all players
SendPlayerInfos(); SendPlayerInfos();
@ -4160,7 +4160,13 @@ void Server::SendTextures(u16 peer_id)
/* Read textures */ /* 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); core::list<ModSpec> mods = getMods(m_modspaths);
for(core::list<ModSpec>::Iterator i = mods.begin(); for(core::list<ModSpec>::Iterator i = mods.begin();
i != mods.end(); i++){ i != mods.end(); i++){
@ -4186,6 +4192,7 @@ void Server::SendTextures(u16 peer_id)
fis.read(buf, 1024); fis.read(buf, 1024);
std::streamsize len = fis.gcount(); std::streamsize len = fis.gcount();
tmp_os.write(buf, len); tmp_os.write(buf, len);
texture_size_bunch_total += len;
if(fis.eof()) if(fis.eof())
break; break;
if(!fis.good()){ if(!fis.good()){
@ -4201,15 +4208,27 @@ void Server::SendTextures(u16 peer_id)
errorstream<<"Server::SendTextures(): Loaded \"" errorstream<<"Server::SendTextures(): Loaded \""
<<tname<<"\""<<std::endl; <<tname<<"\""<<std::endl;
// Put in list // 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 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 { for each texture {
u16 length of name u16 length of name
string name string name
@ -4220,21 +4239,26 @@ void Server::SendTextures(u16 peer_id)
std::ostringstream os(std::ios_base::binary); std::ostringstream os(std::ios_base::binary);
writeU16(os, TOCLIENT_TEXTURES); writeU16(os, TOCLIENT_TEXTURES);
writeU32(os, textures.size()); writeU16(os, num_bunches);
writeU16(os, i);
writeU32(os, texture_bunches[i].size());
for(core::list<SendableTexture>::Iterator i = textures.begin(); for(core::list<SendableTexture>::Iterator
i != textures.end(); i++){ j = texture_bunches[i].begin();
os<<serializeString(i->name); j != texture_bunches[i].end(); j++){
os<<serializeLongString(i->data); os<<serializeString(j->name);
os<<serializeLongString(j->data);
} }
// Make data buffer // Make data buffer
std::string s = os.str(); std::string s = os.str();
infostream<<"Server::SendTextures(): number of textures: " infostream<<"Server::SendTextures(): number of textures in bunch["
<<textures.size()<<", data size: "<<s.size()<<std::endl; <<i<<"]: "<<texture_bunches[i].size()
<<", size: "<<s.size()<<std::endl;
SharedBuffer<u8> data((u8*)s.c_str(), s.size()); SharedBuffer<u8> data((u8*)s.c_str(), s.size());
// Send as reliable // Send as reliable
m_con.Send(peer_id, 0, data, true); m_con.Send(peer_id, 0, data, true);
}
} }
/* /*