From 2f466726e6e8c318d3277eff6d987197c13e8bd3 Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Mon, 4 Apr 2011 02:05:12 +0300 Subject: [PATCH] Made a proper queued thread to client for handling some block mesh updates. Also made client mutex-free to allow easier adding of new stuff. --- src/client.cpp | 476 +++++++++++++++++++++++++++++++++-------------- src/client.h | 171 ++++++++++++++--- src/main.cpp | 16 +- src/map.cpp | 27 +-- src/mapblock.cpp | 374 ++++++++++++++++++------------------- src/mapblock.h | 39 +++- src/utility.h | 9 +- 7 files changed, 722 insertions(+), 390 deletions(-) diff --git a/src/client.cpp b/src/client.cpp index 357cbe3..e381fbc 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -26,24 +26,38 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include "porting.h" -void * ClientUpdateThread::Thread() +void * MeshUpdateThread::Thread() { ThreadStarted(); DSTACK(__FUNCTION_NAME); BEGIN_DEBUG_EXCEPTION_HANDLER - + while(getRun()) { - //m_client->asyncStep(); + QueuedMeshUpdate *q = m_queue_in.pop(); + if(q == NULL) + { + sleep_ms(50); + continue; + } - //m_client->updateSomeExpiredMeshes(); + scene::SMesh *mesh_new = NULL; + mesh_new = makeMapBlockMesh(q->data); - bool was = m_client->AsyncProcessData(); + MeshUpdateResult r; + r.p = q->p; + r.mesh = mesh_new; + r.ack_block_to_server = q->ack_block_to_server; - if(was == false) - sleep_ms(10); + /*dstream<<"MeshUpdateThread: Processed " + <<"("<p.X<<","<p.Y<<","<p.Z<<")" + <getSceneManager()->getRootSceneNode(), @@ -67,7 +81,6 @@ Client::Client( camera_position(0,0,0), camera_direction(0,0,1), m_server_ser_ver(SER_FMT_VER_INVALID), - m_step_dtime(0.0), m_inventory_updated(false), m_time_of_day(0) { @@ -77,19 +90,16 @@ Client::Client( m_avg_rtt_timer = 0.0; m_playerpos_send_timer = 0.0; - //m_fetchblock_mutex.Init(); - m_incoming_queue_mutex.Init(); - m_env_mutex.Init(); - m_con_mutex.Init(); - m_step_dtime_mutex.Init(); + //m_env_mutex.Init(); + //m_con_mutex.Init(); - m_thread.Start(); + m_mesh_update_thread.Start(); /* Add local player */ { - JMutexAutoLock envlock(m_env_mutex); + //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out Player *player = new LocalPlayer(); @@ -102,26 +112,26 @@ Client::Client( Client::~Client() { { - JMutexAutoLock conlock(m_con_mutex); + //JMutexAutoLock conlock(m_con_mutex); //bulk comment-out m_con.Disconnect(); } - m_thread.setRun(false); - while(m_thread.IsRunning()) + m_mesh_update_thread.setRun(false); + while(m_mesh_update_thread.IsRunning()) sleep_ms(100); } void Client::connect(Address address) { DSTACK(__FUNCTION_NAME); - JMutexAutoLock lock(m_con_mutex); + //JMutexAutoLock lock(m_con_mutex); //bulk comment-out m_con.setTimeoutMs(0); m_con.Connect(address); } bool Client::connectedAndInitialized() { - JMutexAutoLock lock(m_con_mutex); + //JMutexAutoLock lock(m_con_mutex); //bulk comment-out if(m_con.Connected() == false) return false; @@ -152,7 +162,7 @@ void Client::step(float dtime) { //TimeTaker timer("m_con_mutex + m_con.RunTimeouts()", m_device); // 0ms - JMutexAutoLock lock(m_con_mutex); + //JMutexAutoLock lock(m_con_mutex); //bulk comment-out m_con.RunTimeouts(dtime); } @@ -188,7 +198,7 @@ void Client::step(float dtime) //counter = 180.0; counter = 60.0; - JMutexAutoLock lock(m_env_mutex); + //JMutexAutoLock lock(m_env_mutex); //bulk comment-out core::list deleted_blocks; @@ -217,7 +227,7 @@ void Client::step(float dtime) */ // Env is locked so con can be locked. - JMutexAutoLock lock(m_con_mutex); + //JMutexAutoLock lock(m_con_mutex); //bulk comment-out core::list::Iterator i = deleted_blocks.begin(); core::list sendlist; @@ -271,7 +281,7 @@ void Client::step(float dtime) { counter = 2.0; - JMutexAutoLock envlock(m_env_mutex); + //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out Player *myplayer = m_env.getLocalPlayer(); assert(myplayer != NULL); @@ -299,7 +309,7 @@ void Client::step(float dtime) { // 0ms - JMutexAutoLock lock(m_env_mutex); + //JMutexAutoLock lock(m_env_mutex); //bulk comment-out // Control local player (0ms) LocalPlayer *player = m_env.getLocalPlayer(); @@ -335,7 +345,7 @@ void Client::step(float dtime) if(counter >= 10) { counter = 0.0; - JMutexAutoLock lock(m_con_mutex); + //JMutexAutoLock lock(m_con_mutex); //bulk comment-out // connectedAndInitialized() is true, peer exists. con::Peer *peer = m_con.GetPeer(PEER_ID_SERVER); dstream< 0) + { + MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_front(); + MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p); + if(block) + { + block->replaceMesh(r.mesh); + } + if(r.ack_block_to_server) + { + /* + 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], r.p); + // Send as reliable + m_con.Send(PEER_ID_SERVER, 1, reply, true); + } + } + } } -#endif // Virtual methods from con::PeerHandler void Client::peerAdded(con::Peer *peer) @@ -420,7 +449,7 @@ void Client::Receive() u32 datasize; { //TimeTaker t1("con mutex and receive", m_device); - JMutexAutoLock lock(m_con_mutex); + //JMutexAutoLock lock(m_con_mutex); //bulk comment-out datasize = m_con.Receive(sender_peer_id, *data, data_maxsize); } //TimeTaker t1("ProcessData", m_device); @@ -460,7 +489,7 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id) con::Peer *peer; { - JMutexAutoLock lock(m_con_mutex); + //JMutexAutoLock lock(m_con_mutex); //bulk comment-out // All data is coming from the server // PeerNotFoundException is handled by caller. peer = m_con.GetPeer(PEER_ID_SERVER); @@ -499,7 +528,7 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id) v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0); { //envlock - JMutexAutoLock envlock(m_env_mutex); + //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out // Set player position Player *player = m_env.getLocalPlayer(); @@ -569,13 +598,138 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id) addNode(p, n); } + else if(command == TOCLIENT_BLOCKDATA) + { + // Ignore too small packet + if(datasize < 8) + return; + + v3s16 p; + p.X = readS16(&data[2]); + p.Y = readS16(&data[4]); + p.Z = readS16(&data[6]); + + /*dout_client<getPos(); + if(sp != p2d) + { + dstream<<"ERROR: Got sector with getPos()=" + <<"("<getPos() == p2d); + + //TimeTaker timer("MapBlock deSerialize"); + // 0ms + + try{ + block = sector->getBlockNoCreate(p.Y); + /* + Update an existing block + */ + //dstream<<"Updating"<deSerialize(istr, ser_version); + //block->setChangedFlag(); + } + catch(InvalidPositionException &e) + { + /* + Create a new block + */ + //dstream<<"Creating new"<deSerialize(istr, ser_version); + sector->insertBlock(block); + //block->setChangedFlag(); + + //DEBUG + /*NodeMod mod; + mod.type = NODEMOD_CHANGECONTENT; + mod.param = CONTENT_MESE; + block->setTempMod(v3s16(8,10,8), mod); + block->setTempMod(v3s16(8,9,8), mod); + block->setTempMod(v3s16(8,8,8), mod); + block->setTempMod(v3s16(8,7,8), mod); + block->setTempMod(v3s16(8,6,8), mod);*/ +#if 0 + /* + Add some coulds + Well, this is a dumb way to do it, they should just + be drawn as separate objects. But the looks of them + can be tested this way. + */ + if(p.Y == 3) + { + NodeMod mod; + mod.type = NODEMOD_CHANGECONTENT; + mod.param = CONTENT_CLOUD; + v3s16 p2; + p2.Y = 8; + for(p2.X=3; p2.X<=13; p2.X++) + for(p2.Z=3; p2.Z<=13; p2.Z++) + { + block->setTempMod(p2, mod); + } + } +#endif + } + } //envlock + +#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 + + /* + Update Mesh of this block and blocks at x-, y- and z-. + Environment should not be locked as it interlocks with the + main thread, from which is will want to retrieve textures. + */ + + //m_env.getClientMap().updateMeshes(block->getPos(), getDayNightRatio()); + + addUpdateMeshTaskWithEdge(p, true); + } else if(command == TOCLIENT_PLAYERPOS) { dstream<<"WARNING: Received deprecated TOCLIENT_PLAYERPOS" <getPos() == p2d); + + //TimeTaker timer("MapBlock deSerialize"); + // 0ms try{ block = sector->getBlockNoCreate(p.Y); @@ -1248,13 +1412,14 @@ bool Client::AsyncProcessPacket() block->setTempMod(v3s16(8,8,8), mod); block->setTempMod(v3s16(8,7,8), mod); block->setTempMod(v3s16(8,6,8), mod);*/ - +#if 0 /* Add some coulds Well, this is a dumb way to do it, they should just - be drawn as separate objects. + be drawn as separate objects. But the looks of them + can be tested this way. */ - /*if(p.Y == 3) + if(p.Y == 3) { NodeMod mod; mod.type = NODEMOD_CHANGECONTENT; @@ -1266,7 +1431,8 @@ bool Client::AsyncProcessPacket() { block->setTempMod(p2, mod); } - }*/ + } +#endif } } //envlock @@ -1294,7 +1460,20 @@ bool Client::AsyncProcessPacket() main thread, from which is will want to retrieve textures. */ - m_env.getClientMap().updateMeshes(block->getPos(), getDayNightRatio()); + //m_env.getClientMap().updateMeshes(block->getPos(), getDayNightRatio()); + + MeshMakeData data; + { + //TimeTaker timer("data fill"); + // 0ms + data.fill(getDayNightRatio(), block); + } + { + TimeTaker timer("make mesh"); + scene::SMesh *mesh_new = NULL; + mesh_new = makeMapBlockMesh(&data); + block->replaceMesh(mesh_new); + } } else { @@ -1323,13 +1502,15 @@ bool Client::AsyncProcessData() } return false; } +#endif void Client::Send(u16 channelnum, SharedBuffer data, bool reliable) { - JMutexAutoLock lock(m_con_mutex); + //JMutexAutoLock lock(m_con_mutex); //bulk comment-out m_con.Send(PEER_ID_SERVER, channelnum, data, reliable); } +#if 0 IncomingPacket Client::getPacket() { JMutexAutoLock lock(m_incoming_queue_mutex); @@ -1349,6 +1530,7 @@ IncomingPacket Client::getPacket() m_incoming_queue.erase(i); return packet; } +#endif void Client::groundAction(u8 action, v3s16 nodepos_undersurface, v3s16 nodepos_oversurface, u16 item) @@ -1496,7 +1678,7 @@ void Client::sendChatMessage(const std::wstring &message) void Client::sendPlayerPos() { - JMutexAutoLock envlock(m_env_mutex); + //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out Player *myplayer = m_env.getLocalPlayer(); if(myplayer == NULL) @@ -1504,7 +1686,7 @@ void Client::sendPlayerPos() u16 our_peer_id; { - JMutexAutoLock lock(m_con_mutex); + //JMutexAutoLock lock(m_con_mutex); //bulk comment-out our_peer_id = m_con.GetPeerID(); } @@ -1543,7 +1725,7 @@ void Client::sendPlayerPos() void Client::removeNode(v3s16 p) { - JMutexAutoLock envlock(m_env_mutex); + //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out core::map modified_blocks; @@ -1567,7 +1749,7 @@ void Client::removeNode(v3s16 p) void Client::addNode(v3s16 p, MapNode n) { - JMutexAutoLock envlock(m_env_mutex); + //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out TimeTaker timer1("Client::addNode()"); @@ -1601,19 +1783,19 @@ void Client::updateCamera(v3f pos, v3f dir) MapNode Client::getNode(v3s16 p) { - JMutexAutoLock envlock(m_env_mutex); + //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out return m_env.getMap().getNode(p); } NodeMetadata* Client::getNodeMetadataClone(v3s16 p) { - JMutexAutoLock envlock(m_env_mutex); + //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out return m_env.getMap().getNodeMetadataClone(p); } v3f Client::getPlayerPosition() { - JMutexAutoLock envlock(m_env_mutex); + //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out LocalPlayer *player = m_env.getLocalPlayer(); assert(player != NULL); return player->getPosition(); @@ -1621,7 +1803,7 @@ v3f Client::getPlayerPosition() void Client::setPlayerControl(PlayerControl &control) { - JMutexAutoLock envlock(m_env_mutex); + //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out LocalPlayer *player = m_env.getLocalPlayer(); assert(player != NULL); player->control = control; @@ -1632,7 +1814,7 @@ void Client::setPlayerControl(PlayerControl &control) bool Client::getLocalInventoryUpdated() { // m_inventory_updated is behind envlock - JMutexAutoLock envlock(m_env_mutex); + //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out bool updated = m_inventory_updated; m_inventory_updated = false; return updated; @@ -1641,7 +1823,7 @@ bool Client::getLocalInventoryUpdated() // Copies the inventory of the local player to parameter void Client::getLocalInventory(Inventory &dst) { - JMutexAutoLock envlock(m_env_mutex); + //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out Player *player = m_env.getLocalPlayer(); assert(player != NULL); dst = player->inventory; @@ -1653,7 +1835,7 @@ MapBlockObject * Client::getSelectedObject( core::line3d shootline_on_map ) { - JMutexAutoLock envlock(m_env_mutex); + //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out core::array objects; @@ -1715,12 +1897,12 @@ MapBlockObject * Client::getSelectedObject( void Client::printDebugInfo(std::ostream &os) { //JMutexAutoLock lock1(m_fetchblock_mutex); - JMutexAutoLock lock2(m_incoming_queue_mutex); + /*JMutexAutoLock lock2(m_incoming_queue_mutex); os<<"m_incoming_queue.getSize()="<fill(getDayNightRatio(), b); } - u32 daynight_ratio = getDayNightRatio(); - - v3f playerpos = player->getPosition(); - v3f playerspeed = player->getSpeed(); - - v3s16 center_nodepos = floatToInt(playerpos, BS); - v3s16 center = getNodeBlockPos(center_nodepos); - - u32 counter = 0; - - s16 d_max = 5; + // Debug wait + //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10); - for(s16 d = 0; d <= d_max; d++) + // Add task to queue + m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server); + + /*dstream<<"Mesh update input queue size is " + < list; - getFacePositions(list, d); - - core::list::Iterator li; - for(li=list.begin(); li!=list.end(); li++) - { - v3s16 p = *li + center; - MapBlock *block = NULL; - try - { - //JMutexAutoLock envlock(m_env_mutex); - block = m_env.getMap().getBlockNoCreate(p); - } - catch(InvalidPositionException &e) - { - } - - if(block == NULL) - continue; - - if(block->getMeshExpired() == false) - continue; - - block->updateMesh(daynight_ratio); - - counter++; - if(counter >= 5) - return; - } + //TimeTaker timer("make mesh"); + // 10ms + scene::SMesh *mesh_new = NULL; + mesh_new = makeMapBlockMesh(data); + b->replaceMesh(mesh_new); + delete data; } -}*/ +#endif + + b->setMeshExpired(false); +} + +void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server) +{ + /*{ + v3s16 p = blockpos; + dstream<<"Client::addUpdateMeshTaskWithEdge(): " + <<"("<::Iterator i; + for(i=m_queue.begin(); i!=m_queue.end(); i++) + { + QueuedMeshUpdate *q = *i; + delete q; + } + } + + /* + peer_id=0 adds with nobody to send to + */ + void addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_server) + { + DSTACK(__FUNCTION_NAME); + + assert(data); + + JMutexAutoLock lock(m_mutex); + + /* + Find if block is already in queue. + If it is, update the data and quit. + */ + core::list::Iterator i; + for(i=m_queue.begin(); i!=m_queue.end(); i++) + { + QueuedMeshUpdate *q = *i; + if(q->p == p) + { + if(q->data) + delete q->data; + q->data = data; + if(ack_block_to_server) + q->ack_block_to_server = true; + return; + } + } + + /* + Add the block + */ + QueuedMeshUpdate *q = new QueuedMeshUpdate; + q->p = p; + q->data = data; + q->ack_block_to_server = ack_block_to_server; + m_queue.push_back(q); + } + + // Returned pointer must be deleted + // Returns NULL if queue is empty + QueuedMeshUpdate * pop() + { + JMutexAutoLock lock(m_mutex); + + core::list::Iterator i = m_queue.begin(); + if(i == m_queue.end()) + return NULL; + QueuedMeshUpdate *q = *i; + m_queue.erase(i); + return q; + } + + u32 size() + { + JMutexAutoLock lock(m_mutex); + return m_queue.size(); + } + +private: + core::list m_queue; + JMutex m_mutex; +}; + +struct MeshUpdateResult +{ + v3s16 p; + scene::SMesh *mesh; + bool ack_block_to_server; + + MeshUpdateResult(): + p(-1338,-1338,-1338), + mesh(NULL), + ack_block_to_server(false) + { + } +}; + +class MeshUpdateThread : public SimpleThread +{ public: - ClientUpdateThread(Client *client): - SimpleThread(), - m_client(client) + MeshUpdateThread() { } void * Thread(); + + MeshUpdateQueue m_queue_in; + + MutexedQueue m_queue_out; }; +#if 0 struct IncomingPacket { IncomingPacket() @@ -101,13 +222,15 @@ struct IncomingPacket u32 m_datalen; s32 *m_refcount; }; +#endif class Client : public con::PeerHandler { public: /* - NOTE: Every public method should be thread-safe + NOTE: Nothing is thread-safe here. */ + Client( IrrlichtDevice *device, const char *playername, @@ -147,7 +270,7 @@ public: void Send(u16 channelnum, SharedBuffer data, bool reliable); // Pops out a packet from the packet queue - IncomingPacket getPacket(); + //IncomingPacket getPacket(); void groundAction(u8 action, v3s16 nodepos_undersurface, v3s16 nodepos_oversurface, u16 item); @@ -196,7 +319,7 @@ public: void setTempMod(v3s16 p, NodeMod mod) { - JMutexAutoLock envlock(m_env_mutex); + //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out assert(m_env.getMap().mapType() == MAPTYPE_CLIENT); core::map affected_blocks; @@ -212,7 +335,7 @@ public: } void clearTempMod(v3s16 p) { - JMutexAutoLock envlock(m_env_mutex); + //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out assert(m_env.getMap().mapType() == MAPTYPE_CLIENT); core::map affected_blocks; @@ -229,7 +352,7 @@ public: float getAvgRtt() { - JMutexAutoLock lock(m_con_mutex); + //JMutexAutoLock lock(m_con_mutex); //bulk comment-out con::Peer *peer = m_con.GetPeerNoEx(PEER_ID_SERVER); if(peer == NULL) return 0.0; @@ -246,7 +369,7 @@ public: void addChatMessage(const std::wstring &message) { - JMutexAutoLock envlock(m_env_mutex); + //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out LocalPlayer *player = m_env.getLocalPlayer(); assert(player != NULL); std::wstring name = narrow_to_wide(player->getName()); @@ -256,6 +379,13 @@ public: u64 getMapSeed(){ return m_map_seed; } + /* + These are not thread-safe + */ + void addUpdateMeshTask(v3s16 blockpos, bool ack_to_server=false); + // Including blocks at appropriate edges + void addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server=false); + private: // Virtual methods from con::PeerHandler @@ -275,19 +405,11 @@ private: float m_avg_rtt_timer; float m_playerpos_send_timer; - ClientUpdateThread m_thread; + MeshUpdateThread m_mesh_update_thread; - // NOTE: If connection and environment are both to be locked, - // environment shall be locked first. - ClientEnvironment m_env; - JMutex m_env_mutex; con::Connection m_con; - JMutex m_con_mutex; - - core::list m_incoming_queue; - JMutex m_incoming_queue_mutex; IrrlichtDevice *m_device; @@ -297,9 +419,6 @@ private: // Server serialization version u8 m_server_ser_ver; - float m_step_dtime; - JMutex m_step_dtime_mutex; - // This is behind m_env_mutex. bool m_inventory_updated; @@ -308,7 +427,7 @@ private: PacketCounter m_packetcounter; // Received from the server. 0-23999 - MutexedVariable m_time_of_day; + u32 m_time_of_day; // 0 <= m_daynight_i < DAYNIGHT_CACHE_COUNT //s32 m_daynight_i; diff --git a/src/main.cpp b/src/main.cpp index a901163..3ddac36 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -482,8 +482,8 @@ Inventory local_inventory; u16 g_selected_item = 0; -bool g_show_map_plot = false; -bool g_refresh_map_plot = false; +/*bool g_show_map_plot = false; +bool g_refresh_map_plot = false;*/ /* Debug streams @@ -601,7 +601,7 @@ public: if(event.KeyInput.PressedDown) { //dstream<<"Pressed key: "<<(char)event.KeyInput.Key< - -/* - MapBlock -*/ - -MapBlock::MapBlock(NodeContainer *parent, v3s16 pos, bool dummy): - m_parent(parent), - m_pos(pos), - changed(true), - is_underground(false), - m_lighting_expired(true), - m_day_night_differs(false), - //m_not_fully_generated(false), - m_objects(this) +void MeshMakeData::fill(u32 daynight_ratio, MapBlock *block) { - data = NULL; - if(dummy == false) - reallocate(); + m_daynight_ratio = daynight_ratio; + m_blockpos = block->getPos(); + + v3s16 blockpos_nodes = m_blockpos*MAP_BLOCKSIZE; - m_spawn_timer = -10000; + /* + There is no harm not copying the TempMods of the neighbors + because they are already copied to this block + */ + m_temp_mods.clear(); + block->copyTempMods(m_temp_mods); + + /* + Copy data + */ -#ifndef SERVER - m_mesh_expired = false; - mesh_mutex.Init(); - mesh = NULL; - m_temp_mods_mutex.Init(); -#endif -} + // Allocate this block + neighbors + m_vmanip.clear(); + m_vmanip.addArea(VoxelArea(blockpos_nodes-v3s16(1,1,1)*MAP_BLOCKSIZE, + blockpos_nodes+v3s16(1,1,1)*MAP_BLOCKSIZE*2-v3s16(1,1,1))); -MapBlock::~MapBlock() -{ -#ifndef SERVER { - JMutexAutoLock lock(mesh_mutex); + //TimeTaker timer("copy central block data"); + // 0ms + + // Copy our data + block->copyTo(m_vmanip); + } + { + //TimeTaker timer("copy neighbor block data"); + // 0ms + + /* + Copy neighbors. This is lightning fast. + Copying only the borders would be *very* slow. + */ - if(mesh) + // Get map + NodeContainer *parentcontainer = block->getParent(); + // This will only work if the parent is the map + assert(parentcontainer->nodeContainerId() == NODECONTAINER_ID_MAP); + // OK, we have the map! + Map *map = (Map*)parentcontainer; + + for(u16 i=0; i<6; i++) { - mesh->drop(); - mesh = NULL; + const v3s16 &dir = g_6dirs[i]; + v3s16 bp = m_blockpos + dir; + MapBlock *b = map->getBlockNoCreateNoEx(bp); + if(b) + b->copyTo(m_vmanip); } } -#endif - - if(data) - delete[] data; -} - -bool MapBlock::isValidPositionParent(v3s16 p) -{ - if(isValidPosition(p)) - { - return true; - } - else{ - return m_parent->isValidPosition(getPosRelative() + p); - } -} - -MapNode MapBlock::getNodeParent(v3s16 p) -{ - if(isValidPosition(p) == false) - { - return m_parent->getNode(getPosRelative() + p); - } - else - { - if(data == NULL) - throw InvalidPositionException(); - return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X]; - } -} - -void MapBlock::setNodeParent(v3s16 p, MapNode & n) -{ - if(isValidPosition(p) == false) - { - m_parent->setNode(getPosRelative() + p, n); - } - else - { - if(data == NULL) - throw InvalidPositionException(); - data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X] = n; - } -} - -MapNode MapBlock::getNodeParentNoEx(v3s16 p) -{ - if(isValidPosition(p) == false) - { - try{ - return m_parent->getNode(getPosRelative() + p); - } - catch(InvalidPositionException &e) - { - return MapNode(CONTENT_IGNORE); - } - } - else - { - if(data == NULL) - { - return MapNode(CONTENT_IGNORE); - } - return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X]; - } } /* @@ -611,17 +560,15 @@ private: core::array m_prebuffers; }; -scene::SMesh* makeMapBlockMesh( - u32 daynight_ratio, - NodeModMap &temp_mods, - VoxelManipulator &vmanip, - v3s16 blockpos_nodes) +scene::SMesh* makeMapBlockMesh(MeshMakeData *data) { // 4-21ms for MAP_BLOCKSIZE=16 // 24-155ms for MAP_BLOCKSIZE=32 //TimeTaker timer1("makeMapBlockMesh()"); core::array fastfaces_new; + + v3s16 blockpos_nodes = data->m_blockpos*MAP_BLOCKSIZE; // floating point conversion v3f posRelative_f(blockpos_nodes.X, blockpos_nodes.Y, blockpos_nodes.Z); @@ -653,15 +600,15 @@ scene::SMesh* makeMapBlockMesh( */ for(s16 y=0; ym_daynight_ratio, posRelative_f, v3s16(0,y,z), MAP_BLOCKSIZE, v3s16(1,0,0), //dir v3f (1,0,0), v3s16(0,1,0), //face dir v3f (0,1,0), fastfaces_new, - temp_mods, - vmanip, + data->m_temp_mods, + data->m_vmanip, blockpos_nodes); } } @@ -670,15 +617,15 @@ scene::SMesh* makeMapBlockMesh( */ for(s16 x=0; xm_daynight_ratio, posRelative_f, v3s16(x,y,0), MAP_BLOCKSIZE, v3s16(0,0,1), v3f (0,0,1), v3s16(1,0,0), v3f (1,0,0), fastfaces_new, - temp_mods, - vmanip, + data->m_temp_mods, + data->m_vmanip, blockpos_nodes); } } @@ -687,15 +634,15 @@ scene::SMesh* makeMapBlockMesh( */ for(s16 z=0; zm_daynight_ratio, posRelative_f, v3s16(0,y,z), MAP_BLOCKSIZE, v3s16(1,0,0), v3f (1,0,0), v3s16(0,0,1), v3f (0,0,1), fastfaces_new, - temp_mods, - vmanip, + data->m_temp_mods, + data->m_vmanip, blockpos_nodes); } } @@ -781,7 +728,7 @@ scene::SMesh* makeMapBlockMesh( { v3s16 p(x,y,z); - MapNode &n = vmanip.getNodeRef(blockpos_nodes+p); + MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes+p); /* Add torches to mesh @@ -851,7 +798,7 @@ scene::SMesh* makeMapBlockMesh( */ if(n.d == CONTENT_SIGN_WALL) { - u8 l = decode_light(n.getLightBlend(daynight_ratio)); + u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio)); video::SColor c(255,l,l,l); float d = (float)BS/16; @@ -907,13 +854,11 @@ scene::SMesh* makeMapBlockMesh( else if(n.d == CONTENT_WATER) { bool top_is_water = false; - try{ - MapNode n = vmanip.getNode(blockpos_nodes + v3s16(x,y+1,z)); - if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE) - top_is_water = true; - }catch(InvalidPositionException &e){} + MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z)); + if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE) + top_is_water = true; - u8 l = decode_light(n.getLightBlend(daynight_ratio)); + u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio)); video::SColor c(WATER_ALPHA,l,l,l); // Neighbor water levels (key = relative position) @@ -938,11 +883,11 @@ scene::SMesh* makeMapBlockMesh( u8 content = CONTENT_AIR; float level = -0.5 * BS; u8 flags = 0; - try{ - // Check neighbor - v3s16 p2 = p + neighbor_dirs[i]; - MapNode n2 = vmanip.getNode(blockpos_nodes + p2); - + // Check neighbor + v3s16 p2 = p + neighbor_dirs[i]; + MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2); + if(n2.d != CONTENT_IGNORE) + { content = n2.d; if(n2.d == CONTENT_WATERSOURCE) @@ -955,11 +900,10 @@ scene::SMesh* makeMapBlockMesh( // NOTE: This doesn't get executed if neighbor // doesn't exist p2.Y += 1; - n2 = vmanip.getNode(blockpos_nodes + p2); + n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2); if(n2.d == CONTENT_WATERSOURCE || n2.d == CONTENT_WATER) flags |= neighborflag_top_is_water; } - catch(InvalidPositionException &e){} neighbor_levels.insert(neighbor_dirs[i], level); neighbor_contents.insert(neighbor_dirs[i], content); @@ -1169,20 +1113,18 @@ scene::SMesh* makeMapBlockMesh( { //bool top_is_water = false; bool top_is_air = false; - try{ - MapNode n = vmanip.getNode(blockpos_nodes + v3s16(x,y+1,z)); - /*if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE) - top_is_water = true;*/ - if(n.d == CONTENT_AIR) - top_is_air = true; - }catch(InvalidPositionException &e){} + MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z)); + /*if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE) + top_is_water = true;*/ + if(n.d == CONTENT_AIR) + top_is_air = true; /*if(top_is_water == true) continue;*/ if(top_is_air == false) continue; - u8 l = decode_light(n.getLightBlend(daynight_ratio)); + u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio)); video::SColor c(WATER_ALPHA,l,l,l); video::S3DVertex vertices[4] = @@ -1216,8 +1158,8 @@ scene::SMesh* makeMapBlockMesh( */ else if(n.d == CONTENT_LEAVES && new_style_leaves) { - /*u8 l = decode_light(n.getLightBlend(daynight_ratio));*/ - u8 l = decode_light(undiminish_light(n.getLightBlend(daynight_ratio))); + /*u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));*/ + u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio))); video::SColor c(255,l,l,l); for(u32 j=0; j<6; j++) @@ -1331,13 +1273,113 @@ scene::SMesh* makeMapBlockMesh( //std::cout<<"added "<drop(); + mesh = NULL; + } + } +#endif + + if(data) + delete[] data; +} + +bool MapBlock::isValidPositionParent(v3s16 p) +{ + if(isValidPosition(p)) + { + return true; + } + else{ + return m_parent->isValidPosition(getPosRelative() + p); + } +} + +MapNode MapBlock::getNodeParent(v3s16 p) +{ + if(isValidPosition(p) == false) + { + return m_parent->getNode(getPosRelative() + p); + } + else + { + if(data == NULL) + throw InvalidPositionException(); + return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X]; + } +} + +void MapBlock::setNodeParent(v3s16 p, MapNode & n) +{ + if(isValidPosition(p) == false) + { + m_parent->setNode(getPosRelative() + p, n); + } + else + { + if(data == NULL) + throw InvalidPositionException(); + data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X] = n; + } +} + +MapNode MapBlock::getNodeParentNoEx(v3s16 p) +{ + if(isValidPosition(p) == false) + { + try{ + return m_parent->getNode(getPosRelative() + p); + } + catch(InvalidPositionException &e) + { + return MapNode(CONTENT_IGNORE); + } + } + else + { + if(data == NULL) + { + return MapNode(CONTENT_IGNORE); + } + return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X]; + } +} #if 1 void MapBlock::updateMesh(u32 daynight_ratio) @@ -1353,52 +1395,10 @@ void MapBlock::updateMesh(u32 daynight_ratio) } #endif - /* - Avoid interlocks by copying m_temp_mods - */ - NodeModMap temp_mods; - copyTempMods(temp_mods); + MeshMakeData data; + data.fill(daynight_ratio, this); - v3s16 blockpos_nodes = getPosRelative(); - - VoxelManipulator vmanip; - // Allocate this block + borders - vmanip.addArea(VoxelArea(blockpos_nodes-v3s16(1,1,1), - blockpos_nodes+v3s16(1,1,1)*MAP_BLOCKSIZE)); - // Copy our data - copyTo(vmanip); - // Copy borders from map - // +-Z - for(s16 x=-1; x<=MAP_BLOCKSIZE; x++) - for(s16 y=-1; y<=MAP_BLOCKSIZE; y++) - for(s16 z=-1; z<=MAP_BLOCKSIZE; z+=MAP_BLOCKSIZE+1) - { - v3s16 p(x,y,z); - vmanip.setNodeNoRef(blockpos_nodes+p, getNodeParentNoEx(p)); - } - // +-Y - for(s16 x=-1; x<=MAP_BLOCKSIZE; x++) - for(s16 y=-1; y<=MAP_BLOCKSIZE; y+=MAP_BLOCKSIZE+1) - for(s16 z=-1; z<=MAP_BLOCKSIZE; z++) - { - v3s16 p(x,y,z); - vmanip.setNodeNoRef(blockpos_nodes+p, getNodeParentNoEx(p)); - } - // +-Z - for(s16 x=-1; x<=MAP_BLOCKSIZE; x+=MAP_BLOCKSIZE+1) - for(s16 y=-1; y<=MAP_BLOCKSIZE; y++) - for(s16 z=-1; z<=MAP_BLOCKSIZE; z++) - { - v3s16 p(x,y,z); - vmanip.setNodeNoRef(blockpos_nodes+p, getNodeParentNoEx(p)); - } - - scene::SMesh *mesh_new = makeMapBlockMesh( - daynight_ratio, - temp_mods, - vmanip, - getPosRelative() - ); + scene::SMesh *mesh_new = makeMapBlockMesh(&data); /* Replace the mesh diff --git a/src/mapblock.h b/src/mapblock.h index 1894269..87abf6a 100644 --- a/src/mapblock.h +++ b/src/mapblock.h @@ -156,20 +156,42 @@ public: virtual MapNode getNode(v3s16 p) = 0; virtual void setNode(v3s16 p, MapNode & n) = 0; virtual u16 nodeContainerId() const = 0; + + MapNode getNodeNoEx(v3s16 p) + { + try{ + return getNode(p); + } + catch(InvalidPositionException &e){ + return MapNode(CONTENT_IGNORE); + } + } }; /* - Plain functions in mapblock.cpp + Mesh making stuff */ +class MapBlock; + +struct MeshMakeData +{ + u32 m_daynight_ratio; + NodeModMap m_temp_mods; + VoxelManipulator m_vmanip; + v3s16 m_blockpos; + + /* + Copy central data directly from block, and other data from + parent of block. + */ + void fill(u32 daynight_ratio, MapBlock *block); +}; + u8 getFaceLight(u32 daynight_ratio, MapNode n, MapNode n2, v3s16 face_dir); -scene::SMesh* makeMapBlockMesh( - u32 daynight_ratio, - NodeModMap &temp_mods, - VoxelManipulator &vmanip, - v3s16 blockpos_nodes); +scene::SMesh* makeMapBlockMesh(MeshMakeData *data); /* MapBlock itself @@ -185,7 +207,7 @@ public: { return NODECONTAINER_ID_MAPBLOCK; } - + NodeContainer * getParent() { return m_parent; @@ -661,8 +683,7 @@ private: Private member variables */ - // Parent container (practically the Map) - // Not a MapSector, it is just a structural element. + // NOTE: Lots of things rely on this being the Map NodeContainer *m_parent; // Position in blocks on parent v3s16 m_pos; diff --git a/src/utility.h b/src/utility.h index 89c5f99..3640b4b 100644 --- a/src/utility.h +++ b/src/utility.h @@ -1467,6 +1467,10 @@ protected: core::list m_list; }; +/* + A single worker thread - multiple client threads queue framework. +*/ + template class CallerInfo { @@ -1516,11 +1520,6 @@ public: core::list > callers; }; -/* - Quickhands for typical request-result queues. - Used for distributing work between threads. -*/ - template class RequestQueue {