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

View File

@ -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,15 +1571,17 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
rfile->drop(); rfile->drop();
} }
// Rebuild inherited images and recreate textures if(m_nodedef_received && m_textures_received){
m_tsrc->rebuildImagesAndTextures(); // Rebuild inherited images and recreate textures
m_tsrc->rebuildImagesAndTextures();
// 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);
@ -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);
// Update texture atlas if(m_textures_received){
if(g_settings->getBool("enable_texture_atlas")) // Update texture atlas
m_tsrc->buildMainAtlas(this); if(g_settings->getBool("enable_texture_atlas"))
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);

View File

@ -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;
}; };

View File

@ -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

View File

@ -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
*/ */

View File

@ -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,40 +4208,57 @@ 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();
u16 command for(u32 i=0; i<num_bunches; i++)
u32 number of textures {
for each texture { /*
u16 length of name u16 command
string name u16 total number of texture bunches
u32 length of data u16 index of this bunch
data 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);
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); // Make data buffer
writeU32(os, textures.size()); std::string s = os.str();
infostream<<"Server::SendTextures(): number of textures in bunch["
for(core::list<SendableTexture>::Iterator i = textures.begin(); <<i<<"]: "<<texture_bunches[i].size()
i != textures.end(); i++){ <<", size: "<<s.size()<<std::endl;
os<<serializeString(i->name); SharedBuffer<u8> data((u8*)s.c_str(), s.size());
os<<serializeLongString(i->data); // 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);
} }
/* /*