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 "util/directiontables.h"
51 #include "util/pointedthing.h"
53 #include "drawscene.h"
55 extern gui::IGUIEnvironment* guienv;
61 QueuedMeshUpdate::QueuedMeshUpdate():
64 ack_block_to_server(false)
68 QueuedMeshUpdate::~QueuedMeshUpdate()
78 MeshUpdateQueue::MeshUpdateQueue()
82 MeshUpdateQueue::~MeshUpdateQueue()
84 JMutexAutoLock lock(m_mutex);
86 for(std::vector<QueuedMeshUpdate*>::iterator
88 i != m_queue.end(); i++)
90 QueuedMeshUpdate *q = *i;
96 peer_id=0 adds with nobody to send to
98 void MeshUpdateQueue::addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_server, bool urgent)
100 DSTACK(__FUNCTION_NAME);
104 JMutexAutoLock lock(m_mutex);
110 Find if block is already in queue.
111 If it is, update the data and quit.
113 for(std::vector<QueuedMeshUpdate*>::iterator
115 i != m_queue.end(); i++)
117 QueuedMeshUpdate *q = *i;
123 if(ack_block_to_server)
124 q->ack_block_to_server = true;
132 QueuedMeshUpdate *q = new QueuedMeshUpdate;
135 q->ack_block_to_server = ack_block_to_server;
136 m_queue.push_back(q);
139 // Returned pointer must be deleted
140 // Returns NULL if queue is empty
141 QueuedMeshUpdate * MeshUpdateQueue::pop()
143 JMutexAutoLock lock(m_mutex);
145 bool must_be_urgent = !m_urgents.empty();
146 for(std::vector<QueuedMeshUpdate*>::iterator
148 i != m_queue.end(); i++)
150 QueuedMeshUpdate *q = *i;
151 if(must_be_urgent && m_urgents.count(q->p) == 0)
154 m_urgents.erase(q->p);
164 void * MeshUpdateThread::Thread()
168 log_register_thread("MeshUpdateThread");
170 DSTACK(__FUNCTION_NAME);
172 BEGIN_DEBUG_EXCEPTION_HANDLER
174 porting::setThreadName("MeshUpdateThread");
176 while(!StopRequested())
178 QueuedMeshUpdate *q = m_queue_in.pop();
185 ScopeProfiler sp(g_profiler, "Client: Mesh making");
187 MapBlockMesh *mesh_new = new MapBlockMesh(q->data, m_camera_offset);
188 if(mesh_new->getMesh()->getMeshBufferCount() == 0)
197 r.ack_block_to_server = q->ack_block_to_server;
199 m_queue_out.push_back(r);
204 END_DEBUG_EXCEPTION_HANDLER(errorstream)
214 IrrlichtDevice *device,
215 const char *playername,
216 std::string password,
217 MapDrawControl &control,
218 IWritableTextureSource *tsrc,
219 IWritableShaderSource *shsrc,
220 IWritableItemDefManager *itemdef,
221 IWritableNodeDefManager *nodedef,
222 ISoundManager *sound,
223 MtEventManager *event,
226 m_packetcounter_timer(0.0),
227 m_connection_reinit_timer(0.1),
228 m_avg_rtt_timer(0.0),
229 m_playerpos_send_timer(0.0),
230 m_ignore_damage_timer(0.0),
237 m_mesh_update_thread(this),
239 new ClientMap(this, this, control,
240 device->getSceneManager()->getRootSceneNode(),
241 device->getSceneManager(), 666),
242 device->getSceneManager(),
245 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, ipv6, this),
247 m_server_ser_ver(SER_FMT_VER_INVALID),
249 m_inventory_updated(false),
250 m_inventory_from_server(NULL),
251 m_inventory_from_server_age(0.0),
256 m_password(password),
257 m_access_denied(false),
258 m_itemdef_received(false),
259 m_nodedef_received(false),
260 m_media_downloader(new ClientMediaDownloader()),
261 m_time_of_day_set(false),
262 m_last_time_of_day_f(-1),
263 m_time_of_day_update_timer(0),
264 m_recommended_send_interval(0.1),
265 m_removed_sounds_check_timer(0),
272 Player *player = new LocalPlayer(this);
274 player->updateName(playername);
276 m_env.addPlayer(player);
282 //request all client managed threads to stop
283 m_mesh_update_thread.Stop();
286 bool Client::isShutdown()
289 if (!m_mesh_update_thread.IsRunning()) return true;
298 m_mesh_update_thread.Stop();
299 m_mesh_update_thread.Wait();
300 while(!m_mesh_update_thread.m_queue_out.empty()) {
301 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_frontNoEx();
306 delete m_inventory_from_server;
308 // Delete detached inventories
309 for(std::map<std::string, Inventory*>::iterator
310 i = m_detached_inventories.begin();
311 i != m_detached_inventories.end(); i++){
315 // cleanup 3d model meshes on client shutdown
316 while (m_device->getSceneManager()->getMeshCache()->getMeshCount() != 0) {
317 scene::IAnimatedMesh * mesh =
318 m_device->getSceneManager()->getMeshCache()->getMeshByIndex(0);
321 m_device->getSceneManager()->getMeshCache()->removeMesh(mesh);
325 void Client::connect(Address address)
327 DSTACK(__FUNCTION_NAME);
328 m_con.SetTimeoutMs(0);
329 m_con.Connect(address);
332 void Client::step(float dtime)
334 DSTACK(__FUNCTION_NAME);
340 if(m_ignore_damage_timer > dtime)
341 m_ignore_damage_timer -= dtime;
343 m_ignore_damage_timer = 0.0;
345 m_animation_time += dtime;
346 if(m_animation_time > 60.0)
347 m_animation_time -= 60.0;
349 m_time_of_day_update_timer += dtime;
357 float &counter = m_packetcounter_timer;
363 infostream << "Client packetcounter (" << m_packetcounter_timer
365 m_packetcounter.print(infostream);
366 m_packetcounter.clear();
373 Delete unused sectors
375 NOTE: This jams the game for a while because deleting sectors
379 float &counter = m_delete_unused_sectors_timer;
387 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
389 core::list<v3s16> deleted_blocks;
391 float delete_unused_sectors_timeout =
392 g_settings->getFloat("client_delete_unused_sectors_timeout");
394 // Delete sector blocks
395 /*u32 num = m_env.getMap().unloadUnusedData
396 (delete_unused_sectors_timeout,
397 true, &deleted_blocks);*/
399 // Delete whole sectors
400 m_env.getMap().unloadUnusedData
401 (delete_unused_sectors_timeout,
404 if(deleted_blocks.size() > 0)
406 /*infostream<<"Client: Deleted blocks of "<<num
407 <<" unused sectors"<<std::endl;*/
408 /*infostream<<"Client: Deleted "<<num
409 <<" unused sectors"<<std::endl;*/
415 // Env is locked so con can be locked.
416 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
418 core::list<v3s16>::Iterator i = deleted_blocks.begin();
419 core::list<v3s16> sendlist;
422 if(sendlist.size() == 255 || i == deleted_blocks.end())
424 if(sendlist.size() == 0)
433 u32 replysize = 2+1+6*sendlist.size();
434 SharedBuffer<u8> reply(replysize);
435 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
436 reply[2] = sendlist.size();
438 for(core::list<v3s16>::Iterator
439 j = sendlist.begin();
440 j != sendlist.end(); j++)
442 writeV3S16(&reply[2+1+6*k], *j);
445 m_con.Send(PEER_ID_SERVER, 1, reply, true);
447 if(i == deleted_blocks.end())
453 sendlist.push_back(*i);
460 // UGLY hack to fix 2 second startup delay caused by non existent
461 // server client startup synchronization in local server or singleplayer mode
462 static bool initial_step = true;
464 initial_step = false;
466 else if(m_state == LC_Created)
468 float &counter = m_connection_reinit_timer;
474 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
476 Player *myplayer = m_env.getLocalPlayer();
477 assert(myplayer != NULL);
478 // Send TOSERVER_INIT
479 // [0] u16 TOSERVER_INIT
480 // [2] u8 SER_FMT_VER_HIGHEST_READ
481 // [3] u8[20] player_name
482 // [23] u8[28] password (new in some version)
483 // [51] u16 minimum supported network protocol version (added sometime)
484 // [53] u16 maximum supported network protocol version (added later than the previous one)
485 SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2+2);
486 writeU16(&data[0], TOSERVER_INIT);
487 writeU8(&data[2], SER_FMT_VER_HIGHEST_READ);
489 memset((char*)&data[3], 0, PLAYERNAME_SIZE);
490 snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
492 /*infostream<<"Client: sending initial password hash: \""<<m_password<<"\""
495 memset((char*)&data[23], 0, PASSWORD_SIZE);
496 snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
498 writeU16(&data[51], CLIENT_PROTOCOL_VERSION_MIN);
499 writeU16(&data[53], CLIENT_PROTOCOL_VERSION_MAX);
501 // Send as unreliable
502 Send(1, data, false);
505 // Not connected, return
510 Do stuff if connected
514 Run Map's timers and unload unused data
516 const float map_timer_and_unload_dtime = 5.25;
517 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
519 ScopeProfiler sp(g_profiler, "Client: map timer and unload");
520 std::list<v3s16> deleted_blocks;
521 m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
522 g_settings->getFloat("client_unload_unused_data_timeout"),
525 /*if(deleted_blocks.size() > 0)
526 infostream<<"Client: Unloaded "<<deleted_blocks.size()
527 <<" unused blocks"<<std::endl;*/
531 NOTE: This loop is intentionally iterated the way it is.
534 std::list<v3s16>::iterator i = deleted_blocks.begin();
535 std::list<v3s16> sendlist;
538 if(sendlist.size() == 255 || i == deleted_blocks.end())
540 if(sendlist.size() == 0)
549 u32 replysize = 2+1+6*sendlist.size();
550 SharedBuffer<u8> reply(replysize);
551 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
552 reply[2] = sendlist.size();
554 for(std::list<v3s16>::iterator
555 j = sendlist.begin();
556 j != sendlist.end(); ++j)
558 writeV3S16(&reply[2+1+6*k], *j);
561 m_con.Send(PEER_ID_SERVER, 2, reply, true);
563 if(i == deleted_blocks.end())
569 sendlist.push_back(*i);
578 // Control local player (0ms)
579 LocalPlayer *player = m_env.getLocalPlayer();
580 assert(player != NULL);
581 player->applyControl(dtime);
591 ClientEnvEvent event = m_env.getClientEvent();
592 if(event.type == CEE_NONE)
596 else if(event.type == CEE_PLAYER_DAMAGE)
598 if(m_ignore_damage_timer <= 0)
600 u8 damage = event.player_damage.amount;
602 if(event.player_damage.send_to_server)
605 // Add to ClientEvent queue
607 event.type = CE_PLAYER_DAMAGE;
608 event.player_damage.amount = damage;
609 m_client_event_queue.push_back(event);
612 else if(event.type == CEE_PLAYER_BREATH)
614 u16 breath = event.player_breath.amount;
624 float &counter = m_avg_rtt_timer;
629 // connectedAndInitialized() is true, peer exists.
630 float avg_rtt = getRTT();
631 infostream<<"Client: avg_rtt="<<avg_rtt<<std::endl;
636 Send player position to server
639 float &counter = m_playerpos_send_timer;
641 if((m_state == LC_Ready) && (counter >= m_recommended_send_interval))
649 Replace updated meshes
652 int num_processed_meshes = 0;
653 while(!m_mesh_update_thread.m_queue_out.empty())
655 num_processed_meshes++;
656 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_frontNoEx();
657 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
660 // Delete the old mesh
661 if(block->mesh != NULL)
663 // TODO: Remove hardware buffers of meshbuffers of block->mesh
668 // Replace with the new mesh
669 block->mesh = r.mesh;
673 if(r.ack_block_to_server)
685 u32 replysize = 2+1+6;
686 SharedBuffer<u8> reply(replysize);
687 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
689 writeV3S16(&reply[3], r.p);
691 m_con.Send(PEER_ID_SERVER, 2, reply, true);
694 if(num_processed_meshes > 0)
695 g_profiler->graphAdd("num_processed_meshes", num_processed_meshes);
701 if (m_media_downloader && m_media_downloader->isStarted()) {
702 m_media_downloader->step(this);
703 if (m_media_downloader->isDone()) {
705 delete m_media_downloader;
706 m_media_downloader = NULL;
711 If the server didn't update the inventory in a while, revert
712 the local inventory (so the player notices the lag problem
713 and knows something is wrong).
715 if(m_inventory_from_server)
717 float interval = 10.0;
718 float count_before = floor(m_inventory_from_server_age / interval);
720 m_inventory_from_server_age += dtime;
722 float count_after = floor(m_inventory_from_server_age / interval);
724 if(count_after != count_before)
726 // Do this every <interval> seconds after TOCLIENT_INVENTORY
727 // Reset the locally changed inventory to the authoritative inventory
728 Player *player = m_env.getLocalPlayer();
729 player->inventory = *m_inventory_from_server;
730 m_inventory_updated = true;
735 Update positions of sounds attached to objects
738 for(std::map<int, u16>::iterator
739 i = m_sounds_to_objects.begin();
740 i != m_sounds_to_objects.end(); i++)
742 int client_id = i->first;
743 u16 object_id = i->second;
744 ClientActiveObject *cao = m_env.getActiveObject(object_id);
747 v3f pos = cao->getPosition();
748 m_sound->updateSoundPosition(client_id, pos);
753 Handle removed remotely initiated sounds
755 m_removed_sounds_check_timer += dtime;
756 if(m_removed_sounds_check_timer >= 2.32)
758 m_removed_sounds_check_timer = 0;
759 // Find removed sounds and clear references to them
760 std::set<s32> removed_server_ids;
761 for(std::map<s32, int>::iterator
762 i = m_sounds_server_to_client.begin();
763 i != m_sounds_server_to_client.end();)
765 s32 server_id = i->first;
766 int client_id = i->second;
768 if(!m_sound->soundExists(client_id)){
769 m_sounds_server_to_client.erase(server_id);
770 m_sounds_client_to_server.erase(client_id);
771 m_sounds_to_objects.erase(client_id);
772 removed_server_ids.insert(server_id);
776 if(removed_server_ids.size() != 0)
778 std::ostringstream os(std::ios_base::binary);
779 writeU16(os, TOSERVER_REMOVED_SOUNDS);
780 size_t server_ids = removed_server_ids.size();
781 assert(server_ids <= 0xFFFF);
782 writeU16(os, (u16) (server_ids & 0xFFFF));
783 for(std::set<s32>::iterator i = removed_server_ids.begin();
784 i != removed_server_ids.end(); i++)
786 std::string s = os.str();
787 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
794 bool Client::loadMedia(const std::string &data, const std::string &filename)
796 // Silly irrlicht's const-incorrectness
797 Buffer<char> data_rw(data.c_str(), data.size());
801 const char *image_ext[] = {
802 ".png", ".jpg", ".bmp", ".tga",
803 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
806 name = removeStringEnd(filename, image_ext);
809 verbosestream<<"Client: Attempting to load image "
810 <<"file \""<<filename<<"\""<<std::endl;
812 io::IFileSystem *irrfs = m_device->getFileSystem();
813 video::IVideoDriver *vdrv = m_device->getVideoDriver();
815 // Create an irrlicht memory file
816 io::IReadFile *rfile = irrfs->createMemoryReadFile(
817 *data_rw, data_rw.getSize(), "_tempreadfile");
820 video::IImage *img = vdrv->createImageFromFile(rfile);
822 errorstream<<"Client: Cannot create image from data of "
823 <<"file \""<<filename<<"\""<<std::endl;
828 m_tsrc->insertSourceImage(filename, img);
835 const char *sound_ext[] = {
836 ".0.ogg", ".1.ogg", ".2.ogg", ".3.ogg", ".4.ogg",
837 ".5.ogg", ".6.ogg", ".7.ogg", ".8.ogg", ".9.ogg",
840 name = removeStringEnd(filename, sound_ext);
843 verbosestream<<"Client: Attempting to load sound "
844 <<"file \""<<filename<<"\""<<std::endl;
845 m_sound->loadSoundData(name, data);
849 const char *model_ext[] = {
850 ".x", ".b3d", ".md2", ".obj",
853 name = removeStringEnd(filename, model_ext);
856 verbosestream<<"Client: Storing model into memory: "
857 <<"\""<<filename<<"\""<<std::endl;
858 if(m_mesh_data.count(filename))
859 errorstream<<"Multiple models with name \""<<filename.c_str()
860 <<"\" found; replacing previous model"<<std::endl;
861 m_mesh_data[filename] = data;
865 errorstream<<"Client: Don't know how to load file \""
866 <<filename<<"\""<<std::endl;
870 // Virtual methods from con::PeerHandler
871 void Client::peerAdded(con::Peer *peer)
873 infostream<<"Client::peerAdded(): peer->id="
874 <<peer->id<<std::endl;
876 void Client::deletingPeer(con::Peer *peer, bool timeout)
878 infostream<<"Client::deletingPeer(): "
879 "Server Peer is getting deleted "
880 <<"(timeout="<<timeout<<")"<<std::endl;
885 u16 number of files requested
891 void Client::request_media(const std::list<std::string> &file_requests)
893 std::ostringstream os(std::ios_base::binary);
894 writeU16(os, TOSERVER_REQUEST_MEDIA);
895 size_t file_requests_size = file_requests.size();
896 assert(file_requests_size <= 0xFFFF);
897 writeU16(os, (u16) (file_requests_size & 0xFFFF));
899 for(std::list<std::string>::const_iterator i = file_requests.begin();
900 i != file_requests.end(); ++i) {
901 os<<serializeString(*i);
905 std::string s = os.str();
906 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
909 infostream<<"Client: Sending media request list to server ("
910 <<file_requests.size()<<" files)"<<std::endl;
913 void Client::received_media()
915 // notify server we received everything
916 std::ostringstream os(std::ios_base::binary);
917 writeU16(os, TOSERVER_RECEIVED_MEDIA);
918 std::string s = os.str();
919 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
922 infostream<<"Client: Notifying server that we received all media"
926 void Client::ReceiveAll()
928 DSTACK(__FUNCTION_NAME);
929 u32 start_ms = porting::getTimeMs();
932 // Limit time even if there would be huge amounts of data to
934 if(porting::getTimeMs() > start_ms + 100)
939 g_profiler->graphAdd("client_received_packets", 1);
941 catch(con::NoIncomingDataException &e)
945 catch(con::InvalidIncomingDataException &e)
947 infostream<<"Client::ReceiveAll(): "
948 "InvalidIncomingDataException: what()="
949 <<e.what()<<std::endl;
954 void Client::Receive()
956 DSTACK(__FUNCTION_NAME);
957 SharedBuffer<u8> data;
959 u32 datasize = m_con.Receive(sender_peer_id, data);
960 ProcessData(*data, datasize, sender_peer_id);
964 sender_peer_id given to this shall be quaranteed to be a valid peer
966 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
968 DSTACK(__FUNCTION_NAME);
970 // Ignore packets that don't even fit a command
973 m_packetcounter.add(60000);
977 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
979 //infostream<<"Client: received command="<<command<<std::endl;
980 m_packetcounter.add((u16)command);
983 If this check is removed, be sure to change the queue
984 system to know the ids
986 if(sender_peer_id != PEER_ID_SERVER)
988 infostream<<"Client::ProcessData(): Discarding data not "
989 "coming from server: peer_id="<<sender_peer_id
994 u8 ser_version = m_server_ser_ver;
996 if(command == TOCLIENT_INIT)
1001 u8 deployed = data[2];
1003 infostream<<"Client: TOCLIENT_INIT received with "
1004 "deployed="<<((int)deployed&0xff)<<std::endl;
1006 if(!ser_ver_supported(deployed))
1008 infostream<<"Client: TOCLIENT_INIT: Server sent "
1009 <<"unsupported ser_fmt_ver"<<std::endl;
1013 m_server_ser_ver = deployed;
1015 // Get player position
1016 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
1017 if(datasize >= 2+1+6)
1018 playerpos_s16 = readV3S16(&data[2+1]);
1019 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
1022 // Set player position
1023 Player *player = m_env.getLocalPlayer();
1024 assert(player != NULL);
1025 player->setPosition(playerpos_f);
1027 if(datasize >= 2+1+6+8)
1030 m_map_seed = readU64(&data[2+1+6]);
1031 infostream<<"Client: received map seed: "<<m_map_seed<<std::endl;
1034 if(datasize >= 2+1+6+8+4)
1037 m_recommended_send_interval = readF1000(&data[2+1+6+8]);
1038 infostream<<"Client: received recommended send interval "
1039 <<m_recommended_send_interval<<std::endl;
1044 SharedBuffer<u8> reply(replysize);
1045 writeU16(&reply[0], TOSERVER_INIT2);
1047 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1054 if(command == TOCLIENT_ACCESS_DENIED)
1056 // The server didn't like our password. Note, this needs
1057 // to be processed even if the serialisation format has
1058 // not been agreed yet, the same as TOCLIENT_INIT.
1059 m_access_denied = true;
1060 m_access_denied_reason = L"Unknown";
1063 std::string datastring((char*)&data[2], datasize-2);
1064 std::istringstream is(datastring, std::ios_base::binary);
1065 m_access_denied_reason = deSerializeWideString(is);
1070 if(ser_version == SER_FMT_VER_INVALID)
1072 infostream<<"Client: Server serialization"
1073 " format invalid or not initialized."
1074 " Skipping incoming command="<<command<<std::endl;
1079 Handle runtime commands
1081 // there's no sane reason why we shouldn't have a player and
1082 // almost everyone needs a player reference
1083 Player *player = m_env.getLocalPlayer();
1084 assert(player != NULL);
1086 if(command == TOCLIENT_REMOVENODE)
1091 p.X = readS16(&data[2]);
1092 p.Y = readS16(&data[4]);
1093 p.Z = readS16(&data[6]);
1096 else if(command == TOCLIENT_ADDNODE)
1098 if(datasize < 8 + MapNode::serializedLength(ser_version))
1102 p.X = readS16(&data[2]);
1103 p.Y = readS16(&data[4]);
1104 p.Z = readS16(&data[6]);
1107 n.deSerialize(&data[8], ser_version);
1109 bool remove_metadata = true;
1110 u32 index = 8 + MapNode::serializedLength(ser_version);
1111 if ((datasize >= index+1) && data[index]){
1112 remove_metadata = false;
1115 addNode(p, n, remove_metadata);
1117 else if(command == TOCLIENT_BLOCKDATA)
1119 // Ignore too small packet
1124 p.X = readS16(&data[2]);
1125 p.Y = readS16(&data[4]);
1126 p.Z = readS16(&data[6]);
1128 std::string datastring((char*)&data[8], datasize-8);
1129 std::istringstream istr(datastring, std::ios_base::binary);
1134 v2s16 p2d(p.X, p.Z);
1135 sector = m_env.getMap().emergeSector(p2d);
1137 assert(sector->getPos() == p2d);
1139 block = sector->getBlockNoCreateNoEx(p.Y);
1143 Update an existing block
1145 block->deSerialize(istr, ser_version, false);
1146 block->deSerializeNetworkSpecific(istr);
1153 block = new MapBlock(&m_env.getMap(), p, this);
1154 block->deSerialize(istr, ser_version, false);
1155 block->deSerializeNetworkSpecific(istr);
1156 sector->insertBlock(block);
1160 Add it to mesh update queue and set it to be acknowledged after update.
1162 addUpdateMeshTaskWithEdge(p, true);
1164 else if(command == TOCLIENT_INVENTORY)
1169 std::string datastring((char*)&data[2], datasize-2);
1170 std::istringstream is(datastring, std::ios_base::binary);
1172 player->inventory.deSerialize(is);
1174 m_inventory_updated = true;
1176 delete m_inventory_from_server;
1177 m_inventory_from_server = new Inventory(player->inventory);
1178 m_inventory_from_server_age = 0.0;
1181 else if(command == TOCLIENT_TIME_OF_DAY)
1186 u16 time_of_day = readU16(&data[2]);
1187 time_of_day = time_of_day % 24000;
1188 float time_speed = 0;
1190 if(datasize >= 2 + 2 + 4)
1192 time_speed = readF1000(&data[4]);
1195 // Old message; try to approximate speed of time by ourselves
1196 float time_of_day_f = (float)time_of_day / 24000.0;
1197 float tod_diff_f = 0;
1199 if(time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
1200 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0;
1202 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
1204 m_last_time_of_day_f = time_of_day_f;
1205 float time_diff = m_time_of_day_update_timer;
1206 m_time_of_day_update_timer = 0;
1208 if(m_time_of_day_set){
1209 time_speed = (3600.0*24.0) * tod_diff_f / time_diff;
1210 infostream<<"Client: Measured time_of_day speed (old format): "
1211 <<time_speed<<" tod_diff_f="<<tod_diff_f
1212 <<" time_diff="<<time_diff<<std::endl;
1216 // Update environment
1217 m_env.setTimeOfDay(time_of_day);
1218 m_env.setTimeOfDaySpeed(time_speed);
1219 m_time_of_day_set = true;
1221 u32 dr = m_env.getDayNightRatio();
1222 infostream<<"Client: time_of_day="<<time_of_day
1223 <<" time_speed="<<time_speed
1224 <<" dr="<<dr<<std::endl;
1226 else if(command == TOCLIENT_CHAT_MESSAGE)
1234 std::string datastring((char*)&data[2], datasize-2);
1235 std::istringstream is(datastring, std::ios_base::binary);
1238 is.read((char*) buf, 2);
1239 u16 len = readU16(buf);
1241 std::wstring message;
1242 for(unsigned int i=0; i<len; i++)
1244 is.read((char*)buf, 2);
1245 message += (wchar_t)readU16(buf);
1248 m_chat_queue.push_back(message);
1250 else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1254 u16 count of removed objects
1255 for all removed objects {
1258 u16 count of added objects
1259 for all added objects {
1262 u32 initialization data length
1263 string initialization data
1268 // Get all data except the command number
1269 std::string datastring((char*)&data[2], datasize-2);
1270 // Throw them in an istringstream
1271 std::istringstream is(datastring, std::ios_base::binary);
1273 // Read removed objects
1275 u16 removed_count = readU16((u8*)buf);
1276 for(unsigned int i=0; i<removed_count; i++)
1279 u16 id = readU16((u8*)buf);
1280 m_env.removeActiveObject(id);
1283 // Read added objects
1285 u16 added_count = readU16((u8*)buf);
1286 for(unsigned int i=0; i<added_count; i++)
1289 u16 id = readU16((u8*)buf);
1291 u8 type = readU8((u8*)buf);
1292 std::string data = deSerializeLongString(is);
1294 m_env.addActiveObject(id, type, data);
1297 else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1309 // Get all data except the command number
1310 std::string datastring((char*)&data[2], datasize-2);
1311 // Throw them in an istringstream
1312 std::istringstream is(datastring, std::ios_base::binary);
1314 while(is.eof() == false)
1317 u16 id = readU16((u8*)buf);
1321 size_t message_size = readU16((u8*)buf);
1322 std::string message;
1323 message.reserve(message_size);
1324 for(unsigned int i=0; i<message_size; i++)
1327 message.append(buf, 1);
1329 // Pass on to the environment
1330 m_env.processActiveObjectMessage(id, message);
1333 else if(command == TOCLIENT_MOVEMENT)
1335 std::string datastring((char*)&data[2], datasize-2);
1336 std::istringstream is(datastring, std::ios_base::binary);
1338 player->movement_acceleration_default = readF1000(is) * BS;
1339 player->movement_acceleration_air = readF1000(is) * BS;
1340 player->movement_acceleration_fast = readF1000(is) * BS;
1341 player->movement_speed_walk = readF1000(is) * BS;
1342 player->movement_speed_crouch = readF1000(is) * BS;
1343 player->movement_speed_fast = readF1000(is) * BS;
1344 player->movement_speed_climb = readF1000(is) * BS;
1345 player->movement_speed_jump = readF1000(is) * BS;
1346 player->movement_liquid_fluidity = readF1000(is) * BS;
1347 player->movement_liquid_fluidity_smooth = readF1000(is) * BS;
1348 player->movement_liquid_sink = readF1000(is) * BS;
1349 player->movement_gravity = readF1000(is) * BS;
1351 else if(command == TOCLIENT_HP)
1353 std::string datastring((char*)&data[2], datasize-2);
1354 std::istringstream is(datastring, std::ios_base::binary);
1356 u8 oldhp = player->hp;
1362 // Add to ClientEvent queue
1364 event.type = CE_PLAYER_DAMAGE;
1365 event.player_damage.amount = oldhp - hp;
1366 m_client_event_queue.push_back(event);
1369 else if(command == TOCLIENT_BREATH)
1371 std::string datastring((char*)&data[2], datasize-2);
1372 std::istringstream is(datastring, std::ios_base::binary);
1374 player->setBreath(readU16(is));
1376 else if(command == TOCLIENT_MOVE_PLAYER)
1378 std::string datastring((char*)&data[2], datasize-2);
1379 std::istringstream is(datastring, std::ios_base::binary);
1381 v3f pos = readV3F1000(is);
1382 f32 pitch = readF1000(is);
1383 f32 yaw = readF1000(is);
1384 player->setPosition(pos);
1386 infostream<<"Client got TOCLIENT_MOVE_PLAYER"
1387 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1393 Add to ClientEvent queue.
1394 This has to be sent to the main program because otherwise
1395 it would just force the pitch and yaw values to whatever
1396 the camera points to.
1399 event.type = CE_PLAYER_FORCE_MOVE;
1400 event.player_force_move.pitch = pitch;
1401 event.player_force_move.yaw = yaw;
1402 m_client_event_queue.push_back(event);
1404 // Ignore damage for a few seconds, so that the player doesn't
1405 // get damage from falling on ground
1406 m_ignore_damage_timer = 3.0;
1408 else if(command == TOCLIENT_PLAYERITEM)
1410 infostream<<"Client: WARNING: Ignoring TOCLIENT_PLAYERITEM"<<std::endl;
1412 else if(command == TOCLIENT_DEATHSCREEN)
1414 std::string datastring((char*)&data[2], datasize-2);
1415 std::istringstream is(datastring, std::ios_base::binary);
1417 bool set_camera_point_target = readU8(is);
1418 v3f camera_point_target = readV3F1000(is);
1421 event.type = CE_DEATHSCREEN;
1422 event.deathscreen.set_camera_point_target = set_camera_point_target;
1423 event.deathscreen.camera_point_target_x = camera_point_target.X;
1424 event.deathscreen.camera_point_target_y = camera_point_target.Y;
1425 event.deathscreen.camera_point_target_z = camera_point_target.Z;
1426 m_client_event_queue.push_back(event);
1428 else if(command == TOCLIENT_ANNOUNCE_MEDIA)
1430 std::string datastring((char*)&data[2], datasize-2);
1431 std::istringstream is(datastring, std::ios_base::binary);
1433 int num_files = readU16(is);
1435 infostream<<"Client: Received media announcement: packet size: "
1436 <<datasize<<std::endl;
1438 if (m_media_downloader == NULL ||
1439 m_media_downloader->isStarted()) {
1440 const char *problem = m_media_downloader ?
1441 "we already saw another announcement" :
1442 "all media has been received already";
1443 errorstream<<"Client: Received media announcement but "
1445 <<" files="<<num_files
1446 <<" size="<<datasize<<std::endl;
1450 // Mesh update thread must be stopped while
1451 // updating content definitions
1452 assert(!m_mesh_update_thread.IsRunning());
1454 for(int i=0; i<num_files; i++)
1456 std::string name = deSerializeString(is);
1457 std::string sha1_base64 = deSerializeString(is);
1458 std::string sha1_raw = base64_decode(sha1_base64);
1459 m_media_downloader->addFile(name, sha1_raw);
1462 std::vector<std::string> remote_media;
1464 Strfnd sf(deSerializeString(is));
1465 while(!sf.atend()) {
1466 std::string baseurl = trim(sf.next(","));
1468 m_media_downloader->addRemoteServer(baseurl);
1471 catch(SerializationError& e) {
1472 // not supported by server or turned off
1475 m_media_downloader->step(this);
1477 else if(command == TOCLIENT_MEDIA)
1479 std::string datastring((char*)&data[2], datasize-2);
1480 std::istringstream is(datastring, std::ios_base::binary);
1484 u16 total number of file bunches
1485 u16 index of this bunch
1486 u32 number of files in this bunch
1494 int num_bunches = readU16(is);
1495 int bunch_i = readU16(is);
1496 u32 num_files = readU32(is);
1497 infostream<<"Client: Received files: bunch "<<bunch_i<<"/"
1498 <<num_bunches<<" files="<<num_files
1499 <<" size="<<datasize<<std::endl;
1504 if (m_media_downloader == NULL ||
1505 !m_media_downloader->isStarted()) {
1506 const char *problem = m_media_downloader ?
1507 "media has not been requested" :
1508 "all media has been received already";
1509 errorstream<<"Client: Received media but "
1511 <<" bunch "<<bunch_i<<"/"<<num_bunches
1512 <<" files="<<num_files
1513 <<" size="<<datasize<<std::endl;
1517 // Mesh update thread must be stopped while
1518 // updating content definitions
1519 assert(!m_mesh_update_thread.IsRunning());
1521 for(unsigned int i=0; i<num_files; i++){
1522 std::string name = deSerializeString(is);
1523 std::string data = deSerializeLongString(is);
1524 m_media_downloader->conventionalTransferDone(
1528 else if(command == TOCLIENT_TOOLDEF)
1530 infostream<<"Client: WARNING: Ignoring TOCLIENT_TOOLDEF"<<std::endl;
1532 else if(command == TOCLIENT_NODEDEF)
1534 infostream<<"Client: Received node definitions: packet size: "
1535 <<datasize<<std::endl;
1537 // Mesh update thread must be stopped while
1538 // updating content definitions
1539 assert(!m_mesh_update_thread.IsRunning());
1541 // Decompress node definitions
1542 std::string datastring((char*)&data[2], datasize-2);
1543 std::istringstream is(datastring, std::ios_base::binary);
1544 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1545 std::ostringstream tmp_os;
1546 decompressZlib(tmp_is, tmp_os);
1548 // Deserialize node definitions
1549 std::istringstream tmp_is2(tmp_os.str());
1550 m_nodedef->deSerialize(tmp_is2);
1551 m_nodedef_received = true;
1553 else if(command == TOCLIENT_CRAFTITEMDEF)
1555 infostream<<"Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF"<<std::endl;
1557 else if(command == TOCLIENT_ITEMDEF)
1559 infostream<<"Client: Received item definitions: packet size: "
1560 <<datasize<<std::endl;
1562 // Mesh update thread must be stopped while
1563 // updating content definitions
1564 assert(!m_mesh_update_thread.IsRunning());
1566 // Decompress item definitions
1567 std::string datastring((char*)&data[2], datasize-2);
1568 std::istringstream is(datastring, std::ios_base::binary);
1569 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1570 std::ostringstream tmp_os;
1571 decompressZlib(tmp_is, tmp_os);
1573 // Deserialize node definitions
1574 std::istringstream tmp_is2(tmp_os.str());
1575 m_itemdef->deSerialize(tmp_is2);
1576 m_itemdef_received = true;
1578 else if(command == TOCLIENT_PLAY_SOUND)
1580 std::string datastring((char*)&data[2], datasize-2);
1581 std::istringstream is(datastring, std::ios_base::binary);
1583 s32 server_id = readS32(is);
1584 std::string name = deSerializeString(is);
1585 float gain = readF1000(is);
1586 int type = readU8(is); // 0=local, 1=positional, 2=object
1587 v3f pos = readV3F1000(is);
1588 u16 object_id = readU16(is);
1589 bool loop = readU8(is);
1594 client_id = m_sound->playSound(name, loop, gain);
1596 case 1: // positional
1597 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1600 ClientActiveObject *cao = m_env.getActiveObject(object_id);
1602 pos = cao->getPosition();
1603 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1604 // TODO: Set up sound to move with object
1609 if(client_id != -1){
1610 m_sounds_server_to_client[server_id] = client_id;
1611 m_sounds_client_to_server[client_id] = server_id;
1613 m_sounds_to_objects[client_id] = object_id;
1616 else if(command == TOCLIENT_STOP_SOUND)
1618 std::string datastring((char*)&data[2], datasize-2);
1619 std::istringstream is(datastring, std::ios_base::binary);
1621 s32 server_id = readS32(is);
1622 std::map<s32, int>::iterator i =
1623 m_sounds_server_to_client.find(server_id);
1624 if(i != m_sounds_server_to_client.end()){
1625 int client_id = i->second;
1626 m_sound->stopSound(client_id);
1629 else if(command == TOCLIENT_PRIVILEGES)
1631 std::string datastring((char*)&data[2], datasize-2);
1632 std::istringstream is(datastring, std::ios_base::binary);
1634 m_privileges.clear();
1635 infostream<<"Client: Privileges updated: ";
1636 u16 num_privileges = readU16(is);
1637 for(unsigned int i=0; i<num_privileges; i++){
1638 std::string priv = deSerializeString(is);
1639 m_privileges.insert(priv);
1640 infostream<<priv<<" ";
1642 infostream<<std::endl;
1644 else if(command == TOCLIENT_INVENTORY_FORMSPEC)
1646 std::string datastring((char*)&data[2], datasize-2);
1647 std::istringstream is(datastring, std::ios_base::binary);
1649 // Store formspec in LocalPlayer
1650 player->inventory_formspec = deSerializeLongString(is);
1652 else if(command == TOCLIENT_DETACHED_INVENTORY)
1654 std::string datastring((char*)&data[2], datasize-2);
1655 std::istringstream is(datastring, std::ios_base::binary);
1657 std::string name = deSerializeString(is);
1659 infostream<<"Client: Detached inventory update: \""<<name<<"\""<<std::endl;
1661 Inventory *inv = NULL;
1662 if(m_detached_inventories.count(name) > 0)
1663 inv = m_detached_inventories[name];
1665 inv = new Inventory(m_itemdef);
1666 m_detached_inventories[name] = inv;
1668 inv->deSerialize(is);
1670 else if(command == TOCLIENT_SHOW_FORMSPEC)
1672 std::string datastring((char*)&data[2], datasize-2);
1673 std::istringstream is(datastring, std::ios_base::binary);
1675 std::string formspec = deSerializeLongString(is);
1676 std::string formname = deSerializeString(is);
1679 event.type = CE_SHOW_FORMSPEC;
1680 // pointer is required as event is a struct only!
1681 // adding a std:string to a struct isn't possible
1682 event.show_formspec.formspec = new std::string(formspec);
1683 event.show_formspec.formname = new std::string(formname);
1684 m_client_event_queue.push_back(event);
1686 else if(command == TOCLIENT_SPAWN_PARTICLE)
1688 std::string datastring((char*)&data[2], datasize-2);
1689 std::istringstream is(datastring, std::ios_base::binary);
1691 v3f pos = readV3F1000(is);
1692 v3f vel = readV3F1000(is);
1693 v3f acc = readV3F1000(is);
1694 float expirationtime = readF1000(is);
1695 float size = readF1000(is);
1696 bool collisiondetection = readU8(is);
1697 std::string texture = deSerializeLongString(is);
1698 bool vertical = false;
1700 vertical = readU8(is);
1704 event.type = CE_SPAWN_PARTICLE;
1705 event.spawn_particle.pos = new v3f (pos);
1706 event.spawn_particle.vel = new v3f (vel);
1707 event.spawn_particle.acc = new v3f (acc);
1708 event.spawn_particle.expirationtime = expirationtime;
1709 event.spawn_particle.size = size;
1710 event.spawn_particle.collisiondetection = collisiondetection;
1711 event.spawn_particle.vertical = vertical;
1712 event.spawn_particle.texture = new std::string(texture);
1714 m_client_event_queue.push_back(event);
1716 else if(command == TOCLIENT_ADD_PARTICLESPAWNER)
1718 std::string datastring((char*)&data[2], datasize-2);
1719 std::istringstream is(datastring, std::ios_base::binary);
1721 u16 amount = readU16(is);
1722 float spawntime = readF1000(is);
1723 v3f minpos = readV3F1000(is);
1724 v3f maxpos = readV3F1000(is);
1725 v3f minvel = readV3F1000(is);
1726 v3f maxvel = readV3F1000(is);
1727 v3f minacc = readV3F1000(is);
1728 v3f maxacc = readV3F1000(is);
1729 float minexptime = readF1000(is);
1730 float maxexptime = readF1000(is);
1731 float minsize = readF1000(is);
1732 float maxsize = readF1000(is);
1733 bool collisiondetection = readU8(is);
1734 std::string texture = deSerializeLongString(is);
1735 u32 id = readU32(is);
1736 bool vertical = false;
1738 vertical = readU8(is);
1742 event.type = CE_ADD_PARTICLESPAWNER;
1743 event.add_particlespawner.amount = amount;
1744 event.add_particlespawner.spawntime = spawntime;
1745 event.add_particlespawner.minpos = new v3f (minpos);
1746 event.add_particlespawner.maxpos = new v3f (maxpos);
1747 event.add_particlespawner.minvel = new v3f (minvel);
1748 event.add_particlespawner.maxvel = new v3f (maxvel);
1749 event.add_particlespawner.minacc = new v3f (minacc);
1750 event.add_particlespawner.maxacc = new v3f (maxacc);
1751 event.add_particlespawner.minexptime = minexptime;
1752 event.add_particlespawner.maxexptime = maxexptime;
1753 event.add_particlespawner.minsize = minsize;
1754 event.add_particlespawner.maxsize = maxsize;
1755 event.add_particlespawner.collisiondetection = collisiondetection;
1756 event.add_particlespawner.vertical = vertical;
1757 event.add_particlespawner.texture = new std::string(texture);
1758 event.add_particlespawner.id = id;
1760 m_client_event_queue.push_back(event);
1762 else if(command == TOCLIENT_DELETE_PARTICLESPAWNER)
1764 std::string datastring((char*)&data[2], datasize-2);
1765 std::istringstream is(datastring, std::ios_base::binary);
1767 u32 id = readU16(is);
1770 event.type = CE_DELETE_PARTICLESPAWNER;
1771 event.delete_particlespawner.id = id;
1773 m_client_event_queue.push_back(event);
1775 else if(command == TOCLIENT_HUDADD)
1777 std::string datastring((char *)&data[2], datasize - 2);
1778 std::istringstream is(datastring, std::ios_base::binary);
1780 u32 id = readU32(is);
1781 u8 type = readU8(is);
1782 v2f pos = readV2F1000(is);
1783 std::string name = deSerializeString(is);
1784 v2f scale = readV2F1000(is);
1785 std::string text = deSerializeString(is);
1786 u32 number = readU32(is);
1787 u32 item = readU32(is);
1788 u32 dir = readU32(is);
1789 v2f align = readV2F1000(is);
1790 v2f offset = readV2F1000(is);
1794 world_pos = readV3F1000(is);
1795 }catch(SerializationError &e) {};
1797 size = readV2S32(is);
1798 } catch(SerializationError &e) {};
1801 event.type = CE_HUDADD;
1802 event.hudadd.id = id;
1803 event.hudadd.type = type;
1804 event.hudadd.pos = new v2f(pos);
1805 event.hudadd.name = new std::string(name);
1806 event.hudadd.scale = new v2f(scale);
1807 event.hudadd.text = new std::string(text);
1808 event.hudadd.number = number;
1809 event.hudadd.item = item;
1810 event.hudadd.dir = dir;
1811 event.hudadd.align = new v2f(align);
1812 event.hudadd.offset = new v2f(offset);
1813 event.hudadd.world_pos = new v3f(world_pos);
1814 event.hudadd.size = new v2s32(size);
1815 m_client_event_queue.push_back(event);
1817 else if(command == TOCLIENT_HUDRM)
1819 std::string datastring((char *)&data[2], datasize - 2);
1820 std::istringstream is(datastring, std::ios_base::binary);
1822 u32 id = readU32(is);
1825 event.type = CE_HUDRM;
1826 event.hudrm.id = id;
1827 m_client_event_queue.push_back(event);
1829 else if(command == TOCLIENT_HUDCHANGE)
1837 std::string datastring((char *)&data[2], datasize - 2);
1838 std::istringstream is(datastring, std::ios_base::binary);
1840 u32 id = readU32(is);
1841 u8 stat = (HudElementStat)readU8(is);
1843 if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
1844 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
1845 v2fdata = readV2F1000(is);
1846 else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
1847 sdata = deSerializeString(is);
1848 else if (stat == HUD_STAT_WORLD_POS)
1849 v3fdata = readV3F1000(is);
1850 else if (stat == HUD_STAT_SIZE )
1851 v2s32data = readV2S32(is);
1853 intdata = readU32(is);
1856 event.type = CE_HUDCHANGE;
1857 event.hudchange.id = id;
1858 event.hudchange.stat = (HudElementStat)stat;
1859 event.hudchange.v2fdata = new v2f(v2fdata);
1860 event.hudchange.v3fdata = new v3f(v3fdata);
1861 event.hudchange.sdata = new std::string(sdata);
1862 event.hudchange.data = intdata;
1863 event.hudchange.v2s32data = new v2s32(v2s32data);
1864 m_client_event_queue.push_back(event);
1866 else if(command == TOCLIENT_HUD_SET_FLAGS)
1868 std::string datastring((char *)&data[2], datasize - 2);
1869 std::istringstream is(datastring, std::ios_base::binary);
1871 u32 flags = readU32(is);
1872 u32 mask = readU32(is);
1874 player->hud_flags &= ~mask;
1875 player->hud_flags |= flags;
1877 else if(command == TOCLIENT_HUD_SET_PARAM)
1879 std::string datastring((char *)&data[2], datasize - 2);
1880 std::istringstream is(datastring, std::ios_base::binary);
1882 u16 param = readU16(is);
1883 std::string value = deSerializeString(is);
1885 if(param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) {
1886 s32 hotbar_itemcount = readS32((u8*) value.c_str());
1887 if(hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
1888 player->hud_hotbar_itemcount = hotbar_itemcount;
1890 else if (param == HUD_PARAM_HOTBAR_IMAGE) {
1891 ((LocalPlayer *) player)->hotbar_image = value;
1893 else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
1894 ((LocalPlayer *) player)->hotbar_selected_image = value;
1897 else if(command == TOCLIENT_SET_SKY)
1899 std::string datastring((char *)&data[2], datasize - 2);
1900 std::istringstream is(datastring, std::ios_base::binary);
1902 video::SColor *bgcolor = new video::SColor(readARGB8(is));
1903 std::string *type = new std::string(deSerializeString(is));
1904 u16 count = readU16(is);
1905 std::vector<std::string> *params = new std::vector<std::string>;
1907 for(size_t i=0; i<count; i++)
1908 params->push_back(deSerializeString(is));
1911 event.type = CE_SET_SKY;
1912 event.set_sky.bgcolor = bgcolor;
1913 event.set_sky.type = type;
1914 event.set_sky.params = params;
1915 m_client_event_queue.push_back(event);
1917 else if(command == TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO)
1919 std::string datastring((char *)&data[2], datasize - 2);
1920 std::istringstream is(datastring, std::ios_base::binary);
1922 bool do_override = readU8(is);
1923 float day_night_ratio_f = (float)readU16(is) / 65536;
1926 event.type = CE_OVERRIDE_DAY_NIGHT_RATIO;
1927 event.override_day_night_ratio.do_override = do_override;
1928 event.override_day_night_ratio.ratio_f = day_night_ratio_f;
1929 m_client_event_queue.push_back(event);
1931 else if(command == TOCLIENT_LOCAL_PLAYER_ANIMATIONS)
1933 std::string datastring((char *)&data[2], datasize - 2);
1934 std::istringstream is(datastring, std::ios_base::binary);
1936 LocalPlayer *player = m_env.getLocalPlayer();
1937 assert(player != NULL);
1939 player->local_animations[0] = readV2S32(is);
1940 player->local_animations[1] = readV2S32(is);
1941 player->local_animations[2] = readV2S32(is);
1942 player->local_animations[3] = readV2S32(is);
1943 player->local_animation_speed = readF1000(is);
1945 else if(command == TOCLIENT_EYE_OFFSET)
1947 std::string datastring((char *)&data[2], datasize - 2);
1948 std::istringstream is(datastring, std::ios_base::binary);
1950 LocalPlayer *player = m_env.getLocalPlayer();
1951 assert(player != NULL);
1953 player->eye_offset_first = readV3F1000(is);
1954 player->eye_offset_third = readV3F1000(is);
1958 infostream<<"Client: Ignoring unknown command "
1959 <<command<<std::endl;
1963 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
1965 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1966 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
1969 void Client::interact(u8 action, const PointedThing& pointed)
1971 if(m_state != LC_Ready){
1972 infostream<<"Client::interact() "
1973 "cancelled (not connected)"
1978 std::ostringstream os(std::ios_base::binary);
1984 [5] u32 length of the next item
1985 [9] serialized PointedThing
1987 0: start digging (from undersurface) or use
1988 1: stop digging (all parameters ignored)
1989 2: digging completed
1990 3: place block or item (to abovesurface)
1993 writeU16(os, TOSERVER_INTERACT);
1994 writeU8(os, action);
1995 writeU16(os, getPlayerItem());
1996 std::ostringstream tmp_os(std::ios::binary);
1997 pointed.serialize(tmp_os);
1998 os<<serializeLongString(tmp_os.str());
2000 std::string s = os.str();
2001 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2004 Send(0, data, true);
2007 void Client::sendNodemetaFields(v3s16 p, const std::string &formname,
2008 const std::map<std::string, std::string> &fields)
2010 std::ostringstream os(std::ios_base::binary);
2012 writeU16(os, TOSERVER_NODEMETA_FIELDS);
2014 os<<serializeString(formname);
2015 size_t fields_size = fields.size();
2016 assert(fields_size <= 0xFFFF);
2017 writeU16(os, (u16) (fields_size & 0xFFFF));
2018 for(std::map<std::string, std::string>::const_iterator
2019 i = fields.begin(); i != fields.end(); i++){
2020 const std::string &name = i->first;
2021 const std::string &value = i->second;
2022 os<<serializeString(name);
2023 os<<serializeLongString(value);
2027 std::string s = os.str();
2028 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2030 Send(0, data, true);
2033 void Client::sendInventoryFields(const std::string &formname,
2034 const std::map<std::string, std::string> &fields)
2036 std::ostringstream os(std::ios_base::binary);
2038 writeU16(os, TOSERVER_INVENTORY_FIELDS);
2039 os<<serializeString(formname);
2040 size_t fields_size = fields.size();
2041 assert(fields_size <= 0xFFFF);
2042 writeU16(os, (u16) (fields_size & 0xFFFF));
2043 for(std::map<std::string, std::string>::const_iterator
2044 i = fields.begin(); i != fields.end(); i++){
2045 const std::string &name = i->first;
2046 const std::string &value = i->second;
2047 os<<serializeString(name);
2048 os<<serializeLongString(value);
2052 std::string s = os.str();
2053 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2055 Send(0, data, true);
2058 void Client::sendInventoryAction(InventoryAction *a)
2060 std::ostringstream os(std::ios_base::binary);
2064 writeU16(buf, TOSERVER_INVENTORY_ACTION);
2065 os.write((char*)buf, 2);
2070 std::string s = os.str();
2071 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2073 Send(0, data, true);
2076 void Client::sendChatMessage(const std::wstring &message)
2078 std::ostringstream os(std::ios_base::binary);
2082 writeU16(buf, TOSERVER_CHAT_MESSAGE);
2083 os.write((char*)buf, 2);
2086 size_t messagesize = message.size();
2087 if (messagesize > 0xFFFF) {
2088 messagesize = 0xFFFF;
2090 writeU16(buf, (u16) messagesize);
2091 os.write((char*)buf, 2);
2094 for(unsigned int i=0; i<message.size(); i++)
2098 os.write((char*)buf, 2);
2102 std::string s = os.str();
2103 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2105 Send(0, data, true);
2108 void Client::sendChangePassword(const std::wstring &oldpassword,
2109 const std::wstring &newpassword)
2111 Player *player = m_env.getLocalPlayer();
2115 std::string playername = player->getName();
2116 std::string oldpwd = translatePassword(playername, oldpassword);
2117 std::string newpwd = translatePassword(playername, newpassword);
2119 std::ostringstream os(std::ios_base::binary);
2120 u8 buf[2+PASSWORD_SIZE*2];
2122 [0] u16 TOSERVER_PASSWORD
2123 [2] u8[28] old password
2124 [30] u8[28] new password
2127 writeU16(buf, TOSERVER_PASSWORD);
2128 for(unsigned int i=0;i<PASSWORD_SIZE-1;i++)
2130 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
2131 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
2133 buf[2+PASSWORD_SIZE-1] = 0;
2134 buf[30+PASSWORD_SIZE-1] = 0;
2135 os.write((char*)buf, 2+PASSWORD_SIZE*2);
2138 std::string s = os.str();
2139 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2141 Send(0, data, true);
2145 void Client::sendDamage(u8 damage)
2147 DSTACK(__FUNCTION_NAME);
2148 std::ostringstream os(std::ios_base::binary);
2150 writeU16(os, TOSERVER_DAMAGE);
2151 writeU8(os, damage);
2154 std::string s = os.str();
2155 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2157 Send(0, data, true);
2160 void Client::sendBreath(u16 breath)
2162 DSTACK(__FUNCTION_NAME);
2163 std::ostringstream os(std::ios_base::binary);
2165 writeU16(os, TOSERVER_BREATH);
2166 writeU16(os, breath);
2168 std::string s = os.str();
2169 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2171 Send(0, data, true);
2174 void Client::sendRespawn()
2176 DSTACK(__FUNCTION_NAME);
2177 std::ostringstream os(std::ios_base::binary);
2179 writeU16(os, TOSERVER_RESPAWN);
2182 std::string s = os.str();
2183 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2185 Send(0, data, true);
2188 void Client::sendReady()
2190 DSTACK(__FUNCTION_NAME);
2191 std::ostringstream os(std::ios_base::binary);
2193 writeU16(os, TOSERVER_CLIENT_READY);
2194 writeU8(os,VERSION_MAJOR);
2195 writeU8(os,VERSION_MINOR);
2196 writeU8(os,VERSION_PATCH_ORIG);
2199 writeU16(os,strlen(CMAKE_VERSION_GITHASH));
2200 os.write(CMAKE_VERSION_GITHASH,strlen(CMAKE_VERSION_GITHASH));
2203 std::string s = os.str();
2204 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2206 Send(0, data, true);
2209 void Client::sendPlayerPos()
2211 LocalPlayer *myplayer = m_env.getLocalPlayer();
2212 if(myplayer == NULL)
2215 // Save bandwidth by only updating position when something changed
2216 if(myplayer->last_position == myplayer->getPosition() &&
2217 myplayer->last_speed == myplayer->getSpeed() &&
2218 myplayer->last_pitch == myplayer->getPitch() &&
2219 myplayer->last_yaw == myplayer->getYaw() &&
2220 myplayer->last_keyPressed == myplayer->keyPressed)
2223 myplayer->last_position = myplayer->getPosition();
2224 myplayer->last_speed = myplayer->getSpeed();
2225 myplayer->last_pitch = myplayer->getPitch();
2226 myplayer->last_yaw = myplayer->getYaw();
2227 myplayer->last_keyPressed = myplayer->keyPressed;
2231 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2232 our_peer_id = m_con.GetPeerID();
2235 // Set peer id if not set already
2236 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2237 myplayer->peer_id = our_peer_id;
2238 // Check that an existing peer_id is the same as the connection's
2239 assert(myplayer->peer_id == our_peer_id);
2241 v3f pf = myplayer->getPosition();
2242 v3f sf = myplayer->getSpeed();
2243 s32 pitch = myplayer->getPitch() * 100;
2244 s32 yaw = myplayer->getYaw() * 100;
2245 u32 keyPressed = myplayer->keyPressed;
2247 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
2248 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
2252 [2] v3s32 position*100
2253 [2+12] v3s32 speed*100
2254 [2+12+12] s32 pitch*100
2255 [2+12+12+4] s32 yaw*100
2256 [2+12+12+4+4] u32 keyPressed
2258 SharedBuffer<u8> data(2+12+12+4+4+4);
2259 writeU16(&data[0], TOSERVER_PLAYERPOS);
2260 writeV3S32(&data[2], position);
2261 writeV3S32(&data[2+12], speed);
2262 writeS32(&data[2+12+12], pitch);
2263 writeS32(&data[2+12+12+4], yaw);
2264 writeU32(&data[2+12+12+4+4], keyPressed);
2265 // Send as unreliable
2266 Send(0, data, false);
2269 void Client::sendPlayerItem(u16 item)
2271 Player *myplayer = m_env.getLocalPlayer();
2272 if(myplayer == NULL)
2275 u16 our_peer_id = m_con.GetPeerID();
2277 // Set peer id if not set already
2278 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2279 myplayer->peer_id = our_peer_id;
2280 // Check that an existing peer_id is the same as the connection's
2281 assert(myplayer->peer_id == our_peer_id);
2283 SharedBuffer<u8> data(2+2);
2284 writeU16(&data[0], TOSERVER_PLAYERITEM);
2285 writeU16(&data[2], item);
2288 Send(0, data, true);
2291 void Client::removeNode(v3s16 p)
2293 std::map<v3s16, MapBlock*> modified_blocks;
2297 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
2299 catch(InvalidPositionException &e)
2303 // add urgent task to update the modified node
2304 addUpdateMeshTaskForNode(p, false, true);
2306 for(std::map<v3s16, MapBlock * >::iterator
2307 i = modified_blocks.begin();
2308 i != modified_blocks.end(); ++i)
2310 addUpdateMeshTaskWithEdge(i->first);
2314 void Client::addNode(v3s16 p, MapNode n, bool remove_metadata)
2316 TimeTaker timer1("Client::addNode()");
2318 std::map<v3s16, MapBlock*> modified_blocks;
2322 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
2323 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
2325 catch(InvalidPositionException &e)
2328 for(std::map<v3s16, MapBlock * >::iterator
2329 i = modified_blocks.begin();
2330 i != modified_blocks.end(); ++i)
2332 addUpdateMeshTaskWithEdge(i->first);
2336 void Client::setPlayerControl(PlayerControl &control)
2338 LocalPlayer *player = m_env.getLocalPlayer();
2339 assert(player != NULL);
2340 player->control = control;
2343 void Client::selectPlayerItem(u16 item)
2345 m_playeritem = item;
2346 m_inventory_updated = true;
2347 sendPlayerItem(item);
2350 // Returns true if the inventory of the local player has been
2351 // updated from the server. If it is true, it is set to false.
2352 bool Client::getLocalInventoryUpdated()
2354 bool updated = m_inventory_updated;
2355 m_inventory_updated = false;
2359 // Copies the inventory of the local player to parameter
2360 void Client::getLocalInventory(Inventory &dst)
2362 Player *player = m_env.getLocalPlayer();
2363 assert(player != NULL);
2364 dst = player->inventory;
2367 Inventory* Client::getInventory(const InventoryLocation &loc)
2370 case InventoryLocation::UNDEFINED:
2373 case InventoryLocation::CURRENT_PLAYER:
2375 Player *player = m_env.getLocalPlayer();
2376 assert(player != NULL);
2377 return &player->inventory;
2380 case InventoryLocation::PLAYER:
2382 Player *player = m_env.getPlayer(loc.name.c_str());
2385 return &player->inventory;
2388 case InventoryLocation::NODEMETA:
2390 NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p);
2393 return meta->getInventory();
2396 case InventoryLocation::DETACHED:
2398 if(m_detached_inventories.count(loc.name) == 0)
2400 return m_detached_inventories[loc.name];
2409 void Client::inventoryAction(InventoryAction *a)
2412 Send it to the server
2414 sendInventoryAction(a);
2417 Predict some local inventory changes
2419 a->clientApply(this, this);
2425 ClientActiveObject * Client::getSelectedActiveObject(
2427 v3f from_pos_f_on_map,
2428 core::line3d<f32> shootline_on_map
2431 std::vector<DistanceSortedActiveObject> objects;
2433 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
2436 // After this, the closest object is the first in the array.
2437 std::sort(objects.begin(), objects.end());
2439 for(unsigned int i=0; i<objects.size(); i++)
2441 ClientActiveObject *obj = objects[i].obj;
2443 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
2444 if(selection_box == NULL)
2447 v3f pos = obj->getPosition();
2449 core::aabbox3d<f32> offsetted_box(
2450 selection_box->MinEdge + pos,
2451 selection_box->MaxEdge + pos
2454 if(offsetted_box.intersectsWithLine(shootline_on_map))
2463 std::list<std::string> Client::getConnectedPlayerNames()
2465 return m_env.getPlayerNames();
2468 float Client::getAnimationTime()
2470 return m_animation_time;
2473 int Client::getCrackLevel()
2475 return m_crack_level;
2478 void Client::setCrack(int level, v3s16 pos)
2480 int old_crack_level = m_crack_level;
2481 v3s16 old_crack_pos = m_crack_pos;
2483 m_crack_level = level;
2486 if(old_crack_level >= 0 && (level < 0 || pos != old_crack_pos))
2489 addUpdateMeshTaskForNode(old_crack_pos, false, true);
2491 if(level >= 0 && (old_crack_level < 0 || pos != old_crack_pos))
2494 addUpdateMeshTaskForNode(pos, false, true);
2500 Player *player = m_env.getLocalPlayer();
2501 assert(player != NULL);
2505 u16 Client::getBreath()
2507 Player *player = m_env.getLocalPlayer();
2508 assert(player != NULL);
2509 return player->getBreath();
2512 bool Client::getChatMessage(std::wstring &message)
2514 if(m_chat_queue.size() == 0)
2516 message = m_chat_queue.pop_front();
2520 void Client::typeChatMessage(const std::wstring &message)
2522 // Discard empty line
2527 sendChatMessage(message);
2530 if (message[0] == L'/')
2532 m_chat_queue.push_back(
2533 (std::wstring)L"issued command: "+message);
2537 LocalPlayer *player = m_env.getLocalPlayer();
2538 assert(player != NULL);
2539 std::wstring name = narrow_to_wide(player->getName());
2540 m_chat_queue.push_back(
2541 (std::wstring)L"<"+name+L"> "+message);
2545 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
2547 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2552 Create a task to update the mesh of the block
2555 MeshMakeData *data = new MeshMakeData(this);
2558 //TimeTaker timer("data fill");
2560 // Debug: 1-6ms, avg=2ms
2562 data->setCrack(m_crack_level, m_crack_pos);
2563 data->setSmoothLighting(g_settings->getBool("smooth_lighting"));
2566 // Add task to queue
2567 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server, urgent);
2570 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent)
2573 v3s16 p = blockpos + v3s16(0,0,0);
2574 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2575 addUpdateMeshTask(p, ack_to_server, urgent);
2577 catch(InvalidPositionException &e){}
2580 for (int i=0;i<6;i++)
2583 v3s16 p = blockpos + g_6dirs[i];
2584 addUpdateMeshTask(p, false, urgent);
2586 catch(InvalidPositionException &e){}
2590 void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool urgent)
2594 infostream<<"Client::addUpdateMeshTaskForNode(): "
2595 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2599 v3s16 blockpos = getNodeBlockPos(nodepos);
2600 v3s16 blockpos_relative = blockpos * MAP_BLOCKSIZE;
2603 v3s16 p = blockpos + v3s16(0,0,0);
2604 addUpdateMeshTask(p, ack_to_server, urgent);
2606 catch(InvalidPositionException &e){}
2609 if(nodepos.X == blockpos_relative.X){
2611 v3s16 p = blockpos + v3s16(-1,0,0);
2612 addUpdateMeshTask(p, false, urgent);
2614 catch(InvalidPositionException &e){}
2617 if(nodepos.Y == blockpos_relative.Y){
2619 v3s16 p = blockpos + v3s16(0,-1,0);
2620 addUpdateMeshTask(p, false, urgent);
2622 catch(InvalidPositionException &e){}
2625 if(nodepos.Z == blockpos_relative.Z){
2627 v3s16 p = blockpos + v3s16(0,0,-1);
2628 addUpdateMeshTask(p, false, urgent);
2630 catch(InvalidPositionException &e){}
2634 ClientEvent Client::getClientEvent()
2636 if(m_client_event_queue.size() == 0)
2639 event.type = CE_NONE;
2642 return m_client_event_queue.pop_front();
2645 float Client::mediaReceiveProgress()
2647 if (m_media_downloader)
2648 return m_media_downloader->getProgress();
2650 return 1.0; // downloader only exists when not yet done
2653 void Client::afterContentReceived(IrrlichtDevice *device, gui::IGUIFont* font)
2655 infostream<<"Client::afterContentReceived() started"<<std::endl;
2656 assert(m_itemdef_received);
2657 assert(m_nodedef_received);
2658 assert(mediaReceived());
2660 // Rebuild inherited images and recreate textures
2661 infostream<<"- Rebuilding images and textures"<<std::endl;
2662 m_tsrc->rebuildImagesAndTextures();
2665 infostream<<"- Rebuilding shaders"<<std::endl;
2666 m_shsrc->rebuildShaders();
2668 // Update node aliases
2669 infostream<<"- Updating node aliases"<<std::endl;
2670 m_nodedef->updateAliases(m_itemdef);
2672 // Update node textures and assign shaders to each tile
2673 infostream<<"- Updating node textures"<<std::endl;
2674 m_nodedef->updateTextures(m_tsrc, m_shsrc);
2676 // Preload item textures and meshes if configured to
2677 if(g_settings->getBool("preload_item_visuals"))
2679 verbosestream<<"Updating item textures and meshes"<<std::endl;
2680 wchar_t* text = wgettext("Item textures...");
2681 draw_load_screen(text, device, guienv, font, 0, 0);
2682 std::set<std::string> names = m_itemdef->getAll();
2683 size_t size = names.size();
2686 for(std::set<std::string>::const_iterator
2687 i = names.begin(); i != names.end(); ++i){
2688 // Asking for these caches the result
2689 m_itemdef->getInventoryTexture(*i, this);
2690 m_itemdef->getWieldMesh(*i, this);
2692 percent = count*100/size;
2693 if (count%50 == 0) // only update every 50 item
2694 draw_load_screen(text, device, guienv, font, 0, percent);
2699 // Start mesh update thread after setting up content definitions
2700 infostream<<"- Starting mesh update thread"<<std::endl;
2701 m_mesh_update_thread.Start();
2705 infostream<<"Client::afterContentReceived() done"<<std::endl;
2708 float Client::getRTT(void)
2710 return m_con.getPeerStat(PEER_ID_SERVER,con::AVG_RTT);
2713 float Client::getCurRate(void)
2715 return ( m_con.getLocalStat(con::CUR_INC_RATE) +
2716 m_con.getLocalStat(con::CUR_DL_RATE));
2719 float Client::getAvgRate(void)
2721 return ( m_con.getLocalStat(con::AVG_INC_RATE) +
2722 m_con.getLocalStat(con::AVG_DL_RATE));
2725 // IGameDef interface
2727 IItemDefManager* Client::getItemDefManager()
2731 INodeDefManager* Client::getNodeDefManager()
2735 ICraftDefManager* Client::getCraftDefManager()
2738 //return m_craftdef;
2740 ITextureSource* Client::getTextureSource()
2744 IShaderSource* Client::getShaderSource()
2748 u16 Client::allocateUnknownNodeId(const std::string &name)
2750 errorstream<<"Client::allocateUnknownNodeId(): "
2751 <<"Client cannot allocate node IDs"<<std::endl;
2753 return CONTENT_IGNORE;
2755 ISoundManager* Client::getSoundManager()
2759 MtEventManager* Client::getEventManager()
2764 scene::IAnimatedMesh* Client::getMesh(const std::string &filename)
2766 std::map<std::string, std::string>::const_iterator i =
2767 m_mesh_data.find(filename);
2768 if(i == m_mesh_data.end()){
2769 errorstream<<"Client::getMesh(): Mesh not found: \""<<filename<<"\""
2773 const std::string &data = i->second;
2774 scene::ISceneManager *smgr = m_device->getSceneManager();
2776 // Create the mesh, remove it from cache and return it
2777 // This allows unique vertex colors and other properties for each instance
2778 Buffer<char> data_rw(data.c_str(), data.size()); // Const-incorrect Irrlicht
2779 io::IFileSystem *irrfs = m_device->getFileSystem();
2780 io::IReadFile *rfile = irrfs->createMemoryReadFile(
2781 *data_rw, data_rw.getSize(), filename.c_str());
2784 scene::IAnimatedMesh *mesh = smgr->getMesh(rfile);
2786 // NOTE: By playing with Irrlicht refcounts, maybe we could cache a bunch
2787 // of uniquely named instances and re-use them
2789 smgr->getMeshCache()->removeMesh(mesh);