3 Copyright (C) 2013 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 Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser 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 "jthread/jmutexautolock.h"
29 #include "mapsector.h"
30 #include "mapblock_mesh.h"
36 #include "nodemetadata.h"
40 #include <IFileSystem.h>
42 #include "clientmap.h"
43 #include "clientmedia.h"
45 #include "util/string.h"
46 #include "IMeshCache.h"
47 #include "serialization.h"
48 #include "util/serialize.h"
50 #include "cmake_config_githash.h"
51 #include "util/directiontables.h"
52 #include "util/pointedthing.h"
54 #include "drawscene.h"
56 extern gui::IGUIEnvironment* guienv;
62 QueuedMeshUpdate::QueuedMeshUpdate():
65 ack_block_to_server(false)
69 QueuedMeshUpdate::~QueuedMeshUpdate()
79 MeshUpdateQueue::MeshUpdateQueue()
83 MeshUpdateQueue::~MeshUpdateQueue()
85 JMutexAutoLock lock(m_mutex);
87 for(std::vector<QueuedMeshUpdate*>::iterator
89 i != m_queue.end(); i++)
91 QueuedMeshUpdate *q = *i;
97 peer_id=0 adds with nobody to send to
99 void MeshUpdateQueue::addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_server, bool urgent)
101 DSTACK(__FUNCTION_NAME);
105 JMutexAutoLock lock(m_mutex);
111 Find if block is already in queue.
112 If it is, update the data and quit.
114 for(std::vector<QueuedMeshUpdate*>::iterator
116 i != m_queue.end(); i++)
118 QueuedMeshUpdate *q = *i;
124 if(ack_block_to_server)
125 q->ack_block_to_server = true;
133 QueuedMeshUpdate *q = new QueuedMeshUpdate;
136 q->ack_block_to_server = ack_block_to_server;
137 m_queue.push_back(q);
140 // Returned pointer must be deleted
141 // Returns NULL if queue is empty
142 QueuedMeshUpdate * MeshUpdateQueue::pop()
144 JMutexAutoLock lock(m_mutex);
146 bool must_be_urgent = !m_urgents.empty();
147 for(std::vector<QueuedMeshUpdate*>::iterator
149 i != m_queue.end(); i++)
151 QueuedMeshUpdate *q = *i;
152 if(must_be_urgent && m_urgents.count(q->p) == 0)
155 m_urgents.erase(q->p);
165 void * MeshUpdateThread::Thread()
169 log_register_thread("MeshUpdateThread");
171 DSTACK(__FUNCTION_NAME);
173 BEGIN_DEBUG_EXCEPTION_HANDLER
175 porting::setThreadName("MeshUpdateThread");
177 while(!StopRequested())
179 QueuedMeshUpdate *q = m_queue_in.pop();
186 ScopeProfiler sp(g_profiler, "Client: Mesh making");
188 MapBlockMesh *mesh_new = new MapBlockMesh(q->data, m_camera_offset);
189 if(mesh_new->getMesh()->getMeshBufferCount() == 0)
198 r.ack_block_to_server = q->ack_block_to_server;
200 m_queue_out.push_back(r);
205 END_DEBUG_EXCEPTION_HANDLER(errorstream)
215 IrrlichtDevice *device,
216 const char *playername,
217 std::string password,
218 MapDrawControl &control,
219 IWritableTextureSource *tsrc,
220 IWritableShaderSource *shsrc,
221 IWritableItemDefManager *itemdef,
222 IWritableNodeDefManager *nodedef,
223 ISoundManager *sound,
224 MtEventManager *event,
227 m_packetcounter_timer(0.0),
228 m_connection_reinit_timer(0.1),
229 m_avg_rtt_timer(0.0),
230 m_playerpos_send_timer(0.0),
231 m_ignore_damage_timer(0.0),
238 m_mesh_update_thread(this),
240 new ClientMap(this, this, control,
241 device->getSceneManager()->getRootSceneNode(),
242 device->getSceneManager(), 666),
243 device->getSceneManager(),
246 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, ipv6, this),
248 m_server_ser_ver(SER_FMT_VER_INVALID),
250 m_inventory_updated(false),
251 m_inventory_from_server(NULL),
252 m_inventory_from_server_age(0.0),
257 m_password(password),
258 m_access_denied(false),
259 m_itemdef_received(false),
260 m_nodedef_received(false),
261 m_media_downloader(new ClientMediaDownloader()),
262 m_time_of_day_set(false),
263 m_last_time_of_day_f(-1),
264 m_time_of_day_update_timer(0),
265 m_recommended_send_interval(0.1),
266 m_removed_sounds_check_timer(0),
273 Player *player = new LocalPlayer(this);
275 player->updateName(playername);
277 m_env.addPlayer(player);
283 //request all client managed threads to stop
284 m_mesh_update_thread.Stop();
287 bool Client::isShutdown()
290 if (!m_mesh_update_thread.IsRunning()) return true;
299 m_mesh_update_thread.Stop();
300 m_mesh_update_thread.Wait();
301 while(!m_mesh_update_thread.m_queue_out.empty()) {
302 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_frontNoEx();
307 delete m_inventory_from_server;
309 // Delete detached inventories
310 for(std::map<std::string, Inventory*>::iterator
311 i = m_detached_inventories.begin();
312 i != m_detached_inventories.end(); i++){
316 // cleanup 3d model meshes on client shutdown
317 while (m_device->getSceneManager()->getMeshCache()->getMeshCount() != 0) {
318 scene::IAnimatedMesh * mesh =
319 m_device->getSceneManager()->getMeshCache()->getMeshByIndex(0);
322 m_device->getSceneManager()->getMeshCache()->removeMesh(mesh);
326 void Client::connect(Address address)
328 DSTACK(__FUNCTION_NAME);
329 m_con.SetTimeoutMs(0);
330 m_con.Connect(address);
333 void Client::step(float dtime)
335 DSTACK(__FUNCTION_NAME);
341 if(m_ignore_damage_timer > dtime)
342 m_ignore_damage_timer -= dtime;
344 m_ignore_damage_timer = 0.0;
346 m_animation_time += dtime;
347 if(m_animation_time > 60.0)
348 m_animation_time -= 60.0;
350 m_time_of_day_update_timer += dtime;
358 float &counter = m_packetcounter_timer;
364 infostream << "Client packetcounter (" << m_packetcounter_timer
366 m_packetcounter.print(infostream);
367 m_packetcounter.clear();
374 Delete unused sectors
376 NOTE: This jams the game for a while because deleting sectors
380 float &counter = m_delete_unused_sectors_timer;
388 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
390 core::list<v3s16> deleted_blocks;
392 float delete_unused_sectors_timeout =
393 g_settings->getFloat("client_delete_unused_sectors_timeout");
395 // Delete sector blocks
396 /*u32 num = m_env.getMap().unloadUnusedData
397 (delete_unused_sectors_timeout,
398 true, &deleted_blocks);*/
400 // Delete whole sectors
401 m_env.getMap().unloadUnusedData
402 (delete_unused_sectors_timeout,
405 if(deleted_blocks.size() > 0)
407 /*infostream<<"Client: Deleted blocks of "<<num
408 <<" unused sectors"<<std::endl;*/
409 /*infostream<<"Client: Deleted "<<num
410 <<" unused sectors"<<std::endl;*/
416 // Env is locked so con can be locked.
417 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
419 core::list<v3s16>::Iterator i = deleted_blocks.begin();
420 core::list<v3s16> sendlist;
423 if(sendlist.size() == 255 || i == deleted_blocks.end())
425 if(sendlist.size() == 0)
434 u32 replysize = 2+1+6*sendlist.size();
435 SharedBuffer<u8> reply(replysize);
436 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
437 reply[2] = sendlist.size();
439 for(core::list<v3s16>::Iterator
440 j = sendlist.begin();
441 j != sendlist.end(); j++)
443 writeV3S16(&reply[2+1+6*k], *j);
446 m_con.Send(PEER_ID_SERVER, 1, reply, true);
448 if(i == deleted_blocks.end())
454 sendlist.push_back(*i);
461 // UGLY hack to fix 2 second startup delay caused by non existent
462 // server client startup synchronization in local server or singleplayer mode
463 static bool initial_step = true;
465 initial_step = false;
467 else if(m_state == LC_Created)
469 float &counter = m_connection_reinit_timer;
475 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
477 Player *myplayer = m_env.getLocalPlayer();
478 assert(myplayer != NULL);
479 // Send TOSERVER_INIT
480 // [0] u16 TOSERVER_INIT
481 // [2] u8 SER_FMT_VER_HIGHEST_READ
482 // [3] u8[20] player_name
483 // [23] u8[28] password (new in some version)
484 // [51] u16 minimum supported network protocol version (added sometime)
485 // [53] u16 maximum supported network protocol version (added later than the previous one)
486 SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2+2);
487 writeU16(&data[0], TOSERVER_INIT);
488 writeU8(&data[2], SER_FMT_VER_HIGHEST_READ);
490 memset((char*)&data[3], 0, PLAYERNAME_SIZE);
491 snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
493 /*infostream<<"Client: sending initial password hash: \""<<m_password<<"\""
496 memset((char*)&data[23], 0, PASSWORD_SIZE);
497 snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
499 writeU16(&data[51], CLIENT_PROTOCOL_VERSION_MIN);
500 writeU16(&data[53], CLIENT_PROTOCOL_VERSION_MAX);
502 // Send as unreliable
503 Send(1, data, false);
506 // Not connected, return
511 Do stuff if connected
515 Run Map's timers and unload unused data
517 const float map_timer_and_unload_dtime = 5.25;
518 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
520 ScopeProfiler sp(g_profiler, "Client: map timer and unload");
521 std::list<v3s16> deleted_blocks;
522 m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
523 g_settings->getFloat("client_unload_unused_data_timeout"),
526 /*if(deleted_blocks.size() > 0)
527 infostream<<"Client: Unloaded "<<deleted_blocks.size()
528 <<" unused blocks"<<std::endl;*/
532 NOTE: This loop is intentionally iterated the way it is.
535 std::list<v3s16>::iterator i = deleted_blocks.begin();
536 std::list<v3s16> sendlist;
539 if(sendlist.size() == 255 || i == deleted_blocks.end())
541 if(sendlist.size() == 0)
550 u32 replysize = 2+1+6*sendlist.size();
551 SharedBuffer<u8> reply(replysize);
552 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
553 reply[2] = sendlist.size();
555 for(std::list<v3s16>::iterator
556 j = sendlist.begin();
557 j != sendlist.end(); ++j)
559 writeV3S16(&reply[2+1+6*k], *j);
562 m_con.Send(PEER_ID_SERVER, 2, reply, true);
564 if(i == deleted_blocks.end())
570 sendlist.push_back(*i);
579 // Control local player (0ms)
580 LocalPlayer *player = m_env.getLocalPlayer();
581 assert(player != NULL);
582 player->applyControl(dtime);
592 ClientEnvEvent event = m_env.getClientEvent();
593 if(event.type == CEE_NONE)
597 else if(event.type == CEE_PLAYER_DAMAGE)
599 if(m_ignore_damage_timer <= 0)
601 u8 damage = event.player_damage.amount;
603 if(event.player_damage.send_to_server)
606 // Add to ClientEvent queue
608 event.type = CE_PLAYER_DAMAGE;
609 event.player_damage.amount = damage;
610 m_client_event_queue.push_back(event);
613 else if(event.type == CEE_PLAYER_BREATH)
615 u16 breath = event.player_breath.amount;
625 float &counter = m_avg_rtt_timer;
630 // connectedAndInitialized() is true, peer exists.
631 float avg_rtt = getRTT();
632 infostream<<"Client: avg_rtt="<<avg_rtt<<std::endl;
637 Send player position to server
640 float &counter = m_playerpos_send_timer;
642 if((m_state == LC_Ready) && (counter >= m_recommended_send_interval))
650 Replace updated meshes
653 int num_processed_meshes = 0;
654 while(!m_mesh_update_thread.m_queue_out.empty())
656 num_processed_meshes++;
657 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_frontNoEx();
658 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
661 // Delete the old mesh
662 if(block->mesh != NULL)
664 // TODO: Remove hardware buffers of meshbuffers of block->mesh
669 // Replace with the new mesh
670 block->mesh = r.mesh;
674 if(r.ack_block_to_server)
686 u32 replysize = 2+1+6;
687 SharedBuffer<u8> reply(replysize);
688 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
690 writeV3S16(&reply[3], r.p);
692 m_con.Send(PEER_ID_SERVER, 2, reply, true);
695 if(num_processed_meshes > 0)
696 g_profiler->graphAdd("num_processed_meshes", num_processed_meshes);
702 if (m_media_downloader && m_media_downloader->isStarted()) {
703 m_media_downloader->step(this);
704 if (m_media_downloader->isDone()) {
706 delete m_media_downloader;
707 m_media_downloader = NULL;
712 If the server didn't update the inventory in a while, revert
713 the local inventory (so the player notices the lag problem
714 and knows something is wrong).
716 if(m_inventory_from_server)
718 float interval = 10.0;
719 float count_before = floor(m_inventory_from_server_age / interval);
721 m_inventory_from_server_age += dtime;
723 float count_after = floor(m_inventory_from_server_age / interval);
725 if(count_after != count_before)
727 // Do this every <interval> seconds after TOCLIENT_INVENTORY
728 // Reset the locally changed inventory to the authoritative inventory
729 Player *player = m_env.getLocalPlayer();
730 player->inventory = *m_inventory_from_server;
731 m_inventory_updated = true;
736 Update positions of sounds attached to objects
739 for(std::map<int, u16>::iterator
740 i = m_sounds_to_objects.begin();
741 i != m_sounds_to_objects.end(); i++)
743 int client_id = i->first;
744 u16 object_id = i->second;
745 ClientActiveObject *cao = m_env.getActiveObject(object_id);
748 v3f pos = cao->getPosition();
749 m_sound->updateSoundPosition(client_id, pos);
754 Handle removed remotely initiated sounds
756 m_removed_sounds_check_timer += dtime;
757 if(m_removed_sounds_check_timer >= 2.32)
759 m_removed_sounds_check_timer = 0;
760 // Find removed sounds and clear references to them
761 std::set<s32> removed_server_ids;
762 for(std::map<s32, int>::iterator
763 i = m_sounds_server_to_client.begin();
764 i != m_sounds_server_to_client.end();)
766 s32 server_id = i->first;
767 int client_id = i->second;
769 if(!m_sound->soundExists(client_id)){
770 m_sounds_server_to_client.erase(server_id);
771 m_sounds_client_to_server.erase(client_id);
772 m_sounds_to_objects.erase(client_id);
773 removed_server_ids.insert(server_id);
777 if(removed_server_ids.size() != 0)
779 std::ostringstream os(std::ios_base::binary);
780 writeU16(os, TOSERVER_REMOVED_SOUNDS);
781 size_t server_ids = removed_server_ids.size();
782 assert(server_ids <= 0xFFFF);
783 writeU16(os, (u16) (server_ids & 0xFFFF));
784 for(std::set<s32>::iterator i = removed_server_ids.begin();
785 i != removed_server_ids.end(); i++)
787 std::string s = os.str();
788 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
795 bool Client::loadMedia(const std::string &data, const std::string &filename)
797 // Silly irrlicht's const-incorrectness
798 Buffer<char> data_rw(data.c_str(), data.size());
802 const char *image_ext[] = {
803 ".png", ".jpg", ".bmp", ".tga",
804 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
807 name = removeStringEnd(filename, image_ext);
810 verbosestream<<"Client: Attempting to load image "
811 <<"file \""<<filename<<"\""<<std::endl;
813 io::IFileSystem *irrfs = m_device->getFileSystem();
814 video::IVideoDriver *vdrv = m_device->getVideoDriver();
816 // Create an irrlicht memory file
817 io::IReadFile *rfile = irrfs->createMemoryReadFile(
818 *data_rw, data_rw.getSize(), "_tempreadfile");
821 video::IImage *img = vdrv->createImageFromFile(rfile);
823 errorstream<<"Client: Cannot create image from data of "
824 <<"file \""<<filename<<"\""<<std::endl;
829 m_tsrc->insertSourceImage(filename, img);
836 const char *sound_ext[] = {
837 ".0.ogg", ".1.ogg", ".2.ogg", ".3.ogg", ".4.ogg",
838 ".5.ogg", ".6.ogg", ".7.ogg", ".8.ogg", ".9.ogg",
841 name = removeStringEnd(filename, sound_ext);
844 verbosestream<<"Client: Attempting to load sound "
845 <<"file \""<<filename<<"\""<<std::endl;
846 m_sound->loadSoundData(name, data);
850 const char *model_ext[] = {
851 ".x", ".b3d", ".md2", ".obj",
854 name = removeStringEnd(filename, model_ext);
857 verbosestream<<"Client: Storing model into memory: "
858 <<"\""<<filename<<"\""<<std::endl;
859 if(m_mesh_data.count(filename))
860 errorstream<<"Multiple models with name \""<<filename.c_str()
861 <<"\" found; replacing previous model"<<std::endl;
862 m_mesh_data[filename] = data;
866 errorstream<<"Client: Don't know how to load file \""
867 <<filename<<"\""<<std::endl;
871 // Virtual methods from con::PeerHandler
872 void Client::peerAdded(con::Peer *peer)
874 infostream<<"Client::peerAdded(): peer->id="
875 <<peer->id<<std::endl;
877 void Client::deletingPeer(con::Peer *peer, bool timeout)
879 infostream<<"Client::deletingPeer(): "
880 "Server Peer is getting deleted "
881 <<"(timeout="<<timeout<<")"<<std::endl;
886 u16 number of files requested
892 void Client::request_media(const std::list<std::string> &file_requests)
894 std::ostringstream os(std::ios_base::binary);
895 writeU16(os, TOSERVER_REQUEST_MEDIA);
896 size_t file_requests_size = file_requests.size();
897 assert(file_requests_size <= 0xFFFF);
898 writeU16(os, (u16) (file_requests_size & 0xFFFF));
900 for(std::list<std::string>::const_iterator i = file_requests.begin();
901 i != file_requests.end(); ++i) {
902 os<<serializeString(*i);
906 std::string s = os.str();
907 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
910 infostream<<"Client: Sending media request list to server ("
911 <<file_requests.size()<<" files)"<<std::endl;
914 void Client::received_media()
916 // notify server we received everything
917 std::ostringstream os(std::ios_base::binary);
918 writeU16(os, TOSERVER_RECEIVED_MEDIA);
919 std::string s = os.str();
920 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
923 infostream<<"Client: Notifying server that we received all media"
927 void Client::ReceiveAll()
929 DSTACK(__FUNCTION_NAME);
930 u32 start_ms = porting::getTimeMs();
933 // Limit time even if there would be huge amounts of data to
935 if(porting::getTimeMs() > start_ms + 100)
940 g_profiler->graphAdd("client_received_packets", 1);
942 catch(con::NoIncomingDataException &e)
946 catch(con::InvalidIncomingDataException &e)
948 infostream<<"Client::ReceiveAll(): "
949 "InvalidIncomingDataException: what()="
950 <<e.what()<<std::endl;
955 void Client::Receive()
957 DSTACK(__FUNCTION_NAME);
958 SharedBuffer<u8> data;
960 u32 datasize = m_con.Receive(sender_peer_id, data);
961 ProcessData(*data, datasize, sender_peer_id);
965 sender_peer_id given to this shall be quaranteed to be a valid peer
967 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
969 DSTACK(__FUNCTION_NAME);
971 // Ignore packets that don't even fit a command
974 m_packetcounter.add(60000);
978 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
980 //infostream<<"Client: received command="<<command<<std::endl;
981 m_packetcounter.add((u16)command);
984 If this check is removed, be sure to change the queue
985 system to know the ids
987 if(sender_peer_id != PEER_ID_SERVER)
989 infostream<<"Client::ProcessData(): Discarding data not "
990 "coming from server: peer_id="<<sender_peer_id
995 u8 ser_version = m_server_ser_ver;
997 if(command == TOCLIENT_INIT)
1002 u8 deployed = data[2];
1004 infostream<<"Client: TOCLIENT_INIT received with "
1005 "deployed="<<((int)deployed&0xff)<<std::endl;
1007 if(!ser_ver_supported(deployed))
1009 infostream<<"Client: TOCLIENT_INIT: Server sent "
1010 <<"unsupported ser_fmt_ver"<<std::endl;
1014 m_server_ser_ver = deployed;
1016 // Get player position
1017 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
1018 if(datasize >= 2+1+6)
1019 playerpos_s16 = readV3S16(&data[2+1]);
1020 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
1023 // Set player position
1024 Player *player = m_env.getLocalPlayer();
1025 assert(player != NULL);
1026 player->setPosition(playerpos_f);
1028 if(datasize >= 2+1+6+8)
1031 m_map_seed = readU64(&data[2+1+6]);
1032 infostream<<"Client: received map seed: "<<m_map_seed<<std::endl;
1035 if(datasize >= 2+1+6+8+4)
1038 m_recommended_send_interval = readF1000(&data[2+1+6+8]);
1039 infostream<<"Client: received recommended send interval "
1040 <<m_recommended_send_interval<<std::endl;
1045 SharedBuffer<u8> reply(replysize);
1046 writeU16(&reply[0], TOSERVER_INIT2);
1048 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1055 if(command == TOCLIENT_ACCESS_DENIED)
1057 // The server didn't like our password. Note, this needs
1058 // to be processed even if the serialisation format has
1059 // not been agreed yet, the same as TOCLIENT_INIT.
1060 m_access_denied = true;
1061 m_access_denied_reason = L"Unknown";
1064 std::string datastring((char*)&data[2], datasize-2);
1065 std::istringstream is(datastring, std::ios_base::binary);
1066 m_access_denied_reason = deSerializeWideString(is);
1071 if(ser_version == SER_FMT_VER_INVALID)
1073 infostream<<"Client: Server serialization"
1074 " format invalid or not initialized."
1075 " Skipping incoming command="<<command<<std::endl;
1080 Handle runtime commands
1082 // there's no sane reason why we shouldn't have a player and
1083 // almost everyone needs a player reference
1084 Player *player = m_env.getLocalPlayer();
1085 assert(player != NULL);
1087 if(command == TOCLIENT_REMOVENODE)
1092 p.X = readS16(&data[2]);
1093 p.Y = readS16(&data[4]);
1094 p.Z = readS16(&data[6]);
1097 else if(command == TOCLIENT_ADDNODE)
1099 if(datasize < 8 + MapNode::serializedLength(ser_version))
1103 p.X = readS16(&data[2]);
1104 p.Y = readS16(&data[4]);
1105 p.Z = readS16(&data[6]);
1108 n.deSerialize(&data[8], ser_version);
1110 bool remove_metadata = true;
1111 u32 index = 8 + MapNode::serializedLength(ser_version);
1112 if ((datasize >= index+1) && data[index]){
1113 remove_metadata = false;
1116 addNode(p, n, remove_metadata);
1118 else if(command == TOCLIENT_BLOCKDATA)
1120 // Ignore too small packet
1125 p.X = readS16(&data[2]);
1126 p.Y = readS16(&data[4]);
1127 p.Z = readS16(&data[6]);
1129 std::string datastring((char*)&data[8], datasize-8);
1130 std::istringstream istr(datastring, std::ios_base::binary);
1135 v2s16 p2d(p.X, p.Z);
1136 sector = m_env.getMap().emergeSector(p2d);
1138 assert(sector->getPos() == p2d);
1140 block = sector->getBlockNoCreateNoEx(p.Y);
1144 Update an existing block
1146 block->deSerialize(istr, ser_version, false);
1147 block->deSerializeNetworkSpecific(istr);
1154 block = new MapBlock(&m_env.getMap(), p, this);
1155 block->deSerialize(istr, ser_version, false);
1156 block->deSerializeNetworkSpecific(istr);
1157 sector->insertBlock(block);
1161 Add it to mesh update queue and set it to be acknowledged after update.
1163 addUpdateMeshTaskWithEdge(p, true);
1165 else if(command == TOCLIENT_INVENTORY)
1170 std::string datastring((char*)&data[2], datasize-2);
1171 std::istringstream is(datastring, std::ios_base::binary);
1173 player->inventory.deSerialize(is);
1175 m_inventory_updated = true;
1177 delete m_inventory_from_server;
1178 m_inventory_from_server = new Inventory(player->inventory);
1179 m_inventory_from_server_age = 0.0;
1182 else if(command == TOCLIENT_TIME_OF_DAY)
1187 u16 time_of_day = readU16(&data[2]);
1188 time_of_day = time_of_day % 24000;
1189 float time_speed = 0;
1191 if(datasize >= 2 + 2 + 4)
1193 time_speed = readF1000(&data[4]);
1196 // Old message; try to approximate speed of time by ourselves
1197 float time_of_day_f = (float)time_of_day / 24000.0;
1198 float tod_diff_f = 0;
1200 if(time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
1201 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0;
1203 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
1205 m_last_time_of_day_f = time_of_day_f;
1206 float time_diff = m_time_of_day_update_timer;
1207 m_time_of_day_update_timer = 0;
1209 if(m_time_of_day_set){
1210 time_speed = (3600.0*24.0) * tod_diff_f / time_diff;
1211 infostream<<"Client: Measured time_of_day speed (old format): "
1212 <<time_speed<<" tod_diff_f="<<tod_diff_f
1213 <<" time_diff="<<time_diff<<std::endl;
1217 // Update environment
1218 m_env.setTimeOfDay(time_of_day);
1219 m_env.setTimeOfDaySpeed(time_speed);
1220 m_time_of_day_set = true;
1222 u32 dr = m_env.getDayNightRatio();
1223 infostream<<"Client: time_of_day="<<time_of_day
1224 <<" time_speed="<<time_speed
1225 <<" dr="<<dr<<std::endl;
1227 else if(command == TOCLIENT_CHAT_MESSAGE)
1235 std::string datastring((char*)&data[2], datasize-2);
1236 std::istringstream is(datastring, std::ios_base::binary);
1239 is.read((char*) buf, 2);
1240 u16 len = readU16(buf);
1242 std::wstring message;
1243 for(unsigned int i=0; i<len; i++)
1245 is.read((char*)buf, 2);
1246 message += (wchar_t)readU16(buf);
1249 m_chat_queue.push_back(message);
1251 else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1255 u16 count of removed objects
1256 for all removed objects {
1259 u16 count of added objects
1260 for all added objects {
1263 u32 initialization data length
1264 string initialization data
1269 // Get all data except the command number
1270 std::string datastring((char*)&data[2], datasize-2);
1271 // Throw them in an istringstream
1272 std::istringstream is(datastring, std::ios_base::binary);
1274 // Read removed objects
1276 u16 removed_count = readU16((u8*)buf);
1277 for(unsigned int i=0; i<removed_count; i++)
1280 u16 id = readU16((u8*)buf);
1281 m_env.removeActiveObject(id);
1284 // Read added objects
1286 u16 added_count = readU16((u8*)buf);
1287 for(unsigned int i=0; i<added_count; i++)
1290 u16 id = readU16((u8*)buf);
1292 u8 type = readU8((u8*)buf);
1293 std::string data = deSerializeLongString(is);
1295 m_env.addActiveObject(id, type, data);
1298 else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1310 // Get all data except the command number
1311 std::string datastring((char*)&data[2], datasize-2);
1312 // Throw them in an istringstream
1313 std::istringstream is(datastring, std::ios_base::binary);
1315 while(is.eof() == false)
1318 u16 id = readU16((u8*)buf);
1322 size_t message_size = readU16((u8*)buf);
1323 std::string message;
1324 message.reserve(message_size);
1325 for(unsigned int i=0; i<message_size; i++)
1328 message.append(buf, 1);
1330 // Pass on to the environment
1331 m_env.processActiveObjectMessage(id, message);
1334 else if(command == TOCLIENT_MOVEMENT)
1336 std::string datastring((char*)&data[2], datasize-2);
1337 std::istringstream is(datastring, std::ios_base::binary);
1339 player->movement_acceleration_default = readF1000(is) * BS;
1340 player->movement_acceleration_air = readF1000(is) * BS;
1341 player->movement_acceleration_fast = readF1000(is) * BS;
1342 player->movement_speed_walk = readF1000(is) * BS;
1343 player->movement_speed_crouch = readF1000(is) * BS;
1344 player->movement_speed_fast = readF1000(is) * BS;
1345 player->movement_speed_climb = readF1000(is) * BS;
1346 player->movement_speed_jump = readF1000(is) * BS;
1347 player->movement_liquid_fluidity = readF1000(is) * BS;
1348 player->movement_liquid_fluidity_smooth = readF1000(is) * BS;
1349 player->movement_liquid_sink = readF1000(is) * BS;
1350 player->movement_gravity = readF1000(is) * BS;
1352 else if(command == TOCLIENT_HP)
1354 std::string datastring((char*)&data[2], datasize-2);
1355 std::istringstream is(datastring, std::ios_base::binary);
1357 u8 oldhp = player->hp;
1363 // Add to ClientEvent queue
1365 event.type = CE_PLAYER_DAMAGE;
1366 event.player_damage.amount = oldhp - hp;
1367 m_client_event_queue.push_back(event);
1370 else if(command == TOCLIENT_BREATH)
1372 std::string datastring((char*)&data[2], datasize-2);
1373 std::istringstream is(datastring, std::ios_base::binary);
1375 player->setBreath(readU16(is));
1377 else if(command == TOCLIENT_MOVE_PLAYER)
1379 std::string datastring((char*)&data[2], datasize-2);
1380 std::istringstream is(datastring, std::ios_base::binary);
1382 v3f pos = readV3F1000(is);
1383 f32 pitch = readF1000(is);
1384 f32 yaw = readF1000(is);
1385 player->setPosition(pos);
1387 infostream<<"Client got TOCLIENT_MOVE_PLAYER"
1388 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1394 Add to ClientEvent queue.
1395 This has to be sent to the main program because otherwise
1396 it would just force the pitch and yaw values to whatever
1397 the camera points to.
1400 event.type = CE_PLAYER_FORCE_MOVE;
1401 event.player_force_move.pitch = pitch;
1402 event.player_force_move.yaw = yaw;
1403 m_client_event_queue.push_back(event);
1405 // Ignore damage for a few seconds, so that the player doesn't
1406 // get damage from falling on ground
1407 m_ignore_damage_timer = 3.0;
1409 else if(command == TOCLIENT_PLAYERITEM)
1411 infostream<<"Client: WARNING: Ignoring TOCLIENT_PLAYERITEM"<<std::endl;
1413 else if(command == TOCLIENT_DEATHSCREEN)
1415 std::string datastring((char*)&data[2], datasize-2);
1416 std::istringstream is(datastring, std::ios_base::binary);
1418 bool set_camera_point_target = readU8(is);
1419 v3f camera_point_target = readV3F1000(is);
1422 event.type = CE_DEATHSCREEN;
1423 event.deathscreen.set_camera_point_target = set_camera_point_target;
1424 event.deathscreen.camera_point_target_x = camera_point_target.X;
1425 event.deathscreen.camera_point_target_y = camera_point_target.Y;
1426 event.deathscreen.camera_point_target_z = camera_point_target.Z;
1427 m_client_event_queue.push_back(event);
1429 else if(command == TOCLIENT_ANNOUNCE_MEDIA)
1431 std::string datastring((char*)&data[2], datasize-2);
1432 std::istringstream is(datastring, std::ios_base::binary);
1434 int num_files = readU16(is);
1436 infostream<<"Client: Received media announcement: packet size: "
1437 <<datasize<<std::endl;
1439 if (m_media_downloader == NULL ||
1440 m_media_downloader->isStarted()) {
1441 const char *problem = m_media_downloader ?
1442 "we already saw another announcement" :
1443 "all media has been received already";
1444 errorstream<<"Client: Received media announcement but "
1446 <<" files="<<num_files
1447 <<" size="<<datasize<<std::endl;
1451 // Mesh update thread must be stopped while
1452 // updating content definitions
1453 assert(!m_mesh_update_thread.IsRunning());
1455 for(int i=0; i<num_files; i++)
1457 std::string name = deSerializeString(is);
1458 std::string sha1_base64 = deSerializeString(is);
1459 std::string sha1_raw = base64_decode(sha1_base64);
1460 m_media_downloader->addFile(name, sha1_raw);
1463 std::vector<std::string> remote_media;
1465 Strfnd sf(deSerializeString(is));
1466 while(!sf.atend()) {
1467 std::string baseurl = trim(sf.next(","));
1469 m_media_downloader->addRemoteServer(baseurl);
1472 catch(SerializationError& e) {
1473 // not supported by server or turned off
1476 m_media_downloader->step(this);
1478 else if(command == TOCLIENT_MEDIA)
1480 std::string datastring((char*)&data[2], datasize-2);
1481 std::istringstream is(datastring, std::ios_base::binary);
1485 u16 total number of file bunches
1486 u16 index of this bunch
1487 u32 number of files in this bunch
1495 int num_bunches = readU16(is);
1496 int bunch_i = readU16(is);
1497 u32 num_files = readU32(is);
1498 infostream<<"Client: Received files: bunch "<<bunch_i<<"/"
1499 <<num_bunches<<" files="<<num_files
1500 <<" size="<<datasize<<std::endl;
1505 if (m_media_downloader == NULL ||
1506 !m_media_downloader->isStarted()) {
1507 const char *problem = m_media_downloader ?
1508 "media has not been requested" :
1509 "all media has been received already";
1510 errorstream<<"Client: Received media but "
1512 <<" bunch "<<bunch_i<<"/"<<num_bunches
1513 <<" files="<<num_files
1514 <<" size="<<datasize<<std::endl;
1518 // Mesh update thread must be stopped while
1519 // updating content definitions
1520 assert(!m_mesh_update_thread.IsRunning());
1522 for(unsigned int i=0; i<num_files; i++){
1523 std::string name = deSerializeString(is);
1524 std::string data = deSerializeLongString(is);
1525 m_media_downloader->conventionalTransferDone(
1529 else if(command == TOCLIENT_TOOLDEF)
1531 infostream<<"Client: WARNING: Ignoring TOCLIENT_TOOLDEF"<<std::endl;
1533 else if(command == TOCLIENT_NODEDEF)
1535 infostream<<"Client: Received node definitions: packet size: "
1536 <<datasize<<std::endl;
1538 // Mesh update thread must be stopped while
1539 // updating content definitions
1540 assert(!m_mesh_update_thread.IsRunning());
1542 // Decompress node definitions
1543 std::string datastring((char*)&data[2], datasize-2);
1544 std::istringstream is(datastring, std::ios_base::binary);
1545 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1546 std::ostringstream tmp_os;
1547 decompressZlib(tmp_is, tmp_os);
1549 // Deserialize node definitions
1550 std::istringstream tmp_is2(tmp_os.str());
1551 m_nodedef->deSerialize(tmp_is2);
1552 m_nodedef_received = true;
1554 else if(command == TOCLIENT_CRAFTITEMDEF)
1556 infostream<<"Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF"<<std::endl;
1558 else if(command == TOCLIENT_ITEMDEF)
1560 infostream<<"Client: Received item definitions: packet size: "
1561 <<datasize<<std::endl;
1563 // Mesh update thread must be stopped while
1564 // updating content definitions
1565 assert(!m_mesh_update_thread.IsRunning());
1567 // Decompress item definitions
1568 std::string datastring((char*)&data[2], datasize-2);
1569 std::istringstream is(datastring, std::ios_base::binary);
1570 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1571 std::ostringstream tmp_os;
1572 decompressZlib(tmp_is, tmp_os);
1574 // Deserialize node definitions
1575 std::istringstream tmp_is2(tmp_os.str());
1576 m_itemdef->deSerialize(tmp_is2);
1577 m_itemdef_received = true;
1579 else if(command == TOCLIENT_PLAY_SOUND)
1581 std::string datastring((char*)&data[2], datasize-2);
1582 std::istringstream is(datastring, std::ios_base::binary);
1584 s32 server_id = readS32(is);
1585 std::string name = deSerializeString(is);
1586 float gain = readF1000(is);
1587 int type = readU8(is); // 0=local, 1=positional, 2=object
1588 v3f pos = readV3F1000(is);
1589 u16 object_id = readU16(is);
1590 bool loop = readU8(is);
1595 client_id = m_sound->playSound(name, loop, gain);
1597 case 1: // positional
1598 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1601 ClientActiveObject *cao = m_env.getActiveObject(object_id);
1603 pos = cao->getPosition();
1604 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1605 // TODO: Set up sound to move with object
1610 if(client_id != -1){
1611 m_sounds_server_to_client[server_id] = client_id;
1612 m_sounds_client_to_server[client_id] = server_id;
1614 m_sounds_to_objects[client_id] = object_id;
1617 else if(command == TOCLIENT_STOP_SOUND)
1619 std::string datastring((char*)&data[2], datasize-2);
1620 std::istringstream is(datastring, std::ios_base::binary);
1622 s32 server_id = readS32(is);
1623 std::map<s32, int>::iterator i =
1624 m_sounds_server_to_client.find(server_id);
1625 if(i != m_sounds_server_to_client.end()){
1626 int client_id = i->second;
1627 m_sound->stopSound(client_id);
1630 else if(command == TOCLIENT_PRIVILEGES)
1632 std::string datastring((char*)&data[2], datasize-2);
1633 std::istringstream is(datastring, std::ios_base::binary);
1635 m_privileges.clear();
1636 infostream<<"Client: Privileges updated: ";
1637 u16 num_privileges = readU16(is);
1638 for(unsigned int i=0; i<num_privileges; i++){
1639 std::string priv = deSerializeString(is);
1640 m_privileges.insert(priv);
1641 infostream<<priv<<" ";
1643 infostream<<std::endl;
1645 else if(command == TOCLIENT_INVENTORY_FORMSPEC)
1647 std::string datastring((char*)&data[2], datasize-2);
1648 std::istringstream is(datastring, std::ios_base::binary);
1650 // Store formspec in LocalPlayer
1651 player->inventory_formspec = deSerializeLongString(is);
1653 else if(command == TOCLIENT_DETACHED_INVENTORY)
1655 std::string datastring((char*)&data[2], datasize-2);
1656 std::istringstream is(datastring, std::ios_base::binary);
1658 std::string name = deSerializeString(is);
1660 infostream<<"Client: Detached inventory update: \""<<name<<"\""<<std::endl;
1662 Inventory *inv = NULL;
1663 if(m_detached_inventories.count(name) > 0)
1664 inv = m_detached_inventories[name];
1666 inv = new Inventory(m_itemdef);
1667 m_detached_inventories[name] = inv;
1669 inv->deSerialize(is);
1671 else if(command == TOCLIENT_SHOW_FORMSPEC)
1673 std::string datastring((char*)&data[2], datasize-2);
1674 std::istringstream is(datastring, std::ios_base::binary);
1676 std::string formspec = deSerializeLongString(is);
1677 std::string formname = deSerializeString(is);
1680 event.type = CE_SHOW_FORMSPEC;
1681 // pointer is required as event is a struct only!
1682 // adding a std:string to a struct isn't possible
1683 event.show_formspec.formspec = new std::string(formspec);
1684 event.show_formspec.formname = new std::string(formname);
1685 m_client_event_queue.push_back(event);
1687 else if(command == TOCLIENT_SPAWN_PARTICLE)
1689 std::string datastring((char*)&data[2], datasize-2);
1690 std::istringstream is(datastring, std::ios_base::binary);
1692 v3f pos = readV3F1000(is);
1693 v3f vel = readV3F1000(is);
1694 v3f acc = readV3F1000(is);
1695 float expirationtime = readF1000(is);
1696 float size = readF1000(is);
1697 bool collisiondetection = readU8(is);
1698 std::string texture = deSerializeLongString(is);
1699 bool vertical = false;
1701 vertical = readU8(is);
1705 event.type = CE_SPAWN_PARTICLE;
1706 event.spawn_particle.pos = new v3f (pos);
1707 event.spawn_particle.vel = new v3f (vel);
1708 event.spawn_particle.acc = new v3f (acc);
1709 event.spawn_particle.expirationtime = expirationtime;
1710 event.spawn_particle.size = size;
1711 event.spawn_particle.collisiondetection = collisiondetection;
1712 event.spawn_particle.vertical = vertical;
1713 event.spawn_particle.texture = new std::string(texture);
1715 m_client_event_queue.push_back(event);
1717 else if(command == TOCLIENT_ADD_PARTICLESPAWNER)
1719 std::string datastring((char*)&data[2], datasize-2);
1720 std::istringstream is(datastring, std::ios_base::binary);
1722 u16 amount = readU16(is);
1723 float spawntime = readF1000(is);
1724 v3f minpos = readV3F1000(is);
1725 v3f maxpos = readV3F1000(is);
1726 v3f minvel = readV3F1000(is);
1727 v3f maxvel = readV3F1000(is);
1728 v3f minacc = readV3F1000(is);
1729 v3f maxacc = readV3F1000(is);
1730 float minexptime = readF1000(is);
1731 float maxexptime = readF1000(is);
1732 float minsize = readF1000(is);
1733 float maxsize = readF1000(is);
1734 bool collisiondetection = readU8(is);
1735 std::string texture = deSerializeLongString(is);
1736 u32 id = readU32(is);
1737 bool vertical = false;
1739 vertical = readU8(is);
1743 event.type = CE_ADD_PARTICLESPAWNER;
1744 event.add_particlespawner.amount = amount;
1745 event.add_particlespawner.spawntime = spawntime;
1746 event.add_particlespawner.minpos = new v3f (minpos);
1747 event.add_particlespawner.maxpos = new v3f (maxpos);
1748 event.add_particlespawner.minvel = new v3f (minvel);
1749 event.add_particlespawner.maxvel = new v3f (maxvel);
1750 event.add_particlespawner.minacc = new v3f (minacc);
1751 event.add_particlespawner.maxacc = new v3f (maxacc);
1752 event.add_particlespawner.minexptime = minexptime;
1753 event.add_particlespawner.maxexptime = maxexptime;
1754 event.add_particlespawner.minsize = minsize;
1755 event.add_particlespawner.maxsize = maxsize;
1756 event.add_particlespawner.collisiondetection = collisiondetection;
1757 event.add_particlespawner.vertical = vertical;
1758 event.add_particlespawner.texture = new std::string(texture);
1759 event.add_particlespawner.id = id;
1761 m_client_event_queue.push_back(event);
1763 else if(command == TOCLIENT_DELETE_PARTICLESPAWNER)
1765 std::string datastring((char*)&data[2], datasize-2);
1766 std::istringstream is(datastring, std::ios_base::binary);
1768 u32 id = readU16(is);
1771 event.type = CE_DELETE_PARTICLESPAWNER;
1772 event.delete_particlespawner.id = id;
1774 m_client_event_queue.push_back(event);
1776 else if(command == TOCLIENT_HUDADD)
1778 std::string datastring((char *)&data[2], datasize - 2);
1779 std::istringstream is(datastring, std::ios_base::binary);
1781 u32 id = readU32(is);
1782 u8 type = readU8(is);
1783 v2f pos = readV2F1000(is);
1784 std::string name = deSerializeString(is);
1785 v2f scale = readV2F1000(is);
1786 std::string text = deSerializeString(is);
1787 u32 number = readU32(is);
1788 u32 item = readU32(is);
1789 u32 dir = readU32(is);
1790 v2f align = readV2F1000(is);
1791 v2f offset = readV2F1000(is);
1795 world_pos = readV3F1000(is);
1796 }catch(SerializationError &e) {};
1798 size = readV2S32(is);
1799 } catch(SerializationError &e) {};
1802 event.type = CE_HUDADD;
1803 event.hudadd.id = id;
1804 event.hudadd.type = type;
1805 event.hudadd.pos = new v2f(pos);
1806 event.hudadd.name = new std::string(name);
1807 event.hudadd.scale = new v2f(scale);
1808 event.hudadd.text = new std::string(text);
1809 event.hudadd.number = number;
1810 event.hudadd.item = item;
1811 event.hudadd.dir = dir;
1812 event.hudadd.align = new v2f(align);
1813 event.hudadd.offset = new v2f(offset);
1814 event.hudadd.world_pos = new v3f(world_pos);
1815 event.hudadd.size = new v2s32(size);
1816 m_client_event_queue.push_back(event);
1818 else if(command == TOCLIENT_HUDRM)
1820 std::string datastring((char *)&data[2], datasize - 2);
1821 std::istringstream is(datastring, std::ios_base::binary);
1823 u32 id = readU32(is);
1826 event.type = CE_HUDRM;
1827 event.hudrm.id = id;
1828 m_client_event_queue.push_back(event);
1830 else if(command == TOCLIENT_HUDCHANGE)
1838 std::string datastring((char *)&data[2], datasize - 2);
1839 std::istringstream is(datastring, std::ios_base::binary);
1841 u32 id = readU32(is);
1842 u8 stat = (HudElementStat)readU8(is);
1844 if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
1845 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
1846 v2fdata = readV2F1000(is);
1847 else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
1848 sdata = deSerializeString(is);
1849 else if (stat == HUD_STAT_WORLD_POS)
1850 v3fdata = readV3F1000(is);
1851 else if (stat == HUD_STAT_SIZE )
1852 v2s32data = readV2S32(is);
1854 intdata = readU32(is);
1857 event.type = CE_HUDCHANGE;
1858 event.hudchange.id = id;
1859 event.hudchange.stat = (HudElementStat)stat;
1860 event.hudchange.v2fdata = new v2f(v2fdata);
1861 event.hudchange.v3fdata = new v3f(v3fdata);
1862 event.hudchange.sdata = new std::string(sdata);
1863 event.hudchange.data = intdata;
1864 event.hudchange.v2s32data = new v2s32(v2s32data);
1865 m_client_event_queue.push_back(event);
1867 else if(command == TOCLIENT_HUD_SET_FLAGS)
1869 std::string datastring((char *)&data[2], datasize - 2);
1870 std::istringstream is(datastring, std::ios_base::binary);
1872 u32 flags = readU32(is);
1873 u32 mask = readU32(is);
1875 player->hud_flags &= ~mask;
1876 player->hud_flags |= flags;
1878 else if(command == TOCLIENT_HUD_SET_PARAM)
1880 std::string datastring((char *)&data[2], datasize - 2);
1881 std::istringstream is(datastring, std::ios_base::binary);
1883 u16 param = readU16(is);
1884 std::string value = deSerializeString(is);
1886 if(param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) {
1887 s32 hotbar_itemcount = readS32((u8*) value.c_str());
1888 if(hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
1889 player->hud_hotbar_itemcount = hotbar_itemcount;
1891 else if (param == HUD_PARAM_HOTBAR_IMAGE) {
1892 ((LocalPlayer *) player)->hotbar_image = value;
1894 else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
1895 ((LocalPlayer *) player)->hotbar_selected_image = value;
1898 else if(command == TOCLIENT_SET_SKY)
1900 std::string datastring((char *)&data[2], datasize - 2);
1901 std::istringstream is(datastring, std::ios_base::binary);
1903 video::SColor *bgcolor = new video::SColor(readARGB8(is));
1904 std::string *type = new std::string(deSerializeString(is));
1905 u16 count = readU16(is);
1906 std::vector<std::string> *params = new std::vector<std::string>;
1908 for(size_t i=0; i<count; i++)
1909 params->push_back(deSerializeString(is));
1912 event.type = CE_SET_SKY;
1913 event.set_sky.bgcolor = bgcolor;
1914 event.set_sky.type = type;
1915 event.set_sky.params = params;
1916 m_client_event_queue.push_back(event);
1918 else if(command == TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO)
1920 std::string datastring((char *)&data[2], datasize - 2);
1921 std::istringstream is(datastring, std::ios_base::binary);
1923 bool do_override = readU8(is);
1924 float day_night_ratio_f = (float)readU16(is) / 65536;
1927 event.type = CE_OVERRIDE_DAY_NIGHT_RATIO;
1928 event.override_day_night_ratio.do_override = do_override;
1929 event.override_day_night_ratio.ratio_f = day_night_ratio_f;
1930 m_client_event_queue.push_back(event);
1932 else if(command == TOCLIENT_LOCAL_PLAYER_ANIMATIONS)
1934 std::string datastring((char *)&data[2], datasize - 2);
1935 std::istringstream is(datastring, std::ios_base::binary);
1937 LocalPlayer *player = m_env.getLocalPlayer();
1938 assert(player != NULL);
1940 player->local_animations[0] = readV2S32(is);
1941 player->local_animations[1] = readV2S32(is);
1942 player->local_animations[2] = readV2S32(is);
1943 player->local_animations[3] = readV2S32(is);
1944 player->local_animation_speed = readF1000(is);
1946 else if(command == TOCLIENT_EYE_OFFSET)
1948 std::string datastring((char *)&data[2], datasize - 2);
1949 std::istringstream is(datastring, std::ios_base::binary);
1951 LocalPlayer *player = m_env.getLocalPlayer();
1952 assert(player != NULL);
1954 player->eye_offset_first = readV3F1000(is);
1955 player->eye_offset_third = readV3F1000(is);
1959 infostream<<"Client: Ignoring unknown command "
1960 <<command<<std::endl;
1964 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
1966 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1967 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
1970 void Client::interact(u8 action, const PointedThing& pointed)
1972 if(m_state != LC_Ready){
1973 infostream<<"Client::interact() "
1974 "cancelled (not connected)"
1979 std::ostringstream os(std::ios_base::binary);
1985 [5] u32 length of the next item
1986 [9] serialized PointedThing
1988 0: start digging (from undersurface) or use
1989 1: stop digging (all parameters ignored)
1990 2: digging completed
1991 3: place block or item (to abovesurface)
1994 writeU16(os, TOSERVER_INTERACT);
1995 writeU8(os, action);
1996 writeU16(os, getPlayerItem());
1997 std::ostringstream tmp_os(std::ios::binary);
1998 pointed.serialize(tmp_os);
1999 os<<serializeLongString(tmp_os.str());
2001 std::string s = os.str();
2002 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2005 Send(0, data, true);
2008 void Client::sendNodemetaFields(v3s16 p, const std::string &formname,
2009 const std::map<std::string, std::string> &fields)
2011 std::ostringstream os(std::ios_base::binary);
2013 writeU16(os, TOSERVER_NODEMETA_FIELDS);
2015 os<<serializeString(formname);
2016 size_t fields_size = fields.size();
2017 assert(fields_size <= 0xFFFF);
2018 writeU16(os, (u16) (fields_size & 0xFFFF));
2019 for(std::map<std::string, std::string>::const_iterator
2020 i = fields.begin(); i != fields.end(); i++){
2021 const std::string &name = i->first;
2022 const std::string &value = i->second;
2023 os<<serializeString(name);
2024 os<<serializeLongString(value);
2028 std::string s = os.str();
2029 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2031 Send(0, data, true);
2034 void Client::sendInventoryFields(const std::string &formname,
2035 const std::map<std::string, std::string> &fields)
2037 std::ostringstream os(std::ios_base::binary);
2039 writeU16(os, TOSERVER_INVENTORY_FIELDS);
2040 os<<serializeString(formname);
2041 size_t fields_size = fields.size();
2042 assert(fields_size <= 0xFFFF);
2043 writeU16(os, (u16) (fields_size & 0xFFFF));
2044 for(std::map<std::string, std::string>::const_iterator
2045 i = fields.begin(); i != fields.end(); i++){
2046 const std::string &name = i->first;
2047 const std::string &value = i->second;
2048 os<<serializeString(name);
2049 os<<serializeLongString(value);
2053 std::string s = os.str();
2054 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2056 Send(0, data, true);
2059 void Client::sendInventoryAction(InventoryAction *a)
2061 std::ostringstream os(std::ios_base::binary);
2065 writeU16(buf, TOSERVER_INVENTORY_ACTION);
2066 os.write((char*)buf, 2);
2071 std::string s = os.str();
2072 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2074 Send(0, data, true);
2077 void Client::sendChatMessage(const std::wstring &message)
2079 std::ostringstream os(std::ios_base::binary);
2083 writeU16(buf, TOSERVER_CHAT_MESSAGE);
2084 os.write((char*)buf, 2);
2087 size_t messagesize = message.size();
2088 if (messagesize > 0xFFFF) {
2089 messagesize = 0xFFFF;
2091 writeU16(buf, (u16) messagesize);
2092 os.write((char*)buf, 2);
2095 for(unsigned int i=0; i<message.size(); i++)
2099 os.write((char*)buf, 2);
2103 std::string s = os.str();
2104 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2106 Send(0, data, true);
2109 void Client::sendChangePassword(const std::wstring &oldpassword,
2110 const std::wstring &newpassword)
2112 Player *player = m_env.getLocalPlayer();
2116 std::string playername = player->getName();
2117 std::string oldpwd = translatePassword(playername, oldpassword);
2118 std::string newpwd = translatePassword(playername, newpassword);
2120 std::ostringstream os(std::ios_base::binary);
2121 u8 buf[2+PASSWORD_SIZE*2];
2123 [0] u16 TOSERVER_PASSWORD
2124 [2] u8[28] old password
2125 [30] u8[28] new password
2128 writeU16(buf, TOSERVER_PASSWORD);
2129 for(unsigned int i=0;i<PASSWORD_SIZE-1;i++)
2131 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
2132 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
2134 buf[2+PASSWORD_SIZE-1] = 0;
2135 buf[30+PASSWORD_SIZE-1] = 0;
2136 os.write((char*)buf, 2+PASSWORD_SIZE*2);
2139 std::string s = os.str();
2140 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2142 Send(0, data, true);
2146 void Client::sendDamage(u8 damage)
2148 DSTACK(__FUNCTION_NAME);
2149 std::ostringstream os(std::ios_base::binary);
2151 writeU16(os, TOSERVER_DAMAGE);
2152 writeU8(os, damage);
2155 std::string s = os.str();
2156 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2158 Send(0, data, true);
2161 void Client::sendBreath(u16 breath)
2163 DSTACK(__FUNCTION_NAME);
2164 std::ostringstream os(std::ios_base::binary);
2166 writeU16(os, TOSERVER_BREATH);
2167 writeU16(os, breath);
2169 std::string s = os.str();
2170 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2172 Send(0, data, true);
2175 void Client::sendRespawn()
2177 DSTACK(__FUNCTION_NAME);
2178 std::ostringstream os(std::ios_base::binary);
2180 writeU16(os, TOSERVER_RESPAWN);
2183 std::string s = os.str();
2184 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2186 Send(0, data, true);
2189 void Client::sendReady()
2191 DSTACK(__FUNCTION_NAME);
2192 std::ostringstream os(std::ios_base::binary);
2194 writeU16(os, TOSERVER_CLIENT_READY);
2195 writeU8(os,VERSION_MAJOR);
2196 writeU8(os,VERSION_MINOR);
2197 writeU8(os,VERSION_PATCH_ORIG);
2200 writeU16(os,strlen(CMAKE_VERSION_GITHASH));
2201 os.write(CMAKE_VERSION_GITHASH,strlen(CMAKE_VERSION_GITHASH));
2204 std::string s = os.str();
2205 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2207 Send(0, data, true);
2210 void Client::sendPlayerPos()
2212 LocalPlayer *myplayer = m_env.getLocalPlayer();
2213 if(myplayer == NULL)
2216 // Save bandwidth by only updating position when something changed
2217 if(myplayer->last_position == myplayer->getPosition() &&
2218 myplayer->last_speed == myplayer->getSpeed() &&
2219 myplayer->last_pitch == myplayer->getPitch() &&
2220 myplayer->last_yaw == myplayer->getYaw() &&
2221 myplayer->last_keyPressed == myplayer->keyPressed)
2224 myplayer->last_position = myplayer->getPosition();
2225 myplayer->last_speed = myplayer->getSpeed();
2226 myplayer->last_pitch = myplayer->getPitch();
2227 myplayer->last_yaw = myplayer->getYaw();
2228 myplayer->last_keyPressed = myplayer->keyPressed;
2232 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2233 our_peer_id = m_con.GetPeerID();
2236 // Set peer id if not set already
2237 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2238 myplayer->peer_id = our_peer_id;
2239 // Check that an existing peer_id is the same as the connection's
2240 assert(myplayer->peer_id == our_peer_id);
2242 v3f pf = myplayer->getPosition();
2243 v3f sf = myplayer->getSpeed();
2244 s32 pitch = myplayer->getPitch() * 100;
2245 s32 yaw = myplayer->getYaw() * 100;
2246 u32 keyPressed = myplayer->keyPressed;
2248 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
2249 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
2253 [2] v3s32 position*100
2254 [2+12] v3s32 speed*100
2255 [2+12+12] s32 pitch*100
2256 [2+12+12+4] s32 yaw*100
2257 [2+12+12+4+4] u32 keyPressed
2259 SharedBuffer<u8> data(2+12+12+4+4+4);
2260 writeU16(&data[0], TOSERVER_PLAYERPOS);
2261 writeV3S32(&data[2], position);
2262 writeV3S32(&data[2+12], speed);
2263 writeS32(&data[2+12+12], pitch);
2264 writeS32(&data[2+12+12+4], yaw);
2265 writeU32(&data[2+12+12+4+4], keyPressed);
2266 // Send as unreliable
2267 Send(0, data, false);
2270 void Client::sendPlayerItem(u16 item)
2272 Player *myplayer = m_env.getLocalPlayer();
2273 if(myplayer == NULL)
2276 u16 our_peer_id = m_con.GetPeerID();
2278 // Set peer id if not set already
2279 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2280 myplayer->peer_id = our_peer_id;
2281 // Check that an existing peer_id is the same as the connection's
2282 assert(myplayer->peer_id == our_peer_id);
2284 SharedBuffer<u8> data(2+2);
2285 writeU16(&data[0], TOSERVER_PLAYERITEM);
2286 writeU16(&data[2], item);
2289 Send(0, data, true);
2292 void Client::removeNode(v3s16 p)
2294 std::map<v3s16, MapBlock*> modified_blocks;
2298 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
2300 catch(InvalidPositionException &e)
2304 // add urgent task to update the modified node
2305 addUpdateMeshTaskForNode(p, false, true);
2307 for(std::map<v3s16, MapBlock * >::iterator
2308 i = modified_blocks.begin();
2309 i != modified_blocks.end(); ++i)
2311 addUpdateMeshTaskWithEdge(i->first);
2315 void Client::addNode(v3s16 p, MapNode n, bool remove_metadata)
2317 TimeTaker timer1("Client::addNode()");
2319 std::map<v3s16, MapBlock*> modified_blocks;
2323 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
2324 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
2326 catch(InvalidPositionException &e)
2329 for(std::map<v3s16, MapBlock * >::iterator
2330 i = modified_blocks.begin();
2331 i != modified_blocks.end(); ++i)
2333 addUpdateMeshTaskWithEdge(i->first);
2337 void Client::setPlayerControl(PlayerControl &control)
2339 LocalPlayer *player = m_env.getLocalPlayer();
2340 assert(player != NULL);
2341 player->control = control;
2344 void Client::selectPlayerItem(u16 item)
2346 m_playeritem = item;
2347 m_inventory_updated = true;
2348 sendPlayerItem(item);
2351 // Returns true if the inventory of the local player has been
2352 // updated from the server. If it is true, it is set to false.
2353 bool Client::getLocalInventoryUpdated()
2355 bool updated = m_inventory_updated;
2356 m_inventory_updated = false;
2360 // Copies the inventory of the local player to parameter
2361 void Client::getLocalInventory(Inventory &dst)
2363 Player *player = m_env.getLocalPlayer();
2364 assert(player != NULL);
2365 dst = player->inventory;
2368 Inventory* Client::getInventory(const InventoryLocation &loc)
2371 case InventoryLocation::UNDEFINED:
2374 case InventoryLocation::CURRENT_PLAYER:
2376 Player *player = m_env.getLocalPlayer();
2377 assert(player != NULL);
2378 return &player->inventory;
2381 case InventoryLocation::PLAYER:
2383 Player *player = m_env.getPlayer(loc.name.c_str());
2386 return &player->inventory;
2389 case InventoryLocation::NODEMETA:
2391 NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p);
2394 return meta->getInventory();
2397 case InventoryLocation::DETACHED:
2399 if(m_detached_inventories.count(loc.name) == 0)
2401 return m_detached_inventories[loc.name];
2410 void Client::inventoryAction(InventoryAction *a)
2413 Send it to the server
2415 sendInventoryAction(a);
2418 Predict some local inventory changes
2420 a->clientApply(this, this);
2426 ClientActiveObject * Client::getSelectedActiveObject(
2428 v3f from_pos_f_on_map,
2429 core::line3d<f32> shootline_on_map
2432 std::vector<DistanceSortedActiveObject> objects;
2434 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
2437 // After this, the closest object is the first in the array.
2438 std::sort(objects.begin(), objects.end());
2440 for(unsigned int i=0; i<objects.size(); i++)
2442 ClientActiveObject *obj = objects[i].obj;
2444 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
2445 if(selection_box == NULL)
2448 v3f pos = obj->getPosition();
2450 core::aabbox3d<f32> offsetted_box(
2451 selection_box->MinEdge + pos,
2452 selection_box->MaxEdge + pos
2455 if(offsetted_box.intersectsWithLine(shootline_on_map))
2464 std::list<std::string> Client::getConnectedPlayerNames()
2466 return m_env.getPlayerNames();
2469 float Client::getAnimationTime()
2471 return m_animation_time;
2474 int Client::getCrackLevel()
2476 return m_crack_level;
2479 void Client::setCrack(int level, v3s16 pos)
2481 int old_crack_level = m_crack_level;
2482 v3s16 old_crack_pos = m_crack_pos;
2484 m_crack_level = level;
2487 if(old_crack_level >= 0 && (level < 0 || pos != old_crack_pos))
2490 addUpdateMeshTaskForNode(old_crack_pos, false, true);
2492 if(level >= 0 && (old_crack_level < 0 || pos != old_crack_pos))
2495 addUpdateMeshTaskForNode(pos, false, true);
2501 Player *player = m_env.getLocalPlayer();
2502 assert(player != NULL);
2506 u16 Client::getBreath()
2508 Player *player = m_env.getLocalPlayer();
2509 assert(player != NULL);
2510 return player->getBreath();
2513 bool Client::getChatMessage(std::wstring &message)
2515 if(m_chat_queue.size() == 0)
2517 message = m_chat_queue.pop_front();
2521 void Client::typeChatMessage(const std::wstring &message)
2523 // Discard empty line
2528 sendChatMessage(message);
2531 if (message[0] == L'/')
2533 m_chat_queue.push_back(
2534 (std::wstring)L"issued command: "+message);
2538 LocalPlayer *player = m_env.getLocalPlayer();
2539 assert(player != NULL);
2540 std::wstring name = narrow_to_wide(player->getName());
2541 m_chat_queue.push_back(
2542 (std::wstring)L"<"+name+L"> "+message);
2546 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
2548 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2553 Create a task to update the mesh of the block
2556 MeshMakeData *data = new MeshMakeData(this);
2559 //TimeTaker timer("data fill");
2561 // Debug: 1-6ms, avg=2ms
2563 data->setCrack(m_crack_level, m_crack_pos);
2564 data->setSmoothLighting(g_settings->getBool("smooth_lighting"));
2567 // Add task to queue
2568 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server, urgent);
2571 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent)
2574 v3s16 p = blockpos + v3s16(0,0,0);
2575 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2576 addUpdateMeshTask(p, ack_to_server, urgent);
2578 catch(InvalidPositionException &e){}
2581 for (int i=0;i<6;i++)
2584 v3s16 p = blockpos + g_6dirs[i];
2585 addUpdateMeshTask(p, false, urgent);
2587 catch(InvalidPositionException &e){}
2591 void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool urgent)
2595 infostream<<"Client::addUpdateMeshTaskForNode(): "
2596 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2600 v3s16 blockpos = getNodeBlockPos(nodepos);
2601 v3s16 blockpos_relative = blockpos * MAP_BLOCKSIZE;
2604 v3s16 p = blockpos + v3s16(0,0,0);
2605 addUpdateMeshTask(p, ack_to_server, urgent);
2607 catch(InvalidPositionException &e){}
2610 if(nodepos.X == blockpos_relative.X){
2612 v3s16 p = blockpos + v3s16(-1,0,0);
2613 addUpdateMeshTask(p, false, urgent);
2615 catch(InvalidPositionException &e){}
2618 if(nodepos.Y == blockpos_relative.Y){
2620 v3s16 p = blockpos + v3s16(0,-1,0);
2621 addUpdateMeshTask(p, false, urgent);
2623 catch(InvalidPositionException &e){}
2626 if(nodepos.Z == blockpos_relative.Z){
2628 v3s16 p = blockpos + v3s16(0,0,-1);
2629 addUpdateMeshTask(p, false, urgent);
2631 catch(InvalidPositionException &e){}
2635 ClientEvent Client::getClientEvent()
2637 if(m_client_event_queue.size() == 0)
2640 event.type = CE_NONE;
2643 return m_client_event_queue.pop_front();
2646 float Client::mediaReceiveProgress()
2648 if (m_media_downloader)
2649 return m_media_downloader->getProgress();
2651 return 1.0; // downloader only exists when not yet done
2654 void Client::afterContentReceived(IrrlichtDevice *device, gui::IGUIFont* font)
2656 infostream<<"Client::afterContentReceived() started"<<std::endl;
2657 assert(m_itemdef_received);
2658 assert(m_nodedef_received);
2659 assert(mediaReceived());
2661 // Rebuild inherited images and recreate textures
2662 infostream<<"- Rebuilding images and textures"<<std::endl;
2663 m_tsrc->rebuildImagesAndTextures();
2666 infostream<<"- Rebuilding shaders"<<std::endl;
2667 m_shsrc->rebuildShaders();
2669 // Update node aliases
2670 infostream<<"- Updating node aliases"<<std::endl;
2671 m_nodedef->updateAliases(m_itemdef);
2673 // Update node textures and assign shaders to each tile
2674 infostream<<"- Updating node textures"<<std::endl;
2675 m_nodedef->updateTextures(m_tsrc, m_shsrc);
2677 // Preload item textures and meshes if configured to
2678 if(g_settings->getBool("preload_item_visuals"))
2680 verbosestream<<"Updating item textures and meshes"<<std::endl;
2681 wchar_t* text = wgettext("Item textures...");
2682 draw_load_screen(text, device, guienv, font, 0, 0);
2683 std::set<std::string> names = m_itemdef->getAll();
2684 size_t size = names.size();
2687 for(std::set<std::string>::const_iterator
2688 i = names.begin(); i != names.end(); ++i){
2689 // Asking for these caches the result
2690 m_itemdef->getInventoryTexture(*i, this);
2691 m_itemdef->getWieldMesh(*i, this);
2693 percent = count*100/size;
2694 if (count%50 == 0) // only update every 50 item
2695 draw_load_screen(text, device, guienv, font, 0, percent);
2700 // Start mesh update thread after setting up content definitions
2701 infostream<<"- Starting mesh update thread"<<std::endl;
2702 m_mesh_update_thread.Start();
2706 infostream<<"Client::afterContentReceived() done"<<std::endl;
2709 float Client::getRTT(void)
2711 return m_con.getPeerStat(PEER_ID_SERVER,con::AVG_RTT);
2714 float Client::getCurRate(void)
2716 return ( m_con.getLocalStat(con::CUR_INC_RATE) +
2717 m_con.getLocalStat(con::CUR_DL_RATE));
2720 float Client::getAvgRate(void)
2722 return ( m_con.getLocalStat(con::AVG_INC_RATE) +
2723 m_con.getLocalStat(con::AVG_DL_RATE));
2726 // IGameDef interface
2728 IItemDefManager* Client::getItemDefManager()
2732 INodeDefManager* Client::getNodeDefManager()
2736 ICraftDefManager* Client::getCraftDefManager()
2739 //return m_craftdef;
2741 ITextureSource* Client::getTextureSource()
2745 IShaderSource* Client::getShaderSource()
2749 u16 Client::allocateUnknownNodeId(const std::string &name)
2751 errorstream<<"Client::allocateUnknownNodeId(): "
2752 <<"Client cannot allocate node IDs"<<std::endl;
2754 return CONTENT_IGNORE;
2756 ISoundManager* Client::getSoundManager()
2760 MtEventManager* Client::getEventManager()
2765 scene::IAnimatedMesh* Client::getMesh(const std::string &filename)
2767 std::map<std::string, std::string>::const_iterator i =
2768 m_mesh_data.find(filename);
2769 if(i == m_mesh_data.end()){
2770 errorstream<<"Client::getMesh(): Mesh not found: \""<<filename<<"\""
2774 const std::string &data = i->second;
2775 scene::ISceneManager *smgr = m_device->getSceneManager();
2777 // Create the mesh, remove it from cache and return it
2778 // This allows unique vertex colors and other properties for each instance
2779 Buffer<char> data_rw(data.c_str(), data.size()); // Const-incorrect Irrlicht
2780 io::IFileSystem *irrfs = m_device->getFileSystem();
2781 io::IReadFile *rfile = irrfs->createMemoryReadFile(
2782 *data_rw, data_rw.getSize(), filename.c_str());
2785 scene::IAnimatedMesh *mesh = smgr->getMesh(rfile);
2787 // NOTE: By playing with Irrlicht refcounts, maybe we could cache a bunch
2788 // of uniquely named instances and re-use them
2790 smgr->getMeshCache()->removeMesh(mesh);