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 <IFileSystem.h>
24 #include "jthread/jmutexautolock.h"
25 #include "util/directiontables.h"
26 #include "util/pointedthing.h"
27 #include "util/serialize.h"
28 #include "util/string.h"
31 #include "network/clientopcodes.h"
35 #include "mapsector.h"
36 #include "mapblock_mesh.h"
42 #include "nodemetadata.h"
47 #include "clientmap.h"
48 #include "clientmedia.h"
50 #include "IMeshCache.h"
51 #include "serialization.h"
54 #include "drawscene.h"
58 #include "database-sqlite3.h"
60 extern gui::IGUIEnvironment* guienv;
66 QueuedMeshUpdate::QueuedMeshUpdate():
69 ack_block_to_server(false)
73 QueuedMeshUpdate::~QueuedMeshUpdate()
83 MeshUpdateQueue::MeshUpdateQueue()
87 MeshUpdateQueue::~MeshUpdateQueue()
89 JMutexAutoLock lock(m_mutex);
91 for(std::vector<QueuedMeshUpdate*>::iterator
93 i != m_queue.end(); i++)
95 QueuedMeshUpdate *q = *i;
101 peer_id=0 adds with nobody to send to
103 void MeshUpdateQueue::addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_server, bool urgent)
105 DSTACK(__FUNCTION_NAME);
109 JMutexAutoLock lock(m_mutex);
115 Find if block is already in queue.
116 If it is, update the data and quit.
118 for(std::vector<QueuedMeshUpdate*>::iterator
120 i != m_queue.end(); i++)
122 QueuedMeshUpdate *q = *i;
128 if(ack_block_to_server)
129 q->ack_block_to_server = true;
137 QueuedMeshUpdate *q = new QueuedMeshUpdate;
140 q->ack_block_to_server = ack_block_to_server;
141 m_queue.push_back(q);
144 // Returned pointer must be deleted
145 // Returns NULL if queue is empty
146 QueuedMeshUpdate * MeshUpdateQueue::pop()
148 JMutexAutoLock lock(m_mutex);
150 bool must_be_urgent = !m_urgents.empty();
151 for(std::vector<QueuedMeshUpdate*>::iterator
153 i != m_queue.end(); i++)
155 QueuedMeshUpdate *q = *i;
156 if(must_be_urgent && m_urgents.count(q->p) == 0)
159 m_urgents.erase(q->p);
169 void * MeshUpdateThread::Thread()
173 log_register_thread("MeshUpdateThread");
175 DSTACK(__FUNCTION_NAME);
177 BEGIN_DEBUG_EXCEPTION_HANDLER
179 porting::setThreadName("MeshUpdateThread");
181 while(!StopRequested())
183 QueuedMeshUpdate *q = m_queue_in.pop();
190 ScopeProfiler sp(g_profiler, "Client: Mesh making");
192 MapBlockMesh *mesh_new = new MapBlockMesh(q->data, m_camera_offset);
193 if(mesh_new->getMesh()->getMeshBufferCount() == 0)
202 r.ack_block_to_server = q->ack_block_to_server;
204 m_queue_out.push_back(r);
209 END_DEBUG_EXCEPTION_HANDLER(errorstream)
219 IrrlichtDevice *device,
220 const char *playername,
221 std::string password,
222 MapDrawControl &control,
223 IWritableTextureSource *tsrc,
224 IWritableShaderSource *shsrc,
225 IWritableItemDefManager *itemdef,
226 IWritableNodeDefManager *nodedef,
227 ISoundManager *sound,
228 MtEventManager *event,
231 m_packetcounter_timer(0.0),
232 m_connection_reinit_timer(0.1),
233 m_avg_rtt_timer(0.0),
234 m_playerpos_send_timer(0.0),
235 m_ignore_damage_timer(0.0),
242 m_mesh_update_thread(this),
244 new ClientMap(this, this, control,
245 device->getSceneManager()->getRootSceneNode(),
246 device->getSceneManager(), 666),
247 device->getSceneManager(),
250 m_particle_manager(&m_env),
251 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, ipv6, this),
253 m_server_ser_ver(SER_FMT_VER_INVALID),
255 m_inventory_updated(false),
256 m_inventory_from_server(NULL),
257 m_inventory_from_server_age(0.0),
258 m_show_highlighted(false),
262 m_highlighted_pos(0,0,0),
264 m_password(password),
265 m_access_denied(false),
266 m_itemdef_received(false),
267 m_nodedef_received(false),
268 m_media_downloader(new ClientMediaDownloader()),
269 m_time_of_day_set(false),
270 m_last_time_of_day_f(-1),
271 m_time_of_day_update_timer(0),
272 m_recommended_send_interval(0.1),
273 m_removed_sounds_check_timer(0),
280 Player *player = new LocalPlayer(this, playername);
282 m_env.addPlayer(player);
285 m_cache_smooth_lighting = g_settings->getBool("smooth_lighting");
290 //request all client managed threads to stop
291 m_mesh_update_thread.Stop();
292 if (localdb != NULL) {
293 actionstream << "Local map saving ended" << std::endl;
299 bool Client::isShutdown()
302 if (!m_mesh_update_thread.IsRunning()) return true;
311 m_mesh_update_thread.Stop();
312 m_mesh_update_thread.Wait();
313 while(!m_mesh_update_thread.m_queue_out.empty()) {
314 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_frontNoEx();
319 delete m_inventory_from_server;
321 // Delete detached inventories
322 for(std::map<std::string, Inventory*>::iterator
323 i = m_detached_inventories.begin();
324 i != m_detached_inventories.end(); i++){
328 // cleanup 3d model meshes on client shutdown
329 while (m_device->getSceneManager()->getMeshCache()->getMeshCount() != 0) {
330 scene::IAnimatedMesh * mesh =
331 m_device->getSceneManager()->getMeshCache()->getMeshByIndex(0);
334 m_device->getSceneManager()->getMeshCache()->removeMesh(mesh);
338 void Client::connect(Address address,
339 const std::string &address_name,
340 bool is_local_server)
342 DSTACK(__FUNCTION_NAME);
344 initLocalMapSaving(address, address_name, is_local_server);
346 m_con.SetTimeoutMs(0);
347 m_con.Connect(address);
350 void Client::step(float dtime)
352 DSTACK(__FUNCTION_NAME);
358 if(m_ignore_damage_timer > dtime)
359 m_ignore_damage_timer -= dtime;
361 m_ignore_damage_timer = 0.0;
363 m_animation_time += dtime;
364 if(m_animation_time > 60.0)
365 m_animation_time -= 60.0;
367 m_time_of_day_update_timer += dtime;
375 float &counter = m_packetcounter_timer;
381 infostream << "Client packetcounter (" << m_packetcounter_timer
383 m_packetcounter.print(infostream);
384 m_packetcounter.clear();
391 Delete unused sectors
393 NOTE: This jams the game for a while because deleting sectors
397 float &counter = m_delete_unused_sectors_timer;
405 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
407 core::list<v3s16> deleted_blocks;
409 float delete_unused_sectors_timeout =
410 g_settings->getFloat("client_delete_unused_sectors_timeout");
412 // Delete sector blocks
413 /*u32 num = m_env.getMap().unloadUnusedData
414 (delete_unused_sectors_timeout,
415 true, &deleted_blocks);*/
417 // Delete whole sectors
418 m_env.getMap().unloadUnusedData
419 (delete_unused_sectors_timeout,
422 if(deleted_blocks.size() > 0)
424 /*infostream<<"Client: Deleted blocks of "<<num
425 <<" unused sectors"<<std::endl;*/
426 /*infostream<<"Client: Deleted "<<num
427 <<" unused sectors"<<std::endl;*/
433 // Env is locked so con can be locked.
434 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
436 core::list<v3s16>::Iterator i = deleted_blocks.begin();
437 core::list<v3s16> sendlist;
440 if(sendlist.size() == 255 || i == deleted_blocks.end())
442 if(sendlist.size() == 0)
451 u32 replysize = 2+1+6*sendlist.size();
452 SharedBuffer<u8> reply(replysize);
453 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
454 reply[2] = sendlist.size();
456 for(core::list<v3s16>::Iterator
457 j = sendlist.begin();
458 j != sendlist.end(); j++)
460 writeV3S16(&reply[2+1+6*k], *j);
463 m_con.Send(PEER_ID_SERVER, 1, reply, true);
465 if(i == deleted_blocks.end())
471 sendlist.push_back(*i);
478 // UGLY hack to fix 2 second startup delay caused by non existent
479 // server client startup synchronization in local server or singleplayer mode
480 static bool initial_step = true;
482 initial_step = false;
484 else if(m_state == LC_Created)
486 float &counter = m_connection_reinit_timer;
492 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
494 Player *myplayer = m_env.getLocalPlayer();
495 assert(myplayer != NULL);
496 // Send TOSERVER_INIT
497 // [0] u16 TOSERVER_INIT
498 // [2] u8 SER_FMT_VER_HIGHEST_READ
499 // [3] u8[20] player_name
500 // [23] u8[28] password (new in some version)
501 // [51] u16 minimum supported network protocol version (added sometime)
502 // [53] u16 maximum supported network protocol version (added later than the previous one)
503 SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2+2);
504 writeU16(&data[0], TOSERVER_INIT);
505 writeU8(&data[2], SER_FMT_VER_HIGHEST_READ);
507 memset((char*)&data[3], 0, PLAYERNAME_SIZE);
508 snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
510 /*infostream<<"Client: sending initial password hash: \""<<m_password<<"\""
513 memset((char*)&data[23], 0, PASSWORD_SIZE);
514 snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
516 writeU16(&data[51], CLIENT_PROTOCOL_VERSION_MIN);
517 writeU16(&data[53], CLIENT_PROTOCOL_VERSION_MAX);
519 // Send as unreliable
520 Send(1, data, false);
523 // Not connected, return
528 Do stuff if connected
532 Run Map's timers and unload unused data
534 const float map_timer_and_unload_dtime = 5.25;
535 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
537 ScopeProfiler sp(g_profiler, "Client: map timer and unload");
538 std::list<v3s16> deleted_blocks;
539 m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
540 g_settings->getFloat("client_unload_unused_data_timeout"),
543 /*if(deleted_blocks.size() > 0)
544 infostream<<"Client: Unloaded "<<deleted_blocks.size()
545 <<" unused blocks"<<std::endl;*/
549 NOTE: This loop is intentionally iterated the way it is.
552 std::list<v3s16>::iterator i = deleted_blocks.begin();
553 std::list<v3s16> sendlist;
556 if(sendlist.size() == 255 || i == deleted_blocks.end())
567 u32 replysize = 2+1+6*sendlist.size();
568 SharedBuffer<u8> reply(replysize);
569 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
570 reply[2] = sendlist.size();
572 for(std::list<v3s16>::iterator
573 j = sendlist.begin();
574 j != sendlist.end(); ++j)
576 writeV3S16(&reply[2+1+6*k], *j);
579 m_con.Send(PEER_ID_SERVER, 2, reply, true);
581 if(i == deleted_blocks.end())
587 sendlist.push_back(*i);
596 // Control local player (0ms)
597 LocalPlayer *player = m_env.getLocalPlayer();
598 assert(player != NULL);
599 player->applyControl(dtime);
609 ClientEnvEvent event = m_env.getClientEvent();
610 if(event.type == CEE_NONE)
614 else if(event.type == CEE_PLAYER_DAMAGE)
616 if(m_ignore_damage_timer <= 0)
618 u8 damage = event.player_damage.amount;
620 if(event.player_damage.send_to_server)
623 // Add to ClientEvent queue
625 event.type = CE_PLAYER_DAMAGE;
626 event.player_damage.amount = damage;
627 m_client_event_queue.push_back(event);
630 else if(event.type == CEE_PLAYER_BREATH)
632 u16 breath = event.player_breath.amount;
642 float &counter = m_avg_rtt_timer;
647 // connectedAndInitialized() is true, peer exists.
648 float avg_rtt = getRTT();
649 infostream<<"Client: avg_rtt="<<avg_rtt<<std::endl;
654 Send player position to server
657 float &counter = m_playerpos_send_timer;
659 if((m_state == LC_Ready) && (counter >= m_recommended_send_interval))
667 Replace updated meshes
670 int num_processed_meshes = 0;
671 while(!m_mesh_update_thread.m_queue_out.empty())
673 num_processed_meshes++;
674 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_frontNoEx();
675 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
678 // Delete the old mesh
679 if(block->mesh != NULL)
681 // TODO: Remove hardware buffers of meshbuffers of block->mesh
686 // Replace with the new mesh
687 block->mesh = r.mesh;
691 if(r.ack_block_to_server)
703 u32 replysize = 2+1+6;
704 SharedBuffer<u8> reply(replysize);
705 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
707 writeV3S16(&reply[3], r.p);
709 m_con.Send(PEER_ID_SERVER, 2, reply, true);
712 if(num_processed_meshes > 0)
713 g_profiler->graphAdd("num_processed_meshes", num_processed_meshes);
719 if (m_media_downloader && m_media_downloader->isStarted()) {
720 m_media_downloader->step(this);
721 if (m_media_downloader->isDone()) {
723 delete m_media_downloader;
724 m_media_downloader = NULL;
729 If the server didn't update the inventory in a while, revert
730 the local inventory (so the player notices the lag problem
731 and knows something is wrong).
733 if(m_inventory_from_server)
735 float interval = 10.0;
736 float count_before = floor(m_inventory_from_server_age / interval);
738 m_inventory_from_server_age += dtime;
740 float count_after = floor(m_inventory_from_server_age / interval);
742 if(count_after != count_before)
744 // Do this every <interval> seconds after TOCLIENT_INVENTORY
745 // Reset the locally changed inventory to the authoritative inventory
746 Player *player = m_env.getLocalPlayer();
747 player->inventory = *m_inventory_from_server;
748 m_inventory_updated = true;
753 Update positions of sounds attached to objects
756 for(std::map<int, u16>::iterator
757 i = m_sounds_to_objects.begin();
758 i != m_sounds_to_objects.end(); i++)
760 int client_id = i->first;
761 u16 object_id = i->second;
762 ClientActiveObject *cao = m_env.getActiveObject(object_id);
765 v3f pos = cao->getPosition();
766 m_sound->updateSoundPosition(client_id, pos);
771 Handle removed remotely initiated sounds
773 m_removed_sounds_check_timer += dtime;
774 if(m_removed_sounds_check_timer >= 2.32)
776 m_removed_sounds_check_timer = 0;
777 // Find removed sounds and clear references to them
778 std::set<s32> removed_server_ids;
779 for(std::map<s32, int>::iterator
780 i = m_sounds_server_to_client.begin();
781 i != m_sounds_server_to_client.end();)
783 s32 server_id = i->first;
784 int client_id = i->second;
786 if(!m_sound->soundExists(client_id)){
787 m_sounds_server_to_client.erase(server_id);
788 m_sounds_client_to_server.erase(client_id);
789 m_sounds_to_objects.erase(client_id);
790 removed_server_ids.insert(server_id);
794 if(!removed_server_ids.empty())
796 std::ostringstream os(std::ios_base::binary);
797 writeU16(os, TOSERVER_REMOVED_SOUNDS);
798 size_t server_ids = removed_server_ids.size();
799 assert(server_ids <= 0xFFFF);
800 writeU16(os, (u16) (server_ids & 0xFFFF));
801 for(std::set<s32>::iterator i = removed_server_ids.begin();
802 i != removed_server_ids.end(); i++)
804 std::string s = os.str();
805 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
812 bool Client::loadMedia(const std::string &data, const std::string &filename)
814 // Silly irrlicht's const-incorrectness
815 Buffer<char> data_rw(data.c_str(), data.size());
819 const char *image_ext[] = {
820 ".png", ".jpg", ".bmp", ".tga",
821 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
824 name = removeStringEnd(filename, image_ext);
827 verbosestream<<"Client: Attempting to load image "
828 <<"file \""<<filename<<"\""<<std::endl;
830 io::IFileSystem *irrfs = m_device->getFileSystem();
831 video::IVideoDriver *vdrv = m_device->getVideoDriver();
833 // Create an irrlicht memory file
834 io::IReadFile *rfile = irrfs->createMemoryReadFile(
835 *data_rw, data_rw.getSize(), "_tempreadfile");
838 video::IImage *img = vdrv->createImageFromFile(rfile);
840 errorstream<<"Client: Cannot create image from data of "
841 <<"file \""<<filename<<"\""<<std::endl;
846 m_tsrc->insertSourceImage(filename, img);
853 const char *sound_ext[] = {
854 ".0.ogg", ".1.ogg", ".2.ogg", ".3.ogg", ".4.ogg",
855 ".5.ogg", ".6.ogg", ".7.ogg", ".8.ogg", ".9.ogg",
858 name = removeStringEnd(filename, sound_ext);
861 verbosestream<<"Client: Attempting to load sound "
862 <<"file \""<<filename<<"\""<<std::endl;
863 m_sound->loadSoundData(name, data);
867 const char *model_ext[] = {
868 ".x", ".b3d", ".md2", ".obj",
871 name = removeStringEnd(filename, model_ext);
874 verbosestream<<"Client: Storing model into memory: "
875 <<"\""<<filename<<"\""<<std::endl;
876 if(m_mesh_data.count(filename))
877 errorstream<<"Multiple models with name \""<<filename.c_str()
878 <<"\" found; replacing previous model"<<std::endl;
879 m_mesh_data[filename] = data;
883 errorstream<<"Client: Don't know how to load file \""
884 <<filename<<"\""<<std::endl;
888 // Virtual methods from con::PeerHandler
889 void Client::peerAdded(con::Peer *peer)
891 infostream<<"Client::peerAdded(): peer->id="
892 <<peer->id<<std::endl;
894 void Client::deletingPeer(con::Peer *peer, bool timeout)
896 infostream<<"Client::deletingPeer(): "
897 "Server Peer is getting deleted "
898 <<"(timeout="<<timeout<<")"<<std::endl;
903 u16 number of files requested
909 void Client::request_media(const std::list<std::string> &file_requests)
911 std::ostringstream os(std::ios_base::binary);
912 writeU16(os, TOSERVER_REQUEST_MEDIA);
913 size_t file_requests_size = file_requests.size();
914 assert(file_requests_size <= 0xFFFF);
915 writeU16(os, (u16) (file_requests_size & 0xFFFF));
917 for(std::list<std::string>::const_iterator i = file_requests.begin();
918 i != file_requests.end(); ++i) {
919 os<<serializeString(*i);
923 std::string s = os.str();
924 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
927 infostream<<"Client: Sending media request list to server ("
928 <<file_requests.size()<<" files)"<<std::endl;
931 void Client::received_media()
933 // notify server we received everything
934 std::ostringstream os(std::ios_base::binary);
935 writeU16(os, TOSERVER_RECEIVED_MEDIA);
936 std::string s = os.str();
937 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
940 infostream<<"Client: Notifying server that we received all media"
944 void Client::initLocalMapSaving(const Address &address,
945 const std::string &hostname,
946 bool is_local_server)
950 if (!g_settings->getBool("enable_local_map_saving") || is_local_server)
953 const std::string world_path = porting::path_user
954 + DIR_DELIM + "worlds"
955 + DIR_DELIM + "server_"
956 + hostname + "_" + to_string(address.getPort());
958 SubgameSpec gamespec;
960 if (!getWorldExists(world_path)) {
961 gamespec = findSubgame(g_settings->get("default_game"));
962 if (!gamespec.isValid())
963 gamespec = findSubgame("minimal");
965 gamespec = findWorldSubgame(world_path);
968 if (!gamespec.isValid()) {
969 errorstream << "Couldn't find subgame for local map saving." << std::endl;
973 localserver = new Server(world_path, gamespec, false, false);
974 localdb = new Database_SQLite3(&(ServerMap&)localserver->getMap(), world_path);
975 localdb->beginSave();
976 actionstream << "Local map saving started, map will be saved at '" << world_path << "'" << std::endl;
979 void Client::ReceiveAll()
981 DSTACK(__FUNCTION_NAME);
982 u32 start_ms = porting::getTimeMs();
985 // Limit time even if there would be huge amounts of data to
987 if(porting::getTimeMs() > start_ms + 100)
992 g_profiler->graphAdd("client_received_packets", 1);
994 catch(con::NoIncomingDataException &e)
998 catch(con::InvalidIncomingDataException &e)
1000 infostream<<"Client::ReceiveAll(): "
1001 "InvalidIncomingDataException: what()="
1002 <<e.what()<<std::endl;
1007 void Client::Receive()
1009 DSTACK(__FUNCTION_NAME);
1010 SharedBuffer<u8> data;
1012 u32 datasize = m_con.Receive(sender_peer_id, data);
1013 ProcessData(*data, datasize, sender_peer_id);
1016 void Client::handleCommand_Deprecated(ToClientPacket* pkt)
1018 infostream << "Got deprecated command "
1019 << toClientCommandTable[pkt->getCommand()].name << " from peer "
1020 << pkt->getPeerId() << "!" << std::endl;
1023 void Client::handleCommand_Init(ToClientPacket* pkt)
1025 if(pkt->getSize() < 1)
1031 infostream << "Client: TOCLIENT_INIT received with "
1032 "deployed=" << ((int)deployed & 0xff) << std::endl;
1034 if(!ser_ver_supported(deployed)) {
1035 infostream << "Client: TOCLIENT_INIT: Server sent "
1036 << "unsupported ser_fmt_ver"<< std::endl;
1040 m_server_ser_ver = deployed;
1042 // Get player position
1043 v3s16 playerpos_s16(0, BS * 2 + BS * 20, 0);
1044 if(pkt->getSize() >= 1 + 6) {
1045 *pkt >> playerpos_s16;
1047 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS / 2, 0);
1050 // Set player position
1051 Player *player = m_env.getLocalPlayer();
1052 assert(player != NULL);
1053 player->setPosition(playerpos_f);
1055 if(pkt->getSize() >= 1 + 6 + 8) {
1058 infostream << "Client: received map seed: " << m_map_seed << std::endl;
1061 if(pkt->getSize() >= 1 + 6 + 8 + 4) {
1062 *pkt >> m_recommended_send_interval;
1063 infostream << "Client: received recommended send interval "
1064 << m_recommended_send_interval<<std::endl;
1069 SharedBuffer<u8> reply(replysize);
1070 writeU16(&reply[0], TOSERVER_INIT2);
1072 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1077 void Client::handleCommand_AccessDenied(ToClientPacket* pkt)
1079 // The server didn't like our password. Note, this needs
1080 // to be processed even if the serialisation format has
1081 // not been agreed yet, the same as TOCLIENT_INIT.
1082 m_access_denied = true;
1083 m_access_denied_reason = L"Unknown";
1084 if(pkt->getSize() >= 2) {
1085 *pkt >> m_access_denied_reason;
1089 void Client::handleCommand_RemoveNode(ToClientPacket* pkt)
1091 if(pkt->getSize() < 6)
1101 void Client::handleCommand_AddNode(ToClientPacket* pkt)
1103 if(pkt->getSize() < 6 + MapNode::serializedLength(m_server_ser_ver))
1112 n.deSerialize(pkt->getU8Ptr(6), m_server_ser_ver);
1114 bool remove_metadata = true;
1115 u32 index = 6 + MapNode::serializedLength(m_server_ser_ver);
1116 if ((pkt->getSize() >= index + 1) && pkt->getU8(index)) {
1117 remove_metadata = false;
1120 addNode(p, n, remove_metadata);
1122 void Client::handleCommand_BlockData(ToClientPacket* pkt)
1124 // Ignore too small packet
1125 if(pkt->getSize() < 6)
1133 std::string datastring(pkt->getString(6), pkt->getSize() - 6);
1134 std::istringstream istr(datastring, std::ios_base::binary);
1139 v2s16 p2d(p.X, p.Z);
1140 sector = m_env.getMap().emergeSector(p2d);
1142 assert(sector->getPos() == p2d);
1144 block = sector->getBlockNoCreateNoEx(p.Y);
1147 Update an existing block
1149 block->deSerialize(istr, m_server_ser_ver, false);
1150 block->deSerializeNetworkSpecific(istr);
1156 block = new MapBlock(&m_env.getMap(), p, this);
1157 block->deSerialize(istr, m_server_ser_ver, false);
1158 block->deSerializeNetworkSpecific(istr);
1159 sector->insertBlock(block);
1162 if (localdb != NULL) {
1163 ((ServerMap&) localserver->getMap()).saveBlock(block, localdb);
1167 Add it to mesh update queue and set it to be acknowledged after update.
1169 addUpdateMeshTaskWithEdge(p, true);
1172 void Client::handleCommand_Inventory(ToClientPacket* pkt)
1174 if(pkt->getSize() < 1)
1177 std::string datastring(pkt->getString(0), pkt->getSize());
1178 std::istringstream is(datastring, std::ios_base::binary);
1180 Player *player = m_env.getLocalPlayer();
1181 assert(player != NULL);
1183 player->inventory.deSerialize(is);
1185 m_inventory_updated = true;
1187 delete m_inventory_from_server;
1188 m_inventory_from_server = new Inventory(player->inventory);
1189 m_inventory_from_server_age = 0.0;
1192 void Client::handleCommand_TimeOfDay(ToClientPacket* pkt)
1194 if(pkt->getSize() < 2)
1199 *pkt >> time_of_day;
1201 time_of_day = time_of_day % 24000;
1202 float time_speed = 0;
1204 if(pkt->getSize() >= 2 + 4) {
1208 // Old message; try to approximate speed of time by ourselves
1209 float time_of_day_f = (float)time_of_day / 24000.0;
1210 float tod_diff_f = 0;
1212 if(time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
1213 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0;
1215 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
1217 m_last_time_of_day_f = time_of_day_f;
1218 float time_diff = m_time_of_day_update_timer;
1219 m_time_of_day_update_timer = 0;
1221 if(m_time_of_day_set){
1222 time_speed = (3600.0 * 24.0) * tod_diff_f / time_diff;
1223 infostream << "Client: Measured time_of_day speed (old format): "
1224 << time_speed << " tod_diff_f=" << tod_diff_f
1225 << " time_diff=" << time_diff << std::endl;
1229 // Update environment
1230 m_env.setTimeOfDay(time_of_day);
1231 m_env.setTimeOfDaySpeed(time_speed);
1232 m_time_of_day_set = true;
1234 u32 dr = m_env.getDayNightRatio();
1235 infostream << "Client: time_of_day=" << time_of_day
1236 << " time_speed=" << time_speed
1237 << " dr=" << dr << std::endl;
1240 void Client::handleCommand_ChatMessage(ToClientPacket* pkt)
1247 u16 len, read_wchar;
1251 std::wstring message;
1252 for(unsigned int i=0; i<len; i++) {
1254 message += (wchar_t)read_wchar;
1257 m_chat_queue.push_back(message);
1260 void Client::handleCommand_ActiveObjectRemoveAdd(ToClientPacket* pkt)
1264 u16 count of removed objects
1265 for all removed objects {
1268 u16 count of added objects
1269 for all added objects {
1272 u32 initialization data length
1273 string initialization data
1277 // Read removed objects
1279 u16 removed_count, added_count, id;
1281 *pkt >> removed_count;
1283 for(u16 i=0; i<removed_count; i++) {
1285 m_env.removeActiveObject(id);
1288 // Read added objects
1289 *pkt >> added_count;
1291 for(u16 i=0; i<added_count; i++) {
1293 m_env.addActiveObject(id, type, pkt->readLongString());
1297 void Client::handleCommand_ActiveObjectMessages(ToClientPacket* pkt)
1309 // Get all data except the command number
1310 std::string datastring(pkt->getString(0), pkt->getSize());
1311 // Throw them in an istringstream
1312 std::istringstream is(datastring, std::ios_base::binary);
1314 while(is.eof() == false) {
1316 u16 id = readU16((u8*)buf);
1320 size_t message_size = readU16((u8*)buf);
1321 std::string message;
1322 message.reserve(message_size);
1323 for(unsigned int i=0; i<message_size; i++)
1326 message.append(buf, 1);
1328 // Pass on to the environment
1329 m_env.processActiveObjectMessage(id, message);
1333 void Client::handleCommand_Movement(ToClientPacket* pkt)
1335 Player *player = m_env.getLocalPlayer();
1336 assert(player != NULL);
1338 float mad, maa, maf, msw, mscr, msf, mscl, msj, lf, lfs, ls, g;
1340 *pkt >> mad >> maa >> maf >> msw >> mscr >> msf >> mscl >> msj
1341 >> lf >> lfs >> ls >> g;
1343 player->movement_acceleration_default = mad * BS;
1344 player->movement_acceleration_air = maa * BS;
1345 player->movement_acceleration_fast = maf * BS;
1346 player->movement_speed_walk = msw * BS;
1347 player->movement_speed_crouch = mscr * BS;
1348 player->movement_speed_fast = msf * BS;
1349 player->movement_speed_climb = mscl * BS;
1350 player->movement_speed_jump = msj * BS;
1351 player->movement_liquid_fluidity = lf * BS;
1352 player->movement_liquid_fluidity_smooth = lfs * BS;
1353 player->movement_liquid_sink = ls * BS;
1354 player->movement_gravity = g * BS;
1357 void Client::handleCommand_HP(ToClientPacket* pkt)
1360 Player *player = m_env.getLocalPlayer();
1361 assert(player != NULL);
1363 u8 oldhp = player->hp;
1371 // Add to ClientEvent queue
1373 event.type = CE_PLAYER_DAMAGE;
1374 event.player_damage.amount = oldhp - hp;
1375 m_client_event_queue.push_back(event);
1379 void Client::handleCommand_Breath(ToClientPacket* pkt)
1381 Player *player = m_env.getLocalPlayer();
1382 assert(player != NULL);
1388 player->setBreath(breath);
1391 void Client::handleCommand_MovePlayer(ToClientPacket* pkt)
1393 Player *player = m_env.getLocalPlayer();
1394 assert(player != NULL);
1399 *pkt >> pos >> pitch >> yaw;
1401 player->setPosition(pos);
1403 infostream << "Client got TOCLIENT_MOVE_PLAYER"
1404 << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
1405 << " pitch=" << pitch
1410 Add to ClientEvent queue.
1411 This has to be sent to the main program because otherwise
1412 it would just force the pitch and yaw values to whatever
1413 the camera points to.
1416 event.type = CE_PLAYER_FORCE_MOVE;
1417 event.player_force_move.pitch = pitch;
1418 event.player_force_move.yaw = yaw;
1419 m_client_event_queue.push_back(event);
1421 // Ignore damage for a few seconds, so that the player doesn't
1422 // get damage from falling on ground
1423 m_ignore_damage_timer = 3.0;
1426 void Client::handleCommand_PlayerItem(ToClientPacket* pkt)
1428 infostream << "Client: WARNING: Ignoring TOCLIENT_PLAYERITEM" << std::endl;
1431 void Client::handleCommand_DeathScreen(ToClientPacket* pkt)
1433 bool set_camera_point_target;
1434 v3f camera_point_target;
1436 *pkt >> set_camera_point_target;
1437 *pkt >> camera_point_target;
1440 event.type = CE_DEATHSCREEN;
1441 event.deathscreen.set_camera_point_target = set_camera_point_target;
1442 event.deathscreen.camera_point_target_x = camera_point_target.X;
1443 event.deathscreen.camera_point_target_y = camera_point_target.Y;
1444 event.deathscreen.camera_point_target_z = camera_point_target.Z;
1445 m_client_event_queue.push_back(event);
1448 void Client::handleCommand_AnnounceMedia(ToClientPacket* pkt)
1454 infostream << "Client: Received media announcement: packet size: "
1455 << pkt->getSize() << std::endl;
1457 if (m_media_downloader == NULL ||
1458 m_media_downloader->isStarted()) {
1459 const char *problem = m_media_downloader ?
1460 "we already saw another announcement" :
1461 "all media has been received already";
1462 errorstream << "Client: Received media announcement but "
1464 << " files=" << num_files
1465 << " size=" << pkt->getSize() << std::endl;
1469 // Mesh update thread must be stopped while
1470 // updating content definitions
1471 assert(!m_mesh_update_thread.IsRunning());
1473 for(int i=0; i<num_files; i++) {
1474 std::string name, sha1_base64;
1476 *pkt >> name >> sha1_base64;
1478 std::string sha1_raw = base64_decode(sha1_base64);
1479 m_media_downloader->addFile(name, sha1_raw);
1482 std::vector<std::string> remote_media;
1489 while(!sf.atend()) {
1490 std::string baseurl = trim(sf.next(","));
1492 m_media_downloader->addRemoteServer(baseurl);
1495 catch(SerializationError& e) {
1496 // not supported by server or turned off
1499 m_media_downloader->step(this);
1502 void Client::handleCommand_Media(ToClientPacket* pkt)
1506 u16 total number of file bunches
1507 u16 index of this bunch
1508 u32 number of files in this bunch
1520 *pkt >> num_bunches >> bunch_i >> num_files;
1522 infostream << "Client: Received files: bunch " << bunch_i << "/"
1523 << num_bunches << " files=" << num_files
1524 << " size=" << pkt->getSize() << std::endl;
1529 if (m_media_downloader == NULL ||
1530 !m_media_downloader->isStarted()) {
1531 const char *problem = m_media_downloader ?
1532 "media has not been requested" :
1533 "all media has been received already";
1534 errorstream << "Client: Received media but "
1536 << " bunch " << bunch_i << "/" << num_bunches
1537 << " files=" << num_files
1538 << " size=" << pkt->getSize() << std::endl;
1542 // Mesh update thread must be stopped while
1543 // updating content definitions
1544 assert(!m_mesh_update_thread.IsRunning());
1546 for(unsigned int i=0; i<num_files; i++) {
1551 std::string data = pkt->readLongString();
1553 m_media_downloader->conventionalTransferDone(
1558 void Client::handleCommand_ToolDef(ToClientPacket* pkt)
1560 infostream << "Client: WARNING: Ignoring TOCLIENT_TOOLDEF" << std::endl;
1563 void Client::handleCommand_NodeDef(ToClientPacket* pkt)
1565 infostream << "Client: Received node definitions: packet size: "
1566 << pkt->getSize() << std::endl;
1568 // Mesh update thread must be stopped while
1569 // updating content definitions
1570 assert(!m_mesh_update_thread.IsRunning());
1572 // Decompress node definitions
1573 std::string datastring(pkt->getString(0), pkt->getSize());
1574 std::istringstream is(datastring, std::ios_base::binary);
1575 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1576 std::ostringstream tmp_os;
1577 decompressZlib(tmp_is, tmp_os);
1579 // Deserialize node definitions
1580 std::istringstream tmp_is2(tmp_os.str());
1581 m_nodedef->deSerialize(tmp_is2);
1582 m_nodedef_received = true;
1585 void Client::handleCommand_CraftItemDef(ToClientPacket* pkt)
1587 infostream << "Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF" << std::endl;
1590 void Client::handleCommand_ItemDef(ToClientPacket* pkt)
1592 infostream << "Client: Received item definitions: packet size: "
1593 << pkt->getSize() << std::endl;
1595 // Mesh update thread must be stopped while
1596 // updating content definitions
1597 assert(!m_mesh_update_thread.IsRunning());
1599 // Decompress item definitions
1600 std::string datastring(pkt->getString(0), pkt->getSize());
1601 std::istringstream is(datastring, std::ios_base::binary);
1602 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1603 std::ostringstream tmp_os;
1604 decompressZlib(tmp_is, tmp_os);
1606 // Deserialize node definitions
1607 std::istringstream tmp_is2(tmp_os.str());
1608 m_itemdef->deSerialize(tmp_is2);
1609 m_itemdef_received = true;
1612 void Client::handleCommand_PlaySound(ToClientPacket* pkt)
1617 u8 type; // 0=local, 1=positional, 2=object
1622 *pkt >> server_id >> name >> gain >> type >> pos >> object_id >> loop;
1628 client_id = m_sound->playSound(name, loop, gain);
1630 case 1: // positional
1631 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1635 ClientActiveObject *cao = m_env.getActiveObject(object_id);
1637 pos = cao->getPosition();
1638 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1639 // TODO: Set up sound to move with object
1646 if(client_id != -1) {
1647 m_sounds_server_to_client[server_id] = client_id;
1648 m_sounds_client_to_server[client_id] = server_id;
1650 m_sounds_to_objects[client_id] = object_id;
1654 void Client::handleCommand_StopSound(ToClientPacket* pkt)
1660 std::map<s32, int>::iterator i =
1661 m_sounds_server_to_client.find(server_id);
1663 if(i != m_sounds_server_to_client.end()) {
1664 int client_id = i->second;
1665 m_sound->stopSound(client_id);
1669 void Client::handleCommand_Privileges(ToClientPacket* pkt)
1671 m_privileges.clear();
1672 infostream << "Client: Privileges updated: ";
1675 *pkt >> num_privileges;
1677 for(unsigned int i=0; i<num_privileges; i++) {
1682 m_privileges.insert(priv);
1683 infostream << priv << " ";
1685 infostream << std::endl;
1688 void Client::handleCommand_InventoryFormSpec(ToClientPacket* pkt)
1690 Player *player = m_env.getLocalPlayer();
1691 assert(player != NULL);
1693 // Store formspec in LocalPlayer
1694 player->inventory_formspec = pkt->readLongString();
1697 void Client::handleCommand_DetachedInventory(ToClientPacket* pkt)
1699 std::string datastring(pkt->getString(0), pkt->getSize());
1700 std::istringstream is(datastring, std::ios_base::binary);
1702 std::string name = deSerializeString(is);
1704 infostream << "Client: Detached inventory update: \"" << name
1705 << "\"" << std::endl;
1707 Inventory *inv = NULL;
1708 if(m_detached_inventories.count(name) > 0)
1709 inv = m_detached_inventories[name];
1711 inv = new Inventory(m_itemdef);
1712 m_detached_inventories[name] = inv;
1714 inv->deSerialize(is);
1717 void Client::handleCommand_ShowFormSpec(ToClientPacket* pkt)
1719 std::string formspec = pkt->readLongString();
1720 std::string formname;
1725 event.type = CE_SHOW_FORMSPEC;
1726 // pointer is required as event is a struct only!
1727 // adding a std:string to a struct isn't possible
1728 event.show_formspec.formspec = new std::string(formspec);
1729 event.show_formspec.formname = new std::string(formname);
1730 m_client_event_queue.push_back(event);
1733 void Client::handleCommand_SpawnParticle(ToClientPacket* pkt)
1735 std::string datastring(pkt->getString(0), pkt->getSize());
1736 std::istringstream is(datastring, std::ios_base::binary);
1738 v3f pos = readV3F1000(is);
1739 v3f vel = readV3F1000(is);
1740 v3f acc = readV3F1000(is);
1741 float expirationtime = readF1000(is);
1742 float size = readF1000(is);
1743 bool collisiondetection = readU8(is);
1744 std::string texture = deSerializeLongString(is);
1745 bool vertical = false;
1747 vertical = readU8(is);
1751 event.type = CE_SPAWN_PARTICLE;
1752 event.spawn_particle.pos = new v3f (pos);
1753 event.spawn_particle.vel = new v3f (vel);
1754 event.spawn_particle.acc = new v3f (acc);
1755 event.spawn_particle.expirationtime = expirationtime;
1756 event.spawn_particle.size = size;
1757 event.spawn_particle.collisiondetection = collisiondetection;
1758 event.spawn_particle.vertical = vertical;
1759 event.spawn_particle.texture = new std::string(texture);
1761 m_client_event_queue.push_back(event);
1764 void Client::handleCommand_AddParticleSpawner(ToClientPacket* pkt)
1778 bool collisiondetection;
1781 *pkt >> amount >> spawntime >> minpos >> maxpos >> minvel >> maxvel
1782 >> minacc >> maxacc >> minexptime >> maxexptime >> minsize
1783 >> maxsize >> collisiondetection;
1785 std::string texture = pkt->readLongString();
1789 bool vertical = false;
1795 event.type = CE_ADD_PARTICLESPAWNER;
1796 event.add_particlespawner.amount = amount;
1797 event.add_particlespawner.spawntime = spawntime;
1798 event.add_particlespawner.minpos = new v3f (minpos);
1799 event.add_particlespawner.maxpos = new v3f (maxpos);
1800 event.add_particlespawner.minvel = new v3f (minvel);
1801 event.add_particlespawner.maxvel = new v3f (maxvel);
1802 event.add_particlespawner.minacc = new v3f (minacc);
1803 event.add_particlespawner.maxacc = new v3f (maxacc);
1804 event.add_particlespawner.minexptime = minexptime;
1805 event.add_particlespawner.maxexptime = maxexptime;
1806 event.add_particlespawner.minsize = minsize;
1807 event.add_particlespawner.maxsize = maxsize;
1808 event.add_particlespawner.collisiondetection = collisiondetection;
1809 event.add_particlespawner.vertical = vertical;
1810 event.add_particlespawner.texture = new std::string(texture);
1811 event.add_particlespawner.id = id;
1813 m_client_event_queue.push_back(event);
1816 void Client::handleCommand_DeleteParticleSpawner(ToClientPacket* pkt)
1823 event.type = CE_DELETE_PARTICLESPAWNER;
1824 event.delete_particlespawner.id = id;
1826 m_client_event_queue.push_back(event);
1829 void Client::handleCommand_HudAdd(ToClientPacket* pkt)
1831 std::string datastring(pkt->getString(0), pkt->getSize());
1832 std::istringstream is(datastring, std::ios_base::binary);
1848 *pkt >> id >> type >> pos >> name >> scale >> text >> number >> item
1849 >> dir >> align >> offset;
1853 catch(SerializationError &e) {};
1857 } catch(SerializationError &e) {};
1860 event.type = CE_HUDADD;
1861 event.hudadd.id = id;
1862 event.hudadd.type = type;
1863 event.hudadd.pos = new v2f(pos);
1864 event.hudadd.name = new std::string(name);
1865 event.hudadd.scale = new v2f(scale);
1866 event.hudadd.text = new std::string(text);
1867 event.hudadd.number = number;
1868 event.hudadd.item = item;
1869 event.hudadd.dir = dir;
1870 event.hudadd.align = new v2f(align);
1871 event.hudadd.offset = new v2f(offset);
1872 event.hudadd.world_pos = new v3f(world_pos);
1873 event.hudadd.size = new v2s32(size);
1874 m_client_event_queue.push_back(event);
1877 void Client::handleCommand_HudRemove(ToClientPacket* pkt)
1884 event.type = CE_HUDRM;
1885 event.hudrm.id = id;
1886 m_client_event_queue.push_back(event);
1889 void Client::handleCommand_HudChange(ToClientPacket* pkt)
1901 if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
1902 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
1904 else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
1906 else if (stat == HUD_STAT_WORLD_POS)
1908 else if (stat == HUD_STAT_SIZE )
1914 event.type = CE_HUDCHANGE;
1915 event.hudchange.id = id;
1916 event.hudchange.stat = (HudElementStat)stat;
1917 event.hudchange.v2fdata = new v2f(v2fdata);
1918 event.hudchange.v3fdata = new v3f(v3fdata);
1919 event.hudchange.sdata = new std::string(sdata);
1920 event.hudchange.data = intdata;
1921 event.hudchange.v2s32data = new v2s32(v2s32data);
1922 m_client_event_queue.push_back(event);
1925 void Client::handleCommand_HudSetFlags(ToClientPacket* pkt)
1929 *pkt >> flags >> mask;
1931 Player *player = m_env.getLocalPlayer();
1932 assert(player != NULL);
1934 player->hud_flags &= ~mask;
1935 player->hud_flags |= flags;
1938 void Client::handleCommand_HudSetParam(ToClientPacket* pkt)
1940 u16 param; std::string value;
1942 *pkt >> param >> value;
1944 Player *player = m_env.getLocalPlayer();
1945 assert(player != NULL);
1947 if(param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) {
1948 s32 hotbar_itemcount = readS32((u8*) value.c_str());
1949 if(hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
1950 player->hud_hotbar_itemcount = hotbar_itemcount;
1952 else if (param == HUD_PARAM_HOTBAR_IMAGE) {
1953 ((LocalPlayer *) player)->hotbar_image = value;
1955 else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
1956 ((LocalPlayer *) player)->hotbar_selected_image = value;
1960 void Client::handleCommand_HudSetSky(ToClientPacket* pkt)
1962 std::string datastring(pkt->getString(0), pkt->getSize());
1963 std::istringstream is(datastring, std::ios_base::binary);
1965 video::SColor *bgcolor = new video::SColor(readARGB8(is));
1966 std::string *type = new std::string(deSerializeString(is));
1967 u16 count = readU16(is);
1968 std::vector<std::string> *params = new std::vector<std::string>;
1970 for(size_t i=0; i<count; i++)
1971 params->push_back(deSerializeString(is));
1974 event.type = CE_SET_SKY;
1975 event.set_sky.bgcolor = bgcolor;
1976 event.set_sky.type = type;
1977 event.set_sky.params = params;
1978 m_client_event_queue.push_back(event);
1981 void Client::handleCommand_OverrideDayNightRatio(ToClientPacket* pkt)
1984 u16 day_night_ratio_u;
1986 *pkt >> do_override >> day_night_ratio_u;
1988 float day_night_ratio_f = (float)day_night_ratio_u / 65536;
1991 event.type = CE_OVERRIDE_DAY_NIGHT_RATIO;
1992 event.override_day_night_ratio.do_override = do_override;
1993 event.override_day_night_ratio.ratio_f = day_night_ratio_f;
1994 m_client_event_queue.push_back(event);
1997 void Client::handleCommand_LocalPlayerAnimations(ToClientPacket* pkt)
1999 LocalPlayer *player = m_env.getLocalPlayer();
2000 assert(player != NULL);
2002 *pkt >> player->local_animations[0];
2003 *pkt >> player->local_animations[1];
2004 *pkt >> player->local_animations[2];
2005 *pkt >> player->local_animations[3];
2006 *pkt >> player->local_animation_speed;
2009 void Client::handleCommand_EyeOffset(ToClientPacket* pkt)
2011 LocalPlayer *player = m_env.getLocalPlayer();
2012 assert(player != NULL);
2014 *pkt >> player->eye_offset_first >> player->eye_offset_third;
2017 inline void Client::handleCommand(ToClientPacket* pkt)
2019 const ToClientCommandHandler& opHandle = toClientCommandTable[pkt->getCommand()];
2020 (this->*opHandle.handler)(pkt);
2024 sender_peer_id given to this shall be quaranteed to be a valid peer
2026 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
2028 DSTACK(__FUNCTION_NAME);
2030 // Ignore packets that don't even fit a command
2032 m_packetcounter.add(60000);
2036 ToClientPacket* pkt = new ToClientPacket(data, datasize, sender_peer_id);
2038 ToClientCommand command = pkt->getCommand();
2040 //infostream<<"Client: received command="<<command<<std::endl;
2041 m_packetcounter.add((u16)command);
2044 If this check is removed, be sure to change the queue
2045 system to know the ids
2047 if(sender_peer_id != PEER_ID_SERVER) {
2048 infostream << "Client::ProcessData(): Discarding data not "
2049 "coming from server: peer_id=" << sender_peer_id
2055 // Command must be handled into ToClientCommandHandler
2056 if (command >= TOCLIENT_NUM_MSG_TYPES) {
2057 infostream << "Client: Ignoring unknown command "
2058 << command << std::endl;
2062 * Those packets are handled before m_server_ser_ver is set, it's normal
2063 * But we must use the new ToClientConnectionState in the future,
2066 if(toClientCommandTable[command].state == TOCLIENT_STATE_NOT_CONNECTED) {
2072 if(m_server_ser_ver == SER_FMT_VER_INVALID) {
2073 infostream << "Client: Server serialization"
2074 " format invalid or not initialized."
2075 " Skipping incoming command=" << command << std::endl;
2081 Handle runtime commands
2088 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
2090 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2091 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
2094 void Client::interact(u8 action, const PointedThing& pointed)
2096 if(m_state != LC_Ready){
2097 infostream<<"Client::interact() "
2098 "cancelled (not connected)"
2103 std::ostringstream os(std::ios_base::binary);
2109 [5] u32 length of the next item
2110 [9] serialized PointedThing
2112 0: start digging (from undersurface) or use
2113 1: stop digging (all parameters ignored)
2114 2: digging completed
2115 3: place block or item (to abovesurface)
2118 writeU16(os, TOSERVER_INTERACT);
2119 writeU8(os, action);
2120 writeU16(os, getPlayerItem());
2121 std::ostringstream tmp_os(std::ios::binary);
2122 pointed.serialize(tmp_os);
2123 os<<serializeLongString(tmp_os.str());
2125 std::string s = os.str();
2126 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2129 Send(0, data, true);
2132 void Client::sendNodemetaFields(v3s16 p, const std::string &formname,
2133 const std::map<std::string, std::string> &fields)
2135 std::ostringstream os(std::ios_base::binary);
2137 writeU16(os, TOSERVER_NODEMETA_FIELDS);
2139 os<<serializeString(formname);
2140 size_t fields_size = fields.size();
2141 assert(fields_size <= 0xFFFF);
2142 writeU16(os, (u16) (fields_size & 0xFFFF));
2143 for(std::map<std::string, std::string>::const_iterator
2144 i = fields.begin(); i != fields.end(); i++){
2145 const std::string &name = i->first;
2146 const std::string &value = i->second;
2147 os<<serializeString(name);
2148 os<<serializeLongString(value);
2152 std::string s = os.str();
2153 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2155 Send(0, data, true);
2158 void Client::sendInventoryFields(const std::string &formname,
2159 const std::map<std::string, std::string> &fields)
2161 std::ostringstream os(std::ios_base::binary);
2163 writeU16(os, TOSERVER_INVENTORY_FIELDS);
2164 os<<serializeString(formname);
2165 size_t fields_size = fields.size();
2166 assert(fields_size <= 0xFFFF);
2167 writeU16(os, (u16) (fields_size & 0xFFFF));
2168 for(std::map<std::string, std::string>::const_iterator
2169 i = fields.begin(); i != fields.end(); i++){
2170 const std::string &name = i->first;
2171 const std::string &value = i->second;
2172 os<<serializeString(name);
2173 os<<serializeLongString(value);
2177 std::string s = os.str();
2178 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2180 Send(0, data, true);
2183 void Client::sendInventoryAction(InventoryAction *a)
2185 std::ostringstream os(std::ios_base::binary);
2189 writeU16(buf, TOSERVER_INVENTORY_ACTION);
2190 os.write((char*)buf, 2);
2195 std::string s = os.str();
2196 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2198 Send(0, data, true);
2201 void Client::sendChatMessage(const std::wstring &message)
2203 std::ostringstream os(std::ios_base::binary);
2207 writeU16(buf, TOSERVER_CHAT_MESSAGE);
2208 os.write((char*)buf, 2);
2211 size_t messagesize = message.size();
2212 if (messagesize > 0xFFFF) {
2213 messagesize = 0xFFFF;
2215 writeU16(buf, (u16) messagesize);
2216 os.write((char*)buf, 2);
2219 for(unsigned int i=0; i<message.size(); i++)
2223 os.write((char*)buf, 2);
2227 std::string s = os.str();
2228 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2230 Send(0, data, true);
2233 void Client::sendChangePassword(const std::wstring &oldpassword,
2234 const std::wstring &newpassword)
2236 Player *player = m_env.getLocalPlayer();
2240 std::string playername = player->getName();
2241 std::string oldpwd = translatePassword(playername, oldpassword);
2242 std::string newpwd = translatePassword(playername, newpassword);
2244 std::ostringstream os(std::ios_base::binary);
2245 u8 buf[2+PASSWORD_SIZE*2];
2247 [0] u16 TOSERVER_PASSWORD
2248 [2] u8[28] old password
2249 [30] u8[28] new password
2252 writeU16(buf, TOSERVER_PASSWORD);
2253 for(unsigned int i=0;i<PASSWORD_SIZE-1;i++)
2255 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
2256 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
2258 buf[2+PASSWORD_SIZE-1] = 0;
2259 buf[30+PASSWORD_SIZE-1] = 0;
2260 os.write((char*)buf, 2+PASSWORD_SIZE*2);
2263 std::string s = os.str();
2264 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2266 Send(0, data, true);
2270 void Client::sendDamage(u8 damage)
2272 DSTACK(__FUNCTION_NAME);
2273 std::ostringstream os(std::ios_base::binary);
2275 writeU16(os, TOSERVER_DAMAGE);
2276 writeU8(os, damage);
2279 std::string s = os.str();
2280 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2282 Send(0, data, true);
2285 void Client::sendBreath(u16 breath)
2287 DSTACK(__FUNCTION_NAME);
2288 std::ostringstream os(std::ios_base::binary);
2290 writeU16(os, TOSERVER_BREATH);
2291 writeU16(os, breath);
2293 std::string s = os.str();
2294 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2296 Send(0, data, true);
2299 void Client::sendRespawn()
2301 DSTACK(__FUNCTION_NAME);
2302 std::ostringstream os(std::ios_base::binary);
2304 writeU16(os, TOSERVER_RESPAWN);
2307 std::string s = os.str();
2308 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2310 Send(0, data, true);
2313 void Client::sendReady()
2315 DSTACK(__FUNCTION_NAME);
2316 std::ostringstream os(std::ios_base::binary);
2318 writeU16(os, TOSERVER_CLIENT_READY);
2319 writeU8(os,VERSION_MAJOR);
2320 writeU8(os,VERSION_MINOR);
2321 writeU8(os,VERSION_PATCH_ORIG);
2324 writeU16(os,strlen(minetest_version_hash));
2325 os.write(minetest_version_hash,strlen(minetest_version_hash));
2328 std::string s = os.str();
2329 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2331 Send(0, data, true);
2334 void Client::sendPlayerPos()
2336 LocalPlayer *myplayer = m_env.getLocalPlayer();
2337 if(myplayer == NULL)
2340 // Save bandwidth by only updating position when something changed
2341 if(myplayer->last_position == myplayer->getPosition() &&
2342 myplayer->last_speed == myplayer->getSpeed() &&
2343 myplayer->last_pitch == myplayer->getPitch() &&
2344 myplayer->last_yaw == myplayer->getYaw() &&
2345 myplayer->last_keyPressed == myplayer->keyPressed)
2348 myplayer->last_position = myplayer->getPosition();
2349 myplayer->last_speed = myplayer->getSpeed();
2350 myplayer->last_pitch = myplayer->getPitch();
2351 myplayer->last_yaw = myplayer->getYaw();
2352 myplayer->last_keyPressed = myplayer->keyPressed;
2356 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2357 our_peer_id = m_con.GetPeerID();
2360 // Set peer id if not set already
2361 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2362 myplayer->peer_id = our_peer_id;
2363 // Check that an existing peer_id is the same as the connection's
2364 assert(myplayer->peer_id == our_peer_id);
2366 v3f pf = myplayer->getPosition();
2367 v3f sf = myplayer->getSpeed();
2368 s32 pitch = myplayer->getPitch() * 100;
2369 s32 yaw = myplayer->getYaw() * 100;
2370 u32 keyPressed = myplayer->keyPressed;
2372 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
2373 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
2377 [2] v3s32 position*100
2378 [2+12] v3s32 speed*100
2379 [2+12+12] s32 pitch*100
2380 [2+12+12+4] s32 yaw*100
2381 [2+12+12+4+4] u32 keyPressed
2383 SharedBuffer<u8> data(2+12+12+4+4+4);
2384 writeU16(&data[0], TOSERVER_PLAYERPOS);
2385 writeV3S32(&data[2], position);
2386 writeV3S32(&data[2+12], speed);
2387 writeS32(&data[2+12+12], pitch);
2388 writeS32(&data[2+12+12+4], yaw);
2389 writeU32(&data[2+12+12+4+4], keyPressed);
2390 // Send as unreliable
2391 Send(0, data, false);
2394 void Client::sendPlayerItem(u16 item)
2396 Player *myplayer = m_env.getLocalPlayer();
2397 if(myplayer == NULL)
2400 u16 our_peer_id = m_con.GetPeerID();
2402 // Set peer id if not set already
2403 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2404 myplayer->peer_id = our_peer_id;
2405 // Check that an existing peer_id is the same as the connection's
2406 assert(myplayer->peer_id == our_peer_id);
2408 SharedBuffer<u8> data(2+2);
2409 writeU16(&data[0], TOSERVER_PLAYERITEM);
2410 writeU16(&data[2], item);
2413 Send(0, data, true);
2416 void Client::removeNode(v3s16 p)
2418 std::map<v3s16, MapBlock*> modified_blocks;
2422 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
2424 catch(InvalidPositionException &e)
2428 for(std::map<v3s16, MapBlock * >::iterator
2429 i = modified_blocks.begin();
2430 i != modified_blocks.end(); ++i)
2432 addUpdateMeshTaskWithEdge(i->first, false, true);
2436 void Client::addNode(v3s16 p, MapNode n, bool remove_metadata)
2438 //TimeTaker timer1("Client::addNode()");
2440 std::map<v3s16, MapBlock*> modified_blocks;
2444 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
2445 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
2447 catch(InvalidPositionException &e)
2450 for(std::map<v3s16, MapBlock * >::iterator
2451 i = modified_blocks.begin();
2452 i != modified_blocks.end(); ++i)
2454 addUpdateMeshTaskWithEdge(i->first, false, true);
2458 void Client::setPlayerControl(PlayerControl &control)
2460 LocalPlayer *player = m_env.getLocalPlayer();
2461 assert(player != NULL);
2462 player->control = control;
2465 void Client::selectPlayerItem(u16 item)
2467 m_playeritem = item;
2468 m_inventory_updated = true;
2469 sendPlayerItem(item);
2472 // Returns true if the inventory of the local player has been
2473 // updated from the server. If it is true, it is set to false.
2474 bool Client::getLocalInventoryUpdated()
2476 bool updated = m_inventory_updated;
2477 m_inventory_updated = false;
2481 // Copies the inventory of the local player to parameter
2482 void Client::getLocalInventory(Inventory &dst)
2484 Player *player = m_env.getLocalPlayer();
2485 assert(player != NULL);
2486 dst = player->inventory;
2489 Inventory* Client::getInventory(const InventoryLocation &loc)
2492 case InventoryLocation::UNDEFINED:
2495 case InventoryLocation::CURRENT_PLAYER:
2497 Player *player = m_env.getLocalPlayer();
2498 assert(player != NULL);
2499 return &player->inventory;
2502 case InventoryLocation::PLAYER:
2504 Player *player = m_env.getPlayer(loc.name.c_str());
2507 return &player->inventory;
2510 case InventoryLocation::NODEMETA:
2512 NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p);
2515 return meta->getInventory();
2518 case InventoryLocation::DETACHED:
2520 if(m_detached_inventories.count(loc.name) == 0)
2522 return m_detached_inventories[loc.name];
2531 void Client::inventoryAction(InventoryAction *a)
2534 Send it to the server
2536 sendInventoryAction(a);
2539 Predict some local inventory changes
2541 a->clientApply(this, this);
2547 ClientActiveObject * Client::getSelectedActiveObject(
2549 v3f from_pos_f_on_map,
2550 core::line3d<f32> shootline_on_map
2553 std::vector<DistanceSortedActiveObject> objects;
2555 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
2558 // After this, the closest object is the first in the array.
2559 std::sort(objects.begin(), objects.end());
2561 for(unsigned int i=0; i<objects.size(); i++)
2563 ClientActiveObject *obj = objects[i].obj;
2565 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
2566 if(selection_box == NULL)
2569 v3f pos = obj->getPosition();
2571 core::aabbox3d<f32> offsetted_box(
2572 selection_box->MinEdge + pos,
2573 selection_box->MaxEdge + pos
2576 if(offsetted_box.intersectsWithLine(shootline_on_map))
2585 std::list<std::string> Client::getConnectedPlayerNames()
2587 return m_env.getPlayerNames();
2590 float Client::getAnimationTime()
2592 return m_animation_time;
2595 int Client::getCrackLevel()
2597 return m_crack_level;
2600 void Client::setHighlighted(v3s16 pos, bool show_highlighted)
2602 m_show_highlighted = show_highlighted;
2603 v3s16 old_highlighted_pos = m_highlighted_pos;
2604 m_highlighted_pos = pos;
2605 addUpdateMeshTaskForNode(old_highlighted_pos, false, true);
2606 addUpdateMeshTaskForNode(m_highlighted_pos, false, true);
2609 void Client::setCrack(int level, v3s16 pos)
2611 int old_crack_level = m_crack_level;
2612 v3s16 old_crack_pos = m_crack_pos;
2614 m_crack_level = level;
2617 if(old_crack_level >= 0 && (level < 0 || pos != old_crack_pos))
2620 addUpdateMeshTaskForNode(old_crack_pos, false, true);
2622 if(level >= 0 && (old_crack_level < 0 || pos != old_crack_pos))
2625 addUpdateMeshTaskForNode(pos, false, true);
2631 Player *player = m_env.getLocalPlayer();
2632 assert(player != NULL);
2636 u16 Client::getBreath()
2638 Player *player = m_env.getLocalPlayer();
2639 assert(player != NULL);
2640 return player->getBreath();
2643 bool Client::getChatMessage(std::wstring &message)
2645 if(m_chat_queue.size() == 0)
2647 message = m_chat_queue.pop_front();
2651 void Client::typeChatMessage(const std::wstring &message)
2653 // Discard empty line
2658 sendChatMessage(message);
2661 if (message[0] == L'/')
2663 m_chat_queue.push_back((std::wstring)L"issued command: " + message);
2667 LocalPlayer *player = m_env.getLocalPlayer();
2668 assert(player != NULL);
2669 std::wstring name = narrow_to_wide(player->getName());
2670 m_chat_queue.push_back((std::wstring)L"<" + name + L"> " + message);
2674 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
2676 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2681 Create a task to update the mesh of the block
2684 MeshMakeData *data = new MeshMakeData(this);
2687 //TimeTaker timer("data fill");
2689 // Debug: 1-6ms, avg=2ms
2691 data->setCrack(m_crack_level, m_crack_pos);
2692 data->setHighlighted(m_highlighted_pos, m_show_highlighted);
2693 data->setSmoothLighting(m_cache_smooth_lighting);
2696 // Add task to queue
2697 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server, urgent);
2700 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent)
2703 addUpdateMeshTask(blockpos, ack_to_server, urgent);
2705 catch(InvalidPositionException &e){}
2708 for (int i=0;i<6;i++)
2711 v3s16 p = blockpos + g_6dirs[i];
2712 addUpdateMeshTask(p, false, urgent);
2714 catch(InvalidPositionException &e){}
2718 void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool urgent)
2722 infostream<<"Client::addUpdateMeshTaskForNode(): "
2723 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2727 v3s16 blockpos = getNodeBlockPos(nodepos);
2728 v3s16 blockpos_relative = blockpos * MAP_BLOCKSIZE;
2731 addUpdateMeshTask(blockpos, ack_to_server, urgent);
2733 catch(InvalidPositionException &e){}
2736 if(nodepos.X == blockpos_relative.X){
2738 v3s16 p = blockpos + v3s16(-1,0,0);
2739 addUpdateMeshTask(p, false, urgent);
2741 catch(InvalidPositionException &e){}
2744 if(nodepos.Y == blockpos_relative.Y){
2746 v3s16 p = blockpos + v3s16(0,-1,0);
2747 addUpdateMeshTask(p, false, urgent);
2749 catch(InvalidPositionException &e){}
2752 if(nodepos.Z == blockpos_relative.Z){
2754 v3s16 p = blockpos + v3s16(0,0,-1);
2755 addUpdateMeshTask(p, false, urgent);
2757 catch(InvalidPositionException &e){}
2761 ClientEvent Client::getClientEvent()
2763 if(m_client_event_queue.size() == 0)
2766 event.type = CE_NONE;
2769 return m_client_event_queue.pop_front();
2772 float Client::mediaReceiveProgress()
2774 if (m_media_downloader)
2775 return m_media_downloader->getProgress();
2777 return 1.0; // downloader only exists when not yet done
2780 void Client::afterContentReceived(IrrlichtDevice *device, gui::IGUIFont* font)
2782 infostream<<"Client::afterContentReceived() started"<<std::endl;
2783 assert(m_itemdef_received);
2784 assert(m_nodedef_received);
2785 assert(mediaReceived());
2787 const wchar_t* text = wgettext("Loading textures...");
2789 // Rebuild inherited images and recreate textures
2790 infostream<<"- Rebuilding images and textures"<<std::endl;
2791 draw_load_screen(text,device, guienv, 0, 70);
2792 m_tsrc->rebuildImagesAndTextures();
2796 infostream<<"- Rebuilding shaders"<<std::endl;
2797 text = wgettext("Rebuilding shaders...");
2798 draw_load_screen(text, device, guienv, 0, 75);
2799 m_shsrc->rebuildShaders();
2802 // Update node aliases
2803 infostream<<"- Updating node aliases"<<std::endl;
2804 text = wgettext("Initializing nodes...");
2805 draw_load_screen(text, device, guienv, 0, 80);
2806 m_nodedef->updateAliases(m_itemdef);
2807 m_nodedef->setNodeRegistrationStatus(true);
2808 m_nodedef->runNodeResolverCallbacks();
2811 // Update node textures and assign shaders to each tile
2812 infostream<<"- Updating node textures"<<std::endl;
2813 m_nodedef->updateTextures(this);
2815 // Preload item textures and meshes if configured to
2816 if(g_settings->getBool("preload_item_visuals"))
2818 verbosestream<<"Updating item textures and meshes"<<std::endl;
2819 text = wgettext("Item textures...");
2820 draw_load_screen(text, device, guienv, 0, 0);
2821 std::set<std::string> names = m_itemdef->getAll();
2822 size_t size = names.size();
2825 for(std::set<std::string>::const_iterator
2826 i = names.begin(); i != names.end(); ++i)
2828 // Asking for these caches the result
2829 m_itemdef->getInventoryTexture(*i, this);
2830 m_itemdef->getWieldMesh(*i, this);
2832 percent = (count * 100 / size * 0.2) + 80;
2833 draw_load_screen(text, device, guienv, 0, percent);
2838 // Start mesh update thread after setting up content definitions
2839 infostream<<"- Starting mesh update thread"<<std::endl;
2840 m_mesh_update_thread.Start();
2844 text = wgettext("Done!");
2845 draw_load_screen(text, device, guienv, 0, 100);
2846 infostream<<"Client::afterContentReceived() done"<<std::endl;
2850 float Client::getRTT(void)
2852 return m_con.getPeerStat(PEER_ID_SERVER,con::AVG_RTT);
2855 float Client::getCurRate(void)
2857 return ( m_con.getLocalStat(con::CUR_INC_RATE) +
2858 m_con.getLocalStat(con::CUR_DL_RATE));
2861 float Client::getAvgRate(void)
2863 return ( m_con.getLocalStat(con::AVG_INC_RATE) +
2864 m_con.getLocalStat(con::AVG_DL_RATE));
2867 void Client::makeScreenshot(IrrlichtDevice *device)
2869 irr::video::IVideoDriver *driver = device->getVideoDriver();
2870 irr::video::IImage* const raw_image = driver->createScreenShot();
2872 irr::video::IImage* const image = driver->createImage(video::ECF_R8G8B8,
2873 raw_image->getDimension());
2876 raw_image->copyTo(image);
2877 irr::c8 filename[256];
2878 snprintf(filename, sizeof(filename), "%s" DIR_DELIM "screenshot_%u.png",
2879 g_settings->get("screenshot_path").c_str(),
2880 device->getTimer()->getRealTime());
2881 std::ostringstream sstr;
2882 if (driver->writeImageToFile(image, filename)) {
2883 sstr << "Saved screenshot to '" << filename << "'";
2885 sstr << "Failed to save screenshot '" << filename << "'";
2887 m_chat_queue.push_back(narrow_to_wide(sstr.str()));
2888 infostream << sstr.str() << std::endl;
2895 // IGameDef interface
2897 IItemDefManager* Client::getItemDefManager()
2901 INodeDefManager* Client::getNodeDefManager()
2905 ICraftDefManager* Client::getCraftDefManager()
2908 //return m_craftdef;
2910 ITextureSource* Client::getTextureSource()
2914 IShaderSource* Client::getShaderSource()
2918 scene::ISceneManager* Client::getSceneManager()
2920 return m_device->getSceneManager();
2922 u16 Client::allocateUnknownNodeId(const std::string &name)
2924 errorstream<<"Client::allocateUnknownNodeId(): "
2925 <<"Client cannot allocate node IDs"<<std::endl;
2927 return CONTENT_IGNORE;
2929 ISoundManager* Client::getSoundManager()
2933 MtEventManager* Client::getEventManager()
2938 ParticleManager* Client::getParticleManager()
2940 return &m_particle_manager;
2943 scene::IAnimatedMesh* Client::getMesh(const std::string &filename)
2945 std::map<std::string, std::string>::const_iterator i =
2946 m_mesh_data.find(filename);
2947 if(i == m_mesh_data.end()){
2948 errorstream<<"Client::getMesh(): Mesh not found: \""<<filename<<"\""
2952 const std::string &data = i->second;
2953 scene::ISceneManager *smgr = m_device->getSceneManager();
2955 // Create the mesh, remove it from cache and return it
2956 // This allows unique vertex colors and other properties for each instance
2957 Buffer<char> data_rw(data.c_str(), data.size()); // Const-incorrect Irrlicht
2958 io::IFileSystem *irrfs = m_device->getFileSystem();
2959 io::IReadFile *rfile = irrfs->createMemoryReadFile(
2960 *data_rw, data_rw.getSize(), filename.c_str());
2963 scene::IAnimatedMesh *mesh = smgr->getMesh(rfile);
2965 // NOTE: By playing with Irrlicht refcounts, maybe we could cache a bunch
2966 // of uniquely named instances and re-use them
2968 smgr->getMeshCache()->removeMesh(mesh);