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");
286 m_cache_enable_shaders = g_settings->getBool("enable_shaders");
291 //request all client managed threads to stop
292 m_mesh_update_thread.Stop();
293 if (localdb != NULL) {
294 actionstream << "Local map saving ended" << std::endl;
300 bool Client::isShutdown()
303 if (!m_mesh_update_thread.IsRunning()) return true;
312 m_mesh_update_thread.Stop();
313 m_mesh_update_thread.Wait();
314 while(!m_mesh_update_thread.m_queue_out.empty()) {
315 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_frontNoEx();
320 delete m_inventory_from_server;
322 // Delete detached inventories
323 for(std::map<std::string, Inventory*>::iterator
324 i = m_detached_inventories.begin();
325 i != m_detached_inventories.end(); i++){
329 // cleanup 3d model meshes on client shutdown
330 while (m_device->getSceneManager()->getMeshCache()->getMeshCount() != 0) {
331 scene::IAnimatedMesh * mesh =
332 m_device->getSceneManager()->getMeshCache()->getMeshByIndex(0);
335 m_device->getSceneManager()->getMeshCache()->removeMesh(mesh);
339 void Client::connect(Address address,
340 const std::string &address_name,
341 bool is_local_server)
343 DSTACK(__FUNCTION_NAME);
345 initLocalMapSaving(address, address_name, is_local_server);
347 m_con.SetTimeoutMs(0);
348 m_con.Connect(address);
351 void Client::step(float dtime)
353 DSTACK(__FUNCTION_NAME);
359 if(m_ignore_damage_timer > dtime)
360 m_ignore_damage_timer -= dtime;
362 m_ignore_damage_timer = 0.0;
364 m_animation_time += dtime;
365 if(m_animation_time > 60.0)
366 m_animation_time -= 60.0;
368 m_time_of_day_update_timer += dtime;
376 float &counter = m_packetcounter_timer;
382 infostream << "Client packetcounter (" << m_packetcounter_timer
384 m_packetcounter.print(infostream);
385 m_packetcounter.clear();
392 Delete unused sectors
394 NOTE: This jams the game for a while because deleting sectors
398 float &counter = m_delete_unused_sectors_timer;
406 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
408 core::list<v3s16> deleted_blocks;
410 float delete_unused_sectors_timeout =
411 g_settings->getFloat("client_delete_unused_sectors_timeout");
413 // Delete sector blocks
414 /*u32 num = m_env.getMap().unloadUnusedData
415 (delete_unused_sectors_timeout,
416 true, &deleted_blocks);*/
418 // Delete whole sectors
419 m_env.getMap().unloadUnusedData
420 (delete_unused_sectors_timeout,
423 if(deleted_blocks.size() > 0)
425 /*infostream<<"Client: Deleted blocks of "<<num
426 <<" unused sectors"<<std::endl;*/
427 /*infostream<<"Client: Deleted "<<num
428 <<" unused sectors"<<std::endl;*/
434 // Env is locked so con can be locked.
435 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
437 core::list<v3s16>::Iterator i = deleted_blocks.begin();
438 core::list<v3s16> sendlist;
441 if(sendlist.size() == 255 || i == deleted_blocks.end())
443 if(sendlist.size() == 0)
452 u32 replysize = 2+1+6*sendlist.size();
453 SharedBuffer<u8> reply(replysize);
454 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
455 reply[2] = sendlist.size();
457 for(core::list<v3s16>::Iterator
458 j = sendlist.begin();
459 j != sendlist.end(); j++)
461 writeV3S16(&reply[2+1+6*k], *j);
464 m_con.Send(PEER_ID_SERVER, 1, reply, true);
466 if(i == deleted_blocks.end())
472 sendlist.push_back(*i);
479 // UGLY hack to fix 2 second startup delay caused by non existent
480 // server client startup synchronization in local server or singleplayer mode
481 static bool initial_step = true;
483 initial_step = false;
485 else if(m_state == LC_Created)
487 float &counter = m_connection_reinit_timer;
493 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
495 Player *myplayer = m_env.getLocalPlayer();
496 assert(myplayer != NULL);
497 // Send TOSERVER_INIT
498 // [0] u16 TOSERVER_INIT
499 // [2] u8 SER_FMT_VER_HIGHEST_READ
500 // [3] u8[20] player_name
501 // [23] u8[28] password (new in some version)
502 // [51] u16 minimum supported network protocol version (added sometime)
503 // [53] u16 maximum supported network protocol version (added later than the previous one)
504 SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2+2);
505 writeU16(&data[0], TOSERVER_INIT);
506 writeU8(&data[2], SER_FMT_VER_HIGHEST_READ);
508 memset((char*)&data[3], 0, PLAYERNAME_SIZE);
509 snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
511 /*infostream<<"Client: sending initial password hash: \""<<m_password<<"\""
514 memset((char*)&data[23], 0, PASSWORD_SIZE);
515 snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
517 writeU16(&data[51], CLIENT_PROTOCOL_VERSION_MIN);
518 writeU16(&data[53], CLIENT_PROTOCOL_VERSION_MAX);
520 // Send as unreliable
521 Send(1, data, false);
524 // Not connected, return
529 Do stuff if connected
533 Run Map's timers and unload unused data
535 const float map_timer_and_unload_dtime = 5.25;
536 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
538 ScopeProfiler sp(g_profiler, "Client: map timer and unload");
539 std::list<v3s16> deleted_blocks;
540 m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
541 g_settings->getFloat("client_unload_unused_data_timeout"),
544 /*if(deleted_blocks.size() > 0)
545 infostream<<"Client: Unloaded "<<deleted_blocks.size()
546 <<" unused blocks"<<std::endl;*/
550 NOTE: This loop is intentionally iterated the way it is.
553 std::list<v3s16>::iterator i = deleted_blocks.begin();
554 std::list<v3s16> sendlist;
557 if(sendlist.size() == 255 || i == deleted_blocks.end())
568 u32 replysize = 2+1+6*sendlist.size();
569 SharedBuffer<u8> reply(replysize);
570 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
571 reply[2] = sendlist.size();
573 for(std::list<v3s16>::iterator
574 j = sendlist.begin();
575 j != sendlist.end(); ++j)
577 writeV3S16(&reply[2+1+6*k], *j);
580 m_con.Send(PEER_ID_SERVER, 2, reply, true);
582 if(i == deleted_blocks.end())
588 sendlist.push_back(*i);
597 // Control local player (0ms)
598 LocalPlayer *player = m_env.getLocalPlayer();
599 assert(player != NULL);
600 player->applyControl(dtime);
610 ClientEnvEvent event = m_env.getClientEvent();
611 if(event.type == CEE_NONE)
615 else if(event.type == CEE_PLAYER_DAMAGE)
617 if(m_ignore_damage_timer <= 0)
619 u8 damage = event.player_damage.amount;
621 if(event.player_damage.send_to_server)
624 // Add to ClientEvent queue
626 event.type = CE_PLAYER_DAMAGE;
627 event.player_damage.amount = damage;
628 m_client_event_queue.push_back(event);
631 else if(event.type == CEE_PLAYER_BREATH)
633 u16 breath = event.player_breath.amount;
643 float &counter = m_avg_rtt_timer;
648 // connectedAndInitialized() is true, peer exists.
649 float avg_rtt = getRTT();
650 infostream<<"Client: avg_rtt="<<avg_rtt<<std::endl;
655 Send player position to server
658 float &counter = m_playerpos_send_timer;
660 if((m_state == LC_Ready) && (counter >= m_recommended_send_interval))
668 Replace updated meshes
671 int num_processed_meshes = 0;
672 while(!m_mesh_update_thread.m_queue_out.empty())
674 num_processed_meshes++;
675 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_frontNoEx();
676 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
679 // Delete the old mesh
680 if(block->mesh != NULL)
682 // TODO: Remove hardware buffers of meshbuffers of block->mesh
687 // Replace with the new mesh
688 block->mesh = r.mesh;
692 if(r.ack_block_to_server)
704 u32 replysize = 2+1+6;
705 SharedBuffer<u8> reply(replysize);
706 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
708 writeV3S16(&reply[3], r.p);
710 m_con.Send(PEER_ID_SERVER, 2, reply, true);
713 if(num_processed_meshes > 0)
714 g_profiler->graphAdd("num_processed_meshes", num_processed_meshes);
720 if (m_media_downloader && m_media_downloader->isStarted()) {
721 m_media_downloader->step(this);
722 if (m_media_downloader->isDone()) {
724 delete m_media_downloader;
725 m_media_downloader = NULL;
730 If the server didn't update the inventory in a while, revert
731 the local inventory (so the player notices the lag problem
732 and knows something is wrong).
734 if(m_inventory_from_server)
736 float interval = 10.0;
737 float count_before = floor(m_inventory_from_server_age / interval);
739 m_inventory_from_server_age += dtime;
741 float count_after = floor(m_inventory_from_server_age / interval);
743 if(count_after != count_before)
745 // Do this every <interval> seconds after TOCLIENT_INVENTORY
746 // Reset the locally changed inventory to the authoritative inventory
747 Player *player = m_env.getLocalPlayer();
748 player->inventory = *m_inventory_from_server;
749 m_inventory_updated = true;
754 Update positions of sounds attached to objects
757 for(std::map<int, u16>::iterator
758 i = m_sounds_to_objects.begin();
759 i != m_sounds_to_objects.end(); i++)
761 int client_id = i->first;
762 u16 object_id = i->second;
763 ClientActiveObject *cao = m_env.getActiveObject(object_id);
766 v3f pos = cao->getPosition();
767 m_sound->updateSoundPosition(client_id, pos);
772 Handle removed remotely initiated sounds
774 m_removed_sounds_check_timer += dtime;
775 if(m_removed_sounds_check_timer >= 2.32)
777 m_removed_sounds_check_timer = 0;
778 // Find removed sounds and clear references to them
779 std::set<s32> removed_server_ids;
780 for(std::map<s32, int>::iterator
781 i = m_sounds_server_to_client.begin();
782 i != m_sounds_server_to_client.end();)
784 s32 server_id = i->first;
785 int client_id = i->second;
787 if(!m_sound->soundExists(client_id)){
788 m_sounds_server_to_client.erase(server_id);
789 m_sounds_client_to_server.erase(client_id);
790 m_sounds_to_objects.erase(client_id);
791 removed_server_ids.insert(server_id);
795 if(!removed_server_ids.empty())
797 std::ostringstream os(std::ios_base::binary);
798 writeU16(os, TOSERVER_REMOVED_SOUNDS);
799 size_t server_ids = removed_server_ids.size();
800 assert(server_ids <= 0xFFFF);
801 writeU16(os, (u16) (server_ids & 0xFFFF));
802 for(std::set<s32>::iterator i = removed_server_ids.begin();
803 i != removed_server_ids.end(); i++)
805 std::string s = os.str();
806 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
813 bool Client::loadMedia(const std::string &data, const std::string &filename)
815 // Silly irrlicht's const-incorrectness
816 Buffer<char> data_rw(data.c_str(), data.size());
820 const char *image_ext[] = {
821 ".png", ".jpg", ".bmp", ".tga",
822 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
825 name = removeStringEnd(filename, image_ext);
828 verbosestream<<"Client: Attempting to load image "
829 <<"file \""<<filename<<"\""<<std::endl;
831 io::IFileSystem *irrfs = m_device->getFileSystem();
832 video::IVideoDriver *vdrv = m_device->getVideoDriver();
834 // Create an irrlicht memory file
835 io::IReadFile *rfile = irrfs->createMemoryReadFile(
836 *data_rw, data_rw.getSize(), "_tempreadfile");
839 video::IImage *img = vdrv->createImageFromFile(rfile);
841 errorstream<<"Client: Cannot create image from data of "
842 <<"file \""<<filename<<"\""<<std::endl;
847 m_tsrc->insertSourceImage(filename, img);
854 const char *sound_ext[] = {
855 ".0.ogg", ".1.ogg", ".2.ogg", ".3.ogg", ".4.ogg",
856 ".5.ogg", ".6.ogg", ".7.ogg", ".8.ogg", ".9.ogg",
859 name = removeStringEnd(filename, sound_ext);
862 verbosestream<<"Client: Attempting to load sound "
863 <<"file \""<<filename<<"\""<<std::endl;
864 m_sound->loadSoundData(name, data);
868 const char *model_ext[] = {
869 ".x", ".b3d", ".md2", ".obj",
872 name = removeStringEnd(filename, model_ext);
875 verbosestream<<"Client: Storing model into memory: "
876 <<"\""<<filename<<"\""<<std::endl;
877 if(m_mesh_data.count(filename))
878 errorstream<<"Multiple models with name \""<<filename.c_str()
879 <<"\" found; replacing previous model"<<std::endl;
880 m_mesh_data[filename] = data;
884 errorstream<<"Client: Don't know how to load file \""
885 <<filename<<"\""<<std::endl;
889 // Virtual methods from con::PeerHandler
890 void Client::peerAdded(con::Peer *peer)
892 infostream<<"Client::peerAdded(): peer->id="
893 <<peer->id<<std::endl;
895 void Client::deletingPeer(con::Peer *peer, bool timeout)
897 infostream<<"Client::deletingPeer(): "
898 "Server Peer is getting deleted "
899 <<"(timeout="<<timeout<<")"<<std::endl;
904 u16 number of files requested
910 void Client::request_media(const std::list<std::string> &file_requests)
912 std::ostringstream os(std::ios_base::binary);
913 writeU16(os, TOSERVER_REQUEST_MEDIA);
914 size_t file_requests_size = file_requests.size();
915 assert(file_requests_size <= 0xFFFF);
916 writeU16(os, (u16) (file_requests_size & 0xFFFF));
918 for(std::list<std::string>::const_iterator i = file_requests.begin();
919 i != file_requests.end(); ++i) {
920 os<<serializeString(*i);
924 std::string s = os.str();
925 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
928 infostream<<"Client: Sending media request list to server ("
929 <<file_requests.size()<<" files)"<<std::endl;
932 void Client::received_media()
934 // notify server we received everything
935 std::ostringstream os(std::ios_base::binary);
936 writeU16(os, TOSERVER_RECEIVED_MEDIA);
937 std::string s = os.str();
938 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
941 infostream<<"Client: Notifying server that we received all media"
945 void Client::initLocalMapSaving(const Address &address,
946 const std::string &hostname,
947 bool is_local_server)
951 if (!g_settings->getBool("enable_local_map_saving") || is_local_server)
954 const std::string world_path = porting::path_user
955 + DIR_DELIM + "worlds"
956 + DIR_DELIM + "server_"
957 + hostname + "_" + to_string(address.getPort());
959 SubgameSpec gamespec;
961 if (!getWorldExists(world_path)) {
962 gamespec = findSubgame(g_settings->get("default_game"));
963 if (!gamespec.isValid())
964 gamespec = findSubgame("minimal");
966 gamespec = findWorldSubgame(world_path);
969 if (!gamespec.isValid()) {
970 errorstream << "Couldn't find subgame for local map saving." << std::endl;
974 localserver = new Server(world_path, gamespec, false, false);
975 localdb = new Database_SQLite3(&(ServerMap&)localserver->getMap(), world_path);
976 localdb->beginSave();
977 actionstream << "Local map saving started, map will be saved at '" << world_path << "'" << std::endl;
980 void Client::ReceiveAll()
982 DSTACK(__FUNCTION_NAME);
983 u32 start_ms = porting::getTimeMs();
986 // Limit time even if there would be huge amounts of data to
988 if(porting::getTimeMs() > start_ms + 100)
993 g_profiler->graphAdd("client_received_packets", 1);
995 catch(con::NoIncomingDataException &e)
999 catch(con::InvalidIncomingDataException &e)
1001 infostream<<"Client::ReceiveAll(): "
1002 "InvalidIncomingDataException: what()="
1003 <<e.what()<<std::endl;
1008 void Client::Receive()
1010 DSTACK(__FUNCTION_NAME);
1011 SharedBuffer<u8> data;
1013 u32 datasize = m_con.Receive(sender_peer_id, data);
1014 ProcessData(*data, datasize, sender_peer_id);
1017 void Client::handleCommand_Deprecated(ToClientPacket* pkt)
1019 infostream << "Got deprecated command "
1020 << toClientCommandTable[pkt->getCommand()].name << " from peer "
1021 << pkt->getPeerId() << "!" << std::endl;
1024 void Client::handleCommand_Init(ToClientPacket* pkt)
1026 if(pkt->getSize() < 1)
1032 infostream << "Client: TOCLIENT_INIT received with "
1033 "deployed=" << ((int)deployed & 0xff) << std::endl;
1035 if(!ser_ver_supported(deployed)) {
1036 infostream << "Client: TOCLIENT_INIT: Server sent "
1037 << "unsupported ser_fmt_ver"<< std::endl;
1041 m_server_ser_ver = deployed;
1043 // Get player position
1044 v3s16 playerpos_s16(0, BS * 2 + BS * 20, 0);
1045 if(pkt->getSize() >= 1 + 6) {
1046 *pkt >> playerpos_s16;
1048 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS / 2, 0);
1051 // Set player position
1052 Player *player = m_env.getLocalPlayer();
1053 assert(player != NULL);
1054 player->setPosition(playerpos_f);
1056 if(pkt->getSize() >= 1 + 6 + 8) {
1059 infostream << "Client: received map seed: " << m_map_seed << std::endl;
1062 if(pkt->getSize() >= 1 + 6 + 8 + 4) {
1063 *pkt >> m_recommended_send_interval;
1064 infostream << "Client: received recommended send interval "
1065 << m_recommended_send_interval<<std::endl;
1070 SharedBuffer<u8> reply(replysize);
1071 writeU16(&reply[0], TOSERVER_INIT2);
1073 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1078 void Client::handleCommand_AccessDenied(ToClientPacket* pkt)
1080 // The server didn't like our password. Note, this needs
1081 // to be processed even if the serialisation format has
1082 // not been agreed yet, the same as TOCLIENT_INIT.
1083 m_access_denied = true;
1084 m_access_denied_reason = L"Unknown";
1085 if(pkt->getSize() >= 2) {
1086 *pkt >> m_access_denied_reason;
1090 void Client::handleCommand_RemoveNode(ToClientPacket* pkt)
1092 if(pkt->getSize() < 6)
1102 void Client::handleCommand_AddNode(ToClientPacket* pkt)
1104 if(pkt->getSize() < 6 + MapNode::serializedLength(m_server_ser_ver))
1113 n.deSerialize(pkt->getU8Ptr(6), m_server_ser_ver);
1115 bool remove_metadata = true;
1116 u32 index = 6 + MapNode::serializedLength(m_server_ser_ver);
1117 if ((pkt->getSize() >= index + 1) && pkt->getU8(index)) {
1118 remove_metadata = false;
1121 addNode(p, n, remove_metadata);
1123 void Client::handleCommand_BlockData(ToClientPacket* pkt)
1125 // Ignore too small packet
1126 if(pkt->getSize() < 6)
1134 std::string datastring(pkt->getString(6), pkt->getSize() - 6);
1135 std::istringstream istr(datastring, std::ios_base::binary);
1140 v2s16 p2d(p.X, p.Z);
1141 sector = m_env.getMap().emergeSector(p2d);
1143 assert(sector->getPos() == p2d);
1145 block = sector->getBlockNoCreateNoEx(p.Y);
1148 Update an existing block
1150 block->deSerialize(istr, m_server_ser_ver, false);
1151 block->deSerializeNetworkSpecific(istr);
1157 block = new MapBlock(&m_env.getMap(), p, this);
1158 block->deSerialize(istr, m_server_ser_ver, false);
1159 block->deSerializeNetworkSpecific(istr);
1160 sector->insertBlock(block);
1163 if (localdb != NULL) {
1164 ((ServerMap&) localserver->getMap()).saveBlock(block, localdb);
1168 Add it to mesh update queue and set it to be acknowledged after update.
1170 addUpdateMeshTaskWithEdge(p, true);
1173 void Client::handleCommand_Inventory(ToClientPacket* pkt)
1175 if(pkt->getSize() < 1)
1178 std::string datastring(pkt->getString(0), pkt->getSize());
1179 std::istringstream is(datastring, std::ios_base::binary);
1181 Player *player = m_env.getLocalPlayer();
1182 assert(player != NULL);
1184 player->inventory.deSerialize(is);
1186 m_inventory_updated = true;
1188 delete m_inventory_from_server;
1189 m_inventory_from_server = new Inventory(player->inventory);
1190 m_inventory_from_server_age = 0.0;
1193 void Client::handleCommand_TimeOfDay(ToClientPacket* pkt)
1195 if(pkt->getSize() < 2)
1200 *pkt >> time_of_day;
1202 time_of_day = time_of_day % 24000;
1203 float time_speed = 0;
1205 if(pkt->getSize() >= 2 + 4) {
1209 // Old message; try to approximate speed of time by ourselves
1210 float time_of_day_f = (float)time_of_day / 24000.0;
1211 float tod_diff_f = 0;
1213 if(time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
1214 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0;
1216 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
1218 m_last_time_of_day_f = time_of_day_f;
1219 float time_diff = m_time_of_day_update_timer;
1220 m_time_of_day_update_timer = 0;
1222 if(m_time_of_day_set){
1223 time_speed = (3600.0 * 24.0) * tod_diff_f / time_diff;
1224 infostream << "Client: Measured time_of_day speed (old format): "
1225 << time_speed << " tod_diff_f=" << tod_diff_f
1226 << " time_diff=" << time_diff << std::endl;
1230 // Update environment
1231 m_env.setTimeOfDay(time_of_day);
1232 m_env.setTimeOfDaySpeed(time_speed);
1233 m_time_of_day_set = true;
1235 u32 dr = m_env.getDayNightRatio();
1236 infostream << "Client: time_of_day=" << time_of_day
1237 << " time_speed=" << time_speed
1238 << " dr=" << dr << std::endl;
1241 void Client::handleCommand_ChatMessage(ToClientPacket* pkt)
1248 u16 len, read_wchar;
1252 std::wstring message;
1253 for(unsigned int i=0; i<len; i++) {
1255 message += (wchar_t)read_wchar;
1258 m_chat_queue.push_back(message);
1261 void Client::handleCommand_ActiveObjectRemoveAdd(ToClientPacket* pkt)
1265 u16 count of removed objects
1266 for all removed objects {
1269 u16 count of added objects
1270 for all added objects {
1273 u32 initialization data length
1274 string initialization data
1278 // Read removed objects
1280 u16 removed_count, added_count, id;
1282 *pkt >> removed_count;
1284 for(u16 i=0; i<removed_count; i++) {
1286 m_env.removeActiveObject(id);
1289 // Read added objects
1290 *pkt >> added_count;
1292 for(u16 i=0; i<added_count; i++) {
1294 m_env.addActiveObject(id, type, pkt->readLongString());
1298 void Client::handleCommand_ActiveObjectMessages(ToClientPacket* pkt)
1310 // Get all data except the command number
1311 std::string datastring(pkt->getString(0), pkt->getSize());
1312 // Throw them in an istringstream
1313 std::istringstream is(datastring, std::ios_base::binary);
1315 while(is.eof() == false) {
1317 u16 id = readU16((u8*)buf);
1321 size_t message_size = readU16((u8*)buf);
1322 std::string message;
1323 message.reserve(message_size);
1324 for(unsigned int i=0; i<message_size; i++)
1327 message.append(buf, 1);
1329 // Pass on to the environment
1330 m_env.processActiveObjectMessage(id, message);
1334 void Client::handleCommand_Movement(ToClientPacket* pkt)
1336 Player *player = m_env.getLocalPlayer();
1337 assert(player != NULL);
1339 float mad, maa, maf, msw, mscr, msf, mscl, msj, lf, lfs, ls, g;
1341 *pkt >> mad >> maa >> maf >> msw >> mscr >> msf >> mscl >> msj
1342 >> lf >> lfs >> ls >> g;
1344 player->movement_acceleration_default = mad * BS;
1345 player->movement_acceleration_air = maa * BS;
1346 player->movement_acceleration_fast = maf * BS;
1347 player->movement_speed_walk = msw * BS;
1348 player->movement_speed_crouch = mscr * BS;
1349 player->movement_speed_fast = msf * BS;
1350 player->movement_speed_climb = mscl * BS;
1351 player->movement_speed_jump = msj * BS;
1352 player->movement_liquid_fluidity = lf * BS;
1353 player->movement_liquid_fluidity_smooth = lfs * BS;
1354 player->movement_liquid_sink = ls * BS;
1355 player->movement_gravity = g * BS;
1358 void Client::handleCommand_HP(ToClientPacket* pkt)
1361 Player *player = m_env.getLocalPlayer();
1362 assert(player != NULL);
1364 u8 oldhp = player->hp;
1372 // Add to ClientEvent queue
1374 event.type = CE_PLAYER_DAMAGE;
1375 event.player_damage.amount = oldhp - hp;
1376 m_client_event_queue.push_back(event);
1380 void Client::handleCommand_Breath(ToClientPacket* pkt)
1382 Player *player = m_env.getLocalPlayer();
1383 assert(player != NULL);
1389 player->setBreath(breath);
1392 void Client::handleCommand_MovePlayer(ToClientPacket* pkt)
1394 Player *player = m_env.getLocalPlayer();
1395 assert(player != NULL);
1400 *pkt >> pos >> pitch >> yaw;
1402 player->setPosition(pos);
1404 infostream << "Client got TOCLIENT_MOVE_PLAYER"
1405 << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
1406 << " pitch=" << pitch
1411 Add to ClientEvent queue.
1412 This has to be sent to the main program because otherwise
1413 it would just force the pitch and yaw values to whatever
1414 the camera points to.
1417 event.type = CE_PLAYER_FORCE_MOVE;
1418 event.player_force_move.pitch = pitch;
1419 event.player_force_move.yaw = yaw;
1420 m_client_event_queue.push_back(event);
1422 // Ignore damage for a few seconds, so that the player doesn't
1423 // get damage from falling on ground
1424 m_ignore_damage_timer = 3.0;
1427 void Client::handleCommand_PlayerItem(ToClientPacket* pkt)
1429 infostream << "Client: WARNING: Ignoring TOCLIENT_PLAYERITEM" << std::endl;
1432 void Client::handleCommand_DeathScreen(ToClientPacket* pkt)
1434 bool set_camera_point_target;
1435 v3f camera_point_target;
1437 *pkt >> set_camera_point_target;
1438 *pkt >> camera_point_target;
1441 event.type = CE_DEATHSCREEN;
1442 event.deathscreen.set_camera_point_target = set_camera_point_target;
1443 event.deathscreen.camera_point_target_x = camera_point_target.X;
1444 event.deathscreen.camera_point_target_y = camera_point_target.Y;
1445 event.deathscreen.camera_point_target_z = camera_point_target.Z;
1446 m_client_event_queue.push_back(event);
1449 void Client::handleCommand_AnnounceMedia(ToClientPacket* pkt)
1455 infostream << "Client: Received media announcement: packet size: "
1456 << pkt->getSize() << std::endl;
1458 if (m_media_downloader == NULL ||
1459 m_media_downloader->isStarted()) {
1460 const char *problem = m_media_downloader ?
1461 "we already saw another announcement" :
1462 "all media has been received already";
1463 errorstream << "Client: Received media announcement but "
1465 << " files=" << num_files
1466 << " size=" << pkt->getSize() << std::endl;
1470 // Mesh update thread must be stopped while
1471 // updating content definitions
1472 assert(!m_mesh_update_thread.IsRunning());
1474 for(int i=0; i<num_files; i++) {
1475 std::string name, sha1_base64;
1477 *pkt >> name >> sha1_base64;
1479 std::string sha1_raw = base64_decode(sha1_base64);
1480 m_media_downloader->addFile(name, sha1_raw);
1483 std::vector<std::string> remote_media;
1490 while(!sf.atend()) {
1491 std::string baseurl = trim(sf.next(","));
1493 m_media_downloader->addRemoteServer(baseurl);
1496 catch(SerializationError& e) {
1497 // not supported by server or turned off
1500 m_media_downloader->step(this);
1503 void Client::handleCommand_Media(ToClientPacket* pkt)
1507 u16 total number of file bunches
1508 u16 index of this bunch
1509 u32 number of files in this bunch
1521 *pkt >> num_bunches >> bunch_i >> num_files;
1523 infostream << "Client: Received files: bunch " << bunch_i << "/"
1524 << num_bunches << " files=" << num_files
1525 << " size=" << pkt->getSize() << std::endl;
1530 if (m_media_downloader == NULL ||
1531 !m_media_downloader->isStarted()) {
1532 const char *problem = m_media_downloader ?
1533 "media has not been requested" :
1534 "all media has been received already";
1535 errorstream << "Client: Received media but "
1537 << " bunch " << bunch_i << "/" << num_bunches
1538 << " files=" << num_files
1539 << " size=" << pkt->getSize() << std::endl;
1543 // Mesh update thread must be stopped while
1544 // updating content definitions
1545 assert(!m_mesh_update_thread.IsRunning());
1547 for(unsigned int i=0; i<num_files; i++) {
1552 std::string data = pkt->readLongString();
1554 m_media_downloader->conventionalTransferDone(
1559 void Client::handleCommand_ToolDef(ToClientPacket* pkt)
1561 infostream << "Client: WARNING: Ignoring TOCLIENT_TOOLDEF" << std::endl;
1564 void Client::handleCommand_NodeDef(ToClientPacket* pkt)
1566 infostream << "Client: Received node definitions: packet size: "
1567 << pkt->getSize() << std::endl;
1569 // Mesh update thread must be stopped while
1570 // updating content definitions
1571 assert(!m_mesh_update_thread.IsRunning());
1573 // Decompress node definitions
1574 std::string datastring(pkt->getString(0), pkt->getSize());
1575 std::istringstream is(datastring, std::ios_base::binary);
1576 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1577 std::ostringstream tmp_os;
1578 decompressZlib(tmp_is, tmp_os);
1580 // Deserialize node definitions
1581 std::istringstream tmp_is2(tmp_os.str());
1582 m_nodedef->deSerialize(tmp_is2);
1583 m_nodedef_received = true;
1586 void Client::handleCommand_CraftItemDef(ToClientPacket* pkt)
1588 infostream << "Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF" << std::endl;
1591 void Client::handleCommand_ItemDef(ToClientPacket* pkt)
1593 infostream << "Client: Received item definitions: packet size: "
1594 << pkt->getSize() << std::endl;
1596 // Mesh update thread must be stopped while
1597 // updating content definitions
1598 assert(!m_mesh_update_thread.IsRunning());
1600 // Decompress item definitions
1601 std::string datastring(pkt->getString(0), pkt->getSize());
1602 std::istringstream is(datastring, std::ios_base::binary);
1603 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1604 std::ostringstream tmp_os;
1605 decompressZlib(tmp_is, tmp_os);
1607 // Deserialize node definitions
1608 std::istringstream tmp_is2(tmp_os.str());
1609 m_itemdef->deSerialize(tmp_is2);
1610 m_itemdef_received = true;
1613 void Client::handleCommand_PlaySound(ToClientPacket* pkt)
1618 u8 type; // 0=local, 1=positional, 2=object
1623 *pkt >> server_id >> name >> gain >> type >> pos >> object_id >> loop;
1629 client_id = m_sound->playSound(name, loop, gain);
1631 case 1: // positional
1632 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1636 ClientActiveObject *cao = m_env.getActiveObject(object_id);
1638 pos = cao->getPosition();
1639 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1640 // TODO: Set up sound to move with object
1647 if(client_id != -1) {
1648 m_sounds_server_to_client[server_id] = client_id;
1649 m_sounds_client_to_server[client_id] = server_id;
1651 m_sounds_to_objects[client_id] = object_id;
1655 void Client::handleCommand_StopSound(ToClientPacket* pkt)
1661 std::map<s32, int>::iterator i =
1662 m_sounds_server_to_client.find(server_id);
1664 if(i != m_sounds_server_to_client.end()) {
1665 int client_id = i->second;
1666 m_sound->stopSound(client_id);
1670 void Client::handleCommand_Privileges(ToClientPacket* pkt)
1672 m_privileges.clear();
1673 infostream << "Client: Privileges updated: ";
1676 *pkt >> num_privileges;
1678 for(unsigned int i=0; i<num_privileges; i++) {
1683 m_privileges.insert(priv);
1684 infostream << priv << " ";
1686 infostream << std::endl;
1689 void Client::handleCommand_InventoryFormSpec(ToClientPacket* pkt)
1691 Player *player = m_env.getLocalPlayer();
1692 assert(player != NULL);
1694 // Store formspec in LocalPlayer
1695 player->inventory_formspec = pkt->readLongString();
1698 void Client::handleCommand_DetachedInventory(ToClientPacket* pkt)
1700 std::string datastring(pkt->getString(0), pkt->getSize());
1701 std::istringstream is(datastring, std::ios_base::binary);
1703 std::string name = deSerializeString(is);
1705 infostream << "Client: Detached inventory update: \"" << name
1706 << "\"" << std::endl;
1708 Inventory *inv = NULL;
1709 if(m_detached_inventories.count(name) > 0)
1710 inv = m_detached_inventories[name];
1712 inv = new Inventory(m_itemdef);
1713 m_detached_inventories[name] = inv;
1715 inv->deSerialize(is);
1718 void Client::handleCommand_ShowFormSpec(ToClientPacket* pkt)
1720 std::string formspec = pkt->readLongString();
1721 std::string formname;
1726 event.type = CE_SHOW_FORMSPEC;
1727 // pointer is required as event is a struct only!
1728 // adding a std:string to a struct isn't possible
1729 event.show_formspec.formspec = new std::string(formspec);
1730 event.show_formspec.formname = new std::string(formname);
1731 m_client_event_queue.push_back(event);
1734 void Client::handleCommand_SpawnParticle(ToClientPacket* pkt)
1736 std::string datastring(pkt->getString(0), pkt->getSize());
1737 std::istringstream is(datastring, std::ios_base::binary);
1739 v3f pos = readV3F1000(is);
1740 v3f vel = readV3F1000(is);
1741 v3f acc = readV3F1000(is);
1742 float expirationtime = readF1000(is);
1743 float size = readF1000(is);
1744 bool collisiondetection = readU8(is);
1745 std::string texture = deSerializeLongString(is);
1746 bool vertical = false;
1748 vertical = readU8(is);
1752 event.type = CE_SPAWN_PARTICLE;
1753 event.spawn_particle.pos = new v3f (pos);
1754 event.spawn_particle.vel = new v3f (vel);
1755 event.spawn_particle.acc = new v3f (acc);
1756 event.spawn_particle.expirationtime = expirationtime;
1757 event.spawn_particle.size = size;
1758 event.spawn_particle.collisiondetection = collisiondetection;
1759 event.spawn_particle.vertical = vertical;
1760 event.spawn_particle.texture = new std::string(texture);
1762 m_client_event_queue.push_back(event);
1765 void Client::handleCommand_AddParticleSpawner(ToClientPacket* pkt)
1779 bool collisiondetection;
1782 *pkt >> amount >> spawntime >> minpos >> maxpos >> minvel >> maxvel
1783 >> minacc >> maxacc >> minexptime >> maxexptime >> minsize
1784 >> maxsize >> collisiondetection;
1786 std::string texture = pkt->readLongString();
1790 bool vertical = false;
1796 event.type = CE_ADD_PARTICLESPAWNER;
1797 event.add_particlespawner.amount = amount;
1798 event.add_particlespawner.spawntime = spawntime;
1799 event.add_particlespawner.minpos = new v3f (minpos);
1800 event.add_particlespawner.maxpos = new v3f (maxpos);
1801 event.add_particlespawner.minvel = new v3f (minvel);
1802 event.add_particlespawner.maxvel = new v3f (maxvel);
1803 event.add_particlespawner.minacc = new v3f (minacc);
1804 event.add_particlespawner.maxacc = new v3f (maxacc);
1805 event.add_particlespawner.minexptime = minexptime;
1806 event.add_particlespawner.maxexptime = maxexptime;
1807 event.add_particlespawner.minsize = minsize;
1808 event.add_particlespawner.maxsize = maxsize;
1809 event.add_particlespawner.collisiondetection = collisiondetection;
1810 event.add_particlespawner.vertical = vertical;
1811 event.add_particlespawner.texture = new std::string(texture);
1812 event.add_particlespawner.id = id;
1814 m_client_event_queue.push_back(event);
1817 void Client::handleCommand_DeleteParticleSpawner(ToClientPacket* pkt)
1824 event.type = CE_DELETE_PARTICLESPAWNER;
1825 event.delete_particlespawner.id = id;
1827 m_client_event_queue.push_back(event);
1830 void Client::handleCommand_HudAdd(ToClientPacket* pkt)
1832 std::string datastring(pkt->getString(0), pkt->getSize());
1833 std::istringstream is(datastring, std::ios_base::binary);
1849 *pkt >> id >> type >> pos >> name >> scale >> text >> number >> item
1850 >> dir >> align >> offset;
1854 catch(SerializationError &e) {};
1858 } catch(SerializationError &e) {};
1861 event.type = CE_HUDADD;
1862 event.hudadd.id = id;
1863 event.hudadd.type = type;
1864 event.hudadd.pos = new v2f(pos);
1865 event.hudadd.name = new std::string(name);
1866 event.hudadd.scale = new v2f(scale);
1867 event.hudadd.text = new std::string(text);
1868 event.hudadd.number = number;
1869 event.hudadd.item = item;
1870 event.hudadd.dir = dir;
1871 event.hudadd.align = new v2f(align);
1872 event.hudadd.offset = new v2f(offset);
1873 event.hudadd.world_pos = new v3f(world_pos);
1874 event.hudadd.size = new v2s32(size);
1875 m_client_event_queue.push_back(event);
1878 void Client::handleCommand_HudRemove(ToClientPacket* pkt)
1885 event.type = CE_HUDRM;
1886 event.hudrm.id = id;
1887 m_client_event_queue.push_back(event);
1890 void Client::handleCommand_HudChange(ToClientPacket* pkt)
1902 if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
1903 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
1905 else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
1907 else if (stat == HUD_STAT_WORLD_POS)
1909 else if (stat == HUD_STAT_SIZE )
1915 event.type = CE_HUDCHANGE;
1916 event.hudchange.id = id;
1917 event.hudchange.stat = (HudElementStat)stat;
1918 event.hudchange.v2fdata = new v2f(v2fdata);
1919 event.hudchange.v3fdata = new v3f(v3fdata);
1920 event.hudchange.sdata = new std::string(sdata);
1921 event.hudchange.data = intdata;
1922 event.hudchange.v2s32data = new v2s32(v2s32data);
1923 m_client_event_queue.push_back(event);
1926 void Client::handleCommand_HudSetFlags(ToClientPacket* pkt)
1930 *pkt >> flags >> mask;
1932 Player *player = m_env.getLocalPlayer();
1933 assert(player != NULL);
1935 player->hud_flags &= ~mask;
1936 player->hud_flags |= flags;
1939 void Client::handleCommand_HudSetParam(ToClientPacket* pkt)
1941 u16 param; std::string value;
1943 *pkt >> param >> value;
1945 Player *player = m_env.getLocalPlayer();
1946 assert(player != NULL);
1948 if(param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) {
1949 s32 hotbar_itemcount = readS32((u8*) value.c_str());
1950 if(hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
1951 player->hud_hotbar_itemcount = hotbar_itemcount;
1953 else if (param == HUD_PARAM_HOTBAR_IMAGE) {
1954 ((LocalPlayer *) player)->hotbar_image = value;
1956 else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
1957 ((LocalPlayer *) player)->hotbar_selected_image = value;
1961 void Client::handleCommand_HudSetSky(ToClientPacket* pkt)
1963 std::string datastring(pkt->getString(0), pkt->getSize());
1964 std::istringstream is(datastring, std::ios_base::binary);
1966 video::SColor *bgcolor = new video::SColor(readARGB8(is));
1967 std::string *type = new std::string(deSerializeString(is));
1968 u16 count = readU16(is);
1969 std::vector<std::string> *params = new std::vector<std::string>;
1971 for(size_t i=0; i<count; i++)
1972 params->push_back(deSerializeString(is));
1975 event.type = CE_SET_SKY;
1976 event.set_sky.bgcolor = bgcolor;
1977 event.set_sky.type = type;
1978 event.set_sky.params = params;
1979 m_client_event_queue.push_back(event);
1982 void Client::handleCommand_OverrideDayNightRatio(ToClientPacket* pkt)
1985 u16 day_night_ratio_u;
1987 *pkt >> do_override >> day_night_ratio_u;
1989 float day_night_ratio_f = (float)day_night_ratio_u / 65536;
1992 event.type = CE_OVERRIDE_DAY_NIGHT_RATIO;
1993 event.override_day_night_ratio.do_override = do_override;
1994 event.override_day_night_ratio.ratio_f = day_night_ratio_f;
1995 m_client_event_queue.push_back(event);
1998 void Client::handleCommand_LocalPlayerAnimations(ToClientPacket* pkt)
2000 LocalPlayer *player = m_env.getLocalPlayer();
2001 assert(player != NULL);
2003 *pkt >> player->local_animations[0];
2004 *pkt >> player->local_animations[1];
2005 *pkt >> player->local_animations[2];
2006 *pkt >> player->local_animations[3];
2007 *pkt >> player->local_animation_speed;
2010 void Client::handleCommand_EyeOffset(ToClientPacket* pkt)
2012 LocalPlayer *player = m_env.getLocalPlayer();
2013 assert(player != NULL);
2015 *pkt >> player->eye_offset_first >> player->eye_offset_third;
2018 inline void Client::handleCommand(ToClientPacket* pkt)
2020 const ToClientCommandHandler& opHandle = toClientCommandTable[pkt->getCommand()];
2021 (this->*opHandle.handler)(pkt);
2025 sender_peer_id given to this shall be quaranteed to be a valid peer
2027 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
2029 DSTACK(__FUNCTION_NAME);
2031 // Ignore packets that don't even fit a command
2033 m_packetcounter.add(60000);
2037 ToClientPacket* pkt = new ToClientPacket(data, datasize, sender_peer_id);
2039 ToClientCommand command = pkt->getCommand();
2041 //infostream<<"Client: received command="<<command<<std::endl;
2042 m_packetcounter.add((u16)command);
2045 If this check is removed, be sure to change the queue
2046 system to know the ids
2048 if(sender_peer_id != PEER_ID_SERVER) {
2049 infostream << "Client::ProcessData(): Discarding data not "
2050 "coming from server: peer_id=" << sender_peer_id
2056 // Command must be handled into ToClientCommandHandler
2057 if (command >= TOCLIENT_NUM_MSG_TYPES) {
2058 infostream << "Client: Ignoring unknown command "
2059 << command << std::endl;
2063 * Those packets are handled before m_server_ser_ver is set, it's normal
2064 * But we must use the new ToClientConnectionState in the future,
2067 if(toClientCommandTable[command].state == TOCLIENT_STATE_NOT_CONNECTED) {
2073 if(m_server_ser_ver == SER_FMT_VER_INVALID) {
2074 infostream << "Client: Server serialization"
2075 " format invalid or not initialized."
2076 " Skipping incoming command=" << command << std::endl;
2082 Handle runtime commands
2089 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
2091 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2092 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
2095 void Client::interact(u8 action, const PointedThing& pointed)
2097 if(m_state != LC_Ready){
2098 infostream<<"Client::interact() "
2099 "cancelled (not connected)"
2104 std::ostringstream os(std::ios_base::binary);
2110 [5] u32 length of the next item
2111 [9] serialized PointedThing
2113 0: start digging (from undersurface) or use
2114 1: stop digging (all parameters ignored)
2115 2: digging completed
2116 3: place block or item (to abovesurface)
2119 writeU16(os, TOSERVER_INTERACT);
2120 writeU8(os, action);
2121 writeU16(os, getPlayerItem());
2122 std::ostringstream tmp_os(std::ios::binary);
2123 pointed.serialize(tmp_os);
2124 os<<serializeLongString(tmp_os.str());
2126 std::string s = os.str();
2127 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2130 Send(0, data, true);
2133 void Client::sendNodemetaFields(v3s16 p, const std::string &formname,
2134 const std::map<std::string, std::string> &fields)
2136 std::ostringstream os(std::ios_base::binary);
2138 writeU16(os, TOSERVER_NODEMETA_FIELDS);
2140 os<<serializeString(formname);
2141 size_t fields_size = fields.size();
2142 assert(fields_size <= 0xFFFF);
2143 writeU16(os, (u16) (fields_size & 0xFFFF));
2144 for(std::map<std::string, std::string>::const_iterator
2145 i = fields.begin(); i != fields.end(); i++){
2146 const std::string &name = i->first;
2147 const std::string &value = i->second;
2148 os<<serializeString(name);
2149 os<<serializeLongString(value);
2153 std::string s = os.str();
2154 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2156 Send(0, data, true);
2159 void Client::sendInventoryFields(const std::string &formname,
2160 const std::map<std::string, std::string> &fields)
2162 std::ostringstream os(std::ios_base::binary);
2164 writeU16(os, TOSERVER_INVENTORY_FIELDS);
2165 os<<serializeString(formname);
2166 size_t fields_size = fields.size();
2167 assert(fields_size <= 0xFFFF);
2168 writeU16(os, (u16) (fields_size & 0xFFFF));
2169 for(std::map<std::string, std::string>::const_iterator
2170 i = fields.begin(); i != fields.end(); i++){
2171 const std::string &name = i->first;
2172 const std::string &value = i->second;
2173 os<<serializeString(name);
2174 os<<serializeLongString(value);
2178 std::string s = os.str();
2179 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2181 Send(0, data, true);
2184 void Client::sendInventoryAction(InventoryAction *a)
2186 std::ostringstream os(std::ios_base::binary);
2190 writeU16(buf, TOSERVER_INVENTORY_ACTION);
2191 os.write((char*)buf, 2);
2196 std::string s = os.str();
2197 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2199 Send(0, data, true);
2202 void Client::sendChatMessage(const std::wstring &message)
2204 std::ostringstream os(std::ios_base::binary);
2208 writeU16(buf, TOSERVER_CHAT_MESSAGE);
2209 os.write((char*)buf, 2);
2212 size_t messagesize = message.size();
2213 if (messagesize > 0xFFFF) {
2214 messagesize = 0xFFFF;
2216 writeU16(buf, (u16) messagesize);
2217 os.write((char*)buf, 2);
2220 for(unsigned int i=0; i<message.size(); i++)
2224 os.write((char*)buf, 2);
2228 std::string s = os.str();
2229 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2231 Send(0, data, true);
2234 void Client::sendChangePassword(const std::wstring &oldpassword,
2235 const std::wstring &newpassword)
2237 Player *player = m_env.getLocalPlayer();
2241 std::string playername = player->getName();
2242 std::string oldpwd = translatePassword(playername, oldpassword);
2243 std::string newpwd = translatePassword(playername, newpassword);
2245 std::ostringstream os(std::ios_base::binary);
2246 u8 buf[2+PASSWORD_SIZE*2];
2248 [0] u16 TOSERVER_PASSWORD
2249 [2] u8[28] old password
2250 [30] u8[28] new password
2253 writeU16(buf, TOSERVER_PASSWORD);
2254 for(unsigned int i=0;i<PASSWORD_SIZE-1;i++)
2256 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
2257 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
2259 buf[2+PASSWORD_SIZE-1] = 0;
2260 buf[30+PASSWORD_SIZE-1] = 0;
2261 os.write((char*)buf, 2+PASSWORD_SIZE*2);
2264 std::string s = os.str();
2265 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2267 Send(0, data, true);
2271 void Client::sendDamage(u8 damage)
2273 DSTACK(__FUNCTION_NAME);
2274 std::ostringstream os(std::ios_base::binary);
2276 writeU16(os, TOSERVER_DAMAGE);
2277 writeU8(os, damage);
2280 std::string s = os.str();
2281 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2283 Send(0, data, true);
2286 void Client::sendBreath(u16 breath)
2288 DSTACK(__FUNCTION_NAME);
2289 std::ostringstream os(std::ios_base::binary);
2291 writeU16(os, TOSERVER_BREATH);
2292 writeU16(os, breath);
2294 std::string s = os.str();
2295 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2297 Send(0, data, true);
2300 void Client::sendRespawn()
2302 DSTACK(__FUNCTION_NAME);
2303 std::ostringstream os(std::ios_base::binary);
2305 writeU16(os, TOSERVER_RESPAWN);
2308 std::string s = os.str();
2309 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2311 Send(0, data, true);
2314 void Client::sendReady()
2316 DSTACK(__FUNCTION_NAME);
2317 std::ostringstream os(std::ios_base::binary);
2319 writeU16(os, TOSERVER_CLIENT_READY);
2320 writeU8(os,VERSION_MAJOR);
2321 writeU8(os,VERSION_MINOR);
2322 writeU8(os,VERSION_PATCH_ORIG);
2325 writeU16(os,strlen(minetest_version_hash));
2326 os.write(minetest_version_hash,strlen(minetest_version_hash));
2329 std::string s = os.str();
2330 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2332 Send(0, data, true);
2335 void Client::sendPlayerPos()
2337 LocalPlayer *myplayer = m_env.getLocalPlayer();
2338 if(myplayer == NULL)
2341 // Save bandwidth by only updating position when something changed
2342 if(myplayer->last_position == myplayer->getPosition() &&
2343 myplayer->last_speed == myplayer->getSpeed() &&
2344 myplayer->last_pitch == myplayer->getPitch() &&
2345 myplayer->last_yaw == myplayer->getYaw() &&
2346 myplayer->last_keyPressed == myplayer->keyPressed)
2349 myplayer->last_position = myplayer->getPosition();
2350 myplayer->last_speed = myplayer->getSpeed();
2351 myplayer->last_pitch = myplayer->getPitch();
2352 myplayer->last_yaw = myplayer->getYaw();
2353 myplayer->last_keyPressed = myplayer->keyPressed;
2357 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2358 our_peer_id = m_con.GetPeerID();
2361 // Set peer id if not set already
2362 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2363 myplayer->peer_id = our_peer_id;
2364 // Check that an existing peer_id is the same as the connection's
2365 assert(myplayer->peer_id == our_peer_id);
2367 v3f pf = myplayer->getPosition();
2368 v3f sf = myplayer->getSpeed();
2369 s32 pitch = myplayer->getPitch() * 100;
2370 s32 yaw = myplayer->getYaw() * 100;
2371 u32 keyPressed = myplayer->keyPressed;
2373 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
2374 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
2378 [2] v3s32 position*100
2379 [2+12] v3s32 speed*100
2380 [2+12+12] s32 pitch*100
2381 [2+12+12+4] s32 yaw*100
2382 [2+12+12+4+4] u32 keyPressed
2384 SharedBuffer<u8> data(2+12+12+4+4+4);
2385 writeU16(&data[0], TOSERVER_PLAYERPOS);
2386 writeV3S32(&data[2], position);
2387 writeV3S32(&data[2+12], speed);
2388 writeS32(&data[2+12+12], pitch);
2389 writeS32(&data[2+12+12+4], yaw);
2390 writeU32(&data[2+12+12+4+4], keyPressed);
2391 // Send as unreliable
2392 Send(0, data, false);
2395 void Client::sendPlayerItem(u16 item)
2397 Player *myplayer = m_env.getLocalPlayer();
2398 if(myplayer == NULL)
2401 u16 our_peer_id = m_con.GetPeerID();
2403 // Set peer id if not set already
2404 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2405 myplayer->peer_id = our_peer_id;
2406 // Check that an existing peer_id is the same as the connection's
2407 assert(myplayer->peer_id == our_peer_id);
2409 SharedBuffer<u8> data(2+2);
2410 writeU16(&data[0], TOSERVER_PLAYERITEM);
2411 writeU16(&data[2], item);
2414 Send(0, data, true);
2417 void Client::removeNode(v3s16 p)
2419 std::map<v3s16, MapBlock*> modified_blocks;
2423 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
2425 catch(InvalidPositionException &e)
2429 for(std::map<v3s16, MapBlock * >::iterator
2430 i = modified_blocks.begin();
2431 i != modified_blocks.end(); ++i)
2433 addUpdateMeshTaskWithEdge(i->first, false, true);
2437 void Client::addNode(v3s16 p, MapNode n, bool remove_metadata)
2439 //TimeTaker timer1("Client::addNode()");
2441 std::map<v3s16, MapBlock*> modified_blocks;
2445 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
2446 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
2448 catch(InvalidPositionException &e)
2451 for(std::map<v3s16, MapBlock * >::iterator
2452 i = modified_blocks.begin();
2453 i != modified_blocks.end(); ++i)
2455 addUpdateMeshTaskWithEdge(i->first, false, true);
2459 void Client::setPlayerControl(PlayerControl &control)
2461 LocalPlayer *player = m_env.getLocalPlayer();
2462 assert(player != NULL);
2463 player->control = control;
2466 void Client::selectPlayerItem(u16 item)
2468 m_playeritem = item;
2469 m_inventory_updated = true;
2470 sendPlayerItem(item);
2473 // Returns true if the inventory of the local player has been
2474 // updated from the server. If it is true, it is set to false.
2475 bool Client::getLocalInventoryUpdated()
2477 bool updated = m_inventory_updated;
2478 m_inventory_updated = false;
2482 // Copies the inventory of the local player to parameter
2483 void Client::getLocalInventory(Inventory &dst)
2485 Player *player = m_env.getLocalPlayer();
2486 assert(player != NULL);
2487 dst = player->inventory;
2490 Inventory* Client::getInventory(const InventoryLocation &loc)
2493 case InventoryLocation::UNDEFINED:
2496 case InventoryLocation::CURRENT_PLAYER:
2498 Player *player = m_env.getLocalPlayer();
2499 assert(player != NULL);
2500 return &player->inventory;
2503 case InventoryLocation::PLAYER:
2505 Player *player = m_env.getPlayer(loc.name.c_str());
2508 return &player->inventory;
2511 case InventoryLocation::NODEMETA:
2513 NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p);
2516 return meta->getInventory();
2519 case InventoryLocation::DETACHED:
2521 if(m_detached_inventories.count(loc.name) == 0)
2523 return m_detached_inventories[loc.name];
2532 void Client::inventoryAction(InventoryAction *a)
2535 Send it to the server
2537 sendInventoryAction(a);
2540 Predict some local inventory changes
2542 a->clientApply(this, this);
2548 ClientActiveObject * Client::getSelectedActiveObject(
2550 v3f from_pos_f_on_map,
2551 core::line3d<f32> shootline_on_map
2554 std::vector<DistanceSortedActiveObject> objects;
2556 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
2559 // After this, the closest object is the first in the array.
2560 std::sort(objects.begin(), objects.end());
2562 for(unsigned int i=0; i<objects.size(); i++)
2564 ClientActiveObject *obj = objects[i].obj;
2566 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
2567 if(selection_box == NULL)
2570 v3f pos = obj->getPosition();
2572 core::aabbox3d<f32> offsetted_box(
2573 selection_box->MinEdge + pos,
2574 selection_box->MaxEdge + pos
2577 if(offsetted_box.intersectsWithLine(shootline_on_map))
2586 std::list<std::string> Client::getConnectedPlayerNames()
2588 return m_env.getPlayerNames();
2591 float Client::getAnimationTime()
2593 return m_animation_time;
2596 int Client::getCrackLevel()
2598 return m_crack_level;
2601 void Client::setHighlighted(v3s16 pos, bool show_highlighted)
2603 m_show_highlighted = show_highlighted;
2604 v3s16 old_highlighted_pos = m_highlighted_pos;
2605 m_highlighted_pos = pos;
2606 addUpdateMeshTaskForNode(old_highlighted_pos, false, true);
2607 addUpdateMeshTaskForNode(m_highlighted_pos, false, true);
2610 void Client::setCrack(int level, v3s16 pos)
2612 int old_crack_level = m_crack_level;
2613 v3s16 old_crack_pos = m_crack_pos;
2615 m_crack_level = level;
2618 if(old_crack_level >= 0 && (level < 0 || pos != old_crack_pos))
2621 addUpdateMeshTaskForNode(old_crack_pos, false, true);
2623 if(level >= 0 && (old_crack_level < 0 || pos != old_crack_pos))
2626 addUpdateMeshTaskForNode(pos, false, true);
2632 Player *player = m_env.getLocalPlayer();
2633 assert(player != NULL);
2637 u16 Client::getBreath()
2639 Player *player = m_env.getLocalPlayer();
2640 assert(player != NULL);
2641 return player->getBreath();
2644 bool Client::getChatMessage(std::wstring &message)
2646 if(m_chat_queue.size() == 0)
2648 message = m_chat_queue.pop_front();
2652 void Client::typeChatMessage(const std::wstring &message)
2654 // Discard empty line
2659 sendChatMessage(message);
2662 if (message[0] == L'/')
2664 m_chat_queue.push_back((std::wstring)L"issued command: " + message);
2668 LocalPlayer *player = m_env.getLocalPlayer();
2669 assert(player != NULL);
2670 std::wstring name = narrow_to_wide(player->getName());
2671 m_chat_queue.push_back((std::wstring)L"<" + name + L"> " + message);
2675 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
2677 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2682 Create a task to update the mesh of the block
2685 MeshMakeData *data = new MeshMakeData(this, m_cache_enable_shaders);
2688 //TimeTaker timer("data fill");
2690 // Debug: 1-6ms, avg=2ms
2692 data->setCrack(m_crack_level, m_crack_pos);
2693 data->setHighlighted(m_highlighted_pos, m_show_highlighted);
2694 data->setSmoothLighting(m_cache_smooth_lighting);
2697 // Add task to queue
2698 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server, urgent);
2701 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent)
2704 addUpdateMeshTask(blockpos, ack_to_server, urgent);
2706 catch(InvalidPositionException &e){}
2709 for (int i=0;i<6;i++)
2712 v3s16 p = blockpos + g_6dirs[i];
2713 addUpdateMeshTask(p, false, urgent);
2715 catch(InvalidPositionException &e){}
2719 void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool urgent)
2723 infostream<<"Client::addUpdateMeshTaskForNode(): "
2724 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2728 v3s16 blockpos = getNodeBlockPos(nodepos);
2729 v3s16 blockpos_relative = blockpos * MAP_BLOCKSIZE;
2732 addUpdateMeshTask(blockpos, ack_to_server, urgent);
2734 catch(InvalidPositionException &e){}
2737 if(nodepos.X == blockpos_relative.X){
2739 v3s16 p = blockpos + v3s16(-1,0,0);
2740 addUpdateMeshTask(p, false, urgent);
2742 catch(InvalidPositionException &e){}
2745 if(nodepos.Y == blockpos_relative.Y){
2747 v3s16 p = blockpos + v3s16(0,-1,0);
2748 addUpdateMeshTask(p, false, urgent);
2750 catch(InvalidPositionException &e){}
2753 if(nodepos.Z == blockpos_relative.Z){
2755 v3s16 p = blockpos + v3s16(0,0,-1);
2756 addUpdateMeshTask(p, false, urgent);
2758 catch(InvalidPositionException &e){}
2762 ClientEvent Client::getClientEvent()
2764 if(m_client_event_queue.size() == 0)
2767 event.type = CE_NONE;
2770 return m_client_event_queue.pop_front();
2773 float Client::mediaReceiveProgress()
2775 if (m_media_downloader)
2776 return m_media_downloader->getProgress();
2778 return 1.0; // downloader only exists when not yet done
2781 void Client::afterContentReceived(IrrlichtDevice *device, gui::IGUIFont* font)
2783 infostream<<"Client::afterContentReceived() started"<<std::endl;
2784 assert(m_itemdef_received);
2785 assert(m_nodedef_received);
2786 assert(mediaReceived());
2788 const wchar_t* text = wgettext("Loading textures...");
2790 // Rebuild inherited images and recreate textures
2791 infostream<<"- Rebuilding images and textures"<<std::endl;
2792 draw_load_screen(text,device, guienv, 0, 70);
2793 m_tsrc->rebuildImagesAndTextures();
2797 infostream<<"- Rebuilding shaders"<<std::endl;
2798 text = wgettext("Rebuilding shaders...");
2799 draw_load_screen(text, device, guienv, 0, 75);
2800 m_shsrc->rebuildShaders();
2803 // Update node aliases
2804 infostream<<"- Updating node aliases"<<std::endl;
2805 text = wgettext("Initializing nodes...");
2806 draw_load_screen(text, device, guienv, 0, 80);
2807 m_nodedef->updateAliases(m_itemdef);
2808 m_nodedef->setNodeRegistrationStatus(true);
2809 m_nodedef->runNodeResolverCallbacks();
2812 // Update node textures and assign shaders to each tile
2813 infostream<<"- Updating node textures"<<std::endl;
2814 m_nodedef->updateTextures(this);
2816 // Preload item textures and meshes if configured to
2817 if(g_settings->getBool("preload_item_visuals"))
2819 verbosestream<<"Updating item textures and meshes"<<std::endl;
2820 text = wgettext("Item textures...");
2821 draw_load_screen(text, device, guienv, 0, 0);
2822 std::set<std::string> names = m_itemdef->getAll();
2823 size_t size = names.size();
2826 for(std::set<std::string>::const_iterator
2827 i = names.begin(); i != names.end(); ++i)
2829 // Asking for these caches the result
2830 m_itemdef->getInventoryTexture(*i, this);
2831 m_itemdef->getWieldMesh(*i, this);
2833 percent = (count * 100 / size * 0.2) + 80;
2834 draw_load_screen(text, device, guienv, 0, percent);
2839 // Start mesh update thread after setting up content definitions
2840 infostream<<"- Starting mesh update thread"<<std::endl;
2841 m_mesh_update_thread.Start();
2845 text = wgettext("Done!");
2846 draw_load_screen(text, device, guienv, 0, 100);
2847 infostream<<"Client::afterContentReceived() done"<<std::endl;
2851 float Client::getRTT(void)
2853 return m_con.getPeerStat(PEER_ID_SERVER,con::AVG_RTT);
2856 float Client::getCurRate(void)
2858 return ( m_con.getLocalStat(con::CUR_INC_RATE) +
2859 m_con.getLocalStat(con::CUR_DL_RATE));
2862 float Client::getAvgRate(void)
2864 return ( m_con.getLocalStat(con::AVG_INC_RATE) +
2865 m_con.getLocalStat(con::AVG_DL_RATE));
2868 void Client::makeScreenshot(IrrlichtDevice *device)
2870 irr::video::IVideoDriver *driver = device->getVideoDriver();
2871 irr::video::IImage* const raw_image = driver->createScreenShot();
2873 irr::video::IImage* const image = driver->createImage(video::ECF_R8G8B8,
2874 raw_image->getDimension());
2877 raw_image->copyTo(image);
2878 irr::c8 filename[256];
2879 snprintf(filename, sizeof(filename), "%s" DIR_DELIM "screenshot_%u.png",
2880 g_settings->get("screenshot_path").c_str(),
2881 device->getTimer()->getRealTime());
2882 std::ostringstream sstr;
2883 if (driver->writeImageToFile(image, filename)) {
2884 sstr << "Saved screenshot to '" << filename << "'";
2886 sstr << "Failed to save screenshot '" << filename << "'";
2888 m_chat_queue.push_back(narrow_to_wide(sstr.str()));
2889 infostream << sstr.str() << std::endl;
2896 // IGameDef interface
2898 IItemDefManager* Client::getItemDefManager()
2902 INodeDefManager* Client::getNodeDefManager()
2906 ICraftDefManager* Client::getCraftDefManager()
2909 //return m_craftdef;
2911 ITextureSource* Client::getTextureSource()
2915 IShaderSource* Client::getShaderSource()
2919 scene::ISceneManager* Client::getSceneManager()
2921 return m_device->getSceneManager();
2923 u16 Client::allocateUnknownNodeId(const std::string &name)
2925 errorstream<<"Client::allocateUnknownNodeId(): "
2926 <<"Client cannot allocate node IDs"<<std::endl;
2928 return CONTENT_IGNORE;
2930 ISoundManager* Client::getSoundManager()
2934 MtEventManager* Client::getEventManager()
2939 ParticleManager* Client::getParticleManager()
2941 return &m_particle_manager;
2944 scene::IAnimatedMesh* Client::getMesh(const std::string &filename)
2946 std::map<std::string, std::string>::const_iterator i =
2947 m_mesh_data.find(filename);
2948 if(i == m_mesh_data.end()){
2949 errorstream<<"Client::getMesh(): Mesh not found: \""<<filename<<"\""
2953 const std::string &data = i->second;
2954 scene::ISceneManager *smgr = m_device->getSceneManager();
2956 // Create the mesh, remove it from cache and return it
2957 // This allows unique vertex colors and other properties for each instance
2958 Buffer<char> data_rw(data.c_str(), data.size()); // Const-incorrect Irrlicht
2959 io::IFileSystem *irrfs = m_device->getFileSystem();
2960 io::IReadFile *rfile = irrfs->createMemoryReadFile(
2961 *data_rw, data_rw.getSize(), filename.c_str());
2964 scene::IAnimatedMesh *mesh = smgr->getMesh(rfile);
2966 // NOTE: By playing with Irrlicht refcounts, maybe we could cache a bunch
2967 // of uniquely named instances and re-use them
2969 smgr->getMeshCache()->removeMesh(mesh);