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"
59 QueuedMeshUpdate::QueuedMeshUpdate():
62 ack_block_to_server(false)
66 QueuedMeshUpdate::~QueuedMeshUpdate()
76 MeshUpdateQueue::MeshUpdateQueue()
80 MeshUpdateQueue::~MeshUpdateQueue()
82 JMutexAutoLock lock(m_mutex);
84 for(std::vector<QueuedMeshUpdate*>::iterator
86 i != m_queue.end(); i++)
88 QueuedMeshUpdate *q = *i;
94 peer_id=0 adds with nobody to send to
96 void MeshUpdateQueue::addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_server, bool urgent)
98 DSTACK(__FUNCTION_NAME);
102 JMutexAutoLock lock(m_mutex);
108 Find if block is already in queue.
109 If it is, update the data and quit.
111 for(std::vector<QueuedMeshUpdate*>::iterator
113 i != m_queue.end(); i++)
115 QueuedMeshUpdate *q = *i;
121 if(ack_block_to_server)
122 q->ack_block_to_server = true;
130 QueuedMeshUpdate *q = new QueuedMeshUpdate;
133 q->ack_block_to_server = ack_block_to_server;
134 m_queue.push_back(q);
137 // Returned pointer must be deleted
138 // Returns NULL if queue is empty
139 QueuedMeshUpdate * MeshUpdateQueue::pop()
141 JMutexAutoLock lock(m_mutex);
143 bool must_be_urgent = !m_urgents.empty();
144 for(std::vector<QueuedMeshUpdate*>::iterator
146 i != m_queue.end(); i++)
148 QueuedMeshUpdate *q = *i;
149 if(must_be_urgent && m_urgents.count(q->p) == 0)
152 m_urgents.erase(q->p);
162 void * MeshUpdateThread::Thread()
166 log_register_thread("MeshUpdateThread");
168 DSTACK(__FUNCTION_NAME);
170 BEGIN_DEBUG_EXCEPTION_HANDLER
172 porting::setThreadName("MeshUpdateThread");
174 while(!StopRequested())
176 QueuedMeshUpdate *q = m_queue_in.pop();
183 ScopeProfiler sp(g_profiler, "Client: Mesh making");
185 MapBlockMesh *mesh_new = new MapBlockMesh(q->data, m_camera_offset);
186 if(mesh_new->getMesh()->getMeshBufferCount() == 0)
195 r.ack_block_to_server = q->ack_block_to_server;
197 m_queue_out.push_back(r);
202 END_DEBUG_EXCEPTION_HANDLER(errorstream)
212 IrrlichtDevice *device,
213 const char *playername,
214 std::string password,
215 MapDrawControl &control,
216 IWritableTextureSource *tsrc,
217 IWritableShaderSource *shsrc,
218 IWritableItemDefManager *itemdef,
219 IWritableNodeDefManager *nodedef,
220 ISoundManager *sound,
221 MtEventManager *event,
230 m_mesh_update_thread(this),
232 new ClientMap(this, this, control,
233 device->getSceneManager()->getRootSceneNode(),
234 device->getSceneManager(), 666),
235 device->getSceneManager(),
238 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, ipv6, this),
240 m_server_ser_ver(SER_FMT_VER_INVALID),
242 m_inventory_updated(false),
243 m_inventory_from_server(NULL),
244 m_inventory_from_server_age(0.0),
249 m_password(password),
250 m_access_denied(false),
251 m_itemdef_received(false),
252 m_nodedef_received(false),
253 m_media_downloader(new ClientMediaDownloader()),
254 m_time_of_day_set(false),
255 m_last_time_of_day_f(-1),
256 m_time_of_day_update_timer(0),
257 m_recommended_send_interval(0.1),
258 m_removed_sounds_check_timer(0),
261 m_packetcounter_timer = 0.0;
262 //m_delete_unused_sectors_timer = 0.0;
263 m_connection_reinit_timer = 0.0;
264 m_avg_rtt_timer = 0.0;
265 m_playerpos_send_timer = 0.0;
266 m_ignore_damage_timer = 0.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 (20s):"<<std::endl;
364 m_packetcounter.print(infostream);
365 m_packetcounter.clear();
372 Delete unused sectors
374 NOTE: This jams the game for a while because deleting sectors
378 float &counter = m_delete_unused_sectors_timer;
386 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
388 core::list<v3s16> deleted_blocks;
390 float delete_unused_sectors_timeout =
391 g_settings->getFloat("client_delete_unused_sectors_timeout");
393 // Delete sector blocks
394 /*u32 num = m_env.getMap().unloadUnusedData
395 (delete_unused_sectors_timeout,
396 true, &deleted_blocks);*/
398 // Delete whole sectors
399 m_env.getMap().unloadUnusedData
400 (delete_unused_sectors_timeout,
403 if(deleted_blocks.size() > 0)
405 /*infostream<<"Client: Deleted blocks of "<<num
406 <<" unused sectors"<<std::endl;*/
407 /*infostream<<"Client: Deleted "<<num
408 <<" unused sectors"<<std::endl;*/
414 // Env is locked so con can be locked.
415 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
417 core::list<v3s16>::Iterator i = deleted_blocks.begin();
418 core::list<v3s16> sendlist;
421 if(sendlist.size() == 255 || i == deleted_blocks.end())
423 if(sendlist.size() == 0)
432 u32 replysize = 2+1+6*sendlist.size();
433 SharedBuffer<u8> reply(replysize);
434 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
435 reply[2] = sendlist.size();
437 for(core::list<v3s16>::Iterator
438 j = sendlist.begin();
439 j != sendlist.end(); j++)
441 writeV3S16(&reply[2+1+6*k], *j);
444 m_con.Send(PEER_ID_SERVER, 1, reply, true);
446 if(i == deleted_blocks.end())
452 sendlist.push_back(*i);
460 if(m_state == LC_Created)
462 float &counter = m_connection_reinit_timer;
468 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
470 Player *myplayer = m_env.getLocalPlayer();
471 assert(myplayer != NULL);
473 // Send TOSERVER_INIT
474 // [0] u16 TOSERVER_INIT
475 // [2] u8 SER_FMT_VER_HIGHEST_READ
476 // [3] u8[20] player_name
477 // [23] u8[28] password (new in some version)
478 // [51] u16 minimum supported network protocol version (added sometime)
479 // [53] u16 maximum supported network protocol version (added later than the previous one)
480 SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2+2);
481 writeU16(&data[0], TOSERVER_INIT);
482 writeU8(&data[2], SER_FMT_VER_HIGHEST_READ);
484 memset((char*)&data[3], 0, PLAYERNAME_SIZE);
485 snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
487 /*infostream<<"Client: sending initial password hash: \""<<m_password<<"\""
490 memset((char*)&data[23], 0, PASSWORD_SIZE);
491 snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
493 writeU16(&data[51], CLIENT_PROTOCOL_VERSION_MIN);
494 writeU16(&data[53], CLIENT_PROTOCOL_VERSION_MAX);
496 // Send as unreliable
497 Send(1, data, false);
500 // Not connected, return
505 Do stuff if connected
509 Run Map's timers and unload unused data
511 const float map_timer_and_unload_dtime = 5.25;
512 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
514 ScopeProfiler sp(g_profiler, "Client: map timer and unload");
515 std::list<v3s16> deleted_blocks;
516 m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
517 g_settings->getFloat("client_unload_unused_data_timeout"),
520 /*if(deleted_blocks.size() > 0)
521 infostream<<"Client: Unloaded "<<deleted_blocks.size()
522 <<" unused blocks"<<std::endl;*/
526 NOTE: This loop is intentionally iterated the way it is.
529 std::list<v3s16>::iterator i = deleted_blocks.begin();
530 std::list<v3s16> sendlist;
533 if(sendlist.size() == 255 || i == deleted_blocks.end())
535 if(sendlist.size() == 0)
544 u32 replysize = 2+1+6*sendlist.size();
545 SharedBuffer<u8> reply(replysize);
546 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
547 reply[2] = sendlist.size();
549 for(std::list<v3s16>::iterator
550 j = sendlist.begin();
551 j != sendlist.end(); ++j)
553 writeV3S16(&reply[2+1+6*k], *j);
556 m_con.Send(PEER_ID_SERVER, 2, reply, true);
558 if(i == deleted_blocks.end())
564 sendlist.push_back(*i);
573 // Control local player (0ms)
574 LocalPlayer *player = m_env.getLocalPlayer();
575 assert(player != NULL);
576 player->applyControl(dtime);
586 ClientEnvEvent event = m_env.getClientEvent();
587 if(event.type == CEE_NONE)
591 else if(event.type == CEE_PLAYER_DAMAGE)
593 if(m_ignore_damage_timer <= 0)
595 u8 damage = event.player_damage.amount;
597 if(event.player_damage.send_to_server)
600 // Add to ClientEvent queue
602 event.type = CE_PLAYER_DAMAGE;
603 event.player_damage.amount = damage;
604 m_client_event_queue.push_back(event);
607 else if(event.type == CEE_PLAYER_BREATH)
609 u16 breath = event.player_breath.amount;
619 float &counter = m_avg_rtt_timer;
624 // connectedAndInitialized() is true, peer exists.
625 float avg_rtt = getRTT();
626 infostream<<"Client: avg_rtt="<<avg_rtt<<std::endl;
631 Send player position to server
634 float &counter = m_playerpos_send_timer;
636 if((m_state == LC_Ready) && (counter >= m_recommended_send_interval))
644 Replace updated meshes
647 int num_processed_meshes = 0;
648 while(!m_mesh_update_thread.m_queue_out.empty())
650 num_processed_meshes++;
651 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_frontNoEx();
652 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
655 // Delete the old mesh
656 if(block->mesh != NULL)
658 // TODO: Remove hardware buffers of meshbuffers of block->mesh
663 // Replace with the new mesh
664 block->mesh = r.mesh;
668 if(r.ack_block_to_server)
680 u32 replysize = 2+1+6;
681 SharedBuffer<u8> reply(replysize);
682 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
684 writeV3S16(&reply[3], r.p);
686 m_con.Send(PEER_ID_SERVER, 2, reply, true);
689 if(num_processed_meshes > 0)
690 g_profiler->graphAdd("num_processed_meshes", num_processed_meshes);
696 if (m_media_downloader && m_media_downloader->isStarted()) {
697 m_media_downloader->step(this);
698 if (m_media_downloader->isDone()) {
700 delete m_media_downloader;
701 m_media_downloader = NULL;
706 If the server didn't update the inventory in a while, revert
707 the local inventory (so the player notices the lag problem
708 and knows something is wrong).
710 if(m_inventory_from_server)
712 float interval = 10.0;
713 float count_before = floor(m_inventory_from_server_age / interval);
715 m_inventory_from_server_age += dtime;
717 float count_after = floor(m_inventory_from_server_age / interval);
719 if(count_after != count_before)
721 // Do this every <interval> seconds after TOCLIENT_INVENTORY
722 // Reset the locally changed inventory to the authoritative inventory
723 Player *player = m_env.getLocalPlayer();
724 player->inventory = *m_inventory_from_server;
725 m_inventory_updated = true;
730 Update positions of sounds attached to objects
733 for(std::map<int, u16>::iterator
734 i = m_sounds_to_objects.begin();
735 i != m_sounds_to_objects.end(); i++)
737 int client_id = i->first;
738 u16 object_id = i->second;
739 ClientActiveObject *cao = m_env.getActiveObject(object_id);
742 v3f pos = cao->getPosition();
743 m_sound->updateSoundPosition(client_id, pos);
748 Handle removed remotely initiated sounds
750 m_removed_sounds_check_timer += dtime;
751 if(m_removed_sounds_check_timer >= 2.32)
753 m_removed_sounds_check_timer = 0;
754 // Find removed sounds and clear references to them
755 std::set<s32> removed_server_ids;
756 for(std::map<s32, int>::iterator
757 i = m_sounds_server_to_client.begin();
758 i != m_sounds_server_to_client.end();)
760 s32 server_id = i->first;
761 int client_id = i->second;
763 if(!m_sound->soundExists(client_id)){
764 m_sounds_server_to_client.erase(server_id);
765 m_sounds_client_to_server.erase(client_id);
766 m_sounds_to_objects.erase(client_id);
767 removed_server_ids.insert(server_id);
771 if(removed_server_ids.size() != 0)
773 std::ostringstream os(std::ios_base::binary);
774 writeU16(os, TOSERVER_REMOVED_SOUNDS);
775 size_t server_ids = removed_server_ids.size();
776 assert(server_ids <= 0xFFFF);
777 writeU16(os, (u16) (server_ids & 0xFFFF));
778 for(std::set<s32>::iterator i = removed_server_ids.begin();
779 i != removed_server_ids.end(); i++)
781 std::string s = os.str();
782 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
789 bool Client::loadMedia(const std::string &data, const std::string &filename)
791 // Silly irrlicht's const-incorrectness
792 Buffer<char> data_rw(data.c_str(), data.size());
796 const char *image_ext[] = {
797 ".png", ".jpg", ".bmp", ".tga",
798 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
801 name = removeStringEnd(filename, image_ext);
804 verbosestream<<"Client: Attempting to load image "
805 <<"file \""<<filename<<"\""<<std::endl;
807 io::IFileSystem *irrfs = m_device->getFileSystem();
808 video::IVideoDriver *vdrv = m_device->getVideoDriver();
810 // Create an irrlicht memory file
811 io::IReadFile *rfile = irrfs->createMemoryReadFile(
812 *data_rw, data_rw.getSize(), "_tempreadfile");
815 video::IImage *img = vdrv->createImageFromFile(rfile);
817 errorstream<<"Client: Cannot create image from data of "
818 <<"file \""<<filename<<"\""<<std::endl;
823 m_tsrc->insertSourceImage(filename, img);
830 const char *sound_ext[] = {
831 ".0.ogg", ".1.ogg", ".2.ogg", ".3.ogg", ".4.ogg",
832 ".5.ogg", ".6.ogg", ".7.ogg", ".8.ogg", ".9.ogg",
835 name = removeStringEnd(filename, sound_ext);
838 verbosestream<<"Client: Attempting to load sound "
839 <<"file \""<<filename<<"\""<<std::endl;
840 m_sound->loadSoundData(name, data);
844 const char *model_ext[] = {
845 ".x", ".b3d", ".md2", ".obj",
848 name = removeStringEnd(filename, model_ext);
851 verbosestream<<"Client: Storing model into memory: "
852 <<"\""<<filename<<"\""<<std::endl;
853 if(m_mesh_data.count(filename))
854 errorstream<<"Multiple models with name \""<<filename.c_str()
855 <<"\" found; replacing previous model"<<std::endl;
856 m_mesh_data[filename] = data;
860 errorstream<<"Client: Don't know how to load file \""
861 <<filename<<"\""<<std::endl;
865 // Virtual methods from con::PeerHandler
866 void Client::peerAdded(con::Peer *peer)
868 infostream<<"Client::peerAdded(): peer->id="
869 <<peer->id<<std::endl;
871 void Client::deletingPeer(con::Peer *peer, bool timeout)
873 infostream<<"Client::deletingPeer(): "
874 "Server Peer is getting deleted "
875 <<"(timeout="<<timeout<<")"<<std::endl;
880 u16 number of files requested
886 void Client::request_media(const std::list<std::string> &file_requests)
888 std::ostringstream os(std::ios_base::binary);
889 writeU16(os, TOSERVER_REQUEST_MEDIA);
890 size_t file_requests_size = file_requests.size();
891 assert(file_requests_size <= 0xFFFF);
892 writeU16(os, (u16) (file_requests_size & 0xFFFF));
894 for(std::list<std::string>::const_iterator i = file_requests.begin();
895 i != file_requests.end(); ++i) {
896 os<<serializeString(*i);
900 std::string s = os.str();
901 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
904 infostream<<"Client: Sending media request list to server ("
905 <<file_requests.size()<<" files)"<<std::endl;
908 void Client::received_media()
910 // notify server we received everything
911 std::ostringstream os(std::ios_base::binary);
912 writeU16(os, TOSERVER_RECEIVED_MEDIA);
913 std::string s = os.str();
914 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
917 infostream<<"Client: Notifying server that we received all media"
921 void Client::ReceiveAll()
923 DSTACK(__FUNCTION_NAME);
924 u32 start_ms = porting::getTimeMs();
927 // Limit time even if there would be huge amounts of data to
929 if(porting::getTimeMs() > start_ms + 100)
934 g_profiler->graphAdd("client_received_packets", 1);
936 catch(con::NoIncomingDataException &e)
940 catch(con::InvalidIncomingDataException &e)
942 infostream<<"Client::ReceiveAll(): "
943 "InvalidIncomingDataException: what()="
944 <<e.what()<<std::endl;
949 void Client::Receive()
951 DSTACK(__FUNCTION_NAME);
952 SharedBuffer<u8> data;
954 u32 datasize = m_con.Receive(sender_peer_id, data);
955 ProcessData(*data, datasize, sender_peer_id);
959 sender_peer_id given to this shall be quaranteed to be a valid peer
961 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
963 DSTACK(__FUNCTION_NAME);
965 // Ignore packets that don't even fit a command
968 m_packetcounter.add(60000);
972 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
974 //infostream<<"Client: received command="<<command<<std::endl;
975 m_packetcounter.add((u16)command);
978 If this check is removed, be sure to change the queue
979 system to know the ids
981 if(sender_peer_id != PEER_ID_SERVER)
983 infostream<<"Client::ProcessData(): Discarding data not "
984 "coming from server: peer_id="<<sender_peer_id
989 u8 ser_version = m_server_ser_ver;
991 if(command == TOCLIENT_INIT)
996 u8 deployed = data[2];
998 infostream<<"Client: TOCLIENT_INIT received with "
999 "deployed="<<((int)deployed&0xff)<<std::endl;
1001 if(!ser_ver_supported(deployed))
1003 infostream<<"Client: TOCLIENT_INIT: Server sent "
1004 <<"unsupported ser_fmt_ver"<<std::endl;
1008 m_server_ser_ver = deployed;
1010 // Get player position
1011 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
1012 if(datasize >= 2+1+6)
1013 playerpos_s16 = readV3S16(&data[2+1]);
1014 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
1017 // Set player position
1018 Player *player = m_env.getLocalPlayer();
1019 assert(player != NULL);
1020 player->setPosition(playerpos_f);
1022 if(datasize >= 2+1+6+8)
1025 m_map_seed = readU64(&data[2+1+6]);
1026 infostream<<"Client: received map seed: "<<m_map_seed<<std::endl;
1029 if(datasize >= 2+1+6+8+4)
1032 m_recommended_send_interval = readF1000(&data[2+1+6+8]);
1033 infostream<<"Client: received recommended send interval "
1034 <<m_recommended_send_interval<<std::endl;
1039 SharedBuffer<u8> reply(replysize);
1040 writeU16(&reply[0], TOSERVER_INIT2);
1042 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1049 if(command == TOCLIENT_ACCESS_DENIED)
1051 // The server didn't like our password. Note, this needs
1052 // to be processed even if the serialisation format has
1053 // not been agreed yet, the same as TOCLIENT_INIT.
1054 m_access_denied = true;
1055 m_access_denied_reason = L"Unknown";
1058 std::string datastring((char*)&data[2], datasize-2);
1059 std::istringstream is(datastring, std::ios_base::binary);
1060 m_access_denied_reason = deSerializeWideString(is);
1065 if(ser_version == SER_FMT_VER_INVALID)
1067 infostream<<"Client: Server serialization"
1068 " format invalid or not initialized."
1069 " Skipping incoming command="<<command<<std::endl;
1074 Handle runtime commands
1076 // there's no sane reason why we shouldn't have a player and
1077 // almost everyone needs a player reference
1078 Player *player = m_env.getLocalPlayer();
1079 assert(player != NULL);
1081 if(command == TOCLIENT_REMOVENODE)
1086 p.X = readS16(&data[2]);
1087 p.Y = readS16(&data[4]);
1088 p.Z = readS16(&data[6]);
1091 else if(command == TOCLIENT_ADDNODE)
1093 if(datasize < 8 + MapNode::serializedLength(ser_version))
1097 p.X = readS16(&data[2]);
1098 p.Y = readS16(&data[4]);
1099 p.Z = readS16(&data[6]);
1102 n.deSerialize(&data[8], ser_version);
1104 bool remove_metadata = true;
1105 u32 index = 8 + MapNode::serializedLength(ser_version);
1106 if ((datasize >= index+1) && data[index]){
1107 remove_metadata = false;
1110 addNode(p, n, remove_metadata);
1112 else if(command == TOCLIENT_BLOCKDATA)
1114 // Ignore too small packet
1119 p.X = readS16(&data[2]);
1120 p.Y = readS16(&data[4]);
1121 p.Z = readS16(&data[6]);
1123 std::string datastring((char*)&data[8], datasize-8);
1124 std::istringstream istr(datastring, std::ios_base::binary);
1129 v2s16 p2d(p.X, p.Z);
1130 sector = m_env.getMap().emergeSector(p2d);
1132 assert(sector->getPos() == p2d);
1134 block = sector->getBlockNoCreateNoEx(p.Y);
1138 Update an existing block
1140 block->deSerialize(istr, ser_version, false);
1141 block->deSerializeNetworkSpecific(istr);
1148 block = new MapBlock(&m_env.getMap(), p, this);
1149 block->deSerialize(istr, ser_version, false);
1150 block->deSerializeNetworkSpecific(istr);
1151 sector->insertBlock(block);
1155 Add it to mesh update queue and set it to be acknowledged after update.
1157 addUpdateMeshTaskWithEdge(p, true);
1159 else if(command == TOCLIENT_INVENTORY)
1164 std::string datastring((char*)&data[2], datasize-2);
1165 std::istringstream is(datastring, std::ios_base::binary);
1167 player->inventory.deSerialize(is);
1169 m_inventory_updated = true;
1171 delete m_inventory_from_server;
1172 m_inventory_from_server = new Inventory(player->inventory);
1173 m_inventory_from_server_age = 0.0;
1176 else if(command == TOCLIENT_TIME_OF_DAY)
1181 u16 time_of_day = readU16(&data[2]);
1182 time_of_day = time_of_day % 24000;
1183 float time_speed = 0;
1185 if(datasize >= 2 + 2 + 4)
1187 time_speed = readF1000(&data[4]);
1190 // Old message; try to approximate speed of time by ourselves
1191 float time_of_day_f = (float)time_of_day / 24000.0;
1192 float tod_diff_f = 0;
1194 if(time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
1195 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0;
1197 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
1199 m_last_time_of_day_f = time_of_day_f;
1200 float time_diff = m_time_of_day_update_timer;
1201 m_time_of_day_update_timer = 0;
1203 if(m_time_of_day_set){
1204 time_speed = (3600.0*24.0) * tod_diff_f / time_diff;
1205 infostream<<"Client: Measured time_of_day speed (old format): "
1206 <<time_speed<<" tod_diff_f="<<tod_diff_f
1207 <<" time_diff="<<time_diff<<std::endl;
1211 // Update environment
1212 m_env.setTimeOfDay(time_of_day);
1213 m_env.setTimeOfDaySpeed(time_speed);
1214 m_time_of_day_set = true;
1216 u32 dr = m_env.getDayNightRatio();
1217 infostream<<"Client: time_of_day="<<time_of_day
1218 <<" time_speed="<<time_speed
1219 <<" dr="<<dr<<std::endl;
1221 else if(command == TOCLIENT_CHAT_MESSAGE)
1229 std::string datastring((char*)&data[2], datasize-2);
1230 std::istringstream is(datastring, std::ios_base::binary);
1233 is.read((char*) buf, 2);
1234 u16 len = readU16(buf);
1236 std::wstring message;
1237 for(unsigned int i=0; i<len; i++)
1239 is.read((char*)buf, 2);
1240 message += (wchar_t)readU16(buf);
1243 m_chat_queue.push_back(message);
1245 else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1249 u16 count of removed objects
1250 for all removed objects {
1253 u16 count of added objects
1254 for all added objects {
1257 u32 initialization data length
1258 string initialization data
1263 // Get all data except the command number
1264 std::string datastring((char*)&data[2], datasize-2);
1265 // Throw them in an istringstream
1266 std::istringstream is(datastring, std::ios_base::binary);
1268 // Read removed objects
1270 u16 removed_count = readU16((u8*)buf);
1271 for(unsigned int i=0; i<removed_count; i++)
1274 u16 id = readU16((u8*)buf);
1275 m_env.removeActiveObject(id);
1278 // Read added objects
1280 u16 added_count = readU16((u8*)buf);
1281 for(unsigned int i=0; i<added_count; i++)
1284 u16 id = readU16((u8*)buf);
1286 u8 type = readU8((u8*)buf);
1287 std::string data = deSerializeLongString(is);
1289 m_env.addActiveObject(id, type, data);
1292 else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1304 // Get all data except the command number
1305 std::string datastring((char*)&data[2], datasize-2);
1306 // Throw them in an istringstream
1307 std::istringstream is(datastring, std::ios_base::binary);
1309 while(is.eof() == false)
1312 u16 id = readU16((u8*)buf);
1316 size_t message_size = readU16((u8*)buf);
1317 std::string message;
1318 message.reserve(message_size);
1319 for(unsigned int i=0; i<message_size; i++)
1322 message.append(buf, 1);
1324 // Pass on to the environment
1325 m_env.processActiveObjectMessage(id, message);
1328 else if(command == TOCLIENT_MOVEMENT)
1330 std::string datastring((char*)&data[2], datasize-2);
1331 std::istringstream is(datastring, std::ios_base::binary);
1333 player->movement_acceleration_default = readF1000(is) * BS;
1334 player->movement_acceleration_air = readF1000(is) * BS;
1335 player->movement_acceleration_fast = readF1000(is) * BS;
1336 player->movement_speed_walk = readF1000(is) * BS;
1337 player->movement_speed_crouch = readF1000(is) * BS;
1338 player->movement_speed_fast = readF1000(is) * BS;
1339 player->movement_speed_climb = readF1000(is) * BS;
1340 player->movement_speed_jump = readF1000(is) * BS;
1341 player->movement_liquid_fluidity = readF1000(is) * BS;
1342 player->movement_liquid_fluidity_smooth = readF1000(is) * BS;
1343 player->movement_liquid_sink = readF1000(is) * BS;
1344 player->movement_gravity = readF1000(is) * BS;
1346 else if(command == TOCLIENT_HP)
1348 std::string datastring((char*)&data[2], datasize-2);
1349 std::istringstream is(datastring, std::ios_base::binary);
1351 u8 oldhp = player->hp;
1357 // Add to ClientEvent queue
1359 event.type = CE_PLAYER_DAMAGE;
1360 event.player_damage.amount = oldhp - hp;
1361 m_client_event_queue.push_back(event);
1364 else if(command == TOCLIENT_BREATH)
1366 std::string datastring((char*)&data[2], datasize-2);
1367 std::istringstream is(datastring, std::ios_base::binary);
1369 player->setBreath(readU16(is));
1371 else if(command == TOCLIENT_MOVE_PLAYER)
1373 std::string datastring((char*)&data[2], datasize-2);
1374 std::istringstream is(datastring, std::ios_base::binary);
1376 v3f pos = readV3F1000(is);
1377 f32 pitch = readF1000(is);
1378 f32 yaw = readF1000(is);
1379 player->setPosition(pos);
1381 infostream<<"Client got TOCLIENT_MOVE_PLAYER"
1382 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1388 Add to ClientEvent queue.
1389 This has to be sent to the main program because otherwise
1390 it would just force the pitch and yaw values to whatever
1391 the camera points to.
1394 event.type = CE_PLAYER_FORCE_MOVE;
1395 event.player_force_move.pitch = pitch;
1396 event.player_force_move.yaw = yaw;
1397 m_client_event_queue.push_back(event);
1399 // Ignore damage for a few seconds, so that the player doesn't
1400 // get damage from falling on ground
1401 m_ignore_damage_timer = 3.0;
1403 else if(command == TOCLIENT_PLAYERITEM)
1405 infostream<<"Client: WARNING: Ignoring TOCLIENT_PLAYERITEM"<<std::endl;
1407 else if(command == TOCLIENT_DEATHSCREEN)
1409 std::string datastring((char*)&data[2], datasize-2);
1410 std::istringstream is(datastring, std::ios_base::binary);
1412 bool set_camera_point_target = readU8(is);
1413 v3f camera_point_target = readV3F1000(is);
1416 event.type = CE_DEATHSCREEN;
1417 event.deathscreen.set_camera_point_target = set_camera_point_target;
1418 event.deathscreen.camera_point_target_x = camera_point_target.X;
1419 event.deathscreen.camera_point_target_y = camera_point_target.Y;
1420 event.deathscreen.camera_point_target_z = camera_point_target.Z;
1421 m_client_event_queue.push_back(event);
1423 else if(command == TOCLIENT_ANNOUNCE_MEDIA)
1425 std::string datastring((char*)&data[2], datasize-2);
1426 std::istringstream is(datastring, std::ios_base::binary);
1428 int num_files = readU16(is);
1430 infostream<<"Client: Received media announcement: packet size: "
1431 <<datasize<<std::endl;
1433 if (m_media_downloader == NULL ||
1434 m_media_downloader->isStarted()) {
1435 const char *problem = m_media_downloader ?
1436 "we already saw another announcement" :
1437 "all media has been received already";
1438 errorstream<<"Client: Received media announcement but "
1440 <<" files="<<num_files
1441 <<" size="<<datasize<<std::endl;
1445 // Mesh update thread must be stopped while
1446 // updating content definitions
1447 assert(!m_mesh_update_thread.IsRunning());
1449 for(int i=0; i<num_files; i++)
1451 std::string name = deSerializeString(is);
1452 std::string sha1_base64 = deSerializeString(is);
1453 std::string sha1_raw = base64_decode(sha1_base64);
1454 m_media_downloader->addFile(name, sha1_raw);
1457 std::vector<std::string> remote_media;
1459 Strfnd sf(deSerializeString(is));
1460 while(!sf.atend()) {
1461 std::string baseurl = trim(sf.next(","));
1463 m_media_downloader->addRemoteServer(baseurl);
1466 catch(SerializationError& e) {
1467 // not supported by server or turned off
1470 m_media_downloader->step(this);
1472 else if(command == TOCLIENT_MEDIA)
1474 std::string datastring((char*)&data[2], datasize-2);
1475 std::istringstream is(datastring, std::ios_base::binary);
1479 u16 total number of file bunches
1480 u16 index of this bunch
1481 u32 number of files in this bunch
1489 int num_bunches = readU16(is);
1490 int bunch_i = readU16(is);
1491 u32 num_files = readU32(is);
1492 infostream<<"Client: Received files: bunch "<<bunch_i<<"/"
1493 <<num_bunches<<" files="<<num_files
1494 <<" size="<<datasize<<std::endl;
1499 if (m_media_downloader == NULL ||
1500 !m_media_downloader->isStarted()) {
1501 const char *problem = m_media_downloader ?
1502 "media has not been requested" :
1503 "all media has been received already";
1504 errorstream<<"Client: Received media but "
1506 <<" bunch "<<bunch_i<<"/"<<num_bunches
1507 <<" files="<<num_files
1508 <<" size="<<datasize<<std::endl;
1512 // Mesh update thread must be stopped while
1513 // updating content definitions
1514 assert(!m_mesh_update_thread.IsRunning());
1516 for(unsigned int i=0; i<num_files; i++){
1517 std::string name = deSerializeString(is);
1518 std::string data = deSerializeLongString(is);
1519 m_media_downloader->conventionalTransferDone(
1523 else if(command == TOCLIENT_TOOLDEF)
1525 infostream<<"Client: WARNING: Ignoring TOCLIENT_TOOLDEF"<<std::endl;
1527 else if(command == TOCLIENT_NODEDEF)
1529 infostream<<"Client: Received node definitions: packet size: "
1530 <<datasize<<std::endl;
1532 // Mesh update thread must be stopped while
1533 // updating content definitions
1534 assert(!m_mesh_update_thread.IsRunning());
1536 // Decompress node definitions
1537 std::string datastring((char*)&data[2], datasize-2);
1538 std::istringstream is(datastring, std::ios_base::binary);
1539 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1540 std::ostringstream tmp_os;
1541 decompressZlib(tmp_is, tmp_os);
1543 // Deserialize node definitions
1544 std::istringstream tmp_is2(tmp_os.str());
1545 m_nodedef->deSerialize(tmp_is2);
1546 m_nodedef_received = true;
1548 else if(command == TOCLIENT_CRAFTITEMDEF)
1550 infostream<<"Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF"<<std::endl;
1552 else if(command == TOCLIENT_ITEMDEF)
1554 infostream<<"Client: Received item definitions: packet size: "
1555 <<datasize<<std::endl;
1557 // Mesh update thread must be stopped while
1558 // updating content definitions
1559 assert(!m_mesh_update_thread.IsRunning());
1561 // Decompress item definitions
1562 std::string datastring((char*)&data[2], datasize-2);
1563 std::istringstream is(datastring, std::ios_base::binary);
1564 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1565 std::ostringstream tmp_os;
1566 decompressZlib(tmp_is, tmp_os);
1568 // Deserialize node definitions
1569 std::istringstream tmp_is2(tmp_os.str());
1570 m_itemdef->deSerialize(tmp_is2);
1571 m_itemdef_received = true;
1573 else if(command == TOCLIENT_PLAY_SOUND)
1575 std::string datastring((char*)&data[2], datasize-2);
1576 std::istringstream is(datastring, std::ios_base::binary);
1578 s32 server_id = readS32(is);
1579 std::string name = deSerializeString(is);
1580 float gain = readF1000(is);
1581 int type = readU8(is); // 0=local, 1=positional, 2=object
1582 v3f pos = readV3F1000(is);
1583 u16 object_id = readU16(is);
1584 bool loop = readU8(is);
1589 client_id = m_sound->playSound(name, loop, gain);
1591 case 1: // positional
1592 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1595 ClientActiveObject *cao = m_env.getActiveObject(object_id);
1597 pos = cao->getPosition();
1598 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1599 // TODO: Set up sound to move with object
1604 if(client_id != -1){
1605 m_sounds_server_to_client[server_id] = client_id;
1606 m_sounds_client_to_server[client_id] = server_id;
1608 m_sounds_to_objects[client_id] = object_id;
1611 else if(command == TOCLIENT_STOP_SOUND)
1613 std::string datastring((char*)&data[2], datasize-2);
1614 std::istringstream is(datastring, std::ios_base::binary);
1616 s32 server_id = readS32(is);
1617 std::map<s32, int>::iterator i =
1618 m_sounds_server_to_client.find(server_id);
1619 if(i != m_sounds_server_to_client.end()){
1620 int client_id = i->second;
1621 m_sound->stopSound(client_id);
1624 else if(command == TOCLIENT_PRIVILEGES)
1626 std::string datastring((char*)&data[2], datasize-2);
1627 std::istringstream is(datastring, std::ios_base::binary);
1629 m_privileges.clear();
1630 infostream<<"Client: Privileges updated: ";
1631 u16 num_privileges = readU16(is);
1632 for(unsigned int i=0; i<num_privileges; i++){
1633 std::string priv = deSerializeString(is);
1634 m_privileges.insert(priv);
1635 infostream<<priv<<" ";
1637 infostream<<std::endl;
1639 else if(command == TOCLIENT_INVENTORY_FORMSPEC)
1641 std::string datastring((char*)&data[2], datasize-2);
1642 std::istringstream is(datastring, std::ios_base::binary);
1644 // Store formspec in LocalPlayer
1645 player->inventory_formspec = deSerializeLongString(is);
1647 else if(command == TOCLIENT_DETACHED_INVENTORY)
1649 std::string datastring((char*)&data[2], datasize-2);
1650 std::istringstream is(datastring, std::ios_base::binary);
1652 std::string name = deSerializeString(is);
1654 infostream<<"Client: Detached inventory update: \""<<name<<"\""<<std::endl;
1656 Inventory *inv = NULL;
1657 if(m_detached_inventories.count(name) > 0)
1658 inv = m_detached_inventories[name];
1660 inv = new Inventory(m_itemdef);
1661 m_detached_inventories[name] = inv;
1663 inv->deSerialize(is);
1665 else if(command == TOCLIENT_SHOW_FORMSPEC)
1667 std::string datastring((char*)&data[2], datasize-2);
1668 std::istringstream is(datastring, std::ios_base::binary);
1670 std::string formspec = deSerializeLongString(is);
1671 std::string formname = deSerializeString(is);
1674 event.type = CE_SHOW_FORMSPEC;
1675 // pointer is required as event is a struct only!
1676 // adding a std:string to a struct isn't possible
1677 event.show_formspec.formspec = new std::string(formspec);
1678 event.show_formspec.formname = new std::string(formname);
1679 m_client_event_queue.push_back(event);
1681 else if(command == TOCLIENT_SPAWN_PARTICLE)
1683 std::string datastring((char*)&data[2], datasize-2);
1684 std::istringstream is(datastring, std::ios_base::binary);
1686 v3f pos = readV3F1000(is);
1687 v3f vel = readV3F1000(is);
1688 v3f acc = readV3F1000(is);
1689 float expirationtime = readF1000(is);
1690 float size = readF1000(is);
1691 bool collisiondetection = readU8(is);
1692 std::string texture = deSerializeLongString(is);
1693 bool vertical = false;
1695 vertical = readU8(is);
1699 event.type = CE_SPAWN_PARTICLE;
1700 event.spawn_particle.pos = new v3f (pos);
1701 event.spawn_particle.vel = new v3f (vel);
1702 event.spawn_particle.acc = new v3f (acc);
1703 event.spawn_particle.expirationtime = expirationtime;
1704 event.spawn_particle.size = size;
1705 event.spawn_particle.collisiondetection = collisiondetection;
1706 event.spawn_particle.vertical = vertical;
1707 event.spawn_particle.texture = new std::string(texture);
1709 m_client_event_queue.push_back(event);
1711 else if(command == TOCLIENT_ADD_PARTICLESPAWNER)
1713 std::string datastring((char*)&data[2], datasize-2);
1714 std::istringstream is(datastring, std::ios_base::binary);
1716 u16 amount = readU16(is);
1717 float spawntime = readF1000(is);
1718 v3f minpos = readV3F1000(is);
1719 v3f maxpos = readV3F1000(is);
1720 v3f minvel = readV3F1000(is);
1721 v3f maxvel = readV3F1000(is);
1722 v3f minacc = readV3F1000(is);
1723 v3f maxacc = readV3F1000(is);
1724 float minexptime = readF1000(is);
1725 float maxexptime = readF1000(is);
1726 float minsize = readF1000(is);
1727 float maxsize = readF1000(is);
1728 bool collisiondetection = readU8(is);
1729 std::string texture = deSerializeLongString(is);
1730 u32 id = readU32(is);
1731 bool vertical = false;
1733 vertical = readU8(is);
1737 event.type = CE_ADD_PARTICLESPAWNER;
1738 event.add_particlespawner.amount = amount;
1739 event.add_particlespawner.spawntime = spawntime;
1740 event.add_particlespawner.minpos = new v3f (minpos);
1741 event.add_particlespawner.maxpos = new v3f (maxpos);
1742 event.add_particlespawner.minvel = new v3f (minvel);
1743 event.add_particlespawner.maxvel = new v3f (maxvel);
1744 event.add_particlespawner.minacc = new v3f (minacc);
1745 event.add_particlespawner.maxacc = new v3f (maxacc);
1746 event.add_particlespawner.minexptime = minexptime;
1747 event.add_particlespawner.maxexptime = maxexptime;
1748 event.add_particlespawner.minsize = minsize;
1749 event.add_particlespawner.maxsize = maxsize;
1750 event.add_particlespawner.collisiondetection = collisiondetection;
1751 event.add_particlespawner.vertical = vertical;
1752 event.add_particlespawner.texture = new std::string(texture);
1753 event.add_particlespawner.id = id;
1755 m_client_event_queue.push_back(event);
1757 else if(command == TOCLIENT_DELETE_PARTICLESPAWNER)
1759 std::string datastring((char*)&data[2], datasize-2);
1760 std::istringstream is(datastring, std::ios_base::binary);
1762 u32 id = readU16(is);
1765 event.type = CE_DELETE_PARTICLESPAWNER;
1766 event.delete_particlespawner.id = id;
1768 m_client_event_queue.push_back(event);
1770 else if(command == TOCLIENT_HUDADD)
1772 std::string datastring((char *)&data[2], datasize - 2);
1773 std::istringstream is(datastring, std::ios_base::binary);
1775 u32 id = readU32(is);
1776 u8 type = readU8(is);
1777 v2f pos = readV2F1000(is);
1778 std::string name = deSerializeString(is);
1779 v2f scale = readV2F1000(is);
1780 std::string text = deSerializeString(is);
1781 u32 number = readU32(is);
1782 u32 item = readU32(is);
1783 u32 dir = readU32(is);
1784 v2f align = readV2F1000(is);
1785 v2f offset = readV2F1000(is);
1788 world_pos = readV3F1000(is);
1789 }catch(SerializationError &e) {};
1792 event.type = CE_HUDADD;
1793 event.hudadd.id = id;
1794 event.hudadd.type = type;
1795 event.hudadd.pos = new v2f(pos);
1796 event.hudadd.name = new std::string(name);
1797 event.hudadd.scale = new v2f(scale);
1798 event.hudadd.text = new std::string(text);
1799 event.hudadd.number = number;
1800 event.hudadd.item = item;
1801 event.hudadd.dir = dir;
1802 event.hudadd.align = new v2f(align);
1803 event.hudadd.offset = new v2f(offset);
1804 event.hudadd.world_pos = new v3f(world_pos);
1805 m_client_event_queue.push_back(event);
1807 else if(command == TOCLIENT_HUDRM)
1809 std::string datastring((char *)&data[2], datasize - 2);
1810 std::istringstream is(datastring, std::ios_base::binary);
1812 u32 id = readU32(is);
1815 event.type = CE_HUDRM;
1816 event.hudrm.id = id;
1817 m_client_event_queue.push_back(event);
1819 else if(command == TOCLIENT_HUDCHANGE)
1826 std::string datastring((char *)&data[2], datasize - 2);
1827 std::istringstream is(datastring, std::ios_base::binary);
1829 u32 id = readU32(is);
1830 u8 stat = (HudElementStat)readU8(is);
1832 if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
1833 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
1834 v2fdata = readV2F1000(is);
1835 else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
1836 sdata = deSerializeString(is);
1837 else if (stat == HUD_STAT_WORLD_POS)
1838 v3fdata = readV3F1000(is);
1840 intdata = readU32(is);
1843 event.type = CE_HUDCHANGE;
1844 event.hudchange.id = id;
1845 event.hudchange.stat = (HudElementStat)stat;
1846 event.hudchange.v2fdata = new v2f(v2fdata);
1847 event.hudchange.v3fdata = new v3f(v3fdata);
1848 event.hudchange.sdata = new std::string(sdata);
1849 event.hudchange.data = intdata;
1850 m_client_event_queue.push_back(event);
1852 else if(command == TOCLIENT_HUD_SET_FLAGS)
1854 std::string datastring((char *)&data[2], datasize - 2);
1855 std::istringstream is(datastring, std::ios_base::binary);
1857 u32 flags = readU32(is);
1858 u32 mask = readU32(is);
1860 player->hud_flags &= ~mask;
1861 player->hud_flags |= flags;
1863 else if(command == TOCLIENT_HUD_SET_PARAM)
1865 std::string datastring((char *)&data[2], datasize - 2);
1866 std::istringstream is(datastring, std::ios_base::binary);
1868 u16 param = readU16(is);
1869 std::string value = deSerializeString(is);
1871 if(param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) {
1872 s32 hotbar_itemcount = readS32((u8*) value.c_str());
1873 if(hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
1874 player->hud_hotbar_itemcount = hotbar_itemcount;
1876 else if (param == HUD_PARAM_HOTBAR_IMAGE) {
1877 ((LocalPlayer *) player)->hotbar_image = value;
1879 else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
1880 ((LocalPlayer *) player)->hotbar_selected_image = value;
1883 else if(command == TOCLIENT_SET_SKY)
1885 std::string datastring((char *)&data[2], datasize - 2);
1886 std::istringstream is(datastring, std::ios_base::binary);
1888 video::SColor *bgcolor = new video::SColor(readARGB8(is));
1889 std::string *type = new std::string(deSerializeString(is));
1890 u16 count = readU16(is);
1891 std::vector<std::string> *params = new std::vector<std::string>;
1893 for(size_t i=0; i<count; i++)
1894 params->push_back(deSerializeString(is));
1897 event.type = CE_SET_SKY;
1898 event.set_sky.bgcolor = bgcolor;
1899 event.set_sky.type = type;
1900 event.set_sky.params = params;
1901 m_client_event_queue.push_back(event);
1903 else if(command == TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO)
1905 std::string datastring((char *)&data[2], datasize - 2);
1906 std::istringstream is(datastring, std::ios_base::binary);
1908 bool do_override = readU8(is);
1909 float day_night_ratio_f = (float)readU16(is) / 65536;
1912 event.type = CE_OVERRIDE_DAY_NIGHT_RATIO;
1913 event.override_day_night_ratio.do_override = do_override;
1914 event.override_day_night_ratio.ratio_f = day_night_ratio_f;
1915 m_client_event_queue.push_back(event);
1917 else if(command == TOCLIENT_LOCAL_PLAYER_ANIMATIONS)
1919 std::string datastring((char *)&data[2], datasize - 2);
1920 std::istringstream is(datastring, std::ios_base::binary);
1922 LocalPlayer *player = m_env.getLocalPlayer();
1923 assert(player != NULL);
1925 player->local_animations[0] = readV2S32(is);
1926 player->local_animations[1] = readV2S32(is);
1927 player->local_animations[2] = readV2S32(is);
1928 player->local_animations[3] = readV2S32(is);
1929 player->local_animation_speed = readF1000(is);
1931 else if(command == TOCLIENT_EYE_OFFSET)
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->eye_offset_first = readV3F1000(is);
1940 player->eye_offset_third = readV3F1000(is);
1944 infostream<<"Client: Ignoring unknown command "
1945 <<command<<std::endl;
1949 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
1951 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1952 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
1955 void Client::interact(u8 action, const PointedThing& pointed)
1957 if(m_state != LC_Ready){
1958 infostream<<"Client::interact() "
1959 "cancelled (not connected)"
1964 std::ostringstream os(std::ios_base::binary);
1970 [5] u32 length of the next item
1971 [9] serialized PointedThing
1973 0: start digging (from undersurface) or use
1974 1: stop digging (all parameters ignored)
1975 2: digging completed
1976 3: place block or item (to abovesurface)
1979 writeU16(os, TOSERVER_INTERACT);
1980 writeU8(os, action);
1981 writeU16(os, getPlayerItem());
1982 std::ostringstream tmp_os(std::ios::binary);
1983 pointed.serialize(tmp_os);
1984 os<<serializeLongString(tmp_os.str());
1986 std::string s = os.str();
1987 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1990 Send(0, data, true);
1993 void Client::sendNodemetaFields(v3s16 p, const std::string &formname,
1994 const std::map<std::string, std::string> &fields)
1996 std::ostringstream os(std::ios_base::binary);
1998 writeU16(os, TOSERVER_NODEMETA_FIELDS);
2000 os<<serializeString(formname);
2001 size_t fields_size = fields.size();
2002 assert(fields_size <= 0xFFFF);
2003 writeU16(os, (u16) (fields_size & 0xFFFF));
2004 for(std::map<std::string, std::string>::const_iterator
2005 i = fields.begin(); i != fields.end(); i++){
2006 const std::string &name = i->first;
2007 const std::string &value = i->second;
2008 os<<serializeString(name);
2009 os<<serializeLongString(value);
2013 std::string s = os.str();
2014 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2016 Send(0, data, true);
2019 void Client::sendInventoryFields(const std::string &formname,
2020 const std::map<std::string, std::string> &fields)
2022 std::ostringstream os(std::ios_base::binary);
2024 writeU16(os, TOSERVER_INVENTORY_FIELDS);
2025 os<<serializeString(formname);
2026 size_t fields_size = fields.size();
2027 assert(fields_size <= 0xFFFF);
2028 writeU16(os, (u16) (fields_size & 0xFFFF));
2029 for(std::map<std::string, std::string>::const_iterator
2030 i = fields.begin(); i != fields.end(); i++){
2031 const std::string &name = i->first;
2032 const std::string &value = i->second;
2033 os<<serializeString(name);
2034 os<<serializeLongString(value);
2038 std::string s = os.str();
2039 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2041 Send(0, data, true);
2044 void Client::sendInventoryAction(InventoryAction *a)
2046 std::ostringstream os(std::ios_base::binary);
2050 writeU16(buf, TOSERVER_INVENTORY_ACTION);
2051 os.write((char*)buf, 2);
2056 std::string s = os.str();
2057 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2059 Send(0, data, true);
2062 void Client::sendChatMessage(const std::wstring &message)
2064 std::ostringstream os(std::ios_base::binary);
2068 writeU16(buf, TOSERVER_CHAT_MESSAGE);
2069 os.write((char*)buf, 2);
2072 size_t messagesize = message.size();
2073 assert(messagesize <= 0xFFFF);
2074 writeU16(buf, (u16) (messagesize & 0xFF));
2075 os.write((char*)buf, 2);
2078 for(unsigned int i=0; i<message.size(); i++)
2082 os.write((char*)buf, 2);
2086 std::string s = os.str();
2087 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2089 Send(0, data, true);
2092 void Client::sendChangePassword(const std::wstring &oldpassword,
2093 const std::wstring &newpassword)
2095 Player *player = m_env.getLocalPlayer();
2099 std::string playername = player->getName();
2100 std::string oldpwd = translatePassword(playername, oldpassword);
2101 std::string newpwd = translatePassword(playername, newpassword);
2103 std::ostringstream os(std::ios_base::binary);
2104 u8 buf[2+PASSWORD_SIZE*2];
2106 [0] u16 TOSERVER_PASSWORD
2107 [2] u8[28] old password
2108 [30] u8[28] new password
2111 writeU16(buf, TOSERVER_PASSWORD);
2112 for(unsigned int i=0;i<PASSWORD_SIZE-1;i++)
2114 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
2115 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
2117 buf[2+PASSWORD_SIZE-1] = 0;
2118 buf[30+PASSWORD_SIZE-1] = 0;
2119 os.write((char*)buf, 2+PASSWORD_SIZE*2);
2122 std::string s = os.str();
2123 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2125 Send(0, data, true);
2129 void Client::sendDamage(u8 damage)
2131 DSTACK(__FUNCTION_NAME);
2132 std::ostringstream os(std::ios_base::binary);
2134 writeU16(os, TOSERVER_DAMAGE);
2135 writeU8(os, damage);
2138 std::string s = os.str();
2139 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2141 Send(0, data, true);
2144 void Client::sendBreath(u16 breath)
2146 DSTACK(__FUNCTION_NAME);
2147 std::ostringstream os(std::ios_base::binary);
2149 writeU16(os, TOSERVER_BREATH);
2150 writeU16(os, breath);
2152 std::string s = os.str();
2153 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2155 Send(0, data, true);
2158 void Client::sendRespawn()
2160 DSTACK(__FUNCTION_NAME);
2161 std::ostringstream os(std::ios_base::binary);
2163 writeU16(os, TOSERVER_RESPAWN);
2166 std::string s = os.str();
2167 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2169 Send(0, data, true);
2172 void Client::sendReady()
2174 DSTACK(__FUNCTION_NAME);
2175 std::ostringstream os(std::ios_base::binary);
2177 writeU16(os, TOSERVER_CLIENT_READY);
2178 writeU8(os,VERSION_MAJOR);
2179 writeU8(os,VERSION_MINOR);
2180 writeU8(os,VERSION_PATCH_ORIG);
2183 writeU16(os,strlen(CMAKE_VERSION_GITHASH));
2184 os.write(CMAKE_VERSION_GITHASH,strlen(CMAKE_VERSION_GITHASH));
2187 std::string s = os.str();
2188 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2190 Send(0, data, true);
2193 void Client::sendPlayerPos()
2195 LocalPlayer *myplayer = m_env.getLocalPlayer();
2196 if(myplayer == NULL)
2199 // Save bandwidth by only updating position when something changed
2200 if(myplayer->last_position == myplayer->getPosition() &&
2201 myplayer->last_speed == myplayer->getSpeed() &&
2202 myplayer->last_pitch == myplayer->getPitch() &&
2203 myplayer->last_yaw == myplayer->getYaw() &&
2204 myplayer->last_keyPressed == myplayer->keyPressed)
2207 myplayer->last_position = myplayer->getPosition();
2208 myplayer->last_speed = myplayer->getSpeed();
2209 myplayer->last_pitch = myplayer->getPitch();
2210 myplayer->last_yaw = myplayer->getYaw();
2211 myplayer->last_keyPressed = myplayer->keyPressed;
2215 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2216 our_peer_id = m_con.GetPeerID();
2219 // Set peer id if not set already
2220 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2221 myplayer->peer_id = our_peer_id;
2222 // Check that an existing peer_id is the same as the connection's
2223 assert(myplayer->peer_id == our_peer_id);
2225 v3f pf = myplayer->getPosition();
2226 v3f sf = myplayer->getSpeed();
2227 s32 pitch = myplayer->getPitch() * 100;
2228 s32 yaw = myplayer->getYaw() * 100;
2229 u32 keyPressed = myplayer->keyPressed;
2231 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
2232 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
2236 [2] v3s32 position*100
2237 [2+12] v3s32 speed*100
2238 [2+12+12] s32 pitch*100
2239 [2+12+12+4] s32 yaw*100
2240 [2+12+12+4+4] u32 keyPressed
2242 SharedBuffer<u8> data(2+12+12+4+4+4);
2243 writeU16(&data[0], TOSERVER_PLAYERPOS);
2244 writeV3S32(&data[2], position);
2245 writeV3S32(&data[2+12], speed);
2246 writeS32(&data[2+12+12], pitch);
2247 writeS32(&data[2+12+12+4], yaw);
2248 writeU32(&data[2+12+12+4+4], keyPressed);
2249 // Send as unreliable
2250 Send(0, data, false);
2253 void Client::sendPlayerItem(u16 item)
2255 Player *myplayer = m_env.getLocalPlayer();
2256 if(myplayer == NULL)
2259 u16 our_peer_id = m_con.GetPeerID();
2261 // Set peer id if not set already
2262 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2263 myplayer->peer_id = our_peer_id;
2264 // Check that an existing peer_id is the same as the connection's
2265 assert(myplayer->peer_id == our_peer_id);
2267 SharedBuffer<u8> data(2+2);
2268 writeU16(&data[0], TOSERVER_PLAYERITEM);
2269 writeU16(&data[2], item);
2272 Send(0, data, true);
2275 void Client::removeNode(v3s16 p)
2277 std::map<v3s16, MapBlock*> modified_blocks;
2281 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
2283 catch(InvalidPositionException &e)
2287 // add urgent task to update the modified node
2288 addUpdateMeshTaskForNode(p, false, true);
2290 for(std::map<v3s16, MapBlock * >::iterator
2291 i = modified_blocks.begin();
2292 i != modified_blocks.end(); ++i)
2294 addUpdateMeshTaskWithEdge(i->first);
2298 void Client::addNode(v3s16 p, MapNode n, bool remove_metadata)
2300 TimeTaker timer1("Client::addNode()");
2302 std::map<v3s16, MapBlock*> modified_blocks;
2306 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
2307 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
2309 catch(InvalidPositionException &e)
2312 for(std::map<v3s16, MapBlock * >::iterator
2313 i = modified_blocks.begin();
2314 i != modified_blocks.end(); ++i)
2316 addUpdateMeshTaskWithEdge(i->first);
2320 void Client::setPlayerControl(PlayerControl &control)
2322 LocalPlayer *player = m_env.getLocalPlayer();
2323 assert(player != NULL);
2324 player->control = control;
2327 void Client::selectPlayerItem(u16 item)
2329 m_playeritem = item;
2330 m_inventory_updated = true;
2331 sendPlayerItem(item);
2334 // Returns true if the inventory of the local player has been
2335 // updated from the server. If it is true, it is set to false.
2336 bool Client::getLocalInventoryUpdated()
2338 bool updated = m_inventory_updated;
2339 m_inventory_updated = false;
2343 // Copies the inventory of the local player to parameter
2344 void Client::getLocalInventory(Inventory &dst)
2346 Player *player = m_env.getLocalPlayer();
2347 assert(player != NULL);
2348 dst = player->inventory;
2351 Inventory* Client::getInventory(const InventoryLocation &loc)
2354 case InventoryLocation::UNDEFINED:
2357 case InventoryLocation::CURRENT_PLAYER:
2359 Player *player = m_env.getLocalPlayer();
2360 assert(player != NULL);
2361 return &player->inventory;
2364 case InventoryLocation::PLAYER:
2366 Player *player = m_env.getPlayer(loc.name.c_str());
2369 return &player->inventory;
2372 case InventoryLocation::NODEMETA:
2374 NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p);
2377 return meta->getInventory();
2380 case InventoryLocation::DETACHED:
2382 if(m_detached_inventories.count(loc.name) == 0)
2384 return m_detached_inventories[loc.name];
2393 void Client::inventoryAction(InventoryAction *a)
2396 Send it to the server
2398 sendInventoryAction(a);
2401 Predict some local inventory changes
2403 a->clientApply(this, this);
2409 ClientActiveObject * Client::getSelectedActiveObject(
2411 v3f from_pos_f_on_map,
2412 core::line3d<f32> shootline_on_map
2415 std::vector<DistanceSortedActiveObject> objects;
2417 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
2420 // After this, the closest object is the first in the array.
2421 std::sort(objects.begin(), objects.end());
2423 for(unsigned int i=0; i<objects.size(); i++)
2425 ClientActiveObject *obj = objects[i].obj;
2427 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
2428 if(selection_box == NULL)
2431 v3f pos = obj->getPosition();
2433 core::aabbox3d<f32> offsetted_box(
2434 selection_box->MinEdge + pos,
2435 selection_box->MaxEdge + pos
2438 if(offsetted_box.intersectsWithLine(shootline_on_map))
2447 std::list<std::string> Client::getConnectedPlayerNames()
2449 return m_env.getPlayerNames();
2452 float Client::getAnimationTime()
2454 return m_animation_time;
2457 int Client::getCrackLevel()
2459 return m_crack_level;
2462 void Client::setCrack(int level, v3s16 pos)
2464 int old_crack_level = m_crack_level;
2465 v3s16 old_crack_pos = m_crack_pos;
2467 m_crack_level = level;
2470 if(old_crack_level >= 0 && (level < 0 || pos != old_crack_pos))
2473 addUpdateMeshTaskForNode(old_crack_pos, false, true);
2475 if(level >= 0 && (old_crack_level < 0 || pos != old_crack_pos))
2478 addUpdateMeshTaskForNode(pos, false, true);
2484 Player *player = m_env.getLocalPlayer();
2485 assert(player != NULL);
2489 u16 Client::getBreath()
2491 Player *player = m_env.getLocalPlayer();
2492 assert(player != NULL);
2493 return player->getBreath();
2496 bool Client::getChatMessage(std::wstring &message)
2498 if(m_chat_queue.size() == 0)
2500 message = m_chat_queue.pop_front();
2504 void Client::typeChatMessage(const std::wstring &message)
2506 // Discard empty line
2511 sendChatMessage(message);
2514 if (message[0] == L'/')
2516 m_chat_queue.push_back(
2517 (std::wstring)L"issued command: "+message);
2521 LocalPlayer *player = m_env.getLocalPlayer();
2522 assert(player != NULL);
2523 std::wstring name = narrow_to_wide(player->getName());
2524 m_chat_queue.push_back(
2525 (std::wstring)L"<"+name+L"> "+message);
2529 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
2531 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2536 Create a task to update the mesh of the block
2539 MeshMakeData *data = new MeshMakeData(this);
2542 //TimeTaker timer("data fill");
2544 // Debug: 1-6ms, avg=2ms
2546 data->setCrack(m_crack_level, m_crack_pos);
2547 data->setSmoothLighting(g_settings->getBool("smooth_lighting"));
2550 // Add task to queue
2551 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server, urgent);
2554 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent)
2557 v3s16 p = blockpos + v3s16(0,0,0);
2558 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2559 addUpdateMeshTask(p, ack_to_server, urgent);
2561 catch(InvalidPositionException &e){}
2564 for (int i=0;i<6;i++)
2567 v3s16 p = blockpos + g_6dirs[i];
2568 addUpdateMeshTask(p, false, urgent);
2570 catch(InvalidPositionException &e){}
2574 void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool urgent)
2578 infostream<<"Client::addUpdateMeshTaskForNode(): "
2579 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2583 v3s16 blockpos = getNodeBlockPos(nodepos);
2584 v3s16 blockpos_relative = blockpos * MAP_BLOCKSIZE;
2587 v3s16 p = blockpos + v3s16(0,0,0);
2588 addUpdateMeshTask(p, ack_to_server, urgent);
2590 catch(InvalidPositionException &e){}
2593 if(nodepos.X == blockpos_relative.X){
2595 v3s16 p = blockpos + v3s16(-1,0,0);
2596 addUpdateMeshTask(p, false, urgent);
2598 catch(InvalidPositionException &e){}
2601 if(nodepos.Y == blockpos_relative.Y){
2603 v3s16 p = blockpos + v3s16(0,-1,0);
2604 addUpdateMeshTask(p, false, urgent);
2606 catch(InvalidPositionException &e){}
2609 if(nodepos.Z == blockpos_relative.Z){
2611 v3s16 p = blockpos + v3s16(0,0,-1);
2612 addUpdateMeshTask(p, false, urgent);
2614 catch(InvalidPositionException &e){}
2618 ClientEvent Client::getClientEvent()
2620 if(m_client_event_queue.size() == 0)
2623 event.type = CE_NONE;
2626 return m_client_event_queue.pop_front();
2629 float Client::mediaReceiveProgress()
2631 if (m_media_downloader)
2632 return m_media_downloader->getProgress();
2634 return 1.0; // downloader only exists when not yet done
2637 void draw_load_screen(const std::wstring &text,
2638 IrrlichtDevice* device, gui::IGUIFont* font,
2639 float dtime=0 ,int percent=0, bool clouds=true);
2641 void Client::afterContentReceived(IrrlichtDevice *device, gui::IGUIFont* font)
2643 infostream<<"Client::afterContentReceived() started"<<std::endl;
2644 assert(m_itemdef_received);
2645 assert(m_nodedef_received);
2646 assert(mediaReceived());
2648 // Rebuild inherited images and recreate textures
2649 infostream<<"- Rebuilding images and textures"<<std::endl;
2650 m_tsrc->rebuildImagesAndTextures();
2653 infostream<<"- Rebuilding shaders"<<std::endl;
2654 m_shsrc->rebuildShaders();
2656 // Update node aliases
2657 infostream<<"- Updating node aliases"<<std::endl;
2658 m_nodedef->updateAliases(m_itemdef);
2660 // Update node textures
2661 infostream<<"- Updating node textures"<<std::endl;
2662 m_nodedef->updateTextures(m_tsrc);
2664 // Preload item textures and meshes if configured to
2665 if(g_settings->getBool("preload_item_visuals"))
2667 verbosestream<<"Updating item textures and meshes"<<std::endl;
2668 wchar_t* text = wgettext("Item textures...");
2669 draw_load_screen(text,device,font,0,0);
2670 std::set<std::string> names = m_itemdef->getAll();
2671 size_t size = names.size();
2674 for(std::set<std::string>::const_iterator
2675 i = names.begin(); i != names.end(); ++i){
2676 // Asking for these caches the result
2677 m_itemdef->getInventoryTexture(*i, this);
2678 m_itemdef->getWieldMesh(*i, this);
2680 percent = count*100/size;
2681 if (count%50 == 0) // only update every 50 item
2682 draw_load_screen(text,device,font,0,percent);
2687 // Start mesh update thread after setting up content definitions
2688 infostream<<"- Starting mesh update thread"<<std::endl;
2689 m_mesh_update_thread.Start();
2693 infostream<<"Client::afterContentReceived() done"<<std::endl;
2696 float Client::getRTT(void)
2698 return m_con.getPeerStat(PEER_ID_SERVER,con::AVG_RTT);
2701 // IGameDef interface
2703 IItemDefManager* Client::getItemDefManager()
2707 INodeDefManager* Client::getNodeDefManager()
2711 ICraftDefManager* Client::getCraftDefManager()
2714 //return m_craftdef;
2716 ITextureSource* Client::getTextureSource()
2720 IShaderSource* Client::getShaderSource()
2724 u16 Client::allocateUnknownNodeId(const std::string &name)
2726 errorstream<<"Client::allocateUnknownNodeId(): "
2727 <<"Client cannot allocate node IDs"<<std::endl;
2729 return CONTENT_IGNORE;
2731 ISoundManager* Client::getSoundManager()
2735 MtEventManager* Client::getEventManager()
2740 scene::IAnimatedMesh* Client::getMesh(const std::string &filename)
2742 std::map<std::string, std::string>::const_iterator i =
2743 m_mesh_data.find(filename);
2744 if(i == m_mesh_data.end()){
2745 errorstream<<"Client::getMesh(): Mesh not found: \""<<filename<<"\""
2749 const std::string &data = i->second;
2750 scene::ISceneManager *smgr = m_device->getSceneManager();
2752 // Create the mesh, remove it from cache and return it
2753 // This allows unique vertex colors and other properties for each instance
2754 Buffer<char> data_rw(data.c_str(), data.size()); // Const-incorrect Irrlicht
2755 io::IFileSystem *irrfs = m_device->getFileSystem();
2756 io::IReadFile *rfile = irrfs->createMemoryReadFile(
2757 *data_rw, data_rw.getSize(), filename.c_str());
2760 scene::IAnimatedMesh *mesh = smgr->getMesh(rfile);
2762 // NOTE: By playing with Irrlicht refcounts, maybe we could cache a bunch
2763 // of uniquely named instances and re-use them
2765 smgr->getMeshCache()->removeMesh(mesh);