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,
224 m_packetcounter_timer(0.0),
225 m_connection_reinit_timer(0.1),
226 m_avg_rtt_timer(0.0),
227 m_playerpos_send_timer(0.0),
228 m_ignore_damage_timer(0.0),
235 m_mesh_update_thread(this),
237 new ClientMap(this, this, control,
238 device->getSceneManager()->getRootSceneNode(),
239 device->getSceneManager(), 666),
240 device->getSceneManager(),
243 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, ipv6, this),
245 m_server_ser_ver(SER_FMT_VER_INVALID),
247 m_inventory_updated(false),
248 m_inventory_from_server(NULL),
249 m_inventory_from_server_age(0.0),
254 m_password(password),
255 m_access_denied(false),
256 m_itemdef_received(false),
257 m_nodedef_received(false),
258 m_media_downloader(new ClientMediaDownloader()),
259 m_time_of_day_set(false),
260 m_last_time_of_day_f(-1),
261 m_time_of_day_update_timer(0),
262 m_recommended_send_interval(0.1),
263 m_removed_sounds_check_timer(0),
270 Player *player = new LocalPlayer(this);
272 player->updateName(playername);
274 m_env.addPlayer(player);
280 //request all client managed threads to stop
281 m_mesh_update_thread.Stop();
284 bool Client::isShutdown()
287 if (!m_mesh_update_thread.IsRunning()) return true;
296 m_mesh_update_thread.Stop();
297 m_mesh_update_thread.Wait();
298 while(!m_mesh_update_thread.m_queue_out.empty()) {
299 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_frontNoEx();
304 delete m_inventory_from_server;
306 // Delete detached inventories
307 for(std::map<std::string, Inventory*>::iterator
308 i = m_detached_inventories.begin();
309 i != m_detached_inventories.end(); i++){
313 // cleanup 3d model meshes on client shutdown
314 while (m_device->getSceneManager()->getMeshCache()->getMeshCount() != 0) {
315 scene::IAnimatedMesh * mesh =
316 m_device->getSceneManager()->getMeshCache()->getMeshByIndex(0);
319 m_device->getSceneManager()->getMeshCache()->removeMesh(mesh);
323 void Client::connect(Address address)
325 DSTACK(__FUNCTION_NAME);
326 m_con.SetTimeoutMs(0);
327 m_con.Connect(address);
330 void Client::step(float dtime)
332 DSTACK(__FUNCTION_NAME);
338 if(m_ignore_damage_timer > dtime)
339 m_ignore_damage_timer -= dtime;
341 m_ignore_damage_timer = 0.0;
343 m_animation_time += dtime;
344 if(m_animation_time > 60.0)
345 m_animation_time -= 60.0;
347 m_time_of_day_update_timer += dtime;
355 float &counter = m_packetcounter_timer;
361 infostream << "Client packetcounter (" << m_packetcounter_timer
363 m_packetcounter.print(infostream);
364 m_packetcounter.clear();
371 Delete unused sectors
373 NOTE: This jams the game for a while because deleting sectors
377 float &counter = m_delete_unused_sectors_timer;
385 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
387 core::list<v3s16> deleted_blocks;
389 float delete_unused_sectors_timeout =
390 g_settings->getFloat("client_delete_unused_sectors_timeout");
392 // Delete sector blocks
393 /*u32 num = m_env.getMap().unloadUnusedData
394 (delete_unused_sectors_timeout,
395 true, &deleted_blocks);*/
397 // Delete whole sectors
398 m_env.getMap().unloadUnusedData
399 (delete_unused_sectors_timeout,
402 if(deleted_blocks.size() > 0)
404 /*infostream<<"Client: Deleted blocks of "<<num
405 <<" unused sectors"<<std::endl;*/
406 /*infostream<<"Client: Deleted "<<num
407 <<" unused sectors"<<std::endl;*/
413 // Env is locked so con can be locked.
414 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
416 core::list<v3s16>::Iterator i = deleted_blocks.begin();
417 core::list<v3s16> sendlist;
420 if(sendlist.size() == 255 || i == deleted_blocks.end())
422 if(sendlist.size() == 0)
431 u32 replysize = 2+1+6*sendlist.size();
432 SharedBuffer<u8> reply(replysize);
433 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
434 reply[2] = sendlist.size();
436 for(core::list<v3s16>::Iterator
437 j = sendlist.begin();
438 j != sendlist.end(); j++)
440 writeV3S16(&reply[2+1+6*k], *j);
443 m_con.Send(PEER_ID_SERVER, 1, reply, true);
445 if(i == deleted_blocks.end())
451 sendlist.push_back(*i);
458 // UGLY hack to fix 2 second startup delay caused by non existent
459 // server client startup synchronization in local server or singleplayer mode
460 static bool initial_step = true;
462 initial_step = false;
464 else if(m_state == LC_Created)
466 float &counter = m_connection_reinit_timer;
472 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
474 Player *myplayer = m_env.getLocalPlayer();
475 assert(myplayer != NULL);
476 // Send TOSERVER_INIT
477 // [0] u16 TOSERVER_INIT
478 // [2] u8 SER_FMT_VER_HIGHEST_READ
479 // [3] u8[20] player_name
480 // [23] u8[28] password (new in some version)
481 // [51] u16 minimum supported network protocol version (added sometime)
482 // [53] u16 maximum supported network protocol version (added later than the previous one)
483 SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2+2);
484 writeU16(&data[0], TOSERVER_INIT);
485 writeU8(&data[2], SER_FMT_VER_HIGHEST_READ);
487 memset((char*)&data[3], 0, PLAYERNAME_SIZE);
488 snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
490 /*infostream<<"Client: sending initial password hash: \""<<m_password<<"\""
493 memset((char*)&data[23], 0, PASSWORD_SIZE);
494 snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
496 writeU16(&data[51], CLIENT_PROTOCOL_VERSION_MIN);
497 writeU16(&data[53], CLIENT_PROTOCOL_VERSION_MAX);
499 // Send as unreliable
500 Send(1, data, false);
503 // Not connected, return
508 Do stuff if connected
512 Run Map's timers and unload unused data
514 const float map_timer_and_unload_dtime = 5.25;
515 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
517 ScopeProfiler sp(g_profiler, "Client: map timer and unload");
518 std::list<v3s16> deleted_blocks;
519 m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
520 g_settings->getFloat("client_unload_unused_data_timeout"),
523 /*if(deleted_blocks.size() > 0)
524 infostream<<"Client: Unloaded "<<deleted_blocks.size()
525 <<" unused blocks"<<std::endl;*/
529 NOTE: This loop is intentionally iterated the way it is.
532 std::list<v3s16>::iterator i = deleted_blocks.begin();
533 std::list<v3s16> sendlist;
536 if(sendlist.size() == 255 || i == deleted_blocks.end())
538 if(sendlist.size() == 0)
547 u32 replysize = 2+1+6*sendlist.size();
548 SharedBuffer<u8> reply(replysize);
549 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
550 reply[2] = sendlist.size();
552 for(std::list<v3s16>::iterator
553 j = sendlist.begin();
554 j != sendlist.end(); ++j)
556 writeV3S16(&reply[2+1+6*k], *j);
559 m_con.Send(PEER_ID_SERVER, 2, reply, true);
561 if(i == deleted_blocks.end())
567 sendlist.push_back(*i);
576 // Control local player (0ms)
577 LocalPlayer *player = m_env.getLocalPlayer();
578 assert(player != NULL);
579 player->applyControl(dtime);
589 ClientEnvEvent event = m_env.getClientEvent();
590 if(event.type == CEE_NONE)
594 else if(event.type == CEE_PLAYER_DAMAGE)
596 if(m_ignore_damage_timer <= 0)
598 u8 damage = event.player_damage.amount;
600 if(event.player_damage.send_to_server)
603 // Add to ClientEvent queue
605 event.type = CE_PLAYER_DAMAGE;
606 event.player_damage.amount = damage;
607 m_client_event_queue.push_back(event);
610 else if(event.type == CEE_PLAYER_BREATH)
612 u16 breath = event.player_breath.amount;
622 float &counter = m_avg_rtt_timer;
627 // connectedAndInitialized() is true, peer exists.
628 float avg_rtt = getRTT();
629 infostream<<"Client: avg_rtt="<<avg_rtt<<std::endl;
634 Send player position to server
637 float &counter = m_playerpos_send_timer;
639 if((m_state == LC_Ready) && (counter >= m_recommended_send_interval))
647 Replace updated meshes
650 int num_processed_meshes = 0;
651 while(!m_mesh_update_thread.m_queue_out.empty())
653 num_processed_meshes++;
654 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_frontNoEx();
655 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
658 // Delete the old mesh
659 if(block->mesh != NULL)
661 // TODO: Remove hardware buffers of meshbuffers of block->mesh
666 // Replace with the new mesh
667 block->mesh = r.mesh;
671 if(r.ack_block_to_server)
683 u32 replysize = 2+1+6;
684 SharedBuffer<u8> reply(replysize);
685 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
687 writeV3S16(&reply[3], r.p);
689 m_con.Send(PEER_ID_SERVER, 2, reply, true);
692 if(num_processed_meshes > 0)
693 g_profiler->graphAdd("num_processed_meshes", num_processed_meshes);
699 if (m_media_downloader && m_media_downloader->isStarted()) {
700 m_media_downloader->step(this);
701 if (m_media_downloader->isDone()) {
703 delete m_media_downloader;
704 m_media_downloader = NULL;
709 If the server didn't update the inventory in a while, revert
710 the local inventory (so the player notices the lag problem
711 and knows something is wrong).
713 if(m_inventory_from_server)
715 float interval = 10.0;
716 float count_before = floor(m_inventory_from_server_age / interval);
718 m_inventory_from_server_age += dtime;
720 float count_after = floor(m_inventory_from_server_age / interval);
722 if(count_after != count_before)
724 // Do this every <interval> seconds after TOCLIENT_INVENTORY
725 // Reset the locally changed inventory to the authoritative inventory
726 Player *player = m_env.getLocalPlayer();
727 player->inventory = *m_inventory_from_server;
728 m_inventory_updated = true;
733 Update positions of sounds attached to objects
736 for(std::map<int, u16>::iterator
737 i = m_sounds_to_objects.begin();
738 i != m_sounds_to_objects.end(); i++)
740 int client_id = i->first;
741 u16 object_id = i->second;
742 ClientActiveObject *cao = m_env.getActiveObject(object_id);
745 v3f pos = cao->getPosition();
746 m_sound->updateSoundPosition(client_id, pos);
751 Handle removed remotely initiated sounds
753 m_removed_sounds_check_timer += dtime;
754 if(m_removed_sounds_check_timer >= 2.32)
756 m_removed_sounds_check_timer = 0;
757 // Find removed sounds and clear references to them
758 std::set<s32> removed_server_ids;
759 for(std::map<s32, int>::iterator
760 i = m_sounds_server_to_client.begin();
761 i != m_sounds_server_to_client.end();)
763 s32 server_id = i->first;
764 int client_id = i->second;
766 if(!m_sound->soundExists(client_id)){
767 m_sounds_server_to_client.erase(server_id);
768 m_sounds_client_to_server.erase(client_id);
769 m_sounds_to_objects.erase(client_id);
770 removed_server_ids.insert(server_id);
774 if(removed_server_ids.size() != 0)
776 std::ostringstream os(std::ios_base::binary);
777 writeU16(os, TOSERVER_REMOVED_SOUNDS);
778 size_t server_ids = removed_server_ids.size();
779 assert(server_ids <= 0xFFFF);
780 writeU16(os, (u16) (server_ids & 0xFFFF));
781 for(std::set<s32>::iterator i = removed_server_ids.begin();
782 i != removed_server_ids.end(); i++)
784 std::string s = os.str();
785 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
792 bool Client::loadMedia(const std::string &data, const std::string &filename)
794 // Silly irrlicht's const-incorrectness
795 Buffer<char> data_rw(data.c_str(), data.size());
799 const char *image_ext[] = {
800 ".png", ".jpg", ".bmp", ".tga",
801 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
804 name = removeStringEnd(filename, image_ext);
807 verbosestream<<"Client: Attempting to load image "
808 <<"file \""<<filename<<"\""<<std::endl;
810 io::IFileSystem *irrfs = m_device->getFileSystem();
811 video::IVideoDriver *vdrv = m_device->getVideoDriver();
813 // Create an irrlicht memory file
814 io::IReadFile *rfile = irrfs->createMemoryReadFile(
815 *data_rw, data_rw.getSize(), "_tempreadfile");
818 video::IImage *img = vdrv->createImageFromFile(rfile);
820 errorstream<<"Client: Cannot create image from data of "
821 <<"file \""<<filename<<"\""<<std::endl;
826 m_tsrc->insertSourceImage(filename, img);
833 const char *sound_ext[] = {
834 ".0.ogg", ".1.ogg", ".2.ogg", ".3.ogg", ".4.ogg",
835 ".5.ogg", ".6.ogg", ".7.ogg", ".8.ogg", ".9.ogg",
838 name = removeStringEnd(filename, sound_ext);
841 verbosestream<<"Client: Attempting to load sound "
842 <<"file \""<<filename<<"\""<<std::endl;
843 m_sound->loadSoundData(name, data);
847 const char *model_ext[] = {
848 ".x", ".b3d", ".md2", ".obj",
851 name = removeStringEnd(filename, model_ext);
854 verbosestream<<"Client: Storing model into memory: "
855 <<"\""<<filename<<"\""<<std::endl;
856 if(m_mesh_data.count(filename))
857 errorstream<<"Multiple models with name \""<<filename.c_str()
858 <<"\" found; replacing previous model"<<std::endl;
859 m_mesh_data[filename] = data;
863 errorstream<<"Client: Don't know how to load file \""
864 <<filename<<"\""<<std::endl;
868 // Virtual methods from con::PeerHandler
869 void Client::peerAdded(con::Peer *peer)
871 infostream<<"Client::peerAdded(): peer->id="
872 <<peer->id<<std::endl;
874 void Client::deletingPeer(con::Peer *peer, bool timeout)
876 infostream<<"Client::deletingPeer(): "
877 "Server Peer is getting deleted "
878 <<"(timeout="<<timeout<<")"<<std::endl;
883 u16 number of files requested
889 void Client::request_media(const std::list<std::string> &file_requests)
891 std::ostringstream os(std::ios_base::binary);
892 writeU16(os, TOSERVER_REQUEST_MEDIA);
893 size_t file_requests_size = file_requests.size();
894 assert(file_requests_size <= 0xFFFF);
895 writeU16(os, (u16) (file_requests_size & 0xFFFF));
897 for(std::list<std::string>::const_iterator i = file_requests.begin();
898 i != file_requests.end(); ++i) {
899 os<<serializeString(*i);
903 std::string s = os.str();
904 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
907 infostream<<"Client: Sending media request list to server ("
908 <<file_requests.size()<<" files)"<<std::endl;
911 void Client::received_media()
913 // notify server we received everything
914 std::ostringstream os(std::ios_base::binary);
915 writeU16(os, TOSERVER_RECEIVED_MEDIA);
916 std::string s = os.str();
917 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
920 infostream<<"Client: Notifying server that we received all media"
924 void Client::ReceiveAll()
926 DSTACK(__FUNCTION_NAME);
927 u32 start_ms = porting::getTimeMs();
930 // Limit time even if there would be huge amounts of data to
932 if(porting::getTimeMs() > start_ms + 100)
937 g_profiler->graphAdd("client_received_packets", 1);
939 catch(con::NoIncomingDataException &e)
943 catch(con::InvalidIncomingDataException &e)
945 infostream<<"Client::ReceiveAll(): "
946 "InvalidIncomingDataException: what()="
947 <<e.what()<<std::endl;
952 void Client::Receive()
954 DSTACK(__FUNCTION_NAME);
955 SharedBuffer<u8> data;
957 u32 datasize = m_con.Receive(sender_peer_id, data);
958 ProcessData(*data, datasize, sender_peer_id);
962 sender_peer_id given to this shall be quaranteed to be a valid peer
964 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
966 DSTACK(__FUNCTION_NAME);
968 // Ignore packets that don't even fit a command
971 m_packetcounter.add(60000);
975 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
977 //infostream<<"Client: received command="<<command<<std::endl;
978 m_packetcounter.add((u16)command);
981 If this check is removed, be sure to change the queue
982 system to know the ids
984 if(sender_peer_id != PEER_ID_SERVER)
986 infostream<<"Client::ProcessData(): Discarding data not "
987 "coming from server: peer_id="<<sender_peer_id
992 u8 ser_version = m_server_ser_ver;
994 if(command == TOCLIENT_INIT)
999 u8 deployed = data[2];
1001 infostream<<"Client: TOCLIENT_INIT received with "
1002 "deployed="<<((int)deployed&0xff)<<std::endl;
1004 if(!ser_ver_supported(deployed))
1006 infostream<<"Client: TOCLIENT_INIT: Server sent "
1007 <<"unsupported ser_fmt_ver"<<std::endl;
1011 m_server_ser_ver = deployed;
1013 // Get player position
1014 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
1015 if(datasize >= 2+1+6)
1016 playerpos_s16 = readV3S16(&data[2+1]);
1017 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
1020 // Set player position
1021 Player *player = m_env.getLocalPlayer();
1022 assert(player != NULL);
1023 player->setPosition(playerpos_f);
1025 if(datasize >= 2+1+6+8)
1028 m_map_seed = readU64(&data[2+1+6]);
1029 infostream<<"Client: received map seed: "<<m_map_seed<<std::endl;
1032 if(datasize >= 2+1+6+8+4)
1035 m_recommended_send_interval = readF1000(&data[2+1+6+8]);
1036 infostream<<"Client: received recommended send interval "
1037 <<m_recommended_send_interval<<std::endl;
1042 SharedBuffer<u8> reply(replysize);
1043 writeU16(&reply[0], TOSERVER_INIT2);
1045 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1052 if(command == TOCLIENT_ACCESS_DENIED)
1054 // The server didn't like our password. Note, this needs
1055 // to be processed even if the serialisation format has
1056 // not been agreed yet, the same as TOCLIENT_INIT.
1057 m_access_denied = true;
1058 m_access_denied_reason = L"Unknown";
1061 std::string datastring((char*)&data[2], datasize-2);
1062 std::istringstream is(datastring, std::ios_base::binary);
1063 m_access_denied_reason = deSerializeWideString(is);
1068 if(ser_version == SER_FMT_VER_INVALID)
1070 infostream<<"Client: Server serialization"
1071 " format invalid or not initialized."
1072 " Skipping incoming command="<<command<<std::endl;
1077 Handle runtime commands
1079 // there's no sane reason why we shouldn't have a player and
1080 // almost everyone needs a player reference
1081 Player *player = m_env.getLocalPlayer();
1082 assert(player != NULL);
1084 if(command == TOCLIENT_REMOVENODE)
1089 p.X = readS16(&data[2]);
1090 p.Y = readS16(&data[4]);
1091 p.Z = readS16(&data[6]);
1094 else if(command == TOCLIENT_ADDNODE)
1096 if(datasize < 8 + MapNode::serializedLength(ser_version))
1100 p.X = readS16(&data[2]);
1101 p.Y = readS16(&data[4]);
1102 p.Z = readS16(&data[6]);
1105 n.deSerialize(&data[8], ser_version);
1107 bool remove_metadata = true;
1108 u32 index = 8 + MapNode::serializedLength(ser_version);
1109 if ((datasize >= index+1) && data[index]){
1110 remove_metadata = false;
1113 addNode(p, n, remove_metadata);
1115 else if(command == TOCLIENT_BLOCKDATA)
1117 // Ignore too small packet
1122 p.X = readS16(&data[2]);
1123 p.Y = readS16(&data[4]);
1124 p.Z = readS16(&data[6]);
1126 std::string datastring((char*)&data[8], datasize-8);
1127 std::istringstream istr(datastring, std::ios_base::binary);
1132 v2s16 p2d(p.X, p.Z);
1133 sector = m_env.getMap().emergeSector(p2d);
1135 assert(sector->getPos() == p2d);
1137 block = sector->getBlockNoCreateNoEx(p.Y);
1141 Update an existing block
1143 block->deSerialize(istr, ser_version, false);
1144 block->deSerializeNetworkSpecific(istr);
1151 block = new MapBlock(&m_env.getMap(), p, this);
1152 block->deSerialize(istr, ser_version, false);
1153 block->deSerializeNetworkSpecific(istr);
1154 sector->insertBlock(block);
1158 Add it to mesh update queue and set it to be acknowledged after update.
1160 addUpdateMeshTaskWithEdge(p, true);
1162 else if(command == TOCLIENT_INVENTORY)
1167 std::string datastring((char*)&data[2], datasize-2);
1168 std::istringstream is(datastring, std::ios_base::binary);
1170 player->inventory.deSerialize(is);
1172 m_inventory_updated = true;
1174 delete m_inventory_from_server;
1175 m_inventory_from_server = new Inventory(player->inventory);
1176 m_inventory_from_server_age = 0.0;
1179 else if(command == TOCLIENT_TIME_OF_DAY)
1184 u16 time_of_day = readU16(&data[2]);
1185 time_of_day = time_of_day % 24000;
1186 float time_speed = 0;
1188 if(datasize >= 2 + 2 + 4)
1190 time_speed = readF1000(&data[4]);
1193 // Old message; try to approximate speed of time by ourselves
1194 float time_of_day_f = (float)time_of_day / 24000.0;
1195 float tod_diff_f = 0;
1197 if(time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
1198 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0;
1200 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
1202 m_last_time_of_day_f = time_of_day_f;
1203 float time_diff = m_time_of_day_update_timer;
1204 m_time_of_day_update_timer = 0;
1206 if(m_time_of_day_set){
1207 time_speed = (3600.0*24.0) * tod_diff_f / time_diff;
1208 infostream<<"Client: Measured time_of_day speed (old format): "
1209 <<time_speed<<" tod_diff_f="<<tod_diff_f
1210 <<" time_diff="<<time_diff<<std::endl;
1214 // Update environment
1215 m_env.setTimeOfDay(time_of_day);
1216 m_env.setTimeOfDaySpeed(time_speed);
1217 m_time_of_day_set = true;
1219 u32 dr = m_env.getDayNightRatio();
1220 infostream<<"Client: time_of_day="<<time_of_day
1221 <<" time_speed="<<time_speed
1222 <<" dr="<<dr<<std::endl;
1224 else if(command == TOCLIENT_CHAT_MESSAGE)
1232 std::string datastring((char*)&data[2], datasize-2);
1233 std::istringstream is(datastring, std::ios_base::binary);
1236 is.read((char*) buf, 2);
1237 u16 len = readU16(buf);
1239 std::wstring message;
1240 for(unsigned int i=0; i<len; i++)
1242 is.read((char*)buf, 2);
1243 message += (wchar_t)readU16(buf);
1246 m_chat_queue.push_back(message);
1248 else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1252 u16 count of removed objects
1253 for all removed objects {
1256 u16 count of added objects
1257 for all added objects {
1260 u32 initialization data length
1261 string initialization data
1266 // Get all data except the command number
1267 std::string datastring((char*)&data[2], datasize-2);
1268 // Throw them in an istringstream
1269 std::istringstream is(datastring, std::ios_base::binary);
1271 // Read removed objects
1273 u16 removed_count = readU16((u8*)buf);
1274 for(unsigned int i=0; i<removed_count; i++)
1277 u16 id = readU16((u8*)buf);
1278 m_env.removeActiveObject(id);
1281 // Read added objects
1283 u16 added_count = readU16((u8*)buf);
1284 for(unsigned int i=0; i<added_count; i++)
1287 u16 id = readU16((u8*)buf);
1289 u8 type = readU8((u8*)buf);
1290 std::string data = deSerializeLongString(is);
1292 m_env.addActiveObject(id, type, data);
1295 else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1307 // Get all data except the command number
1308 std::string datastring((char*)&data[2], datasize-2);
1309 // Throw them in an istringstream
1310 std::istringstream is(datastring, std::ios_base::binary);
1312 while(is.eof() == false)
1315 u16 id = readU16((u8*)buf);
1319 size_t message_size = readU16((u8*)buf);
1320 std::string message;
1321 message.reserve(message_size);
1322 for(unsigned int i=0; i<message_size; i++)
1325 message.append(buf, 1);
1327 // Pass on to the environment
1328 m_env.processActiveObjectMessage(id, message);
1331 else if(command == TOCLIENT_MOVEMENT)
1333 std::string datastring((char*)&data[2], datasize-2);
1334 std::istringstream is(datastring, std::ios_base::binary);
1336 player->movement_acceleration_default = readF1000(is) * BS;
1337 player->movement_acceleration_air = readF1000(is) * BS;
1338 player->movement_acceleration_fast = readF1000(is) * BS;
1339 player->movement_speed_walk = readF1000(is) * BS;
1340 player->movement_speed_crouch = readF1000(is) * BS;
1341 player->movement_speed_fast = readF1000(is) * BS;
1342 player->movement_speed_climb = readF1000(is) * BS;
1343 player->movement_speed_jump = readF1000(is) * BS;
1344 player->movement_liquid_fluidity = readF1000(is) * BS;
1345 player->movement_liquid_fluidity_smooth = readF1000(is) * BS;
1346 player->movement_liquid_sink = readF1000(is) * BS;
1347 player->movement_gravity = readF1000(is) * BS;
1349 else if(command == TOCLIENT_HP)
1351 std::string datastring((char*)&data[2], datasize-2);
1352 std::istringstream is(datastring, std::ios_base::binary);
1354 u8 oldhp = player->hp;
1360 // Add to ClientEvent queue
1362 event.type = CE_PLAYER_DAMAGE;
1363 event.player_damage.amount = oldhp - hp;
1364 m_client_event_queue.push_back(event);
1367 else if(command == TOCLIENT_BREATH)
1369 std::string datastring((char*)&data[2], datasize-2);
1370 std::istringstream is(datastring, std::ios_base::binary);
1372 player->setBreath(readU16(is));
1374 else if(command == TOCLIENT_MOVE_PLAYER)
1376 std::string datastring((char*)&data[2], datasize-2);
1377 std::istringstream is(datastring, std::ios_base::binary);
1379 v3f pos = readV3F1000(is);
1380 f32 pitch = readF1000(is);
1381 f32 yaw = readF1000(is);
1382 player->setPosition(pos);
1384 infostream<<"Client got TOCLIENT_MOVE_PLAYER"
1385 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1391 Add to ClientEvent queue.
1392 This has to be sent to the main program because otherwise
1393 it would just force the pitch and yaw values to whatever
1394 the camera points to.
1397 event.type = CE_PLAYER_FORCE_MOVE;
1398 event.player_force_move.pitch = pitch;
1399 event.player_force_move.yaw = yaw;
1400 m_client_event_queue.push_back(event);
1402 // Ignore damage for a few seconds, so that the player doesn't
1403 // get damage from falling on ground
1404 m_ignore_damage_timer = 3.0;
1406 else if(command == TOCLIENT_PLAYERITEM)
1408 infostream<<"Client: WARNING: Ignoring TOCLIENT_PLAYERITEM"<<std::endl;
1410 else if(command == TOCLIENT_DEATHSCREEN)
1412 std::string datastring((char*)&data[2], datasize-2);
1413 std::istringstream is(datastring, std::ios_base::binary);
1415 bool set_camera_point_target = readU8(is);
1416 v3f camera_point_target = readV3F1000(is);
1419 event.type = CE_DEATHSCREEN;
1420 event.deathscreen.set_camera_point_target = set_camera_point_target;
1421 event.deathscreen.camera_point_target_x = camera_point_target.X;
1422 event.deathscreen.camera_point_target_y = camera_point_target.Y;
1423 event.deathscreen.camera_point_target_z = camera_point_target.Z;
1424 m_client_event_queue.push_back(event);
1426 else if(command == TOCLIENT_ANNOUNCE_MEDIA)
1428 std::string datastring((char*)&data[2], datasize-2);
1429 std::istringstream is(datastring, std::ios_base::binary);
1431 int num_files = readU16(is);
1433 infostream<<"Client: Received media announcement: packet size: "
1434 <<datasize<<std::endl;
1436 if (m_media_downloader == NULL ||
1437 m_media_downloader->isStarted()) {
1438 const char *problem = m_media_downloader ?
1439 "we already saw another announcement" :
1440 "all media has been received already";
1441 errorstream<<"Client: Received media announcement but "
1443 <<" files="<<num_files
1444 <<" size="<<datasize<<std::endl;
1448 // Mesh update thread must be stopped while
1449 // updating content definitions
1450 assert(!m_mesh_update_thread.IsRunning());
1452 for(int i=0; i<num_files; i++)
1454 std::string name = deSerializeString(is);
1455 std::string sha1_base64 = deSerializeString(is);
1456 std::string sha1_raw = base64_decode(sha1_base64);
1457 m_media_downloader->addFile(name, sha1_raw);
1460 std::vector<std::string> remote_media;
1462 Strfnd sf(deSerializeString(is));
1463 while(!sf.atend()) {
1464 std::string baseurl = trim(sf.next(","));
1466 m_media_downloader->addRemoteServer(baseurl);
1469 catch(SerializationError& e) {
1470 // not supported by server or turned off
1473 m_media_downloader->step(this);
1475 else if(command == TOCLIENT_MEDIA)
1477 std::string datastring((char*)&data[2], datasize-2);
1478 std::istringstream is(datastring, std::ios_base::binary);
1482 u16 total number of file bunches
1483 u16 index of this bunch
1484 u32 number of files in this bunch
1492 int num_bunches = readU16(is);
1493 int bunch_i = readU16(is);
1494 u32 num_files = readU32(is);
1495 infostream<<"Client: Received files: bunch "<<bunch_i<<"/"
1496 <<num_bunches<<" files="<<num_files
1497 <<" size="<<datasize<<std::endl;
1502 if (m_media_downloader == NULL ||
1503 !m_media_downloader->isStarted()) {
1504 const char *problem = m_media_downloader ?
1505 "media has not been requested" :
1506 "all media has been received already";
1507 errorstream<<"Client: Received media but "
1509 <<" bunch "<<bunch_i<<"/"<<num_bunches
1510 <<" files="<<num_files
1511 <<" size="<<datasize<<std::endl;
1515 // Mesh update thread must be stopped while
1516 // updating content definitions
1517 assert(!m_mesh_update_thread.IsRunning());
1519 for(unsigned int i=0; i<num_files; i++){
1520 std::string name = deSerializeString(is);
1521 std::string data = deSerializeLongString(is);
1522 m_media_downloader->conventionalTransferDone(
1526 else if(command == TOCLIENT_TOOLDEF)
1528 infostream<<"Client: WARNING: Ignoring TOCLIENT_TOOLDEF"<<std::endl;
1530 else if(command == TOCLIENT_NODEDEF)
1532 infostream<<"Client: Received node definitions: packet size: "
1533 <<datasize<<std::endl;
1535 // Mesh update thread must be stopped while
1536 // updating content definitions
1537 assert(!m_mesh_update_thread.IsRunning());
1539 // Decompress node definitions
1540 std::string datastring((char*)&data[2], datasize-2);
1541 std::istringstream is(datastring, std::ios_base::binary);
1542 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1543 std::ostringstream tmp_os;
1544 decompressZlib(tmp_is, tmp_os);
1546 // Deserialize node definitions
1547 std::istringstream tmp_is2(tmp_os.str());
1548 m_nodedef->deSerialize(tmp_is2);
1549 m_nodedef_received = true;
1551 else if(command == TOCLIENT_CRAFTITEMDEF)
1553 infostream<<"Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF"<<std::endl;
1555 else if(command == TOCLIENT_ITEMDEF)
1557 infostream<<"Client: Received item definitions: packet size: "
1558 <<datasize<<std::endl;
1560 // Mesh update thread must be stopped while
1561 // updating content definitions
1562 assert(!m_mesh_update_thread.IsRunning());
1564 // Decompress item definitions
1565 std::string datastring((char*)&data[2], datasize-2);
1566 std::istringstream is(datastring, std::ios_base::binary);
1567 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1568 std::ostringstream tmp_os;
1569 decompressZlib(tmp_is, tmp_os);
1571 // Deserialize node definitions
1572 std::istringstream tmp_is2(tmp_os.str());
1573 m_itemdef->deSerialize(tmp_is2);
1574 m_itemdef_received = true;
1576 else if(command == TOCLIENT_PLAY_SOUND)
1578 std::string datastring((char*)&data[2], datasize-2);
1579 std::istringstream is(datastring, std::ios_base::binary);
1581 s32 server_id = readS32(is);
1582 std::string name = deSerializeString(is);
1583 float gain = readF1000(is);
1584 int type = readU8(is); // 0=local, 1=positional, 2=object
1585 v3f pos = readV3F1000(is);
1586 u16 object_id = readU16(is);
1587 bool loop = readU8(is);
1592 client_id = m_sound->playSound(name, loop, gain);
1594 case 1: // positional
1595 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1598 ClientActiveObject *cao = m_env.getActiveObject(object_id);
1600 pos = cao->getPosition();
1601 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1602 // TODO: Set up sound to move with object
1607 if(client_id != -1){
1608 m_sounds_server_to_client[server_id] = client_id;
1609 m_sounds_client_to_server[client_id] = server_id;
1611 m_sounds_to_objects[client_id] = object_id;
1614 else if(command == TOCLIENT_STOP_SOUND)
1616 std::string datastring((char*)&data[2], datasize-2);
1617 std::istringstream is(datastring, std::ios_base::binary);
1619 s32 server_id = readS32(is);
1620 std::map<s32, int>::iterator i =
1621 m_sounds_server_to_client.find(server_id);
1622 if(i != m_sounds_server_to_client.end()){
1623 int client_id = i->second;
1624 m_sound->stopSound(client_id);
1627 else if(command == TOCLIENT_PRIVILEGES)
1629 std::string datastring((char*)&data[2], datasize-2);
1630 std::istringstream is(datastring, std::ios_base::binary);
1632 m_privileges.clear();
1633 infostream<<"Client: Privileges updated: ";
1634 u16 num_privileges = readU16(is);
1635 for(unsigned int i=0; i<num_privileges; i++){
1636 std::string priv = deSerializeString(is);
1637 m_privileges.insert(priv);
1638 infostream<<priv<<" ";
1640 infostream<<std::endl;
1642 else if(command == TOCLIENT_INVENTORY_FORMSPEC)
1644 std::string datastring((char*)&data[2], datasize-2);
1645 std::istringstream is(datastring, std::ios_base::binary);
1647 // Store formspec in LocalPlayer
1648 player->inventory_formspec = deSerializeLongString(is);
1650 else if(command == TOCLIENT_DETACHED_INVENTORY)
1652 std::string datastring((char*)&data[2], datasize-2);
1653 std::istringstream is(datastring, std::ios_base::binary);
1655 std::string name = deSerializeString(is);
1657 infostream<<"Client: Detached inventory update: \""<<name<<"\""<<std::endl;
1659 Inventory *inv = NULL;
1660 if(m_detached_inventories.count(name) > 0)
1661 inv = m_detached_inventories[name];
1663 inv = new Inventory(m_itemdef);
1664 m_detached_inventories[name] = inv;
1666 inv->deSerialize(is);
1668 else if(command == TOCLIENT_SHOW_FORMSPEC)
1670 std::string datastring((char*)&data[2], datasize-2);
1671 std::istringstream is(datastring, std::ios_base::binary);
1673 std::string formspec = deSerializeLongString(is);
1674 std::string formname = deSerializeString(is);
1677 event.type = CE_SHOW_FORMSPEC;
1678 // pointer is required as event is a struct only!
1679 // adding a std:string to a struct isn't possible
1680 event.show_formspec.formspec = new std::string(formspec);
1681 event.show_formspec.formname = new std::string(formname);
1682 m_client_event_queue.push_back(event);
1684 else if(command == TOCLIENT_SPAWN_PARTICLE)
1686 std::string datastring((char*)&data[2], datasize-2);
1687 std::istringstream is(datastring, std::ios_base::binary);
1689 v3f pos = readV3F1000(is);
1690 v3f vel = readV3F1000(is);
1691 v3f acc = readV3F1000(is);
1692 float expirationtime = readF1000(is);
1693 float size = readF1000(is);
1694 bool collisiondetection = readU8(is);
1695 std::string texture = deSerializeLongString(is);
1696 bool vertical = false;
1698 vertical = readU8(is);
1702 event.type = CE_SPAWN_PARTICLE;
1703 event.spawn_particle.pos = new v3f (pos);
1704 event.spawn_particle.vel = new v3f (vel);
1705 event.spawn_particle.acc = new v3f (acc);
1706 event.spawn_particle.expirationtime = expirationtime;
1707 event.spawn_particle.size = size;
1708 event.spawn_particle.collisiondetection = collisiondetection;
1709 event.spawn_particle.vertical = vertical;
1710 event.spawn_particle.texture = new std::string(texture);
1712 m_client_event_queue.push_back(event);
1714 else if(command == TOCLIENT_ADD_PARTICLESPAWNER)
1716 std::string datastring((char*)&data[2], datasize-2);
1717 std::istringstream is(datastring, std::ios_base::binary);
1719 u16 amount = readU16(is);
1720 float spawntime = readF1000(is);
1721 v3f minpos = readV3F1000(is);
1722 v3f maxpos = readV3F1000(is);
1723 v3f minvel = readV3F1000(is);
1724 v3f maxvel = readV3F1000(is);
1725 v3f minacc = readV3F1000(is);
1726 v3f maxacc = readV3F1000(is);
1727 float minexptime = readF1000(is);
1728 float maxexptime = readF1000(is);
1729 float minsize = readF1000(is);
1730 float maxsize = readF1000(is);
1731 bool collisiondetection = readU8(is);
1732 std::string texture = deSerializeLongString(is);
1733 u32 id = readU32(is);
1734 bool vertical = false;
1736 vertical = readU8(is);
1740 event.type = CE_ADD_PARTICLESPAWNER;
1741 event.add_particlespawner.amount = amount;
1742 event.add_particlespawner.spawntime = spawntime;
1743 event.add_particlespawner.minpos = new v3f (minpos);
1744 event.add_particlespawner.maxpos = new v3f (maxpos);
1745 event.add_particlespawner.minvel = new v3f (minvel);
1746 event.add_particlespawner.maxvel = new v3f (maxvel);
1747 event.add_particlespawner.minacc = new v3f (minacc);
1748 event.add_particlespawner.maxacc = new v3f (maxacc);
1749 event.add_particlespawner.minexptime = minexptime;
1750 event.add_particlespawner.maxexptime = maxexptime;
1751 event.add_particlespawner.minsize = minsize;
1752 event.add_particlespawner.maxsize = maxsize;
1753 event.add_particlespawner.collisiondetection = collisiondetection;
1754 event.add_particlespawner.vertical = vertical;
1755 event.add_particlespawner.texture = new std::string(texture);
1756 event.add_particlespawner.id = id;
1758 m_client_event_queue.push_back(event);
1760 else if(command == TOCLIENT_DELETE_PARTICLESPAWNER)
1762 std::string datastring((char*)&data[2], datasize-2);
1763 std::istringstream is(datastring, std::ios_base::binary);
1765 u32 id = readU16(is);
1768 event.type = CE_DELETE_PARTICLESPAWNER;
1769 event.delete_particlespawner.id = id;
1771 m_client_event_queue.push_back(event);
1773 else if(command == TOCLIENT_HUDADD)
1775 std::string datastring((char *)&data[2], datasize - 2);
1776 std::istringstream is(datastring, std::ios_base::binary);
1778 u32 id = readU32(is);
1779 u8 type = readU8(is);
1780 v2f pos = readV2F1000(is);
1781 std::string name = deSerializeString(is);
1782 v2f scale = readV2F1000(is);
1783 std::string text = deSerializeString(is);
1784 u32 number = readU32(is);
1785 u32 item = readU32(is);
1786 u32 dir = readU32(is);
1787 v2f align = readV2F1000(is);
1788 v2f offset = readV2F1000(is);
1792 world_pos = readV3F1000(is);
1793 }catch(SerializationError &e) {};
1795 size = readV2S32(is);
1796 } catch(SerializationError &e) {};
1799 event.type = CE_HUDADD;
1800 event.hudadd.id = id;
1801 event.hudadd.type = type;
1802 event.hudadd.pos = new v2f(pos);
1803 event.hudadd.name = new std::string(name);
1804 event.hudadd.scale = new v2f(scale);
1805 event.hudadd.text = new std::string(text);
1806 event.hudadd.number = number;
1807 event.hudadd.item = item;
1808 event.hudadd.dir = dir;
1809 event.hudadd.align = new v2f(align);
1810 event.hudadd.offset = new v2f(offset);
1811 event.hudadd.world_pos = new v3f(world_pos);
1812 event.hudadd.size = new v2s32(size);
1813 m_client_event_queue.push_back(event);
1815 else if(command == TOCLIENT_HUDRM)
1817 std::string datastring((char *)&data[2], datasize - 2);
1818 std::istringstream is(datastring, std::ios_base::binary);
1820 u32 id = readU32(is);
1823 event.type = CE_HUDRM;
1824 event.hudrm.id = id;
1825 m_client_event_queue.push_back(event);
1827 else if(command == TOCLIENT_HUDCHANGE)
1835 std::string datastring((char *)&data[2], datasize - 2);
1836 std::istringstream is(datastring, std::ios_base::binary);
1838 u32 id = readU32(is);
1839 u8 stat = (HudElementStat)readU8(is);
1841 if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
1842 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
1843 v2fdata = readV2F1000(is);
1844 else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
1845 sdata = deSerializeString(is);
1846 else if (stat == HUD_STAT_WORLD_POS)
1847 v3fdata = readV3F1000(is);
1848 else if (stat == HUD_STAT_SIZE )
1849 v2s32data = readV2S32(is);
1851 intdata = readU32(is);
1854 event.type = CE_HUDCHANGE;
1855 event.hudchange.id = id;
1856 event.hudchange.stat = (HudElementStat)stat;
1857 event.hudchange.v2fdata = new v2f(v2fdata);
1858 event.hudchange.v3fdata = new v3f(v3fdata);
1859 event.hudchange.sdata = new std::string(sdata);
1860 event.hudchange.data = intdata;
1861 event.hudchange.v2s32data = new v2s32(v2s32data);
1862 m_client_event_queue.push_back(event);
1864 else if(command == TOCLIENT_HUD_SET_FLAGS)
1866 std::string datastring((char *)&data[2], datasize - 2);
1867 std::istringstream is(datastring, std::ios_base::binary);
1869 u32 flags = readU32(is);
1870 u32 mask = readU32(is);
1872 player->hud_flags &= ~mask;
1873 player->hud_flags |= flags;
1875 else if(command == TOCLIENT_HUD_SET_PARAM)
1877 std::string datastring((char *)&data[2], datasize - 2);
1878 std::istringstream is(datastring, std::ios_base::binary);
1880 u16 param = readU16(is);
1881 std::string value = deSerializeString(is);
1883 if(param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) {
1884 s32 hotbar_itemcount = readS32((u8*) value.c_str());
1885 if(hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
1886 player->hud_hotbar_itemcount = hotbar_itemcount;
1888 else if (param == HUD_PARAM_HOTBAR_IMAGE) {
1889 ((LocalPlayer *) player)->hotbar_image = value;
1891 else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
1892 ((LocalPlayer *) player)->hotbar_selected_image = value;
1895 else if(command == TOCLIENT_SET_SKY)
1897 std::string datastring((char *)&data[2], datasize - 2);
1898 std::istringstream is(datastring, std::ios_base::binary);
1900 video::SColor *bgcolor = new video::SColor(readARGB8(is));
1901 std::string *type = new std::string(deSerializeString(is));
1902 u16 count = readU16(is);
1903 std::vector<std::string> *params = new std::vector<std::string>;
1905 for(size_t i=0; i<count; i++)
1906 params->push_back(deSerializeString(is));
1909 event.type = CE_SET_SKY;
1910 event.set_sky.bgcolor = bgcolor;
1911 event.set_sky.type = type;
1912 event.set_sky.params = params;
1913 m_client_event_queue.push_back(event);
1915 else if(command == TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO)
1917 std::string datastring((char *)&data[2], datasize - 2);
1918 std::istringstream is(datastring, std::ios_base::binary);
1920 bool do_override = readU8(is);
1921 float day_night_ratio_f = (float)readU16(is) / 65536;
1924 event.type = CE_OVERRIDE_DAY_NIGHT_RATIO;
1925 event.override_day_night_ratio.do_override = do_override;
1926 event.override_day_night_ratio.ratio_f = day_night_ratio_f;
1927 m_client_event_queue.push_back(event);
1929 else if(command == TOCLIENT_LOCAL_PLAYER_ANIMATIONS)
1931 std::string datastring((char *)&data[2], datasize - 2);
1932 std::istringstream is(datastring, std::ios_base::binary);
1934 LocalPlayer *player = m_env.getLocalPlayer();
1935 assert(player != NULL);
1937 player->local_animations[0] = readV2S32(is);
1938 player->local_animations[1] = readV2S32(is);
1939 player->local_animations[2] = readV2S32(is);
1940 player->local_animations[3] = readV2S32(is);
1941 player->local_animation_speed = readF1000(is);
1943 else if(command == TOCLIENT_EYE_OFFSET)
1945 std::string datastring((char *)&data[2], datasize - 2);
1946 std::istringstream is(datastring, std::ios_base::binary);
1948 LocalPlayer *player = m_env.getLocalPlayer();
1949 assert(player != NULL);
1951 player->eye_offset_first = readV3F1000(is);
1952 player->eye_offset_third = readV3F1000(is);
1956 infostream<<"Client: Ignoring unknown command "
1957 <<command<<std::endl;
1961 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
1963 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1964 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
1967 void Client::interact(u8 action, const PointedThing& pointed)
1969 if(m_state != LC_Ready){
1970 infostream<<"Client::interact() "
1971 "cancelled (not connected)"
1976 std::ostringstream os(std::ios_base::binary);
1982 [5] u32 length of the next item
1983 [9] serialized PointedThing
1985 0: start digging (from undersurface) or use
1986 1: stop digging (all parameters ignored)
1987 2: digging completed
1988 3: place block or item (to abovesurface)
1991 writeU16(os, TOSERVER_INTERACT);
1992 writeU8(os, action);
1993 writeU16(os, getPlayerItem());
1994 std::ostringstream tmp_os(std::ios::binary);
1995 pointed.serialize(tmp_os);
1996 os<<serializeLongString(tmp_os.str());
1998 std::string s = os.str();
1999 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2002 Send(0, data, true);
2005 void Client::sendNodemetaFields(v3s16 p, const std::string &formname,
2006 const std::map<std::string, std::string> &fields)
2008 std::ostringstream os(std::ios_base::binary);
2010 writeU16(os, TOSERVER_NODEMETA_FIELDS);
2012 os<<serializeString(formname);
2013 size_t fields_size = fields.size();
2014 assert(fields_size <= 0xFFFF);
2015 writeU16(os, (u16) (fields_size & 0xFFFF));
2016 for(std::map<std::string, std::string>::const_iterator
2017 i = fields.begin(); i != fields.end(); i++){
2018 const std::string &name = i->first;
2019 const std::string &value = i->second;
2020 os<<serializeString(name);
2021 os<<serializeLongString(value);
2025 std::string s = os.str();
2026 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2028 Send(0, data, true);
2031 void Client::sendInventoryFields(const std::string &formname,
2032 const std::map<std::string, std::string> &fields)
2034 std::ostringstream os(std::ios_base::binary);
2036 writeU16(os, TOSERVER_INVENTORY_FIELDS);
2037 os<<serializeString(formname);
2038 size_t fields_size = fields.size();
2039 assert(fields_size <= 0xFFFF);
2040 writeU16(os, (u16) (fields_size & 0xFFFF));
2041 for(std::map<std::string, std::string>::const_iterator
2042 i = fields.begin(); i != fields.end(); i++){
2043 const std::string &name = i->first;
2044 const std::string &value = i->second;
2045 os<<serializeString(name);
2046 os<<serializeLongString(value);
2050 std::string s = os.str();
2051 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2053 Send(0, data, true);
2056 void Client::sendInventoryAction(InventoryAction *a)
2058 std::ostringstream os(std::ios_base::binary);
2062 writeU16(buf, TOSERVER_INVENTORY_ACTION);
2063 os.write((char*)buf, 2);
2068 std::string s = os.str();
2069 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2071 Send(0, data, true);
2074 void Client::sendChatMessage(const std::wstring &message)
2076 std::ostringstream os(std::ios_base::binary);
2080 writeU16(buf, TOSERVER_CHAT_MESSAGE);
2081 os.write((char*)buf, 2);
2084 size_t messagesize = message.size();
2085 if (messagesize > 0xFFFF) {
2086 messagesize = 0xFFFF;
2088 writeU16(buf, (u16) messagesize);
2089 os.write((char*)buf, 2);
2092 for(unsigned int i=0; i<message.size(); i++)
2096 os.write((char*)buf, 2);
2100 std::string s = os.str();
2101 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2103 Send(0, data, true);
2106 void Client::sendChangePassword(const std::wstring &oldpassword,
2107 const std::wstring &newpassword)
2109 Player *player = m_env.getLocalPlayer();
2113 std::string playername = player->getName();
2114 std::string oldpwd = translatePassword(playername, oldpassword);
2115 std::string newpwd = translatePassword(playername, newpassword);
2117 std::ostringstream os(std::ios_base::binary);
2118 u8 buf[2+PASSWORD_SIZE*2];
2120 [0] u16 TOSERVER_PASSWORD
2121 [2] u8[28] old password
2122 [30] u8[28] new password
2125 writeU16(buf, TOSERVER_PASSWORD);
2126 for(unsigned int i=0;i<PASSWORD_SIZE-1;i++)
2128 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
2129 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
2131 buf[2+PASSWORD_SIZE-1] = 0;
2132 buf[30+PASSWORD_SIZE-1] = 0;
2133 os.write((char*)buf, 2+PASSWORD_SIZE*2);
2136 std::string s = os.str();
2137 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2139 Send(0, data, true);
2143 void Client::sendDamage(u8 damage)
2145 DSTACK(__FUNCTION_NAME);
2146 std::ostringstream os(std::ios_base::binary);
2148 writeU16(os, TOSERVER_DAMAGE);
2149 writeU8(os, damage);
2152 std::string s = os.str();
2153 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2155 Send(0, data, true);
2158 void Client::sendBreath(u16 breath)
2160 DSTACK(__FUNCTION_NAME);
2161 std::ostringstream os(std::ios_base::binary);
2163 writeU16(os, TOSERVER_BREATH);
2164 writeU16(os, breath);
2166 std::string s = os.str();
2167 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2169 Send(0, data, true);
2172 void Client::sendRespawn()
2174 DSTACK(__FUNCTION_NAME);
2175 std::ostringstream os(std::ios_base::binary);
2177 writeU16(os, TOSERVER_RESPAWN);
2180 std::string s = os.str();
2181 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2183 Send(0, data, true);
2186 void Client::sendReady()
2188 DSTACK(__FUNCTION_NAME);
2189 std::ostringstream os(std::ios_base::binary);
2191 writeU16(os, TOSERVER_CLIENT_READY);
2192 writeU8(os,VERSION_MAJOR);
2193 writeU8(os,VERSION_MINOR);
2194 writeU8(os,VERSION_PATCH_ORIG);
2197 writeU16(os,strlen(CMAKE_VERSION_GITHASH));
2198 os.write(CMAKE_VERSION_GITHASH,strlen(CMAKE_VERSION_GITHASH));
2201 std::string s = os.str();
2202 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2204 Send(0, data, true);
2207 void Client::sendPlayerPos()
2209 LocalPlayer *myplayer = m_env.getLocalPlayer();
2210 if(myplayer == NULL)
2213 // Save bandwidth by only updating position when something changed
2214 if(myplayer->last_position == myplayer->getPosition() &&
2215 myplayer->last_speed == myplayer->getSpeed() &&
2216 myplayer->last_pitch == myplayer->getPitch() &&
2217 myplayer->last_yaw == myplayer->getYaw() &&
2218 myplayer->last_keyPressed == myplayer->keyPressed)
2221 myplayer->last_position = myplayer->getPosition();
2222 myplayer->last_speed = myplayer->getSpeed();
2223 myplayer->last_pitch = myplayer->getPitch();
2224 myplayer->last_yaw = myplayer->getYaw();
2225 myplayer->last_keyPressed = myplayer->keyPressed;
2229 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2230 our_peer_id = m_con.GetPeerID();
2233 // Set peer id if not set already
2234 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2235 myplayer->peer_id = our_peer_id;
2236 // Check that an existing peer_id is the same as the connection's
2237 assert(myplayer->peer_id == our_peer_id);
2239 v3f pf = myplayer->getPosition();
2240 v3f sf = myplayer->getSpeed();
2241 s32 pitch = myplayer->getPitch() * 100;
2242 s32 yaw = myplayer->getYaw() * 100;
2243 u32 keyPressed = myplayer->keyPressed;
2245 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
2246 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
2250 [2] v3s32 position*100
2251 [2+12] v3s32 speed*100
2252 [2+12+12] s32 pitch*100
2253 [2+12+12+4] s32 yaw*100
2254 [2+12+12+4+4] u32 keyPressed
2256 SharedBuffer<u8> data(2+12+12+4+4+4);
2257 writeU16(&data[0], TOSERVER_PLAYERPOS);
2258 writeV3S32(&data[2], position);
2259 writeV3S32(&data[2+12], speed);
2260 writeS32(&data[2+12+12], pitch);
2261 writeS32(&data[2+12+12+4], yaw);
2262 writeU32(&data[2+12+12+4+4], keyPressed);
2263 // Send as unreliable
2264 Send(0, data, false);
2267 void Client::sendPlayerItem(u16 item)
2269 Player *myplayer = m_env.getLocalPlayer();
2270 if(myplayer == NULL)
2273 u16 our_peer_id = m_con.GetPeerID();
2275 // Set peer id if not set already
2276 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2277 myplayer->peer_id = our_peer_id;
2278 // Check that an existing peer_id is the same as the connection's
2279 assert(myplayer->peer_id == our_peer_id);
2281 SharedBuffer<u8> data(2+2);
2282 writeU16(&data[0], TOSERVER_PLAYERITEM);
2283 writeU16(&data[2], item);
2286 Send(0, data, true);
2289 void Client::removeNode(v3s16 p)
2291 std::map<v3s16, MapBlock*> modified_blocks;
2295 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
2297 catch(InvalidPositionException &e)
2301 // add urgent task to update the modified node
2302 addUpdateMeshTaskForNode(p, false, true);
2304 for(std::map<v3s16, MapBlock * >::iterator
2305 i = modified_blocks.begin();
2306 i != modified_blocks.end(); ++i)
2308 addUpdateMeshTaskWithEdge(i->first);
2312 void Client::addNode(v3s16 p, MapNode n, bool remove_metadata)
2314 TimeTaker timer1("Client::addNode()");
2316 std::map<v3s16, MapBlock*> modified_blocks;
2320 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
2321 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
2323 catch(InvalidPositionException &e)
2326 for(std::map<v3s16, MapBlock * >::iterator
2327 i = modified_blocks.begin();
2328 i != modified_blocks.end(); ++i)
2330 addUpdateMeshTaskWithEdge(i->first);
2334 void Client::setPlayerControl(PlayerControl &control)
2336 LocalPlayer *player = m_env.getLocalPlayer();
2337 assert(player != NULL);
2338 player->control = control;
2341 void Client::selectPlayerItem(u16 item)
2343 m_playeritem = item;
2344 m_inventory_updated = true;
2345 sendPlayerItem(item);
2348 // Returns true if the inventory of the local player has been
2349 // updated from the server. If it is true, it is set to false.
2350 bool Client::getLocalInventoryUpdated()
2352 bool updated = m_inventory_updated;
2353 m_inventory_updated = false;
2357 // Copies the inventory of the local player to parameter
2358 void Client::getLocalInventory(Inventory &dst)
2360 Player *player = m_env.getLocalPlayer();
2361 assert(player != NULL);
2362 dst = player->inventory;
2365 Inventory* Client::getInventory(const InventoryLocation &loc)
2368 case InventoryLocation::UNDEFINED:
2371 case InventoryLocation::CURRENT_PLAYER:
2373 Player *player = m_env.getLocalPlayer();
2374 assert(player != NULL);
2375 return &player->inventory;
2378 case InventoryLocation::PLAYER:
2380 Player *player = m_env.getPlayer(loc.name.c_str());
2383 return &player->inventory;
2386 case InventoryLocation::NODEMETA:
2388 NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p);
2391 return meta->getInventory();
2394 case InventoryLocation::DETACHED:
2396 if(m_detached_inventories.count(loc.name) == 0)
2398 return m_detached_inventories[loc.name];
2407 void Client::inventoryAction(InventoryAction *a)
2410 Send it to the server
2412 sendInventoryAction(a);
2415 Predict some local inventory changes
2417 a->clientApply(this, this);
2423 ClientActiveObject * Client::getSelectedActiveObject(
2425 v3f from_pos_f_on_map,
2426 core::line3d<f32> shootline_on_map
2429 std::vector<DistanceSortedActiveObject> objects;
2431 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
2434 // After this, the closest object is the first in the array.
2435 std::sort(objects.begin(), objects.end());
2437 for(unsigned int i=0; i<objects.size(); i++)
2439 ClientActiveObject *obj = objects[i].obj;
2441 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
2442 if(selection_box == NULL)
2445 v3f pos = obj->getPosition();
2447 core::aabbox3d<f32> offsetted_box(
2448 selection_box->MinEdge + pos,
2449 selection_box->MaxEdge + pos
2452 if(offsetted_box.intersectsWithLine(shootline_on_map))
2461 std::list<std::string> Client::getConnectedPlayerNames()
2463 return m_env.getPlayerNames();
2466 float Client::getAnimationTime()
2468 return m_animation_time;
2471 int Client::getCrackLevel()
2473 return m_crack_level;
2476 void Client::setCrack(int level, v3s16 pos)
2478 int old_crack_level = m_crack_level;
2479 v3s16 old_crack_pos = m_crack_pos;
2481 m_crack_level = level;
2484 if(old_crack_level >= 0 && (level < 0 || pos != old_crack_pos))
2487 addUpdateMeshTaskForNode(old_crack_pos, false, true);
2489 if(level >= 0 && (old_crack_level < 0 || pos != old_crack_pos))
2492 addUpdateMeshTaskForNode(pos, false, true);
2498 Player *player = m_env.getLocalPlayer();
2499 assert(player != NULL);
2503 u16 Client::getBreath()
2505 Player *player = m_env.getLocalPlayer();
2506 assert(player != NULL);
2507 return player->getBreath();
2510 bool Client::getChatMessage(std::wstring &message)
2512 if(m_chat_queue.size() == 0)
2514 message = m_chat_queue.pop_front();
2518 void Client::typeChatMessage(const std::wstring &message)
2520 // Discard empty line
2525 sendChatMessage(message);
2528 if (message[0] == L'/')
2530 m_chat_queue.push_back(
2531 (std::wstring)L"issued command: "+message);
2535 LocalPlayer *player = m_env.getLocalPlayer();
2536 assert(player != NULL);
2537 std::wstring name = narrow_to_wide(player->getName());
2538 m_chat_queue.push_back(
2539 (std::wstring)L"<"+name+L"> "+message);
2543 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
2545 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2550 Create a task to update the mesh of the block
2553 MeshMakeData *data = new MeshMakeData(this);
2556 //TimeTaker timer("data fill");
2558 // Debug: 1-6ms, avg=2ms
2560 data->setCrack(m_crack_level, m_crack_pos);
2561 data->setSmoothLighting(g_settings->getBool("smooth_lighting"));
2564 // Add task to queue
2565 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server, urgent);
2568 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent)
2571 v3s16 p = blockpos + v3s16(0,0,0);
2572 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2573 addUpdateMeshTask(p, ack_to_server, urgent);
2575 catch(InvalidPositionException &e){}
2578 for (int i=0;i<6;i++)
2581 v3s16 p = blockpos + g_6dirs[i];
2582 addUpdateMeshTask(p, false, urgent);
2584 catch(InvalidPositionException &e){}
2588 void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool urgent)
2592 infostream<<"Client::addUpdateMeshTaskForNode(): "
2593 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2597 v3s16 blockpos = getNodeBlockPos(nodepos);
2598 v3s16 blockpos_relative = blockpos * MAP_BLOCKSIZE;
2601 v3s16 p = blockpos + v3s16(0,0,0);
2602 addUpdateMeshTask(p, ack_to_server, urgent);
2604 catch(InvalidPositionException &e){}
2607 if(nodepos.X == blockpos_relative.X){
2609 v3s16 p = blockpos + v3s16(-1,0,0);
2610 addUpdateMeshTask(p, false, urgent);
2612 catch(InvalidPositionException &e){}
2615 if(nodepos.Y == blockpos_relative.Y){
2617 v3s16 p = blockpos + v3s16(0,-1,0);
2618 addUpdateMeshTask(p, false, urgent);
2620 catch(InvalidPositionException &e){}
2623 if(nodepos.Z == blockpos_relative.Z){
2625 v3s16 p = blockpos + v3s16(0,0,-1);
2626 addUpdateMeshTask(p, false, urgent);
2628 catch(InvalidPositionException &e){}
2632 ClientEvent Client::getClientEvent()
2634 if(m_client_event_queue.size() == 0)
2637 event.type = CE_NONE;
2640 return m_client_event_queue.pop_front();
2643 float Client::mediaReceiveProgress()
2645 if (m_media_downloader)
2646 return m_media_downloader->getProgress();
2648 return 1.0; // downloader only exists when not yet done
2651 void draw_load_screen(const std::wstring &text,
2652 IrrlichtDevice* device, gui::IGUIFont* font,
2653 float dtime=0 ,int percent=0, bool clouds=true);
2655 void Client::afterContentReceived(IrrlichtDevice *device, gui::IGUIFont* font)
2657 infostream<<"Client::afterContentReceived() started"<<std::endl;
2658 assert(m_itemdef_received);
2659 assert(m_nodedef_received);
2660 assert(mediaReceived());
2662 // Rebuild inherited images and recreate textures
2663 infostream<<"- Rebuilding images and textures"<<std::endl;
2664 m_tsrc->rebuildImagesAndTextures();
2667 infostream<<"- Rebuilding shaders"<<std::endl;
2668 m_shsrc->rebuildShaders();
2670 // Update node aliases
2671 infostream<<"- Updating node aliases"<<std::endl;
2672 m_nodedef->updateAliases(m_itemdef);
2674 // Update node textures
2675 infostream<<"- Updating node textures"<<std::endl;
2676 m_nodedef->updateTextures(m_tsrc);
2678 // Preload item textures and meshes if configured to
2679 if(g_settings->getBool("preload_item_visuals"))
2681 verbosestream<<"Updating item textures and meshes"<<std::endl;
2682 wchar_t* text = wgettext("Item textures...");
2683 draw_load_screen(text,device,font,0,0);
2684 std::set<std::string> names = m_itemdef->getAll();
2685 size_t size = names.size();
2688 for(std::set<std::string>::const_iterator
2689 i = names.begin(); i != names.end(); ++i){
2690 // Asking for these caches the result
2691 m_itemdef->getInventoryTexture(*i, this);
2692 m_itemdef->getWieldMesh(*i, this);
2694 percent = count*100/size;
2695 if (count%50 == 0) // only update every 50 item
2696 draw_load_screen(text,device,font,0,percent);
2701 // Start mesh update thread after setting up content definitions
2702 infostream<<"- Starting mesh update thread"<<std::endl;
2703 m_mesh_update_thread.Start();
2707 infostream<<"Client::afterContentReceived() done"<<std::endl;
2710 float Client::getRTT(void)
2712 return m_con.getPeerStat(PEER_ID_SERVER,con::AVG_RTT);
2715 float Client::getCurRate(void)
2717 return ( m_con.getLocalStat(con::CUR_INC_RATE) +
2718 m_con.getLocalStat(con::CUR_DL_RATE));
2721 float Client::getAvgRate(void)
2723 return ( m_con.getLocalStat(con::AVG_INC_RATE) +
2724 m_con.getLocalStat(con::AVG_DL_RATE));
2727 // IGameDef interface
2729 IItemDefManager* Client::getItemDefManager()
2733 INodeDefManager* Client::getNodeDefManager()
2737 ICraftDefManager* Client::getCraftDefManager()
2740 //return m_craftdef;
2742 ITextureSource* Client::getTextureSource()
2746 IShaderSource* Client::getShaderSource()
2750 u16 Client::allocateUnknownNodeId(const std::string &name)
2752 errorstream<<"Client::allocateUnknownNodeId(): "
2753 <<"Client cannot allocate node IDs"<<std::endl;
2755 return CONTENT_IGNORE;
2757 ISoundManager* Client::getSoundManager()
2761 MtEventManager* Client::getEventManager()
2766 scene::IAnimatedMesh* Client::getMesh(const std::string &filename)
2768 std::map<std::string, std::string>::const_iterator i =
2769 m_mesh_data.find(filename);
2770 if(i == m_mesh_data.end()){
2771 errorstream<<"Client::getMesh(): Mesh not found: \""<<filename<<"\""
2775 const std::string &data = i->second;
2776 scene::ISceneManager *smgr = m_device->getSceneManager();
2778 // Create the mesh, remove it from cache and return it
2779 // This allows unique vertex colors and other properties for each instance
2780 Buffer<char> data_rw(data.c_str(), data.size()); // Const-incorrect Irrlicht
2781 io::IFileSystem *irrfs = m_device->getFileSystem();
2782 io::IReadFile *rfile = irrfs->createMemoryReadFile(
2783 *data_rw, data_rw.getSize(), filename.c_str());
2786 scene::IAnimatedMesh *mesh = smgr->getMesh(rfile);
2788 // NOTE: By playing with Irrlicht refcounts, maybe we could cache a bunch
2789 // of uniquely named instances and re-use them
2791 smgr->getMeshCache()->removeMesh(mesh);