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"
58 #include "database-sqlite3.h"
60 extern gui::IGUIEnvironment* guienv;
66 QueuedMeshUpdate::QueuedMeshUpdate():
69 ack_block_to_server(false)
73 QueuedMeshUpdate::~QueuedMeshUpdate()
83 MeshUpdateQueue::MeshUpdateQueue()
87 MeshUpdateQueue::~MeshUpdateQueue()
89 JMutexAutoLock lock(m_mutex);
91 for(std::vector<QueuedMeshUpdate*>::iterator
93 i != m_queue.end(); i++)
95 QueuedMeshUpdate *q = *i;
101 peer_id=0 adds with nobody to send to
103 void MeshUpdateQueue::addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_server, bool urgent)
105 DSTACK(__FUNCTION_NAME);
109 JMutexAutoLock lock(m_mutex);
115 Find if block is already in queue.
116 If it is, update the data and quit.
118 for(std::vector<QueuedMeshUpdate*>::iterator
120 i != m_queue.end(); i++)
122 QueuedMeshUpdate *q = *i;
128 if(ack_block_to_server)
129 q->ack_block_to_server = true;
137 QueuedMeshUpdate *q = new QueuedMeshUpdate;
140 q->ack_block_to_server = ack_block_to_server;
141 m_queue.push_back(q);
144 // Returned pointer must be deleted
145 // Returns NULL if queue is empty
146 QueuedMeshUpdate * MeshUpdateQueue::pop()
148 JMutexAutoLock lock(m_mutex);
150 bool must_be_urgent = !m_urgents.empty();
151 for(std::vector<QueuedMeshUpdate*>::iterator
153 i != m_queue.end(); i++)
155 QueuedMeshUpdate *q = *i;
156 if(must_be_urgent && m_urgents.count(q->p) == 0)
159 m_urgents.erase(q->p);
169 void * MeshUpdateThread::Thread()
173 log_register_thread("MeshUpdateThread");
175 DSTACK(__FUNCTION_NAME);
177 BEGIN_DEBUG_EXCEPTION_HANDLER
179 porting::setThreadName("MeshUpdateThread");
181 while(!StopRequested())
183 QueuedMeshUpdate *q = m_queue_in.pop();
190 ScopeProfiler sp(g_profiler, "Client: Mesh making");
192 MapBlockMesh *mesh_new = new MapBlockMesh(q->data, m_camera_offset);
193 if(mesh_new->getMesh()->getMeshBufferCount() == 0)
202 r.ack_block_to_server = q->ack_block_to_server;
204 m_queue_out.push_back(r);
209 END_DEBUG_EXCEPTION_HANDLER(errorstream)
219 IrrlichtDevice *device,
220 const char *playername,
221 std::string password,
222 MapDrawControl &control,
223 IWritableTextureSource *tsrc,
224 IWritableShaderSource *shsrc,
225 IWritableItemDefManager *itemdef,
226 IWritableNodeDefManager *nodedef,
227 ISoundManager *sound,
228 MtEventManager *event,
231 m_packetcounter_timer(0.0),
232 m_connection_reinit_timer(0.1),
233 m_avg_rtt_timer(0.0),
234 m_playerpos_send_timer(0.0),
235 m_ignore_damage_timer(0.0),
242 m_mesh_update_thread(this),
244 new ClientMap(this, this, control,
245 device->getSceneManager()->getRootSceneNode(),
246 device->getSceneManager(), 666),
247 device->getSceneManager(),
250 m_particle_manager(&m_env),
251 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, ipv6, this),
253 m_server_ser_ver(SER_FMT_VER_INVALID),
255 m_inventory_updated(false),
256 m_inventory_from_server(NULL),
257 m_inventory_from_server_age(0.0),
258 m_show_highlighted(false),
262 m_highlighted_pos(0,0,0),
264 m_password(password),
265 m_access_denied(false),
266 m_itemdef_received(false),
267 m_nodedef_received(false),
268 m_media_downloader(new ClientMediaDownloader()),
269 m_time_of_day_set(false),
270 m_last_time_of_day_f(-1),
271 m_time_of_day_update_timer(0),
272 m_recommended_send_interval(0.1),
273 m_removed_sounds_check_timer(0),
280 Player *player = new LocalPlayer(this, playername);
282 m_env.addPlayer(player);
285 m_cache_smooth_lighting = g_settings->getBool("smooth_lighting");
290 //request all client managed threads to stop
291 m_mesh_update_thread.Stop();
292 if (localdb != NULL) {
293 actionstream << "Local map saving ended" << std::endl;
299 bool Client::isShutdown()
302 if (!m_mesh_update_thread.IsRunning()) return true;
311 m_mesh_update_thread.Stop();
312 m_mesh_update_thread.Wait();
313 while(!m_mesh_update_thread.m_queue_out.empty()) {
314 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_frontNoEx();
319 delete m_inventory_from_server;
321 // Delete detached inventories
322 for(std::map<std::string, Inventory*>::iterator
323 i = m_detached_inventories.begin();
324 i != m_detached_inventories.end(); i++){
328 // cleanup 3d model meshes on client shutdown
329 while (m_device->getSceneManager()->getMeshCache()->getMeshCount() != 0) {
330 scene::IAnimatedMesh * mesh =
331 m_device->getSceneManager()->getMeshCache()->getMeshByIndex(0);
334 m_device->getSceneManager()->getMeshCache()->removeMesh(mesh);
338 void Client::connect(Address address,
339 const std::string &address_name,
340 bool is_local_server)
342 DSTACK(__FUNCTION_NAME);
344 initLocalMapSaving(address, address_name, is_local_server);
346 m_con.SetTimeoutMs(0);
347 m_con.Connect(address);
350 void Client::step(float dtime)
352 DSTACK(__FUNCTION_NAME);
358 if(m_ignore_damage_timer > dtime)
359 m_ignore_damage_timer -= dtime;
361 m_ignore_damage_timer = 0.0;
363 m_animation_time += dtime;
364 if(m_animation_time > 60.0)
365 m_animation_time -= 60.0;
367 m_time_of_day_update_timer += dtime;
375 float &counter = m_packetcounter_timer;
381 infostream << "Client packetcounter (" << m_packetcounter_timer
383 m_packetcounter.print(infostream);
384 m_packetcounter.clear();
391 Delete unused sectors
393 NOTE: This jams the game for a while because deleting sectors
397 float &counter = m_delete_unused_sectors_timer;
405 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
407 core::list<v3s16> deleted_blocks;
409 float delete_unused_sectors_timeout =
410 g_settings->getFloat("client_delete_unused_sectors_timeout");
412 // Delete sector blocks
413 /*u32 num = m_env.getMap().unloadUnusedData
414 (delete_unused_sectors_timeout,
415 true, &deleted_blocks);*/
417 // Delete whole sectors
418 m_env.getMap().unloadUnusedData
419 (delete_unused_sectors_timeout,
422 if(deleted_blocks.size() > 0)
424 /*infostream<<"Client: Deleted blocks of "<<num
425 <<" unused sectors"<<std::endl;*/
426 /*infostream<<"Client: Deleted "<<num
427 <<" unused sectors"<<std::endl;*/
433 // Env is locked so con can be locked.
434 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
436 core::list<v3s16>::Iterator i = deleted_blocks.begin();
437 core::list<v3s16> sendlist;
440 if(sendlist.size() == 255 || i == deleted_blocks.end())
442 if(sendlist.size() == 0)
451 u32 replysize = 2+1+6*sendlist.size();
452 SharedBuffer<u8> reply(replysize);
453 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
454 reply[2] = sendlist.size();
456 for(core::list<v3s16>::Iterator
457 j = sendlist.begin();
458 j != sendlist.end(); j++)
460 writeV3S16(&reply[2+1+6*k], *j);
463 m_con.Send(PEER_ID_SERVER, 1, reply, true);
465 if(i == deleted_blocks.end())
471 sendlist.push_back(*i);
478 // UGLY hack to fix 2 second startup delay caused by non existent
479 // server client startup synchronization in local server or singleplayer mode
480 static bool initial_step = true;
482 initial_step = false;
484 else if(m_state == LC_Created)
486 float &counter = m_connection_reinit_timer;
492 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
494 Player *myplayer = m_env.getLocalPlayer();
495 assert(myplayer != NULL);
496 // Send TOSERVER_INIT
497 // [0] u16 TOSERVER_INIT
498 // [2] u8 SER_FMT_VER_HIGHEST_READ
499 // [3] u8[20] player_name
500 // [23] u8[28] password (new in some version)
501 // [51] u16 minimum supported network protocol version (added sometime)
502 // [53] u16 maximum supported network protocol version (added later than the previous one)
503 SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2+2);
504 writeU16(&data[0], TOSERVER_INIT);
505 writeU8(&data[2], SER_FMT_VER_HIGHEST_READ);
507 memset((char*)&data[3], 0, PLAYERNAME_SIZE);
508 snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
510 /*infostream<<"Client: sending initial password hash: \""<<m_password<<"\""
513 memset((char*)&data[23], 0, PASSWORD_SIZE);
514 snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
516 writeU16(&data[51], CLIENT_PROTOCOL_VERSION_MIN);
517 writeU16(&data[53], CLIENT_PROTOCOL_VERSION_MAX);
519 // Send as unreliable
520 Send(1, data, false);
523 // Not connected, return
528 Do stuff if connected
532 Run Map's timers and unload unused data
534 const float map_timer_and_unload_dtime = 5.25;
535 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
537 ScopeProfiler sp(g_profiler, "Client: map timer and unload");
538 std::list<v3s16> deleted_blocks;
539 m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
540 g_settings->getFloat("client_unload_unused_data_timeout"),
543 /*if(deleted_blocks.size() > 0)
544 infostream<<"Client: Unloaded "<<deleted_blocks.size()
545 <<" unused blocks"<<std::endl;*/
549 NOTE: This loop is intentionally iterated the way it is.
552 std::list<v3s16>::iterator i = deleted_blocks.begin();
553 std::list<v3s16> sendlist;
556 if(sendlist.size() == 255 || i == deleted_blocks.end())
567 u32 replysize = 2+1+6*sendlist.size();
568 SharedBuffer<u8> reply(replysize);
569 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
570 reply[2] = sendlist.size();
572 for(std::list<v3s16>::iterator
573 j = sendlist.begin();
574 j != sendlist.end(); ++j)
576 writeV3S16(&reply[2+1+6*k], *j);
579 m_con.Send(PEER_ID_SERVER, 2, reply, true);
581 if(i == deleted_blocks.end())
587 sendlist.push_back(*i);
596 // Control local player (0ms)
597 LocalPlayer *player = m_env.getLocalPlayer();
598 assert(player != NULL);
599 player->applyControl(dtime);
609 ClientEnvEvent event = m_env.getClientEvent();
610 if(event.type == CEE_NONE)
614 else if(event.type == CEE_PLAYER_DAMAGE)
616 if(m_ignore_damage_timer <= 0)
618 u8 damage = event.player_damage.amount;
620 if(event.player_damage.send_to_server)
623 // Add to ClientEvent queue
625 event.type = CE_PLAYER_DAMAGE;
626 event.player_damage.amount = damage;
627 m_client_event_queue.push_back(event);
630 else if(event.type == CEE_PLAYER_BREATH)
632 u16 breath = event.player_breath.amount;
642 float &counter = m_avg_rtt_timer;
647 // connectedAndInitialized() is true, peer exists.
648 float avg_rtt = getRTT();
649 infostream<<"Client: avg_rtt="<<avg_rtt<<std::endl;
654 Send player position to server
657 float &counter = m_playerpos_send_timer;
659 if((m_state == LC_Ready) && (counter >= m_recommended_send_interval))
667 Replace updated meshes
670 int num_processed_meshes = 0;
671 while(!m_mesh_update_thread.m_queue_out.empty())
673 num_processed_meshes++;
674 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_frontNoEx();
675 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
678 // Delete the old mesh
679 if(block->mesh != NULL)
681 // TODO: Remove hardware buffers of meshbuffers of block->mesh
686 // Replace with the new mesh
687 block->mesh = r.mesh;
691 if(r.ack_block_to_server)
703 u32 replysize = 2+1+6;
704 SharedBuffer<u8> reply(replysize);
705 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
707 writeV3S16(&reply[3], r.p);
709 m_con.Send(PEER_ID_SERVER, 2, reply, true);
712 if(num_processed_meshes > 0)
713 g_profiler->graphAdd("num_processed_meshes", num_processed_meshes);
719 if (m_media_downloader && m_media_downloader->isStarted()) {
720 m_media_downloader->step(this);
721 if (m_media_downloader->isDone()) {
723 delete m_media_downloader;
724 m_media_downloader = NULL;
729 If the server didn't update the inventory in a while, revert
730 the local inventory (so the player notices the lag problem
731 and knows something is wrong).
733 if(m_inventory_from_server)
735 float interval = 10.0;
736 float count_before = floor(m_inventory_from_server_age / interval);
738 m_inventory_from_server_age += dtime;
740 float count_after = floor(m_inventory_from_server_age / interval);
742 if(count_after != count_before)
744 // Do this every <interval> seconds after TOCLIENT_INVENTORY
745 // Reset the locally changed inventory to the authoritative inventory
746 Player *player = m_env.getLocalPlayer();
747 player->inventory = *m_inventory_from_server;
748 m_inventory_updated = true;
753 Update positions of sounds attached to objects
756 for(std::map<int, u16>::iterator
757 i = m_sounds_to_objects.begin();
758 i != m_sounds_to_objects.end(); i++)
760 int client_id = i->first;
761 u16 object_id = i->second;
762 ClientActiveObject *cao = m_env.getActiveObject(object_id);
765 v3f pos = cao->getPosition();
766 m_sound->updateSoundPosition(client_id, pos);
771 Handle removed remotely initiated sounds
773 m_removed_sounds_check_timer += dtime;
774 if(m_removed_sounds_check_timer >= 2.32)
776 m_removed_sounds_check_timer = 0;
777 // Find removed sounds and clear references to them
778 std::set<s32> removed_server_ids;
779 for(std::map<s32, int>::iterator
780 i = m_sounds_server_to_client.begin();
781 i != m_sounds_server_to_client.end();)
783 s32 server_id = i->first;
784 int client_id = i->second;
786 if(!m_sound->soundExists(client_id)){
787 m_sounds_server_to_client.erase(server_id);
788 m_sounds_client_to_server.erase(client_id);
789 m_sounds_to_objects.erase(client_id);
790 removed_server_ids.insert(server_id);
794 if(!removed_server_ids.empty())
796 std::ostringstream os(std::ios_base::binary);
797 writeU16(os, TOSERVER_REMOVED_SOUNDS);
798 size_t server_ids = removed_server_ids.size();
799 assert(server_ids <= 0xFFFF);
800 writeU16(os, (u16) (server_ids & 0xFFFF));
801 for(std::set<s32>::iterator i = removed_server_ids.begin();
802 i != removed_server_ids.end(); i++)
804 std::string s = os.str();
805 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
812 bool Client::loadMedia(const std::string &data, const std::string &filename)
814 // Silly irrlicht's const-incorrectness
815 Buffer<char> data_rw(data.c_str(), data.size());
819 const char *image_ext[] = {
820 ".png", ".jpg", ".bmp", ".tga",
821 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
824 name = removeStringEnd(filename, image_ext);
827 verbosestream<<"Client: Attempting to load image "
828 <<"file \""<<filename<<"\""<<std::endl;
830 io::IFileSystem *irrfs = m_device->getFileSystem();
831 video::IVideoDriver *vdrv = m_device->getVideoDriver();
833 // Create an irrlicht memory file
834 io::IReadFile *rfile = irrfs->createMemoryReadFile(
835 *data_rw, data_rw.getSize(), "_tempreadfile");
838 video::IImage *img = vdrv->createImageFromFile(rfile);
840 errorstream<<"Client: Cannot create image from data of "
841 <<"file \""<<filename<<"\""<<std::endl;
846 m_tsrc->insertSourceImage(filename, img);
853 const char *sound_ext[] = {
854 ".0.ogg", ".1.ogg", ".2.ogg", ".3.ogg", ".4.ogg",
855 ".5.ogg", ".6.ogg", ".7.ogg", ".8.ogg", ".9.ogg",
858 name = removeStringEnd(filename, sound_ext);
861 verbosestream<<"Client: Attempting to load sound "
862 <<"file \""<<filename<<"\""<<std::endl;
863 m_sound->loadSoundData(name, data);
867 const char *model_ext[] = {
868 ".x", ".b3d", ".md2", ".obj",
871 name = removeStringEnd(filename, model_ext);
874 verbosestream<<"Client: Storing model into memory: "
875 <<"\""<<filename<<"\""<<std::endl;
876 if(m_mesh_data.count(filename))
877 errorstream<<"Multiple models with name \""<<filename.c_str()
878 <<"\" found; replacing previous model"<<std::endl;
879 m_mesh_data[filename] = data;
883 errorstream<<"Client: Don't know how to load file \""
884 <<filename<<"\""<<std::endl;
888 // Virtual methods from con::PeerHandler
889 void Client::peerAdded(con::Peer *peer)
891 infostream<<"Client::peerAdded(): peer->id="
892 <<peer->id<<std::endl;
894 void Client::deletingPeer(con::Peer *peer, bool timeout)
896 infostream<<"Client::deletingPeer(): "
897 "Server Peer is getting deleted "
898 <<"(timeout="<<timeout<<")"<<std::endl;
903 u16 number of files requested
909 void Client::request_media(const std::list<std::string> &file_requests)
911 std::ostringstream os(std::ios_base::binary);
912 writeU16(os, TOSERVER_REQUEST_MEDIA);
913 size_t file_requests_size = file_requests.size();
914 assert(file_requests_size <= 0xFFFF);
915 writeU16(os, (u16) (file_requests_size & 0xFFFF));
917 for(std::list<std::string>::const_iterator i = file_requests.begin();
918 i != file_requests.end(); ++i) {
919 os<<serializeString(*i);
923 std::string s = os.str();
924 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
927 infostream<<"Client: Sending media request list to server ("
928 <<file_requests.size()<<" files)"<<std::endl;
931 void Client::received_media()
933 // notify server we received everything
934 std::ostringstream os(std::ios_base::binary);
935 writeU16(os, TOSERVER_RECEIVED_MEDIA);
936 std::string s = os.str();
937 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
940 infostream<<"Client: Notifying server that we received all media"
944 void Client::initLocalMapSaving(const Address &address,
945 const std::string &hostname,
946 bool is_local_server)
950 if (!g_settings->getBool("enable_local_map_saving") || is_local_server)
953 const std::string world_path = porting::path_user
954 + DIR_DELIM + "worlds"
955 + DIR_DELIM + "server_"
956 + hostname + "_" + to_string(address.getPort());
958 SubgameSpec gamespec;
960 if (!getWorldExists(world_path)) {
961 gamespec = findSubgame(g_settings->get("default_game"));
962 if (!gamespec.isValid())
963 gamespec = findSubgame("minimal");
965 gamespec = findWorldSubgame(world_path);
968 if (!gamespec.isValid()) {
969 errorstream << "Couldn't find subgame for local map saving." << std::endl;
973 localserver = new Server(world_path, gamespec, false, false);
974 localdb = new Database_SQLite3(&(ServerMap&)localserver->getMap(), world_path);
975 localdb->beginSave();
976 actionstream << "Local map saving started, map will be saved at '" << world_path << "'" << std::endl;
979 void Client::ReceiveAll()
981 DSTACK(__FUNCTION_NAME);
982 u32 start_ms = porting::getTimeMs();
985 // Limit time even if there would be huge amounts of data to
987 if(porting::getTimeMs() > start_ms + 100)
992 g_profiler->graphAdd("client_received_packets", 1);
994 catch(con::NoIncomingDataException &e)
998 catch(con::InvalidIncomingDataException &e)
1000 infostream<<"Client::ReceiveAll(): "
1001 "InvalidIncomingDataException: what()="
1002 <<e.what()<<std::endl;
1007 void Client::Receive()
1009 DSTACK(__FUNCTION_NAME);
1010 SharedBuffer<u8> data;
1012 u32 datasize = m_con.Receive(sender_peer_id, data);
1013 ProcessData(*data, datasize, sender_peer_id);
1017 sender_peer_id given to this shall be quaranteed to be a valid peer
1019 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
1021 DSTACK(__FUNCTION_NAME);
1023 // Ignore packets that don't even fit a command
1026 m_packetcounter.add(60000);
1030 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
1032 //infostream<<"Client: received command="<<command<<std::endl;
1033 m_packetcounter.add((u16)command);
1036 If this check is removed, be sure to change the queue
1037 system to know the ids
1039 if(sender_peer_id != PEER_ID_SERVER)
1041 infostream<<"Client::ProcessData(): Discarding data not "
1042 "coming from server: peer_id="<<sender_peer_id
1047 u8 ser_version = m_server_ser_ver;
1049 if(command == TOCLIENT_INIT)
1054 u8 deployed = data[2];
1056 infostream<<"Client: TOCLIENT_INIT received with "
1057 "deployed="<<((int)deployed&0xff)<<std::endl;
1059 if(!ser_ver_supported(deployed))
1061 infostream<<"Client: TOCLIENT_INIT: Server sent "
1062 <<"unsupported ser_fmt_ver"<<std::endl;
1066 m_server_ser_ver = deployed;
1068 // Get player position
1069 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
1070 if(datasize >= 2+1+6)
1071 playerpos_s16 = readV3S16(&data[2+1]);
1072 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
1075 // Set player position
1076 Player *player = m_env.getLocalPlayer();
1077 assert(player != NULL);
1078 player->setPosition(playerpos_f);
1080 if(datasize >= 2+1+6+8)
1083 m_map_seed = readU64(&data[2+1+6]);
1084 infostream<<"Client: received map seed: "<<m_map_seed<<std::endl;
1087 if(datasize >= 2+1+6+8+4)
1090 m_recommended_send_interval = readF1000(&data[2+1+6+8]);
1091 infostream<<"Client: received recommended send interval "
1092 <<m_recommended_send_interval<<std::endl;
1097 SharedBuffer<u8> reply(replysize);
1098 writeU16(&reply[0], TOSERVER_INIT2);
1100 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1107 if(command == TOCLIENT_ACCESS_DENIED)
1109 // The server didn't like our password. Note, this needs
1110 // to be processed even if the serialisation format has
1111 // not been agreed yet, the same as TOCLIENT_INIT.
1112 m_access_denied = true;
1113 m_access_denied_reason = L"Unknown";
1116 std::string datastring((char*)&data[2], datasize-2);
1117 std::istringstream is(datastring, std::ios_base::binary);
1118 m_access_denied_reason = deSerializeWideString(is);
1123 if(ser_version == SER_FMT_VER_INVALID)
1125 infostream<<"Client: Server serialization"
1126 " format invalid or not initialized."
1127 " Skipping incoming command="<<command<<std::endl;
1132 Handle runtime commands
1134 // there's no sane reason why we shouldn't have a player and
1135 // almost everyone needs a player reference
1136 Player *player = m_env.getLocalPlayer();
1137 assert(player != NULL);
1139 if(command == TOCLIENT_REMOVENODE)
1144 p.X = readS16(&data[2]);
1145 p.Y = readS16(&data[4]);
1146 p.Z = readS16(&data[6]);
1149 else if(command == TOCLIENT_ADDNODE)
1151 if(datasize < 8 + MapNode::serializedLength(ser_version))
1155 p.X = readS16(&data[2]);
1156 p.Y = readS16(&data[4]);
1157 p.Z = readS16(&data[6]);
1160 n.deSerialize(&data[8], ser_version);
1162 bool remove_metadata = true;
1163 u32 index = 8 + MapNode::serializedLength(ser_version);
1164 if ((datasize >= index+1) && data[index]){
1165 remove_metadata = false;
1168 addNode(p, n, remove_metadata);
1170 else if(command == TOCLIENT_BLOCKDATA)
1172 // Ignore too small packet
1177 p.X = readS16(&data[2]);
1178 p.Y = readS16(&data[4]);
1179 p.Z = readS16(&data[6]);
1181 std::string datastring((char*)&data[8], datasize-8);
1182 std::istringstream istr(datastring, std::ios_base::binary);
1187 v2s16 p2d(p.X, p.Z);
1188 sector = m_env.getMap().emergeSector(p2d);
1190 assert(sector->getPos() == p2d);
1192 block = sector->getBlockNoCreateNoEx(p.Y);
1196 Update an existing block
1198 block->deSerialize(istr, ser_version, false);
1199 block->deSerializeNetworkSpecific(istr);
1206 block = new MapBlock(&m_env.getMap(), p, this);
1207 block->deSerialize(istr, ser_version, false);
1208 block->deSerializeNetworkSpecific(istr);
1209 sector->insertBlock(block);
1212 if (localdb != NULL) {
1213 ((ServerMap&) localserver->getMap()).saveBlock(block, localdb);
1217 Add it to mesh update queue and set it to be acknowledged after update.
1219 addUpdateMeshTaskWithEdge(p, true);
1221 else if(command == TOCLIENT_INVENTORY)
1226 std::string datastring((char*)&data[2], datasize-2);
1227 std::istringstream is(datastring, std::ios_base::binary);
1229 player->inventory.deSerialize(is);
1231 m_inventory_updated = true;
1233 delete m_inventory_from_server;
1234 m_inventory_from_server = new Inventory(player->inventory);
1235 m_inventory_from_server_age = 0.0;
1238 else if(command == TOCLIENT_TIME_OF_DAY)
1243 u16 time_of_day = readU16(&data[2]);
1244 time_of_day = time_of_day % 24000;
1245 float time_speed = 0;
1247 if(datasize >= 2 + 2 + 4)
1249 time_speed = readF1000(&data[4]);
1252 // Old message; try to approximate speed of time by ourselves
1253 float time_of_day_f = (float)time_of_day / 24000.0;
1254 float tod_diff_f = 0;
1256 if(time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
1257 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0;
1259 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
1261 m_last_time_of_day_f = time_of_day_f;
1262 float time_diff = m_time_of_day_update_timer;
1263 m_time_of_day_update_timer = 0;
1265 if(m_time_of_day_set){
1266 time_speed = (3600.0*24.0) * tod_diff_f / time_diff;
1267 infostream<<"Client: Measured time_of_day speed (old format): "
1268 <<time_speed<<" tod_diff_f="<<tod_diff_f
1269 <<" time_diff="<<time_diff<<std::endl;
1273 // Update environment
1274 m_env.setTimeOfDay(time_of_day);
1275 m_env.setTimeOfDaySpeed(time_speed);
1276 m_time_of_day_set = true;
1278 u32 dr = m_env.getDayNightRatio();
1279 infostream<<"Client: time_of_day="<<time_of_day
1280 <<" time_speed="<<time_speed
1281 <<" dr="<<dr<<std::endl;
1283 else if(command == TOCLIENT_CHAT_MESSAGE)
1291 std::string datastring((char*)&data[2], datasize-2);
1292 std::istringstream is(datastring, std::ios_base::binary);
1295 is.read((char*) buf, 2);
1296 u16 len = readU16(buf);
1298 std::wstring message;
1299 for(unsigned int i=0; i<len; i++)
1301 is.read((char*)buf, 2);
1302 message += (wchar_t)readU16(buf);
1305 m_chat_queue.push_back(message);
1307 else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1311 u16 count of removed objects
1312 for all removed objects {
1315 u16 count of added objects
1316 for all added objects {
1319 u32 initialization data length
1320 string initialization data
1325 // Get all data except the command number
1326 std::string datastring((char*)&data[2], datasize-2);
1327 // Throw them in an istringstream
1328 std::istringstream is(datastring, std::ios_base::binary);
1330 // Read removed objects
1332 u16 removed_count = readU16((u8*)buf);
1333 for(unsigned int i=0; i<removed_count; i++)
1336 u16 id = readU16((u8*)buf);
1337 m_env.removeActiveObject(id);
1340 // Read added objects
1342 u16 added_count = readU16((u8*)buf);
1343 for(unsigned int i=0; i<added_count; i++)
1346 u16 id = readU16((u8*)buf);
1348 u8 type = readU8((u8*)buf);
1349 std::string data = deSerializeLongString(is);
1351 m_env.addActiveObject(id, type, data);
1354 else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1366 // Get all data except the command number
1367 std::string datastring((char*)&data[2], datasize-2);
1368 // Throw them in an istringstream
1369 std::istringstream is(datastring, std::ios_base::binary);
1371 while(is.eof() == false)
1374 u16 id = readU16((u8*)buf);
1378 size_t message_size = readU16((u8*)buf);
1379 std::string message;
1380 message.reserve(message_size);
1381 for(unsigned int i=0; i<message_size; i++)
1384 message.append(buf, 1);
1386 // Pass on to the environment
1387 m_env.processActiveObjectMessage(id, message);
1390 else if(command == TOCLIENT_MOVEMENT)
1392 std::string datastring((char*)&data[2], datasize-2);
1393 std::istringstream is(datastring, std::ios_base::binary);
1395 player->movement_acceleration_default = readF1000(is) * BS;
1396 player->movement_acceleration_air = readF1000(is) * BS;
1397 player->movement_acceleration_fast = readF1000(is) * BS;
1398 player->movement_speed_walk = readF1000(is) * BS;
1399 player->movement_speed_crouch = readF1000(is) * BS;
1400 player->movement_speed_fast = readF1000(is) * BS;
1401 player->movement_speed_climb = readF1000(is) * BS;
1402 player->movement_speed_jump = readF1000(is) * BS;
1403 player->movement_liquid_fluidity = readF1000(is) * BS;
1404 player->movement_liquid_fluidity_smooth = readF1000(is) * BS;
1405 player->movement_liquid_sink = readF1000(is) * BS;
1406 player->movement_gravity = readF1000(is) * BS;
1408 else if(command == TOCLIENT_HP)
1410 std::string datastring((char*)&data[2], datasize-2);
1411 std::istringstream is(datastring, std::ios_base::binary);
1413 u8 oldhp = player->hp;
1419 // Add to ClientEvent queue
1421 event.type = CE_PLAYER_DAMAGE;
1422 event.player_damage.amount = oldhp - hp;
1423 m_client_event_queue.push_back(event);
1426 else if(command == TOCLIENT_BREATH)
1428 std::string datastring((char*)&data[2], datasize-2);
1429 std::istringstream is(datastring, std::ios_base::binary);
1431 player->setBreath(readU16(is));
1433 else if(command == TOCLIENT_MOVE_PLAYER)
1435 std::string datastring((char*)&data[2], datasize-2);
1436 std::istringstream is(datastring, std::ios_base::binary);
1438 v3f pos = readV3F1000(is);
1439 f32 pitch = readF1000(is);
1440 f32 yaw = readF1000(is);
1441 player->setPosition(pos);
1443 infostream<<"Client got TOCLIENT_MOVE_PLAYER"
1444 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1450 Add to ClientEvent queue.
1451 This has to be sent to the main program because otherwise
1452 it would just force the pitch and yaw values to whatever
1453 the camera points to.
1456 event.type = CE_PLAYER_FORCE_MOVE;
1457 event.player_force_move.pitch = pitch;
1458 event.player_force_move.yaw = yaw;
1459 m_client_event_queue.push_back(event);
1461 // Ignore damage for a few seconds, so that the player doesn't
1462 // get damage from falling on ground
1463 m_ignore_damage_timer = 3.0;
1465 else if(command == TOCLIENT_PLAYERITEM)
1467 infostream<<"Client: WARNING: Ignoring TOCLIENT_PLAYERITEM"<<std::endl;
1469 else if(command == TOCLIENT_DEATHSCREEN)
1471 std::string datastring((char*)&data[2], datasize-2);
1472 std::istringstream is(datastring, std::ios_base::binary);
1474 bool set_camera_point_target = readU8(is);
1475 v3f camera_point_target = readV3F1000(is);
1478 event.type = CE_DEATHSCREEN;
1479 event.deathscreen.set_camera_point_target = set_camera_point_target;
1480 event.deathscreen.camera_point_target_x = camera_point_target.X;
1481 event.deathscreen.camera_point_target_y = camera_point_target.Y;
1482 event.deathscreen.camera_point_target_z = camera_point_target.Z;
1483 m_client_event_queue.push_back(event);
1485 else if(command == TOCLIENT_ANNOUNCE_MEDIA)
1487 std::string datastring((char*)&data[2], datasize-2);
1488 std::istringstream is(datastring, std::ios_base::binary);
1490 int num_files = readU16(is);
1492 infostream<<"Client: Received media announcement: packet size: "
1493 <<datasize<<std::endl;
1495 if (m_media_downloader == NULL ||
1496 m_media_downloader->isStarted()) {
1497 const char *problem = m_media_downloader ?
1498 "we already saw another announcement" :
1499 "all media has been received already";
1500 errorstream<<"Client: Received media announcement but "
1502 <<" files="<<num_files
1503 <<" size="<<datasize<<std::endl;
1507 // Mesh update thread must be stopped while
1508 // updating content definitions
1509 assert(!m_mesh_update_thread.IsRunning());
1511 for(int i=0; i<num_files; i++)
1513 std::string name = deSerializeString(is);
1514 std::string sha1_base64 = deSerializeString(is);
1515 std::string sha1_raw = base64_decode(sha1_base64);
1516 m_media_downloader->addFile(name, sha1_raw);
1519 std::vector<std::string> remote_media;
1521 Strfnd sf(deSerializeString(is));
1522 while(!sf.atend()) {
1523 std::string baseurl = trim(sf.next(","));
1525 m_media_downloader->addRemoteServer(baseurl);
1528 catch(SerializationError& e) {
1529 // not supported by server or turned off
1532 m_media_downloader->step(this);
1534 else if(command == TOCLIENT_MEDIA)
1536 std::string datastring((char*)&data[2], datasize-2);
1537 std::istringstream is(datastring, std::ios_base::binary);
1541 u16 total number of file bunches
1542 u16 index of this bunch
1543 u32 number of files in this bunch
1551 int num_bunches = readU16(is);
1552 int bunch_i = readU16(is);
1553 u32 num_files = readU32(is);
1554 infostream<<"Client: Received files: bunch "<<bunch_i<<"/"
1555 <<num_bunches<<" files="<<num_files
1556 <<" size="<<datasize<<std::endl;
1561 if (m_media_downloader == NULL ||
1562 !m_media_downloader->isStarted()) {
1563 const char *problem = m_media_downloader ?
1564 "media has not been requested" :
1565 "all media has been received already";
1566 errorstream<<"Client: Received media but "
1568 <<" bunch "<<bunch_i<<"/"<<num_bunches
1569 <<" files="<<num_files
1570 <<" size="<<datasize<<std::endl;
1574 // Mesh update thread must be stopped while
1575 // updating content definitions
1576 assert(!m_mesh_update_thread.IsRunning());
1578 for(unsigned int i=0; i<num_files; i++){
1579 std::string name = deSerializeString(is);
1580 std::string data = deSerializeLongString(is);
1581 m_media_downloader->conventionalTransferDone(
1585 else if(command == TOCLIENT_TOOLDEF)
1587 infostream<<"Client: WARNING: Ignoring TOCLIENT_TOOLDEF"<<std::endl;
1589 else if(command == TOCLIENT_NODEDEF)
1591 infostream<<"Client: Received node definitions: packet size: "
1592 <<datasize<<std::endl;
1594 // Mesh update thread must be stopped while
1595 // updating content definitions
1596 assert(!m_mesh_update_thread.IsRunning());
1598 // Decompress node definitions
1599 std::string datastring((char*)&data[2], datasize-2);
1600 std::istringstream is(datastring, std::ios_base::binary);
1601 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1602 std::ostringstream tmp_os;
1603 decompressZlib(tmp_is, tmp_os);
1605 // Deserialize node definitions
1606 std::istringstream tmp_is2(tmp_os.str());
1607 m_nodedef->deSerialize(tmp_is2);
1608 m_nodedef_received = true;
1610 else if(command == TOCLIENT_CRAFTITEMDEF)
1612 infostream<<"Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF"<<std::endl;
1614 else if(command == TOCLIENT_ITEMDEF)
1616 infostream<<"Client: Received item definitions: packet size: "
1617 <<datasize<<std::endl;
1619 // Mesh update thread must be stopped while
1620 // updating content definitions
1621 assert(!m_mesh_update_thread.IsRunning());
1623 // Decompress item definitions
1624 std::string datastring((char*)&data[2], datasize-2);
1625 std::istringstream is(datastring, std::ios_base::binary);
1626 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1627 std::ostringstream tmp_os;
1628 decompressZlib(tmp_is, tmp_os);
1630 // Deserialize node definitions
1631 std::istringstream tmp_is2(tmp_os.str());
1632 m_itemdef->deSerialize(tmp_is2);
1633 m_itemdef_received = true;
1635 else if(command == TOCLIENT_PLAY_SOUND)
1637 std::string datastring((char*)&data[2], datasize-2);
1638 std::istringstream is(datastring, std::ios_base::binary);
1640 s32 server_id = readS32(is);
1641 std::string name = deSerializeString(is);
1642 float gain = readF1000(is);
1643 int type = readU8(is); // 0=local, 1=positional, 2=object
1644 v3f pos = readV3F1000(is);
1645 u16 object_id = readU16(is);
1646 bool loop = readU8(is);
1651 client_id = m_sound->playSound(name, loop, gain);
1653 case 1: // positional
1654 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1657 ClientActiveObject *cao = m_env.getActiveObject(object_id);
1659 pos = cao->getPosition();
1660 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1661 // TODO: Set up sound to move with object
1666 if(client_id != -1){
1667 m_sounds_server_to_client[server_id] = client_id;
1668 m_sounds_client_to_server[client_id] = server_id;
1670 m_sounds_to_objects[client_id] = object_id;
1673 else if(command == TOCLIENT_STOP_SOUND)
1675 std::string datastring((char*)&data[2], datasize-2);
1676 std::istringstream is(datastring, std::ios_base::binary);
1678 s32 server_id = readS32(is);
1679 std::map<s32, int>::iterator i =
1680 m_sounds_server_to_client.find(server_id);
1681 if(i != m_sounds_server_to_client.end()){
1682 int client_id = i->second;
1683 m_sound->stopSound(client_id);
1686 else if(command == TOCLIENT_PRIVILEGES)
1688 std::string datastring((char*)&data[2], datasize-2);
1689 std::istringstream is(datastring, std::ios_base::binary);
1691 m_privileges.clear();
1692 infostream<<"Client: Privileges updated: ";
1693 u16 num_privileges = readU16(is);
1694 for(unsigned int i=0; i<num_privileges; i++){
1695 std::string priv = deSerializeString(is);
1696 m_privileges.insert(priv);
1697 infostream<<priv<<" ";
1699 infostream<<std::endl;
1701 else if(command == TOCLIENT_INVENTORY_FORMSPEC)
1703 std::string datastring((char*)&data[2], datasize-2);
1704 std::istringstream is(datastring, std::ios_base::binary);
1706 // Store formspec in LocalPlayer
1707 player->inventory_formspec = deSerializeLongString(is);
1709 else if(command == TOCLIENT_DETACHED_INVENTORY)
1711 std::string datastring((char*)&data[2], datasize-2);
1712 std::istringstream is(datastring, std::ios_base::binary);
1714 std::string name = deSerializeString(is);
1716 infostream<<"Client: Detached inventory update: \""<<name<<"\""<<std::endl;
1718 Inventory *inv = NULL;
1719 if(m_detached_inventories.count(name) > 0)
1720 inv = m_detached_inventories[name];
1722 inv = new Inventory(m_itemdef);
1723 m_detached_inventories[name] = inv;
1725 inv->deSerialize(is);
1727 else if(command == TOCLIENT_SHOW_FORMSPEC)
1729 std::string datastring((char*)&data[2], datasize-2);
1730 std::istringstream is(datastring, std::ios_base::binary);
1732 std::string formspec = deSerializeLongString(is);
1733 std::string formname = deSerializeString(is);
1736 event.type = CE_SHOW_FORMSPEC;
1737 // pointer is required as event is a struct only!
1738 // adding a std:string to a struct isn't possible
1739 event.show_formspec.formspec = new std::string(formspec);
1740 event.show_formspec.formname = new std::string(formname);
1741 m_client_event_queue.push_back(event);
1743 else if(command == TOCLIENT_SPAWN_PARTICLE)
1745 std::string datastring((char*)&data[2], datasize-2);
1746 std::istringstream is(datastring, std::ios_base::binary);
1748 v3f pos = readV3F1000(is);
1749 v3f vel = readV3F1000(is);
1750 v3f acc = readV3F1000(is);
1751 float expirationtime = readF1000(is);
1752 float size = readF1000(is);
1753 bool collisiondetection = readU8(is);
1754 std::string texture = deSerializeLongString(is);
1755 bool vertical = false;
1757 vertical = readU8(is);
1761 event.type = CE_SPAWN_PARTICLE;
1762 event.spawn_particle.pos = new v3f (pos);
1763 event.spawn_particle.vel = new v3f (vel);
1764 event.spawn_particle.acc = new v3f (acc);
1765 event.spawn_particle.expirationtime = expirationtime;
1766 event.spawn_particle.size = size;
1767 event.spawn_particle.collisiondetection = collisiondetection;
1768 event.spawn_particle.vertical = vertical;
1769 event.spawn_particle.texture = new std::string(texture);
1771 m_client_event_queue.push_back(event);
1773 else if(command == TOCLIENT_ADD_PARTICLESPAWNER)
1775 std::string datastring((char*)&data[2], datasize-2);
1776 std::istringstream is(datastring, std::ios_base::binary);
1778 u16 amount = readU16(is);
1779 float spawntime = readF1000(is);
1780 v3f minpos = readV3F1000(is);
1781 v3f maxpos = readV3F1000(is);
1782 v3f minvel = readV3F1000(is);
1783 v3f maxvel = readV3F1000(is);
1784 v3f minacc = readV3F1000(is);
1785 v3f maxacc = readV3F1000(is);
1786 float minexptime = readF1000(is);
1787 float maxexptime = readF1000(is);
1788 float minsize = readF1000(is);
1789 float maxsize = readF1000(is);
1790 bool collisiondetection = readU8(is);
1791 std::string texture = deSerializeLongString(is);
1792 u32 id = readU32(is);
1793 bool vertical = false;
1795 vertical = readU8(is);
1799 event.type = CE_ADD_PARTICLESPAWNER;
1800 event.add_particlespawner.amount = amount;
1801 event.add_particlespawner.spawntime = spawntime;
1802 event.add_particlespawner.minpos = new v3f (minpos);
1803 event.add_particlespawner.maxpos = new v3f (maxpos);
1804 event.add_particlespawner.minvel = new v3f (minvel);
1805 event.add_particlespawner.maxvel = new v3f (maxvel);
1806 event.add_particlespawner.minacc = new v3f (minacc);
1807 event.add_particlespawner.maxacc = new v3f (maxacc);
1808 event.add_particlespawner.minexptime = minexptime;
1809 event.add_particlespawner.maxexptime = maxexptime;
1810 event.add_particlespawner.minsize = minsize;
1811 event.add_particlespawner.maxsize = maxsize;
1812 event.add_particlespawner.collisiondetection = collisiondetection;
1813 event.add_particlespawner.vertical = vertical;
1814 event.add_particlespawner.texture = new std::string(texture);
1815 event.add_particlespawner.id = id;
1817 m_client_event_queue.push_back(event);
1819 else if(command == TOCLIENT_DELETE_PARTICLESPAWNER)
1821 std::string datastring((char*)&data[2], datasize-2);
1822 std::istringstream is(datastring, std::ios_base::binary);
1824 u32 id = readU16(is);
1827 event.type = CE_DELETE_PARTICLESPAWNER;
1828 event.delete_particlespawner.id = id;
1830 m_client_event_queue.push_back(event);
1832 else if(command == TOCLIENT_HUDADD)
1834 std::string datastring((char *)&data[2], datasize - 2);
1835 std::istringstream is(datastring, std::ios_base::binary);
1837 u32 id = readU32(is);
1838 u8 type = readU8(is);
1839 v2f pos = readV2F1000(is);
1840 std::string name = deSerializeString(is);
1841 v2f scale = readV2F1000(is);
1842 std::string text = deSerializeString(is);
1843 u32 number = readU32(is);
1844 u32 item = readU32(is);
1845 u32 dir = readU32(is);
1846 v2f align = readV2F1000(is);
1847 v2f offset = readV2F1000(is);
1851 world_pos = readV3F1000(is);
1852 }catch(SerializationError &e) {};
1854 size = readV2S32(is);
1855 } catch(SerializationError &e) {};
1858 event.type = CE_HUDADD;
1859 event.hudadd.id = id;
1860 event.hudadd.type = type;
1861 event.hudadd.pos = new v2f(pos);
1862 event.hudadd.name = new std::string(name);
1863 event.hudadd.scale = new v2f(scale);
1864 event.hudadd.text = new std::string(text);
1865 event.hudadd.number = number;
1866 event.hudadd.item = item;
1867 event.hudadd.dir = dir;
1868 event.hudadd.align = new v2f(align);
1869 event.hudadd.offset = new v2f(offset);
1870 event.hudadd.world_pos = new v3f(world_pos);
1871 event.hudadd.size = new v2s32(size);
1872 m_client_event_queue.push_back(event);
1874 else if(command == TOCLIENT_HUDRM)
1876 std::string datastring((char *)&data[2], datasize - 2);
1877 std::istringstream is(datastring, std::ios_base::binary);
1879 u32 id = readU32(is);
1882 event.type = CE_HUDRM;
1883 event.hudrm.id = id;
1884 m_client_event_queue.push_back(event);
1886 else if(command == TOCLIENT_HUDCHANGE)
1894 std::string datastring((char *)&data[2], datasize - 2);
1895 std::istringstream is(datastring, std::ios_base::binary);
1897 u32 id = readU32(is);
1898 u8 stat = (HudElementStat)readU8(is);
1900 if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
1901 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
1902 v2fdata = readV2F1000(is);
1903 else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
1904 sdata = deSerializeString(is);
1905 else if (stat == HUD_STAT_WORLD_POS)
1906 v3fdata = readV3F1000(is);
1907 else if (stat == HUD_STAT_SIZE )
1908 v2s32data = readV2S32(is);
1910 intdata = readU32(is);
1913 event.type = CE_HUDCHANGE;
1914 event.hudchange.id = id;
1915 event.hudchange.stat = (HudElementStat)stat;
1916 event.hudchange.v2fdata = new v2f(v2fdata);
1917 event.hudchange.v3fdata = new v3f(v3fdata);
1918 event.hudchange.sdata = new std::string(sdata);
1919 event.hudchange.data = intdata;
1920 event.hudchange.v2s32data = new v2s32(v2s32data);
1921 m_client_event_queue.push_back(event);
1923 else if(command == TOCLIENT_HUD_SET_FLAGS)
1925 std::string datastring((char *)&data[2], datasize - 2);
1926 std::istringstream is(datastring, std::ios_base::binary);
1928 u32 flags = readU32(is);
1929 u32 mask = readU32(is);
1931 player->hud_flags &= ~mask;
1932 player->hud_flags |= flags;
1934 else if(command == TOCLIENT_HUD_SET_PARAM)
1936 std::string datastring((char *)&data[2], datasize - 2);
1937 std::istringstream is(datastring, std::ios_base::binary);
1939 u16 param = readU16(is);
1940 std::string value = deSerializeString(is);
1942 if(param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) {
1943 s32 hotbar_itemcount = readS32((u8*) value.c_str());
1944 if(hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
1945 player->hud_hotbar_itemcount = hotbar_itemcount;
1947 else if (param == HUD_PARAM_HOTBAR_IMAGE) {
1948 ((LocalPlayer *) player)->hotbar_image = value;
1950 else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
1951 ((LocalPlayer *) player)->hotbar_selected_image = value;
1954 else if(command == TOCLIENT_SET_SKY)
1956 std::string datastring((char *)&data[2], datasize - 2);
1957 std::istringstream is(datastring, std::ios_base::binary);
1959 video::SColor *bgcolor = new video::SColor(readARGB8(is));
1960 std::string *type = new std::string(deSerializeString(is));
1961 u16 count = readU16(is);
1962 std::vector<std::string> *params = new std::vector<std::string>;
1964 for(size_t i=0; i<count; i++)
1965 params->push_back(deSerializeString(is));
1968 event.type = CE_SET_SKY;
1969 event.set_sky.bgcolor = bgcolor;
1970 event.set_sky.type = type;
1971 event.set_sky.params = params;
1972 m_client_event_queue.push_back(event);
1974 else if(command == TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO)
1976 std::string datastring((char *)&data[2], datasize - 2);
1977 std::istringstream is(datastring, std::ios_base::binary);
1979 bool do_override = readU8(is);
1980 float day_night_ratio_f = (float)readU16(is) / 65536;
1983 event.type = CE_OVERRIDE_DAY_NIGHT_RATIO;
1984 event.override_day_night_ratio.do_override = do_override;
1985 event.override_day_night_ratio.ratio_f = day_night_ratio_f;
1986 m_client_event_queue.push_back(event);
1988 else if(command == TOCLIENT_LOCAL_PLAYER_ANIMATIONS)
1990 std::string datastring((char *)&data[2], datasize - 2);
1991 std::istringstream is(datastring, std::ios_base::binary);
1993 LocalPlayer *player = m_env.getLocalPlayer();
1994 assert(player != NULL);
1996 player->local_animations[0] = readV2S32(is);
1997 player->local_animations[1] = readV2S32(is);
1998 player->local_animations[2] = readV2S32(is);
1999 player->local_animations[3] = readV2S32(is);
2000 player->local_animation_speed = readF1000(is);
2002 else if(command == TOCLIENT_EYE_OFFSET)
2004 std::string datastring((char *)&data[2], datasize - 2);
2005 std::istringstream is(datastring, std::ios_base::binary);
2007 LocalPlayer *player = m_env.getLocalPlayer();
2008 assert(player != NULL);
2010 player->eye_offset_first = readV3F1000(is);
2011 player->eye_offset_third = readV3F1000(is);
2015 infostream<<"Client: Ignoring unknown command "
2016 <<command<<std::endl;
2020 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
2022 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2023 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
2026 void Client::interact(u8 action, const PointedThing& pointed)
2028 if(m_state != LC_Ready){
2029 infostream<<"Client::interact() "
2030 "cancelled (not connected)"
2035 std::ostringstream os(std::ios_base::binary);
2041 [5] u32 length of the next item
2042 [9] serialized PointedThing
2044 0: start digging (from undersurface) or use
2045 1: stop digging (all parameters ignored)
2046 2: digging completed
2047 3: place block or item (to abovesurface)
2050 writeU16(os, TOSERVER_INTERACT);
2051 writeU8(os, action);
2052 writeU16(os, getPlayerItem());
2053 std::ostringstream tmp_os(std::ios::binary);
2054 pointed.serialize(tmp_os);
2055 os<<serializeLongString(tmp_os.str());
2057 std::string s = os.str();
2058 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2061 Send(0, data, true);
2064 void Client::sendNodemetaFields(v3s16 p, const std::string &formname,
2065 const std::map<std::string, std::string> &fields)
2067 std::ostringstream os(std::ios_base::binary);
2069 writeU16(os, TOSERVER_NODEMETA_FIELDS);
2071 os<<serializeString(formname);
2072 size_t fields_size = fields.size();
2073 assert(fields_size <= 0xFFFF);
2074 writeU16(os, (u16) (fields_size & 0xFFFF));
2075 for(std::map<std::string, std::string>::const_iterator
2076 i = fields.begin(); i != fields.end(); i++){
2077 const std::string &name = i->first;
2078 const std::string &value = i->second;
2079 os<<serializeString(name);
2080 os<<serializeLongString(value);
2084 std::string s = os.str();
2085 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2087 Send(0, data, true);
2090 void Client::sendInventoryFields(const std::string &formname,
2091 const std::map<std::string, std::string> &fields)
2093 std::ostringstream os(std::ios_base::binary);
2095 writeU16(os, TOSERVER_INVENTORY_FIELDS);
2096 os<<serializeString(formname);
2097 size_t fields_size = fields.size();
2098 assert(fields_size <= 0xFFFF);
2099 writeU16(os, (u16) (fields_size & 0xFFFF));
2100 for(std::map<std::string, std::string>::const_iterator
2101 i = fields.begin(); i != fields.end(); i++){
2102 const std::string &name = i->first;
2103 const std::string &value = i->second;
2104 os<<serializeString(name);
2105 os<<serializeLongString(value);
2109 std::string s = os.str();
2110 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2112 Send(0, data, true);
2115 void Client::sendInventoryAction(InventoryAction *a)
2117 std::ostringstream os(std::ios_base::binary);
2121 writeU16(buf, TOSERVER_INVENTORY_ACTION);
2122 os.write((char*)buf, 2);
2127 std::string s = os.str();
2128 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2130 Send(0, data, true);
2133 void Client::sendChatMessage(const std::wstring &message)
2135 std::ostringstream os(std::ios_base::binary);
2139 writeU16(buf, TOSERVER_CHAT_MESSAGE);
2140 os.write((char*)buf, 2);
2143 size_t messagesize = message.size();
2144 if (messagesize > 0xFFFF) {
2145 messagesize = 0xFFFF;
2147 writeU16(buf, (u16) messagesize);
2148 os.write((char*)buf, 2);
2151 for(unsigned int i=0; i<message.size(); i++)
2155 os.write((char*)buf, 2);
2159 std::string s = os.str();
2160 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2162 Send(0, data, true);
2165 void Client::sendChangePassword(const std::wstring &oldpassword,
2166 const std::wstring &newpassword)
2168 Player *player = m_env.getLocalPlayer();
2172 std::string playername = player->getName();
2173 std::string oldpwd = translatePassword(playername, oldpassword);
2174 std::string newpwd = translatePassword(playername, newpassword);
2176 std::ostringstream os(std::ios_base::binary);
2177 u8 buf[2+PASSWORD_SIZE*2];
2179 [0] u16 TOSERVER_PASSWORD
2180 [2] u8[28] old password
2181 [30] u8[28] new password
2184 writeU16(buf, TOSERVER_PASSWORD);
2185 for(unsigned int i=0;i<PASSWORD_SIZE-1;i++)
2187 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
2188 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
2190 buf[2+PASSWORD_SIZE-1] = 0;
2191 buf[30+PASSWORD_SIZE-1] = 0;
2192 os.write((char*)buf, 2+PASSWORD_SIZE*2);
2195 std::string s = os.str();
2196 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2198 Send(0, data, true);
2202 void Client::sendDamage(u8 damage)
2204 DSTACK(__FUNCTION_NAME);
2205 std::ostringstream os(std::ios_base::binary);
2207 writeU16(os, TOSERVER_DAMAGE);
2208 writeU8(os, damage);
2211 std::string s = os.str();
2212 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2214 Send(0, data, true);
2217 void Client::sendBreath(u16 breath)
2219 DSTACK(__FUNCTION_NAME);
2220 std::ostringstream os(std::ios_base::binary);
2222 writeU16(os, TOSERVER_BREATH);
2223 writeU16(os, breath);
2225 std::string s = os.str();
2226 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2228 Send(0, data, true);
2231 void Client::sendRespawn()
2233 DSTACK(__FUNCTION_NAME);
2234 std::ostringstream os(std::ios_base::binary);
2236 writeU16(os, TOSERVER_RESPAWN);
2239 std::string s = os.str();
2240 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2242 Send(0, data, true);
2245 void Client::sendReady()
2247 DSTACK(__FUNCTION_NAME);
2248 std::ostringstream os(std::ios_base::binary);
2250 writeU16(os, TOSERVER_CLIENT_READY);
2251 writeU8(os,VERSION_MAJOR);
2252 writeU8(os,VERSION_MINOR);
2253 writeU8(os,VERSION_PATCH_ORIG);
2256 writeU16(os,strlen(minetest_version_hash));
2257 os.write(minetest_version_hash,strlen(minetest_version_hash));
2260 std::string s = os.str();
2261 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2263 Send(0, data, true);
2266 void Client::sendPlayerPos()
2268 LocalPlayer *myplayer = m_env.getLocalPlayer();
2269 if(myplayer == NULL)
2272 // Save bandwidth by only updating position when something changed
2273 if(myplayer->last_position == myplayer->getPosition() &&
2274 myplayer->last_speed == myplayer->getSpeed() &&
2275 myplayer->last_pitch == myplayer->getPitch() &&
2276 myplayer->last_yaw == myplayer->getYaw() &&
2277 myplayer->last_keyPressed == myplayer->keyPressed)
2280 myplayer->last_position = myplayer->getPosition();
2281 myplayer->last_speed = myplayer->getSpeed();
2282 myplayer->last_pitch = myplayer->getPitch();
2283 myplayer->last_yaw = myplayer->getYaw();
2284 myplayer->last_keyPressed = myplayer->keyPressed;
2288 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2289 our_peer_id = m_con.GetPeerID();
2292 // Set peer id if not set already
2293 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2294 myplayer->peer_id = our_peer_id;
2295 // Check that an existing peer_id is the same as the connection's
2296 assert(myplayer->peer_id == our_peer_id);
2298 v3f pf = myplayer->getPosition();
2299 v3f sf = myplayer->getSpeed();
2300 s32 pitch = myplayer->getPitch() * 100;
2301 s32 yaw = myplayer->getYaw() * 100;
2302 u32 keyPressed = myplayer->keyPressed;
2304 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
2305 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
2309 [2] v3s32 position*100
2310 [2+12] v3s32 speed*100
2311 [2+12+12] s32 pitch*100
2312 [2+12+12+4] s32 yaw*100
2313 [2+12+12+4+4] u32 keyPressed
2315 SharedBuffer<u8> data(2+12+12+4+4+4);
2316 writeU16(&data[0], TOSERVER_PLAYERPOS);
2317 writeV3S32(&data[2], position);
2318 writeV3S32(&data[2+12], speed);
2319 writeS32(&data[2+12+12], pitch);
2320 writeS32(&data[2+12+12+4], yaw);
2321 writeU32(&data[2+12+12+4+4], keyPressed);
2322 // Send as unreliable
2323 Send(0, data, false);
2326 void Client::sendPlayerItem(u16 item)
2328 Player *myplayer = m_env.getLocalPlayer();
2329 if(myplayer == NULL)
2332 u16 our_peer_id = m_con.GetPeerID();
2334 // Set peer id if not set already
2335 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2336 myplayer->peer_id = our_peer_id;
2337 // Check that an existing peer_id is the same as the connection's
2338 assert(myplayer->peer_id == our_peer_id);
2340 SharedBuffer<u8> data(2+2);
2341 writeU16(&data[0], TOSERVER_PLAYERITEM);
2342 writeU16(&data[2], item);
2345 Send(0, data, true);
2348 void Client::removeNode(v3s16 p)
2350 std::map<v3s16, MapBlock*> modified_blocks;
2354 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
2356 catch(InvalidPositionException &e)
2360 for(std::map<v3s16, MapBlock * >::iterator
2361 i = modified_blocks.begin();
2362 i != modified_blocks.end(); ++i)
2364 addUpdateMeshTaskWithEdge(i->first, false, true);
2368 void Client::addNode(v3s16 p, MapNode n, bool remove_metadata)
2370 //TimeTaker timer1("Client::addNode()");
2372 std::map<v3s16, MapBlock*> modified_blocks;
2376 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
2377 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
2379 catch(InvalidPositionException &e)
2382 for(std::map<v3s16, MapBlock * >::iterator
2383 i = modified_blocks.begin();
2384 i != modified_blocks.end(); ++i)
2386 addUpdateMeshTaskWithEdge(i->first, false, true);
2390 void Client::setPlayerControl(PlayerControl &control)
2392 LocalPlayer *player = m_env.getLocalPlayer();
2393 assert(player != NULL);
2394 player->control = control;
2397 void Client::selectPlayerItem(u16 item)
2399 m_playeritem = item;
2400 m_inventory_updated = true;
2401 sendPlayerItem(item);
2404 // Returns true if the inventory of the local player has been
2405 // updated from the server. If it is true, it is set to false.
2406 bool Client::getLocalInventoryUpdated()
2408 bool updated = m_inventory_updated;
2409 m_inventory_updated = false;
2413 // Copies the inventory of the local player to parameter
2414 void Client::getLocalInventory(Inventory &dst)
2416 Player *player = m_env.getLocalPlayer();
2417 assert(player != NULL);
2418 dst = player->inventory;
2421 Inventory* Client::getInventory(const InventoryLocation &loc)
2424 case InventoryLocation::UNDEFINED:
2427 case InventoryLocation::CURRENT_PLAYER:
2429 Player *player = m_env.getLocalPlayer();
2430 assert(player != NULL);
2431 return &player->inventory;
2434 case InventoryLocation::PLAYER:
2436 Player *player = m_env.getPlayer(loc.name.c_str());
2439 return &player->inventory;
2442 case InventoryLocation::NODEMETA:
2444 NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p);
2447 return meta->getInventory();
2450 case InventoryLocation::DETACHED:
2452 if(m_detached_inventories.count(loc.name) == 0)
2454 return m_detached_inventories[loc.name];
2463 void Client::inventoryAction(InventoryAction *a)
2466 Send it to the server
2468 sendInventoryAction(a);
2471 Predict some local inventory changes
2473 a->clientApply(this, this);
2479 ClientActiveObject * Client::getSelectedActiveObject(
2481 v3f from_pos_f_on_map,
2482 core::line3d<f32> shootline_on_map
2485 std::vector<DistanceSortedActiveObject> objects;
2487 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
2490 // After this, the closest object is the first in the array.
2491 std::sort(objects.begin(), objects.end());
2493 for(unsigned int i=0; i<objects.size(); i++)
2495 ClientActiveObject *obj = objects[i].obj;
2497 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
2498 if(selection_box == NULL)
2501 v3f pos = obj->getPosition();
2503 core::aabbox3d<f32> offsetted_box(
2504 selection_box->MinEdge + pos,
2505 selection_box->MaxEdge + pos
2508 if(offsetted_box.intersectsWithLine(shootline_on_map))
2517 std::list<std::string> Client::getConnectedPlayerNames()
2519 return m_env.getPlayerNames();
2522 float Client::getAnimationTime()
2524 return m_animation_time;
2527 int Client::getCrackLevel()
2529 return m_crack_level;
2532 void Client::setHighlighted(v3s16 pos, bool show_highlighted)
2534 m_show_highlighted = show_highlighted;
2535 v3s16 old_highlighted_pos = m_highlighted_pos;
2536 m_highlighted_pos = pos;
2537 addUpdateMeshTaskForNode(old_highlighted_pos, false, true);
2538 addUpdateMeshTaskForNode(m_highlighted_pos, false, true);
2541 void Client::setCrack(int level, v3s16 pos)
2543 int old_crack_level = m_crack_level;
2544 v3s16 old_crack_pos = m_crack_pos;
2546 m_crack_level = level;
2549 if(old_crack_level >= 0 && (level < 0 || pos != old_crack_pos))
2552 addUpdateMeshTaskForNode(old_crack_pos, false, true);
2554 if(level >= 0 && (old_crack_level < 0 || pos != old_crack_pos))
2557 addUpdateMeshTaskForNode(pos, false, true);
2563 Player *player = m_env.getLocalPlayer();
2564 assert(player != NULL);
2568 u16 Client::getBreath()
2570 Player *player = m_env.getLocalPlayer();
2571 assert(player != NULL);
2572 return player->getBreath();
2575 bool Client::getChatMessage(std::wstring &message)
2577 if(m_chat_queue.size() == 0)
2579 message = m_chat_queue.pop_front();
2583 void Client::typeChatMessage(const std::wstring &message)
2585 // Discard empty line
2590 sendChatMessage(message);
2593 if (message[0] == L'/')
2595 m_chat_queue.push_back((std::wstring)L"issued command: " + message);
2599 LocalPlayer *player = m_env.getLocalPlayer();
2600 assert(player != NULL);
2601 std::wstring name = narrow_to_wide(player->getName());
2602 m_chat_queue.push_back((std::wstring)L"<" + name + L"> " + message);
2606 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
2608 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2613 Create a task to update the mesh of the block
2616 MeshMakeData *data = new MeshMakeData(this);
2619 //TimeTaker timer("data fill");
2621 // Debug: 1-6ms, avg=2ms
2623 data->setCrack(m_crack_level, m_crack_pos);
2624 data->setHighlighted(m_highlighted_pos, m_show_highlighted);
2625 data->setSmoothLighting(m_cache_smooth_lighting);
2628 // Add task to queue
2629 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server, urgent);
2632 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent)
2635 addUpdateMeshTask(blockpos, ack_to_server, urgent);
2637 catch(InvalidPositionException &e){}
2640 for (int i=0;i<6;i++)
2643 v3s16 p = blockpos + g_6dirs[i];
2644 addUpdateMeshTask(p, false, urgent);
2646 catch(InvalidPositionException &e){}
2650 void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool urgent)
2654 infostream<<"Client::addUpdateMeshTaskForNode(): "
2655 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2659 v3s16 blockpos = getNodeBlockPos(nodepos);
2660 v3s16 blockpos_relative = blockpos * MAP_BLOCKSIZE;
2663 addUpdateMeshTask(blockpos, ack_to_server, urgent);
2665 catch(InvalidPositionException &e){}
2668 if(nodepos.X == blockpos_relative.X){
2670 v3s16 p = blockpos + v3s16(-1,0,0);
2671 addUpdateMeshTask(p, false, urgent);
2673 catch(InvalidPositionException &e){}
2676 if(nodepos.Y == blockpos_relative.Y){
2678 v3s16 p = blockpos + v3s16(0,-1,0);
2679 addUpdateMeshTask(p, false, urgent);
2681 catch(InvalidPositionException &e){}
2684 if(nodepos.Z == blockpos_relative.Z){
2686 v3s16 p = blockpos + v3s16(0,0,-1);
2687 addUpdateMeshTask(p, false, urgent);
2689 catch(InvalidPositionException &e){}
2693 ClientEvent Client::getClientEvent()
2695 if(m_client_event_queue.size() == 0)
2698 event.type = CE_NONE;
2701 return m_client_event_queue.pop_front();
2704 float Client::mediaReceiveProgress()
2706 if (m_media_downloader)
2707 return m_media_downloader->getProgress();
2709 return 1.0; // downloader only exists when not yet done
2712 void Client::afterContentReceived(IrrlichtDevice *device, gui::IGUIFont* font)
2714 infostream<<"Client::afterContentReceived() started"<<std::endl;
2715 assert(m_itemdef_received);
2716 assert(m_nodedef_received);
2717 assert(mediaReceived());
2719 wchar_t* text = wgettext("Loading textures...");
2721 // Rebuild inherited images and recreate textures
2722 infostream<<"- Rebuilding images and textures"<<std::endl;
2723 draw_load_screen(text,device, guienv, 0, 70);
2724 m_tsrc->rebuildImagesAndTextures();
2728 infostream<<"- Rebuilding shaders"<<std::endl;
2729 text = wgettext("Rebuilding shaders...");
2730 draw_load_screen(text, device, guienv, 0, 75);
2731 m_shsrc->rebuildShaders();
2734 // Update node aliases
2735 infostream<<"- Updating node aliases"<<std::endl;
2736 text = wgettext("Initializing nodes...");
2737 draw_load_screen(text, device, guienv, 0, 80);
2738 m_nodedef->updateAliases(m_itemdef);
2739 m_nodedef->setNodeRegistrationStatus(true);
2740 m_nodedef->runNodeResolverCallbacks();
2743 // Update node textures and assign shaders to each tile
2744 infostream<<"- Updating node textures"<<std::endl;
2745 m_nodedef->updateTextures(this);
2747 // Preload item textures and meshes if configured to
2748 if(g_settings->getBool("preload_item_visuals"))
2750 verbosestream<<"Updating item textures and meshes"<<std::endl;
2751 text = wgettext("Item textures...");
2752 draw_load_screen(text, device, guienv, 0, 0);
2753 std::set<std::string> names = m_itemdef->getAll();
2754 size_t size = names.size();
2757 for(std::set<std::string>::const_iterator
2758 i = names.begin(); i != names.end(); ++i)
2760 // Asking for these caches the result
2761 m_itemdef->getInventoryTexture(*i, this);
2762 m_itemdef->getWieldMesh(*i, this);
2764 percent = (count * 100 / size * 0.2) + 80;
2765 draw_load_screen(text, device, guienv, 0, percent);
2770 // Start mesh update thread after setting up content definitions
2771 infostream<<"- Starting mesh update thread"<<std::endl;
2772 m_mesh_update_thread.Start();
2776 text = wgettext("Done!");
2777 draw_load_screen(text, device, guienv, 0, 100);
2778 infostream<<"Client::afterContentReceived() done"<<std::endl;
2782 float Client::getRTT(void)
2784 return m_con.getPeerStat(PEER_ID_SERVER,con::AVG_RTT);
2787 float Client::getCurRate(void)
2789 return ( m_con.getLocalStat(con::CUR_INC_RATE) +
2790 m_con.getLocalStat(con::CUR_DL_RATE));
2793 float Client::getAvgRate(void)
2795 return ( m_con.getLocalStat(con::AVG_INC_RATE) +
2796 m_con.getLocalStat(con::AVG_DL_RATE));
2799 void Client::makeScreenshot(IrrlichtDevice *device)
2801 irr::video::IVideoDriver *driver = device->getVideoDriver();
2802 irr::video::IImage* const raw_image = driver->createScreenShot();
2804 irr::video::IImage* const image = driver->createImage(video::ECF_R8G8B8,
2805 raw_image->getDimension());
2808 raw_image->copyTo(image);
2809 irr::c8 filename[256];
2810 snprintf(filename, sizeof(filename), "%s" DIR_DELIM "screenshot_%u.png",
2811 g_settings->get("screenshot_path").c_str(),
2812 device->getTimer()->getRealTime());
2813 std::ostringstream sstr;
2814 if (driver->writeImageToFile(image, filename)) {
2815 sstr << "Saved screenshot to '" << filename << "'";
2817 sstr << "Failed to save screenshot '" << filename << "'";
2819 m_chat_queue.push_back(narrow_to_wide(sstr.str()));
2820 infostream << sstr.str() << std::endl;
2827 // IGameDef interface
2829 IItemDefManager* Client::getItemDefManager()
2833 INodeDefManager* Client::getNodeDefManager()
2837 ICraftDefManager* Client::getCraftDefManager()
2840 //return m_craftdef;
2842 ITextureSource* Client::getTextureSource()
2846 IShaderSource* Client::getShaderSource()
2850 scene::ISceneManager* Client::getSceneManager()
2852 return m_device->getSceneManager();
2854 u16 Client::allocateUnknownNodeId(const std::string &name)
2856 errorstream<<"Client::allocateUnknownNodeId(): "
2857 <<"Client cannot allocate node IDs"<<std::endl;
2859 return CONTENT_IGNORE;
2861 ISoundManager* Client::getSoundManager()
2865 MtEventManager* Client::getEventManager()
2870 ParticleManager* Client::getParticleManager()
2872 return &m_particle_manager;
2875 scene::IAnimatedMesh* Client::getMesh(const std::string &filename)
2877 std::map<std::string, std::string>::const_iterator i =
2878 m_mesh_data.find(filename);
2879 if(i == m_mesh_data.end()){
2880 errorstream<<"Client::getMesh(): Mesh not found: \""<<filename<<"\""
2884 const std::string &data = i->second;
2885 scene::ISceneManager *smgr = m_device->getSceneManager();
2887 // Create the mesh, remove it from cache and return it
2888 // This allows unique vertex colors and other properties for each instance
2889 Buffer<char> data_rw(data.c_str(), data.size()); // Const-incorrect Irrlicht
2890 io::IFileSystem *irrfs = m_device->getFileSystem();
2891 io::IReadFile *rfile = irrfs->createMemoryReadFile(
2892 *data_rw, data_rw.getSize(), filename.c_str());
2895 scene::IAnimatedMesh *mesh = smgr->getMesh(rfile);
2897 // NOTE: By playing with Irrlicht refcounts, maybe we could cache a bunch
2898 // of uniquely named instances and re-use them
2900 smgr->getMeshCache()->removeMesh(mesh);