diff --git a/src/client.cpp b/src/client.cpp index e47bce1..abae198 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -42,6 +42,8 @@ #include "util/string.h" #include "hex.h" +#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")" + static std::string getMediaCacheDir() { return porting::path_user + DIR_DELIM + "cache" + DIR_DELIM + "media"; @@ -62,8 +64,7 @@ struct MediaRequest QueuedMeshUpdate::QueuedMeshUpdate(): p(-1337,-1337,-1337), - data(NULL), - ack_block_to_server(false) + data(NULL) { } @@ -98,7 +99,7 @@ struct MediaRequest /* peer_id=0 adds with nobody to send to */ -void MeshUpdateQueue::addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_server, bool urgent) +void MeshUpdateQueue::addBlock(v3s16 p, MeshMakeData *data, bool urgent) { DSTACK(__FUNCTION_NAME); @@ -123,8 +124,6 @@ void MeshUpdateQueue::addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_se if(q->data) delete q->data; q->data = data; - if(ack_block_to_server) - q->ack_block_to_server = true; return; } } @@ -135,7 +134,6 @@ void MeshUpdateQueue::addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_se QueuedMeshUpdate *q = new QueuedMeshUpdate; q->p = p; q->data = data; - q->ack_block_to_server = ack_block_to_server; m_queue.push_back(q); } @@ -184,7 +182,8 @@ void * MeshUpdateThread::Thread() sleep_ms(3); continue; }*/ - + + bool is_urgent = (m_queue_in.urgent_size() > 0); QueuedMeshUpdate *q = m_queue_in.pop(); if(q == NULL) { @@ -204,13 +203,14 @@ void * MeshUpdateThread::Thread() MeshUpdateResult r; r.p = q->p; r.mesh = mesh_new; - r.ack_block_to_server = q->ack_block_to_server; /*infostream<<"MeshUpdateThread: Processed " <<"("<p.X<<","<p.Y<<","<p.Z<<")" <getBool("enable_texture_atlas")) @@ -344,6 +346,8 @@ void Client::step(float dtime) if(dtime > 2.0) dtime = 2.0; + m_dtime_avg = m_dtime_avg * 0.98 + dtime * 0.2; + if(m_ignore_damage_timer > dtime) m_ignore_damage_timer -= dtime; else @@ -363,13 +367,6 @@ void Client::step(float dtime) ReceiveAll(); } - { - //TimeTaker timer("m_con_mutex + m_con.RunTimeouts()", m_device); - // 0ms - //JMutexAutoLock lock(m_con_mutex); //bulk comment-out - m_con.RunTimeouts(dtime); - } - /* Packet counter */ @@ -395,7 +392,7 @@ void Client::step(float dtime) Delete unused sectors NOTE: This jams the game for a while because deleting sectors - clear caches + clear caches */ float &counter = m_delete_unused_sectors_timer; @@ -542,50 +539,6 @@ void Client::step(float dtime) /*if(deleted_blocks.size() > 0) infostream<<"Client: Unloaded "<::Iterator i = deleted_blocks.begin(); - core::list sendlist; - for(;;) - { - if(sendlist.size() == 255 || i == deleted_blocks.end()) - { - if(sendlist.size() == 0) - break; - /* - [0] u16 command - [2] u8 count - [3] v3s16 pos_0 - [3+6] v3s16 pos_1 - ... - */ - u32 replysize = 2+1+6*sendlist.size(); - SharedBuffer reply(replysize); - writeU16(&reply[0], TOSERVER_DELETEDBLOCKS); - reply[2] = sendlist.size(); - u32 k = 0; - for(core::list::Iterator - j = sendlist.begin(); - j != sendlist.end(); j++) - { - writeV3S16(&reply[2+1+6*k], *j); - k++; - } - m_con.Send(PEER_ID_SERVER, 1, reply, true); - - if(i == deleted_blocks.end()) - break; - - sendlist.clear(); - } - - sendlist.push_back(*i); - i++; - } } /* @@ -650,6 +603,21 @@ void Client::step(float dtime) } /* + Request blocks + */ + { + float interval = 0.5; + float &counter = m_request_blocks_timer; + counter += dtime; + if (counter >= interval && + m_con.GetPeerOutgoingQueueSizeSeconds(PEER_ID_SERVER) < interval) + { + counter = 0.0; + sendRequestForBlocks(interval); + } + } + + /* Send player position to server */ { @@ -676,10 +644,15 @@ void Client::step(float dtime) < 0) + while(m_mesh_update_thread.m_queue_out_urgent.size() > 0 || + m_mesh_update_thread.m_queue_out.size() > 0) { num_processed_meshes++; - MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_front(); + MeshUpdateResult r; + if(m_mesh_update_thread.m_queue_out_urgent.size() > 0) + r = m_mesh_update_thread.m_queue_out_urgent.pop_front(); + else + r = m_mesh_update_thread.m_queue_out.pop_front(); MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p); if(block) { @@ -696,34 +669,32 @@ void Client::step(float dtime) // Replace with the new mesh block->mesh = r.mesh; } - if(r.ack_block_to_server) - { - /*infostream<<"Client: ACK block ("< reply(replysize); - writeU16(&reply[0], TOSERVER_GOTBLOCKS); - reply[2] = 1; - writeV3S16(&reply[3], r.p); - // Send as reliable - m_con.Send(PEER_ID_SERVER, 1, reply, true); - } } if(num_processed_meshes > 0) g_profiler->graphAdd("num_processed_meshes", num_processed_meshes); } /* + Handle pre-queued mesh updates + */ + { + int num_processed = 0; + float sqrt_norm = 0.030; + int max_norm = 100; + int max_per_frame = sqrt(m_dtime_avg/sqrt_norm) * sqrt_norm * max_norm + 1; + while(m_update_mesh_task_pre_queue.size() > 0 && + num_processed < max_per_frame) + { + num_processed++; + PreQueuedMeshUpdate pq = m_update_mesh_task_pre_queue.pop_front(); + if(pq.with_edge) + addUpdateMeshTaskWithEdge(pq.p, pq.urgent); + else + addUpdateMeshTask(pq.p, pq.urgent); + } + } + + /* If the server didn't update the inventory in a while, revert the local inventory (so the player notices the lag problem and knows something is wrong). @@ -1069,78 +1040,66 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id) else if(command == TOCLIENT_BLOCKDATA) { // Ignore too small packet - if(datasize < 8) + if(datasize < 2) return; - v3s16 p; - p.X = readS16(&data[2]); - p.Y = readS16(&data[4]); - p.Z = readS16(&data[6]); - - /*infostream<<"Client: Thread: BLOCKDATA for (" - <getPos() == p2d); + block = sector->getBlockNoCreateNoEx(p.Y); + if (block && block->getChangeCounter() >= change_counter) { + // We already have a newer version of this block + // Don't need to bother deserializing + /*infostream<<"Client: TOCLIENT_BLOCKDATA: Already have " + <getChangeCounter() + <<", server sent rev. "<getBlockNoCreateNoEx(p.Y); + if(block) { - /* - Update an existing block - */ + // Update an existing block //infostream<<"Updating"<deSerialize(istr, ser_version, false); + block->deSerialize(is, ser_version, false); + block->setChangeCounter(change_counter); } else { - /* - Create a new block - */ + // Create a new block //infostream<<"Creating new"<deSerialize(istr, ser_version, false); + block->deSerialize(is, ser_version, false); + block->setChangeCounter(change_counter); sector->insertBlock(block); } -#if 0 - /* - Acknowledge block - */ - /* - [0] u16 command - [2] u8 count - [3] v3s16 pos_0 - [3+6] v3s16 pos_1 - ... - */ - u32 replysize = 2+1+6; - SharedBuffer reply(replysize); - writeU16(&reply[0], TOSERVER_GOTBLOCKS); - reply[2] = 1; - writeV3S16(&reply[3], p); - // Send as reliable - m_con.Send(PEER_ID_SERVER, 1, reply, true); -#endif - - /* - Add it to mesh update queue and set it to be acknowledged after update. - */ - //infostream<<"Adding mesh update task for received block"< adjacent_blocks; + Player* player = m_env.getLocalPlayer(); + v3s16 pbpos = getNodeBlockPos(floatToInt(player->getPosition(), BS)); + for(int x = pbpos.X-1; x <= pbpos.X+1; ++x) { + for(int y = pbpos.Y-1; y <= pbpos.Y+1; ++y) { + for(int z = pbpos.Z-1; z <= pbpos.Z+1; ++z) { + adjacent_blocks.set(v3s16(x,y,z), true); + } + } + } + + // Request blocks viewable by the player at last draw + core::map& viewable_blocks = + const_cast&>( + m_env.getClientMap().nextBlocksToRequest() + ); + + core::map::Iterator i; + + // Find the coordinate range and change counters of requested blocks + bool first_block = true; + v3s16 req_min_pos, req_max_pos; + core::map request_blocks; // Value is dummy + for (int m = 0; m < 2; ++m) + { + // If mesh generation is fully booked, skip other than adjacent blocks + if(m == 1 && m_update_mesh_task_pre_queue.size() > 0) + continue; + + core::map* map = + (m == 0 ? &adjacent_blocks : &viewable_blocks); + + for (core::map::Iterator i = map->getIterator(); + i.atEnd() == false; i++) + { + v3s16 bpos = i->getKey(); + if (first_block) { + req_min_pos = req_max_pos = bpos; + first_block = false; + } else { + if (bpos.X < req_min_pos.X) req_min_pos.X = bpos.X; + if (bpos.Y < req_min_pos.Y) req_min_pos.Y = bpos.Y; + if (bpos.Z < req_min_pos.Z) req_min_pos.Z = bpos.Z; + if (bpos.X > req_max_pos.X) req_max_pos.X = bpos.X; + if (bpos.Y > req_max_pos.Y) req_max_pos.Y = bpos.Y; + if (bpos.Z > req_max_pos.Z) req_max_pos.Z = bpos.Z; + } + + request_blocks.set(bpos, true); + } + } + + // Write the request + std::ostringstream os(std::ios_base::binary); + writeU16(os, TOSERVER_REQUEST_BLOCKS); + writeU16(os, timeout * 1000); + writeV3S16(os, req_min_pos); + writeV3S16(os, req_max_pos); + for (int x = req_min_pos.X; x <= req_max_pos.X; ++x) { + for (int y = req_min_pos.Y; y <= req_max_pos.Y; ++y) { + for (int z = req_min_pos.Z; z <= req_max_pos.Z; ++z) { + v3s16 bpos = v3s16(x,y,z); + u32 cc = BLOCK_CHANGECOUNTER_UNDEFINED; // Don't request + if(request_blocks.find(bpos)){ + MapBlock* b = m_env.getMap().getBlockNoCreateNoEx(bpos); + cc = 0; // Request any version unless block exists + if (b != NULL) cc = b->getChangeCounter(); + } + //infostream<<"Client: version of "< data((u8*)s.c_str(), s.size()); + Send(0, data, true); // Send as reliable +} + void Client::removeNode(v3s16 p) { core::map modified_blocks; @@ -2018,7 +2066,7 @@ void Client::removeNode(v3s16 p) } // add urgent task to update the modified node - addUpdateMeshTaskForNode(p, false, true); + addUpdateMeshTaskForNode(p, true); for(core::map::Iterator i = modified_blocks.getIterator(); @@ -2230,12 +2278,12 @@ void Client::setCrack(int level, v3s16 pos) if(old_crack_level >= 0 && (level < 0 || pos != old_crack_pos)) { // remove old crack - addUpdateMeshTaskForNode(old_crack_pos, false, true); + addUpdateMeshTaskForNode(old_crack_pos, true); } if(level >= 0 && (old_crack_level < 0 || pos != old_crack_pos)) { // add new crack - addUpdateMeshTaskForNode(pos, false, true); + addUpdateMeshTaskForNode(pos, true); } } @@ -2279,11 +2327,10 @@ void Client::typeChatMessage(const std::wstring &message) } } -void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent) +void Client::addUpdateMeshTask(v3s16 p, bool urgent) { /*infostream<<"Client::addUpdateMeshTask(): " <<"("<fill(b); + { + ScopeProfiler sp(g_profiler, "Client: data->fill", SPT_AVG); + data->fill(b); + } data->setCrack(m_crack_level, m_crack_pos); data->setSmoothLighting(g_settings->getBool("smooth_lighting")); } @@ -2310,14 +2360,14 @@ void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent) //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10); // Add task to queue - m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server, urgent); + m_mesh_update_thread.m_queue_in.addBlock(p, data, urgent); /*infostream<<"Mesh update input queue size is " < m_queue; @@ -100,12 +117,10 @@ struct MeshUpdateResult { v3s16 p; MapBlockMesh *mesh; - bool ack_block_to_server; MeshUpdateResult(): p(-1338,-1338,-1338), - mesh(NULL), - ack_block_to_server(false) + mesh(NULL) { } }; @@ -123,6 +138,7 @@ class MeshUpdateThread : public SimpleThread MeshUpdateQueue m_queue_in; + MutexedQueue m_queue_out_urgent; MutexedQueue m_queue_out; IGameDef *m_gamedef; @@ -272,10 +288,10 @@ class Client : public con::PeerHandler, public InventoryManager, public IGameDef u64 getMapSeed(){ return m_map_seed; } - void addUpdateMeshTask(v3s16 blockpos, bool ack_to_server=false, bool urgent=false); + void addUpdateMeshTask(v3s16 blockpos, bool urgent=false); // Including blocks at appropriate edges - void addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server=false, bool urgent=false); - void addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server=false, bool urgent=false); + void addUpdateMeshTaskWithEdge(v3s16 blockpos, bool urgent=false); + void addUpdateMeshTaskForNode(v3s16 nodepos, bool urgent=false); // Get event from queue. CE_NONE is returned if queue is empty. ClientEvent getClientEvent(); @@ -334,8 +350,11 @@ class Client : public con::PeerHandler, public InventoryManager, public IGameDef float m_avg_rtt_timer; float m_playerpos_send_timer; float m_ignore_damage_timer; // Used after server moves player + float m_request_blocks_timer; IntervalLimiter m_map_timer_and_unload_interval; + void sendRequestForBlocks(float timeout); + IWritableTextureSource *m_tsrc; IWritableItemDefManager *m_itemdef; IWritableNodeDefManager *m_nodedef; @@ -397,6 +416,11 @@ class Client : public con::PeerHandler, public InventoryManager, public IGameDef // Detached inventories // key = name std::map m_detached_inventories; + + // Pre-queue of mesh updates for balancing load between frames + Queue m_update_mesh_task_pre_queue; + + float m_dtime_avg; }; #endif // !CLIENT_HEADER diff --git a/src/clientmap.cpp b/src/clientmap.cpp index 64d5656..3cc6b67 100644 --- a/src/clientmap.cpp +++ b/src/clientmap.cpp @@ -47,7 +47,8 @@ m_control(control), m_camera_position(0,0,0), m_camera_direction(0,0,1), - m_camera_fov(M_PI) + m_camera_fov(M_PI), + m_next_to_request_list_needs_culling(false) { m_camera_mutex.Init(); assert(m_camera_mutex.IsInitialized()); @@ -185,7 +186,7 @@ void ClientMap::updateDrawList(video::IVideoDriver* driver) camera_fov *= 1.2; v3s16 cam_pos_nodes = floatToInt(camera_position, BS); - v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1); + v3s16 box_nodes_d(m_control.wanted_range); v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d; v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d; // Take a fair amount as we will be dropping more out later @@ -215,11 +216,21 @@ void ClientMap::updateDrawList(video::IVideoDriver* driver) // Blocks from which stuff was actually drawn //u32 blocks_without_stuff = 0; - for(core::map::Iterator - si = m_sectors.getIterator(); - si.atEnd() == false; si++) + std::vector drawable_sectors; + drawable_sectors.reserve( + (p_blocks_max.X-p_blocks_min.X+1)* + (p_blocks_max.Z-p_blocks_min.Z+1) + ); + for(int sx = p_blocks_min.X; sx <= p_blocks_max.X; ++sx){ + for(int sy = p_blocks_min.Z; sy <= p_blocks_max.Z; ++sy){ + drawable_sectors.push_back(emergeSector(v2s16(sx,sy))); + } + } + + for(std::vector::iterator si = drawable_sectors.begin(); + si != drawable_sectors.end(); ++si) { - MapSector *sector = si.getNode()->getValue(); + MapSector *sector = *si; v2s16 sp = sector->getPos(); if(m_control.range_all == false) @@ -240,10 +251,9 @@ void ClientMap::updateDrawList(video::IVideoDriver* driver) u32 sector_blocks_drawn = 0; - core::list< MapBlock * >::Iterator i; - for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++) + for(int by = p_blocks_min.Y; by <= p_blocks_max.Y; ++by) { - MapBlock *block = *i; + v3s16 bp(sp.X, by, sp.Y); /* Compare block position to camera position, skip @@ -255,7 +265,7 @@ void ClientMap::updateDrawList(video::IVideoDriver* driver) range = m_control.wanted_range * BS; float d = 0.0; - if(isBlockInSight(block->getPos(), camera_position, + if(isBlockInSight(bp, camera_position, camera_direction, camera_fov, range, &d) == false) { @@ -270,18 +280,6 @@ void ClientMap::updateDrawList(video::IVideoDriver* driver) blocks_in_range++; /* - Ignore if mesh doesn't exist - */ - { - //JMutexAutoLock lock(block->mesh_mutex); - - if(block->mesh == NULL){ - blocks_in_range_without_mesh++; - continue; - } - } - - /* Occlusion culling */ @@ -295,7 +293,7 @@ void ClientMap::updateDrawList(video::IVideoDriver* driver) occlusion_culling_enabled = false; } - v3s16 cpn = block->getPos() * MAP_BLOCKSIZE; + v3s16 cpn = bp * MAP_BLOCKSIZE; cpn += v3s16(MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2); float step = BS*1; float stepfac = 1.1; @@ -329,6 +327,33 @@ void ClientMap::updateDrawList(video::IVideoDriver* driver) blocks_occlusion_culled++; continue; } + + /* + Note for later that we were trying to display this block. + */ + MapBlock* block = sector->getBlockNoCreateNoEx(bp.Y); + if(block == NULL || block->isDummy()) { + core::map::Iterator i = + m_last_blocks_needed.find(bp); + if (!(i.getNode() && i->getValue() == true)) { + // Don't set it to false if it's already true. + m_last_blocks_needed.set(bp, false); + } + } else { + m_last_blocks_needed.set(bp, true); + } + + /* + Ignore if block or mesh doesn't exist + */ + { + //JMutexAutoLock lock(block->mesh_mutex); + + if(block == NULL || block->mesh == NULL){ + blocks_in_range_without_mesh++; + continue; + } + } // This block is in range. Reset usage timer. block->resetUsageTimer(); @@ -342,7 +367,7 @@ void ClientMap::updateDrawList(video::IVideoDriver* driver) // Add to set block->refGrab(); - m_drawlist[block->getPos()] = block; + m_drawlist[bp] = block; sector_blocks_drawn++; blocks_drawn++; @@ -871,9 +896,96 @@ void ClientMap::renderPostFx() } } +void ClientMap::renderBlockBoundaries() +{ + video::IVideoDriver* driver = SceneManager->getVideoDriver(); + video::SMaterial mat; + mat.Lighting = false; + mat.ZWriteEnable = false; + + core::aabbox3d bound; + core::map& blocks = + const_cast&>(nextBlocksToRequest()); + core::map::Iterator i; + const v3f inset(BS/2); + const v3f blocksize(MAP_BLOCKSIZE); + + for (int pass = 0; pass < 2; ++pass) { + video::SColor color_offset(0, 0, 0, 0); + if (pass == 0) { + mat.Thickness = 1; + mat.ZBuffer = video::ECFN_ALWAYS; + color_offset.setGreen(64); + } else { + mat.Thickness = 3; + mat.ZBuffer = video::ECFN_LESSEQUAL; + } + driver->setMaterial(mat); + + for (i=blocks.getIterator(); !i.atEnd(); i++) { + video::SColor color(255, 0, 0, 0); + if (i->getValue() == true) { + color.setBlue(255); + } else { + color.setRed(255); + color.setGreen(128); + } + + v3s16 bpos = i->getKey(); + bound.MinEdge = intToFloat(i->getKey(), BS)*blocksize + + inset + - v3f(BS)*0.5; + bound.MaxEdge = bound.MinEdge + + blocksize*BS + - inset + - inset; + color = color + color_offset; + + driver->draw3DBox(bound, color); + } + } +} + void ClientMap::PrintInfo(std::ostream &out) { out<<"ClientMap: "; } +const core::map& ClientMap::nextBlocksToRequest() +{ + if (m_next_to_request_list_needs_culling) { + // Don't try to request blocks that have no data and whose neighbors + // also all lack data. We can't tell if these blocks would + // be occlusion-culled until we have nearby geometry. + core::map::Iterator i; + for(i=m_last_blocks_needed.getIterator(); !i.atEnd(); i++) { + if (i->getValue() == true) continue; // Don't cull blocks with data + + bool cull = true; + v3s16 p = i->getKey(); + v3s16 p0 = p - v3s16(1,1,1); + v3s16 p1 = p + v3s16(1,1,1); + for(int x = p0.X; x <= p1.X; ++x) { + for(int y = p0.Y; y <= p1.Y; ++y) { + for(int z = p0.Z; z <= p1.Z; ++z) { + core::map::Iterator i = + m_last_blocks_needed.find(v3s16(x,y,z)); + if (i.getNode() && i->getValue() == true) { + cull = false; break; + } + } + if (!cull) break; + } + if (!cull) break; + } + if (cull) { + m_last_blocks_needed.remove(p); + } + } + + m_next_to_request_list_needs_culling = false; + } + + return m_last_blocks_needed; +} diff --git a/src/clientmap.h b/src/clientmap.h index f8a6963..a061b96 100644 --- a/src/clientmap.h +++ b/src/clientmap.h @@ -122,6 +122,9 @@ class ClientMap : public Map, public scene::ISceneNode void renderPostFx(); + // For debugging the status and position of MapBlocks + void renderBlockBoundaries(); + // For debug printing virtual void PrintInfo(std::ostream &out); @@ -130,7 +133,10 @@ class ClientMap : public Map, public scene::ISceneNode { return (m_last_drawn_sectors.find(p) != NULL); } - + + // Find blocks that were drawn, or that are just outside the drawn area + const core::map& nextBlocksToRequest(); + private: Client *m_client; @@ -146,7 +152,10 @@ class ClientMap : public Map, public scene::ISceneNode core::map m_drawlist; core::map m_last_drawn_sectors; + + // True: drawn; False: not drawn due to lack of mesh data + core::map m_last_blocks_needed; + bool m_next_to_request_list_needs_culling; }; #endif - diff --git a/src/clientserver.h b/src/clientserver.h index 82e485d..ef8cc7f 100644 --- a/src/clientserver.h +++ b/src/clientserver.h @@ -49,7 +49,7 @@ Many things PROTOCOL_VERSION 9: ContentFeatures and NodeDefManager use a different serialization - format; better for future version cross-compatibility + format; better for future version cross-compatibility Many things PROTOCOL_VERSION 10: TOCLIENT_PRIVILEGES @@ -67,9 +67,12 @@ TOCLIENT_DETACHED_INVENTORY PROTOCOL_VERSION 13: InventoryList field "Width" (deserialization fails with old versions) + PROTOCOL_VERSION 14: + TOSERVER_REQUEST_BLOCKS + Versioned block data */ -#define PROTOCOL_VERSION 13 +#define PROTOCOL_VERSION 14 #define PROTOCOL_ID 0x4f457403 @@ -95,6 +98,13 @@ enum ToClientCommand */ TOCLIENT_BLOCKDATA = 0x20, //TODO: Multiple blocks + /* + [0] u16 command + [2] v3s16 position + [8] u32 change counter + Remainder is serialized MapBlock + */ + TOCLIENT_ADDNODE = 0x21, TOCLIENT_REMOVENODE = 0x22, @@ -355,7 +365,17 @@ enum ToServerCommand [0] u16 TOSERVER_INIT2 */ - TOSERVER_GETBLOCK=0x20, // Obsolete + TOSERVER_REQUEST_BLOCKS=0x20, + /* + u16 command + u16 timeout_ms + v3s16 pos_0 + v3s16 pos_1 + for each block { + u32 client_changenum + } + */ + TOSERVER_ADDNODE = 0x21, // Obsolete TOSERVER_REMOVENODE = 0x22, // Obsolete @@ -368,7 +388,7 @@ enum ToServerCommand [2+12+12+4] s32 yaw*100 */ - TOSERVER_GOTBLOCKS = 0x24, + TOSERVER_GOTBLOCKS = 0x24, // Obsolete /* [0] u16 command [2] u8 count @@ -377,7 +397,7 @@ enum ToServerCommand ... */ - TOSERVER_DELETEDBLOCKS = 0x25, + TOSERVER_DELETEDBLOCKS = 0x25, // Obsolete /* [0] u16 command [2] u8 count diff --git a/src/connection.cpp b/src/connection.cpp index 4f5d095..f5547b2 100644 --- a/src/connection.cpp +++ b/src/connection.cpp @@ -466,7 +466,8 @@ void IncomingSplitBuffer::removeUnreliableTimedOuts(float dtime, float timeout) m_sendtime_accu(0), m_max_packets_per_second(10), m_num_sent(0), - m_max_num_sent(0) + m_max_num_sent(0), + m_num_queued(0) { } Peer::~Peer() @@ -479,13 +480,13 @@ void Peer::reportRTT(float rtt) if(rtt < 0.01){ if(m_max_packets_per_second < 400) m_max_packets_per_second += 10; - } else if(rtt < 0.2){ + } else if(rtt < 0.3){ if(m_max_packets_per_second < 100) m_max_packets_per_second += 2; } else { m_max_packets_per_second *= 0.8; - if(m_max_packets_per_second < 10) - m_max_packets_per_second = 10; + if(m_max_packets_per_second < 20) + m_max_packets_per_second = 20; } } @@ -515,16 +516,15 @@ void Peer::reportRTT(float rtt) } /* - Connection + ConnectionThread */ -Connection::Connection(u32 protocol_id, u32 max_packet_size, float timeout): +ConnectionThread::ConnectionThread(u32 protocol_id, u32 max_packet_size, float timeout): m_protocol_id(protocol_id), m_max_packet_size(max_packet_size), m_timeout(timeout), m_peer_id(0), - m_bc_peerhandler(NULL), - m_bc_receive_timeout(0), + m_is_listening(false), m_indentation(0) { m_socket.setTimeoutMs(5); @@ -532,23 +532,7 @@ void Peer::reportRTT(float rtt) Start(); } -Connection::Connection(u32 protocol_id, u32 max_packet_size, float timeout, - PeerHandler *peerhandler): - m_protocol_id(protocol_id), - m_max_packet_size(max_packet_size), - m_timeout(timeout), - m_peer_id(0), - m_bc_peerhandler(peerhandler), - m_bc_receive_timeout(0), - m_indentation(0) -{ - m_socket.setTimeoutMs(5); - - Start(); -} - - -Connection::~Connection() +ConnectionThread::~ConnectionThread() { stop(); // Delete peers @@ -563,12 +547,12 @@ void Peer::reportRTT(float rtt) /* Internal stuff */ -void * Connection::Thread() +void * ConnectionThread::Thread() { ThreadStarted(); - log_register_thread("Connection"); + log_register_thread("ConnectionThread"); - dout_con<<"Connection thread started"<::Iterator j = m_peers.getIterator(); @@ -653,6 +637,7 @@ void Connection::send(float dtime) peer->m_num_sent = 0; peer->m_max_num_sent = peer->m_sendtime_accu * peer->m_max_packets_per_second; + peer->m_num_queued = 0; } Queue postponed_packets; while(m_outgoing_queue.size() != 0){ @@ -668,6 +653,7 @@ void Connection::send(float dtime) peer->m_num_sent++; } else { postponed_packets.push_back(packet); + peer->m_num_queued++; } } while(postponed_packets.size() != 0){ @@ -686,7 +672,7 @@ void Connection::send(float dtime) } // Receive packets from the network and buffers and create ConnectionEvents -void Connection::receive() +void ConnectionThread::receive() { u32 datasize = m_max_packet_size * 2; // Double it just to be safe // TODO: We can not know how many layers of header there are. @@ -694,6 +680,8 @@ void Connection::receive() u32 packet_maxsize = datasize + BASE_HEADER_SIZE; SharedBuffer packetdata(packet_maxsize); + /* UDP */ + bool single_wait_done = false; for(;;) @@ -887,9 +875,132 @@ void Connection::receive() catch(ProcessedSilentlyException &e){ } } // for + + /* TCP */ + + while(m_is_listening && m_bound_tcp.WaitData(0)) + { + TCPSocket *socket = m_bound_tcp.Accept(); + if(socket == NULL) + continue; + m_unknown_tcps.push_back(socket); + + PrintInfo(derr_con); + dout_con<<"receive(): Accepted a TCP connection"< >::Iterator + i = m_unknown_tcps.begin(); + i != m_unknown_tcps.end();) + { + SharedPtr socket = *i; + bool remove = false; + u16 peer_id = 0; + while(socket->WaitData(0)){ + u8 buf[10]; + s32 received_size = socket->Receive(buf, 4); + // protocol_id + peer_id is a regular connection + if(received_size == 4 && readU32(&buf[0]) == m_protocol_id){ + s32 size2 = socket->Receive(buf, 2); + if(size2 == 2){ + peer_id = readU16(&buf[0]); + break; + } + } + // Reply to HTTP because it's fun + if(received_size == 4 && std::string((char*)buf, 4) == "GET "){ + char reply[] = "HTTP/1.1 200 OK\r\n" + "Content-Type: text/plain; charset=utf-8\r\n" + "\r\n" + "Minetest."; + socket->Send(reply, sizeof(reply)-1); + remove = true; + PrintInfo(derr_con); + dout_con<<"receive(): TCP client asked HTTP"< >::Iterator next = i+1; + m_unknown_tcps.erase(i); + i = next; + } else{ + ++i; + } + } + + for(std::map >::iterator + i = m_peer_tcps.begin(); i != m_peer_tcps.end(); ++i) + { + u16 peer_id = i->first; + const SharedPtr &socket = i->second; + if(m_peer_streambufs.find(peer_id) == m_peer_streambufs.end()) + m_peer_streambufs[peer_id] = StreamBuffer(); + StreamBuffer &sbuf = m_peer_streambufs[peer_id]; + + bool got_something = false; + while(socket->WaitData(0)){ + u8 ba[10000]; + s32 received_size = socket->Receive(ba, 10000); + if(received_size <= 0) + break; + dout_con< b(ba, received_size); + sbuf.push(b); + got_something = true; + } + if(got_something) for(;;){ + SharedBuffer init_buf = sbuf.peek(4); + //dstream<<"a: "< data = sbuf.pop(size); + dout_con<timeout_counter = 0.0; + peer->ping_timer = 0.0; + } + } + } } -void Connection::runTimeouts(float dtime) +void ConnectionThread::runTimeouts(float dtime) { core::list timeouted_peers; core::map::Iterator j; @@ -986,7 +1097,7 @@ void Connection::runTimeouts(float dtime) SharedBuffer data(2); writeU8(&data[0], TYPE_CONTROL); writeU8(&data[1], CONTROLTYPE_PING); - rawSendAsPacket(peer->id, 0, data, true); + rawSendAsPacket(peer->id, 0, data, false); peer->ping_timer = 0.0; } @@ -1005,12 +1116,14 @@ void Connection::runTimeouts(float dtime) } } -void Connection::serve(u16 port) +void ConnectionThread::serve(u16 port) { dout_con< data(0); - Send(PEER_ID_SERVER, 0, data, true); + ConnectionCommand c; + c.send(PEER_ID_SERVER, 0, data, true); + putCommand(c); + + // Connect to server using TCP (initialization will be sent later) + try{ + SharedPtr tcp = new TCPSocket; + tcp->Connect(address); + m_initial_peer_tcps[PEER_ID_SERVER] = tcp; + } catch(SocketException &e){ + derr_con< data, bool reliable) +void ConnectionThread::sendToAll(u8 channelnum, SharedBuffer data, bool reliable) { core::map::Iterator j; j = m_peers.getIterator(); @@ -1076,7 +1201,7 @@ void Connection::sendToAll(u8 channelnum, SharedBuffer data, bool reliable) } } -void Connection::send(u16 peer_id, u8 channelnum, +void ConnectionThread::send(u16 peer_id, u8 channelnum, SharedBuffer data, bool reliable) { dout_con< b(data.getSize()+4); + writeU32(&b[0], data.getSize()); + memcpy(&b[4], &data[0], data.getSize()); + socket->Send(&b[0], b.getSize()); + return; + } + } catch(SendFailedException &e){ + // Use UDP instead + derr_con<channels[channelnum]); u32 chunksize_max = m_max_packet_size - BASE_HEADER_SIZE; @@ -1106,14 +1251,14 @@ void Connection::send(u16 peer_id, u8 channelnum, } } -void Connection::sendAsPacket(u16 peer_id, u8 channelnum, +void ConnectionThread::sendAsPacket(u16 peer_id, u8 channelnum, SharedBuffer data, bool reliable) { OutgoingPacket packet(peer_id, channelnum, data, reliable); m_outgoing_queue.push_back(packet); } -void Connection::rawSendAsPacket(u16 peer_id, u8 channelnum, +void ConnectionThread::rawSendAsPacket(u16 peer_id, u8 channelnum, SharedBuffer data, bool reliable) { Peer *peer = getPeerNoEx(peer_id); @@ -1159,17 +1304,17 @@ void Connection::rawSendAsPacket(u16 peer_id, u8 channelnum, } } -void Connection::rawSend(const BufferedPacket &packet) +void ConnectionThread::rawSend(const BufferedPacket &packet) { try{ m_socket.Send(packet.address, *packet.data, packet.data.getSize()); } catch(SendFailedException &e){ - derr_con<<"Connection::rawSend(): SendFailedException: " + derr_con<<"ConnectionThread::rawSend(): SendFailedException: " <::Node *node = m_peers.find(peer_id); @@ -1183,7 +1328,7 @@ Peer* Connection::getPeer(u16 peer_id) return node->getValue(); } -Peer* Connection::getPeerNoEx(u16 peer_id) +Peer* ConnectionThread::getPeerNoEx(u16 peer_id) { core::map::Node *node = m_peers.find(peer_id); @@ -1197,7 +1342,7 @@ Peer* Connection::getPeerNoEx(u16 peer_id) return node->getValue(); } -core::list Connection::getPeers() +core::list ConnectionThread::getPeers() { core::list list; core::map::Iterator j; @@ -1210,7 +1355,7 @@ Peer* Connection::getPeerNoEx(u16 peer_id) return list; } -bool Connection::getFromBuffers(u16 &peer_id, SharedBuffer &dst) +bool ConnectionThread::getFromBuffers(u16 &peer_id, SharedBuffer &dst) { core::map::Iterator j; j = m_peers.getIterator(); @@ -1231,7 +1376,7 @@ bool Connection::getFromBuffers(u16 &peer_id, SharedBuffer &dst) return false; } -bool Connection::checkIncomingBuffers(Channel *channel, u16 &peer_id, +bool ConnectionThread::checkIncomingBuffers(Channel *channel, u16 &peer_id, SharedBuffer &dst) { u16 firstseqnum = 0; @@ -1279,7 +1424,7 @@ bool Connection::checkIncomingBuffers(Channel *channel, u16 &peer_id, return false; } -SharedBuffer Connection::processPacket(Channel *channel, +SharedBuffer ConnectionThread::processPacket(Channel *channel, SharedBuffer packetdata, u16 peer_id, u8 channelnum, bool reliable) { @@ -1355,6 +1500,25 @@ bool Connection::checkIncomingBuffers(Channel *channel, u16 &peer_id, { dout_con<<"changing."< tcp = m_initial_peer_tcps[peer_id]; + u8 buf[6]; + writeU32(&buf[0], m_protocol_id); + writeU16(&buf[4], peer_id_new); + tcp->Send(buf, 6); + m_peer_tcps[peer_id] = tcp; + m_initial_peer_tcps.erase(peer_id); + } + } catch(SocketException &e){ + derr_con<address; +} + +float ConnectionThread::GetPeerAvgRTT(u16 peer_id) +{ + JMutexAutoLock peerlock(m_peers_mutex); + return getPeer(peer_id)->avg_rtt; +} + +u32 ConnectionThread::GetPeerOutgoingQueueSize(u16 peer_id) +{ + JMutexAutoLock peerlock(m_peers_mutex); + return getPeer(peer_id)->m_num_queued; +} + +float ConnectionThread::GetPeerOutgoingQueueSizeSeconds(u16 peer_id) +{ + JMutexAutoLock peerlock(m_peers_mutex); + Peer *peer = getPeer(peer_id); + return (float)peer->m_num_queued / peer->m_max_packets_per_second; +} + +void ConnectionThread::PrintInfo(std::ostream &out) +{ + out< &data) +void Connection::Connect(Address address) { - for(;;){ - ConnectionEvent e = waitEvent(m_bc_receive_timeout); - if(e.type != CONNEVENT_NONE) - dout_con<(e.data); - return e.data.getSize(); - case CONNEVENT_PEER_ADDED: { - Peer tmp(e.peer_id, e.address); - if(m_bc_peerhandler) - m_bc_peerhandler->peerAdded(&tmp); - continue; } - case CONNEVENT_PEER_REMOVED: { - Peer tmp(e.peer_id, e.address); - if(m_bc_peerhandler) - m_bc_peerhandler->deletingPeer(&tmp, e.timeout); - continue; } - case CONNEVENT_BIND_FAILED: - throw ConnectionBindFailed("Failed to bind socket " - "(port already in use?)"); - } - } - throw NoIncomingDataException("No incoming data"); + ConnectionCommand c; + c.connect(address); + putCommand(c); +} + +void Connection::Disconnect() +{ + ConnectionCommand c; + c.disconnect(); + putCommand(c); } void Connection::SendToAll(u8 channelnum, SharedBuffer data, bool reliable) @@ -1654,43 +1866,73 @@ void Connection::Send(u16 peer_id, u8 channelnum, putCommand(c); } -void Connection::RunTimeouts(float dtime) +void Connection::DeletePeer(u16 peer_id) { - // No-op + ConnectionCommand c; + c.deletePeer(peer_id); + putCommand(c); } -Address Connection::GetPeerAddress(u16 peer_id) +bool Connection::Connected() { - JMutexAutoLock peerlock(m_peers_mutex); - return getPeer(peer_id)->address; + return m_thread.Connected(); } -float Connection::GetPeerAvgRTT(u16 peer_id) +u16 Connection::GetPeerID() { - JMutexAutoLock peerlock(m_peers_mutex); - return getPeer(peer_id)->avg_rtt; + return m_thread.GetPeerID(); } -void Connection::DeletePeer(u16 peer_id) +Address Connection::GetPeerAddress(u16 peer_id) { - ConnectionCommand c; - c.deletePeer(peer_id); - putCommand(c); + return m_thread.GetPeerAddress(peer_id); } -void Connection::PrintInfo(std::ostream &out) +float Connection::GetPeerAvgRTT(u16 peer_id) { - out< &data) +{ + for(;;){ + ConnectionEvent e = waitEvent(m_bc_receive_timeout); + if(e.type != CONNEVENT_NONE) + dout_con<(e.data); + return e.data.getSize(); + case CONNEVENT_PEER_ADDED: { + Peer tmp(e.peer_id, e.address); + if(m_bc_peerhandler) + m_bc_peerhandler->peerAdded(&tmp); + continue; } + case CONNEVENT_PEER_REMOVED: { + Peer tmp(e.peer_id, e.address); + if(m_bc_peerhandler) + m_bc_peerhandler->deletingPeer(&tmp, e.timeout); + continue; } + case CONNEVENT_BIND_FAILED: + throw ConnectionBindFailed("Failed to bind socket " + "(port already in use?)"); + } + } + throw NoIncomingDataException("No incoming data"); } } // namespace diff --git a/src/connection.h b/src/connection.h index f88e813..3148d8f 100644 --- a/src/connection.h +++ b/src/connection.h @@ -29,6 +29,9 @@ #include "util/thread.h" #include #include +#include +#include +#include namespace con { @@ -131,6 +134,37 @@ struct BufferedPacket Address address; // Sender or destination }; +struct StreamBuffer +{ + std::deque buffer; + + void push(SharedBuffer data) + { + buffer.insert(buffer.end(), &data[0], &data[0]+data.getSize()); + } + u32 size() + { + return buffer.size(); + } + SharedBuffer peek(u32 req_size) + { + if(size() < req_size) + return SharedBuffer(0); + SharedBuffer data(req_size); + std::copy(buffer.begin(), buffer.begin() + req_size, &data[0]); + return data; + } + SharedBuffer pop(u32 req_size) + { + if(size() < req_size) + return SharedBuffer(0); + SharedBuffer data(req_size); + std::copy(buffer.begin(), buffer.begin() + req_size, &data[0]); + buffer.erase(buffer.begin(), buffer.begin() + req_size); + return data; + } +}; + // This adds the base headers to the data and makes a packet out of it BufferedPacket makePacket(Address &address, u8 *data, u32 datasize, u32 protocol_id, u16 sender_peer_id, u8 channel); @@ -395,6 +429,9 @@ class Peer int m_num_sent; int m_max_num_sent; + // Updated in Connection::send() for throttling of sending on upper layer + u32 m_num_queued; + private: }; @@ -538,13 +575,11 @@ struct ConnectionCommand } }; -class Connection: public SimpleThread +class ConnectionThread: public SimpleThread { public: - Connection(u32 protocol_id, u32 max_packet_size, float timeout); - Connection(u32 protocol_id, u32 max_packet_size, float timeout, - PeerHandler *peerhandler); - ~Connection(); + ConnectionThread(u32 protocol_id, u32 max_packet_size, float timeout); + ~ConnectionThread(); void * Thread(); /* Interface */ @@ -553,19 +588,12 @@ class Connection: public SimpleThread ConnectionEvent waitEvent(u32 timeout_ms); void putCommand(ConnectionCommand &c); - void SetTimeoutMs(int timeout){ m_bc_receive_timeout = timeout; } - void Serve(unsigned short port); - void Connect(Address address); bool Connected(); - void Disconnect(); - u32 Receive(u16 &peer_id, SharedBuffer &data); - void SendToAll(u8 channelnum, SharedBuffer data, bool reliable); - void Send(u16 peer_id, u8 channelnum, SharedBuffer data, bool reliable); - void RunTimeouts(float dtime); // dummy u16 GetPeerID(){ return m_peer_id; } Address GetPeerAddress(u16 peer_id); float GetPeerAvgRTT(u16 peer_id); - void DeletePeer(u16 peer_id); + u32 GetPeerOutgoingQueueSize(u16 peer_id); + float GetPeerOutgoingQueueSizeSeconds(u16 peer_id); private: void putEvent(ConnectionEvent &e); @@ -618,16 +646,58 @@ class Connection: public SimpleThread core::map m_peers; JMutex m_peers_mutex; - // Backwards compatibility - PeerHandler *m_bc_peerhandler; - int m_bc_receive_timeout; - + bool m_is_listening; + TCPSocket m_bound_tcp; + core::list > m_unknown_tcps; + std::map > m_initial_peer_tcps; + std::map > m_peer_tcps; + std::map m_peer_streambufs; + void SetPeerID(u16 id){ m_peer_id = id; } u32 GetProtocolID(){ return m_protocol_id; } void PrintInfo(std::ostream &out); void PrintInfo(); - std::string getDesc(); u16 m_indentation; +public: + std::string getDesc(); +}; + +class Connection +{ +public: + Connection(u32 protocol_id, u32 max_packet_size, float timeout); + Connection(u32 protocol_id, u32 max_packet_size, float timeout, + PeerHandler *peerhandler); + ~Connection(); + + ConnectionEvent getEvent(); + ConnectionEvent waitEvent(u32 timeout_ms); + void putCommand(ConnectionCommand &c); + + void Serve(unsigned short port); + void Connect(Address address); + void Disconnect(); + void SendToAll(u8 channelnum, SharedBuffer data, bool reliable); + void Send(u16 peer_id, u8 channelnum, SharedBuffer data, bool reliable); + void DeletePeer(u16 peer_id); + + bool Connected(); + u16 GetPeerID(); + Address GetPeerAddress(u16 peer_id); + float GetPeerAvgRTT(u16 peer_id); + u32 GetPeerOutgoingQueueSize(u16 peer_id); + float GetPeerOutgoingQueueSizeSeconds(u16 peer_id); + + // Backwards compatibility + u32 Receive(u16 &peer_id, SharedBuffer &data); + void SetTimeoutMs(int timeout){ m_bc_receive_timeout = timeout; } + +private: + ConnectionThread m_thread; + + // Backwards compatibility + PeerHandler *m_bc_peerhandler; + int m_bc_receive_timeout; }; } // namespace diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index 3b37c18..eb244d0 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -50,6 +50,7 @@ void set_default_settings(Settings *settings) settings->setDefault("keymap_toggle_update_camera", "KEY_F4"); settings->setDefault("keymap_toggle_debug", "KEY_F5"); settings->setDefault("keymap_toggle_profiler", "KEY_F6"); + settings->setDefault("keymap_toggle_block_boundaries", "KEY_F7"); settings->setDefault("keymap_increase_viewing_range_min", "+"); settings->setDefault("keymap_decrease_viewing_range_min", "-"); settings->setDefault("aux1_descends", "false"); diff --git a/src/environment.cpp b/src/environment.cpp index 4abba63..c15b8e9 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -319,11 +319,10 @@ void ActiveBlockList::update(core::list &active_positions, */ ServerEnvironment::ServerEnvironment(ServerMap *map, lua_State *L, - IGameDef *gamedef, IBackgroundBlockEmerger *emerger): + IGameDef *gamedef): m_map(map), m_lua(L), m_gamedef(gamedef), - m_emerger(emerger), m_random_spawn_timer(3), m_send_recommended_timer(0), m_active_block_interval_overload_skip(0), @@ -1042,7 +1041,6 @@ void ServerEnvironment::step(float dtime) MapBlock *block = m_map->getBlockNoCreateNoEx(p); if(block==NULL){ // Block needs to be fetched first - m_emerger->queueBlockEmerge(p, false); m_active_blocks.m_list.remove(p); continue; } diff --git a/src/environment.h b/src/environment.h index 0422290..f4aa43d 100644 --- a/src/environment.h +++ b/src/environment.h @@ -175,12 +175,6 @@ class ActiveBlockList private: }; -class IBackgroundBlockEmerger -{ -public: - virtual void queueBlockEmerge(v3s16 blockpos, bool allow_generate)=0; -}; - /* The server-side environment. @@ -190,8 +184,7 @@ class IBackgroundBlockEmerger class ServerEnvironment : public Environment { public: - ServerEnvironment(ServerMap *map, lua_State *L, IGameDef *gamedef, - IBackgroundBlockEmerger *emerger); + ServerEnvironment(ServerMap *map, lua_State *L, IGameDef *gamedef); ~ServerEnvironment(); Map & getMap(); @@ -345,8 +338,6 @@ class ServerEnvironment : public Environment lua_State *m_lua; // Game definition IGameDef *m_gamedef; - // Background block emerger (the server, in practice) - IBackgroundBlockEmerger *m_emerger; // Active object list core::map m_active_objects; // Outgoing network message buffer for active objects diff --git a/src/game.cpp b/src/game.cpp index a1a1972..29616d1 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -1249,6 +1249,7 @@ void the_game( bool disable_camera_update = false; bool show_debug = g_settings->getBool("show_debug"); bool show_profiler_graph = false; + bool show_block_boundaries = false; u32 show_profiler = 0; u32 show_profiler_max = 3; // Number of pages @@ -1704,6 +1705,15 @@ void the_game( statustext_time = 0; } } + else if(input->wasKeyDown(getKeySetting("keymap_toggle_block_boundaries"))) + { + show_block_boundaries = !show_block_boundaries; + if(show_block_boundaries) + statustext = L"Block boundaries shown"; + else + statustext = L"Block boundaries hidden"; + statustext_time = 0; + } else if(input->wasKeyDown(getKeySetting("keymap_increase_viewing_range_min"))) { s16 range = g_settings->getS16("viewing_range_nodes_min"); @@ -2039,7 +2049,7 @@ void the_game( } /* - Calculate what block is the crosshair pointing to + Calculate what object is the crosshair pointing to */ //u32 t1 = device->getTimer()->getRealTime(); @@ -2743,7 +2753,15 @@ void the_game( //timer9.stop(); //TimeTaker //timer10("//timer10"); - + + /* + Block boundary visualization + */ + + if (show_block_boundaries) { + client.getEnv().getClientMap().renderBlockBoundaries(); + } + video::SMaterial m; //m.Thickness = 10; m.Thickness = 3; diff --git a/src/map.cpp b/src/map.cpp index 39c6d29..d47bcca 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -1921,6 +1921,7 @@ void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta) return; } block->m_node_metadata.set(p_rel, meta); + block->raiseModified(MOD_STATE_WRITE_NEEDED, "setNodeMetadata"); } void Map::removeNodeMetadata(v3s16 p) diff --git a/src/mapblock.cpp b/src/mapblock.cpp index 2ae6e9b..625d1dc 100644 --- a/src/mapblock.cpp +++ b/src/mapblock.cpp @@ -56,6 +56,7 @@ m_generated(false), m_timestamp(BLOCK_TIMESTAMP_UNDEFINED), m_disk_timestamp(BLOCK_TIMESTAMP_UNDEFINED), + m_change_counter(0), m_usage_timer(0), m_refcount(0) { @@ -570,7 +571,7 @@ void MapBlock::serialize(std::ostream &os, u8 version, bool disk) if(m_generated == false) flags |= 0x08; writeU8(os, flags); - + /* Bulk node data */ diff --git a/src/mapblock.h b/src/mapblock.h index d56d93d..2c2b580 100644 --- a/src/mapblock.h +++ b/src/mapblock.h @@ -37,13 +37,14 @@ #include "nodetimer.h" #include "modifiedstate.h" #include "util/numeric.h" // getContainerPos +#include "map.h" -class Map; class NodeMetadataList; class IGameDef; class MapBlockMesh; #define BLOCK_TIMESTAMP_UNDEFINED 0xffffffff +#define BLOCK_CHANGECOUNTER_UNDEFINED 0xffffffff /*// Named by looking towards z+ enum{ @@ -152,21 +153,25 @@ class MapBlock /*: public NodeContainer*/ // m_modified methods void raiseModified(u32 mod, const std::string &reason="unknown") { - if(mod > m_modified){ - m_modified = mod; - m_modified_reason = reason; - m_modified_reason_too_long = false; + if (m_parent->mapType() == MAPTYPE_SERVER) { + ++m_change_counter; - if(m_modified >= MOD_STATE_WRITE_AT_UNLOAD){ - m_disk_timestamp = m_timestamp; - } - } else if(mod == m_modified){ - if(!m_modified_reason_too_long){ - if(m_modified_reason.size() < 40) - m_modified_reason += ", " + reason; - else{ - m_modified_reason += "..."; - m_modified_reason_too_long = true; + if(mod > m_modified){ + m_modified = mod; + m_modified_reason = reason; + m_modified_reason_too_long = false; + + if(m_modified >= MOD_STATE_WRITE_AT_UNLOAD){ + m_disk_timestamp = m_timestamp; + } + } else if(mod == m_modified){ + if(!m_modified_reason_too_long){ + if(m_modified_reason.size() < 40) + m_modified_reason += ", " + reason; + else{ + m_modified_reason += "..."; + m_modified_reason_too_long = true; + } } } } @@ -414,6 +419,18 @@ class MapBlock /*: public NodeContainer*/ { return m_disk_timestamp; } + + /* + Only use setChangeCounter on the client side, where it can mean + "this is the change number that this block is contemporary with". + On the server, the counter should only be updated by raiseModified. + */ + void setChangeCounter(u32 cc) { + m_change_counter = cc; + } + u32 getChangeCounter() { + return m_change_counter; + } /* See m_usage_timer @@ -537,7 +554,7 @@ class MapBlock /*: public NodeContainer*/ /* - On the server, this is used for telling whether the - block has been modified from the one on disk. + block has been modified from the one on disk. - On the client, this is used for nothing. */ u32 m_modified; @@ -578,6 +595,17 @@ class MapBlock /*: public NodeContainer*/ u32 m_disk_timestamp; /* + Incremented whenever the block changes. Used to + determine whether clients have the latest version of a block or + not. + - On the server, this is updated only when the block + changes. + - On the client, updated when the client receives updated + block information from the server. + */ + u32 m_change_counter; + + /* When the block is accessed, this is set to 0. Map will unload the block when this reaches a timeout. */ @@ -593,7 +621,7 @@ class MapBlock /*: public NodeContainer*/ inline bool blockpos_over_limit(v3s16 p) { return - (p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE + (p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE diff --git a/src/porting.cpp b/src/porting.cpp index 945aea6..f4a63e0 100644 --- a/src/porting.cpp +++ b/src/porting.cpp @@ -72,9 +72,15 @@ void sigint_handler(int sig) } } +void sigpipe_handler(int sig) +{ + // Ignore +} + void signal_handler_init(void) { (void)signal(SIGINT, sigint_handler); + (void)signal(SIGPIPE, sigpipe_handler); } #else // _WIN32 diff --git a/src/server.cpp b/src/server.cpp index 2da9cbe..58de74c 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -180,6 +180,7 @@ void * EmergeThread::Thread() if(qptr == NULL) break; + g_profiler->add("EmergeThread: blocks emerged", 1); SharedPtr q(qptr); v3s16 &p = q->pos; @@ -204,39 +205,14 @@ void * EmergeThread::Thread() will be allowed. */ - /* - Check if any peer wants it as non-optional. In that case it - will be generated. - - Also decrement the emerge queue count in clients. - */ - - bool only_from_disk = true; - - { - core::map::Iterator i; - for(i=q->peer_ids.getIterator(); i.atEnd()==false; i++) - { - //u16 peer_id = i.getNode()->getKey(); - - // Check flags - u8 flags = i.getNode()->getValue(); - if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false) - only_from_disk = false; - - } - } - if(enable_mapgen_debug_info) infostream<<"EmergeThread: p=" - <<"("<m_env->getMap()); - + MapBlock *block = NULL; - bool got_block = true; - core::map modified_blocks; + core::map modified_blocks; // FIXME Do we need this? /* Try to fetch block from memory or disk. @@ -266,8 +242,7 @@ void * EmergeThread::Thread() // If could not load and allowed to generate, start generation // inside this same envlock - if(only_from_disk == false && - (block == NULL || block->isGenerated() == false)){ + if(block == NULL || block->isGenerated() == false){ if(enable_mapgen_debug_info) infostream<<"EmergeThread: generating"<add("EmergeThread: blocks generated", 1); mapgen::make_block(&data); if(enable_mapgen_debug_info == false) @@ -344,41 +320,6 @@ void * EmergeThread::Thread() m_server->m_env->activateBlock(block, 0); }while(false); } - - if(block == NULL) - got_block = false; - - /* - Set sent status of modified blocks on clients - */ - - // NOTE: Server's clients are also behind the connection mutex - JMutexAutoLock lock(m_server->m_con_mutex); - - /* - Add the originally fetched block to the modified list - */ - if(got_block) - { - modified_blocks.insert(p, block); - } - - /* - Set the modified blocks unsent for all the clients - */ - - for(core::map::Iterator - i = m_server->m_clients.getIterator(); - i.atEnd() == false; i++) - { - RemoteClient *client = i.getNode()->getValue(); - - if(modified_blocks.size() > 0) - { - // Remove block from sent history - client->SetBlocksNotSent(modified_blocks); - } - } } catch(VersionMismatchException &e) { @@ -429,471 +370,6 @@ v3f ServerSoundParams::getPos(ServerEnvironment *env, bool *pos_exists) const return v3f(0,0,0); } -void RemoteClient::GetNextBlocks(Server *server, float dtime, - core::array &dest) -{ - DSTACK(__FUNCTION_NAME); - - /*u32 timer_result; - TimeTaker timer("RemoteClient::GetNextBlocks", &timer_result);*/ - - // Increment timers - m_nothing_to_send_pause_timer -= dtime; - m_nearest_unsent_reset_timer += dtime; - - if(m_nothing_to_send_pause_timer >= 0) - return; - - Player *player = server->m_env->getPlayer(peer_id); - // This can happen sometimes; clients and players are not in perfect sync. - if(player == NULL) - return; - - // Won't send anything if already sending - if(m_blocks_sending.size() >= g_settings->getU16 - ("max_simultaneous_block_sends_per_client")) - { - //infostream<<"Not sending any blocks, Queue full."<getPosition(); - v3f playerspeed = player->getSpeed(); - v3f playerspeeddir(0,0,0); - if(playerspeed.getLength() > 1.0*BS) - playerspeeddir = playerspeed / playerspeed.getLength(); - // Predict to next block - v3f playerpos_predicted = playerpos + playerspeeddir*MAP_BLOCKSIZE*BS; - - v3s16 center_nodepos = floatToInt(playerpos_predicted, BS); - - v3s16 center = getNodeBlockPos(center_nodepos); - - // Camera position and direction - v3f camera_pos = player->getEyePosition(); - v3f camera_dir = v3f(0,0,1); - camera_dir.rotateYZBy(player->getPitch()); - camera_dir.rotateXZBy(player->getYaw()); - - /*infostream<<"camera_dir=("<getPlayerName(peer_id)<getFloat( - "full_block_send_enable_min_time_from_building")) - { - max_simul_sends_usually - = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS; - } - - /* - Number of blocks sending + number of blocks selected for sending - */ - u32 num_blocks_selected = m_blocks_sending.size(); - - /* - next time d will be continued from the d from which the nearest - unsent block was found this time. - - This is because not necessarily any of the blocks found this - time are actually sent. - */ - s32 new_nearest_unsent_d = -1; - - s16 d_max = g_settings->getS16("max_block_send_distance"); - s16 d_max_gen = g_settings->getS16("max_block_generate_distance"); - - // Don't loop very much at a time - s16 max_d_increment_at_time = 2; - if(d_max > d_start + max_d_increment_at_time) - d_max = d_start + max_d_increment_at_time; - /*if(d_max_gen > d_start+2) - d_max_gen = d_start+2;*/ - - //infostream<<"Starting from "<getPlayerName(peer_id)< list; - getFacePositions(list, d); - - core::list::Iterator li; - for(li=list.begin(); li!=list.end(); li++) - { - v3s16 p = *li + center; - - /* - Send throttling - - Don't allow too many simultaneous transfers - - EXCEPT when the blocks are very close - - Also, don't send blocks that are already flying. - */ - - // Start with the usual maximum - u16 max_simul_dynamic = max_simul_sends_usually; - - // If block is very close, allow full maximum - if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D) - max_simul_dynamic = max_simul_sends_setting; - - // Don't select too many blocks for sending - if(num_blocks_selected >= max_simul_dynamic) - { - queue_is_full = true; - goto queue_full_break; - } - - // Don't send blocks that are currently being transferred - if(m_blocks_sending.find(p) != NULL) - continue; - - /* - Do not go over-limit - */ - if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE - || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE - || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE - || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE - || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE - || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE) - continue; - - // If this is true, inexistent block will be made from scratch - bool generate = d <= d_max_gen; - - { - /*// Limit the generating area vertically to 2/3 - if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3) - generate = false;*/ - - // Limit the send area vertically to 1/2 - if(abs(p.Y - center.Y) > d_max / 2) - continue; - } - -#if 0 - /* - If block is far away, don't generate it unless it is - near ground level. - */ - if(d >= 4) - { - #if 1 - // Block center y in nodes - f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2); - // Don't generate if it's very high or very low - if(y < -64 || y > 64) - generate = false; - #endif - #if 0 - v2s16 p2d_nodes_center( - MAP_BLOCKSIZE*p.X, - MAP_BLOCKSIZE*p.Z); - - // Get ground height in nodes - s16 gh = server->m_env->getServerMap().findGroundLevel( - p2d_nodes_center); - - // If differs a lot, don't generate - if(fabs(gh - y) > MAP_BLOCKSIZE*2) - generate = false; - // Actually, don't even send it - //continue; - #endif - } -#endif - - //infostream<<"d="<m_env->getMap().getBlockNoCreateNoEx(p); - - bool surely_not_found_on_disk = false; - bool block_is_invalid = false; - if(block != NULL) - { - // Reset usage timer, this block will be of use in the future. - block->resetUsageTimer(); - - // Block is dummy if data doesn't exist. - // It means it has been not found from disk and not generated - if(block->isDummy()) - { - surely_not_found_on_disk = true; - } - - // Block is valid if lighting is up-to-date and data exists - if(block->isValid() == false) - { - block_is_invalid = true; - } - - /*if(block->isFullyGenerated() == false) - { - block_is_invalid = true; - }*/ - -#if 0 - v2s16 p2d(p.X, p.Z); - ServerMap *map = (ServerMap*)(&server->m_env->getMap()); - v2s16 chunkpos = map->sector_to_chunk(p2d); - if(map->chunkNonVolatile(chunkpos) == false) - block_is_invalid = true; -#endif - if(block->isGenerated() == false) - block_is_invalid = true; -#if 1 - /* - If block is not close, don't send it unless it is near - ground level. - - Block is near ground level if night-time mesh - differs from day-time mesh. - */ - if(d >= 4) - { - if(block->getDayNightDiff() == false) - continue; - } -#endif - } - - /* - If block has been marked to not exist on disk (dummy) - and generating new ones is not wanted, skip block. - */ - if(generate == false && surely_not_found_on_disk == true) - { - // get next one. - continue; - } - - /* - Add inexistent block to emerge queue. - */ - if(block == NULL || surely_not_found_on_disk || block_is_invalid) - { - //TODO: Get value from somewhere - // Allow only one block in emerge queue - //if(server->m_emerge_queue.peerItemCount(peer_id) < 1) - // Allow two blocks in queue per client - //if(server->m_emerge_queue.peerItemCount(peer_id) < 2) - u32 max_emerge = 5; - // Make it more responsive when needing to generate stuff - if(surely_not_found_on_disk) - max_emerge = 1; - if(server->m_emerge_queue.peerItemCount(peer_id) < max_emerge) - { - //infostream<<"Adding block to emerge queue"<m_emerge_queue.addBlock(peer_id, p, flags); - server->m_emergethread.trigger(); - - if(nearest_emerged_d == -1) - nearest_emerged_d = d; - } else { - if(nearest_emergefull_d == -1) - nearest_emergefull_d = d; - } - - // get next one. - continue; - } - - if(nearest_sent_d == -1) - nearest_sent_d = d; - - /* - Add block to send queue - */ - - /*errorstream<<"sending from d="<getPlayerName(peer_id)< g_settings->getS16("max_block_send_distance")){ - new_nearest_unsent_d = 0; - m_nothing_to_send_pause_timer = 2.0; - /*infostream<<"GetNextBlocks(): d wrapped around for " - <getPlayerName(peer_id) - <<"; setting to 0 and pausing"< &blocks) -{ - m_nearest_unsent_d = 0; - - for(core::map::Iterator - i = blocks.getIterator(); - i.atEnd()==false; i++) - { - v3s16 p = i.getNode()->getKey(); - - if(m_blocks_sending.find(p) != NULL) - m_blocks_sending.remove(p); - if(m_blocks_sent.find(p) != NULL) - m_blocks_sent.remove(p); - } -} - /* PlayerInfo */ @@ -915,6 +391,40 @@ void PlayerInfo::PrintLine(std::ostream *s) (*s)<::Iterator i = m_queue.begin(); + while(i != m_queue.end()) + { + core::list::Iterator i_next = i; + i_next++; + QueuedBlockSend *q = *i; + + if(m_timestamp >= q->timeout_timestamp){ + delete q; + m_queue.erase(i); + i = i_next; + continue; + } + + // FIXME: GetPeerOutgoingQueueSizeSeconds does not work for TCP + float peer_packet_queue_seconds = + server.m_con.GetPeerOutgoingQueueSizeSeconds(q->peer_id); + if(peer_packet_queue_seconds < packet_queue_max_seconds){ + MapBlock *block = server.m_env->getMap().getBlockNoCreateNoEx(q->pos); + if (block != NULL && !(block->isDummy()) && block->isValid() && + block->isGenerated()){ + RemoteClient *client = server.getClient(q->peer_id); + server.SendBlockNoLock(q->peer_id, block, + client->serialization_version); + } + delete q; + m_queue.erase(i); + } + i = i_next; + } +} + /* Server */ @@ -1053,8 +563,7 @@ void PlayerInfo::PrintLine(std::ostream *s) // Initialize Environment - m_env = new ServerEnvironment(new ServerMap(path_world, this), m_lua, - this, this); + m_env = new ServerEnvironment(new ServerMap(path_world, this), m_lua, this); // Give environment reference to scripting api scriptapi_add_environment(m_lua, m_env); @@ -1250,12 +759,7 @@ void Server::AsyncRunStep() JMutexAutoLock lock1(m_step_dtime_mutex); dtime = m_step_dtime; } - - { - // Send blocks to clients - SendBlocks(dtime); - } - + if(dtime < 0.001) return; @@ -1277,19 +781,25 @@ 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 handlePeerChanges(); } /* + Send blocks to clients + */ + { + m_block_send_queue.step(dtime); + + JMutexAutoLock lock(m_env_mutex); + JMutexAutoLock lock2(m_con_mutex); + + // Send stuff enough to fill the outgoing buffer half of the time + m_block_send_queue.send(*this, dtime * 0.5); + } + + /* Update time of day and overall game time */ { @@ -1395,7 +905,7 @@ void Server::AsyncRunStep() ScopeProfiler sp(g_profiler, "Server: liquid transform"); - core::map modified_blocks; + core::map modified_blocks; // FIXME Needed? m_env->getMap().transformLiquids(modified_blocks); #if 0 /* @@ -1414,24 +924,6 @@ void Server::AsyncRunStep() modified_blocks.insert(block->getPos(), block); } #endif - /* - Set the modified blocks unsent for all the clients - */ - - JMutexAutoLock lock2(m_con_mutex); - - for(core::map::Iterator - i = m_clients.getIterator(); - i.atEnd() == false; i++) - { - RemoteClient *client = i.getNode()->getValue(); - - if(modified_blocks.size() > 0) - { - // Remove block from sent history - client->SetBlocksNotSent(modified_blocks); - } - } } // Periodically print some info @@ -1762,50 +1254,29 @@ void Server::AsyncRunStep() { MapEditEvent* event = m_unsent_map_edit_queue.pop_front(); - // Players far away from the change are stored here. - // Instead of sending the changes, MapBlocks are set not sent - // for them. - core::list far_players; - if(event->type == MEET_ADDNODE) { //infostream<<"Server: MEET_ADDNODE"<p, event->n, event->already_known_by_peer, - &far_players, 5); - else - sendAddNode(event->p, event->n, event->already_known_by_peer, - &far_players, 30); + float max_d = disable_single_change_sending ? 5 : 30; + sendAddNode(event->p, event->n, event->already_known_by_peer, max_d); } else if(event->type == MEET_REMOVENODE) { //infostream<<"Server: MEET_REMOVENODE"<p, event->already_known_by_peer, - &far_players, 5); - else - sendRemoveNode(event->p, event->already_known_by_peer, - &far_players, 30); + float max_d = disable_single_change_sending ? 5 : 30; + sendRemoveNode(event->p, event->already_known_by_peer, max_d); } else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED) { infostream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<p); } else if(event->type == MEET_OTHER) { infostream<<"Server: MEET_OTHER"<::Iterator - i = event->modified_blocks.getIterator(); - i.atEnd()==false; i++) - { - v3s16 p = i.getNode()->getKey(); - setBlockNotSent(p); - } } else { @@ -1814,34 +1285,6 @@ void Server::AsyncRunStep() <<((u32)event->type)< 0) - { - // Convert list format to that wanted by SetBlocksNotSent - core::map modified_blocks2; - for(core::map::Iterator - i = event->modified_blocks.getIterator(); - i.atEnd()==false; i++) - { - v3s16 p = i.getNode()->getKey(); - modified_blocks2.insert(p, - m_env->getMap().getBlockNoCreateNoEx(p)); - } - // Set blocks not sent - for(core::list::Iterator - i = far_players.begin(); - i != far_players.end(); i++) - { - u16 peer_id = *i; - RemoteClient *client = getClient(peer_id); - if(client==NULL) - continue; - client->SetBlocksNotSent(modified_blocks2); - } - } - delete event; /*// Don't send too many at a time @@ -2352,6 +1795,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) } Player *player = m_env->getPlayer(peer_id); + if(player == NULL){ infostream<<"Server::ProcessData(): Cancelling: " "No player for peer_id="<getMap()); + + if(command == TOSERVER_REQUEST_BLOCKS) + { + /* + u16 command + u16 timeout_ms TODO: Send blocks elsewhere with a bit of bandwidth + throttling and obey this + v3s16 pos_0 + v3s16 pos_1 + for each block { + u32 client_version + } + */ + + if(datasize < 2) + return; + + std::string datastring((char*)&data[2], datasize-2); + std::istringstream is(datastring, std::ios_base::binary); + + int timeout_ms = readU16(is); + double timeout = (double)timeout_ms / 1000; + v3s16 pos_0 = readV3S16(is); + v3s16 pos_1 = readV3S16(is); + + verbosestream<<"REQUEST_BLOCKS: "<getName()<<": " + <<"pos: "<getPosition() - pf).getLength(); + + MapBlock *block = map.getBlockNoCreateNoEx(p); + if (block != NULL && !(block->isDummy()) && block->isValid() && + block->isGenerated()) + { + block->resetUsageTimer(); + if (block->getChangeCounter() > client_change_counter) { + /*infostream<<"Server: Client's version of "<getChangeCounter() + <GotBlock(p); - } - } - else if(command == TOSERVER_DELETEDBLOCKS) - { - if(datasize < 2+1) - return; - - /* - [0] u16 command - [2] u8 count - [3] v3s16 pos_0 - [3+6] v3s16 pos_1 - ... - */ - - u16 count = data[2]; - for(u16 i=0; iSetBlockNotSent(p); - } - } else if(command == TOSERVER_CLICK_OBJECT) { infostream<<"Server: CLICK_OBJECT not supported anymore"<getName() - <<" tried to interact, but is dead!"<getLastGoodPosition(); // Update wielded item @@ -2903,11 +2358,23 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) } /* - Check that target is reasonably close - (only when digging or placing things) + Make sure the player is allowed to do it */ + bool interact_allowed = true; + + if(player->hp == 0) + { + verbosestream<<"TOSERVER_INTERACT: "<getName() + <<" tried to interact, but is dead!"< max_d){ @@ -2916,35 +2383,43 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) <<" from too far: " <<"d="<SetBlockNotSent(blockpos); - // Do nothing else - return; + interact_allowed = false; } } - /* - Make sure the player is allowed to do it - */ if(!checkPriv(player->getName(), "interact")) { actionstream<getName()<<" attempted to interact with " < under + v3s16 under_blockpos = getNodeBlockPos(p_under); if(action == 2){ - v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS)); - client->SetBlockNotSent(blockpos); + MapBlock *block = map.getBlockNoCreateNoEx(under_blockpos); + if (block) + block->raiseModified(MOD_STATE_CLEAN,"interactDenied"); } // Placement -> above - if(action == 3){ - v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS)); - client->SetBlockNotSent(blockpos); + v3s16 above_blockpos = getNodeBlockPos(p_above); + if(action == 3 && above_blockpos != under_blockpos){ + MapBlock *block = map.getBlockNoCreateNoEx(above_blockpos); + if (block) + block->raiseModified(MOD_STATE_CLEAN,"interactDenied"); } + + // Do nothing else return; } @@ -2973,10 +2448,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) catch(InvalidPositionException &e) { infostream<<"Server: Not punching: Node not found." - <<" Adding block to emerge queue." <getMap().getNodeNoEx(p_under).getContent() != CONTENT_AIR) { // Re-send block to revert change on client-side - RemoteClient *client = getClient(peer_id); + //RemoteClient *client = getClient(peer_id); v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS)); - client->SetBlockNotSent(blockpos); + // FIXME: This is not optimal at all because this re-sends them + // to everyone instead of the current client only + MapBlock *block = map.getBlockNoCreateNoEx(blockpos); + if (block) + block->raiseModified(MOD_STATE_CLEAN,"interactDenied"); } } } // action == 2 @@ -3142,9 +2615,13 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) // If item has node placement prediction, always send the above // node to make sure the client knows what exactly happened if(item.getDefinition(m_itemdef).node_placement_prediction != ""){ - RemoteClient *client = getClient(peer_id); + //RemoteClient *client = getClient(peer_id); v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS)); - client->SetBlockNotSent(blockpos); + // FIXME: This is not optimal at all because this re-sends them + // to everyone instead of the current client only + MapBlock *block = map.getBlockNoCreateNoEx(blockpos); + if (block) + block->raiseModified(MOD_STATE_CLEAN,"interactDenied"); } } // action == 3 @@ -3334,8 +2811,6 @@ void Server::setInventoryModified(const InventoryLocation &loc) MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos); if(block) block->raiseModified(MOD_STATE_WRITE_NEEDED); - - setBlockNotSent(blockpos); } break; case InventoryLocation::DETACHED: @@ -3781,8 +3256,7 @@ void Server::stopSound(s32 handle) m_playing_sounds.erase(i); } -void Server::sendRemoveNode(v3s16 p, u16 ignore_id, - core::list *far_players, float far_d_nodes) +void Server::sendRemoveNode(v3s16 p, u16 ignore_id, float far_d_nodes) { float maxd = far_d_nodes*BS; v3f p_f = intToFloat(p, BS); @@ -3809,29 +3283,23 @@ void Server::sendRemoveNode(v3s16 p, u16 ignore_id, if(client->peer_id == ignore_id) continue; - if(far_players) - { - // Get player + // Don't send if player is far away from event Player *player = m_env->getPlayer(client->peer_id); if(player) { - // If player is far away, only set modified blocks not sent v3f player_pos = player->getPosition(); if(player_pos.getDistanceFrom(p_f) > maxd) { - far_players->push_back(client->peer_id); continue; } } - } // Send as reliable m_con.Send(client->peer_id, 0, reply, true); } } -void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id, - core::list *far_players, float far_d_nodes) +void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id, float far_d_nodes) { float maxd = far_d_nodes*BS; v3f p_f = intToFloat(p, BS); @@ -3850,21 +3318,16 @@ void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id, if(client->peer_id == ignore_id) continue; - if(far_players) - { - // Get player + // Don't send if player is far away from event Player *player = m_env->getPlayer(client->peer_id); if(player) { - // If player is far away, only set modified blocks not sent v3f player_pos = player->getPosition(); if(player_pos.getDistanceFrom(p_f) > maxd) { - far_players->push_back(client->peer_id); continue; } } - } // Create packet u32 replysize = 8 + MapNode::serializedLength(client->serialization_version); @@ -3880,17 +3343,6 @@ void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id, } } -void Server::setBlockNotSent(v3s16 p) -{ - for(core::map::Iterator - i = m_clients.getIterator(); - i.atEnd()==false; i++) - { - RemoteClient *client = i.getNode()->getValue(); - client->SetBlockNotSent(p); - } -} - void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver) { DSTACK(__FUNCTION_NAME); @@ -3927,16 +3379,20 @@ void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver) std::string s = os.str(); SharedBuffer blockdata((u8*)s.c_str(), s.size()); - u32 replysize = 8 + blockdata.getSize(); + u32 replysize = 12 + blockdata.getSize(); SharedBuffer reply(replysize); writeU16(&reply[0], TOCLIENT_BLOCKDATA); writeS16(&reply[2], p.X); writeS16(&reply[4], p.Y); writeS16(&reply[6], p.Z); - memcpy(&reply[8], *blockdata, blockdata.getSize()); + writeU32(&reply[8], block->getChangeCounter()); + memcpy(&reply[12], *blockdata, blockdata.getSize()); + + g_profiler->add("Server: blocks sent", 1); /*infostream<<"Server: Sending block ("< queue; - - s32 total_sending = 0; - - { - ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending"); - - for(core::map::Iterator - i = m_clients.getIterator(); - i.atEnd() == false; i++) - { - RemoteClient *client = i.getNode()->getValue(); - assert(client->peer_id == i.getNode()->getKey()); - - // If definitions and textures have not been sent, don't - // send MapBlocks either - if(!client->definitions_sent) - continue; - - total_sending += client->SendingCount(); - - if(client->serialization_version == SER_FMT_VER_INVALID) - continue; - - client->GetNextBlocks(this, dtime, queue); - } - } - - // Sort. - // Lowest priority number comes first. - // Lowest is most important. - queue.sort(); - - for(u32 i=0; i= g_settings->getS32 - ("max_simultaneous_block_sends_server_total")) - break; - - PrioritySortedBlockTransfer q = queue[i]; - - MapBlock *block = NULL; - try - { - block = m_env->getMap().getBlockNoCreate(q.pos); - } - catch(InvalidPositionException &e) - { - continue; - } - - RemoteClient *client = getClient(q.peer_id); - - SendBlockNoLock(q.peer_id, block, client->serialization_version); - - client->SentBlock(q.pos); - - total_sending++; - } -} - void Server::fillMediaCache() { DSTACK(__FUNCTION_NAME); @@ -4524,14 +3909,6 @@ void Server::notifyPlayers(const std::wstring msg) BroadcastChatMessage(msg); } -void Server::queueBlockEmerge(v3s16 blockpos, bool allow_generate) -{ - u8 flags = 0; - if(!allow_generate) - flags |= BLOCK_EMERGE_FLAG_FROMDISK; - m_emerge_queue.addBlock(PEER_ID_INEXISTENT, blockpos, flags); -} - Inventory* Server::createDetachedInventory(const std::string &name) { if(m_detached_inventories.count(name) > 0){ diff --git a/src/server.h b/src/server.h index 223c1b0..7ace0d5 100644 --- a/src/server.h +++ b/src/server.h @@ -77,8 +77,7 @@ class ServerError : public std::exception struct QueuedBlockEmerge { v3s16 pos; - // key = peer_id, value = flags - core::map peer_ids; + float priority; // Larger = more important; 0 = highest }; /* @@ -104,41 +103,49 @@ class BlockEmergeQueue } } - /* - peer_id=0 adds with nobody to send to - */ - void addBlock(u16 peer_id, v3s16 pos, u8 flags) + void addBlock(v3s16 pos, float priority) { DSTACK(__FUNCTION_NAME); JMutexAutoLock lock(m_mutex); - - if(peer_id != 0) + + // Remove from queue if it's not already queued + for(core::list::Iterator + i=m_queue.begin(); i!=m_queue.end(); i++) { - /* - Find if block is already in queue. - If it is, update the peer to it and quit. - */ - core::list::Iterator i; - for(i=m_queue.begin(); i!=m_queue.end(); i++) + QueuedBlockEmerge *q = *i; + if(q->pos == pos){ + if(q->priority > priority){ + // Already in queue with a higher priority + return; + } else{ + // In queue with a lower priority; remove and re-add + delete q; + m_queue.erase(i); + break; + } + } + } + + // Add to queue + + QueuedBlockEmerge *newq = new QueuedBlockEmerge; + newq->pos = pos; + newq->priority = priority; + + if(m_queue.empty()){ + m_queue.push_back(newq); + } else { + for(core::list::Iterator + i=m_queue.begin(); i!=m_queue.end(); i++) { QueuedBlockEmerge *q = *i; - if(q->pos == pos) - { - q->peer_ids[peer_id] = flags; + if(q->priority < priority){ + m_queue.insert_before(i, newq); return; } } } - - /* - Add the block - */ - QueuedBlockEmerge *q = new QueuedBlockEmerge; - q->pos = pos; - if(peer_id != 0) - q->peer_ids[peer_id] = flags; - m_queue.push_back(q); } // Returned pointer must be deleted @@ -161,24 +168,8 @@ class BlockEmergeQueue return m_queue.size(); } - u32 peerItemCount(u16 peer_id) - { - JMutexAutoLock lock(m_mutex); - - u32 count = 0; - - core::list::Iterator i; - for(i=m_queue.begin(); i!=m_queue.end(); i++) - { - QueuedBlockEmerge *q = *i; - if(q->peer_ids.find(peer_id) != NULL) - count++; - } - - return count; - } - private: + // Sorted by priority; highest first core::list m_queue; JMutex m_mutex; }; @@ -313,6 +304,103 @@ struct ServerPlayingSound std::set clients; // peer ids }; +struct QueuedBlockSend +{ + int peer_id; + v3s16 pos; + float priority; // Larger = more important; 0 = highest + double timeout_timestamp; + + QueuedBlockSend(): + peer_id(0), + pos(0,0,0), + priority(0), + timeout_timestamp(0) + {} +}; + +class BlockSendQueue +{ +public: + BlockSendQueue() + { + } + + ~BlockSendQueue() + { + core::list::Iterator i; + for(i=m_queue.begin(); i!=m_queue.end(); i++) + { + QueuedBlockSend *q = *i; + delete q; + } + } + + void addBlock(int peer_id, v3s16 pos, float priority, float timeout) + { + float timeout_timestamp = m_timestamp + timeout; + + // Remove from queue if it's not already queued + for(core::list::Iterator + i=m_queue.begin(); i!=m_queue.end(); i++) + { + QueuedBlockSend *q = *i; + if(q->peer_id == peer_id && q->pos == pos){ + if(q->priority > priority && q->timeout_timestamp > timeout_timestamp){ + // Already in queue with a higher priority and higher timoeut + return; + } else{ + // In queue with a lower priority; remove and re-add + delete q; + m_queue.erase(i); + break; + } + } + } + + // Add to queue + + QueuedBlockSend *newq = new QueuedBlockSend; + newq->peer_id = peer_id; + newq->pos = pos; + newq->priority = priority; + newq->timeout_timestamp = timeout_timestamp; + + if(m_queue.empty()){ + m_queue.push_back(newq); + } else { + for(core::list::Iterator + i=m_queue.begin(); i!=m_queue.end(); i++) + { + QueuedBlockSend *q = *i; + if(q->priority < priority){ + m_queue.insert_before(i, newq); + return; + } + } + } + } + + void step(double dtime) + { + m_timestamp += dtime; + } + + u32 size() + { + return m_queue.size(); + } + + // FIXME: This shouldn't require the server at all; the data should be fed + // in addBlock and only the connection should be needed here + void send(Server &server, float packet_queue_max_seconds); + +private: + // Sorted by priority; highest first + core::list m_queue; + double m_timestamp; +}; + class RemoteClient { public: @@ -331,8 +419,7 @@ class RemoteClient bool definitions_sent; RemoteClient(): - m_time_from_building(9999), - m_excess_gotblocks(0) + m_time_from_building(9999) { peer_id = 0; serialization_version = SER_FMT_VER_INVALID; @@ -348,26 +435,6 @@ class RemoteClient { } - /* - Finds block that should be sent next to the client. - Environment should be locked when this is called. - dtime is used for resetting send radius at slow interval - */ - void GetNextBlocks(Server *server, float dtime, - core::array &dest); - - void GotBlock(v3s16 p); - - void SentBlock(v3s16 p); - - void SetBlockNotSent(v3s16 p); - void SetBlocksNotSent(core::map &blocks); - - s32 SendingCount() - { - return m_blocks_sending.size(); - } - // Increments timeouts and removes timed-out blocks from list // NOTE: This doesn't fix the server-not-sending-block bug // because it is related to emerging, not sending. @@ -376,12 +443,8 @@ class RemoteClient void PrintInfo(std::ostream &o) { o<<"RemoteClient "<20) + dstream<<"..."; + dstream<m_handle = client; + return socket; +} + +int TCPSocket::GetHandle() +{ + return m_handle; +} + +void TCPSocket::setTimeoutMs(int timeout_ms) +{ + m_timeout_ms = timeout_ms; +} + +bool TCPSocket::WaitData(int timeout_ms) +{ + fd_set readset; + int result; + + // Initialize the set + FD_ZERO(&readset); + FD_SET(m_handle, &readset); + + // Initialize time out struct + struct timeval tv; + tv.tv_sec = 0; + tv.tv_usec = timeout_ms * 1000; + // select() + result = select(m_handle+1, &readset, NULL, NULL, &tv); + + if(result == 0){ + // Timeout + /*dstream<<"Select timed out (timeout_ms=" + < &t) + SharedPtr(const SharedPtr &t) { - //*this = t; - drop(); refcount = t.refcount; (*refcount)++; ptr = t.ptr; @@ -54,7 +52,7 @@ class SharedPtr ptr = t; return *this; } - SharedPtr & operator=(SharedPtr &t) + SharedPtr & operator=(const SharedPtr &t) { drop(); refcount = t.refcount; @@ -62,23 +60,27 @@ class SharedPtr ptr = t.ptr; return *this; } - T* operator->() + T* get() const { return ptr; } - T & operator*() + T* operator->() const + { + return ptr; + } + T & operator*() const { return *ptr; } - bool operator!=(T *t) + bool operator!=(T *t) const { return ptr != t; } - bool operator==(T *t) + bool operator==(T *t) const { return ptr == t; } - T & operator[](unsigned int i) + T & operator[](unsigned int i) const { return ptr[i]; }