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),
258 m_password(password),
259 m_access_denied(false),
260 m_itemdef_received(false),
261 m_nodedef_received(false),
262 m_media_downloader(new ClientMediaDownloader()),
263 m_time_of_day_set(false),
264 m_last_time_of_day_f(-1),
265 m_time_of_day_update_timer(0),
266 m_recommended_send_interval(0.1),
267 m_removed_sounds_check_timer(0),
274 Player *player = new LocalPlayer(this, playername);
276 m_env.addPlayer(player);
282 //request all client managed threads to stop
283 m_mesh_update_thread.Stop();
286 bool Client::isShutdown()
289 if (!m_mesh_update_thread.IsRunning()) return true;
298 m_mesh_update_thread.Stop();
299 m_mesh_update_thread.Wait();
300 while(!m_mesh_update_thread.m_queue_out.empty()) {
301 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_frontNoEx();
306 delete m_inventory_from_server;
308 // Delete detached inventories
309 for(std::map<std::string, Inventory*>::iterator
310 i = m_detached_inventories.begin();
311 i != m_detached_inventories.end(); i++){
315 // cleanup 3d model meshes on client shutdown
316 while (m_device->getSceneManager()->getMeshCache()->getMeshCount() != 0) {
317 scene::IAnimatedMesh * mesh =
318 m_device->getSceneManager()->getMeshCache()->getMeshByIndex(0);
321 m_device->getSceneManager()->getMeshCache()->removeMesh(mesh);
325 void Client::connect(Address address)
327 DSTACK(__FUNCTION_NAME);
328 m_con.SetTimeoutMs(0);
329 m_con.Connect(address);
332 void Client::step(float dtime)
334 DSTACK(__FUNCTION_NAME);
340 if(m_ignore_damage_timer > dtime)
341 m_ignore_damage_timer -= dtime;
343 m_ignore_damage_timer = 0.0;
345 m_animation_time += dtime;
346 if(m_animation_time > 60.0)
347 m_animation_time -= 60.0;
349 m_time_of_day_update_timer += dtime;
357 float &counter = m_packetcounter_timer;
363 infostream << "Client packetcounter (" << m_packetcounter_timer
365 m_packetcounter.print(infostream);
366 m_packetcounter.clear();
373 Delete unused sectors
375 NOTE: This jams the game for a while because deleting sectors
379 float &counter = m_delete_unused_sectors_timer;
387 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
389 core::list<v3s16> deleted_blocks;
391 float delete_unused_sectors_timeout =
392 g_settings->getFloat("client_delete_unused_sectors_timeout");
394 // Delete sector blocks
395 /*u32 num = m_env.getMap().unloadUnusedData
396 (delete_unused_sectors_timeout,
397 true, &deleted_blocks);*/
399 // Delete whole sectors
400 m_env.getMap().unloadUnusedData
401 (delete_unused_sectors_timeout,
404 if(deleted_blocks.size() > 0)
406 /*infostream<<"Client: Deleted blocks of "<<num
407 <<" unused sectors"<<std::endl;*/
408 /*infostream<<"Client: Deleted "<<num
409 <<" unused sectors"<<std::endl;*/
415 // Env is locked so con can be locked.
416 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
418 core::list<v3s16>::Iterator i = deleted_blocks.begin();
419 core::list<v3s16> sendlist;
422 if(sendlist.size() == 255 || i == deleted_blocks.end())
424 if(sendlist.size() == 0)
433 u32 replysize = 2+1+6*sendlist.size();
434 SharedBuffer<u8> reply(replysize);
435 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
436 reply[2] = sendlist.size();
438 for(core::list<v3s16>::Iterator
439 j = sendlist.begin();
440 j != sendlist.end(); j++)
442 writeV3S16(&reply[2+1+6*k], *j);
445 m_con.Send(PEER_ID_SERVER, 1, reply, true);
447 if(i == deleted_blocks.end())
453 sendlist.push_back(*i);
460 // UGLY hack to fix 2 second startup delay caused by non existent
461 // server client startup synchronization in local server or singleplayer mode
462 static bool initial_step = true;
464 initial_step = false;
466 else if(m_state == LC_Created)
468 float &counter = m_connection_reinit_timer;
474 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
476 Player *myplayer = m_env.getLocalPlayer();
477 assert(myplayer != NULL);
478 // Send TOSERVER_INIT
479 // [0] u16 TOSERVER_INIT
480 // [2] u8 SER_FMT_VER_HIGHEST_READ
481 // [3] u8[20] player_name
482 // [23] u8[28] password (new in some version)
483 // [51] u16 minimum supported network protocol version (added sometime)
484 // [53] u16 maximum supported network protocol version (added later than the previous one)
485 SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2+2);
486 writeU16(&data[0], TOSERVER_INIT);
487 writeU8(&data[2], SER_FMT_VER_HIGHEST_READ);
489 memset((char*)&data[3], 0, PLAYERNAME_SIZE);
490 snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
492 /*infostream<<"Client: sending initial password hash: \""<<m_password<<"\""
495 memset((char*)&data[23], 0, PASSWORD_SIZE);
496 snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
498 writeU16(&data[51], CLIENT_PROTOCOL_VERSION_MIN);
499 writeU16(&data[53], CLIENT_PROTOCOL_VERSION_MAX);
501 // Send as unreliable
502 Send(1, data, false);
505 // Not connected, return
510 Do stuff if connected
514 Run Map's timers and unload unused data
516 const float map_timer_and_unload_dtime = 5.25;
517 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
519 ScopeProfiler sp(g_profiler, "Client: map timer and unload");
520 std::list<v3s16> deleted_blocks;
521 m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
522 g_settings->getFloat("client_unload_unused_data_timeout"),
525 /*if(deleted_blocks.size() > 0)
526 infostream<<"Client: Unloaded "<<deleted_blocks.size()
527 <<" unused blocks"<<std::endl;*/
531 NOTE: This loop is intentionally iterated the way it is.
534 std::list<v3s16>::iterator i = deleted_blocks.begin();
535 std::list<v3s16> sendlist;
538 if(sendlist.size() == 255 || i == deleted_blocks.end())
540 if(sendlist.size() == 0)
549 u32 replysize = 2+1+6*sendlist.size();
550 SharedBuffer<u8> reply(replysize);
551 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
552 reply[2] = sendlist.size();
554 for(std::list<v3s16>::iterator
555 j = sendlist.begin();
556 j != sendlist.end(); ++j)
558 writeV3S16(&reply[2+1+6*k], *j);
561 m_con.Send(PEER_ID_SERVER, 2, reply, true);
563 if(i == deleted_blocks.end())
569 sendlist.push_back(*i);
578 // Control local player (0ms)
579 LocalPlayer *player = m_env.getLocalPlayer();
580 assert(player != NULL);
581 player->applyControl(dtime);
591 ClientEnvEvent event = m_env.getClientEvent();
592 if(event.type == CEE_NONE)
596 else if(event.type == CEE_PLAYER_DAMAGE)
598 if(m_ignore_damage_timer <= 0)
600 u8 damage = event.player_damage.amount;
602 if(event.player_damage.send_to_server)
605 // Add to ClientEvent queue
607 event.type = CE_PLAYER_DAMAGE;
608 event.player_damage.amount = damage;
609 m_client_event_queue.push_back(event);
612 else if(event.type == CEE_PLAYER_BREATH)
614 u16 breath = event.player_breath.amount;
624 float &counter = m_avg_rtt_timer;
629 // connectedAndInitialized() is true, peer exists.
630 float avg_rtt = getRTT();
631 infostream<<"Client: avg_rtt="<<avg_rtt<<std::endl;
636 Send player position to server
639 float &counter = m_playerpos_send_timer;
641 if((m_state == LC_Ready) && (counter >= m_recommended_send_interval))
649 Replace updated meshes
652 int num_processed_meshes = 0;
653 while(!m_mesh_update_thread.m_queue_out.empty())
655 num_processed_meshes++;
656 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_frontNoEx();
657 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
660 // Delete the old mesh
661 if(block->mesh != NULL)
663 // TODO: Remove hardware buffers of meshbuffers of block->mesh
668 // Replace with the new mesh
669 block->mesh = r.mesh;
673 if(r.ack_block_to_server)
685 u32 replysize = 2+1+6;
686 SharedBuffer<u8> reply(replysize);
687 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
689 writeV3S16(&reply[3], r.p);
691 m_con.Send(PEER_ID_SERVER, 2, reply, true);
694 if(num_processed_meshes > 0)
695 g_profiler->graphAdd("num_processed_meshes", num_processed_meshes);
701 if (m_media_downloader && m_media_downloader->isStarted()) {
702 m_media_downloader->step(this);
703 if (m_media_downloader->isDone()) {
705 delete m_media_downloader;
706 m_media_downloader = NULL;
711 If the server didn't update the inventory in a while, revert
712 the local inventory (so the player notices the lag problem
713 and knows something is wrong).
715 if(m_inventory_from_server)
717 float interval = 10.0;
718 float count_before = floor(m_inventory_from_server_age / interval);
720 m_inventory_from_server_age += dtime;
722 float count_after = floor(m_inventory_from_server_age / interval);
724 if(count_after != count_before)
726 // Do this every <interval> seconds after TOCLIENT_INVENTORY
727 // Reset the locally changed inventory to the authoritative inventory
728 Player *player = m_env.getLocalPlayer();
729 player->inventory = *m_inventory_from_server;
730 m_inventory_updated = true;
735 Update positions of sounds attached to objects
738 for(std::map<int, u16>::iterator
739 i = m_sounds_to_objects.begin();
740 i != m_sounds_to_objects.end(); i++)
742 int client_id = i->first;
743 u16 object_id = i->second;
744 ClientActiveObject *cao = m_env.getActiveObject(object_id);
747 v3f pos = cao->getPosition();
748 m_sound->updateSoundPosition(client_id, pos);
753 Handle removed remotely initiated sounds
755 m_removed_sounds_check_timer += dtime;
756 if(m_removed_sounds_check_timer >= 2.32)
758 m_removed_sounds_check_timer = 0;
759 // Find removed sounds and clear references to them
760 std::set<s32> removed_server_ids;
761 for(std::map<s32, int>::iterator
762 i = m_sounds_server_to_client.begin();
763 i != m_sounds_server_to_client.end();)
765 s32 server_id = i->first;
766 int client_id = i->second;
768 if(!m_sound->soundExists(client_id)){
769 m_sounds_server_to_client.erase(server_id);
770 m_sounds_client_to_server.erase(client_id);
771 m_sounds_to_objects.erase(client_id);
772 removed_server_ids.insert(server_id);
776 if(removed_server_ids.size() != 0)
778 std::ostringstream os(std::ios_base::binary);
779 writeU16(os, TOSERVER_REMOVED_SOUNDS);
780 size_t server_ids = removed_server_ids.size();
781 assert(server_ids <= 0xFFFF);
782 writeU16(os, (u16) (server_ids & 0xFFFF));
783 for(std::set<s32>::iterator i = removed_server_ids.begin();
784 i != removed_server_ids.end(); i++)
786 std::string s = os.str();
787 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
794 bool Client::loadMedia(const std::string &data, const std::string &filename)
796 // Silly irrlicht's const-incorrectness
797 Buffer<char> data_rw(data.c_str(), data.size());
801 const char *image_ext[] = {
802 ".png", ".jpg", ".bmp", ".tga",
803 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
806 name = removeStringEnd(filename, image_ext);
809 verbosestream<<"Client: Attempting to load image "
810 <<"file \""<<filename<<"\""<<std::endl;
812 io::IFileSystem *irrfs = m_device->getFileSystem();
813 video::IVideoDriver *vdrv = m_device->getVideoDriver();
815 // Create an irrlicht memory file
816 io::IReadFile *rfile = irrfs->createMemoryReadFile(
817 *data_rw, data_rw.getSize(), "_tempreadfile");
820 video::IImage *img = vdrv->createImageFromFile(rfile);
822 errorstream<<"Client: Cannot create image from data of "
823 <<"file \""<<filename<<"\""<<std::endl;
828 m_tsrc->insertSourceImage(filename, img);
835 const char *sound_ext[] = {
836 ".0.ogg", ".1.ogg", ".2.ogg", ".3.ogg", ".4.ogg",
837 ".5.ogg", ".6.ogg", ".7.ogg", ".8.ogg", ".9.ogg",
840 name = removeStringEnd(filename, sound_ext);
843 verbosestream<<"Client: Attempting to load sound "
844 <<"file \""<<filename<<"\""<<std::endl;
845 m_sound->loadSoundData(name, data);
849 const char *model_ext[] = {
850 ".x", ".b3d", ".md2", ".obj",
853 name = removeStringEnd(filename, model_ext);
856 verbosestream<<"Client: Storing model into memory: "
857 <<"\""<<filename<<"\""<<std::endl;
858 if(m_mesh_data.count(filename))
859 errorstream<<"Multiple models with name \""<<filename.c_str()
860 <<"\" found; replacing previous model"<<std::endl;
861 m_mesh_data[filename] = data;
865 errorstream<<"Client: Don't know how to load file \""
866 <<filename<<"\""<<std::endl;
870 // Virtual methods from con::PeerHandler
871 void Client::peerAdded(con::Peer *peer)
873 infostream<<"Client::peerAdded(): peer->id="
874 <<peer->id<<std::endl;
876 void Client::deletingPeer(con::Peer *peer, bool timeout)
878 infostream<<"Client::deletingPeer(): "
879 "Server Peer is getting deleted "
880 <<"(timeout="<<timeout<<")"<<std::endl;
885 u16 number of files requested
891 void Client::request_media(const std::list<std::string> &file_requests)
893 std::ostringstream os(std::ios_base::binary);
894 writeU16(os, TOSERVER_REQUEST_MEDIA);
895 size_t file_requests_size = file_requests.size();
896 assert(file_requests_size <= 0xFFFF);
897 writeU16(os, (u16) (file_requests_size & 0xFFFF));
899 for(std::list<std::string>::const_iterator i = file_requests.begin();
900 i != file_requests.end(); ++i) {
901 os<<serializeString(*i);
905 std::string s = os.str();
906 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
909 infostream<<"Client: Sending media request list to server ("
910 <<file_requests.size()<<" files)"<<std::endl;
913 void Client::received_media()
915 // notify server we received everything
916 std::ostringstream os(std::ios_base::binary);
917 writeU16(os, TOSERVER_RECEIVED_MEDIA);
918 std::string s = os.str();
919 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
922 infostream<<"Client: Notifying server that we received all media"
926 void Client::ReceiveAll()
928 DSTACK(__FUNCTION_NAME);
929 u32 start_ms = porting::getTimeMs();
932 // Limit time even if there would be huge amounts of data to
934 if(porting::getTimeMs() > start_ms + 100)
939 g_profiler->graphAdd("client_received_packets", 1);
941 catch(con::NoIncomingDataException &e)
945 catch(con::InvalidIncomingDataException &e)
947 infostream<<"Client::ReceiveAll(): "
948 "InvalidIncomingDataException: what()="
949 <<e.what()<<std::endl;
954 void Client::Receive()
956 DSTACK(__FUNCTION_NAME);
957 SharedBuffer<u8> data;
959 u32 datasize = m_con.Receive(sender_peer_id, data);
960 ProcessData(*data, datasize, sender_peer_id);
964 sender_peer_id given to this shall be quaranteed to be a valid peer
966 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
968 DSTACK(__FUNCTION_NAME);
970 // Ignore packets that don't even fit a command
973 m_packetcounter.add(60000);
977 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
979 //infostream<<"Client: received command="<<command<<std::endl;
980 m_packetcounter.add((u16)command);
983 If this check is removed, be sure to change the queue
984 system to know the ids
986 if(sender_peer_id != PEER_ID_SERVER)
988 infostream<<"Client::ProcessData(): Discarding data not "
989 "coming from server: peer_id="<<sender_peer_id
994 u8 ser_version = m_server_ser_ver;
996 if(command == TOCLIENT_INIT)
1001 u8 deployed = data[2];
1003 infostream<<"Client: TOCLIENT_INIT received with "
1004 "deployed="<<((int)deployed&0xff)<<std::endl;
1006 if(!ser_ver_supported(deployed))
1008 infostream<<"Client: TOCLIENT_INIT: Server sent "
1009 <<"unsupported ser_fmt_ver"<<std::endl;
1013 m_server_ser_ver = deployed;
1015 // Get player position
1016 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
1017 if(datasize >= 2+1+6)
1018 playerpos_s16 = readV3S16(&data[2+1]);
1019 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
1022 // Set player position
1023 Player *player = m_env.getLocalPlayer();
1024 assert(player != NULL);
1025 player->setPosition(playerpos_f);
1027 if(datasize >= 2+1+6+8)
1030 m_map_seed = readU64(&data[2+1+6]);
1031 infostream<<"Client: received map seed: "<<m_map_seed<<std::endl;
1034 if(datasize >= 2+1+6+8+4)
1037 m_recommended_send_interval = readF1000(&data[2+1+6+8]);
1038 infostream<<"Client: received recommended send interval "
1039 <<m_recommended_send_interval<<std::endl;
1044 SharedBuffer<u8> reply(replysize);
1045 writeU16(&reply[0], TOSERVER_INIT2);
1047 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1054 if(command == TOCLIENT_ACCESS_DENIED)
1056 // The server didn't like our password. Note, this needs
1057 // to be processed even if the serialisation format has
1058 // not been agreed yet, the same as TOCLIENT_INIT.
1059 m_access_denied = true;
1060 m_access_denied_reason = L"Unknown";
1063 std::string datastring((char*)&data[2], datasize-2);
1064 std::istringstream is(datastring, std::ios_base::binary);
1065 m_access_denied_reason = deSerializeWideString(is);
1070 if(ser_version == SER_FMT_VER_INVALID)
1072 infostream<<"Client: Server serialization"
1073 " format invalid or not initialized."
1074 " Skipping incoming command="<<command<<std::endl;
1079 Handle runtime commands
1081 // there's no sane reason why we shouldn't have a player and
1082 // almost everyone needs a player reference
1083 Player *player = m_env.getLocalPlayer();
1084 assert(player != NULL);
1086 if(command == TOCLIENT_REMOVENODE)
1091 p.X = readS16(&data[2]);
1092 p.Y = readS16(&data[4]);
1093 p.Z = readS16(&data[6]);
1096 else if(command == TOCLIENT_ADDNODE)
1098 if(datasize < 8 + MapNode::serializedLength(ser_version))
1102 p.X = readS16(&data[2]);
1103 p.Y = readS16(&data[4]);
1104 p.Z = readS16(&data[6]);
1107 n.deSerialize(&data[8], ser_version);
1109 bool remove_metadata = true;
1110 u32 index = 8 + MapNode::serializedLength(ser_version);
1111 if ((datasize >= index+1) && data[index]){
1112 remove_metadata = false;
1115 addNode(p, n, remove_metadata);
1117 else if(command == TOCLIENT_BLOCKDATA)
1119 // Ignore too small packet
1124 p.X = readS16(&data[2]);
1125 p.Y = readS16(&data[4]);
1126 p.Z = readS16(&data[6]);
1128 std::string datastring((char*)&data[8], datasize-8);
1129 std::istringstream istr(datastring, std::ios_base::binary);
1134 v2s16 p2d(p.X, p.Z);
1135 sector = m_env.getMap().emergeSector(p2d);
1137 assert(sector->getPos() == p2d);
1139 block = sector->getBlockNoCreateNoEx(p.Y);
1143 Update an existing block
1145 block->deSerialize(istr, ser_version, false);
1146 block->deSerializeNetworkSpecific(istr);
1153 block = new MapBlock(&m_env.getMap(), p, this);
1154 block->deSerialize(istr, ser_version, false);
1155 block->deSerializeNetworkSpecific(istr);
1156 sector->insertBlock(block);
1160 Add it to mesh update queue and set it to be acknowledged after update.
1162 addUpdateMeshTaskWithEdge(p, true);
1164 else if(command == TOCLIENT_INVENTORY)
1169 std::string datastring((char*)&data[2], datasize-2);
1170 std::istringstream is(datastring, std::ios_base::binary);
1172 player->inventory.deSerialize(is);
1174 m_inventory_updated = true;
1176 delete m_inventory_from_server;
1177 m_inventory_from_server = new Inventory(player->inventory);
1178 m_inventory_from_server_age = 0.0;
1181 else if(command == TOCLIENT_TIME_OF_DAY)
1186 u16 time_of_day = readU16(&data[2]);
1187 time_of_day = time_of_day % 24000;
1188 float time_speed = 0;
1190 if(datasize >= 2 + 2 + 4)
1192 time_speed = readF1000(&data[4]);
1195 // Old message; try to approximate speed of time by ourselves
1196 float time_of_day_f = (float)time_of_day / 24000.0;
1197 float tod_diff_f = 0;
1199 if(time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
1200 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0;
1202 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
1204 m_last_time_of_day_f = time_of_day_f;
1205 float time_diff = m_time_of_day_update_timer;
1206 m_time_of_day_update_timer = 0;
1208 if(m_time_of_day_set){
1209 time_speed = (3600.0*24.0) * tod_diff_f / time_diff;
1210 infostream<<"Client: Measured time_of_day speed (old format): "
1211 <<time_speed<<" tod_diff_f="<<tod_diff_f
1212 <<" time_diff="<<time_diff<<std::endl;
1216 // Update environment
1217 m_env.setTimeOfDay(time_of_day);
1218 m_env.setTimeOfDaySpeed(time_speed);
1219 m_time_of_day_set = true;
1221 u32 dr = m_env.getDayNightRatio();
1222 infostream<<"Client: time_of_day="<<time_of_day
1223 <<" time_speed="<<time_speed
1224 <<" dr="<<dr<<std::endl;
1226 else if(command == TOCLIENT_CHAT_MESSAGE)
1234 std::string datastring((char*)&data[2], datasize-2);
1235 std::istringstream is(datastring, std::ios_base::binary);
1238 is.read((char*) buf, 2);
1239 u16 len = readU16(buf);
1241 std::wstring message;
1242 for(unsigned int i=0; i<len; i++)
1244 is.read((char*)buf, 2);
1245 message += (wchar_t)readU16(buf);
1248 m_chat_queue.push_back(message);
1250 else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1254 u16 count of removed objects
1255 for all removed objects {
1258 u16 count of added objects
1259 for all added objects {
1262 u32 initialization data length
1263 string initialization data
1268 // Get all data except the command number
1269 std::string datastring((char*)&data[2], datasize-2);
1270 // Throw them in an istringstream
1271 std::istringstream is(datastring, std::ios_base::binary);
1273 // Read removed objects
1275 u16 removed_count = readU16((u8*)buf);
1276 for(unsigned int i=0; i<removed_count; i++)
1279 u16 id = readU16((u8*)buf);
1280 m_env.removeActiveObject(id);
1283 // Read added objects
1285 u16 added_count = readU16((u8*)buf);
1286 for(unsigned int i=0; i<added_count; i++)
1289 u16 id = readU16((u8*)buf);
1291 u8 type = readU8((u8*)buf);
1292 std::string data = deSerializeLongString(is);
1294 m_env.addActiveObject(id, type, data);
1297 else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1309 // Get all data except the command number
1310 std::string datastring((char*)&data[2], datasize-2);
1311 // Throw them in an istringstream
1312 std::istringstream is(datastring, std::ios_base::binary);
1314 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);
1333 else if(command == TOCLIENT_MOVEMENT)
1335 std::string datastring((char*)&data[2], datasize-2);
1336 std::istringstream is(datastring, std::ios_base::binary);
1338 player->movement_acceleration_default = readF1000(is) * BS;
1339 player->movement_acceleration_air = readF1000(is) * BS;
1340 player->movement_acceleration_fast = readF1000(is) * BS;
1341 player->movement_speed_walk = readF1000(is) * BS;
1342 player->movement_speed_crouch = readF1000(is) * BS;
1343 player->movement_speed_fast = readF1000(is) * BS;
1344 player->movement_speed_climb = readF1000(is) * BS;
1345 player->movement_speed_jump = readF1000(is) * BS;
1346 player->movement_liquid_fluidity = readF1000(is) * BS;
1347 player->movement_liquid_fluidity_smooth = readF1000(is) * BS;
1348 player->movement_liquid_sink = readF1000(is) * BS;
1349 player->movement_gravity = readF1000(is) * BS;
1351 else if(command == TOCLIENT_HP)
1353 std::string datastring((char*)&data[2], datasize-2);
1354 std::istringstream is(datastring, std::ios_base::binary);
1356 u8 oldhp = player->hp;
1362 // Add to ClientEvent queue
1364 event.type = CE_PLAYER_DAMAGE;
1365 event.player_damage.amount = oldhp - hp;
1366 m_client_event_queue.push_back(event);
1369 else if(command == TOCLIENT_BREATH)
1371 std::string datastring((char*)&data[2], datasize-2);
1372 std::istringstream is(datastring, std::ios_base::binary);
1374 player->setBreath(readU16(is));
1376 else if(command == TOCLIENT_MOVE_PLAYER)
1378 std::string datastring((char*)&data[2], datasize-2);
1379 std::istringstream is(datastring, std::ios_base::binary);
1381 v3f pos = readV3F1000(is);
1382 f32 pitch = readF1000(is);
1383 f32 yaw = readF1000(is);
1384 player->setPosition(pos);
1386 infostream<<"Client got TOCLIENT_MOVE_PLAYER"
1387 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1393 Add to ClientEvent queue.
1394 This has to be sent to the main program because otherwise
1395 it would just force the pitch and yaw values to whatever
1396 the camera points to.
1399 event.type = CE_PLAYER_FORCE_MOVE;
1400 event.player_force_move.pitch = pitch;
1401 event.player_force_move.yaw = yaw;
1402 m_client_event_queue.push_back(event);
1404 // Ignore damage for a few seconds, so that the player doesn't
1405 // get damage from falling on ground
1406 m_ignore_damage_timer = 3.0;
1408 else if(command == TOCLIENT_PLAYERITEM)
1410 infostream<<"Client: WARNING: Ignoring TOCLIENT_PLAYERITEM"<<std::endl;
1412 else if(command == TOCLIENT_DEATHSCREEN)
1414 std::string datastring((char*)&data[2], datasize-2);
1415 std::istringstream is(datastring, std::ios_base::binary);
1417 bool set_camera_point_target = readU8(is);
1418 v3f camera_point_target = readV3F1000(is);
1421 event.type = CE_DEATHSCREEN;
1422 event.deathscreen.set_camera_point_target = set_camera_point_target;
1423 event.deathscreen.camera_point_target_x = camera_point_target.X;
1424 event.deathscreen.camera_point_target_y = camera_point_target.Y;
1425 event.deathscreen.camera_point_target_z = camera_point_target.Z;
1426 m_client_event_queue.push_back(event);
1428 else if(command == TOCLIENT_ANNOUNCE_MEDIA)
1430 std::string datastring((char*)&data[2], datasize-2);
1431 std::istringstream is(datastring, std::ios_base::binary);
1433 int num_files = readU16(is);
1435 infostream<<"Client: Received media announcement: packet size: "
1436 <<datasize<<std::endl;
1438 if (m_media_downloader == NULL ||
1439 m_media_downloader->isStarted()) {
1440 const char *problem = m_media_downloader ?
1441 "we already saw another announcement" :
1442 "all media has been received already";
1443 errorstream<<"Client: Received media announcement but "
1445 <<" files="<<num_files
1446 <<" size="<<datasize<<std::endl;
1450 // Mesh update thread must be stopped while
1451 // updating content definitions
1452 assert(!m_mesh_update_thread.IsRunning());
1454 for(int i=0; i<num_files; i++)
1456 std::string name = deSerializeString(is);
1457 std::string sha1_base64 = deSerializeString(is);
1458 std::string sha1_raw = base64_decode(sha1_base64);
1459 m_media_downloader->addFile(name, sha1_raw);
1462 std::vector<std::string> remote_media;
1464 Strfnd sf(deSerializeString(is));
1465 while(!sf.atend()) {
1466 std::string baseurl = trim(sf.next(","));
1468 m_media_downloader->addRemoteServer(baseurl);
1471 catch(SerializationError& e) {
1472 // not supported by server or turned off
1475 m_media_downloader->step(this);
1477 else if(command == TOCLIENT_MEDIA)
1479 std::string datastring((char*)&data[2], datasize-2);
1480 std::istringstream is(datastring, std::ios_base::binary);
1484 u16 total number of file bunches
1485 u16 index of this bunch
1486 u32 number of files in this bunch
1494 int num_bunches = readU16(is);
1495 int bunch_i = readU16(is);
1496 u32 num_files = readU32(is);
1497 infostream<<"Client: Received files: bunch "<<bunch_i<<"/"
1498 <<num_bunches<<" files="<<num_files
1499 <<" size="<<datasize<<std::endl;
1504 if (m_media_downloader == NULL ||
1505 !m_media_downloader->isStarted()) {
1506 const char *problem = m_media_downloader ?
1507 "media has not been requested" :
1508 "all media has been received already";
1509 errorstream<<"Client: Received media but "
1511 <<" bunch "<<bunch_i<<"/"<<num_bunches
1512 <<" files="<<num_files
1513 <<" size="<<datasize<<std::endl;
1517 // Mesh update thread must be stopped while
1518 // updating content definitions
1519 assert(!m_mesh_update_thread.IsRunning());
1521 for(unsigned int i=0; i<num_files; i++){
1522 std::string name = deSerializeString(is);
1523 std::string data = deSerializeLongString(is);
1524 m_media_downloader->conventionalTransferDone(
1528 else if(command == TOCLIENT_TOOLDEF)
1530 infostream<<"Client: WARNING: Ignoring TOCLIENT_TOOLDEF"<<std::endl;
1532 else if(command == TOCLIENT_NODEDEF)
1534 infostream<<"Client: Received node definitions: packet size: "
1535 <<datasize<<std::endl;
1537 // Mesh update thread must be stopped while
1538 // updating content definitions
1539 assert(!m_mesh_update_thread.IsRunning());
1541 // Decompress node definitions
1542 std::string datastring((char*)&data[2], datasize-2);
1543 std::istringstream is(datastring, std::ios_base::binary);
1544 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1545 std::ostringstream tmp_os;
1546 decompressZlib(tmp_is, tmp_os);
1548 // Deserialize node definitions
1549 std::istringstream tmp_is2(tmp_os.str());
1550 m_nodedef->deSerialize(tmp_is2);
1551 m_nodedef_received = true;
1553 else if(command == TOCLIENT_CRAFTITEMDEF)
1555 infostream<<"Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF"<<std::endl;
1557 else if(command == TOCLIENT_ITEMDEF)
1559 infostream<<"Client: Received item definitions: packet size: "
1560 <<datasize<<std::endl;
1562 // Mesh update thread must be stopped while
1563 // updating content definitions
1564 assert(!m_mesh_update_thread.IsRunning());
1566 // Decompress item definitions
1567 std::string datastring((char*)&data[2], datasize-2);
1568 std::istringstream is(datastring, std::ios_base::binary);
1569 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1570 std::ostringstream tmp_os;
1571 decompressZlib(tmp_is, tmp_os);
1573 // Deserialize node definitions
1574 std::istringstream tmp_is2(tmp_os.str());
1575 m_itemdef->deSerialize(tmp_is2);
1576 m_itemdef_received = true;
1578 else if(command == TOCLIENT_PLAY_SOUND)
1580 std::string datastring((char*)&data[2], datasize-2);
1581 std::istringstream is(datastring, std::ios_base::binary);
1583 s32 server_id = readS32(is);
1584 std::string name = deSerializeString(is);
1585 float gain = readF1000(is);
1586 int type = readU8(is); // 0=local, 1=positional, 2=object
1587 v3f pos = readV3F1000(is);
1588 u16 object_id = readU16(is);
1589 bool loop = readU8(is);
1594 client_id = m_sound->playSound(name, loop, gain);
1596 case 1: // positional
1597 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1600 ClientActiveObject *cao = m_env.getActiveObject(object_id);
1602 pos = cao->getPosition();
1603 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1604 // TODO: Set up sound to move with object
1609 if(client_id != -1){
1610 m_sounds_server_to_client[server_id] = client_id;
1611 m_sounds_client_to_server[client_id] = server_id;
1613 m_sounds_to_objects[client_id] = object_id;
1616 else if(command == TOCLIENT_STOP_SOUND)
1618 std::string datastring((char*)&data[2], datasize-2);
1619 std::istringstream is(datastring, std::ios_base::binary);
1621 s32 server_id = readS32(is);
1622 std::map<s32, int>::iterator i =
1623 m_sounds_server_to_client.find(server_id);
1624 if(i != m_sounds_server_to_client.end()){
1625 int client_id = i->second;
1626 m_sound->stopSound(client_id);
1629 else if(command == TOCLIENT_PRIVILEGES)
1631 std::string datastring((char*)&data[2], datasize-2);
1632 std::istringstream is(datastring, std::ios_base::binary);
1634 m_privileges.clear();
1635 infostream<<"Client: Privileges updated: ";
1636 u16 num_privileges = readU16(is);
1637 for(unsigned int i=0; i<num_privileges; i++){
1638 std::string priv = deSerializeString(is);
1639 m_privileges.insert(priv);
1640 infostream<<priv<<" ";
1642 infostream<<std::endl;
1644 else if(command == TOCLIENT_INVENTORY_FORMSPEC)
1646 std::string datastring((char*)&data[2], datasize-2);
1647 std::istringstream is(datastring, std::ios_base::binary);
1649 // Store formspec in LocalPlayer
1650 player->inventory_formspec = deSerializeLongString(is);
1652 else if(command == TOCLIENT_DETACHED_INVENTORY)
1654 std::string datastring((char*)&data[2], datasize-2);
1655 std::istringstream is(datastring, std::ios_base::binary);
1657 std::string name = deSerializeString(is);
1659 infostream<<"Client: Detached inventory update: \""<<name<<"\""<<std::endl;
1661 Inventory *inv = NULL;
1662 if(m_detached_inventories.count(name) > 0)
1663 inv = m_detached_inventories[name];
1665 inv = new Inventory(m_itemdef);
1666 m_detached_inventories[name] = inv;
1668 inv->deSerialize(is);
1670 else if(command == TOCLIENT_SHOW_FORMSPEC)
1672 std::string datastring((char*)&data[2], datasize-2);
1673 std::istringstream is(datastring, std::ios_base::binary);
1675 std::string formspec = deSerializeLongString(is);
1676 std::string formname = deSerializeString(is);
1679 event.type = CE_SHOW_FORMSPEC;
1680 // pointer is required as event is a struct only!
1681 // adding a std:string to a struct isn't possible
1682 event.show_formspec.formspec = new std::string(formspec);
1683 event.show_formspec.formname = new std::string(formname);
1684 m_client_event_queue.push_back(event);
1686 else if(command == TOCLIENT_SPAWN_PARTICLE)
1688 std::string datastring((char*)&data[2], datasize-2);
1689 std::istringstream is(datastring, std::ios_base::binary);
1691 v3f pos = readV3F1000(is);
1692 v3f vel = readV3F1000(is);
1693 v3f acc = readV3F1000(is);
1694 float expirationtime = readF1000(is);
1695 float size = readF1000(is);
1696 bool collisiondetection = readU8(is);
1697 std::string texture = deSerializeLongString(is);
1698 bool vertical = false;
1700 vertical = readU8(is);
1704 event.type = CE_SPAWN_PARTICLE;
1705 event.spawn_particle.pos = new v3f (pos);
1706 event.spawn_particle.vel = new v3f (vel);
1707 event.spawn_particle.acc = new v3f (acc);
1708 event.spawn_particle.expirationtime = expirationtime;
1709 event.spawn_particle.size = size;
1710 event.spawn_particle.collisiondetection = collisiondetection;
1711 event.spawn_particle.vertical = vertical;
1712 event.spawn_particle.texture = new std::string(texture);
1714 m_client_event_queue.push_back(event);
1716 else if(command == TOCLIENT_ADD_PARTICLESPAWNER)
1718 std::string datastring((char*)&data[2], datasize-2);
1719 std::istringstream is(datastring, std::ios_base::binary);
1721 u16 amount = readU16(is);
1722 float spawntime = readF1000(is);
1723 v3f minpos = readV3F1000(is);
1724 v3f maxpos = readV3F1000(is);
1725 v3f minvel = readV3F1000(is);
1726 v3f maxvel = readV3F1000(is);
1727 v3f minacc = readV3F1000(is);
1728 v3f maxacc = readV3F1000(is);
1729 float minexptime = readF1000(is);
1730 float maxexptime = readF1000(is);
1731 float minsize = readF1000(is);
1732 float maxsize = readF1000(is);
1733 bool collisiondetection = readU8(is);
1734 std::string texture = deSerializeLongString(is);
1735 u32 id = readU32(is);
1736 bool vertical = false;
1738 vertical = readU8(is);
1742 event.type = CE_ADD_PARTICLESPAWNER;
1743 event.add_particlespawner.amount = amount;
1744 event.add_particlespawner.spawntime = spawntime;
1745 event.add_particlespawner.minpos = new v3f (minpos);
1746 event.add_particlespawner.maxpos = new v3f (maxpos);
1747 event.add_particlespawner.minvel = new v3f (minvel);
1748 event.add_particlespawner.maxvel = new v3f (maxvel);
1749 event.add_particlespawner.minacc = new v3f (minacc);
1750 event.add_particlespawner.maxacc = new v3f (maxacc);
1751 event.add_particlespawner.minexptime = minexptime;
1752 event.add_particlespawner.maxexptime = maxexptime;
1753 event.add_particlespawner.minsize = minsize;
1754 event.add_particlespawner.maxsize = maxsize;
1755 event.add_particlespawner.collisiondetection = collisiondetection;
1756 event.add_particlespawner.vertical = vertical;
1757 event.add_particlespawner.texture = new std::string(texture);
1758 event.add_particlespawner.id = id;
1760 m_client_event_queue.push_back(event);
1762 else if(command == TOCLIENT_DELETE_PARTICLESPAWNER)
1764 std::string datastring((char*)&data[2], datasize-2);
1765 std::istringstream is(datastring, std::ios_base::binary);
1767 u32 id = readU16(is);
1770 event.type = CE_DELETE_PARTICLESPAWNER;
1771 event.delete_particlespawner.id = id;
1773 m_client_event_queue.push_back(event);
1775 else if(command == TOCLIENT_HUDADD)
1777 std::string datastring((char *)&data[2], datasize - 2);
1778 std::istringstream is(datastring, std::ios_base::binary);
1780 u32 id = readU32(is);
1781 u8 type = readU8(is);
1782 v2f pos = readV2F1000(is);
1783 std::string name = deSerializeString(is);
1784 v2f scale = readV2F1000(is);
1785 std::string text = deSerializeString(is);
1786 u32 number = readU32(is);
1787 u32 item = readU32(is);
1788 u32 dir = readU32(is);
1789 v2f align = readV2F1000(is);
1790 v2f offset = readV2F1000(is);
1794 world_pos = readV3F1000(is);
1795 }catch(SerializationError &e) {};
1797 size = readV2S32(is);
1798 } catch(SerializationError &e) {};
1801 event.type = CE_HUDADD;
1802 event.hudadd.id = id;
1803 event.hudadd.type = type;
1804 event.hudadd.pos = new v2f(pos);
1805 event.hudadd.name = new std::string(name);
1806 event.hudadd.scale = new v2f(scale);
1807 event.hudadd.text = new std::string(text);
1808 event.hudadd.number = number;
1809 event.hudadd.item = item;
1810 event.hudadd.dir = dir;
1811 event.hudadd.align = new v2f(align);
1812 event.hudadd.offset = new v2f(offset);
1813 event.hudadd.world_pos = new v3f(world_pos);
1814 event.hudadd.size = new v2s32(size);
1815 m_client_event_queue.push_back(event);
1817 else if(command == TOCLIENT_HUDRM)
1819 std::string datastring((char *)&data[2], datasize - 2);
1820 std::istringstream is(datastring, std::ios_base::binary);
1822 u32 id = readU32(is);
1825 event.type = CE_HUDRM;
1826 event.hudrm.id = id;
1827 m_client_event_queue.push_back(event);
1829 else if(command == TOCLIENT_HUDCHANGE)
1837 std::string datastring((char *)&data[2], datasize - 2);
1838 std::istringstream is(datastring, std::ios_base::binary);
1840 u32 id = readU32(is);
1841 u8 stat = (HudElementStat)readU8(is);
1843 if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
1844 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
1845 v2fdata = readV2F1000(is);
1846 else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
1847 sdata = deSerializeString(is);
1848 else if (stat == HUD_STAT_WORLD_POS)
1849 v3fdata = readV3F1000(is);
1850 else if (stat == HUD_STAT_SIZE )
1851 v2s32data = readV2S32(is);
1853 intdata = readU32(is);
1856 event.type = CE_HUDCHANGE;
1857 event.hudchange.id = id;
1858 event.hudchange.stat = (HudElementStat)stat;
1859 event.hudchange.v2fdata = new v2f(v2fdata);
1860 event.hudchange.v3fdata = new v3f(v3fdata);
1861 event.hudchange.sdata = new std::string(sdata);
1862 event.hudchange.data = intdata;
1863 event.hudchange.v2s32data = new v2s32(v2s32data);
1864 m_client_event_queue.push_back(event);
1866 else if(command == TOCLIENT_HUD_SET_FLAGS)
1868 std::string datastring((char *)&data[2], datasize - 2);
1869 std::istringstream is(datastring, std::ios_base::binary);
1871 u32 flags = readU32(is);
1872 u32 mask = readU32(is);
1874 player->hud_flags &= ~mask;
1875 player->hud_flags |= flags;
1877 else if(command == TOCLIENT_HUD_SET_PARAM)
1879 std::string datastring((char *)&data[2], datasize - 2);
1880 std::istringstream is(datastring, std::ios_base::binary);
1882 u16 param = readU16(is);
1883 std::string value = deSerializeString(is);
1885 if(param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) {
1886 s32 hotbar_itemcount = readS32((u8*) value.c_str());
1887 if(hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
1888 player->hud_hotbar_itemcount = hotbar_itemcount;
1890 else if (param == HUD_PARAM_HOTBAR_IMAGE) {
1891 ((LocalPlayer *) player)->hotbar_image = value;
1893 else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
1894 ((LocalPlayer *) player)->hotbar_selected_image = value;
1897 else if(command == TOCLIENT_SET_SKY)
1899 std::string datastring((char *)&data[2], datasize - 2);
1900 std::istringstream is(datastring, std::ios_base::binary);
1902 video::SColor *bgcolor = new video::SColor(readARGB8(is));
1903 std::string *type = new std::string(deSerializeString(is));
1904 u16 count = readU16(is);
1905 std::vector<std::string> *params = new std::vector<std::string>;
1907 for(size_t i=0; i<count; i++)
1908 params->push_back(deSerializeString(is));
1911 event.type = CE_SET_SKY;
1912 event.set_sky.bgcolor = bgcolor;
1913 event.set_sky.type = type;
1914 event.set_sky.params = params;
1915 m_client_event_queue.push_back(event);
1917 else if(command == TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO)
1919 std::string datastring((char *)&data[2], datasize - 2);
1920 std::istringstream is(datastring, std::ios_base::binary);
1922 bool do_override = readU8(is);
1923 float day_night_ratio_f = (float)readU16(is) / 65536;
1926 event.type = CE_OVERRIDE_DAY_NIGHT_RATIO;
1927 event.override_day_night_ratio.do_override = do_override;
1928 event.override_day_night_ratio.ratio_f = day_night_ratio_f;
1929 m_client_event_queue.push_back(event);
1931 else if(command == TOCLIENT_LOCAL_PLAYER_ANIMATIONS)
1933 std::string datastring((char *)&data[2], datasize - 2);
1934 std::istringstream is(datastring, std::ios_base::binary);
1936 LocalPlayer *player = m_env.getLocalPlayer();
1937 assert(player != NULL);
1939 player->local_animations[0] = readV2S32(is);
1940 player->local_animations[1] = readV2S32(is);
1941 player->local_animations[2] = readV2S32(is);
1942 player->local_animations[3] = readV2S32(is);
1943 player->local_animation_speed = readF1000(is);
1945 else if(command == TOCLIENT_EYE_OFFSET)
1947 std::string datastring((char *)&data[2], datasize - 2);
1948 std::istringstream is(datastring, std::ios_base::binary);
1950 LocalPlayer *player = m_env.getLocalPlayer();
1951 assert(player != NULL);
1953 player->eye_offset_first = readV3F1000(is);
1954 player->eye_offset_third = readV3F1000(is);
1958 infostream<<"Client: Ignoring unknown command "
1959 <<command<<std::endl;
1963 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
1965 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1966 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
1969 void Client::interact(u8 action, const PointedThing& pointed)
1971 if(m_state != LC_Ready){
1972 infostream<<"Client::interact() "
1973 "cancelled (not connected)"
1978 std::ostringstream os(std::ios_base::binary);
1984 [5] u32 length of the next item
1985 [9] serialized PointedThing
1987 0: start digging (from undersurface) or use
1988 1: stop digging (all parameters ignored)
1989 2: digging completed
1990 3: place block or item (to abovesurface)
1993 writeU16(os, TOSERVER_INTERACT);
1994 writeU8(os, action);
1995 writeU16(os, getPlayerItem());
1996 std::ostringstream tmp_os(std::ios::binary);
1997 pointed.serialize(tmp_os);
1998 os<<serializeLongString(tmp_os.str());
2000 std::string s = os.str();
2001 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2004 Send(0, data, true);
2007 void Client::sendNodemetaFields(v3s16 p, const std::string &formname,
2008 const std::map<std::string, std::string> &fields)
2010 std::ostringstream os(std::ios_base::binary);
2012 writeU16(os, TOSERVER_NODEMETA_FIELDS);
2014 os<<serializeString(formname);
2015 size_t fields_size = fields.size();
2016 assert(fields_size <= 0xFFFF);
2017 writeU16(os, (u16) (fields_size & 0xFFFF));
2018 for(std::map<std::string, std::string>::const_iterator
2019 i = fields.begin(); i != fields.end(); i++){
2020 const std::string &name = i->first;
2021 const std::string &value = i->second;
2022 os<<serializeString(name);
2023 os<<serializeLongString(value);
2027 std::string s = os.str();
2028 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2030 Send(0, data, true);
2033 void Client::sendInventoryFields(const std::string &formname,
2034 const std::map<std::string, std::string> &fields)
2036 std::ostringstream os(std::ios_base::binary);
2038 writeU16(os, TOSERVER_INVENTORY_FIELDS);
2039 os<<serializeString(formname);
2040 size_t fields_size = fields.size();
2041 assert(fields_size <= 0xFFFF);
2042 writeU16(os, (u16) (fields_size & 0xFFFF));
2043 for(std::map<std::string, std::string>::const_iterator
2044 i = fields.begin(); i != fields.end(); i++){
2045 const std::string &name = i->first;
2046 const std::string &value = i->second;
2047 os<<serializeString(name);
2048 os<<serializeLongString(value);
2052 std::string s = os.str();
2053 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2055 Send(0, data, true);
2058 void Client::sendInventoryAction(InventoryAction *a)
2060 std::ostringstream os(std::ios_base::binary);
2064 writeU16(buf, TOSERVER_INVENTORY_ACTION);
2065 os.write((char*)buf, 2);
2070 std::string s = os.str();
2071 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2073 Send(0, data, true);
2076 void Client::sendChatMessage(const std::wstring &message)
2078 std::ostringstream os(std::ios_base::binary);
2082 writeU16(buf, TOSERVER_CHAT_MESSAGE);
2083 os.write((char*)buf, 2);
2086 size_t messagesize = message.size();
2087 if (messagesize > 0xFFFF) {
2088 messagesize = 0xFFFF;
2090 writeU16(buf, (u16) messagesize);
2091 os.write((char*)buf, 2);
2094 for(unsigned int i=0; i<message.size(); i++)
2098 os.write((char*)buf, 2);
2102 std::string s = os.str();
2103 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2105 Send(0, data, true);
2108 void Client::sendChangePassword(const std::wstring &oldpassword,
2109 const std::wstring &newpassword)
2111 Player *player = m_env.getLocalPlayer();
2115 std::string playername = player->getName();
2116 std::string oldpwd = translatePassword(playername, oldpassword);
2117 std::string newpwd = translatePassword(playername, newpassword);
2119 std::ostringstream os(std::ios_base::binary);
2120 u8 buf[2+PASSWORD_SIZE*2];
2122 [0] u16 TOSERVER_PASSWORD
2123 [2] u8[28] old password
2124 [30] u8[28] new password
2127 writeU16(buf, TOSERVER_PASSWORD);
2128 for(unsigned int i=0;i<PASSWORD_SIZE-1;i++)
2130 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
2131 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
2133 buf[2+PASSWORD_SIZE-1] = 0;
2134 buf[30+PASSWORD_SIZE-1] = 0;
2135 os.write((char*)buf, 2+PASSWORD_SIZE*2);
2138 std::string s = os.str();
2139 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2141 Send(0, data, true);
2145 void Client::sendDamage(u8 damage)
2147 DSTACK(__FUNCTION_NAME);
2148 std::ostringstream os(std::ios_base::binary);
2150 writeU16(os, TOSERVER_DAMAGE);
2151 writeU8(os, damage);
2154 std::string s = os.str();
2155 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2157 Send(0, data, true);
2160 void Client::sendBreath(u16 breath)
2162 DSTACK(__FUNCTION_NAME);
2163 std::ostringstream os(std::ios_base::binary);
2165 writeU16(os, TOSERVER_BREATH);
2166 writeU16(os, breath);
2168 std::string s = os.str();
2169 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2171 Send(0, data, true);
2174 void Client::sendRespawn()
2176 DSTACK(__FUNCTION_NAME);
2177 std::ostringstream os(std::ios_base::binary);
2179 writeU16(os, TOSERVER_RESPAWN);
2182 std::string s = os.str();
2183 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2185 Send(0, data, true);
2188 void Client::sendReady()
2190 DSTACK(__FUNCTION_NAME);
2191 std::ostringstream os(std::ios_base::binary);
2193 writeU16(os, TOSERVER_CLIENT_READY);
2194 writeU8(os,VERSION_MAJOR);
2195 writeU8(os,VERSION_MINOR);
2196 writeU8(os,VERSION_PATCH_ORIG);
2199 writeU16(os,strlen(minetest_version_hash));
2200 os.write(minetest_version_hash,strlen(minetest_version_hash));
2203 std::string s = os.str();
2204 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2206 Send(0, data, true);
2209 void Client::sendPlayerPos()
2211 LocalPlayer *myplayer = m_env.getLocalPlayer();
2212 if(myplayer == NULL)
2215 // Save bandwidth by only updating position when something changed
2216 if(myplayer->last_position == myplayer->getPosition() &&
2217 myplayer->last_speed == myplayer->getSpeed() &&
2218 myplayer->last_pitch == myplayer->getPitch() &&
2219 myplayer->last_yaw == myplayer->getYaw() &&
2220 myplayer->last_keyPressed == myplayer->keyPressed)
2223 myplayer->last_position = myplayer->getPosition();
2224 myplayer->last_speed = myplayer->getSpeed();
2225 myplayer->last_pitch = myplayer->getPitch();
2226 myplayer->last_yaw = myplayer->getYaw();
2227 myplayer->last_keyPressed = myplayer->keyPressed;
2231 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2232 our_peer_id = m_con.GetPeerID();
2235 // Set peer id if not set already
2236 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2237 myplayer->peer_id = our_peer_id;
2238 // Check that an existing peer_id is the same as the connection's
2239 assert(myplayer->peer_id == our_peer_id);
2241 v3f pf = myplayer->getPosition();
2242 v3f sf = myplayer->getSpeed();
2243 s32 pitch = myplayer->getPitch() * 100;
2244 s32 yaw = myplayer->getYaw() * 100;
2245 u32 keyPressed = myplayer->keyPressed;
2247 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
2248 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
2252 [2] v3s32 position*100
2253 [2+12] v3s32 speed*100
2254 [2+12+12] s32 pitch*100
2255 [2+12+12+4] s32 yaw*100
2256 [2+12+12+4+4] u32 keyPressed
2258 SharedBuffer<u8> data(2+12+12+4+4+4);
2259 writeU16(&data[0], TOSERVER_PLAYERPOS);
2260 writeV3S32(&data[2], position);
2261 writeV3S32(&data[2+12], speed);
2262 writeS32(&data[2+12+12], pitch);
2263 writeS32(&data[2+12+12+4], yaw);
2264 writeU32(&data[2+12+12+4+4], keyPressed);
2265 // Send as unreliable
2266 Send(0, data, false);
2269 void Client::sendPlayerItem(u16 item)
2271 Player *myplayer = m_env.getLocalPlayer();
2272 if(myplayer == NULL)
2275 u16 our_peer_id = m_con.GetPeerID();
2277 // Set peer id if not set already
2278 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2279 myplayer->peer_id = our_peer_id;
2280 // Check that an existing peer_id is the same as the connection's
2281 assert(myplayer->peer_id == our_peer_id);
2283 SharedBuffer<u8> data(2+2);
2284 writeU16(&data[0], TOSERVER_PLAYERITEM);
2285 writeU16(&data[2], item);
2288 Send(0, data, true);
2291 void Client::removeNode(v3s16 p)
2293 std::map<v3s16, MapBlock*> modified_blocks;
2297 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
2299 catch(InvalidPositionException &e)
2303 for(std::map<v3s16, MapBlock * >::iterator
2304 i = modified_blocks.begin();
2305 i != modified_blocks.end(); ++i)
2307 addUpdateMeshTask(i->first, false, false);
2309 // add urgent task to update the modified node
2310 addUpdateMeshTaskForNode(p, false, true);
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 addUpdateMeshTask(i->first, false, false);
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(this);
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 scene::ISceneManager* Client::getSceneManager()
2785 return m_device->getSceneManager();
2787 u16 Client::allocateUnknownNodeId(const std::string &name)
2789 errorstream<<"Client::allocateUnknownNodeId(): "
2790 <<"Client cannot allocate node IDs"<<std::endl;
2792 return CONTENT_IGNORE;
2794 ISoundManager* Client::getSoundManager()
2798 MtEventManager* Client::getEventManager()
2803 scene::IAnimatedMesh* Client::getMesh(const std::string &filename)
2805 std::map<std::string, std::string>::const_iterator i =
2806 m_mesh_data.find(filename);
2807 if(i == m_mesh_data.end()){
2808 errorstream<<"Client::getMesh(): Mesh not found: \""<<filename<<"\""
2812 const std::string &data = i->second;
2813 scene::ISceneManager *smgr = m_device->getSceneManager();
2815 // Create the mesh, remove it from cache and return it
2816 // This allows unique vertex colors and other properties for each instance
2817 Buffer<char> data_rw(data.c_str(), data.size()); // Const-incorrect Irrlicht
2818 io::IFileSystem *irrfs = m_device->getFileSystem();
2819 io::IReadFile *rfile = irrfs->createMemoryReadFile(
2820 *data_rw, data_rw.getSize(), filename.c_str());
2823 scene::IAnimatedMesh *mesh = smgr->getMesh(rfile);
2825 // NOTE: By playing with Irrlicht refcounts, maybe we could cache a bunch
2826 // of uniquely named instances and re-use them
2828 smgr->getMeshCache()->removeMesh(mesh);