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 bool is_simple_singleplayer_game,
223 MapDrawControl &control,
224 IWritableTextureSource *tsrc,
225 IWritableShaderSource *shsrc,
226 IWritableItemDefManager *itemdef,
227 IWritableNodeDefManager *nodedef,
228 ISoundManager *sound,
229 MtEventManager *event,
232 m_packetcounter_timer(0.0),
233 m_connection_reinit_timer(0.1),
234 m_avg_rtt_timer(0.0),
235 m_playerpos_send_timer(0.0),
236 m_ignore_damage_timer(0.0),
243 m_mesh_update_thread(this),
245 new ClientMap(this, this, control,
246 device->getSceneManager()->getRootSceneNode(),
247 device->getSceneManager(), 666),
248 device->getSceneManager(),
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),
263 m_password(password),
264 m_access_denied(false),
265 m_itemdef_received(false),
266 m_nodedef_received(false),
267 m_media_downloader(new ClientMediaDownloader()),
268 m_time_of_day_set(false),
269 m_last_time_of_day_f(-1),
270 m_time_of_day_update_timer(0),
271 m_recommended_send_interval(0.1),
272 m_removed_sounds_check_timer(0),
279 Player *player = new LocalPlayer(this, playername);
281 m_env.addPlayer(player);
284 if (g_settings->getBool("enable_local_map_saving")
285 && !is_simple_singleplayer_game) {
286 const std::string world_path = porting::path_user + DIR_DELIM + "worlds"
287 + DIR_DELIM + "server_" + g_settings->get("address")
288 + "_" + g_settings->get("remote_port");
290 SubgameSpec gamespec;
291 if (!getWorldExists(world_path)) {
292 gamespec = findSubgame(g_settings->get("default_game"));
293 if (!gamespec.isValid())
294 gamespec = findSubgame("minimal");
296 std::string world_gameid = getWorldGameId(world_path, false);
297 gamespec = findWorldSubgame(world_path);
299 if (!gamespec.isValid()) {
300 errorstream << "Couldn't find subgame for local map saving." << std::endl;
304 localserver = new Server(world_path, gamespec, false, false);
305 localdb = new Database_SQLite3(&(ServerMap&)localserver->getMap(), world_path);
306 localdb->beginSave();
307 actionstream << "Local map saving started, map will be saved at '" << world_path << "'" << std::endl;
315 //request all client managed threads to stop
316 m_mesh_update_thread.Stop();
317 if (localdb != NULL) {
318 actionstream << "Local map saving ended" << std::endl;
323 bool Client::isShutdown()
326 if (!m_mesh_update_thread.IsRunning()) return true;
335 m_mesh_update_thread.Stop();
336 m_mesh_update_thread.Wait();
337 while(!m_mesh_update_thread.m_queue_out.empty()) {
338 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_frontNoEx();
343 delete m_inventory_from_server;
345 // Delete detached inventories
346 for(std::map<std::string, Inventory*>::iterator
347 i = m_detached_inventories.begin();
348 i != m_detached_inventories.end(); i++){
352 // cleanup 3d model meshes on client shutdown
353 while (m_device->getSceneManager()->getMeshCache()->getMeshCount() != 0) {
354 scene::IAnimatedMesh * mesh =
355 m_device->getSceneManager()->getMeshCache()->getMeshByIndex(0);
358 m_device->getSceneManager()->getMeshCache()->removeMesh(mesh);
362 void Client::connect(Address address)
364 DSTACK(__FUNCTION_NAME);
365 m_con.SetTimeoutMs(0);
366 m_con.Connect(address);
369 void Client::step(float dtime)
371 DSTACK(__FUNCTION_NAME);
377 if(m_ignore_damage_timer > dtime)
378 m_ignore_damage_timer -= dtime;
380 m_ignore_damage_timer = 0.0;
382 m_animation_time += dtime;
383 if(m_animation_time > 60.0)
384 m_animation_time -= 60.0;
386 m_time_of_day_update_timer += dtime;
394 float &counter = m_packetcounter_timer;
400 infostream << "Client packetcounter (" << m_packetcounter_timer
402 m_packetcounter.print(infostream);
403 m_packetcounter.clear();
410 Delete unused sectors
412 NOTE: This jams the game for a while because deleting sectors
416 float &counter = m_delete_unused_sectors_timer;
424 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
426 core::list<v3s16> deleted_blocks;
428 float delete_unused_sectors_timeout =
429 g_settings->getFloat("client_delete_unused_sectors_timeout");
431 // Delete sector blocks
432 /*u32 num = m_env.getMap().unloadUnusedData
433 (delete_unused_sectors_timeout,
434 true, &deleted_blocks);*/
436 // Delete whole sectors
437 m_env.getMap().unloadUnusedData
438 (delete_unused_sectors_timeout,
441 if(deleted_blocks.size() > 0)
443 /*infostream<<"Client: Deleted blocks of "<<num
444 <<" unused sectors"<<std::endl;*/
445 /*infostream<<"Client: Deleted "<<num
446 <<" unused sectors"<<std::endl;*/
452 // Env is locked so con can be locked.
453 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
455 core::list<v3s16>::Iterator i = deleted_blocks.begin();
456 core::list<v3s16> sendlist;
459 if(sendlist.size() == 255 || i == deleted_blocks.end())
461 if(sendlist.size() == 0)
470 u32 replysize = 2+1+6*sendlist.size();
471 SharedBuffer<u8> reply(replysize);
472 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
473 reply[2] = sendlist.size();
475 for(core::list<v3s16>::Iterator
476 j = sendlist.begin();
477 j != sendlist.end(); j++)
479 writeV3S16(&reply[2+1+6*k], *j);
482 m_con.Send(PEER_ID_SERVER, 1, reply, true);
484 if(i == deleted_blocks.end())
490 sendlist.push_back(*i);
497 // UGLY hack to fix 2 second startup delay caused by non existent
498 // server client startup synchronization in local server or singleplayer mode
499 static bool initial_step = true;
501 initial_step = false;
503 else if(m_state == LC_Created)
505 float &counter = m_connection_reinit_timer;
511 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
513 Player *myplayer = m_env.getLocalPlayer();
514 assert(myplayer != NULL);
515 // Send TOSERVER_INIT
516 // [0] u16 TOSERVER_INIT
517 // [2] u8 SER_FMT_VER_HIGHEST_READ
518 // [3] u8[20] player_name
519 // [23] u8[28] password (new in some version)
520 // [51] u16 minimum supported network protocol version (added sometime)
521 // [53] u16 maximum supported network protocol version (added later than the previous one)
522 SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2+2);
523 writeU16(&data[0], TOSERVER_INIT);
524 writeU8(&data[2], SER_FMT_VER_HIGHEST_READ);
526 memset((char*)&data[3], 0, PLAYERNAME_SIZE);
527 snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
529 /*infostream<<"Client: sending initial password hash: \""<<m_password<<"\""
532 memset((char*)&data[23], 0, PASSWORD_SIZE);
533 snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
535 writeU16(&data[51], CLIENT_PROTOCOL_VERSION_MIN);
536 writeU16(&data[53], CLIENT_PROTOCOL_VERSION_MAX);
538 // Send as unreliable
539 Send(1, data, false);
542 // Not connected, return
547 Do stuff if connected
551 Run Map's timers and unload unused data
553 const float map_timer_and_unload_dtime = 5.25;
554 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
556 ScopeProfiler sp(g_profiler, "Client: map timer and unload");
557 std::list<v3s16> deleted_blocks;
558 m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
559 g_settings->getFloat("client_unload_unused_data_timeout"),
562 /*if(deleted_blocks.size() > 0)
563 infostream<<"Client: Unloaded "<<deleted_blocks.size()
564 <<" unused blocks"<<std::endl;*/
568 NOTE: This loop is intentionally iterated the way it is.
571 std::list<v3s16>::iterator i = deleted_blocks.begin();
572 std::list<v3s16> sendlist;
575 if(sendlist.size() == 255 || i == deleted_blocks.end())
577 if(sendlist.size() == 0)
586 u32 replysize = 2+1+6*sendlist.size();
587 SharedBuffer<u8> reply(replysize);
588 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
589 reply[2] = sendlist.size();
591 for(std::list<v3s16>::iterator
592 j = sendlist.begin();
593 j != sendlist.end(); ++j)
595 writeV3S16(&reply[2+1+6*k], *j);
598 m_con.Send(PEER_ID_SERVER, 2, reply, true);
600 if(i == deleted_blocks.end())
606 sendlist.push_back(*i);
615 // Control local player (0ms)
616 LocalPlayer *player = m_env.getLocalPlayer();
617 assert(player != NULL);
618 player->applyControl(dtime);
628 ClientEnvEvent event = m_env.getClientEvent();
629 if(event.type == CEE_NONE)
633 else if(event.type == CEE_PLAYER_DAMAGE)
635 if(m_ignore_damage_timer <= 0)
637 u8 damage = event.player_damage.amount;
639 if(event.player_damage.send_to_server)
642 // Add to ClientEvent queue
644 event.type = CE_PLAYER_DAMAGE;
645 event.player_damage.amount = damage;
646 m_client_event_queue.push_back(event);
649 else if(event.type == CEE_PLAYER_BREATH)
651 u16 breath = event.player_breath.amount;
661 float &counter = m_avg_rtt_timer;
666 // connectedAndInitialized() is true, peer exists.
667 float avg_rtt = getRTT();
668 infostream<<"Client: avg_rtt="<<avg_rtt<<std::endl;
673 Send player position to server
676 float &counter = m_playerpos_send_timer;
678 if((m_state == LC_Ready) && (counter >= m_recommended_send_interval))
686 Replace updated meshes
689 int num_processed_meshes = 0;
690 while(!m_mesh_update_thread.m_queue_out.empty())
692 num_processed_meshes++;
693 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_frontNoEx();
694 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
697 // Delete the old mesh
698 if(block->mesh != NULL)
700 // TODO: Remove hardware buffers of meshbuffers of block->mesh
705 // Replace with the new mesh
706 block->mesh = r.mesh;
710 if(r.ack_block_to_server)
722 u32 replysize = 2+1+6;
723 SharedBuffer<u8> reply(replysize);
724 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
726 writeV3S16(&reply[3], r.p);
728 m_con.Send(PEER_ID_SERVER, 2, reply, true);
731 if(num_processed_meshes > 0)
732 g_profiler->graphAdd("num_processed_meshes", num_processed_meshes);
738 if (m_media_downloader && m_media_downloader->isStarted()) {
739 m_media_downloader->step(this);
740 if (m_media_downloader->isDone()) {
742 delete m_media_downloader;
743 m_media_downloader = NULL;
748 If the server didn't update the inventory in a while, revert
749 the local inventory (so the player notices the lag problem
750 and knows something is wrong).
752 if(m_inventory_from_server)
754 float interval = 10.0;
755 float count_before = floor(m_inventory_from_server_age / interval);
757 m_inventory_from_server_age += dtime;
759 float count_after = floor(m_inventory_from_server_age / interval);
761 if(count_after != count_before)
763 // Do this every <interval> seconds after TOCLIENT_INVENTORY
764 // Reset the locally changed inventory to the authoritative inventory
765 Player *player = m_env.getLocalPlayer();
766 player->inventory = *m_inventory_from_server;
767 m_inventory_updated = true;
772 Update positions of sounds attached to objects
775 for(std::map<int, u16>::iterator
776 i = m_sounds_to_objects.begin();
777 i != m_sounds_to_objects.end(); i++)
779 int client_id = i->first;
780 u16 object_id = i->second;
781 ClientActiveObject *cao = m_env.getActiveObject(object_id);
784 v3f pos = cao->getPosition();
785 m_sound->updateSoundPosition(client_id, pos);
790 Handle removed remotely initiated sounds
792 m_removed_sounds_check_timer += dtime;
793 if(m_removed_sounds_check_timer >= 2.32)
795 m_removed_sounds_check_timer = 0;
796 // Find removed sounds and clear references to them
797 std::set<s32> removed_server_ids;
798 for(std::map<s32, int>::iterator
799 i = m_sounds_server_to_client.begin();
800 i != m_sounds_server_to_client.end();)
802 s32 server_id = i->first;
803 int client_id = i->second;
805 if(!m_sound->soundExists(client_id)){
806 m_sounds_server_to_client.erase(server_id);
807 m_sounds_client_to_server.erase(client_id);
808 m_sounds_to_objects.erase(client_id);
809 removed_server_ids.insert(server_id);
813 if(removed_server_ids.size() != 0)
815 std::ostringstream os(std::ios_base::binary);
816 writeU16(os, TOSERVER_REMOVED_SOUNDS);
817 size_t server_ids = removed_server_ids.size();
818 assert(server_ids <= 0xFFFF);
819 writeU16(os, (u16) (server_ids & 0xFFFF));
820 for(std::set<s32>::iterator i = removed_server_ids.begin();
821 i != removed_server_ids.end(); i++)
823 std::string s = os.str();
824 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
831 bool Client::loadMedia(const std::string &data, const std::string &filename)
833 // Silly irrlicht's const-incorrectness
834 Buffer<char> data_rw(data.c_str(), data.size());
838 const char *image_ext[] = {
839 ".png", ".jpg", ".bmp", ".tga",
840 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
843 name = removeStringEnd(filename, image_ext);
846 verbosestream<<"Client: Attempting to load image "
847 <<"file \""<<filename<<"\""<<std::endl;
849 io::IFileSystem *irrfs = m_device->getFileSystem();
850 video::IVideoDriver *vdrv = m_device->getVideoDriver();
852 // Create an irrlicht memory file
853 io::IReadFile *rfile = irrfs->createMemoryReadFile(
854 *data_rw, data_rw.getSize(), "_tempreadfile");
857 video::IImage *img = vdrv->createImageFromFile(rfile);
859 errorstream<<"Client: Cannot create image from data of "
860 <<"file \""<<filename<<"\""<<std::endl;
865 m_tsrc->insertSourceImage(filename, img);
872 const char *sound_ext[] = {
873 ".0.ogg", ".1.ogg", ".2.ogg", ".3.ogg", ".4.ogg",
874 ".5.ogg", ".6.ogg", ".7.ogg", ".8.ogg", ".9.ogg",
877 name = removeStringEnd(filename, sound_ext);
880 verbosestream<<"Client: Attempting to load sound "
881 <<"file \""<<filename<<"\""<<std::endl;
882 m_sound->loadSoundData(name, data);
886 const char *model_ext[] = {
887 ".x", ".b3d", ".md2", ".obj",
890 name = removeStringEnd(filename, model_ext);
893 verbosestream<<"Client: Storing model into memory: "
894 <<"\""<<filename<<"\""<<std::endl;
895 if(m_mesh_data.count(filename))
896 errorstream<<"Multiple models with name \""<<filename.c_str()
897 <<"\" found; replacing previous model"<<std::endl;
898 m_mesh_data[filename] = data;
902 errorstream<<"Client: Don't know how to load file \""
903 <<filename<<"\""<<std::endl;
907 // Virtual methods from con::PeerHandler
908 void Client::peerAdded(con::Peer *peer)
910 infostream<<"Client::peerAdded(): peer->id="
911 <<peer->id<<std::endl;
913 void Client::deletingPeer(con::Peer *peer, bool timeout)
915 infostream<<"Client::deletingPeer(): "
916 "Server Peer is getting deleted "
917 <<"(timeout="<<timeout<<")"<<std::endl;
922 u16 number of files requested
928 void Client::request_media(const std::list<std::string> &file_requests)
930 std::ostringstream os(std::ios_base::binary);
931 writeU16(os, TOSERVER_REQUEST_MEDIA);
932 size_t file_requests_size = file_requests.size();
933 assert(file_requests_size <= 0xFFFF);
934 writeU16(os, (u16) (file_requests_size & 0xFFFF));
936 for(std::list<std::string>::const_iterator i = file_requests.begin();
937 i != file_requests.end(); ++i) {
938 os<<serializeString(*i);
942 std::string s = os.str();
943 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
946 infostream<<"Client: Sending media request list to server ("
947 <<file_requests.size()<<" files)"<<std::endl;
950 void Client::received_media()
952 // notify server we received everything
953 std::ostringstream os(std::ios_base::binary);
954 writeU16(os, TOSERVER_RECEIVED_MEDIA);
955 std::string s = os.str();
956 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
959 infostream<<"Client: Notifying server that we received all media"
963 void Client::ReceiveAll()
965 DSTACK(__FUNCTION_NAME);
966 u32 start_ms = porting::getTimeMs();
969 // Limit time even if there would be huge amounts of data to
971 if(porting::getTimeMs() > start_ms + 100)
976 g_profiler->graphAdd("client_received_packets", 1);
978 catch(con::NoIncomingDataException &e)
982 catch(con::InvalidIncomingDataException &e)
984 infostream<<"Client::ReceiveAll(): "
985 "InvalidIncomingDataException: what()="
986 <<e.what()<<std::endl;
991 void Client::Receive()
993 DSTACK(__FUNCTION_NAME);
994 SharedBuffer<u8> data;
996 u32 datasize = m_con.Receive(sender_peer_id, data);
997 ProcessData(*data, datasize, sender_peer_id);
1001 sender_peer_id given to this shall be quaranteed to be a valid peer
1003 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
1005 DSTACK(__FUNCTION_NAME);
1007 // Ignore packets that don't even fit a command
1010 m_packetcounter.add(60000);
1014 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
1016 //infostream<<"Client: received command="<<command<<std::endl;
1017 m_packetcounter.add((u16)command);
1020 If this check is removed, be sure to change the queue
1021 system to know the ids
1023 if(sender_peer_id != PEER_ID_SERVER)
1025 infostream<<"Client::ProcessData(): Discarding data not "
1026 "coming from server: peer_id="<<sender_peer_id
1031 u8 ser_version = m_server_ser_ver;
1033 if(command == TOCLIENT_INIT)
1038 u8 deployed = data[2];
1040 infostream<<"Client: TOCLIENT_INIT received with "
1041 "deployed="<<((int)deployed&0xff)<<std::endl;
1043 if(!ser_ver_supported(deployed))
1045 infostream<<"Client: TOCLIENT_INIT: Server sent "
1046 <<"unsupported ser_fmt_ver"<<std::endl;
1050 m_server_ser_ver = deployed;
1052 // Get player position
1053 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
1054 if(datasize >= 2+1+6)
1055 playerpos_s16 = readV3S16(&data[2+1]);
1056 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
1059 // Set player position
1060 Player *player = m_env.getLocalPlayer();
1061 assert(player != NULL);
1062 player->setPosition(playerpos_f);
1064 if(datasize >= 2+1+6+8)
1067 m_map_seed = readU64(&data[2+1+6]);
1068 infostream<<"Client: received map seed: "<<m_map_seed<<std::endl;
1071 if(datasize >= 2+1+6+8+4)
1074 m_recommended_send_interval = readF1000(&data[2+1+6+8]);
1075 infostream<<"Client: received recommended send interval "
1076 <<m_recommended_send_interval<<std::endl;
1081 SharedBuffer<u8> reply(replysize);
1082 writeU16(&reply[0], TOSERVER_INIT2);
1084 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1091 if(command == TOCLIENT_ACCESS_DENIED)
1093 // The server didn't like our password. Note, this needs
1094 // to be processed even if the serialisation format has
1095 // not been agreed yet, the same as TOCLIENT_INIT.
1096 m_access_denied = true;
1097 m_access_denied_reason = L"Unknown";
1100 std::string datastring((char*)&data[2], datasize-2);
1101 std::istringstream is(datastring, std::ios_base::binary);
1102 m_access_denied_reason = deSerializeWideString(is);
1107 if(ser_version == SER_FMT_VER_INVALID)
1109 infostream<<"Client: Server serialization"
1110 " format invalid or not initialized."
1111 " Skipping incoming command="<<command<<std::endl;
1116 Handle runtime commands
1118 // there's no sane reason why we shouldn't have a player and
1119 // almost everyone needs a player reference
1120 Player *player = m_env.getLocalPlayer();
1121 assert(player != NULL);
1123 if(command == TOCLIENT_REMOVENODE)
1128 p.X = readS16(&data[2]);
1129 p.Y = readS16(&data[4]);
1130 p.Z = readS16(&data[6]);
1133 else if(command == TOCLIENT_ADDNODE)
1135 if(datasize < 8 + MapNode::serializedLength(ser_version))
1139 p.X = readS16(&data[2]);
1140 p.Y = readS16(&data[4]);
1141 p.Z = readS16(&data[6]);
1144 n.deSerialize(&data[8], ser_version);
1146 bool remove_metadata = true;
1147 u32 index = 8 + MapNode::serializedLength(ser_version);
1148 if ((datasize >= index+1) && data[index]){
1149 remove_metadata = false;
1152 addNode(p, n, remove_metadata);
1154 else if(command == TOCLIENT_BLOCKDATA)
1156 // Ignore too small packet
1161 p.X = readS16(&data[2]);
1162 p.Y = readS16(&data[4]);
1163 p.Z = readS16(&data[6]);
1165 std::string datastring((char*)&data[8], datasize-8);
1166 std::istringstream istr(datastring, std::ios_base::binary);
1171 v2s16 p2d(p.X, p.Z);
1172 sector = m_env.getMap().emergeSector(p2d);
1174 assert(sector->getPos() == p2d);
1176 block = sector->getBlockNoCreateNoEx(p.Y);
1180 Update an existing block
1182 block->deSerialize(istr, ser_version, false);
1183 block->deSerializeNetworkSpecific(istr);
1190 block = new MapBlock(&m_env.getMap(), p, this);
1191 block->deSerialize(istr, ser_version, false);
1192 block->deSerializeNetworkSpecific(istr);
1193 sector->insertBlock(block);
1196 if (localdb != NULL) {
1197 ((ServerMap&) localserver->getMap()).saveBlock(block, localdb);
1201 Add it to mesh update queue and set it to be acknowledged after update.
1203 addUpdateMeshTaskWithEdge(p, true);
1205 else if(command == TOCLIENT_INVENTORY)
1210 std::string datastring((char*)&data[2], datasize-2);
1211 std::istringstream is(datastring, std::ios_base::binary);
1213 player->inventory.deSerialize(is);
1215 m_inventory_updated = true;
1217 delete m_inventory_from_server;
1218 m_inventory_from_server = new Inventory(player->inventory);
1219 m_inventory_from_server_age = 0.0;
1222 else if(command == TOCLIENT_TIME_OF_DAY)
1227 u16 time_of_day = readU16(&data[2]);
1228 time_of_day = time_of_day % 24000;
1229 float time_speed = 0;
1231 if(datasize >= 2 + 2 + 4)
1233 time_speed = readF1000(&data[4]);
1236 // Old message; try to approximate speed of time by ourselves
1237 float time_of_day_f = (float)time_of_day / 24000.0;
1238 float tod_diff_f = 0;
1240 if(time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
1241 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0;
1243 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
1245 m_last_time_of_day_f = time_of_day_f;
1246 float time_diff = m_time_of_day_update_timer;
1247 m_time_of_day_update_timer = 0;
1249 if(m_time_of_day_set){
1250 time_speed = (3600.0*24.0) * tod_diff_f / time_diff;
1251 infostream<<"Client: Measured time_of_day speed (old format): "
1252 <<time_speed<<" tod_diff_f="<<tod_diff_f
1253 <<" time_diff="<<time_diff<<std::endl;
1257 // Update environment
1258 m_env.setTimeOfDay(time_of_day);
1259 m_env.setTimeOfDaySpeed(time_speed);
1260 m_time_of_day_set = true;
1262 u32 dr = m_env.getDayNightRatio();
1263 infostream<<"Client: time_of_day="<<time_of_day
1264 <<" time_speed="<<time_speed
1265 <<" dr="<<dr<<std::endl;
1267 else if(command == TOCLIENT_CHAT_MESSAGE)
1275 std::string datastring((char*)&data[2], datasize-2);
1276 std::istringstream is(datastring, std::ios_base::binary);
1279 is.read((char*) buf, 2);
1280 u16 len = readU16(buf);
1282 std::wstring message;
1283 for(unsigned int i=0; i<len; i++)
1285 is.read((char*)buf, 2);
1286 message += (wchar_t)readU16(buf);
1289 m_chat_queue.push_back(message);
1291 else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1295 u16 count of removed objects
1296 for all removed objects {
1299 u16 count of added objects
1300 for all added objects {
1303 u32 initialization data length
1304 string initialization data
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 // Read removed objects
1316 u16 removed_count = readU16((u8*)buf);
1317 for(unsigned int i=0; i<removed_count; i++)
1320 u16 id = readU16((u8*)buf);
1321 m_env.removeActiveObject(id);
1324 // Read added objects
1326 u16 added_count = readU16((u8*)buf);
1327 for(unsigned int i=0; i<added_count; i++)
1330 u16 id = readU16((u8*)buf);
1332 u8 type = readU8((u8*)buf);
1333 std::string data = deSerializeLongString(is);
1335 m_env.addActiveObject(id, type, data);
1338 else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1350 // Get all data except the command number
1351 std::string datastring((char*)&data[2], datasize-2);
1352 // Throw them in an istringstream
1353 std::istringstream is(datastring, std::ios_base::binary);
1355 while(is.eof() == false)
1358 u16 id = readU16((u8*)buf);
1362 size_t message_size = readU16((u8*)buf);
1363 std::string message;
1364 message.reserve(message_size);
1365 for(unsigned int i=0; i<message_size; i++)
1368 message.append(buf, 1);
1370 // Pass on to the environment
1371 m_env.processActiveObjectMessage(id, message);
1374 else if(command == TOCLIENT_MOVEMENT)
1376 std::string datastring((char*)&data[2], datasize-2);
1377 std::istringstream is(datastring, std::ios_base::binary);
1379 player->movement_acceleration_default = readF1000(is) * BS;
1380 player->movement_acceleration_air = readF1000(is) * BS;
1381 player->movement_acceleration_fast = readF1000(is) * BS;
1382 player->movement_speed_walk = readF1000(is) * BS;
1383 player->movement_speed_crouch = readF1000(is) * BS;
1384 player->movement_speed_fast = readF1000(is) * BS;
1385 player->movement_speed_climb = readF1000(is) * BS;
1386 player->movement_speed_jump = readF1000(is) * BS;
1387 player->movement_liquid_fluidity = readF1000(is) * BS;
1388 player->movement_liquid_fluidity_smooth = readF1000(is) * BS;
1389 player->movement_liquid_sink = readF1000(is) * BS;
1390 player->movement_gravity = readF1000(is) * BS;
1392 else if(command == TOCLIENT_HP)
1394 std::string datastring((char*)&data[2], datasize-2);
1395 std::istringstream is(datastring, std::ios_base::binary);
1397 u8 oldhp = player->hp;
1403 // Add to ClientEvent queue
1405 event.type = CE_PLAYER_DAMAGE;
1406 event.player_damage.amount = oldhp - hp;
1407 m_client_event_queue.push_back(event);
1410 else if(command == TOCLIENT_BREATH)
1412 std::string datastring((char*)&data[2], datasize-2);
1413 std::istringstream is(datastring, std::ios_base::binary);
1415 player->setBreath(readU16(is));
1417 else if(command == TOCLIENT_MOVE_PLAYER)
1419 std::string datastring((char*)&data[2], datasize-2);
1420 std::istringstream is(datastring, std::ios_base::binary);
1422 v3f pos = readV3F1000(is);
1423 f32 pitch = readF1000(is);
1424 f32 yaw = readF1000(is);
1425 player->setPosition(pos);
1427 infostream<<"Client got TOCLIENT_MOVE_PLAYER"
1428 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1434 Add to ClientEvent queue.
1435 This has to be sent to the main program because otherwise
1436 it would just force the pitch and yaw values to whatever
1437 the camera points to.
1440 event.type = CE_PLAYER_FORCE_MOVE;
1441 event.player_force_move.pitch = pitch;
1442 event.player_force_move.yaw = yaw;
1443 m_client_event_queue.push_back(event);
1445 // Ignore damage for a few seconds, so that the player doesn't
1446 // get damage from falling on ground
1447 m_ignore_damage_timer = 3.0;
1449 else if(command == TOCLIENT_PLAYERITEM)
1451 infostream<<"Client: WARNING: Ignoring TOCLIENT_PLAYERITEM"<<std::endl;
1453 else if(command == TOCLIENT_DEATHSCREEN)
1455 std::string datastring((char*)&data[2], datasize-2);
1456 std::istringstream is(datastring, std::ios_base::binary);
1458 bool set_camera_point_target = readU8(is);
1459 v3f camera_point_target = readV3F1000(is);
1462 event.type = CE_DEATHSCREEN;
1463 event.deathscreen.set_camera_point_target = set_camera_point_target;
1464 event.deathscreen.camera_point_target_x = camera_point_target.X;
1465 event.deathscreen.camera_point_target_y = camera_point_target.Y;
1466 event.deathscreen.camera_point_target_z = camera_point_target.Z;
1467 m_client_event_queue.push_back(event);
1469 else if(command == TOCLIENT_ANNOUNCE_MEDIA)
1471 std::string datastring((char*)&data[2], datasize-2);
1472 std::istringstream is(datastring, std::ios_base::binary);
1474 int num_files = readU16(is);
1476 infostream<<"Client: Received media announcement: packet size: "
1477 <<datasize<<std::endl;
1479 if (m_media_downloader == NULL ||
1480 m_media_downloader->isStarted()) {
1481 const char *problem = m_media_downloader ?
1482 "we already saw another announcement" :
1483 "all media has been received already";
1484 errorstream<<"Client: Received media announcement but "
1486 <<" files="<<num_files
1487 <<" size="<<datasize<<std::endl;
1491 // Mesh update thread must be stopped while
1492 // updating content definitions
1493 assert(!m_mesh_update_thread.IsRunning());
1495 for(int i=0; i<num_files; i++)
1497 std::string name = deSerializeString(is);
1498 std::string sha1_base64 = deSerializeString(is);
1499 std::string sha1_raw = base64_decode(sha1_base64);
1500 m_media_downloader->addFile(name, sha1_raw);
1503 std::vector<std::string> remote_media;
1505 Strfnd sf(deSerializeString(is));
1506 while(!sf.atend()) {
1507 std::string baseurl = trim(sf.next(","));
1509 m_media_downloader->addRemoteServer(baseurl);
1512 catch(SerializationError& e) {
1513 // not supported by server or turned off
1516 m_media_downloader->step(this);
1518 else if(command == TOCLIENT_MEDIA)
1520 std::string datastring((char*)&data[2], datasize-2);
1521 std::istringstream is(datastring, std::ios_base::binary);
1525 u16 total number of file bunches
1526 u16 index of this bunch
1527 u32 number of files in this bunch
1535 int num_bunches = readU16(is);
1536 int bunch_i = readU16(is);
1537 u32 num_files = readU32(is);
1538 infostream<<"Client: Received files: bunch "<<bunch_i<<"/"
1539 <<num_bunches<<" files="<<num_files
1540 <<" size="<<datasize<<std::endl;
1545 if (m_media_downloader == NULL ||
1546 !m_media_downloader->isStarted()) {
1547 const char *problem = m_media_downloader ?
1548 "media has not been requested" :
1549 "all media has been received already";
1550 errorstream<<"Client: Received media but "
1552 <<" bunch "<<bunch_i<<"/"<<num_bunches
1553 <<" files="<<num_files
1554 <<" size="<<datasize<<std::endl;
1558 // Mesh update thread must be stopped while
1559 // updating content definitions
1560 assert(!m_mesh_update_thread.IsRunning());
1562 for(unsigned int i=0; i<num_files; i++){
1563 std::string name = deSerializeString(is);
1564 std::string data = deSerializeLongString(is);
1565 m_media_downloader->conventionalTransferDone(
1569 else if(command == TOCLIENT_TOOLDEF)
1571 infostream<<"Client: WARNING: Ignoring TOCLIENT_TOOLDEF"<<std::endl;
1573 else if(command == TOCLIENT_NODEDEF)
1575 infostream<<"Client: Received node definitions: packet size: "
1576 <<datasize<<std::endl;
1578 // Mesh update thread must be stopped while
1579 // updating content definitions
1580 assert(!m_mesh_update_thread.IsRunning());
1582 // Decompress node definitions
1583 std::string datastring((char*)&data[2], datasize-2);
1584 std::istringstream is(datastring, std::ios_base::binary);
1585 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1586 std::ostringstream tmp_os;
1587 decompressZlib(tmp_is, tmp_os);
1589 // Deserialize node definitions
1590 std::istringstream tmp_is2(tmp_os.str());
1591 m_nodedef->deSerialize(tmp_is2);
1592 m_nodedef_received = true;
1594 else if(command == TOCLIENT_CRAFTITEMDEF)
1596 infostream<<"Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF"<<std::endl;
1598 else if(command == TOCLIENT_ITEMDEF)
1600 infostream<<"Client: Received item definitions: packet size: "
1601 <<datasize<<std::endl;
1603 // Mesh update thread must be stopped while
1604 // updating content definitions
1605 assert(!m_mesh_update_thread.IsRunning());
1607 // Decompress item definitions
1608 std::string datastring((char*)&data[2], datasize-2);
1609 std::istringstream is(datastring, std::ios_base::binary);
1610 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1611 std::ostringstream tmp_os;
1612 decompressZlib(tmp_is, tmp_os);
1614 // Deserialize node definitions
1615 std::istringstream tmp_is2(tmp_os.str());
1616 m_itemdef->deSerialize(tmp_is2);
1617 m_itemdef_received = true;
1619 else if(command == TOCLIENT_PLAY_SOUND)
1621 std::string datastring((char*)&data[2], datasize-2);
1622 std::istringstream is(datastring, std::ios_base::binary);
1624 s32 server_id = readS32(is);
1625 std::string name = deSerializeString(is);
1626 float gain = readF1000(is);
1627 int type = readU8(is); // 0=local, 1=positional, 2=object
1628 v3f pos = readV3F1000(is);
1629 u16 object_id = readU16(is);
1630 bool loop = readU8(is);
1635 client_id = m_sound->playSound(name, loop, gain);
1637 case 1: // positional
1638 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1641 ClientActiveObject *cao = m_env.getActiveObject(object_id);
1643 pos = cao->getPosition();
1644 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1645 // TODO: Set up sound to move with object
1650 if(client_id != -1){
1651 m_sounds_server_to_client[server_id] = client_id;
1652 m_sounds_client_to_server[client_id] = server_id;
1654 m_sounds_to_objects[client_id] = object_id;
1657 else if(command == TOCLIENT_STOP_SOUND)
1659 std::string datastring((char*)&data[2], datasize-2);
1660 std::istringstream is(datastring, std::ios_base::binary);
1662 s32 server_id = readS32(is);
1663 std::map<s32, int>::iterator i =
1664 m_sounds_server_to_client.find(server_id);
1665 if(i != m_sounds_server_to_client.end()){
1666 int client_id = i->second;
1667 m_sound->stopSound(client_id);
1670 else if(command == TOCLIENT_PRIVILEGES)
1672 std::string datastring((char*)&data[2], datasize-2);
1673 std::istringstream is(datastring, std::ios_base::binary);
1675 m_privileges.clear();
1676 infostream<<"Client: Privileges updated: ";
1677 u16 num_privileges = readU16(is);
1678 for(unsigned int i=0; i<num_privileges; i++){
1679 std::string priv = deSerializeString(is);
1680 m_privileges.insert(priv);
1681 infostream<<priv<<" ";
1683 infostream<<std::endl;
1685 else if(command == TOCLIENT_INVENTORY_FORMSPEC)
1687 std::string datastring((char*)&data[2], datasize-2);
1688 std::istringstream is(datastring, std::ios_base::binary);
1690 // Store formspec in LocalPlayer
1691 player->inventory_formspec = deSerializeLongString(is);
1693 else if(command == TOCLIENT_DETACHED_INVENTORY)
1695 std::string datastring((char*)&data[2], datasize-2);
1696 std::istringstream is(datastring, std::ios_base::binary);
1698 std::string name = deSerializeString(is);
1700 infostream<<"Client: Detached inventory update: \""<<name<<"\""<<std::endl;
1702 Inventory *inv = NULL;
1703 if(m_detached_inventories.count(name) > 0)
1704 inv = m_detached_inventories[name];
1706 inv = new Inventory(m_itemdef);
1707 m_detached_inventories[name] = inv;
1709 inv->deSerialize(is);
1711 else if(command == TOCLIENT_SHOW_FORMSPEC)
1713 std::string datastring((char*)&data[2], datasize-2);
1714 std::istringstream is(datastring, std::ios_base::binary);
1716 std::string formspec = deSerializeLongString(is);
1717 std::string formname = deSerializeString(is);
1720 event.type = CE_SHOW_FORMSPEC;
1721 // pointer is required as event is a struct only!
1722 // adding a std:string to a struct isn't possible
1723 event.show_formspec.formspec = new std::string(formspec);
1724 event.show_formspec.formname = new std::string(formname);
1725 m_client_event_queue.push_back(event);
1727 else if(command == TOCLIENT_SPAWN_PARTICLE)
1729 std::string datastring((char*)&data[2], datasize-2);
1730 std::istringstream is(datastring, std::ios_base::binary);
1732 v3f pos = readV3F1000(is);
1733 v3f vel = readV3F1000(is);
1734 v3f acc = readV3F1000(is);
1735 float expirationtime = readF1000(is);
1736 float size = readF1000(is);
1737 bool collisiondetection = readU8(is);
1738 std::string texture = deSerializeLongString(is);
1739 bool vertical = false;
1741 vertical = readU8(is);
1745 event.type = CE_SPAWN_PARTICLE;
1746 event.spawn_particle.pos = new v3f (pos);
1747 event.spawn_particle.vel = new v3f (vel);
1748 event.spawn_particle.acc = new v3f (acc);
1749 event.spawn_particle.expirationtime = expirationtime;
1750 event.spawn_particle.size = size;
1751 event.spawn_particle.collisiondetection = collisiondetection;
1752 event.spawn_particle.vertical = vertical;
1753 event.spawn_particle.texture = new std::string(texture);
1755 m_client_event_queue.push_back(event);
1757 else if(command == TOCLIENT_ADD_PARTICLESPAWNER)
1759 std::string datastring((char*)&data[2], datasize-2);
1760 std::istringstream is(datastring, std::ios_base::binary);
1762 u16 amount = readU16(is);
1763 float spawntime = readF1000(is);
1764 v3f minpos = readV3F1000(is);
1765 v3f maxpos = readV3F1000(is);
1766 v3f minvel = readV3F1000(is);
1767 v3f maxvel = readV3F1000(is);
1768 v3f minacc = readV3F1000(is);
1769 v3f maxacc = readV3F1000(is);
1770 float minexptime = readF1000(is);
1771 float maxexptime = readF1000(is);
1772 float minsize = readF1000(is);
1773 float maxsize = readF1000(is);
1774 bool collisiondetection = readU8(is);
1775 std::string texture = deSerializeLongString(is);
1776 u32 id = readU32(is);
1777 bool vertical = false;
1779 vertical = readU8(is);
1783 event.type = CE_ADD_PARTICLESPAWNER;
1784 event.add_particlespawner.amount = amount;
1785 event.add_particlespawner.spawntime = spawntime;
1786 event.add_particlespawner.minpos = new v3f (minpos);
1787 event.add_particlespawner.maxpos = new v3f (maxpos);
1788 event.add_particlespawner.minvel = new v3f (minvel);
1789 event.add_particlespawner.maxvel = new v3f (maxvel);
1790 event.add_particlespawner.minacc = new v3f (minacc);
1791 event.add_particlespawner.maxacc = new v3f (maxacc);
1792 event.add_particlespawner.minexptime = minexptime;
1793 event.add_particlespawner.maxexptime = maxexptime;
1794 event.add_particlespawner.minsize = minsize;
1795 event.add_particlespawner.maxsize = maxsize;
1796 event.add_particlespawner.collisiondetection = collisiondetection;
1797 event.add_particlespawner.vertical = vertical;
1798 event.add_particlespawner.texture = new std::string(texture);
1799 event.add_particlespawner.id = id;
1801 m_client_event_queue.push_back(event);
1803 else if(command == TOCLIENT_DELETE_PARTICLESPAWNER)
1805 std::string datastring((char*)&data[2], datasize-2);
1806 std::istringstream is(datastring, std::ios_base::binary);
1808 u32 id = readU16(is);
1811 event.type = CE_DELETE_PARTICLESPAWNER;
1812 event.delete_particlespawner.id = id;
1814 m_client_event_queue.push_back(event);
1816 else if(command == TOCLIENT_HUDADD)
1818 std::string datastring((char *)&data[2], datasize - 2);
1819 std::istringstream is(datastring, std::ios_base::binary);
1821 u32 id = readU32(is);
1822 u8 type = readU8(is);
1823 v2f pos = readV2F1000(is);
1824 std::string name = deSerializeString(is);
1825 v2f scale = readV2F1000(is);
1826 std::string text = deSerializeString(is);
1827 u32 number = readU32(is);
1828 u32 item = readU32(is);
1829 u32 dir = readU32(is);
1830 v2f align = readV2F1000(is);
1831 v2f offset = readV2F1000(is);
1835 world_pos = readV3F1000(is);
1836 }catch(SerializationError &e) {};
1838 size = readV2S32(is);
1839 } catch(SerializationError &e) {};
1842 event.type = CE_HUDADD;
1843 event.hudadd.id = id;
1844 event.hudadd.type = type;
1845 event.hudadd.pos = new v2f(pos);
1846 event.hudadd.name = new std::string(name);
1847 event.hudadd.scale = new v2f(scale);
1848 event.hudadd.text = new std::string(text);
1849 event.hudadd.number = number;
1850 event.hudadd.item = item;
1851 event.hudadd.dir = dir;
1852 event.hudadd.align = new v2f(align);
1853 event.hudadd.offset = new v2f(offset);
1854 event.hudadd.world_pos = new v3f(world_pos);
1855 event.hudadd.size = new v2s32(size);
1856 m_client_event_queue.push_back(event);
1858 else if(command == TOCLIENT_HUDRM)
1860 std::string datastring((char *)&data[2], datasize - 2);
1861 std::istringstream is(datastring, std::ios_base::binary);
1863 u32 id = readU32(is);
1866 event.type = CE_HUDRM;
1867 event.hudrm.id = id;
1868 m_client_event_queue.push_back(event);
1870 else if(command == TOCLIENT_HUDCHANGE)
1878 std::string datastring((char *)&data[2], datasize - 2);
1879 std::istringstream is(datastring, std::ios_base::binary);
1881 u32 id = readU32(is);
1882 u8 stat = (HudElementStat)readU8(is);
1884 if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
1885 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
1886 v2fdata = readV2F1000(is);
1887 else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
1888 sdata = deSerializeString(is);
1889 else if (stat == HUD_STAT_WORLD_POS)
1890 v3fdata = readV3F1000(is);
1891 else if (stat == HUD_STAT_SIZE )
1892 v2s32data = readV2S32(is);
1894 intdata = readU32(is);
1897 event.type = CE_HUDCHANGE;
1898 event.hudchange.id = id;
1899 event.hudchange.stat = (HudElementStat)stat;
1900 event.hudchange.v2fdata = new v2f(v2fdata);
1901 event.hudchange.v3fdata = new v3f(v3fdata);
1902 event.hudchange.sdata = new std::string(sdata);
1903 event.hudchange.data = intdata;
1904 event.hudchange.v2s32data = new v2s32(v2s32data);
1905 m_client_event_queue.push_back(event);
1907 else if(command == TOCLIENT_HUD_SET_FLAGS)
1909 std::string datastring((char *)&data[2], datasize - 2);
1910 std::istringstream is(datastring, std::ios_base::binary);
1912 u32 flags = readU32(is);
1913 u32 mask = readU32(is);
1915 player->hud_flags &= ~mask;
1916 player->hud_flags |= flags;
1918 else if(command == TOCLIENT_HUD_SET_PARAM)
1920 std::string datastring((char *)&data[2], datasize - 2);
1921 std::istringstream is(datastring, std::ios_base::binary);
1923 u16 param = readU16(is);
1924 std::string value = deSerializeString(is);
1926 if(param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) {
1927 s32 hotbar_itemcount = readS32((u8*) value.c_str());
1928 if(hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
1929 player->hud_hotbar_itemcount = hotbar_itemcount;
1931 else if (param == HUD_PARAM_HOTBAR_IMAGE) {
1932 ((LocalPlayer *) player)->hotbar_image = value;
1934 else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
1935 ((LocalPlayer *) player)->hotbar_selected_image = value;
1938 else if(command == TOCLIENT_SET_SKY)
1940 std::string datastring((char *)&data[2], datasize - 2);
1941 std::istringstream is(datastring, std::ios_base::binary);
1943 video::SColor *bgcolor = new video::SColor(readARGB8(is));
1944 std::string *type = new std::string(deSerializeString(is));
1945 u16 count = readU16(is);
1946 std::vector<std::string> *params = new std::vector<std::string>;
1948 for(size_t i=0; i<count; i++)
1949 params->push_back(deSerializeString(is));
1952 event.type = CE_SET_SKY;
1953 event.set_sky.bgcolor = bgcolor;
1954 event.set_sky.type = type;
1955 event.set_sky.params = params;
1956 m_client_event_queue.push_back(event);
1958 else if(command == TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO)
1960 std::string datastring((char *)&data[2], datasize - 2);
1961 std::istringstream is(datastring, std::ios_base::binary);
1963 bool do_override = readU8(is);
1964 float day_night_ratio_f = (float)readU16(is) / 65536;
1967 event.type = CE_OVERRIDE_DAY_NIGHT_RATIO;
1968 event.override_day_night_ratio.do_override = do_override;
1969 event.override_day_night_ratio.ratio_f = day_night_ratio_f;
1970 m_client_event_queue.push_back(event);
1972 else if(command == TOCLIENT_LOCAL_PLAYER_ANIMATIONS)
1974 std::string datastring((char *)&data[2], datasize - 2);
1975 std::istringstream is(datastring, std::ios_base::binary);
1977 LocalPlayer *player = m_env.getLocalPlayer();
1978 assert(player != NULL);
1980 player->local_animations[0] = readV2S32(is);
1981 player->local_animations[1] = readV2S32(is);
1982 player->local_animations[2] = readV2S32(is);
1983 player->local_animations[3] = readV2S32(is);
1984 player->local_animation_speed = readF1000(is);
1986 else if(command == TOCLIENT_EYE_OFFSET)
1988 std::string datastring((char *)&data[2], datasize - 2);
1989 std::istringstream is(datastring, std::ios_base::binary);
1991 LocalPlayer *player = m_env.getLocalPlayer();
1992 assert(player != NULL);
1994 player->eye_offset_first = readV3F1000(is);
1995 player->eye_offset_third = readV3F1000(is);
1999 infostream<<"Client: Ignoring unknown command "
2000 <<command<<std::endl;
2004 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
2006 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2007 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
2010 void Client::interact(u8 action, const PointedThing& pointed)
2012 if(m_state != LC_Ready){
2013 infostream<<"Client::interact() "
2014 "cancelled (not connected)"
2019 std::ostringstream os(std::ios_base::binary);
2025 [5] u32 length of the next item
2026 [9] serialized PointedThing
2028 0: start digging (from undersurface) or use
2029 1: stop digging (all parameters ignored)
2030 2: digging completed
2031 3: place block or item (to abovesurface)
2034 writeU16(os, TOSERVER_INTERACT);
2035 writeU8(os, action);
2036 writeU16(os, getPlayerItem());
2037 std::ostringstream tmp_os(std::ios::binary);
2038 pointed.serialize(tmp_os);
2039 os<<serializeLongString(tmp_os.str());
2041 std::string s = os.str();
2042 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2045 Send(0, data, true);
2048 void Client::sendNodemetaFields(v3s16 p, const std::string &formname,
2049 const std::map<std::string, std::string> &fields)
2051 std::ostringstream os(std::ios_base::binary);
2053 writeU16(os, TOSERVER_NODEMETA_FIELDS);
2055 os<<serializeString(formname);
2056 size_t fields_size = fields.size();
2057 assert(fields_size <= 0xFFFF);
2058 writeU16(os, (u16) (fields_size & 0xFFFF));
2059 for(std::map<std::string, std::string>::const_iterator
2060 i = fields.begin(); i != fields.end(); i++){
2061 const std::string &name = i->first;
2062 const std::string &value = i->second;
2063 os<<serializeString(name);
2064 os<<serializeLongString(value);
2068 std::string s = os.str();
2069 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2071 Send(0, data, true);
2074 void Client::sendInventoryFields(const std::string &formname,
2075 const std::map<std::string, std::string> &fields)
2077 std::ostringstream os(std::ios_base::binary);
2079 writeU16(os, TOSERVER_INVENTORY_FIELDS);
2080 os<<serializeString(formname);
2081 size_t fields_size = fields.size();
2082 assert(fields_size <= 0xFFFF);
2083 writeU16(os, (u16) (fields_size & 0xFFFF));
2084 for(std::map<std::string, std::string>::const_iterator
2085 i = fields.begin(); i != fields.end(); i++){
2086 const std::string &name = i->first;
2087 const std::string &value = i->second;
2088 os<<serializeString(name);
2089 os<<serializeLongString(value);
2093 std::string s = os.str();
2094 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2096 Send(0, data, true);
2099 void Client::sendInventoryAction(InventoryAction *a)
2101 std::ostringstream os(std::ios_base::binary);
2105 writeU16(buf, TOSERVER_INVENTORY_ACTION);
2106 os.write((char*)buf, 2);
2111 std::string s = os.str();
2112 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2114 Send(0, data, true);
2117 void Client::sendChatMessage(const std::wstring &message)
2119 std::ostringstream os(std::ios_base::binary);
2123 writeU16(buf, TOSERVER_CHAT_MESSAGE);
2124 os.write((char*)buf, 2);
2127 size_t messagesize = message.size();
2128 if (messagesize > 0xFFFF) {
2129 messagesize = 0xFFFF;
2131 writeU16(buf, (u16) messagesize);
2132 os.write((char*)buf, 2);
2135 for(unsigned int i=0; i<message.size(); i++)
2139 os.write((char*)buf, 2);
2143 std::string s = os.str();
2144 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2146 Send(0, data, true);
2149 void Client::sendChangePassword(const std::wstring &oldpassword,
2150 const std::wstring &newpassword)
2152 Player *player = m_env.getLocalPlayer();
2156 std::string playername = player->getName();
2157 std::string oldpwd = translatePassword(playername, oldpassword);
2158 std::string newpwd = translatePassword(playername, newpassword);
2160 std::ostringstream os(std::ios_base::binary);
2161 u8 buf[2+PASSWORD_SIZE*2];
2163 [0] u16 TOSERVER_PASSWORD
2164 [2] u8[28] old password
2165 [30] u8[28] new password
2168 writeU16(buf, TOSERVER_PASSWORD);
2169 for(unsigned int i=0;i<PASSWORD_SIZE-1;i++)
2171 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
2172 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
2174 buf[2+PASSWORD_SIZE-1] = 0;
2175 buf[30+PASSWORD_SIZE-1] = 0;
2176 os.write((char*)buf, 2+PASSWORD_SIZE*2);
2179 std::string s = os.str();
2180 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2182 Send(0, data, true);
2186 void Client::sendDamage(u8 damage)
2188 DSTACK(__FUNCTION_NAME);
2189 std::ostringstream os(std::ios_base::binary);
2191 writeU16(os, TOSERVER_DAMAGE);
2192 writeU8(os, damage);
2195 std::string s = os.str();
2196 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2198 Send(0, data, true);
2201 void Client::sendBreath(u16 breath)
2203 DSTACK(__FUNCTION_NAME);
2204 std::ostringstream os(std::ios_base::binary);
2206 writeU16(os, TOSERVER_BREATH);
2207 writeU16(os, breath);
2209 std::string s = os.str();
2210 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2212 Send(0, data, true);
2215 void Client::sendRespawn()
2217 DSTACK(__FUNCTION_NAME);
2218 std::ostringstream os(std::ios_base::binary);
2220 writeU16(os, TOSERVER_RESPAWN);
2223 std::string s = os.str();
2224 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2226 Send(0, data, true);
2229 void Client::sendReady()
2231 DSTACK(__FUNCTION_NAME);
2232 std::ostringstream os(std::ios_base::binary);
2234 writeU16(os, TOSERVER_CLIENT_READY);
2235 writeU8(os,VERSION_MAJOR);
2236 writeU8(os,VERSION_MINOR);
2237 writeU8(os,VERSION_PATCH_ORIG);
2240 writeU16(os,strlen(minetest_version_hash));
2241 os.write(minetest_version_hash,strlen(minetest_version_hash));
2244 std::string s = os.str();
2245 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2247 Send(0, data, true);
2250 void Client::sendPlayerPos()
2252 LocalPlayer *myplayer = m_env.getLocalPlayer();
2253 if(myplayer == NULL)
2256 // Save bandwidth by only updating position when something changed
2257 if(myplayer->last_position == myplayer->getPosition() &&
2258 myplayer->last_speed == myplayer->getSpeed() &&
2259 myplayer->last_pitch == myplayer->getPitch() &&
2260 myplayer->last_yaw == myplayer->getYaw() &&
2261 myplayer->last_keyPressed == myplayer->keyPressed)
2264 myplayer->last_position = myplayer->getPosition();
2265 myplayer->last_speed = myplayer->getSpeed();
2266 myplayer->last_pitch = myplayer->getPitch();
2267 myplayer->last_yaw = myplayer->getYaw();
2268 myplayer->last_keyPressed = myplayer->keyPressed;
2272 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2273 our_peer_id = m_con.GetPeerID();
2276 // Set peer id if not set already
2277 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2278 myplayer->peer_id = our_peer_id;
2279 // Check that an existing peer_id is the same as the connection's
2280 assert(myplayer->peer_id == our_peer_id);
2282 v3f pf = myplayer->getPosition();
2283 v3f sf = myplayer->getSpeed();
2284 s32 pitch = myplayer->getPitch() * 100;
2285 s32 yaw = myplayer->getYaw() * 100;
2286 u32 keyPressed = myplayer->keyPressed;
2288 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
2289 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
2293 [2] v3s32 position*100
2294 [2+12] v3s32 speed*100
2295 [2+12+12] s32 pitch*100
2296 [2+12+12+4] s32 yaw*100
2297 [2+12+12+4+4] u32 keyPressed
2299 SharedBuffer<u8> data(2+12+12+4+4+4);
2300 writeU16(&data[0], TOSERVER_PLAYERPOS);
2301 writeV3S32(&data[2], position);
2302 writeV3S32(&data[2+12], speed);
2303 writeS32(&data[2+12+12], pitch);
2304 writeS32(&data[2+12+12+4], yaw);
2305 writeU32(&data[2+12+12+4+4], keyPressed);
2306 // Send as unreliable
2307 Send(0, data, false);
2310 void Client::sendPlayerItem(u16 item)
2312 Player *myplayer = m_env.getLocalPlayer();
2313 if(myplayer == NULL)
2316 u16 our_peer_id = m_con.GetPeerID();
2318 // Set peer id if not set already
2319 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2320 myplayer->peer_id = our_peer_id;
2321 // Check that an existing peer_id is the same as the connection's
2322 assert(myplayer->peer_id == our_peer_id);
2324 SharedBuffer<u8> data(2+2);
2325 writeU16(&data[0], TOSERVER_PLAYERITEM);
2326 writeU16(&data[2], item);
2329 Send(0, data, true);
2332 void Client::removeNode(v3s16 p)
2334 std::map<v3s16, MapBlock*> modified_blocks;
2338 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
2340 catch(InvalidPositionException &e)
2344 for(std::map<v3s16, MapBlock * >::iterator
2345 i = modified_blocks.begin();
2346 i != modified_blocks.end(); ++i)
2348 addUpdateMeshTask(i->first, false, false);
2350 // add urgent task to update the modified node
2351 addUpdateMeshTaskForNode(p, false, true);
2354 void Client::addNode(v3s16 p, MapNode n, bool remove_metadata)
2356 //TimeTaker timer1("Client::addNode()");
2358 std::map<v3s16, MapBlock*> modified_blocks;
2362 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
2363 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
2365 catch(InvalidPositionException &e)
2368 for(std::map<v3s16, MapBlock * >::iterator
2369 i = modified_blocks.begin();
2370 i != modified_blocks.end(); ++i)
2372 addUpdateMeshTask(i->first, false, false);
2376 void Client::setPlayerControl(PlayerControl &control)
2378 LocalPlayer *player = m_env.getLocalPlayer();
2379 assert(player != NULL);
2380 player->control = control;
2383 void Client::selectPlayerItem(u16 item)
2385 m_playeritem = item;
2386 m_inventory_updated = true;
2387 sendPlayerItem(item);
2390 // Returns true if the inventory of the local player has been
2391 // updated from the server. If it is true, it is set to false.
2392 bool Client::getLocalInventoryUpdated()
2394 bool updated = m_inventory_updated;
2395 m_inventory_updated = false;
2399 // Copies the inventory of the local player to parameter
2400 void Client::getLocalInventory(Inventory &dst)
2402 Player *player = m_env.getLocalPlayer();
2403 assert(player != NULL);
2404 dst = player->inventory;
2407 Inventory* Client::getInventory(const InventoryLocation &loc)
2410 case InventoryLocation::UNDEFINED:
2413 case InventoryLocation::CURRENT_PLAYER:
2415 Player *player = m_env.getLocalPlayer();
2416 assert(player != NULL);
2417 return &player->inventory;
2420 case InventoryLocation::PLAYER:
2422 Player *player = m_env.getPlayer(loc.name.c_str());
2425 return &player->inventory;
2428 case InventoryLocation::NODEMETA:
2430 NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p);
2433 return meta->getInventory();
2436 case InventoryLocation::DETACHED:
2438 if(m_detached_inventories.count(loc.name) == 0)
2440 return m_detached_inventories[loc.name];
2449 void Client::inventoryAction(InventoryAction *a)
2452 Send it to the server
2454 sendInventoryAction(a);
2457 Predict some local inventory changes
2459 a->clientApply(this, this);
2465 ClientActiveObject * Client::getSelectedActiveObject(
2467 v3f from_pos_f_on_map,
2468 core::line3d<f32> shootline_on_map
2471 std::vector<DistanceSortedActiveObject> objects;
2473 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
2476 // After this, the closest object is the first in the array.
2477 std::sort(objects.begin(), objects.end());
2479 for(unsigned int i=0; i<objects.size(); i++)
2481 ClientActiveObject *obj = objects[i].obj;
2483 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
2484 if(selection_box == NULL)
2487 v3f pos = obj->getPosition();
2489 core::aabbox3d<f32> offsetted_box(
2490 selection_box->MinEdge + pos,
2491 selection_box->MaxEdge + pos
2494 if(offsetted_box.intersectsWithLine(shootline_on_map))
2503 std::list<std::string> Client::getConnectedPlayerNames()
2505 return m_env.getPlayerNames();
2508 float Client::getAnimationTime()
2510 return m_animation_time;
2513 int Client::getCrackLevel()
2515 return m_crack_level;
2518 void Client::setHighlighted(v3s16 pos, bool show_hud)
2520 m_show_hud = show_hud;
2521 v3s16 old_highlighted_pos = m_highlighted_pos;
2522 m_highlighted_pos = pos;
2523 addUpdateMeshTaskForNode(old_highlighted_pos, false, true);
2524 addUpdateMeshTaskForNode(m_highlighted_pos, false, true);
2527 void Client::setCrack(int level, v3s16 pos)
2529 int old_crack_level = m_crack_level;
2530 v3s16 old_crack_pos = m_crack_pos;
2532 m_crack_level = level;
2535 if(old_crack_level >= 0 && (level < 0 || pos != old_crack_pos))
2538 addUpdateMeshTaskForNode(old_crack_pos, false, true);
2540 if(level >= 0 && (old_crack_level < 0 || pos != old_crack_pos))
2543 addUpdateMeshTaskForNode(pos, false, true);
2549 Player *player = m_env.getLocalPlayer();
2550 assert(player != NULL);
2554 u16 Client::getBreath()
2556 Player *player = m_env.getLocalPlayer();
2557 assert(player != NULL);
2558 return player->getBreath();
2561 bool Client::getChatMessage(std::wstring &message)
2563 if(m_chat_queue.size() == 0)
2565 message = m_chat_queue.pop_front();
2569 void Client::typeChatMessage(const std::wstring &message)
2571 // Discard empty line
2576 sendChatMessage(message);
2579 if (message[0] == L'/')
2581 m_chat_queue.push_back((std::wstring)L"issued command: " + message);
2585 LocalPlayer *player = m_env.getLocalPlayer();
2586 assert(player != NULL);
2587 std::wstring name = narrow_to_wide(player->getName());
2588 m_chat_queue.push_back((std::wstring)L"<" + name + L"> " + message);
2592 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
2594 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2599 Create a task to update the mesh of the block
2602 MeshMakeData *data = new MeshMakeData(this);
2605 //TimeTaker timer("data fill");
2607 // Debug: 1-6ms, avg=2ms
2609 data->setCrack(m_crack_level, m_crack_pos);
2610 data->setHighlighted(m_highlighted_pos, m_show_hud);
2611 data->setSmoothLighting(g_settings->getBool("smooth_lighting"));
2614 // Add task to queue
2615 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server, urgent);
2618 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent)
2621 v3s16 p = blockpos + v3s16(0,0,0);
2622 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2623 addUpdateMeshTask(p, ack_to_server, urgent);
2625 catch(InvalidPositionException &e){}
2628 for (int i=0;i<6;i++)
2631 v3s16 p = blockpos + g_6dirs[i];
2632 addUpdateMeshTask(p, false, urgent);
2634 catch(InvalidPositionException &e){}
2638 void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool urgent)
2642 infostream<<"Client::addUpdateMeshTaskForNode(): "
2643 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2647 v3s16 blockpos = getNodeBlockPos(nodepos);
2648 v3s16 blockpos_relative = blockpos * MAP_BLOCKSIZE;
2651 v3s16 p = blockpos + v3s16(0,0,0);
2652 addUpdateMeshTask(p, ack_to_server, urgent);
2654 catch(InvalidPositionException &e){}
2657 if(nodepos.X == blockpos_relative.X){
2659 v3s16 p = blockpos + v3s16(-1,0,0);
2660 addUpdateMeshTask(p, false, urgent);
2662 catch(InvalidPositionException &e){}
2665 if(nodepos.Y == blockpos_relative.Y){
2667 v3s16 p = blockpos + v3s16(0,-1,0);
2668 addUpdateMeshTask(p, false, urgent);
2670 catch(InvalidPositionException &e){}
2673 if(nodepos.Z == blockpos_relative.Z){
2675 v3s16 p = blockpos + v3s16(0,0,-1);
2676 addUpdateMeshTask(p, false, urgent);
2678 catch(InvalidPositionException &e){}
2682 ClientEvent Client::getClientEvent()
2684 if(m_client_event_queue.size() == 0)
2687 event.type = CE_NONE;
2690 return m_client_event_queue.pop_front();
2693 float Client::mediaReceiveProgress()
2695 if (m_media_downloader)
2696 return m_media_downloader->getProgress();
2698 return 1.0; // downloader only exists when not yet done
2701 void Client::afterContentReceived(IrrlichtDevice *device, gui::IGUIFont* font)
2703 infostream<<"Client::afterContentReceived() started"<<std::endl;
2704 assert(m_itemdef_received);
2705 assert(m_nodedef_received);
2706 assert(mediaReceived());
2708 // Rebuild inherited images and recreate textures
2709 infostream<<"- Rebuilding images and textures"<<std::endl;
2710 m_tsrc->rebuildImagesAndTextures();
2713 infostream<<"- Rebuilding shaders"<<std::endl;
2714 m_shsrc->rebuildShaders();
2716 // Update node aliases
2717 infostream<<"- Updating node aliases"<<std::endl;
2718 m_nodedef->updateAliases(m_itemdef);
2720 // Update node textures and assign shaders to each tile
2721 infostream<<"- Updating node textures"<<std::endl;
2722 m_nodedef->updateTextures(this);
2724 // Preload item textures and meshes if configured to
2725 if(g_settings->getBool("preload_item_visuals"))
2727 verbosestream<<"Updating item textures and meshes"<<std::endl;
2728 wchar_t* text = wgettext("Item textures...");
2729 draw_load_screen(text, device, guienv, 0, 0);
2730 std::set<std::string> names = m_itemdef->getAll();
2731 size_t size = names.size();
2734 for(std::set<std::string>::const_iterator
2735 i = names.begin(); i != names.end(); ++i){
2736 // Asking for these caches the result
2737 m_itemdef->getInventoryTexture(*i, this);
2738 m_itemdef->getWieldMesh(*i, this);
2740 percent = count*100/size;
2741 if (count%50 == 0) // only update every 50 item
2742 draw_load_screen(text, device, guienv, 0, percent);
2747 // Start mesh update thread after setting up content definitions
2748 infostream<<"- Starting mesh update thread"<<std::endl;
2749 m_mesh_update_thread.Start();
2753 infostream<<"Client::afterContentReceived() done"<<std::endl;
2756 float Client::getRTT(void)
2758 return m_con.getPeerStat(PEER_ID_SERVER,con::AVG_RTT);
2761 float Client::getCurRate(void)
2763 return ( m_con.getLocalStat(con::CUR_INC_RATE) +
2764 m_con.getLocalStat(con::CUR_DL_RATE));
2767 float Client::getAvgRate(void)
2769 return ( m_con.getLocalStat(con::AVG_INC_RATE) +
2770 m_con.getLocalStat(con::AVG_DL_RATE));
2773 void Client::makeScreenshot(IrrlichtDevice *device)
2775 irr::video::IVideoDriver *driver = device->getVideoDriver();
2776 irr::video::IImage* const raw_image = driver->createScreenShot();
2778 irr::video::IImage* const image = driver->createImage(video::ECF_R8G8B8,
2779 raw_image->getDimension());
2782 raw_image->copyTo(image);
2783 irr::c8 filename[256];
2784 snprintf(filename, sizeof(filename), "%s" DIR_DELIM "screenshot_%u.png",
2785 g_settings->get("screenshot_path").c_str(),
2786 device->getTimer()->getRealTime());
2787 std::stringstream sstr;
2788 if (driver->writeImageToFile(image, filename)) {
2789 sstr << "Saved screenshot to '" << filename << "'";
2791 sstr << "Failed to save screenshot '" << filename << "'";
2793 m_chat_queue.push_back(narrow_to_wide(sstr.str()));
2794 infostream << sstr << std::endl;
2801 // IGameDef interface
2803 IItemDefManager* Client::getItemDefManager()
2807 INodeDefManager* Client::getNodeDefManager()
2811 ICraftDefManager* Client::getCraftDefManager()
2814 //return m_craftdef;
2816 ITextureSource* Client::getTextureSource()
2820 IShaderSource* Client::getShaderSource()
2824 scene::ISceneManager* Client::getSceneManager()
2826 return m_device->getSceneManager();
2828 u16 Client::allocateUnknownNodeId(const std::string &name)
2830 errorstream<<"Client::allocateUnknownNodeId(): "
2831 <<"Client cannot allocate node IDs"<<std::endl;
2833 return CONTENT_IGNORE;
2835 ISoundManager* Client::getSoundManager()
2839 MtEventManager* Client::getEventManager()
2844 scene::IAnimatedMesh* Client::getMesh(const std::string &filename)
2846 std::map<std::string, std::string>::const_iterator i =
2847 m_mesh_data.find(filename);
2848 if(i == m_mesh_data.end()){
2849 errorstream<<"Client::getMesh(): Mesh not found: \""<<filename<<"\""
2853 const std::string &data = i->second;
2854 scene::ISceneManager *smgr = m_device->getSceneManager();
2856 // Create the mesh, remove it from cache and return it
2857 // This allows unique vertex colors and other properties for each instance
2858 Buffer<char> data_rw(data.c_str(), data.size()); // Const-incorrect Irrlicht
2859 io::IFileSystem *irrfs = m_device->getFileSystem();
2860 io::IReadFile *rfile = irrfs->createMemoryReadFile(
2861 *data_rw, data_rw.getSize(), filename.c_str());
2864 scene::IAnimatedMesh *mesh = smgr->getMesh(rfile);
2866 // NOTE: By playing with Irrlicht refcounts, maybe we could cache a bunch
2867 // of uniquely named instances and re-use them
2869 smgr->getMeshCache()->removeMesh(mesh);