3 Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include "clientserver.h"
24 #include "jmutexautolock.h"
28 #include "mapsector.h"
29 #include "mapblock_mesh.h"
34 #include "nodemetadata.h"
37 #include <IFileSystem.h>
40 #include "clientmap.h"
41 #include "filecache.h"
43 #include "utility_string.h"
46 static std::string getMediaCacheDir()
48 return porting::path_user + DIR_DELIM + "cache" + DIR_DELIM + "media";
55 MediaRequest(const std::string &name_=""):
64 QueuedMeshUpdate::QueuedMeshUpdate():
67 ack_block_to_server(false)
71 QueuedMeshUpdate::~QueuedMeshUpdate()
81 MeshUpdateQueue::MeshUpdateQueue()
86 MeshUpdateQueue::~MeshUpdateQueue()
88 JMutexAutoLock lock(m_mutex);
90 for(std::vector<QueuedMeshUpdate*>::iterator
92 i != m_queue.end(); i++)
94 QueuedMeshUpdate *q = *i;
100 peer_id=0 adds with nobody to send to
102 void MeshUpdateQueue::addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_server, bool urgent)
104 DSTACK(__FUNCTION_NAME);
108 JMutexAutoLock lock(m_mutex);
114 Find if block is already in queue.
115 If it is, update the data and quit.
117 for(std::vector<QueuedMeshUpdate*>::iterator
119 i != m_queue.end(); i++)
121 QueuedMeshUpdate *q = *i;
127 if(ack_block_to_server)
128 q->ack_block_to_server = true;
136 QueuedMeshUpdate *q = new QueuedMeshUpdate;
139 q->ack_block_to_server = ack_block_to_server;
140 m_queue.push_back(q);
143 // Returned pointer must be deleted
144 // Returns NULL if queue is empty
145 QueuedMeshUpdate * MeshUpdateQueue::pop()
147 JMutexAutoLock lock(m_mutex);
149 bool must_be_urgent = !m_urgents.empty();
150 for(std::vector<QueuedMeshUpdate*>::iterator
152 i != m_queue.end(); i++)
154 QueuedMeshUpdate *q = *i;
155 if(must_be_urgent && m_urgents.count(q->p) == 0)
158 m_urgents.erase(q->p);
168 void * MeshUpdateThread::Thread()
172 log_register_thread("MeshUpdateThread");
174 DSTACK(__FUNCTION_NAME);
176 BEGIN_DEBUG_EXCEPTION_HANDLER
180 /*// Wait for output queue to flush.
181 // Allow 2 in queue, this makes less frametime jitter.
182 // Umm actually, there is no much difference
183 if(m_queue_out.size() >= 2)
189 QueuedMeshUpdate *q = m_queue_in.pop();
196 ScopeProfiler sp(g_profiler, "Client: Mesh making");
198 MapBlockMesh *mesh_new = new MapBlockMesh(q->data);
199 if(mesh_new->getMesh()->getMeshBufferCount() == 0)
208 r.ack_block_to_server = q->ack_block_to_server;
210 /*infostream<<"MeshUpdateThread: Processed "
211 <<"("<<q->p.X<<","<<q->p.Y<<","<<q->p.Z<<")"
214 m_queue_out.push_back(r);
219 END_DEBUG_EXCEPTION_HANDLER(errorstream)
225 IrrlichtDevice *device,
226 const char *playername,
227 std::string password,
228 MapDrawControl &control,
229 IWritableTextureSource *tsrc,
230 IWritableItemDefManager *itemdef,
231 IWritableNodeDefManager *nodedef,
232 ISoundManager *sound,
233 MtEventManager *event
240 m_mesh_update_thread(this),
242 new ClientMap(this, this, control,
243 device->getSceneManager()->getRootSceneNode(),
244 device->getSceneManager(), 666),
245 device->getSceneManager(),
248 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
250 m_server_ser_ver(SER_FMT_VER_INVALID),
252 m_inventory_updated(false),
253 m_inventory_from_server(NULL),
254 m_inventory_from_server_age(0.0),
259 m_password(password),
260 m_access_denied(false),
261 m_media_cache(getMediaCacheDir()),
262 m_media_receive_progress(0),
263 m_media_received(false),
264 m_itemdef_received(false),
265 m_nodedef_received(false),
266 m_time_of_day_set(false),
267 m_last_time_of_day_f(-1),
268 m_time_of_day_update_timer(0),
269 m_removed_sounds_check_timer(0)
271 m_packetcounter_timer = 0.0;
272 //m_delete_unused_sectors_timer = 0.0;
273 m_connection_reinit_timer = 0.0;
274 m_avg_rtt_timer = 0.0;
275 m_playerpos_send_timer = 0.0;
276 m_ignore_damage_timer = 0.0;
278 // Build main texture atlas, now that the GameDef exists (that is, us)
279 if(g_settings->getBool("enable_texture_atlas"))
280 m_tsrc->buildMainAtlas(this);
282 infostream<<"Not building texture atlas."<<std::endl;
288 Player *player = new LocalPlayer(this);
290 player->updateName(playername);
292 m_env.addPlayer(player);
299 //JMutexAutoLock conlock(m_con_mutex); //bulk comment-out
303 m_mesh_update_thread.setRun(false);
304 while(m_mesh_update_thread.IsRunning())
307 delete m_inventory_from_server;
310 void Client::connect(Address address)
312 DSTACK(__FUNCTION_NAME);
313 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
314 m_con.SetTimeoutMs(0);
315 m_con.Connect(address);
318 bool Client::connectedAndInitialized()
320 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
322 if(m_con.Connected() == false)
325 if(m_server_ser_ver == SER_FMT_VER_INVALID)
331 void Client::step(float dtime)
333 DSTACK(__FUNCTION_NAME);
339 if(m_ignore_damage_timer > dtime)
340 m_ignore_damage_timer -= dtime;
342 m_ignore_damage_timer = 0.0;
344 m_animation_time += dtime;
345 if(m_animation_time > 60.0)
346 m_animation_time -= 60.0;
348 m_time_of_day_update_timer += dtime;
350 //infostream<<"Client steps "<<dtime<<std::endl;
353 //TimeTaker timer("ReceiveAll()", m_device);
359 //TimeTaker timer("m_con_mutex + m_con.RunTimeouts()", m_device);
361 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
362 m_con.RunTimeouts(dtime);
369 float &counter = m_packetcounter_timer;
375 infostream<<"Client packetcounter (20s):"<<std::endl;
376 m_packetcounter.print(infostream);
377 m_packetcounter.clear();
381 // Get connection status
382 bool connected = connectedAndInitialized();
387 Delete unused sectors
389 NOTE: This jams the game for a while because deleting sectors
393 float &counter = m_delete_unused_sectors_timer;
401 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
403 core::list<v3s16> deleted_blocks;
405 float delete_unused_sectors_timeout =
406 g_settings->getFloat("client_delete_unused_sectors_timeout");
408 // Delete sector blocks
409 /*u32 num = m_env.getMap().unloadUnusedData
410 (delete_unused_sectors_timeout,
411 true, &deleted_blocks);*/
413 // Delete whole sectors
414 m_env.getMap().unloadUnusedData
415 (delete_unused_sectors_timeout,
418 if(deleted_blocks.size() > 0)
420 /*infostream<<"Client: Deleted blocks of "<<num
421 <<" unused sectors"<<std::endl;*/
422 /*infostream<<"Client: Deleted "<<num
423 <<" unused sectors"<<std::endl;*/
429 // Env is locked so con can be locked.
430 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
432 core::list<v3s16>::Iterator i = deleted_blocks.begin();
433 core::list<v3s16> sendlist;
436 if(sendlist.size() == 255 || i == deleted_blocks.end())
438 if(sendlist.size() == 0)
447 u32 replysize = 2+1+6*sendlist.size();
448 SharedBuffer<u8> reply(replysize);
449 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
450 reply[2] = sendlist.size();
452 for(core::list<v3s16>::Iterator
453 j = sendlist.begin();
454 j != sendlist.end(); j++)
456 writeV3S16(&reply[2+1+6*k], *j);
459 m_con.Send(PEER_ID_SERVER, 1, reply, true);
461 if(i == deleted_blocks.end())
467 sendlist.push_back(*i);
475 if(connected == false)
477 float &counter = m_connection_reinit_timer;
483 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
485 Player *myplayer = m_env.getLocalPlayer();
486 assert(myplayer != NULL);
488 // Send TOSERVER_INIT
489 // [0] u16 TOSERVER_INIT
490 // [2] u8 SER_FMT_VER_HIGHEST
491 // [3] u8[20] player_name
492 // [23] u8[28] password (new in some version)
493 // [51] u16 client network protocol version (new in some version)
494 SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2);
495 writeU16(&data[0], TOSERVER_INIT);
496 writeU8(&data[2], SER_FMT_VER_HIGHEST);
498 memset((char*)&data[3], 0, PLAYERNAME_SIZE);
499 snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
501 /*infostream<<"Client: sending initial password hash: \""<<m_password<<"\""
504 memset((char*)&data[23], 0, PASSWORD_SIZE);
505 snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
507 // This should be incremented in each version
508 writeU16(&data[51], PROTOCOL_VERSION);
510 // Send as unreliable
511 Send(0, data, false);
514 // Not connected, return
519 Do stuff if connected
523 Run Map's timers and unload unused data
525 const float map_timer_and_unload_dtime = 5.25;
526 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
528 ScopeProfiler sp(g_profiler, "Client: map timer and unload");
529 core::list<v3s16> deleted_blocks;
530 m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
531 g_settings->getFloat("client_unload_unused_data_timeout"),
534 /*if(deleted_blocks.size() > 0)
535 infostream<<"Client: Unloaded "<<deleted_blocks.size()
536 <<" unused blocks"<<std::endl;*/
540 NOTE: This loop is intentionally iterated the way it is.
543 core::list<v3s16>::Iterator i = deleted_blocks.begin();
544 core::list<v3s16> sendlist;
547 if(sendlist.size() == 255 || i == deleted_blocks.end())
549 if(sendlist.size() == 0)
558 u32 replysize = 2+1+6*sendlist.size();
559 SharedBuffer<u8> reply(replysize);
560 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
561 reply[2] = sendlist.size();
563 for(core::list<v3s16>::Iterator
564 j = sendlist.begin();
565 j != sendlist.end(); j++)
567 writeV3S16(&reply[2+1+6*k], *j);
570 m_con.Send(PEER_ID_SERVER, 1, reply, true);
572 if(i == deleted_blocks.end())
578 sendlist.push_back(*i);
588 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
590 // Control local player (0ms)
591 LocalPlayer *player = m_env.getLocalPlayer();
592 assert(player != NULL);
593 player->applyControl(dtime);
595 //TimeTaker envtimer("env step", m_device);
604 ClientEnvEvent event = m_env.getClientEvent();
605 if(event.type == CEE_NONE)
609 else if(event.type == CEE_PLAYER_DAMAGE)
611 if(m_ignore_damage_timer <= 0)
613 u8 damage = event.player_damage.amount;
615 if(event.player_damage.send_to_server)
618 // Add to ClientEvent queue
620 event.type = CE_PLAYER_DAMAGE;
621 event.player_damage.amount = damage;
622 m_client_event_queue.push_back(event);
632 float &counter = m_avg_rtt_timer;
637 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
638 // connectedAndInitialized() is true, peer exists.
639 float avg_rtt = m_con.GetPeerAvgRTT(PEER_ID_SERVER);
640 infostream<<"Client: avg_rtt="<<avg_rtt<<std::endl;
645 Send player position to server
648 float &counter = m_playerpos_send_timer;
658 Replace updated meshes
661 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
663 //TimeTaker timer("** Processing mesh update result queue");
666 /*infostream<<"Mesh update result queue size is "
667 <<m_mesh_update_thread.m_queue_out.size()
670 int num_processed_meshes = 0;
671 while(m_mesh_update_thread.m_queue_out.size() > 0)
673 num_processed_meshes++;
674 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_front();
675 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
678 //JMutexAutoLock lock(block->mesh_mutex);
680 // Delete the old mesh
681 if(block->mesh != NULL)
683 // TODO: Remove hardware buffers of meshbuffers of block->mesh
688 // Replace with the new mesh
689 block->mesh = r.mesh;
691 if(r.ack_block_to_server)
693 /*infostream<<"Client: ACK block ("<<r.p.X<<","<<r.p.Y
694 <<","<<r.p.Z<<")"<<std::endl;*/
705 u32 replysize = 2+1+6;
706 SharedBuffer<u8> reply(replysize);
707 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
709 writeV3S16(&reply[3], r.p);
711 m_con.Send(PEER_ID_SERVER, 1, reply, true);
714 if(num_processed_meshes > 0)
715 g_profiler->graphAdd("num_processed_meshes", num_processed_meshes);
719 If the server didn't update the inventory in a while, revert
720 the local inventory (so the player notices the lag problem
721 and knows something is wrong).
723 if(m_inventory_from_server)
725 float interval = 10.0;
726 float count_before = floor(m_inventory_from_server_age / interval);
728 m_inventory_from_server_age += dtime;
730 float count_after = floor(m_inventory_from_server_age / interval);
732 if(count_after != count_before)
734 // Do this every <interval> seconds after TOCLIENT_INVENTORY
735 // Reset the locally changed inventory to the authoritative inventory
736 Player *player = m_env.getLocalPlayer();
737 player->inventory = *m_inventory_from_server;
738 m_inventory_updated = true;
743 Update positions of sounds attached to objects
746 for(std::map<int, u16>::iterator
747 i = m_sounds_to_objects.begin();
748 i != m_sounds_to_objects.end(); i++)
750 int client_id = i->first;
751 u16 object_id = i->second;
752 ClientActiveObject *cao = m_env.getActiveObject(object_id);
755 v3f pos = cao->getPosition();
756 m_sound->updateSoundPosition(client_id, pos);
761 Handle removed remotely initiated sounds
763 m_removed_sounds_check_timer += dtime;
764 if(m_removed_sounds_check_timer >= 2.32)
766 m_removed_sounds_check_timer = 0;
767 // Find removed sounds and clear references to them
768 std::set<s32> removed_server_ids;
769 for(std::map<s32, int>::iterator
770 i = m_sounds_server_to_client.begin();
771 i != m_sounds_server_to_client.end();)
773 s32 server_id = i->first;
774 int client_id = i->second;
776 if(!m_sound->soundExists(client_id)){
777 m_sounds_server_to_client.erase(server_id);
778 m_sounds_client_to_server.erase(client_id);
779 m_sounds_to_objects.erase(client_id);
780 removed_server_ids.insert(server_id);
784 if(removed_server_ids.size() != 0)
786 std::ostringstream os(std::ios_base::binary);
787 writeU16(os, TOSERVER_REMOVED_SOUNDS);
788 writeU16(os, removed_server_ids.size());
789 for(std::set<s32>::iterator i = removed_server_ids.begin();
790 i != removed_server_ids.end(); i++)
792 std::string s = os.str();
793 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
800 bool Client::loadMedia(const std::string &data, const std::string &filename)
802 // Silly irrlicht's const-incorrectness
803 Buffer<char> data_rw(data.c_str(), data.size());
807 const char *image_ext[] = {
808 ".png", ".jpg", ".bmp", ".tga",
809 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
812 name = removeStringEnd(filename, image_ext);
815 verbosestream<<"Client: Attempting to load image "
816 <<"file \""<<filename<<"\""<<std::endl;
818 io::IFileSystem *irrfs = m_device->getFileSystem();
819 video::IVideoDriver *vdrv = m_device->getVideoDriver();
821 // Create an irrlicht memory file
822 io::IReadFile *rfile = irrfs->createMemoryReadFile(
823 *data_rw, data_rw.getSize(), "_tempreadfile");
826 video::IImage *img = vdrv->createImageFromFile(rfile);
828 errorstream<<"Client: Cannot create image from data of "
829 <<"file \""<<filename<<"\""<<std::endl;
834 m_tsrc->insertSourceImage(filename, img);
841 const char *sound_ext[] = {
842 ".0.ogg", ".1.ogg", ".2.ogg", ".3.ogg", ".4.ogg",
843 ".5.ogg", ".6.ogg", ".7.ogg", ".8.ogg", ".9.ogg",
846 name = removeStringEnd(filename, sound_ext);
849 verbosestream<<"Client: Attempting to load sound "
850 <<"file \""<<filename<<"\""<<std::endl;
851 m_sound->loadSoundData(name, data);
855 errorstream<<"Client: Don't know how to load file \""
856 <<filename<<"\""<<std::endl;
860 // Virtual methods from con::PeerHandler
861 void Client::peerAdded(con::Peer *peer)
863 infostream<<"Client::peerAdded(): peer->id="
864 <<peer->id<<std::endl;
866 void Client::deletingPeer(con::Peer *peer, bool timeout)
868 infostream<<"Client::deletingPeer(): "
869 "Server Peer is getting deleted "
870 <<"(timeout="<<timeout<<")"<<std::endl;
873 void Client::ReceiveAll()
875 DSTACK(__FUNCTION_NAME);
876 u32 start_ms = porting::getTimeMs();
879 // Limit time even if there would be huge amounts of data to
881 if(porting::getTimeMs() > start_ms + 100)
886 g_profiler->graphAdd("client_received_packets", 1);
888 catch(con::NoIncomingDataException &e)
892 catch(con::InvalidIncomingDataException &e)
894 infostream<<"Client::ReceiveAll(): "
895 "InvalidIncomingDataException: what()="
896 <<e.what()<<std::endl;
901 void Client::Receive()
903 DSTACK(__FUNCTION_NAME);
904 SharedBuffer<u8> data;
908 //TimeTaker t1("con mutex and receive", m_device);
909 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
910 datasize = m_con.Receive(sender_peer_id, data);
912 //TimeTaker t1("ProcessData", m_device);
913 ProcessData(*data, datasize, sender_peer_id);
917 sender_peer_id given to this shall be quaranteed to be a valid peer
919 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
921 DSTACK(__FUNCTION_NAME);
923 // Ignore packets that don't even fit a command
926 m_packetcounter.add(60000);
930 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
932 //infostream<<"Client: received command="<<command<<std::endl;
933 m_packetcounter.add((u16)command);
936 If this check is removed, be sure to change the queue
937 system to know the ids
939 if(sender_peer_id != PEER_ID_SERVER)
941 infostream<<"Client::ProcessData(): Discarding data not "
942 "coming from server: peer_id="<<sender_peer_id
947 u8 ser_version = m_server_ser_ver;
949 //infostream<<"Client received command="<<(int)command<<std::endl;
951 if(command == TOCLIENT_INIT)
956 u8 deployed = data[2];
958 infostream<<"Client: TOCLIENT_INIT received with "
959 "deployed="<<((int)deployed&0xff)<<std::endl;
961 if(deployed < SER_FMT_VER_LOWEST
962 || deployed > SER_FMT_VER_HIGHEST)
964 infostream<<"Client: TOCLIENT_INIT: Server sent "
965 <<"unsupported ser_fmt_ver"<<std::endl;
969 m_server_ser_ver = deployed;
971 // Get player position
972 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
973 if(datasize >= 2+1+6)
974 playerpos_s16 = readV3S16(&data[2+1]);
975 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
978 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
980 // Set player position
981 Player *player = m_env.getLocalPlayer();
982 assert(player != NULL);
983 player->setPosition(playerpos_f);
986 if(datasize >= 2+1+6+8)
989 m_map_seed = readU64(&data[2+1+6]);
990 infostream<<"Client: received map seed: "<<m_map_seed<<std::endl;
995 SharedBuffer<u8> reply(replysize);
996 writeU16(&reply[0], TOSERVER_INIT2);
998 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1003 if(command == TOCLIENT_ACCESS_DENIED)
1005 // The server didn't like our password. Note, this needs
1006 // to be processed even if the serialisation format has
1007 // not been agreed yet, the same as TOCLIENT_INIT.
1008 m_access_denied = true;
1009 m_access_denied_reason = L"Unknown";
1012 std::string datastring((char*)&data[2], datasize-2);
1013 std::istringstream is(datastring, std::ios_base::binary);
1014 m_access_denied_reason = deSerializeWideString(is);
1019 if(ser_version == SER_FMT_VER_INVALID)
1021 infostream<<"Client: Server serialization"
1022 " format invalid or not initialized."
1023 " Skipping incoming command="<<command<<std::endl;
1027 // Just here to avoid putting the two if's together when
1028 // making some copypasta
1031 if(command == TOCLIENT_REMOVENODE)
1036 p.X = readS16(&data[2]);
1037 p.Y = readS16(&data[4]);
1038 p.Z = readS16(&data[6]);
1040 //TimeTaker t1("TOCLIENT_REMOVENODE");
1044 else if(command == TOCLIENT_ADDNODE)
1046 if(datasize < 8 + MapNode::serializedLength(ser_version))
1050 p.X = readS16(&data[2]);
1051 p.Y = readS16(&data[4]);
1052 p.Z = readS16(&data[6]);
1054 //TimeTaker t1("TOCLIENT_ADDNODE");
1057 n.deSerialize(&data[8], ser_version);
1061 else if(command == TOCLIENT_BLOCKDATA)
1063 // Ignore too small packet
1068 p.X = readS16(&data[2]);
1069 p.Y = readS16(&data[4]);
1070 p.Z = readS16(&data[6]);
1072 /*infostream<<"Client: Thread: BLOCKDATA for ("
1073 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1074 /*infostream<<"Client: Thread: BLOCKDATA for ("
1075 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1077 std::string datastring((char*)&data[8], datasize-8);
1078 std::istringstream istr(datastring, std::ios_base::binary);
1083 v2s16 p2d(p.X, p.Z);
1084 sector = m_env.getMap().emergeSector(p2d);
1086 assert(sector->getPos() == p2d);
1088 //TimeTaker timer("MapBlock deSerialize");
1091 block = sector->getBlockNoCreateNoEx(p.Y);
1095 Update an existing block
1097 //infostream<<"Updating"<<std::endl;
1098 block->deSerialize(istr, ser_version, false);
1105 //infostream<<"Creating new"<<std::endl;
1106 block = new MapBlock(&m_env.getMap(), p, this);
1107 block->deSerialize(istr, ser_version, false);
1108 sector->insertBlock(block);
1122 u32 replysize = 2+1+6;
1123 SharedBuffer<u8> reply(replysize);
1124 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
1126 writeV3S16(&reply[3], p);
1128 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1132 Add it to mesh update queue and set it to be acknowledged after update.
1134 //infostream<<"Adding mesh update task for received block"<<std::endl;
1135 addUpdateMeshTaskWithEdge(p, true);
1137 else if(command == TOCLIENT_INVENTORY)
1142 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
1145 //TimeTaker t2("mutex locking", m_device);
1146 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1149 //TimeTaker t3("istringstream init", m_device);
1150 std::string datastring((char*)&data[2], datasize-2);
1151 std::istringstream is(datastring, std::ios_base::binary);
1154 //m_env.printPlayers(infostream);
1156 //TimeTaker t4("player get", m_device);
1157 Player *player = m_env.getLocalPlayer();
1158 assert(player != NULL);
1161 //TimeTaker t1("inventory.deSerialize()", m_device);
1162 player->inventory.deSerialize(is);
1165 m_inventory_updated = true;
1167 delete m_inventory_from_server;
1168 m_inventory_from_server = new Inventory(player->inventory);
1169 m_inventory_from_server_age = 0.0;
1171 //infostream<<"Client got player inventory:"<<std::endl;
1172 //player->inventory.print(infostream);
1175 else if(command == TOCLIENT_TIME_OF_DAY)
1180 u16 time_of_day = readU16(&data[2]);
1181 time_of_day = time_of_day % 24000;
1182 //infostream<<"Client: time_of_day="<<time_of_day<<std::endl;
1183 float time_speed = 0;
1184 if(datasize >= 2 + 2 + 4){
1185 time_speed = readF1000(&data[4]);
1187 // Old message; try to approximate speed of time by ourselves
1188 float time_of_day_f = (float)time_of_day / 24000.0;
1189 float tod_diff_f = 0;
1190 if(time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
1191 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0;
1193 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
1194 m_last_time_of_day_f = time_of_day_f;
1195 float time_diff = m_time_of_day_update_timer;
1196 m_time_of_day_update_timer = 0;
1197 if(m_time_of_day_set){
1198 time_speed = 3600.0*24.0 * tod_diff_f / time_diff;
1199 infostream<<"Client: Measured time_of_day speed (old format): "
1200 <<time_speed<<" tod_diff_f="<<tod_diff_f
1201 <<" time_diff="<<time_diff<<std::endl;
1205 // Update environment
1206 m_env.setTimeOfDay(time_of_day);
1207 m_env.setTimeOfDaySpeed(time_speed);
1208 m_time_of_day_set = true;
1210 u32 dr = m_env.getDayNightRatio();
1211 verbosestream<<"Client: time_of_day="<<time_of_day
1212 <<" time_speed="<<time_speed
1213 <<" dr="<<dr<<std::endl;
1215 else if(command == TOCLIENT_CHAT_MESSAGE)
1223 std::string datastring((char*)&data[2], datasize-2);
1224 std::istringstream is(datastring, std::ios_base::binary);
1227 is.read((char*)buf, 2);
1228 u16 len = readU16(buf);
1230 std::wstring message;
1231 for(u16 i=0; i<len; i++)
1233 is.read((char*)buf, 2);
1234 message += (wchar_t)readU16(buf);
1237 /*infostream<<"Client received chat message: "
1238 <<wide_to_narrow(message)<<std::endl;*/
1240 m_chat_queue.push_back(message);
1242 else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1244 //if(g_settings->getBool("enable_experimental"))
1248 u16 count of removed objects
1249 for all removed objects {
1252 u16 count of added objects
1253 for all added objects {
1256 u32 initialization data length
1257 string initialization data
1262 // Get all data except the command number
1263 std::string datastring((char*)&data[2], datasize-2);
1264 // Throw them in an istringstream
1265 std::istringstream is(datastring, std::ios_base::binary);
1269 // Read removed objects
1271 u16 removed_count = readU16((u8*)buf);
1272 for(u16 i=0; i<removed_count; i++)
1275 u16 id = readU16((u8*)buf);
1278 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1279 m_env.removeActiveObject(id);
1283 // Read added objects
1285 u16 added_count = readU16((u8*)buf);
1286 for(u16 i=0; i<added_count; i++)
1289 u16 id = readU16((u8*)buf);
1291 u8 type = readU8((u8*)buf);
1292 std::string data = deSerializeLongString(is);
1295 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1296 m_env.addActiveObject(id, type, data);
1301 else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1303 //if(g_settings->getBool("enable_experimental"))
1315 // Get all data except the command number
1316 std::string datastring((char*)&data[2], datasize-2);
1317 // Throw them in an istringstream
1318 std::istringstream is(datastring, std::ios_base::binary);
1320 while(is.eof() == false)
1324 u16 id = readU16((u8*)buf);
1328 u16 message_size = readU16((u8*)buf);
1329 std::string message;
1330 message.reserve(message_size);
1331 for(u16 i=0; i<message_size; i++)
1334 message.append(buf, 1);
1336 // Pass on to the environment
1338 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1339 m_env.processActiveObjectMessage(id, message);
1344 else if(command == TOCLIENT_HP)
1346 std::string datastring((char*)&data[2], datasize-2);
1347 std::istringstream is(datastring, std::ios_base::binary);
1348 Player *player = m_env.getLocalPlayer();
1349 assert(player != NULL);
1350 u8 oldhp = player->hp;
1356 // Add to ClientEvent queue
1358 event.type = CE_PLAYER_DAMAGE;
1359 event.player_damage.amount = oldhp - hp;
1360 m_client_event_queue.push_back(event);
1363 else if(command == TOCLIENT_MOVE_PLAYER)
1365 std::string datastring((char*)&data[2], datasize-2);
1366 std::istringstream is(datastring, std::ios_base::binary);
1367 Player *player = m_env.getLocalPlayer();
1368 assert(player != NULL);
1369 v3f pos = readV3F1000(is);
1370 f32 pitch = readF1000(is);
1371 f32 yaw = readF1000(is);
1372 player->setPosition(pos);
1373 /*player->setPitch(pitch);
1374 player->setYaw(yaw);*/
1376 infostream<<"Client got TOCLIENT_MOVE_PLAYER"
1377 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1383 Add to ClientEvent queue.
1384 This has to be sent to the main program because otherwise
1385 it would just force the pitch and yaw values to whatever
1386 the camera points to.
1389 event.type = CE_PLAYER_FORCE_MOVE;
1390 event.player_force_move.pitch = pitch;
1391 event.player_force_move.yaw = yaw;
1392 m_client_event_queue.push_back(event);
1394 // Ignore damage for a few seconds, so that the player doesn't
1395 // get damage from falling on ground
1396 m_ignore_damage_timer = 3.0;
1398 else if(command == TOCLIENT_PLAYERITEM)
1400 infostream<<"Client: WARNING: Ignoring TOCLIENT_PLAYERITEM"<<std::endl;
1402 else if(command == TOCLIENT_DEATHSCREEN)
1404 std::string datastring((char*)&data[2], datasize-2);
1405 std::istringstream is(datastring, std::ios_base::binary);
1407 bool set_camera_point_target = readU8(is);
1408 v3f camera_point_target = readV3F1000(is);
1411 event.type = CE_DEATHSCREEN;
1412 event.deathscreen.set_camera_point_target = set_camera_point_target;
1413 event.deathscreen.camera_point_target_x = camera_point_target.X;
1414 event.deathscreen.camera_point_target_y = camera_point_target.Y;
1415 event.deathscreen.camera_point_target_z = camera_point_target.Z;
1416 m_client_event_queue.push_back(event);
1418 else if(command == TOCLIENT_ANNOUNCE_MEDIA)
1420 std::string datastring((char*)&data[2], datasize-2);
1421 std::istringstream is(datastring, std::ios_base::binary);
1423 // Mesh update thread must be stopped while
1424 // updating content definitions
1425 assert(!m_mesh_update_thread.IsRunning());
1427 int num_files = readU16(is);
1429 verbosestream<<"Client received TOCLIENT_ANNOUNCE_MEDIA ("
1430 <<num_files<<" files)"<<std::endl;
1432 core::list<MediaRequest> file_requests;
1434 for(int i=0; i<num_files; i++)
1436 //read file from cache
1437 std::string name = deSerializeString(is);
1438 std::string sha1_base64 = deSerializeString(is);
1440 // if name contains illegal characters, ignore the file
1441 if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
1442 errorstream<<"Client: ignoring illegal file name "
1443 <<"sent by server: \""<<name<<"\""<<std::endl;
1447 std::string sha1_raw = base64_decode(sha1_base64);
1448 std::string sha1_hex = hex_encode(sha1_raw);
1449 std::ostringstream tmp_os(std::ios_base::binary);
1450 bool found_in_cache = m_media_cache.load_sha1(sha1_raw, tmp_os);
1451 m_media_name_sha1_map.set(name, sha1_raw);
1453 // If found in cache, try to load it from there
1456 bool success = loadMedia(tmp_os.str(), name);
1458 verbosestream<<"Client: Loaded cached media: "
1459 <<sha1_hex<<" \""<<name<<"\""<<std::endl;
1462 infostream<<"Client: Failed to load cached media: "
1463 <<sha1_hex<<" \""<<name<<"\""<<std::endl;
1466 // Didn't load from cache; queue it to be requested
1467 verbosestream<<"Client: Adding file to request list: \""
1468 <<sha1_hex<<" \""<<name<<"\""<<std::endl;
1469 file_requests.push_back(MediaRequest(name));
1473 event.type = CE_TEXTURES_UPDATED;
1474 m_client_event_queue.push_back(event);
1478 u16 number of files requested
1484 std::ostringstream os(std::ios_base::binary);
1485 writeU16(os, TOSERVER_REQUEST_MEDIA);
1486 writeU16(os, file_requests.size());
1488 for(core::list<MediaRequest>::Iterator i = file_requests.begin();
1489 i != file_requests.end(); i++) {
1490 os<<serializeString(i->name);
1494 std::string s = os.str();
1495 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1497 Send(0, data, true);
1498 infostream<<"Client: Sending media request list to server ("
1499 <<file_requests.size()<<" files)"<<std::endl;
1501 else if(command == TOCLIENT_MEDIA)
1503 std::string datastring((char*)&data[2], datasize-2);
1504 std::istringstream is(datastring, std::ios_base::binary);
1506 // Mesh update thread must be stopped while
1507 // updating content definitions
1508 assert(!m_mesh_update_thread.IsRunning());
1512 u16 total number of file bunches
1513 u16 index of this bunch
1514 u32 number of files in this bunch
1522 int num_bunches = readU16(is);
1523 int bunch_i = readU16(is);
1524 if(num_bunches >= 2)
1525 m_media_receive_progress = (float)bunch_i / (float)(num_bunches - 1);
1527 m_media_receive_progress = 1.0;
1528 if(bunch_i == num_bunches - 1)
1529 m_media_received = true;
1530 int num_files = readU32(is);
1531 infostream<<"Client: Received files: bunch "<<bunch_i<<"/"
1532 <<num_bunches<<" files="<<num_files
1533 <<" size="<<datasize<<std::endl;
1534 for(int i=0; i<num_files; i++){
1535 std::string name = deSerializeString(is);
1536 std::string data = deSerializeLongString(is);
1538 // if name contains illegal characters, ignore the file
1539 if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
1540 errorstream<<"Client: ignoring illegal file name "
1541 <<"sent by server: \""<<name<<"\""<<std::endl;
1545 bool success = loadMedia(data, name);
1547 verbosestream<<"Client: Loaded received media: "
1548 <<"\""<<name<<"\". Caching."<<std::endl;
1550 infostream<<"Client: Failed to load received media: "
1551 <<"\""<<name<<"\". Not caching."<<std::endl;
1555 bool did = fs::CreateAllDirs(getMediaCacheDir());
1557 errorstream<<"Could not create media cache directory"
1562 core::map<std::string, std::string>::Node *n;
1563 n = m_media_name_sha1_map.find(name);
1565 errorstream<<"The server sent a file that has not "
1566 <<"been announced."<<std::endl;
1568 m_media_cache.update_sha1(data);
1573 event.type = CE_TEXTURES_UPDATED;
1574 m_client_event_queue.push_back(event);
1576 else if(command == TOCLIENT_TOOLDEF)
1578 infostream<<"Client: WARNING: Ignoring TOCLIENT_TOOLDEF"<<std::endl;
1580 else if(command == TOCLIENT_NODEDEF)
1582 infostream<<"Client: Received node definitions: packet size: "
1583 <<datasize<<std::endl;
1585 // Mesh update thread must be stopped while
1586 // updating content definitions
1587 assert(!m_mesh_update_thread.IsRunning());
1589 // Decompress node definitions
1590 std::string datastring((char*)&data[2], datasize-2);
1591 std::istringstream is(datastring, std::ios_base::binary);
1592 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1593 std::ostringstream tmp_os;
1594 decompressZlib(tmp_is, tmp_os);
1596 // Deserialize node definitions
1597 std::istringstream tmp_is2(tmp_os.str());
1598 m_nodedef->deSerialize(tmp_is2);
1599 m_nodedef_received = true;
1601 else if(command == TOCLIENT_CRAFTITEMDEF)
1603 infostream<<"Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF"<<std::endl;
1605 else if(command == TOCLIENT_ITEMDEF)
1607 infostream<<"Client: Received item definitions: packet size: "
1608 <<datasize<<std::endl;
1610 // Mesh update thread must be stopped while
1611 // updating content definitions
1612 assert(!m_mesh_update_thread.IsRunning());
1614 // Decompress item definitions
1615 std::string datastring((char*)&data[2], datasize-2);
1616 std::istringstream is(datastring, std::ios_base::binary);
1617 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1618 std::ostringstream tmp_os;
1619 decompressZlib(tmp_is, tmp_os);
1621 // Deserialize node definitions
1622 std::istringstream tmp_is2(tmp_os.str());
1623 m_itemdef->deSerialize(tmp_is2);
1624 m_itemdef_received = true;
1626 else if(command == TOCLIENT_PLAY_SOUND)
1628 std::string datastring((char*)&data[2], datasize-2);
1629 std::istringstream is(datastring, std::ios_base::binary);
1631 s32 server_id = readS32(is);
1632 std::string name = deSerializeString(is);
1633 float gain = readF1000(is);
1634 int type = readU8(is); // 0=local, 1=positional, 2=object
1635 v3f pos = readV3F1000(is);
1636 u16 object_id = readU16(is);
1637 bool loop = readU8(is);
1642 client_id = m_sound->playSound(name, false, gain);
1644 case 1: // positional
1645 client_id = m_sound->playSoundAt(name, false, gain, pos);
1648 ClientActiveObject *cao = m_env.getActiveObject(object_id);
1650 pos = cao->getPosition();
1651 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1652 // TODO: Set up sound to move with object
1657 if(client_id != -1){
1658 m_sounds_server_to_client[server_id] = client_id;
1659 m_sounds_client_to_server[client_id] = server_id;
1661 m_sounds_to_objects[client_id] = object_id;
1664 else if(command == TOCLIENT_STOP_SOUND)
1666 std::string datastring((char*)&data[2], datasize-2);
1667 std::istringstream is(datastring, std::ios_base::binary);
1669 s32 server_id = readS32(is);
1670 std::map<s32, int>::iterator i =
1671 m_sounds_server_to_client.find(server_id);
1672 if(i != m_sounds_server_to_client.end()){
1673 int client_id = i->second;
1674 m_sound->stopSound(client_id);
1679 infostream<<"Client: Ignoring unknown command "
1680 <<command<<std::endl;
1684 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
1686 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1687 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
1690 void Client::interact(u8 action, const PointedThing& pointed)
1692 if(connectedAndInitialized() == false){
1693 infostream<<"Client::interact() "
1694 "cancelled (not connected)"
1699 std::ostringstream os(std::ios_base::binary);
1705 [5] u32 length of the next item
1706 [9] serialized PointedThing
1708 0: start digging (from undersurface) or use
1709 1: stop digging (all parameters ignored)
1710 2: digging completed
1711 3: place block or item (to abovesurface)
1714 writeU16(os, TOSERVER_INTERACT);
1715 writeU8(os, action);
1716 writeU16(os, getPlayerItem());
1717 std::ostringstream tmp_os(std::ios::binary);
1718 pointed.serialize(tmp_os);
1719 os<<serializeLongString(tmp_os.str());
1721 std::string s = os.str();
1722 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1725 Send(0, data, true);
1728 void Client::sendSignNodeText(v3s16 p, std::string text)
1736 std::ostringstream os(std::ios_base::binary);
1740 writeU16(buf, TOSERVER_SIGNNODETEXT);
1741 os.write((char*)buf, 2);
1745 os.write((char*)buf, 6);
1747 u16 textlen = text.size();
1748 // Write text length
1749 writeS16(buf, textlen);
1750 os.write((char*)buf, 2);
1753 os.write((char*)text.c_str(), textlen);
1756 std::string s = os.str();
1757 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1759 Send(0, data, true);
1762 void Client::sendInventoryAction(InventoryAction *a)
1764 std::ostringstream os(std::ios_base::binary);
1768 writeU16(buf, TOSERVER_INVENTORY_ACTION);
1769 os.write((char*)buf, 2);
1774 std::string s = os.str();
1775 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1777 Send(0, data, true);
1780 void Client::sendChatMessage(const std::wstring &message)
1782 std::ostringstream os(std::ios_base::binary);
1786 writeU16(buf, TOSERVER_CHAT_MESSAGE);
1787 os.write((char*)buf, 2);
1790 writeU16(buf, message.size());
1791 os.write((char*)buf, 2);
1794 for(u32 i=0; i<message.size(); i++)
1798 os.write((char*)buf, 2);
1802 std::string s = os.str();
1803 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1805 Send(0, data, true);
1808 void Client::sendChangePassword(const std::wstring oldpassword,
1809 const std::wstring newpassword)
1811 Player *player = m_env.getLocalPlayer();
1815 std::string playername = player->getName();
1816 std::string oldpwd = translatePassword(playername, oldpassword);
1817 std::string newpwd = translatePassword(playername, newpassword);
1819 std::ostringstream os(std::ios_base::binary);
1820 u8 buf[2+PASSWORD_SIZE*2];
1822 [0] u16 TOSERVER_PASSWORD
1823 [2] u8[28] old password
1824 [30] u8[28] new password
1827 writeU16(buf, TOSERVER_PASSWORD);
1828 for(u32 i=0;i<PASSWORD_SIZE-1;i++)
1830 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
1831 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
1833 buf[2+PASSWORD_SIZE-1] = 0;
1834 buf[30+PASSWORD_SIZE-1] = 0;
1835 os.write((char*)buf, 2+PASSWORD_SIZE*2);
1838 std::string s = os.str();
1839 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1841 Send(0, data, true);
1845 void Client::sendDamage(u8 damage)
1847 DSTACK(__FUNCTION_NAME);
1848 std::ostringstream os(std::ios_base::binary);
1850 writeU16(os, TOSERVER_DAMAGE);
1851 writeU8(os, damage);
1854 std::string s = os.str();
1855 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1857 Send(0, data, true);
1860 void Client::sendRespawn()
1862 DSTACK(__FUNCTION_NAME);
1863 std::ostringstream os(std::ios_base::binary);
1865 writeU16(os, TOSERVER_RESPAWN);
1868 std::string s = os.str();
1869 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1871 Send(0, data, true);
1874 void Client::sendPlayerPos()
1876 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1878 Player *myplayer = m_env.getLocalPlayer();
1879 if(myplayer == NULL)
1884 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1885 our_peer_id = m_con.GetPeerID();
1888 // Set peer id if not set already
1889 if(myplayer->peer_id == PEER_ID_INEXISTENT)
1890 myplayer->peer_id = our_peer_id;
1891 // Check that an existing peer_id is the same as the connection's
1892 assert(myplayer->peer_id == our_peer_id);
1894 v3f pf = myplayer->getPosition();
1895 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
1896 v3f sf = myplayer->getSpeed();
1897 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
1898 s32 pitch = myplayer->getPitch() * 100;
1899 s32 yaw = myplayer->getYaw() * 100;
1904 [2] v3s32 position*100
1905 [2+12] v3s32 speed*100
1906 [2+12+12] s32 pitch*100
1907 [2+12+12+4] s32 yaw*100
1910 SharedBuffer<u8> data(2+12+12+4+4);
1911 writeU16(&data[0], TOSERVER_PLAYERPOS);
1912 writeV3S32(&data[2], position);
1913 writeV3S32(&data[2+12], speed);
1914 writeS32(&data[2+12+12], pitch);
1915 writeS32(&data[2+12+12+4], yaw);
1917 // Send as unreliable
1918 Send(0, data, false);
1921 void Client::sendPlayerItem(u16 item)
1923 Player *myplayer = m_env.getLocalPlayer();
1924 if(myplayer == NULL)
1927 u16 our_peer_id = m_con.GetPeerID();
1929 // Set peer id if not set already
1930 if(myplayer->peer_id == PEER_ID_INEXISTENT)
1931 myplayer->peer_id = our_peer_id;
1932 // Check that an existing peer_id is the same as the connection's
1933 assert(myplayer->peer_id == our_peer_id);
1935 SharedBuffer<u8> data(2+2);
1936 writeU16(&data[0], TOSERVER_PLAYERITEM);
1937 writeU16(&data[2], item);
1940 Send(0, data, true);
1943 void Client::removeNode(v3s16 p)
1945 core::map<v3s16, MapBlock*> modified_blocks;
1949 //TimeTaker t("removeNodeAndUpdate", m_device);
1950 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
1952 catch(InvalidPositionException &e)
1956 // add urgent task to update the modified node
1957 addUpdateMeshTaskForNode(p, false, true);
1959 for(core::map<v3s16, MapBlock * >::Iterator
1960 i = modified_blocks.getIterator();
1961 i.atEnd() == false; i++)
1963 v3s16 p = i.getNode()->getKey();
1964 addUpdateMeshTaskWithEdge(p);
1968 void Client::addNode(v3s16 p, MapNode n)
1970 TimeTaker timer1("Client::addNode()");
1972 core::map<v3s16, MapBlock*> modified_blocks;
1976 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
1977 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks);
1979 catch(InvalidPositionException &e)
1982 for(core::map<v3s16, MapBlock * >::Iterator
1983 i = modified_blocks.getIterator();
1984 i.atEnd() == false; i++)
1986 v3s16 p = i.getNode()->getKey();
1987 addUpdateMeshTaskWithEdge(p);
1991 void Client::setPlayerControl(PlayerControl &control)
1993 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1994 LocalPlayer *player = m_env.getLocalPlayer();
1995 assert(player != NULL);
1996 player->control = control;
1999 void Client::selectPlayerItem(u16 item)
2001 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2002 m_playeritem = item;
2003 m_inventory_updated = true;
2004 sendPlayerItem(item);
2007 // Returns true if the inventory of the local player has been
2008 // updated from the server. If it is true, it is set to false.
2009 bool Client::getLocalInventoryUpdated()
2011 // m_inventory_updated is behind envlock
2012 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2013 bool updated = m_inventory_updated;
2014 m_inventory_updated = false;
2018 // Copies the inventory of the local player to parameter
2019 void Client::getLocalInventory(Inventory &dst)
2021 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2022 Player *player = m_env.getLocalPlayer();
2023 assert(player != NULL);
2024 dst = player->inventory;
2027 Inventory* Client::getInventory(const InventoryLocation &loc)
2030 case InventoryLocation::UNDEFINED:
2033 case InventoryLocation::CURRENT_PLAYER:
2035 Player *player = m_env.getLocalPlayer();
2036 assert(player != NULL);
2037 return &player->inventory;
2040 case InventoryLocation::PLAYER:
2042 Player *player = m_env.getPlayer(loc.name.c_str());
2045 return &player->inventory;
2048 case InventoryLocation::NODEMETA:
2050 NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p);
2053 return meta->getInventory();
2061 void Client::inventoryAction(InventoryAction *a)
2064 Send it to the server
2066 sendInventoryAction(a);
2069 Predict some local inventory changes
2071 a->clientApply(this, this);
2074 ClientActiveObject * Client::getSelectedActiveObject(
2076 v3f from_pos_f_on_map,
2077 core::line3d<f32> shootline_on_map
2080 core::array<DistanceSortedActiveObject> objects;
2082 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
2084 //infostream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
2087 // After this, the closest object is the first in the array.
2090 for(u32 i=0; i<objects.size(); i++)
2092 ClientActiveObject *obj = objects[i].obj;
2094 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
2095 if(selection_box == NULL)
2098 v3f pos = obj->getPosition();
2100 core::aabbox3d<f32> offsetted_box(
2101 selection_box->MinEdge + pos,
2102 selection_box->MaxEdge + pos
2105 if(offsetted_box.intersectsWithLine(shootline_on_map))
2107 //infostream<<"Returning selected object"<<std::endl;
2112 //infostream<<"No object selected; returning NULL."<<std::endl;
2116 void Client::printDebugInfo(std::ostream &os)
2118 //JMutexAutoLock lock1(m_fetchblock_mutex);
2119 /*JMutexAutoLock lock2(m_incoming_queue_mutex);
2121 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
2122 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
2123 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
2127 core::list<std::wstring> Client::getConnectedPlayerNames()
2129 core::list<Player*> players = m_env.getPlayers(true);
2130 core::list<std::wstring> playerNames;
2131 for(core::list<Player*>::Iterator
2132 i = players.begin();
2133 i != players.end(); i++)
2135 Player *player = *i;
2136 playerNames.push_back(narrow_to_wide(player->getName()));
2141 float Client::getAnimationTime()
2143 return m_animation_time;
2146 int Client::getCrackLevel()
2148 return m_crack_level;
2151 void Client::setCrack(int level, v3s16 pos)
2153 int old_crack_level = m_crack_level;
2154 v3s16 old_crack_pos = m_crack_pos;
2156 m_crack_level = level;
2159 if(old_crack_level >= 0 && (level < 0 || pos != old_crack_pos))
2162 addUpdateMeshTaskForNode(old_crack_pos, false, true);
2164 if(level >= 0 && (old_crack_level < 0 || pos != old_crack_pos))
2167 addUpdateMeshTaskForNode(pos, false, true);
2173 Player *player = m_env.getLocalPlayer();
2174 assert(player != NULL);
2178 bool Client::getChatMessage(std::wstring &message)
2180 if(m_chat_queue.size() == 0)
2182 message = m_chat_queue.pop_front();
2186 void Client::typeChatMessage(const std::wstring &message)
2188 // Discard empty line
2193 sendChatMessage(message);
2196 if (message[0] == L'/')
2198 m_chat_queue.push_back(
2199 (std::wstring)L"issued command: "+message);
2203 LocalPlayer *player = m_env.getLocalPlayer();
2204 assert(player != NULL);
2205 std::wstring name = narrow_to_wide(player->getName());
2206 m_chat_queue.push_back(
2207 (std::wstring)L"<"+name+L"> "+message);
2211 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
2213 /*infostream<<"Client::addUpdateMeshTask(): "
2214 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2215 <<" ack_to_server="<<ack_to_server
2216 <<" urgent="<<urgent
2219 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2224 Create a task to update the mesh of the block
2227 MeshMakeData *data = new MeshMakeData(this);
2230 //TimeTaker timer("data fill");
2232 // Debug: 1-6ms, avg=2ms
2234 data->setCrack(m_crack_level, m_crack_pos);
2235 data->setSmoothLighting(g_settings->getBool("smooth_lighting"));
2239 //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
2241 // Add task to queue
2242 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server, urgent);
2244 /*infostream<<"Mesh update input queue size is "
2245 <<m_mesh_update_thread.m_queue_in.size()
2249 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent)
2253 infostream<<"Client::addUpdateMeshTaskWithEdge(): "
2254 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2259 v3s16 p = blockpos + v3s16(0,0,0);
2260 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2261 addUpdateMeshTask(p, ack_to_server, urgent);
2263 catch(InvalidPositionException &e){}
2266 v3s16 p = blockpos + v3s16(-1,0,0);
2267 addUpdateMeshTask(p, false, urgent);
2269 catch(InvalidPositionException &e){}
2271 v3s16 p = blockpos + v3s16(0,-1,0);
2272 addUpdateMeshTask(p, false, urgent);
2274 catch(InvalidPositionException &e){}
2276 v3s16 p = blockpos + v3s16(0,0,-1);
2277 addUpdateMeshTask(p, false, urgent);
2279 catch(InvalidPositionException &e){}
2282 void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool urgent)
2286 infostream<<"Client::addUpdateMeshTaskForNode(): "
2287 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2291 v3s16 blockpos = getNodeBlockPos(nodepos);
2292 v3s16 blockpos_relative = blockpos * MAP_BLOCKSIZE;
2295 v3s16 p = blockpos + v3s16(0,0,0);
2296 addUpdateMeshTask(p, ack_to_server, urgent);
2298 catch(InvalidPositionException &e){}
2300 if(nodepos.X == blockpos_relative.X){
2302 v3s16 p = blockpos + v3s16(-1,0,0);
2303 addUpdateMeshTask(p, false, urgent);
2305 catch(InvalidPositionException &e){}
2307 if(nodepos.Y == blockpos_relative.Y){
2309 v3s16 p = blockpos + v3s16(0,-1,0);
2310 addUpdateMeshTask(p, false, urgent);
2312 catch(InvalidPositionException &e){}
2314 if(nodepos.Z == blockpos_relative.Z){
2316 v3s16 p = blockpos + v3s16(0,0,-1);
2317 addUpdateMeshTask(p, false, urgent);
2319 catch(InvalidPositionException &e){}
2323 ClientEvent Client::getClientEvent()
2325 if(m_client_event_queue.size() == 0)
2328 event.type = CE_NONE;
2331 return m_client_event_queue.pop_front();
2334 void Client::afterContentReceived()
2336 assert(m_itemdef_received);
2337 assert(m_nodedef_received);
2338 assert(m_media_received);
2340 // remove the information about which checksum each texture
2342 m_media_name_sha1_map.clear();
2344 // Rebuild inherited images and recreate textures
2345 m_tsrc->rebuildImagesAndTextures();
2347 // Update texture atlas
2348 if(g_settings->getBool("enable_texture_atlas"))
2349 m_tsrc->buildMainAtlas(this);
2351 // Update node aliases
2352 m_nodedef->updateAliases(m_itemdef);
2354 // Update node textures
2355 m_nodedef->updateTextures(m_tsrc);
2357 // Update item textures and meshes
2358 m_itemdef->updateTexturesAndMeshes(this);
2360 // Start mesh update thread after setting up content definitions
2361 m_mesh_update_thread.Start();
2364 float Client::getRTT(void)
2367 return m_con.GetPeerAvgRTT(PEER_ID_SERVER);
2368 } catch(con::PeerNotFoundException &e){
2373 // IGameDef interface
2375 IItemDefManager* Client::getItemDefManager()
2379 INodeDefManager* Client::getNodeDefManager()
2383 ICraftDefManager* Client::getCraftDefManager()
2386 //return m_craftdef;
2388 ITextureSource* Client::getTextureSource()
2392 u16 Client::allocateUnknownNodeId(const std::string &name)
2394 errorstream<<"Client::allocateUnknownNodeId(): "
2395 <<"Client cannot allocate node IDs"<<std::endl;
2397 return CONTENT_IGNORE;
2399 ISoundManager* Client::getSoundManager()
2403 MtEventManager* Client::getEventManager()