X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Fclient.cpp;h=2148bfe59b438749d52dcac8ed668c3f6e5d080f;hb=569fca53089b7b7e87b02edd44e2ad47166f7af6;hp=e494056f27dcaeaeb56b75e4082df1423a32012d;hpb=75f3e6845187b539a82f8a5e66c2aa48caa0dca8;p=dragonfireclient.git diff --git a/src/client.cpp b/src/client.cpp index e494056f2..2148bfe59 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -25,33 +25,185 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "main.h" #include #include "porting.h" +#include "mapsector.h" +#include "mapblock_mesh.h" +#include "mapblock.h" +#include "settings.h" +#include "profiler.h" +#include "log.h" +#include "nodemetadata.h" +#include "nodedef.h" +#include "itemdef.h" +#include +#include "sha1.h" +#include "base64.h" +#include "clientmap.h" + +static std::string getTextureCacheDir() +{ + return porting::path_user + DIR_DELIM + "cache" + DIR_DELIM + "textures"; +} + +struct TextureRequest +{ + std::string name; + + TextureRequest(const std::string &name_=""): + name(name_) + {} +}; + +/* + QueuedMeshUpdate +*/ + +QueuedMeshUpdate::QueuedMeshUpdate(): + p(-1337,-1337,-1337), + data(NULL), + ack_block_to_server(false) +{ +} + +QueuedMeshUpdate::~QueuedMeshUpdate() +{ + if(data) + delete data; +} + +/* + MeshUpdateQueue +*/ + +MeshUpdateQueue::MeshUpdateQueue() +{ + m_mutex.Init(); +} + +MeshUpdateQueue::~MeshUpdateQueue() +{ + JMutexAutoLock lock(m_mutex); + + for(std::vector::iterator + i = m_queue.begin(); + i != m_queue.end(); i++) + { + QueuedMeshUpdate *q = *i; + delete q; + } +} + +/* + peer_id=0 adds with nobody to send to +*/ +void MeshUpdateQueue::addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_server, bool urgent) +{ + DSTACK(__FUNCTION_NAME); + + assert(data); + + JMutexAutoLock lock(m_mutex); + + if(urgent) + m_urgents.insert(p); + + /* + Find if block is already in queue. + If it is, update the data and quit. + */ + for(std::vector::iterator + 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 * MeshUpdateQueue::pop() +{ + JMutexAutoLock lock(m_mutex); + + bool must_be_urgent = !m_urgents.empty(); + for(std::vector::iterator + i = m_queue.begin(); + i != m_queue.end(); i++) + { + QueuedMeshUpdate *q = *i; + if(must_be_urgent && m_urgents.count(q->p) == 0) + continue; + m_queue.erase(i); + m_urgents.erase(q->p); + return q; + } + return NULL; +} + +/* + MeshUpdateThread +*/ void * MeshUpdateThread::Thread() { ThreadStarted(); + log_register_thread("MeshUpdateThread"); + DSTACK(__FUNCTION_NAME); BEGIN_DEBUG_EXCEPTION_HANDLER while(getRun()) { + /*// Wait for output queue to flush. + // Allow 2 in queue, this makes less frametime jitter. + // Umm actually, there is no much difference + if(m_queue_out.size() >= 2) + { + sleep_ms(3); + continue; + }*/ + QueuedMeshUpdate *q = m_queue_in.pop(); if(q == NULL) { - sleep_ms(50); + sleep_ms(3); continue; } - scene::SMesh *mesh_new = NULL; - mesh_new = makeMapBlockMesh(q->data); + ScopeProfiler sp(g_profiler, "Client: Mesh making"); + + MapBlockMesh *mesh_new = new MapBlockMesh(q->data); + if(mesh_new->getMesh()->getMeshBufferCount() == 0) + { + delete mesh_new; + mesh_new = NULL; + } MeshUpdateResult r; r.p = q->p; r.mesh = mesh_new; r.ack_block_to_server = q->ack_block_to_server; - /*dstream<<"MeshUpdateThread: Processed " + /*infostream<<"MeshUpdateThread: Processed " <<"("<p.X<<","<p.Y<<","<p.Z<<")" <getSceneManager()->getRootSceneNode(), device->getSceneManager(), 666), - device->getSceneManager() + device->getSceneManager(), + tsrc, this, device ), m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this), m_device(device), - camera_position(0,0,0), - camera_direction(0,0,1), m_server_ser_ver(SER_FMT_VER_INVALID), + m_playeritem(0), m_inventory_updated(false), - m_time_of_day(0), + m_inventory_from_server(NULL), + m_inventory_from_server_age(0.0), + m_animation_time(0), + m_crack_level(-1), + m_crack_pos(0,0,0), m_map_seed(0), m_password(password), - m_access_denied(false) + m_access_denied(false), + m_texture_receive_progress(0), + m_textures_received(false), + m_itemdef_received(false), + m_nodedef_received(false), + m_time_of_day_set(false), + m_last_time_of_day_f(-1), + m_time_of_day_update_timer(0) { m_packetcounter_timer = 0.0; - m_delete_unused_sectors_timer = 0.0; + //m_delete_unused_sectors_timer = 0.0; m_connection_reinit_timer = 0.0; m_avg_rtt_timer = 0.0; m_playerpos_send_timer = 0.0; m_ignore_damage_timer = 0.0; - //m_env_mutex.Init(); - //m_con_mutex.Init(); - - m_mesh_update_thread.Start(); - + // Build main texture atlas, now that the GameDef exists (that is, us) + if(g_settings->getBool("enable_texture_atlas")) + m_tsrc->buildMainAtlas(this); + else + infostream<<"Not building texture atlas."<updateName(playername); m_env.addPlayer(player); - - // Initialize player in the inventory context - m_inventory_context.current_player = player; } } @@ -127,13 +293,15 @@ Client::~Client() m_mesh_update_thread.setRun(false); while(m_mesh_update_thread.IsRunning()) sleep_ms(100); + + delete m_inventory_from_server; } void Client::connect(Address address) { DSTACK(__FUNCTION_NAME); //JMutexAutoLock lock(m_con_mutex); //bulk comment-out - m_con.setTimeoutMs(0); + m_con.SetTimeoutMs(0); m_con.Connect(address); } @@ -163,7 +331,13 @@ void Client::step(float dtime) else m_ignore_damage_timer = 0.0; - //dstream<<"Client steps "< 60.0) + m_animation_time -= 60.0; + + m_time_of_day_update_timer += dtime; + + //infostream<<"Client steps "< deleted_blocks; float delete_unused_sectors_timeout = - g_settings.getFloat("client_delete_unused_sectors_timeout"); + g_settings->getFloat("client_delete_unused_sectors_timeout"); // Delete sector blocks - /*u32 num = m_env.getMap().deleteUnusedSectors + /*u32 num = m_env.getMap().unloadUnusedData (delete_unused_sectors_timeout, true, &deleted_blocks);*/ // Delete whole sectors - u32 num = m_env.getMap().deleteUnusedSectors + m_env.getMap().unloadUnusedData (delete_unused_sectors_timeout, - false, &deleted_blocks); + &deleted_blocks); - if(num > 0) + if(deleted_blocks.size() > 0) { - /*dstream< data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE); + // [23] u8[28] password (new in some version) + // [51] u16 client network protocol version (new in some version) + SharedBuffer data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2); writeU16(&data[0], TOSERVER_INIT); writeU8(&data[2], SER_FMT_VER_HIGHEST); memset((char*)&data[3], 0, PLAYERNAME_SIZE); snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName()); - /*dstream<<"Client: password hash is \""< deleted_blocks; + m_env.getMap().timerUpdate(map_timer_and_unload_dtime, + g_settings->getFloat("client_unload_unused_data_timeout"), + &deleted_blocks); + + /*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++; + } + } + /* Handle environment */ @@ -343,25 +585,7 @@ void Client::step(float dtime) //TimeTaker envtimer("env step", m_device); // Step environment m_env.step(dtime); - - // Step active blocks - for(core::map::Iterator - i = m_active_blocks.getIterator(); - i.atEnd() == false; i++) - { - v3s16 p = i.getNode()->getKey(); - - MapBlock *block = NULL; - try - { - block = m_env.getMap().getBlockNoCreate(p); - block->stepObjects(dtime, false, m_env.getDayNightRatio()); - } - catch(InvalidPositionException &e) - { - } - } - + /* Get events */ @@ -377,7 +601,9 @@ void Client::step(float dtime) if(m_ignore_damage_timer <= 0) { u8 damage = event.player_damage.amount; - sendDamage(damage); + + if(event.player_damage.send_to_server) + sendDamage(damage); // Add to ClientEvent queue ClientEvent event; @@ -400,8 +626,8 @@ void Client::step(float dtime) counter = 0.0; //JMutexAutoLock lock(m_con_mutex); //bulk comment-out // connectedAndInitialized() is true, peer exists. - con::Peer *peer = m_con.GetPeer(PEER_ID_SERVER); - dstream<replaceMesh(r.mesh); + //JMutexAutoLock lock(block->mesh_mutex); + + // Delete the old mesh + if(block->mesh != NULL) + { + // TODO: Remove hardware buffers of meshbuffers of block->mesh + delete block->mesh; + block->mesh = NULL; + } + + // Replace with the new mesh + block->mesh = r.mesh; } if(r.ack_block_to_server) { - /*dstream<<"Client: ACK block ("< seconds after TOCLIENT_INVENTORY + // Reset the locally changed inventory to the authoritative inventory + Player *player = m_env.getLocalPlayer(); + player->inventory = *m_inventory_from_server; + m_inventory_updated = true; + } + } } // Virtual methods from con::PeerHandler void Client::peerAdded(con::Peer *peer) { - derr_client<<"Client::peerAdded(): peer->id=" + infostream<<"Client::peerAdded(): peer->id=" <id< start_ms + 100) + break; + try{ Receive(); } @@ -492,7 +759,7 @@ void Client::ReceiveAll() } catch(con::InvalidIncomingDataException &e) { - dout_client<getPos(); - if(sp != p2d) - { - dstream<<"ERROR: Got sector with getPos()=" - <<"("<getPos() == p2d); + v2s16 p2d(p.X, p.Z); + sector = m_env.getMap().emergeSector(p2d); + + assert(sector->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 + //TimeTaker timer("MapBlock deSerialize"); + // 0ms + + block = sector->getBlockNoCreateNoEx(p.Y); + if(block) + { + /* + Update an existing block + */ + //infostream<<"Updating"<deSerialize(istr, ser_version, false); + } + else + { + /* + Create a new block + */ + //infostream<<"Creating new"<deSerialize(istr, ser_version, false); + sector->insertBlock(block); + } #if 0 /* @@ -787,475 +997,119 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id) #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. + Add it to mesh update queue and set it to be acknowledged after update. */ - - //m_env.getClientMap().updateMeshes(block->getPos(), getDayNightRatio()); - + //infostream<<"Adding mesh update task for received block"<inventory.deSerialize(is); + //t1.stop(); - // Skip if player doesn't exist - if(player == NULL) - { - start += player_size; - continue; - } + m_inventory_updated = true; - // Skip if player is local player - if(player->isLocal()) - { - start += player_size; - continue; - } + delete m_inventory_from_server; + m_inventory_from_server = new Inventory(player->inventory); + m_inventory_from_server_age = 0.0; - v3s32 ps = readV3S32(&data[start+2]); - v3s32 ss = readV3S32(&data[start+2+12]); - s32 pitch_i = readS32(&data[start+2+12+12]); - s32 yaw_i = readS32(&data[start+2+12+12+4]); - /*dstream<<"Client: got " - <<"pitch_i="<inventory.print(infostream); + } } - else if(command == TOCLIENT_PLAYERINFO) + else if(command == TOCLIENT_TIME_OF_DAY) { - u16 our_peer_id; - { - //JMutexAutoLock lock(m_con_mutex); //bulk comment-out - our_peer_id = m_con.GetPeerID(); - } - // Cancel if we don't have a peer id - if(our_peer_id == PEER_ID_INEXISTENT){ - dout_client< players_alive; - for(u32 i=0; iupdateName((char*)&data[start+2]); - - start += item_size; - } - - /* - Remove those players from the environment that - weren't listed by the server. - */ - //dstream< players = m_env.getPlayers(); - core::list::Iterator ip; - for(ip=players.begin(); ip!=players.end(); ip++) - { - // Ingore local player - if((*ip)->isLocal()) - continue; - - // Warn about a special case - if((*ip)->peer_id == 0) - { - dstream<::Iterator i; - for(i=players_alive.begin(); i!=players_alive.end(); i++) - { - if((*ip)->peer_id == *i) - { - is_alive = true; - break; - } - } - /*dstream<peer_id) - <<" is_alive="<peer_id - <peer_id); - } - } //envlock - } - else if(command == TOCLIENT_SECTORMETA) - { - /* - [0] u16 command - [2] u8 sector count - [3...] v2s16 pos + sector metadata - */ - if(datasize < 3) - return; - - //dstream<<"Client received TOCLIENT_SECTORMETA"<inventory.deSerialize(is); - //t1.stop(); - - m_inventory_updated = true; - - //dstream<<"Client got player inventory:"<inventory.print(dstream); - } - } - //DEBUG - else if(command == TOCLIENT_OBJECTDATA) - //else if(0) - { - // Strip command word and create a stringstream - std::string datastring((char*)&data[2], datasize-2); - std::istringstream is(datastring, std::ios_base::binary); - - { //envlock - - //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out - - u8 buf[12]; - - /* - Read players - */ - - is.read((char*)buf, 2); - u16 playercount = readU16(buf); - - for(u16 i=0; iisLocal()) - { - continue; - } - - f32 pitch = (f32)pitch_i / 100.0; - f32 yaw = (f32)yaw_i / 100.0; - v3f position((f32)p_i.X/100., (f32)p_i.Y/100., (f32)p_i.Z/100.); - v3f speed((f32)s_i.X/100., (f32)s_i.Y/100., (f32)s_i.Z/100.); - - player->setPosition(position); - player->setSpeed(speed); - player->setPitch(pitch); - player->setYaw(yaw); - } - - /* - Read block objects - */ - - // Read active block count - is.read((char*)buf, 2); - u16 blockcount = readU16(buf); - - // Initialize delete queue with all active blocks - core::map abs_to_delete; - for(core::map::Iterator - i = m_active_blocks.getIterator(); - i.atEnd() == false; i++) - { - v3s16 p = i.getNode()->getKey(); - /*dstream<<"adding " - <<"("<updateObjects(is, m_server_ser_ver, - m_device->getSceneManager(), m_env.getDayNightRatio()); - } - - /*dstream<<"Final delete queue size: "<::Iterator - i = abs_to_delete.getIterator(); - i.atEnd() == false; i++) - { - v3s16 p = i.getNode()->getKey(); - try - { - MapBlock *block = m_env.getMap().getBlockNoCreate(p); - - // Clear objects - block->clearObjects(); - // Remove from active blocks list - m_active_blocks.remove(p); - } - catch(InvalidPositionException &e) - { - dstream<<"WARNAING: Client: " - <<"Couldn't clear objects of active->inactive" - <<" block " - <<"("<getBool("enable_experimental")) { /* u16 command @@ -1267,7 +1121,7 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id) for all added objects { u16 id u8 type - u16 initialization data length + u32 initialization data length string initialization data } */ @@ -1314,7 +1168,7 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id) } else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES) { - //if(g_settings.getBool("enable_experimental")) + //if(g_settings->getBool("enable_experimental")) { /* u16 command @@ -1361,8 +1215,18 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id) std::istringstream is(datastring, std::ios_base::binary); Player *player = m_env.getLocalPlayer(); assert(player != NULL); + u8 oldhp = player->hp; u8 hp = readU8(is); player->hp = hp; + + if(hp < oldhp) + { + // Add to ClientEvent queue + ClientEvent event; + event.type = CE_PLAYER_DAMAGE; + event.player_damage.amount = oldhp - hp; + m_client_event_queue.push_back(event); + } } else if(command == TOCLIENT_MOVE_PLAYER) { @@ -1377,7 +1241,7 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id) /*player->setPitch(pitch); player->setYaw(yaw);*/ - dstream<<"Client got TOCLIENT_MOVE_PLAYER" + infostream<<"Client got TOCLIENT_MOVE_PLAYER" <<" pos=("<isLocal()) { + infostream<<"Client: ignoring player item " + << deSerializeString(is) + << " for local player" << std::endl; + continue; + } else { + InventoryList *inv = player->inventory.getList("main"); + std::string itemstring(deSerializeString(is)); + ItemStack item; + item.deSerialize(itemstring, m_itemdef); + inv->changeItem(0, item); + if(itemstring.empty()) + { + infostream<<"Client: empty player item for peer " + <getFileSystem(); + video::IVideoDriver *vdrv = m_device->getVideoDriver(); -void Client::Send(u16 channelnum, SharedBuffer data, bool reliable) -{ - //JMutexAutoLock lock(m_con_mutex); //bulk comment-out - m_con.Send(PEER_ID_SERVER, channelnum, data, reliable); -} + std::string datastring((char*)&data[2], datasize-2); + std::istringstream is(datastring, std::ios_base::binary); -void Client::groundAction(u8 action, v3s16 nodepos_undersurface, - v3s16 nodepos_oversurface, u16 item) -{ - if(connectedAndInitialized() == false){ - dout_client< texture_requests; + + for(int i=0; i data_rw(tmp_os.str().c_str(), tmp_os.str().size()); + + // Create an irrlicht memory file + io::IReadFile *rfile = irrfs->createMemoryReadFile( + *data_rw, tmp_os.str().size(), "_tempreadfile"); + assert(rfile); + // Read image + video::IImage *img = vdrv->createImageFromFile(rfile); + if(!img){ + infostream<<"Client: Cannot create image from data of " + <<"received texture \""<drop(); + } + else { + m_tsrc->insertSourceImage(name, img); + img->drop(); + rfile->drop(); + + texture_found = true; + } + } + else { + infostream<<"Client::Texture cached sha1 hash not matching server hash: " + <"< "<::Iterator i = texture_requests.begin(); + i != texture_requests.end(); i++) { + os<name); + } + + // Make data buffer + std::string s = os.str(); + SharedBuffer data((u8*)s.c_str(), s.size()); + // Send as reliable + Send(0, data, true); + infostream<<"Client: Sending request list to server " <getFileSystem(); + video::IVideoDriver *vdrv = m_device->getVideoDriver(); + + std::string datastring((char*)&data[2], datasize-2); + std::istringstream is(datastring, std::ios_base::binary); + + // Mesh update thread must be stopped while + // updating content definitions + assert(!m_mesh_update_thread.IsRunning()); + + /* + u16 command + u16 total number of texture bunches + u16 index of this bunch + u32 number of textures in this bunch + for each texture { + u16 length of name + string name + u32 length of data + data + } + */ + int num_bunches = readU16(is); + int bunch_i = readU16(is); + m_texture_receive_progress = (float)bunch_i / (float)(num_bunches - 1); + if(bunch_i == num_bunches - 1) + m_textures_received = true; + int num_textures = readU32(is); + infostream<<"Client: Received textures: bunch "< data_rw(data.c_str(), data.size()); + // Create an irrlicht memory file + io::IReadFile *rfile = irrfs->createMemoryReadFile( + *data_rw, data.size(), "_tempreadfile"); + assert(rfile); + // Read image + video::IImage *img = vdrv->createImageFromFile(rfile); + if(!img){ + errorstream<<"Client: Cannot create image from data of " + <<"received texture \""<drop(); + continue; + } + + fs::CreateAllDirs(getTextureCacheDir()); + + std::string filename = getTextureCacheDir() + DIR_DELIM + name; + std::ofstream outfile(filename.c_str(), std::ios_base::binary | std::ios_base::trunc); + + if (outfile.good()) { + outfile.write(data.c_str(),data.length()); + outfile.close(); + } + else { + errorstream<<"Client: Unable to open cached texture file "<< filename <insertSourceImage(name, img); + img->drop(); + rfile->drop(); + } + + ClientEvent event; + event.type = CE_TEXTURES_UPDATED; + m_client_event_queue.push_back(event); + } + else if(command == TOCLIENT_TOOLDEF) + { + infostream<<"Client: WARNING: Ignoring TOCLIENT_TOOLDEF"<deSerialize(tmp_is2); + m_nodedef_received = true; + } + else if(command == TOCLIENT_CRAFTITEMDEF) + { + infostream<<"Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF"<deSerialize(tmp_is2); + m_itemdef_received = true; + } + else + { + infostream<<"Client: Ignoring unknown command " + < data(datasize); - writeU16(&data[0], TOSERVER_GROUND_ACTION); - writeU8(&data[2], action); - writeV3S16(&data[3], nodepos_undersurface); - writeV3S16(&data[9], nodepos_oversurface); - writeU16(&data[15], item); - Send(0, data, true); } -void Client::clickObject(u8 button, v3s16 blockpos, s16 id, u16 item) +void Client::Send(u16 channelnum, SharedBuffer data, bool reliable) { - if(connectedAndInitialized() == false){ - dout_client< data(datasize); - writeU16(&data[0], TOSERVER_CLICK_OBJECT); - writeU8(&data[2], button); - writeV3S16(&data[3], blockpos); - writeS16(&data[9], id); - writeU16(&data[11], item); - Send(0, data, true); + //JMutexAutoLock lock(m_con_mutex); //bulk comment-out + m_con.Send(PEER_ID_SERVER, channelnum, data, reliable); } -void Client::clickActiveObject(u8 button, u16 id, u16 item) +void Client::interact(u8 action, const PointedThing& pointed) { if(connectedAndInitialized() == false){ - dout_client< data(datasize); - writeU16(&data[0], TOSERVER_CLICK_ACTIVEOBJECT); - writeU8(&data[2], button); - writeU16(&data[3], id); - writeU16(&data[5], item); - Send(0, data, true); -} -void Client::sendSignText(v3s16 blockpos, s16 id, std::string text) -{ - /* - u16 command - v3s16 blockpos - s16 id - u16 textlen - textdata - */ std::ostringstream os(std::ios_base::binary); - u8 buf[12]; - - // Write command - writeU16(buf, TOSERVER_SIGNTEXT); - os.write((char*)buf, 2); - - // Write blockpos - writeV3S16(buf, blockpos); - os.write((char*)buf, 6); - - // Write id - writeS16(buf, id); - os.write((char*)buf, 2); - u16 textlen = text.size(); - // Write text length - writeS16(buf, textlen); - os.write((char*)buf, 2); + /* + [0] u16 command + [2] u8 action + [3] u16 item + [5] u32 length of the next item + [9] serialized PointedThing + actions: + 0: start digging (from undersurface) or use + 1: stop digging (all parameters ignored) + 2: digging completed + 3: place block or item (to abovesurface) + 4: use item + */ + writeU16(os, TOSERVER_INTERACT); + writeU8(os, action); + writeU16(os, getPlayerItem()); + std::ostringstream tmp_os(std::ios::binary); + pointed.serialize(tmp_os); + os< data((u8*)s.c_str(), s.size()); + // Send as reliable Send(0, data, true); } - + void Client::sendSignNodeText(v3s16 p, std::string text) { /* @@ -1667,6 +1783,20 @@ void Client::sendDamage(u8 damage) Send(0, data, true); } +void Client::sendRespawn() +{ + DSTACK(__FUNCTION_NAME); + std::ostringstream os(std::ios_base::binary); + + writeU16(os, TOSERVER_RESPAWN); + + // Make data buffer + std::string s = os.str(); + SharedBuffer data((u8*)s.c_str(), s.size()); + // Send as reliable + Send(0, data, true); +} + void Client::sendPlayerPos() { //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out @@ -1714,10 +1844,30 @@ void Client::sendPlayerPos() Send(0, data, false); } +void Client::sendPlayerItem(u16 item) +{ + Player *myplayer = m_env.getLocalPlayer(); + if(myplayer == NULL) + return; + + u16 our_peer_id = m_con.GetPeerID(); + + // Set peer id if not set already + if(myplayer->peer_id == PEER_ID_INEXISTENT) + myplayer->peer_id = our_peer_id; + // Check that an existing peer_id is the same as the connection's + assert(myplayer->peer_id == our_peer_id); + + SharedBuffer data(2+2); + writeU16(&data[0], TOSERVER_PLAYERITEM); + writeU16(&data[2], item); + + // Send as reliable + Send(0, data, true); +} + void Client::removeNode(v3s16 p) { - //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out - core::map modified_blocks; try @@ -1729,76 +1879,55 @@ void Client::removeNode(v3s16 p) { } + // add urgent task to update the modified node + addUpdateMeshTaskForNode(p, false, true); + for(core::map::Iterator i = modified_blocks.getIterator(); i.atEnd() == false; i++) { v3s16 p = i.getNode()->getKey(); - //m_env.getClientMap().updateMeshes(p, m_env.getDayNightRatio()); addUpdateMeshTaskWithEdge(p); } } void Client::addNode(v3s16 p, MapNode n) { - //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out - TimeTaker timer1("Client::addNode()"); core::map modified_blocks; try { - TimeTaker timer3("Client::addNode(): addNodeAndUpdate"); + //TimeTaker timer3("Client::addNode(): addNodeAndUpdate"); m_env.getMap().addNodeAndUpdate(p, n, modified_blocks); } catch(InvalidPositionException &e) {} - //TimeTaker timer2("Client::addNode(): updateMeshes"); - for(core::map::Iterator i = modified_blocks.getIterator(); i.atEnd() == false; i++) { v3s16 p = i.getNode()->getKey(); - //m_env.getClientMap().updateMeshes(p, m_env.getDayNightRatio()); addUpdateMeshTaskWithEdge(p); } } -void Client::updateCamera(v3f pos, v3f dir) -{ - m_env.getClientMap().updateCamera(pos, dir); - camera_position = pos; - camera_direction = dir; -} - -MapNode Client::getNode(v3s16 p) -{ - //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out - return m_env.getMap().getNode(p); -} - -NodeMetadata* Client::getNodeMetadata(v3s16 p) -{ - return m_env.getMap().getNodeMetadata(p); -} - -v3f Client::getPlayerPosition() +void Client::setPlayerControl(PlayerControl &control) { //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out LocalPlayer *player = m_env.getLocalPlayer(); assert(player != NULL); - return player->getPosition(); + player->control = control; } -void Client::setPlayerControl(PlayerControl &control) +void Client::selectPlayerItem(u16 item) { //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out - LocalPlayer *player = m_env.getLocalPlayer(); - assert(player != NULL); - player->control = control; + m_playeritem = item; + m_inventory_updated = true; + sendPlayerItem(item); } // Returns true if the inventory of the local player has been @@ -1821,107 +1950,51 @@ void Client::getLocalInventory(Inventory &dst) dst = player->inventory; } -InventoryContext *Client::getInventoryContext() -{ - return &m_inventory_context; -} - -Inventory* Client::getInventory(InventoryContext *c, std::string id) +Inventory* Client::getInventory(const InventoryLocation &loc) { - if(id == "current_player") + switch(loc.type){ + case InventoryLocation::UNDEFINED: + {} + break; + case InventoryLocation::CURRENT_PLAYER: + { + Player *player = m_env.getLocalPlayer(); + assert(player != NULL); + return &player->inventory; + } + break; + case InventoryLocation::PLAYER: { - assert(c->current_player); - return &(c->current_player->inventory); + Player *player = m_env.getPlayer(loc.name.c_str()); + if(!player) + return NULL; + return &player->inventory; } - - Strfnd fn(id); - std::string id0 = fn.next(":"); - - if(id0 == "nodemeta") + break; + case InventoryLocation::NODEMETA: { - v3s16 p; - p.X = stoi(fn.next(",")); - p.Y = stoi(fn.next(",")); - p.Z = stoi(fn.next(",")); - NodeMetadata* meta = getNodeMetadata(p); - if(meta) - return meta->getInventory(); - dstream<<"nodemeta at ("<getInventory(); + } + break; + default: + assert(0); + } return NULL; } void Client::inventoryAction(InventoryAction *a) { + /* + Send it to the server + */ sendInventoryAction(a); -} - -MapBlockObject * Client::getSelectedObject( - f32 max_d, - v3f from_pos_f_on_map, - core::line3d shootline_on_map - ) -{ - //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out - - core::array objects; - - for(core::map::Iterator - i = m_active_blocks.getIterator(); - i.atEnd() == false; i++) - { - v3s16 p = i.getNode()->getKey(); - - MapBlock *block = NULL; - try - { - block = m_env.getMap().getBlockNoCreate(p); - } - catch(InvalidPositionException &e) - { - continue; - } - - // Calculate from_pos relative to block - v3s16 block_pos_i_on_map = block->getPosRelative(); - v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map, BS); - v3f from_pos_f_on_block = from_pos_f_on_map - block_pos_f_on_map; - - block->getObjects(from_pos_f_on_block, max_d, objects); - //block->getPseudoObjects(from_pos_f_on_block, max_d, objects); - } - //dstream<<"Collected "<getBlock(); - - // Calculate shootline relative to block - v3s16 block_pos_i_on_map = block->getPosRelative(); - v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map, BS); - core::line3d shootline_on_block( - shootline_on_map.start - block_pos_f_on_map, - shootline_on_map.end - block_pos_f_on_map - ); - - if(obj->isSelected(shootline_on_block)) - { - //dstream<<"Returning selected object"<clientApply(this, this); } ClientActiveObject * Client::getSelectedActiveObject( @@ -1934,7 +2007,7 @@ ClientActiveObject * Client::getSelectedActiveObject( m_env.getActiveObjects(from_pos_f_on_map, max_d, objects); - //dstream<<"Collected "<getName()); + m_chat_queue.push_back( + (std::wstring)L"<"+name+L"> "+message); + } +} + +void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent) { - /*dstream<<"Client::addUpdateMeshTask(): " + /*infostream<<"Client::addUpdateMeshTask(): " <<"("<fill(getDayNightRatio(), b); + // Release: ~0ms + // Debug: 1-6ms, avg=2ms + data->fill(b); + data->setCrack(m_crack_level, m_crack_pos); + data->setSmoothLighting(g_settings->getBool("smooth_lighting")); } // Debug wait //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); + m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server, urgent); - /*dstream<<"Mesh update input queue size is " + /*infostream<<"Mesh update input queue size is " <replaceMesh(mesh_new); - delete data; - } -#endif - - b->setMeshExpired(false); } -void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server) +void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent) { /*{ v3s16 p = blockpos; - dstream<<"Client::addUpdateMeshTaskWithEdge(): " + infostream<<"Client::addUpdateMeshTaskWithEdge(): " <<"("<rebuildImagesAndTextures(); + + // Update texture atlas + if(g_settings->getBool("enable_texture_atlas")) + m_tsrc->buildMainAtlas(this); + + // Update node aliases + m_nodedef->updateAliases(m_itemdef); + + // Update node textures + m_nodedef->updateTextures(m_tsrc); + + // Update item textures and meshes + m_itemdef->updateTexturesAndMeshes(this); + + // Start mesh update thread after setting up content definitions + m_mesh_update_thread.Start(); +} + +float Client::getRTT(void) +{ + try{ + return m_con.GetPeerAvgRTT(PEER_ID_SERVER); + } catch(con::PeerNotFoundException &e){ + return 1337; + } +} + +// IGameDef interface +// Under envlock +IItemDefManager* Client::getItemDefManager() +{ + return m_itemdef; +} +INodeDefManager* Client::getNodeDefManager() +{ + return m_nodedef; +} +ICraftDefManager* Client::getCraftDefManager() +{ + return NULL; + //return m_craftdef; +} +ITextureSource* Client::getTextureSource() +{ + return m_tsrc; +} +u16 Client::allocateUnknownNodeId(const std::string &name) +{ + errorstream<<"Client::allocateUnknownNodeId(): " + <<"Client cannot allocate node IDs"<