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 "clientserver.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"
56 extern gui::IGUIEnvironment* guienv;
62 QueuedMeshUpdate::QueuedMeshUpdate():
65 ack_block_to_server(false)
69 QueuedMeshUpdate::~QueuedMeshUpdate()
79 MeshUpdateQueue::MeshUpdateQueue()
83 MeshUpdateQueue::~MeshUpdateQueue()
85 JMutexAutoLock lock(m_mutex);
87 for(std::vector<QueuedMeshUpdate*>::iterator
89 i != m_queue.end(); i++)
91 QueuedMeshUpdate *q = *i;
97 peer_id=0 adds with nobody to send to
99 void MeshUpdateQueue::addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_server, bool urgent)
101 DSTACK(__FUNCTION_NAME);
105 JMutexAutoLock lock(m_mutex);
111 Find if block is already in queue.
112 If it is, update the data and quit.
114 for(std::vector<QueuedMeshUpdate*>::iterator
116 i != m_queue.end(); i++)
118 QueuedMeshUpdate *q = *i;
124 if(ack_block_to_server)
125 q->ack_block_to_server = true;
133 QueuedMeshUpdate *q = new QueuedMeshUpdate;
136 q->ack_block_to_server = ack_block_to_server;
137 m_queue.push_back(q);
140 // Returned pointer must be deleted
141 // Returns NULL if queue is empty
142 QueuedMeshUpdate * MeshUpdateQueue::pop()
144 JMutexAutoLock lock(m_mutex);
146 bool must_be_urgent = !m_urgents.empty();
147 for(std::vector<QueuedMeshUpdate*>::iterator
149 i != m_queue.end(); i++)
151 QueuedMeshUpdate *q = *i;
152 if(must_be_urgent && m_urgents.count(q->p) == 0)
155 m_urgents.erase(q->p);
165 void * MeshUpdateThread::Thread()
169 log_register_thread("MeshUpdateThread");
171 DSTACK(__FUNCTION_NAME);
173 BEGIN_DEBUG_EXCEPTION_HANDLER
175 porting::setThreadName("MeshUpdateThread");
177 while(!StopRequested())
179 QueuedMeshUpdate *q = m_queue_in.pop();
186 ScopeProfiler sp(g_profiler, "Client: Mesh making");
188 MapBlockMesh *mesh_new = new MapBlockMesh(q->data, m_camera_offset);
189 if(mesh_new->getMesh()->getMeshBufferCount() == 0)
198 r.ack_block_to_server = q->ack_block_to_server;
200 m_queue_out.push_back(r);
205 END_DEBUG_EXCEPTION_HANDLER(errorstream)
215 IrrlichtDevice *device,
216 const char *playername,
217 std::string password,
218 MapDrawControl &control,
219 IWritableTextureSource *tsrc,
220 IWritableShaderSource *shsrc,
221 IWritableItemDefManager *itemdef,
222 IWritableNodeDefManager *nodedef,
223 ISoundManager *sound,
224 MtEventManager *event,
227 m_packetcounter_timer(0.0),
228 m_connection_reinit_timer(0.1),
229 m_avg_rtt_timer(0.0),
230 m_playerpos_send_timer(0.0),
231 m_ignore_damage_timer(0.0),
238 m_mesh_update_thread(this),
240 new ClientMap(this, this, control,
241 device->getSceneManager()->getRootSceneNode(),
242 device->getSceneManager(), 666),
243 device->getSceneManager(),
246 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, ipv6, this),
248 m_server_ser_ver(SER_FMT_VER_INVALID),
250 m_inventory_updated(false),
251 m_inventory_from_server(NULL),
252 m_inventory_from_server_age(0.0),
257 m_password(password),
258 m_access_denied(false),
259 m_itemdef_received(false),
260 m_nodedef_received(false),
261 m_media_downloader(new ClientMediaDownloader()),
262 m_time_of_day_set(false),
263 m_last_time_of_day_f(-1),
264 m_time_of_day_update_timer(0),
265 m_recommended_send_interval(0.1),
266 m_removed_sounds_check_timer(0),
273 Player *player = new LocalPlayer(this, playername);
275 m_env.addPlayer(player);
281 //request all client managed threads to stop
282 m_mesh_update_thread.Stop();
285 bool Client::isShutdown()
288 if (!m_mesh_update_thread.IsRunning()) return true;
297 m_mesh_update_thread.Stop();
298 m_mesh_update_thread.Wait();
299 while(!m_mesh_update_thread.m_queue_out.empty()) {
300 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_frontNoEx();
305 delete m_inventory_from_server;
307 // Delete detached inventories
308 for(std::map<std::string, Inventory*>::iterator
309 i = m_detached_inventories.begin();
310 i != m_detached_inventories.end(); i++){
314 // cleanup 3d model meshes on client shutdown
315 while (m_device->getSceneManager()->getMeshCache()->getMeshCount() != 0) {
316 scene::IAnimatedMesh * mesh =
317 m_device->getSceneManager()->getMeshCache()->getMeshByIndex(0);
320 m_device->getSceneManager()->getMeshCache()->removeMesh(mesh);
324 void Client::connect(Address address)
326 DSTACK(__FUNCTION_NAME);
327 m_con.SetTimeoutMs(0);
328 m_con.Connect(address);
331 void Client::step(float dtime)
333 DSTACK(__FUNCTION_NAME);
339 if(m_ignore_damage_timer > dtime)
340 m_ignore_damage_timer -= dtime;
342 m_ignore_damage_timer = 0.0;
344 m_animation_time += dtime;
345 if(m_animation_time > 60.0)
346 m_animation_time -= 60.0;
348 m_time_of_day_update_timer += dtime;
356 float &counter = m_packetcounter_timer;
362 infostream << "Client packetcounter (" << m_packetcounter_timer
364 m_packetcounter.print(infostream);
365 m_packetcounter.clear();
372 Delete unused sectors
374 NOTE: This jams the game for a while because deleting sectors
378 float &counter = m_delete_unused_sectors_timer;
386 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
388 core::list<v3s16> deleted_blocks;
390 float delete_unused_sectors_timeout =
391 g_settings->getFloat("client_delete_unused_sectors_timeout");
393 // Delete sector blocks
394 /*u32 num = m_env.getMap().unloadUnusedData
395 (delete_unused_sectors_timeout,
396 true, &deleted_blocks);*/
398 // Delete whole sectors
399 m_env.getMap().unloadUnusedData
400 (delete_unused_sectors_timeout,
403 if(deleted_blocks.size() > 0)
405 /*infostream<<"Client: Deleted blocks of "<<num
406 <<" unused sectors"<<std::endl;*/
407 /*infostream<<"Client: Deleted "<<num
408 <<" unused sectors"<<std::endl;*/
414 // Env is locked so con can be locked.
415 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
417 core::list<v3s16>::Iterator i = deleted_blocks.begin();
418 core::list<v3s16> sendlist;
421 if(sendlist.size() == 255 || i == deleted_blocks.end())
423 if(sendlist.size() == 0)
432 u32 replysize = 2+1+6*sendlist.size();
433 SharedBuffer<u8> reply(replysize);
434 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
435 reply[2] = sendlist.size();
437 for(core::list<v3s16>::Iterator
438 j = sendlist.begin();
439 j != sendlist.end(); j++)
441 writeV3S16(&reply[2+1+6*k], *j);
444 m_con.Send(PEER_ID_SERVER, 1, reply, true);
446 if(i == deleted_blocks.end())
452 sendlist.push_back(*i);
459 // UGLY hack to fix 2 second startup delay caused by non existent
460 // server client startup synchronization in local server or singleplayer mode
461 static bool initial_step = true;
463 initial_step = false;
465 else if(m_state == LC_Created)
467 float &counter = m_connection_reinit_timer;
473 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
475 Player *myplayer = m_env.getLocalPlayer();
476 assert(myplayer != NULL);
477 // Send TOSERVER_INIT
478 // [0] u16 TOSERVER_INIT
479 // [2] u8 SER_FMT_VER_HIGHEST_READ
480 // [3] u8[20] player_name
481 // [23] u8[28] password (new in some version)
482 // [51] u16 minimum supported network protocol version (added sometime)
483 // [53] u16 maximum supported network protocol version (added later than the previous one)
484 SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2+2);
485 writeU16(&data[0], TOSERVER_INIT);
486 writeU8(&data[2], SER_FMT_VER_HIGHEST_READ);
488 memset((char*)&data[3], 0, PLAYERNAME_SIZE);
489 snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
491 /*infostream<<"Client: sending initial password hash: \""<<m_password<<"\""
494 memset((char*)&data[23], 0, PASSWORD_SIZE);
495 snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
497 writeU16(&data[51], CLIENT_PROTOCOL_VERSION_MIN);
498 writeU16(&data[53], CLIENT_PROTOCOL_VERSION_MAX);
500 // Send as unreliable
501 Send(1, data, false);
504 // Not connected, return
509 Do stuff if connected
513 Run Map's timers and unload unused data
515 const float map_timer_and_unload_dtime = 5.25;
516 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
518 ScopeProfiler sp(g_profiler, "Client: map timer and unload");
519 std::list<v3s16> deleted_blocks;
520 m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
521 g_settings->getFloat("client_unload_unused_data_timeout"),
524 /*if(deleted_blocks.size() > 0)
525 infostream<<"Client: Unloaded "<<deleted_blocks.size()
526 <<" unused blocks"<<std::endl;*/
530 NOTE: This loop is intentionally iterated the way it is.
533 std::list<v3s16>::iterator i = deleted_blocks.begin();
534 std::list<v3s16> sendlist;
537 if(sendlist.size() == 255 || i == deleted_blocks.end())
539 if(sendlist.size() == 0)
548 u32 replysize = 2+1+6*sendlist.size();
549 SharedBuffer<u8> reply(replysize);
550 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
551 reply[2] = sendlist.size();
553 for(std::list<v3s16>::iterator
554 j = sendlist.begin();
555 j != sendlist.end(); ++j)
557 writeV3S16(&reply[2+1+6*k], *j);
560 m_con.Send(PEER_ID_SERVER, 2, reply, true);
562 if(i == deleted_blocks.end())
568 sendlist.push_back(*i);
577 // Control local player (0ms)
578 LocalPlayer *player = m_env.getLocalPlayer();
579 assert(player != NULL);
580 player->applyControl(dtime);
590 ClientEnvEvent event = m_env.getClientEvent();
591 if(event.type == CEE_NONE)
595 else if(event.type == CEE_PLAYER_DAMAGE)
597 if(m_ignore_damage_timer <= 0)
599 u8 damage = event.player_damage.amount;
601 if(event.player_damage.send_to_server)
604 // Add to ClientEvent queue
606 event.type = CE_PLAYER_DAMAGE;
607 event.player_damage.amount = damage;
608 m_client_event_queue.push_back(event);
611 else if(event.type == CEE_PLAYER_BREATH)
613 u16 breath = event.player_breath.amount;
623 float &counter = m_avg_rtt_timer;
628 // connectedAndInitialized() is true, peer exists.
629 float avg_rtt = getRTT();
630 infostream<<"Client: avg_rtt="<<avg_rtt<<std::endl;
635 Send player position to server
638 float &counter = m_playerpos_send_timer;
640 if((m_state == LC_Ready) && (counter >= m_recommended_send_interval))
648 Replace updated meshes
651 int num_processed_meshes = 0;
652 while(!m_mesh_update_thread.m_queue_out.empty())
654 num_processed_meshes++;
655 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_frontNoEx();
656 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
659 // Delete the old mesh
660 if(block->mesh != NULL)
662 // TODO: Remove hardware buffers of meshbuffers of block->mesh
667 // Replace with the new mesh
668 block->mesh = r.mesh;
672 if(r.ack_block_to_server)
684 u32 replysize = 2+1+6;
685 SharedBuffer<u8> reply(replysize);
686 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
688 writeV3S16(&reply[3], r.p);
690 m_con.Send(PEER_ID_SERVER, 2, reply, true);
693 if(num_processed_meshes > 0)
694 g_profiler->graphAdd("num_processed_meshes", num_processed_meshes);
700 if (m_media_downloader && m_media_downloader->isStarted()) {
701 m_media_downloader->step(this);
702 if (m_media_downloader->isDone()) {
704 delete m_media_downloader;
705 m_media_downloader = NULL;
710 If the server didn't update the inventory in a while, revert
711 the local inventory (so the player notices the lag problem
712 and knows something is wrong).
714 if(m_inventory_from_server)
716 float interval = 10.0;
717 float count_before = floor(m_inventory_from_server_age / interval);
719 m_inventory_from_server_age += dtime;
721 float count_after = floor(m_inventory_from_server_age / interval);
723 if(count_after != count_before)
725 // Do this every <interval> seconds after TOCLIENT_INVENTORY
726 // Reset the locally changed inventory to the authoritative inventory
727 Player *player = m_env.getLocalPlayer();
728 player->inventory = *m_inventory_from_server;
729 m_inventory_updated = true;
734 Update positions of sounds attached to objects
737 for(std::map<int, u16>::iterator
738 i = m_sounds_to_objects.begin();
739 i != m_sounds_to_objects.end(); i++)
741 int client_id = i->first;
742 u16 object_id = i->second;
743 ClientActiveObject *cao = m_env.getActiveObject(object_id);
746 v3f pos = cao->getPosition();
747 m_sound->updateSoundPosition(client_id, pos);
752 Handle removed remotely initiated sounds
754 m_removed_sounds_check_timer += dtime;
755 if(m_removed_sounds_check_timer >= 2.32)
757 m_removed_sounds_check_timer = 0;
758 // Find removed sounds and clear references to them
759 std::set<s32> removed_server_ids;
760 for(std::map<s32, int>::iterator
761 i = m_sounds_server_to_client.begin();
762 i != m_sounds_server_to_client.end();)
764 s32 server_id = i->first;
765 int client_id = i->second;
767 if(!m_sound->soundExists(client_id)){
768 m_sounds_server_to_client.erase(server_id);
769 m_sounds_client_to_server.erase(client_id);
770 m_sounds_to_objects.erase(client_id);
771 removed_server_ids.insert(server_id);
775 if(removed_server_ids.size() != 0)
777 std::ostringstream os(std::ios_base::binary);
778 writeU16(os, TOSERVER_REMOVED_SOUNDS);
779 size_t server_ids = removed_server_ids.size();
780 assert(server_ids <= 0xFFFF);
781 writeU16(os, (u16) (server_ids & 0xFFFF));
782 for(std::set<s32>::iterator i = removed_server_ids.begin();
783 i != removed_server_ids.end(); i++)
785 std::string s = os.str();
786 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
793 bool Client::loadMedia(const std::string &data, const std::string &filename)
795 // Silly irrlicht's const-incorrectness
796 Buffer<char> data_rw(data.c_str(), data.size());
800 const char *image_ext[] = {
801 ".png", ".jpg", ".bmp", ".tga",
802 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
805 name = removeStringEnd(filename, image_ext);
808 verbosestream<<"Client: Attempting to load image "
809 <<"file \""<<filename<<"\""<<std::endl;
811 io::IFileSystem *irrfs = m_device->getFileSystem();
812 video::IVideoDriver *vdrv = m_device->getVideoDriver();
814 // Create an irrlicht memory file
815 io::IReadFile *rfile = irrfs->createMemoryReadFile(
816 *data_rw, data_rw.getSize(), "_tempreadfile");
819 video::IImage *img = vdrv->createImageFromFile(rfile);
821 errorstream<<"Client: Cannot create image from data of "
822 <<"file \""<<filename<<"\""<<std::endl;
827 m_tsrc->insertSourceImage(filename, img);
834 const char *sound_ext[] = {
835 ".0.ogg", ".1.ogg", ".2.ogg", ".3.ogg", ".4.ogg",
836 ".5.ogg", ".6.ogg", ".7.ogg", ".8.ogg", ".9.ogg",
839 name = removeStringEnd(filename, sound_ext);
842 verbosestream<<"Client: Attempting to load sound "
843 <<"file \""<<filename<<"\""<<std::endl;
844 m_sound->loadSoundData(name, data);
848 const char *model_ext[] = {
849 ".x", ".b3d", ".md2", ".obj",
852 name = removeStringEnd(filename, model_ext);
855 verbosestream<<"Client: Storing model into memory: "
856 <<"\""<<filename<<"\""<<std::endl;
857 if(m_mesh_data.count(filename))
858 errorstream<<"Multiple models with name \""<<filename.c_str()
859 <<"\" found; replacing previous model"<<std::endl;
860 m_mesh_data[filename] = data;
864 errorstream<<"Client: Don't know how to load file \""
865 <<filename<<"\""<<std::endl;
869 // Virtual methods from con::PeerHandler
870 void Client::peerAdded(con::Peer *peer)
872 infostream<<"Client::peerAdded(): peer->id="
873 <<peer->id<<std::endl;
875 void Client::deletingPeer(con::Peer *peer, bool timeout)
877 infostream<<"Client::deletingPeer(): "
878 "Server Peer is getting deleted "
879 <<"(timeout="<<timeout<<")"<<std::endl;
884 u16 number of files requested
890 void Client::request_media(const std::list<std::string> &file_requests)
892 std::ostringstream os(std::ios_base::binary);
893 writeU16(os, TOSERVER_REQUEST_MEDIA);
894 size_t file_requests_size = file_requests.size();
895 assert(file_requests_size <= 0xFFFF);
896 writeU16(os, (u16) (file_requests_size & 0xFFFF));
898 for(std::list<std::string>::const_iterator i = file_requests.begin();
899 i != file_requests.end(); ++i) {
900 os<<serializeString(*i);
904 std::string s = os.str();
905 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
908 infostream<<"Client: Sending media request list to server ("
909 <<file_requests.size()<<" files)"<<std::endl;
912 void Client::received_media()
914 // notify server we received everything
915 std::ostringstream os(std::ios_base::binary);
916 writeU16(os, TOSERVER_RECEIVED_MEDIA);
917 std::string s = os.str();
918 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
921 infostream<<"Client: Notifying server that we received all media"
925 void Client::ReceiveAll()
927 DSTACK(__FUNCTION_NAME);
928 u32 start_ms = porting::getTimeMs();
931 // Limit time even if there would be huge amounts of data to
933 if(porting::getTimeMs() > start_ms + 100)
938 g_profiler->graphAdd("client_received_packets", 1);
940 catch(con::NoIncomingDataException &e)
944 catch(con::InvalidIncomingDataException &e)
946 infostream<<"Client::ReceiveAll(): "
947 "InvalidIncomingDataException: what()="
948 <<e.what()<<std::endl;
953 void Client::Receive()
955 DSTACK(__FUNCTION_NAME);
956 SharedBuffer<u8> data;
958 u32 datasize = m_con.Receive(sender_peer_id, data);
959 ProcessData(*data, datasize, sender_peer_id);
963 sender_peer_id given to this shall be quaranteed to be a valid peer
965 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
967 DSTACK(__FUNCTION_NAME);
969 // Ignore packets that don't even fit a command
972 m_packetcounter.add(60000);
976 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
978 //infostream<<"Client: received command="<<command<<std::endl;
979 m_packetcounter.add((u16)command);
982 If this check is removed, be sure to change the queue
983 system to know the ids
985 if(sender_peer_id != PEER_ID_SERVER)
987 infostream<<"Client::ProcessData(): Discarding data not "
988 "coming from server: peer_id="<<sender_peer_id
993 u8 ser_version = m_server_ser_ver;
995 if(command == TOCLIENT_INIT)
1000 u8 deployed = data[2];
1002 infostream<<"Client: TOCLIENT_INIT received with "
1003 "deployed="<<((int)deployed&0xff)<<std::endl;
1005 if(!ser_ver_supported(deployed))
1007 infostream<<"Client: TOCLIENT_INIT: Server sent "
1008 <<"unsupported ser_fmt_ver"<<std::endl;
1012 m_server_ser_ver = deployed;
1014 // Get player position
1015 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
1016 if(datasize >= 2+1+6)
1017 playerpos_s16 = readV3S16(&data[2+1]);
1018 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
1021 // Set player position
1022 Player *player = m_env.getLocalPlayer();
1023 assert(player != NULL);
1024 player->setPosition(playerpos_f);
1026 if(datasize >= 2+1+6+8)
1029 m_map_seed = readU64(&data[2+1+6]);
1030 infostream<<"Client: received map seed: "<<m_map_seed<<std::endl;
1033 if(datasize >= 2+1+6+8+4)
1036 m_recommended_send_interval = readF1000(&data[2+1+6+8]);
1037 infostream<<"Client: received recommended send interval "
1038 <<m_recommended_send_interval<<std::endl;
1043 SharedBuffer<u8> reply(replysize);
1044 writeU16(&reply[0], TOSERVER_INIT2);
1046 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1053 if(command == TOCLIENT_ACCESS_DENIED)
1055 // The server didn't like our password. Note, this needs
1056 // to be processed even if the serialisation format has
1057 // not been agreed yet, the same as TOCLIENT_INIT.
1058 m_access_denied = true;
1059 m_access_denied_reason = L"Unknown";
1062 std::string datastring((char*)&data[2], datasize-2);
1063 std::istringstream is(datastring, std::ios_base::binary);
1064 m_access_denied_reason = deSerializeWideString(is);
1069 if(ser_version == SER_FMT_VER_INVALID)
1071 infostream<<"Client: Server serialization"
1072 " format invalid or not initialized."
1073 " Skipping incoming command="<<command<<std::endl;
1078 Handle runtime commands
1080 // there's no sane reason why we shouldn't have a player and
1081 // almost everyone needs a player reference
1082 Player *player = m_env.getLocalPlayer();
1083 assert(player != NULL);
1085 if(command == TOCLIENT_REMOVENODE)
1090 p.X = readS16(&data[2]);
1091 p.Y = readS16(&data[4]);
1092 p.Z = readS16(&data[6]);
1095 else if(command == TOCLIENT_ADDNODE)
1097 if(datasize < 8 + MapNode::serializedLength(ser_version))
1101 p.X = readS16(&data[2]);
1102 p.Y = readS16(&data[4]);
1103 p.Z = readS16(&data[6]);
1106 n.deSerialize(&data[8], ser_version);
1108 bool remove_metadata = true;
1109 u32 index = 8 + MapNode::serializedLength(ser_version);
1110 if ((datasize >= index+1) && data[index]){
1111 remove_metadata = false;
1114 addNode(p, n, remove_metadata);
1116 else if(command == TOCLIENT_BLOCKDATA)
1118 // Ignore too small packet
1123 p.X = readS16(&data[2]);
1124 p.Y = readS16(&data[4]);
1125 p.Z = readS16(&data[6]);
1127 std::string datastring((char*)&data[8], datasize-8);
1128 std::istringstream istr(datastring, std::ios_base::binary);
1133 v2s16 p2d(p.X, p.Z);
1134 sector = m_env.getMap().emergeSector(p2d);
1136 assert(sector->getPos() == p2d);
1138 block = sector->getBlockNoCreateNoEx(p.Y);
1142 Update an existing block
1144 block->deSerialize(istr, ser_version, false);
1145 block->deSerializeNetworkSpecific(istr);
1152 block = new MapBlock(&m_env.getMap(), p, this);
1153 block->deSerialize(istr, ser_version, false);
1154 block->deSerializeNetworkSpecific(istr);
1155 sector->insertBlock(block);
1159 Add it to mesh update queue and set it to be acknowledged after update.
1161 addUpdateMeshTaskWithEdge(p, true);
1163 else if(command == TOCLIENT_INVENTORY)
1168 std::string datastring((char*)&data[2], datasize-2);
1169 std::istringstream is(datastring, std::ios_base::binary);
1171 player->inventory.deSerialize(is);
1173 m_inventory_updated = true;
1175 delete m_inventory_from_server;
1176 m_inventory_from_server = new Inventory(player->inventory);
1177 m_inventory_from_server_age = 0.0;
1180 else if(command == TOCLIENT_TIME_OF_DAY)
1185 u16 time_of_day = readU16(&data[2]);
1186 time_of_day = time_of_day % 24000;
1187 float time_speed = 0;
1189 if(datasize >= 2 + 2 + 4)
1191 time_speed = readF1000(&data[4]);
1194 // Old message; try to approximate speed of time by ourselves
1195 float time_of_day_f = (float)time_of_day / 24000.0;
1196 float tod_diff_f = 0;
1198 if(time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
1199 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0;
1201 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
1203 m_last_time_of_day_f = time_of_day_f;
1204 float time_diff = m_time_of_day_update_timer;
1205 m_time_of_day_update_timer = 0;
1207 if(m_time_of_day_set){
1208 time_speed = (3600.0*24.0) * tod_diff_f / time_diff;
1209 infostream<<"Client: Measured time_of_day speed (old format): "
1210 <<time_speed<<" tod_diff_f="<<tod_diff_f
1211 <<" time_diff="<<time_diff<<std::endl;
1215 // Update environment
1216 m_env.setTimeOfDay(time_of_day);
1217 m_env.setTimeOfDaySpeed(time_speed);
1218 m_time_of_day_set = true;
1220 u32 dr = m_env.getDayNightRatio();
1221 infostream<<"Client: time_of_day="<<time_of_day
1222 <<" time_speed="<<time_speed
1223 <<" dr="<<dr<<std::endl;
1225 else if(command == TOCLIENT_CHAT_MESSAGE)
1233 std::string datastring((char*)&data[2], datasize-2);
1234 std::istringstream is(datastring, std::ios_base::binary);
1237 is.read((char*) buf, 2);
1238 u16 len = readU16(buf);
1240 std::wstring message;
1241 for(unsigned int i=0; i<len; i++)
1243 is.read((char*)buf, 2);
1244 message += (wchar_t)readU16(buf);
1247 m_chat_queue.push_back(message);
1249 else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1253 u16 count of removed objects
1254 for all removed objects {
1257 u16 count of added objects
1258 for all added objects {
1261 u32 initialization data length
1262 string initialization data
1267 // Get all data except the command number
1268 std::string datastring((char*)&data[2], datasize-2);
1269 // Throw them in an istringstream
1270 std::istringstream is(datastring, std::ios_base::binary);
1272 // Read removed objects
1274 u16 removed_count = readU16((u8*)buf);
1275 for(unsigned int i=0; i<removed_count; i++)
1278 u16 id = readU16((u8*)buf);
1279 m_env.removeActiveObject(id);
1282 // Read added objects
1284 u16 added_count = readU16((u8*)buf);
1285 for(unsigned int i=0; i<added_count; i++)
1288 u16 id = readU16((u8*)buf);
1290 u8 type = readU8((u8*)buf);
1291 std::string data = deSerializeLongString(is);
1293 m_env.addActiveObject(id, type, data);
1296 else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1308 // Get all data except the command number
1309 std::string datastring((char*)&data[2], datasize-2);
1310 // Throw them in an istringstream
1311 std::istringstream is(datastring, std::ios_base::binary);
1313 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);
1332 else if(command == TOCLIENT_MOVEMENT)
1334 std::string datastring((char*)&data[2], datasize-2);
1335 std::istringstream is(datastring, std::ios_base::binary);
1337 player->movement_acceleration_default = readF1000(is) * BS;
1338 player->movement_acceleration_air = readF1000(is) * BS;
1339 player->movement_acceleration_fast = readF1000(is) * BS;
1340 player->movement_speed_walk = readF1000(is) * BS;
1341 player->movement_speed_crouch = readF1000(is) * BS;
1342 player->movement_speed_fast = readF1000(is) * BS;
1343 player->movement_speed_climb = readF1000(is) * BS;
1344 player->movement_speed_jump = readF1000(is) * BS;
1345 player->movement_liquid_fluidity = readF1000(is) * BS;
1346 player->movement_liquid_fluidity_smooth = readF1000(is) * BS;
1347 player->movement_liquid_sink = readF1000(is) * BS;
1348 player->movement_gravity = readF1000(is) * BS;
1350 else if(command == TOCLIENT_HP)
1352 std::string datastring((char*)&data[2], datasize-2);
1353 std::istringstream is(datastring, std::ios_base::binary);
1355 u8 oldhp = player->hp;
1361 // Add to ClientEvent queue
1363 event.type = CE_PLAYER_DAMAGE;
1364 event.player_damage.amount = oldhp - hp;
1365 m_client_event_queue.push_back(event);
1368 else if(command == TOCLIENT_BREATH)
1370 std::string datastring((char*)&data[2], datasize-2);
1371 std::istringstream is(datastring, std::ios_base::binary);
1373 player->setBreath(readU16(is));
1375 else if(command == TOCLIENT_MOVE_PLAYER)
1377 std::string datastring((char*)&data[2], datasize-2);
1378 std::istringstream is(datastring, std::ios_base::binary);
1380 v3f pos = readV3F1000(is);
1381 f32 pitch = readF1000(is);
1382 f32 yaw = readF1000(is);
1383 player->setPosition(pos);
1385 infostream<<"Client got TOCLIENT_MOVE_PLAYER"
1386 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1392 Add to ClientEvent queue.
1393 This has to be sent to the main program because otherwise
1394 it would just force the pitch and yaw values to whatever
1395 the camera points to.
1398 event.type = CE_PLAYER_FORCE_MOVE;
1399 event.player_force_move.pitch = pitch;
1400 event.player_force_move.yaw = yaw;
1401 m_client_event_queue.push_back(event);
1403 // Ignore damage for a few seconds, so that the player doesn't
1404 // get damage from falling on ground
1405 m_ignore_damage_timer = 3.0;
1407 else if(command == TOCLIENT_PLAYERITEM)
1409 infostream<<"Client: WARNING: Ignoring TOCLIENT_PLAYERITEM"<<std::endl;
1411 else if(command == TOCLIENT_DEATHSCREEN)
1413 std::string datastring((char*)&data[2], datasize-2);
1414 std::istringstream is(datastring, std::ios_base::binary);
1416 bool set_camera_point_target = readU8(is);
1417 v3f camera_point_target = readV3F1000(is);
1420 event.type = CE_DEATHSCREEN;
1421 event.deathscreen.set_camera_point_target = set_camera_point_target;
1422 event.deathscreen.camera_point_target_x = camera_point_target.X;
1423 event.deathscreen.camera_point_target_y = camera_point_target.Y;
1424 event.deathscreen.camera_point_target_z = camera_point_target.Z;
1425 m_client_event_queue.push_back(event);
1427 else if(command == TOCLIENT_ANNOUNCE_MEDIA)
1429 std::string datastring((char*)&data[2], datasize-2);
1430 std::istringstream is(datastring, std::ios_base::binary);
1432 int num_files = readU16(is);
1434 infostream<<"Client: Received media announcement: packet size: "
1435 <<datasize<<std::endl;
1437 if (m_media_downloader == NULL ||
1438 m_media_downloader->isStarted()) {
1439 const char *problem = m_media_downloader ?
1440 "we already saw another announcement" :
1441 "all media has been received already";
1442 errorstream<<"Client: Received media announcement but "
1444 <<" files="<<num_files
1445 <<" size="<<datasize<<std::endl;
1449 // Mesh update thread must be stopped while
1450 // updating content definitions
1451 assert(!m_mesh_update_thread.IsRunning());
1453 for(int i=0; i<num_files; i++)
1455 std::string name = deSerializeString(is);
1456 std::string sha1_base64 = deSerializeString(is);
1457 std::string sha1_raw = base64_decode(sha1_base64);
1458 m_media_downloader->addFile(name, sha1_raw);
1461 std::vector<std::string> remote_media;
1463 Strfnd sf(deSerializeString(is));
1464 while(!sf.atend()) {
1465 std::string baseurl = trim(sf.next(","));
1467 m_media_downloader->addRemoteServer(baseurl);
1470 catch(SerializationError& e) {
1471 // not supported by server or turned off
1474 m_media_downloader->step(this);
1476 else if(command == TOCLIENT_MEDIA)
1478 std::string datastring((char*)&data[2], datasize-2);
1479 std::istringstream is(datastring, std::ios_base::binary);
1483 u16 total number of file bunches
1484 u16 index of this bunch
1485 u32 number of files in this bunch
1493 int num_bunches = readU16(is);
1494 int bunch_i = readU16(is);
1495 u32 num_files = readU32(is);
1496 infostream<<"Client: Received files: bunch "<<bunch_i<<"/"
1497 <<num_bunches<<" files="<<num_files
1498 <<" size="<<datasize<<std::endl;
1503 if (m_media_downloader == NULL ||
1504 !m_media_downloader->isStarted()) {
1505 const char *problem = m_media_downloader ?
1506 "media has not been requested" :
1507 "all media has been received already";
1508 errorstream<<"Client: Received media but "
1510 <<" bunch "<<bunch_i<<"/"<<num_bunches
1511 <<" files="<<num_files
1512 <<" size="<<datasize<<std::endl;
1516 // Mesh update thread must be stopped while
1517 // updating content definitions
1518 assert(!m_mesh_update_thread.IsRunning());
1520 for(unsigned int i=0; i<num_files; i++){
1521 std::string name = deSerializeString(is);
1522 std::string data = deSerializeLongString(is);
1523 m_media_downloader->conventionalTransferDone(
1527 else if(command == TOCLIENT_TOOLDEF)
1529 infostream<<"Client: WARNING: Ignoring TOCLIENT_TOOLDEF"<<std::endl;
1531 else if(command == TOCLIENT_NODEDEF)
1533 infostream<<"Client: Received node definitions: packet size: "
1534 <<datasize<<std::endl;
1536 // Mesh update thread must be stopped while
1537 // updating content definitions
1538 assert(!m_mesh_update_thread.IsRunning());
1540 // Decompress node definitions
1541 std::string datastring((char*)&data[2], datasize-2);
1542 std::istringstream is(datastring, std::ios_base::binary);
1543 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1544 std::ostringstream tmp_os;
1545 decompressZlib(tmp_is, tmp_os);
1547 // Deserialize node definitions
1548 std::istringstream tmp_is2(tmp_os.str());
1549 m_nodedef->deSerialize(tmp_is2);
1550 m_nodedef_received = true;
1552 else if(command == TOCLIENT_CRAFTITEMDEF)
1554 infostream<<"Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF"<<std::endl;
1556 else if(command == TOCLIENT_ITEMDEF)
1558 infostream<<"Client: Received item definitions: packet size: "
1559 <<datasize<<std::endl;
1561 // Mesh update thread must be stopped while
1562 // updating content definitions
1563 assert(!m_mesh_update_thread.IsRunning());
1565 // Decompress item definitions
1566 std::string datastring((char*)&data[2], datasize-2);
1567 std::istringstream is(datastring, std::ios_base::binary);
1568 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1569 std::ostringstream tmp_os;
1570 decompressZlib(tmp_is, tmp_os);
1572 // Deserialize node definitions
1573 std::istringstream tmp_is2(tmp_os.str());
1574 m_itemdef->deSerialize(tmp_is2);
1575 m_itemdef_received = true;
1577 else if(command == TOCLIENT_PLAY_SOUND)
1579 std::string datastring((char*)&data[2], datasize-2);
1580 std::istringstream is(datastring, std::ios_base::binary);
1582 s32 server_id = readS32(is);
1583 std::string name = deSerializeString(is);
1584 float gain = readF1000(is);
1585 int type = readU8(is); // 0=local, 1=positional, 2=object
1586 v3f pos = readV3F1000(is);
1587 u16 object_id = readU16(is);
1588 bool loop = readU8(is);
1593 client_id = m_sound->playSound(name, loop, gain);
1595 case 1: // positional
1596 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1599 ClientActiveObject *cao = m_env.getActiveObject(object_id);
1601 pos = cao->getPosition();
1602 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1603 // TODO: Set up sound to move with object
1608 if(client_id != -1){
1609 m_sounds_server_to_client[server_id] = client_id;
1610 m_sounds_client_to_server[client_id] = server_id;
1612 m_sounds_to_objects[client_id] = object_id;
1615 else if(command == TOCLIENT_STOP_SOUND)
1617 std::string datastring((char*)&data[2], datasize-2);
1618 std::istringstream is(datastring, std::ios_base::binary);
1620 s32 server_id = readS32(is);
1621 std::map<s32, int>::iterator i =
1622 m_sounds_server_to_client.find(server_id);
1623 if(i != m_sounds_server_to_client.end()){
1624 int client_id = i->second;
1625 m_sound->stopSound(client_id);
1628 else if(command == TOCLIENT_PRIVILEGES)
1630 std::string datastring((char*)&data[2], datasize-2);
1631 std::istringstream is(datastring, std::ios_base::binary);
1633 m_privileges.clear();
1634 infostream<<"Client: Privileges updated: ";
1635 u16 num_privileges = readU16(is);
1636 for(unsigned int i=0; i<num_privileges; i++){
1637 std::string priv = deSerializeString(is);
1638 m_privileges.insert(priv);
1639 infostream<<priv<<" ";
1641 infostream<<std::endl;
1643 else if(command == TOCLIENT_INVENTORY_FORMSPEC)
1645 std::string datastring((char*)&data[2], datasize-2);
1646 std::istringstream is(datastring, std::ios_base::binary);
1648 // Store formspec in LocalPlayer
1649 player->inventory_formspec = deSerializeLongString(is);
1651 else if(command == TOCLIENT_DETACHED_INVENTORY)
1653 std::string datastring((char*)&data[2], datasize-2);
1654 std::istringstream is(datastring, std::ios_base::binary);
1656 std::string name = deSerializeString(is);
1658 infostream<<"Client: Detached inventory update: \""<<name<<"\""<<std::endl;
1660 Inventory *inv = NULL;
1661 if(m_detached_inventories.count(name) > 0)
1662 inv = m_detached_inventories[name];
1664 inv = new Inventory(m_itemdef);
1665 m_detached_inventories[name] = inv;
1667 inv->deSerialize(is);
1669 else if(command == TOCLIENT_SHOW_FORMSPEC)
1671 std::string datastring((char*)&data[2], datasize-2);
1672 std::istringstream is(datastring, std::ios_base::binary);
1674 std::string formspec = deSerializeLongString(is);
1675 std::string formname = deSerializeString(is);
1678 event.type = CE_SHOW_FORMSPEC;
1679 // pointer is required as event is a struct only!
1680 // adding a std:string to a struct isn't possible
1681 event.show_formspec.formspec = new std::string(formspec);
1682 event.show_formspec.formname = new std::string(formname);
1683 m_client_event_queue.push_back(event);
1685 else if(command == TOCLIENT_SPAWN_PARTICLE)
1687 std::string datastring((char*)&data[2], datasize-2);
1688 std::istringstream is(datastring, std::ios_base::binary);
1690 v3f pos = readV3F1000(is);
1691 v3f vel = readV3F1000(is);
1692 v3f acc = readV3F1000(is);
1693 float expirationtime = readF1000(is);
1694 float size = readF1000(is);
1695 bool collisiondetection = readU8(is);
1696 std::string texture = deSerializeLongString(is);
1697 bool vertical = false;
1699 vertical = readU8(is);
1703 event.type = CE_SPAWN_PARTICLE;
1704 event.spawn_particle.pos = new v3f (pos);
1705 event.spawn_particle.vel = new v3f (vel);
1706 event.spawn_particle.acc = new v3f (acc);
1707 event.spawn_particle.expirationtime = expirationtime;
1708 event.spawn_particle.size = size;
1709 event.spawn_particle.collisiondetection = collisiondetection;
1710 event.spawn_particle.vertical = vertical;
1711 event.spawn_particle.texture = new std::string(texture);
1713 m_client_event_queue.push_back(event);
1715 else if(command == TOCLIENT_ADD_PARTICLESPAWNER)
1717 std::string datastring((char*)&data[2], datasize-2);
1718 std::istringstream is(datastring, std::ios_base::binary);
1720 u16 amount = readU16(is);
1721 float spawntime = readF1000(is);
1722 v3f minpos = readV3F1000(is);
1723 v3f maxpos = readV3F1000(is);
1724 v3f minvel = readV3F1000(is);
1725 v3f maxvel = readV3F1000(is);
1726 v3f minacc = readV3F1000(is);
1727 v3f maxacc = readV3F1000(is);
1728 float minexptime = readF1000(is);
1729 float maxexptime = readF1000(is);
1730 float minsize = readF1000(is);
1731 float maxsize = readF1000(is);
1732 bool collisiondetection = readU8(is);
1733 std::string texture = deSerializeLongString(is);
1734 u32 id = readU32(is);
1735 bool vertical = false;
1737 vertical = readU8(is);
1741 event.type = CE_ADD_PARTICLESPAWNER;
1742 event.add_particlespawner.amount = amount;
1743 event.add_particlespawner.spawntime = spawntime;
1744 event.add_particlespawner.minpos = new v3f (minpos);
1745 event.add_particlespawner.maxpos = new v3f (maxpos);
1746 event.add_particlespawner.minvel = new v3f (minvel);
1747 event.add_particlespawner.maxvel = new v3f (maxvel);
1748 event.add_particlespawner.minacc = new v3f (minacc);
1749 event.add_particlespawner.maxacc = new v3f (maxacc);
1750 event.add_particlespawner.minexptime = minexptime;
1751 event.add_particlespawner.maxexptime = maxexptime;
1752 event.add_particlespawner.minsize = minsize;
1753 event.add_particlespawner.maxsize = maxsize;
1754 event.add_particlespawner.collisiondetection = collisiondetection;
1755 event.add_particlespawner.vertical = vertical;
1756 event.add_particlespawner.texture = new std::string(texture);
1757 event.add_particlespawner.id = id;
1759 m_client_event_queue.push_back(event);
1761 else if(command == TOCLIENT_DELETE_PARTICLESPAWNER)
1763 std::string datastring((char*)&data[2], datasize-2);
1764 std::istringstream is(datastring, std::ios_base::binary);
1766 u32 id = readU16(is);
1769 event.type = CE_DELETE_PARTICLESPAWNER;
1770 event.delete_particlespawner.id = id;
1772 m_client_event_queue.push_back(event);
1774 else if(command == TOCLIENT_HUDADD)
1776 std::string datastring((char *)&data[2], datasize - 2);
1777 std::istringstream is(datastring, std::ios_base::binary);
1779 u32 id = readU32(is);
1780 u8 type = readU8(is);
1781 v2f pos = readV2F1000(is);
1782 std::string name = deSerializeString(is);
1783 v2f scale = readV2F1000(is);
1784 std::string text = deSerializeString(is);
1785 u32 number = readU32(is);
1786 u32 item = readU32(is);
1787 u32 dir = readU32(is);
1788 v2f align = readV2F1000(is);
1789 v2f offset = readV2F1000(is);
1793 world_pos = readV3F1000(is);
1794 }catch(SerializationError &e) {};
1796 size = readV2S32(is);
1797 } catch(SerializationError &e) {};
1800 event.type = CE_HUDADD;
1801 event.hudadd.id = id;
1802 event.hudadd.type = type;
1803 event.hudadd.pos = new v2f(pos);
1804 event.hudadd.name = new std::string(name);
1805 event.hudadd.scale = new v2f(scale);
1806 event.hudadd.text = new std::string(text);
1807 event.hudadd.number = number;
1808 event.hudadd.item = item;
1809 event.hudadd.dir = dir;
1810 event.hudadd.align = new v2f(align);
1811 event.hudadd.offset = new v2f(offset);
1812 event.hudadd.world_pos = new v3f(world_pos);
1813 event.hudadd.size = new v2s32(size);
1814 m_client_event_queue.push_back(event);
1816 else if(command == TOCLIENT_HUDRM)
1818 std::string datastring((char *)&data[2], datasize - 2);
1819 std::istringstream is(datastring, std::ios_base::binary);
1821 u32 id = readU32(is);
1824 event.type = CE_HUDRM;
1825 event.hudrm.id = id;
1826 m_client_event_queue.push_back(event);
1828 else if(command == TOCLIENT_HUDCHANGE)
1836 std::string datastring((char *)&data[2], datasize - 2);
1837 std::istringstream is(datastring, std::ios_base::binary);
1839 u32 id = readU32(is);
1840 u8 stat = (HudElementStat)readU8(is);
1842 if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
1843 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
1844 v2fdata = readV2F1000(is);
1845 else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
1846 sdata = deSerializeString(is);
1847 else if (stat == HUD_STAT_WORLD_POS)
1848 v3fdata = readV3F1000(is);
1849 else if (stat == HUD_STAT_SIZE )
1850 v2s32data = readV2S32(is);
1852 intdata = readU32(is);
1855 event.type = CE_HUDCHANGE;
1856 event.hudchange.id = id;
1857 event.hudchange.stat = (HudElementStat)stat;
1858 event.hudchange.v2fdata = new v2f(v2fdata);
1859 event.hudchange.v3fdata = new v3f(v3fdata);
1860 event.hudchange.sdata = new std::string(sdata);
1861 event.hudchange.data = intdata;
1862 event.hudchange.v2s32data = new v2s32(v2s32data);
1863 m_client_event_queue.push_back(event);
1865 else if(command == TOCLIENT_HUD_SET_FLAGS)
1867 std::string datastring((char *)&data[2], datasize - 2);
1868 std::istringstream is(datastring, std::ios_base::binary);
1870 u32 flags = readU32(is);
1871 u32 mask = readU32(is);
1873 player->hud_flags &= ~mask;
1874 player->hud_flags |= flags;
1876 else if(command == TOCLIENT_HUD_SET_PARAM)
1878 std::string datastring((char *)&data[2], datasize - 2);
1879 std::istringstream is(datastring, std::ios_base::binary);
1881 u16 param = readU16(is);
1882 std::string value = deSerializeString(is);
1884 if(param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) {
1885 s32 hotbar_itemcount = readS32((u8*) value.c_str());
1886 if(hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
1887 player->hud_hotbar_itemcount = hotbar_itemcount;
1889 else if (param == HUD_PARAM_HOTBAR_IMAGE) {
1890 ((LocalPlayer *) player)->hotbar_image = value;
1892 else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
1893 ((LocalPlayer *) player)->hotbar_selected_image = value;
1896 else if(command == TOCLIENT_SET_SKY)
1898 std::string datastring((char *)&data[2], datasize - 2);
1899 std::istringstream is(datastring, std::ios_base::binary);
1901 video::SColor *bgcolor = new video::SColor(readARGB8(is));
1902 std::string *type = new std::string(deSerializeString(is));
1903 u16 count = readU16(is);
1904 std::vector<std::string> *params = new std::vector<std::string>;
1906 for(size_t i=0; i<count; i++)
1907 params->push_back(deSerializeString(is));
1910 event.type = CE_SET_SKY;
1911 event.set_sky.bgcolor = bgcolor;
1912 event.set_sky.type = type;
1913 event.set_sky.params = params;
1914 m_client_event_queue.push_back(event);
1916 else if(command == TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO)
1918 std::string datastring((char *)&data[2], datasize - 2);
1919 std::istringstream is(datastring, std::ios_base::binary);
1921 bool do_override = readU8(is);
1922 float day_night_ratio_f = (float)readU16(is) / 65536;
1925 event.type = CE_OVERRIDE_DAY_NIGHT_RATIO;
1926 event.override_day_night_ratio.do_override = do_override;
1927 event.override_day_night_ratio.ratio_f = day_night_ratio_f;
1928 m_client_event_queue.push_back(event);
1930 else if(command == TOCLIENT_LOCAL_PLAYER_ANIMATIONS)
1932 std::string datastring((char *)&data[2], datasize - 2);
1933 std::istringstream is(datastring, std::ios_base::binary);
1935 LocalPlayer *player = m_env.getLocalPlayer();
1936 assert(player != NULL);
1938 player->local_animations[0] = readV2S32(is);
1939 player->local_animations[1] = readV2S32(is);
1940 player->local_animations[2] = readV2S32(is);
1941 player->local_animations[3] = readV2S32(is);
1942 player->local_animation_speed = readF1000(is);
1944 else if(command == TOCLIENT_EYE_OFFSET)
1946 std::string datastring((char *)&data[2], datasize - 2);
1947 std::istringstream is(datastring, std::ios_base::binary);
1949 LocalPlayer *player = m_env.getLocalPlayer();
1950 assert(player != NULL);
1952 player->eye_offset_first = readV3F1000(is);
1953 player->eye_offset_third = readV3F1000(is);
1957 infostream<<"Client: Ignoring unknown command "
1958 <<command<<std::endl;
1962 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
1964 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1965 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
1968 void Client::interact(u8 action, const PointedThing& pointed)
1970 if(m_state != LC_Ready){
1971 infostream<<"Client::interact() "
1972 "cancelled (not connected)"
1977 std::ostringstream os(std::ios_base::binary);
1983 [5] u32 length of the next item
1984 [9] serialized PointedThing
1986 0: start digging (from undersurface) or use
1987 1: stop digging (all parameters ignored)
1988 2: digging completed
1989 3: place block or item (to abovesurface)
1992 writeU16(os, TOSERVER_INTERACT);
1993 writeU8(os, action);
1994 writeU16(os, getPlayerItem());
1995 std::ostringstream tmp_os(std::ios::binary);
1996 pointed.serialize(tmp_os);
1997 os<<serializeLongString(tmp_os.str());
1999 std::string s = os.str();
2000 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2003 Send(0, data, true);
2006 void Client::sendNodemetaFields(v3s16 p, const std::string &formname,
2007 const std::map<std::string, std::string> &fields)
2009 std::ostringstream os(std::ios_base::binary);
2011 writeU16(os, TOSERVER_NODEMETA_FIELDS);
2013 os<<serializeString(formname);
2014 size_t fields_size = fields.size();
2015 assert(fields_size <= 0xFFFF);
2016 writeU16(os, (u16) (fields_size & 0xFFFF));
2017 for(std::map<std::string, std::string>::const_iterator
2018 i = fields.begin(); i != fields.end(); i++){
2019 const std::string &name = i->first;
2020 const std::string &value = i->second;
2021 os<<serializeString(name);
2022 os<<serializeLongString(value);
2026 std::string s = os.str();
2027 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2029 Send(0, data, true);
2032 void Client::sendInventoryFields(const std::string &formname,
2033 const std::map<std::string, std::string> &fields)
2035 std::ostringstream os(std::ios_base::binary);
2037 writeU16(os, TOSERVER_INVENTORY_FIELDS);
2038 os<<serializeString(formname);
2039 size_t fields_size = fields.size();
2040 assert(fields_size <= 0xFFFF);
2041 writeU16(os, (u16) (fields_size & 0xFFFF));
2042 for(std::map<std::string, std::string>::const_iterator
2043 i = fields.begin(); i != fields.end(); i++){
2044 const std::string &name = i->first;
2045 const std::string &value = i->second;
2046 os<<serializeString(name);
2047 os<<serializeLongString(value);
2051 std::string s = os.str();
2052 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2054 Send(0, data, true);
2057 void Client::sendInventoryAction(InventoryAction *a)
2059 std::ostringstream os(std::ios_base::binary);
2063 writeU16(buf, TOSERVER_INVENTORY_ACTION);
2064 os.write((char*)buf, 2);
2069 std::string s = os.str();
2070 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2072 Send(0, data, true);
2075 void Client::sendChatMessage(const std::wstring &message)
2077 std::ostringstream os(std::ios_base::binary);
2081 writeU16(buf, TOSERVER_CHAT_MESSAGE);
2082 os.write((char*)buf, 2);
2085 size_t messagesize = message.size();
2086 if (messagesize > 0xFFFF) {
2087 messagesize = 0xFFFF;
2089 writeU16(buf, (u16) messagesize);
2090 os.write((char*)buf, 2);
2093 for(unsigned int i=0; i<message.size(); i++)
2097 os.write((char*)buf, 2);
2101 std::string s = os.str();
2102 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2104 Send(0, data, true);
2107 void Client::sendChangePassword(const std::wstring &oldpassword,
2108 const std::wstring &newpassword)
2110 Player *player = m_env.getLocalPlayer();
2114 std::string playername = player->getName();
2115 std::string oldpwd = translatePassword(playername, oldpassword);
2116 std::string newpwd = translatePassword(playername, newpassword);
2118 std::ostringstream os(std::ios_base::binary);
2119 u8 buf[2+PASSWORD_SIZE*2];
2121 [0] u16 TOSERVER_PASSWORD
2122 [2] u8[28] old password
2123 [30] u8[28] new password
2126 writeU16(buf, TOSERVER_PASSWORD);
2127 for(unsigned int i=0;i<PASSWORD_SIZE-1;i++)
2129 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
2130 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
2132 buf[2+PASSWORD_SIZE-1] = 0;
2133 buf[30+PASSWORD_SIZE-1] = 0;
2134 os.write((char*)buf, 2+PASSWORD_SIZE*2);
2137 std::string s = os.str();
2138 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2140 Send(0, data, true);
2144 void Client::sendDamage(u8 damage)
2146 DSTACK(__FUNCTION_NAME);
2147 std::ostringstream os(std::ios_base::binary);
2149 writeU16(os, TOSERVER_DAMAGE);
2150 writeU8(os, damage);
2153 std::string s = os.str();
2154 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2156 Send(0, data, true);
2159 void Client::sendBreath(u16 breath)
2161 DSTACK(__FUNCTION_NAME);
2162 std::ostringstream os(std::ios_base::binary);
2164 writeU16(os, TOSERVER_BREATH);
2165 writeU16(os, breath);
2167 std::string s = os.str();
2168 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2170 Send(0, data, true);
2173 void Client::sendRespawn()
2175 DSTACK(__FUNCTION_NAME);
2176 std::ostringstream os(std::ios_base::binary);
2178 writeU16(os, TOSERVER_RESPAWN);
2181 std::string s = os.str();
2182 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2184 Send(0, data, true);
2187 void Client::sendReady()
2189 DSTACK(__FUNCTION_NAME);
2190 std::ostringstream os(std::ios_base::binary);
2192 writeU16(os, TOSERVER_CLIENT_READY);
2193 writeU8(os,VERSION_MAJOR);
2194 writeU8(os,VERSION_MINOR);
2195 writeU8(os,VERSION_PATCH_ORIG);
2198 writeU16(os,strlen(minetest_version_hash));
2199 os.write(minetest_version_hash,strlen(minetest_version_hash));
2202 std::string s = os.str();
2203 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2205 Send(0, data, true);
2208 void Client::sendPlayerPos()
2210 LocalPlayer *myplayer = m_env.getLocalPlayer();
2211 if(myplayer == NULL)
2214 // Save bandwidth by only updating position when something changed
2215 if(myplayer->last_position == myplayer->getPosition() &&
2216 myplayer->last_speed == myplayer->getSpeed() &&
2217 myplayer->last_pitch == myplayer->getPitch() &&
2218 myplayer->last_yaw == myplayer->getYaw() &&
2219 myplayer->last_keyPressed == myplayer->keyPressed)
2222 myplayer->last_position = myplayer->getPosition();
2223 myplayer->last_speed = myplayer->getSpeed();
2224 myplayer->last_pitch = myplayer->getPitch();
2225 myplayer->last_yaw = myplayer->getYaw();
2226 myplayer->last_keyPressed = myplayer->keyPressed;
2230 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2231 our_peer_id = m_con.GetPeerID();
2234 // Set peer id if not set already
2235 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2236 myplayer->peer_id = our_peer_id;
2237 // Check that an existing peer_id is the same as the connection's
2238 assert(myplayer->peer_id == our_peer_id);
2240 v3f pf = myplayer->getPosition();
2241 v3f sf = myplayer->getSpeed();
2242 s32 pitch = myplayer->getPitch() * 100;
2243 s32 yaw = myplayer->getYaw() * 100;
2244 u32 keyPressed = myplayer->keyPressed;
2246 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
2247 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
2251 [2] v3s32 position*100
2252 [2+12] v3s32 speed*100
2253 [2+12+12] s32 pitch*100
2254 [2+12+12+4] s32 yaw*100
2255 [2+12+12+4+4] u32 keyPressed
2257 SharedBuffer<u8> data(2+12+12+4+4+4);
2258 writeU16(&data[0], TOSERVER_PLAYERPOS);
2259 writeV3S32(&data[2], position);
2260 writeV3S32(&data[2+12], speed);
2261 writeS32(&data[2+12+12], pitch);
2262 writeS32(&data[2+12+12+4], yaw);
2263 writeU32(&data[2+12+12+4+4], keyPressed);
2264 // Send as unreliable
2265 Send(0, data, false);
2268 void Client::sendPlayerItem(u16 item)
2270 Player *myplayer = m_env.getLocalPlayer();
2271 if(myplayer == NULL)
2274 u16 our_peer_id = m_con.GetPeerID();
2276 // Set peer id if not set already
2277 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2278 myplayer->peer_id = our_peer_id;
2279 // Check that an existing peer_id is the same as the connection's
2280 assert(myplayer->peer_id == our_peer_id);
2282 SharedBuffer<u8> data(2+2);
2283 writeU16(&data[0], TOSERVER_PLAYERITEM);
2284 writeU16(&data[2], item);
2287 Send(0, data, true);
2290 void Client::removeNode(v3s16 p)
2292 std::map<v3s16, MapBlock*> modified_blocks;
2296 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
2298 catch(InvalidPositionException &e)
2302 // add urgent task to update the modified node
2303 addUpdateMeshTaskForNode(p, false, true);
2305 for(std::map<v3s16, MapBlock * >::iterator
2306 i = modified_blocks.begin();
2307 i != modified_blocks.end(); ++i)
2309 addUpdateMeshTaskWithEdge(i->first);
2313 void Client::addNode(v3s16 p, MapNode n, bool remove_metadata)
2315 TimeTaker timer1("Client::addNode()");
2317 std::map<v3s16, MapBlock*> modified_blocks;
2321 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
2322 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
2324 catch(InvalidPositionException &e)
2327 for(std::map<v3s16, MapBlock * >::iterator
2328 i = modified_blocks.begin();
2329 i != modified_blocks.end(); ++i)
2331 addUpdateMeshTaskWithEdge(i->first);
2335 void Client::setPlayerControl(PlayerControl &control)
2337 LocalPlayer *player = m_env.getLocalPlayer();
2338 assert(player != NULL);
2339 player->control = control;
2342 void Client::selectPlayerItem(u16 item)
2344 m_playeritem = item;
2345 m_inventory_updated = true;
2346 sendPlayerItem(item);
2349 // Returns true if the inventory of the local player has been
2350 // updated from the server. If it is true, it is set to false.
2351 bool Client::getLocalInventoryUpdated()
2353 bool updated = m_inventory_updated;
2354 m_inventory_updated = false;
2358 // Copies the inventory of the local player to parameter
2359 void Client::getLocalInventory(Inventory &dst)
2361 Player *player = m_env.getLocalPlayer();
2362 assert(player != NULL);
2363 dst = player->inventory;
2366 Inventory* Client::getInventory(const InventoryLocation &loc)
2369 case InventoryLocation::UNDEFINED:
2372 case InventoryLocation::CURRENT_PLAYER:
2374 Player *player = m_env.getLocalPlayer();
2375 assert(player != NULL);
2376 return &player->inventory;
2379 case InventoryLocation::PLAYER:
2381 Player *player = m_env.getPlayer(loc.name.c_str());
2384 return &player->inventory;
2387 case InventoryLocation::NODEMETA:
2389 NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p);
2392 return meta->getInventory();
2395 case InventoryLocation::DETACHED:
2397 if(m_detached_inventories.count(loc.name) == 0)
2399 return m_detached_inventories[loc.name];
2408 void Client::inventoryAction(InventoryAction *a)
2411 Send it to the server
2413 sendInventoryAction(a);
2416 Predict some local inventory changes
2418 a->clientApply(this, this);
2424 ClientActiveObject * Client::getSelectedActiveObject(
2426 v3f from_pos_f_on_map,
2427 core::line3d<f32> shootline_on_map
2430 std::vector<DistanceSortedActiveObject> objects;
2432 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
2435 // After this, the closest object is the first in the array.
2436 std::sort(objects.begin(), objects.end());
2438 for(unsigned int i=0; i<objects.size(); i++)
2440 ClientActiveObject *obj = objects[i].obj;
2442 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
2443 if(selection_box == NULL)
2446 v3f pos = obj->getPosition();
2448 core::aabbox3d<f32> offsetted_box(
2449 selection_box->MinEdge + pos,
2450 selection_box->MaxEdge + pos
2453 if(offsetted_box.intersectsWithLine(shootline_on_map))
2462 std::list<std::string> Client::getConnectedPlayerNames()
2464 return m_env.getPlayerNames();
2467 float Client::getAnimationTime()
2469 return m_animation_time;
2472 int Client::getCrackLevel()
2474 return m_crack_level;
2477 void Client::setHighlighted(v3s16 pos, bool show_hud)
2479 m_show_hud = show_hud;
2480 v3s16 old_highlighted_pos = m_highlighted_pos;
2481 m_highlighted_pos = pos;
2482 addUpdateMeshTaskForNode(old_highlighted_pos, false, true);
2483 addUpdateMeshTaskForNode(m_highlighted_pos, false, true);
2486 void Client::setCrack(int level, v3s16 pos)
2488 int old_crack_level = m_crack_level;
2489 v3s16 old_crack_pos = m_crack_pos;
2491 m_crack_level = level;
2494 if(old_crack_level >= 0 && (level < 0 || pos != old_crack_pos))
2497 addUpdateMeshTaskForNode(old_crack_pos, false, true);
2499 if(level >= 0 && (old_crack_level < 0 || pos != old_crack_pos))
2502 addUpdateMeshTaskForNode(pos, false, true);
2508 Player *player = m_env.getLocalPlayer();
2509 assert(player != NULL);
2513 u16 Client::getBreath()
2515 Player *player = m_env.getLocalPlayer();
2516 assert(player != NULL);
2517 return player->getBreath();
2520 bool Client::getChatMessage(std::wstring &message)
2522 if(m_chat_queue.size() == 0)
2524 message = m_chat_queue.pop_front();
2528 void Client::typeChatMessage(const std::wstring &message)
2530 // Discard empty line
2535 sendChatMessage(message);
2538 if (message[0] == L'/')
2540 m_chat_queue.push_back((std::wstring)L"issued command: " + message);
2544 LocalPlayer *player = m_env.getLocalPlayer();
2545 assert(player != NULL);
2546 std::wstring name = narrow_to_wide(player->getName());
2547 m_chat_queue.push_back((std::wstring)L"<" + name + L"> " + message);
2551 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
2553 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2558 Create a task to update the mesh of the block
2561 MeshMakeData *data = new MeshMakeData(this);
2564 //TimeTaker timer("data fill");
2566 // Debug: 1-6ms, avg=2ms
2568 data->setCrack(m_crack_level, m_crack_pos);
2569 data->setHighlighted(m_highlighted_pos, m_show_hud);
2570 data->setSmoothLighting(g_settings->getBool("smooth_lighting"));
2573 // Add task to queue
2574 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server, urgent);
2577 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent)
2580 v3s16 p = blockpos + v3s16(0,0,0);
2581 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2582 addUpdateMeshTask(p, ack_to_server, urgent);
2584 catch(InvalidPositionException &e){}
2587 for (int i=0;i<6;i++)
2590 v3s16 p = blockpos + g_6dirs[i];
2591 addUpdateMeshTask(p, false, urgent);
2593 catch(InvalidPositionException &e){}
2597 void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool urgent)
2601 infostream<<"Client::addUpdateMeshTaskForNode(): "
2602 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2606 v3s16 blockpos = getNodeBlockPos(nodepos);
2607 v3s16 blockpos_relative = blockpos * MAP_BLOCKSIZE;
2610 v3s16 p = blockpos + v3s16(0,0,0);
2611 addUpdateMeshTask(p, ack_to_server, urgent);
2613 catch(InvalidPositionException &e){}
2616 if(nodepos.X == blockpos_relative.X){
2618 v3s16 p = blockpos + v3s16(-1,0,0);
2619 addUpdateMeshTask(p, false, urgent);
2621 catch(InvalidPositionException &e){}
2624 if(nodepos.Y == blockpos_relative.Y){
2626 v3s16 p = blockpos + v3s16(0,-1,0);
2627 addUpdateMeshTask(p, false, urgent);
2629 catch(InvalidPositionException &e){}
2632 if(nodepos.Z == blockpos_relative.Z){
2634 v3s16 p = blockpos + v3s16(0,0,-1);
2635 addUpdateMeshTask(p, false, urgent);
2637 catch(InvalidPositionException &e){}
2641 ClientEvent Client::getClientEvent()
2643 if(m_client_event_queue.size() == 0)
2646 event.type = CE_NONE;
2649 return m_client_event_queue.pop_front();
2652 float Client::mediaReceiveProgress()
2654 if (m_media_downloader)
2655 return m_media_downloader->getProgress();
2657 return 1.0; // downloader only exists when not yet done
2660 void Client::afterContentReceived(IrrlichtDevice *device, gui::IGUIFont* font)
2662 infostream<<"Client::afterContentReceived() started"<<std::endl;
2663 assert(m_itemdef_received);
2664 assert(m_nodedef_received);
2665 assert(mediaReceived());
2667 // Rebuild inherited images and recreate textures
2668 infostream<<"- Rebuilding images and textures"<<std::endl;
2669 m_tsrc->rebuildImagesAndTextures();
2672 infostream<<"- Rebuilding shaders"<<std::endl;
2673 m_shsrc->rebuildShaders();
2675 // Update node aliases
2676 infostream<<"- Updating node aliases"<<std::endl;
2677 m_nodedef->updateAliases(m_itemdef);
2679 // Update node textures and assign shaders to each tile
2680 infostream<<"- Updating node textures"<<std::endl;
2681 m_nodedef->updateTextures(m_tsrc, m_shsrc);
2683 // Preload item textures and meshes if configured to
2684 if(g_settings->getBool("preload_item_visuals"))
2686 verbosestream<<"Updating item textures and meshes"<<std::endl;
2687 wchar_t* text = wgettext("Item textures...");
2688 draw_load_screen(text, device, guienv, font, 0, 0);
2689 std::set<std::string> names = m_itemdef->getAll();
2690 size_t size = names.size();
2693 for(std::set<std::string>::const_iterator
2694 i = names.begin(); i != names.end(); ++i){
2695 // Asking for these caches the result
2696 m_itemdef->getInventoryTexture(*i, this);
2697 m_itemdef->getWieldMesh(*i, this);
2699 percent = count*100/size;
2700 if (count%50 == 0) // only update every 50 item
2701 draw_load_screen(text, device, guienv, font, 0, percent);
2706 // Start mesh update thread after setting up content definitions
2707 infostream<<"- Starting mesh update thread"<<std::endl;
2708 m_mesh_update_thread.Start();
2712 infostream<<"Client::afterContentReceived() done"<<std::endl;
2715 float Client::getRTT(void)
2717 return m_con.getPeerStat(PEER_ID_SERVER,con::AVG_RTT);
2720 float Client::getCurRate(void)
2722 return ( m_con.getLocalStat(con::CUR_INC_RATE) +
2723 m_con.getLocalStat(con::CUR_DL_RATE));
2726 float Client::getAvgRate(void)
2728 return ( m_con.getLocalStat(con::AVG_INC_RATE) +
2729 m_con.getLocalStat(con::AVG_DL_RATE));
2732 void Client::makeScreenshot(IrrlichtDevice *device)
2734 irr::video::IVideoDriver *driver = device->getVideoDriver();
2735 irr::video::IImage* const raw_image = driver->createScreenShot();
2737 irr::video::IImage* const image = driver->createImage(video::ECF_R8G8B8,
2738 raw_image->getDimension());
2741 raw_image->copyTo(image);
2742 irr::c8 filename[256];
2743 snprintf(filename, sizeof(filename), "%s" DIR_DELIM "screenshot_%u.png",
2744 g_settings->get("screenshot_path").c_str(),
2745 device->getTimer()->getRealTime());
2746 std::stringstream sstr;
2747 if (driver->writeImageToFile(image, filename)) {
2748 sstr << "Saved screenshot to '" << filename << "'";
2750 sstr << "Failed to save screenshot '" << filename << "'";
2752 m_chat_queue.push_back(narrow_to_wide(sstr.str()));
2753 infostream << sstr << std::endl;
2760 // IGameDef interface
2762 IItemDefManager* Client::getItemDefManager()
2766 INodeDefManager* Client::getNodeDefManager()
2770 ICraftDefManager* Client::getCraftDefManager()
2773 //return m_craftdef;
2775 ITextureSource* Client::getTextureSource()
2779 IShaderSource* Client::getShaderSource()
2783 u16 Client::allocateUnknownNodeId(const std::string &name)
2785 errorstream<<"Client::allocateUnknownNodeId(): "
2786 <<"Client cannot allocate node IDs"<<std::endl;
2788 return CONTENT_IGNORE;
2790 ISoundManager* Client::getSoundManager()
2794 MtEventManager* Client::getEventManager()
2799 scene::IAnimatedMesh* Client::getMesh(const std::string &filename)
2801 std::map<std::string, std::string>::const_iterator i =
2802 m_mesh_data.find(filename);
2803 if(i == m_mesh_data.end()){
2804 errorstream<<"Client::getMesh(): Mesh not found: \""<<filename<<"\""
2808 const std::string &data = i->second;
2809 scene::ISceneManager *smgr = m_device->getSceneManager();
2811 // Create the mesh, remove it from cache and return it
2812 // This allows unique vertex colors and other properties for each instance
2813 Buffer<char> data_rw(data.c_str(), data.size()); // Const-incorrect Irrlicht
2814 io::IFileSystem *irrfs = m_device->getFileSystem();
2815 io::IReadFile *rfile = irrfs->createMemoryReadFile(
2816 *data_rw, data_rw.getSize(), filename.c_str());
2819 scene::IAnimatedMesh *mesh = smgr->getMesh(rfile);
2821 // NOTE: By playing with Irrlicht refcounts, maybe we could cache a bunch
2822 // of uniquely named instances and re-use them
2824 smgr->getMeshCache()->removeMesh(mesh);