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_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, ipv6, this),
252 m_server_ser_ver(SER_FMT_VER_INVALID),
254 m_inventory_updated(false),
255 m_inventory_from_server(NULL),
256 m_inventory_from_server_age(0.0),
262 m_password(password),
263 m_access_denied(false),
264 m_itemdef_received(false),
265 m_nodedef_received(false),
266 m_media_downloader(new ClientMediaDownloader()),
267 m_time_of_day_set(false),
268 m_last_time_of_day_f(-1),
269 m_time_of_day_update_timer(0),
270 m_recommended_send_interval(0.1),
271 m_removed_sounds_check_timer(0),
278 Player *player = new LocalPlayer(this, playername);
280 m_env.addPlayer(player);
283 if (g_settings->getBool("enable_local_map_saving")) {
284 const std::string world_path = porting::path_user + DIR_DELIM + "worlds"
285 + DIR_DELIM + "server_" + g_settings->get("address")
286 + "_" + g_settings->get("remote_port");
288 SubgameSpec gamespec;
289 if (!getWorldExists(world_path)) {
290 gamespec = findSubgame(g_settings->get("default_game"));
291 if (!gamespec.isValid())
292 gamespec = findSubgame("minimal");
294 std::string world_gameid = getWorldGameId(world_path, false);
295 gamespec = findWorldSubgame(world_path);
297 if (!gamespec.isValid()) {
298 errorstream << "Couldn't find subgame for local map saving." << std::endl;
302 localserver = new Server(world_path, gamespec, false, false);
303 localdb = new Database_SQLite3(&(ServerMap&)localserver->getMap(), world_path);
304 localdb->beginSave();
305 actionstream << "Local map saving started, map will be saved at '" << world_path << "'" << std::endl;
313 //request all client managed threads to stop
314 m_mesh_update_thread.Stop();
315 if (localdb != NULL) {
316 actionstream << "Local map saving ended" << std::endl;
321 bool Client::isShutdown()
324 if (!m_mesh_update_thread.IsRunning()) return true;
333 m_mesh_update_thread.Stop();
334 m_mesh_update_thread.Wait();
335 while(!m_mesh_update_thread.m_queue_out.empty()) {
336 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_frontNoEx();
341 delete m_inventory_from_server;
343 // Delete detached inventories
344 for(std::map<std::string, Inventory*>::iterator
345 i = m_detached_inventories.begin();
346 i != m_detached_inventories.end(); i++){
350 // cleanup 3d model meshes on client shutdown
351 while (m_device->getSceneManager()->getMeshCache()->getMeshCount() != 0) {
352 scene::IAnimatedMesh * mesh =
353 m_device->getSceneManager()->getMeshCache()->getMeshByIndex(0);
356 m_device->getSceneManager()->getMeshCache()->removeMesh(mesh);
360 void Client::connect(Address address)
362 DSTACK(__FUNCTION_NAME);
363 m_con.SetTimeoutMs(0);
364 m_con.Connect(address);
367 void Client::step(float dtime)
369 DSTACK(__FUNCTION_NAME);
375 if(m_ignore_damage_timer > dtime)
376 m_ignore_damage_timer -= dtime;
378 m_ignore_damage_timer = 0.0;
380 m_animation_time += dtime;
381 if(m_animation_time > 60.0)
382 m_animation_time -= 60.0;
384 m_time_of_day_update_timer += dtime;
392 float &counter = m_packetcounter_timer;
398 infostream << "Client packetcounter (" << m_packetcounter_timer
400 m_packetcounter.print(infostream);
401 m_packetcounter.clear();
408 Delete unused sectors
410 NOTE: This jams the game for a while because deleting sectors
414 float &counter = m_delete_unused_sectors_timer;
422 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
424 core::list<v3s16> deleted_blocks;
426 float delete_unused_sectors_timeout =
427 g_settings->getFloat("client_delete_unused_sectors_timeout");
429 // Delete sector blocks
430 /*u32 num = m_env.getMap().unloadUnusedData
431 (delete_unused_sectors_timeout,
432 true, &deleted_blocks);*/
434 // Delete whole sectors
435 m_env.getMap().unloadUnusedData
436 (delete_unused_sectors_timeout,
439 if(deleted_blocks.size() > 0)
441 /*infostream<<"Client: Deleted blocks of "<<num
442 <<" unused sectors"<<std::endl;*/
443 /*infostream<<"Client: Deleted "<<num
444 <<" unused sectors"<<std::endl;*/
450 // Env is locked so con can be locked.
451 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
453 core::list<v3s16>::Iterator i = deleted_blocks.begin();
454 core::list<v3s16> sendlist;
457 if(sendlist.size() == 255 || i == deleted_blocks.end())
459 if(sendlist.size() == 0)
468 u32 replysize = 2+1+6*sendlist.size();
469 SharedBuffer<u8> reply(replysize);
470 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
471 reply[2] = sendlist.size();
473 for(core::list<v3s16>::Iterator
474 j = sendlist.begin();
475 j != sendlist.end(); j++)
477 writeV3S16(&reply[2+1+6*k], *j);
480 m_con.Send(PEER_ID_SERVER, 1, reply, true);
482 if(i == deleted_blocks.end())
488 sendlist.push_back(*i);
495 // UGLY hack to fix 2 second startup delay caused by non existent
496 // server client startup synchronization in local server or singleplayer mode
497 static bool initial_step = true;
499 initial_step = false;
501 else if(m_state == LC_Created)
503 float &counter = m_connection_reinit_timer;
509 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
511 Player *myplayer = m_env.getLocalPlayer();
512 assert(myplayer != NULL);
513 // Send TOSERVER_INIT
514 // [0] u16 TOSERVER_INIT
515 // [2] u8 SER_FMT_VER_HIGHEST_READ
516 // [3] u8[20] player_name
517 // [23] u8[28] password (new in some version)
518 // [51] u16 minimum supported network protocol version (added sometime)
519 // [53] u16 maximum supported network protocol version (added later than the previous one)
520 SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2+2);
521 writeU16(&data[0], TOSERVER_INIT);
522 writeU8(&data[2], SER_FMT_VER_HIGHEST_READ);
524 memset((char*)&data[3], 0, PLAYERNAME_SIZE);
525 snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
527 /*infostream<<"Client: sending initial password hash: \""<<m_password<<"\""
530 memset((char*)&data[23], 0, PASSWORD_SIZE);
531 snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
533 writeU16(&data[51], CLIENT_PROTOCOL_VERSION_MIN);
534 writeU16(&data[53], CLIENT_PROTOCOL_VERSION_MAX);
536 // Send as unreliable
537 Send(1, data, false);
540 // Not connected, return
545 Do stuff if connected
549 Run Map's timers and unload unused data
551 const float map_timer_and_unload_dtime = 5.25;
552 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
554 ScopeProfiler sp(g_profiler, "Client: map timer and unload");
555 std::list<v3s16> deleted_blocks;
556 m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
557 g_settings->getFloat("client_unload_unused_data_timeout"),
560 /*if(deleted_blocks.size() > 0)
561 infostream<<"Client: Unloaded "<<deleted_blocks.size()
562 <<" unused blocks"<<std::endl;*/
566 NOTE: This loop is intentionally iterated the way it is.
569 std::list<v3s16>::iterator i = deleted_blocks.begin();
570 std::list<v3s16> sendlist;
573 if(sendlist.size() == 255 || i == deleted_blocks.end())
575 if(sendlist.size() == 0)
584 u32 replysize = 2+1+6*sendlist.size();
585 SharedBuffer<u8> reply(replysize);
586 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
587 reply[2] = sendlist.size();
589 for(std::list<v3s16>::iterator
590 j = sendlist.begin();
591 j != sendlist.end(); ++j)
593 writeV3S16(&reply[2+1+6*k], *j);
596 m_con.Send(PEER_ID_SERVER, 2, reply, true);
598 if(i == deleted_blocks.end())
604 sendlist.push_back(*i);
613 // Control local player (0ms)
614 LocalPlayer *player = m_env.getLocalPlayer();
615 assert(player != NULL);
616 player->applyControl(dtime);
626 ClientEnvEvent event = m_env.getClientEvent();
627 if(event.type == CEE_NONE)
631 else if(event.type == CEE_PLAYER_DAMAGE)
633 if(m_ignore_damage_timer <= 0)
635 u8 damage = event.player_damage.amount;
637 if(event.player_damage.send_to_server)
640 // Add to ClientEvent queue
642 event.type = CE_PLAYER_DAMAGE;
643 event.player_damage.amount = damage;
644 m_client_event_queue.push_back(event);
647 else if(event.type == CEE_PLAYER_BREATH)
649 u16 breath = event.player_breath.amount;
659 float &counter = m_avg_rtt_timer;
664 // connectedAndInitialized() is true, peer exists.
665 float avg_rtt = getRTT();
666 infostream<<"Client: avg_rtt="<<avg_rtt<<std::endl;
671 Send player position to server
674 float &counter = m_playerpos_send_timer;
676 if((m_state == LC_Ready) && (counter >= m_recommended_send_interval))
684 Replace updated meshes
687 int num_processed_meshes = 0;
688 while(!m_mesh_update_thread.m_queue_out.empty())
690 num_processed_meshes++;
691 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_frontNoEx();
692 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
695 // Delete the old mesh
696 if(block->mesh != NULL)
698 // TODO: Remove hardware buffers of meshbuffers of block->mesh
703 // Replace with the new mesh
704 block->mesh = r.mesh;
708 if(r.ack_block_to_server)
720 u32 replysize = 2+1+6;
721 SharedBuffer<u8> reply(replysize);
722 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
724 writeV3S16(&reply[3], r.p);
726 m_con.Send(PEER_ID_SERVER, 2, reply, true);
729 if(num_processed_meshes > 0)
730 g_profiler->graphAdd("num_processed_meshes", num_processed_meshes);
736 if (m_media_downloader && m_media_downloader->isStarted()) {
737 m_media_downloader->step(this);
738 if (m_media_downloader->isDone()) {
740 delete m_media_downloader;
741 m_media_downloader = NULL;
746 If the server didn't update the inventory in a while, revert
747 the local inventory (so the player notices the lag problem
748 and knows something is wrong).
750 if(m_inventory_from_server)
752 float interval = 10.0;
753 float count_before = floor(m_inventory_from_server_age / interval);
755 m_inventory_from_server_age += dtime;
757 float count_after = floor(m_inventory_from_server_age / interval);
759 if(count_after != count_before)
761 // Do this every <interval> seconds after TOCLIENT_INVENTORY
762 // Reset the locally changed inventory to the authoritative inventory
763 Player *player = m_env.getLocalPlayer();
764 player->inventory = *m_inventory_from_server;
765 m_inventory_updated = true;
770 Update positions of sounds attached to objects
773 for(std::map<int, u16>::iterator
774 i = m_sounds_to_objects.begin();
775 i != m_sounds_to_objects.end(); i++)
777 int client_id = i->first;
778 u16 object_id = i->second;
779 ClientActiveObject *cao = m_env.getActiveObject(object_id);
782 v3f pos = cao->getPosition();
783 m_sound->updateSoundPosition(client_id, pos);
788 Handle removed remotely initiated sounds
790 m_removed_sounds_check_timer += dtime;
791 if(m_removed_sounds_check_timer >= 2.32)
793 m_removed_sounds_check_timer = 0;
794 // Find removed sounds and clear references to them
795 std::set<s32> removed_server_ids;
796 for(std::map<s32, int>::iterator
797 i = m_sounds_server_to_client.begin();
798 i != m_sounds_server_to_client.end();)
800 s32 server_id = i->first;
801 int client_id = i->second;
803 if(!m_sound->soundExists(client_id)){
804 m_sounds_server_to_client.erase(server_id);
805 m_sounds_client_to_server.erase(client_id);
806 m_sounds_to_objects.erase(client_id);
807 removed_server_ids.insert(server_id);
811 if(removed_server_ids.size() != 0)
813 std::ostringstream os(std::ios_base::binary);
814 writeU16(os, TOSERVER_REMOVED_SOUNDS);
815 size_t server_ids = removed_server_ids.size();
816 assert(server_ids <= 0xFFFF);
817 writeU16(os, (u16) (server_ids & 0xFFFF));
818 for(std::set<s32>::iterator i = removed_server_ids.begin();
819 i != removed_server_ids.end(); i++)
821 std::string s = os.str();
822 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
829 bool Client::loadMedia(const std::string &data, const std::string &filename)
831 // Silly irrlicht's const-incorrectness
832 Buffer<char> data_rw(data.c_str(), data.size());
836 const char *image_ext[] = {
837 ".png", ".jpg", ".bmp", ".tga",
838 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
841 name = removeStringEnd(filename, image_ext);
844 verbosestream<<"Client: Attempting to load image "
845 <<"file \""<<filename<<"\""<<std::endl;
847 io::IFileSystem *irrfs = m_device->getFileSystem();
848 video::IVideoDriver *vdrv = m_device->getVideoDriver();
850 // Create an irrlicht memory file
851 io::IReadFile *rfile = irrfs->createMemoryReadFile(
852 *data_rw, data_rw.getSize(), "_tempreadfile");
855 video::IImage *img = vdrv->createImageFromFile(rfile);
857 errorstream<<"Client: Cannot create image from data of "
858 <<"file \""<<filename<<"\""<<std::endl;
863 m_tsrc->insertSourceImage(filename, img);
870 const char *sound_ext[] = {
871 ".0.ogg", ".1.ogg", ".2.ogg", ".3.ogg", ".4.ogg",
872 ".5.ogg", ".6.ogg", ".7.ogg", ".8.ogg", ".9.ogg",
875 name = removeStringEnd(filename, sound_ext);
878 verbosestream<<"Client: Attempting to load sound "
879 <<"file \""<<filename<<"\""<<std::endl;
880 m_sound->loadSoundData(name, data);
884 const char *model_ext[] = {
885 ".x", ".b3d", ".md2", ".obj",
888 name = removeStringEnd(filename, model_ext);
891 verbosestream<<"Client: Storing model into memory: "
892 <<"\""<<filename<<"\""<<std::endl;
893 if(m_mesh_data.count(filename))
894 errorstream<<"Multiple models with name \""<<filename.c_str()
895 <<"\" found; replacing previous model"<<std::endl;
896 m_mesh_data[filename] = data;
900 errorstream<<"Client: Don't know how to load file \""
901 <<filename<<"\""<<std::endl;
905 // Virtual methods from con::PeerHandler
906 void Client::peerAdded(con::Peer *peer)
908 infostream<<"Client::peerAdded(): peer->id="
909 <<peer->id<<std::endl;
911 void Client::deletingPeer(con::Peer *peer, bool timeout)
913 infostream<<"Client::deletingPeer(): "
914 "Server Peer is getting deleted "
915 <<"(timeout="<<timeout<<")"<<std::endl;
920 u16 number of files requested
926 void Client::request_media(const std::list<std::string> &file_requests)
928 std::ostringstream os(std::ios_base::binary);
929 writeU16(os, TOSERVER_REQUEST_MEDIA);
930 size_t file_requests_size = file_requests.size();
931 assert(file_requests_size <= 0xFFFF);
932 writeU16(os, (u16) (file_requests_size & 0xFFFF));
934 for(std::list<std::string>::const_iterator i = file_requests.begin();
935 i != file_requests.end(); ++i) {
936 os<<serializeString(*i);
940 std::string s = os.str();
941 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
944 infostream<<"Client: Sending media request list to server ("
945 <<file_requests.size()<<" files)"<<std::endl;
948 void Client::received_media()
950 // notify server we received everything
951 std::ostringstream os(std::ios_base::binary);
952 writeU16(os, TOSERVER_RECEIVED_MEDIA);
953 std::string s = os.str();
954 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
957 infostream<<"Client: Notifying server that we received all media"
961 void Client::ReceiveAll()
963 DSTACK(__FUNCTION_NAME);
964 u32 start_ms = porting::getTimeMs();
967 // Limit time even if there would be huge amounts of data to
969 if(porting::getTimeMs() > start_ms + 100)
974 g_profiler->graphAdd("client_received_packets", 1);
976 catch(con::NoIncomingDataException &e)
980 catch(con::InvalidIncomingDataException &e)
982 infostream<<"Client::ReceiveAll(): "
983 "InvalidIncomingDataException: what()="
984 <<e.what()<<std::endl;
989 void Client::Receive()
991 DSTACK(__FUNCTION_NAME);
992 SharedBuffer<u8> data;
994 u32 datasize = m_con.Receive(sender_peer_id, data);
995 ProcessData(*data, datasize, sender_peer_id);
999 sender_peer_id given to this shall be quaranteed to be a valid peer
1001 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
1003 DSTACK(__FUNCTION_NAME);
1005 // Ignore packets that don't even fit a command
1008 m_packetcounter.add(60000);
1012 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
1014 //infostream<<"Client: received command="<<command<<std::endl;
1015 m_packetcounter.add((u16)command);
1018 If this check is removed, be sure to change the queue
1019 system to know the ids
1021 if(sender_peer_id != PEER_ID_SERVER)
1023 infostream<<"Client::ProcessData(): Discarding data not "
1024 "coming from server: peer_id="<<sender_peer_id
1029 u8 ser_version = m_server_ser_ver;
1031 if(command == TOCLIENT_INIT)
1036 u8 deployed = data[2];
1038 infostream<<"Client: TOCLIENT_INIT received with "
1039 "deployed="<<((int)deployed&0xff)<<std::endl;
1041 if(!ser_ver_supported(deployed))
1043 infostream<<"Client: TOCLIENT_INIT: Server sent "
1044 <<"unsupported ser_fmt_ver"<<std::endl;
1048 m_server_ser_ver = deployed;
1050 // Get player position
1051 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
1052 if(datasize >= 2+1+6)
1053 playerpos_s16 = readV3S16(&data[2+1]);
1054 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
1057 // Set player position
1058 Player *player = m_env.getLocalPlayer();
1059 assert(player != NULL);
1060 player->setPosition(playerpos_f);
1062 if(datasize >= 2+1+6+8)
1065 m_map_seed = readU64(&data[2+1+6]);
1066 infostream<<"Client: received map seed: "<<m_map_seed<<std::endl;
1069 if(datasize >= 2+1+6+8+4)
1072 m_recommended_send_interval = readF1000(&data[2+1+6+8]);
1073 infostream<<"Client: received recommended send interval "
1074 <<m_recommended_send_interval<<std::endl;
1079 SharedBuffer<u8> reply(replysize);
1080 writeU16(&reply[0], TOSERVER_INIT2);
1082 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1089 if(command == TOCLIENT_ACCESS_DENIED)
1091 // The server didn't like our password. Note, this needs
1092 // to be processed even if the serialisation format has
1093 // not been agreed yet, the same as TOCLIENT_INIT.
1094 m_access_denied = true;
1095 m_access_denied_reason = L"Unknown";
1098 std::string datastring((char*)&data[2], datasize-2);
1099 std::istringstream is(datastring, std::ios_base::binary);
1100 m_access_denied_reason = deSerializeWideString(is);
1105 if(ser_version == SER_FMT_VER_INVALID)
1107 infostream<<"Client: Server serialization"
1108 " format invalid or not initialized."
1109 " Skipping incoming command="<<command<<std::endl;
1114 Handle runtime commands
1116 // there's no sane reason why we shouldn't have a player and
1117 // almost everyone needs a player reference
1118 Player *player = m_env.getLocalPlayer();
1119 assert(player != NULL);
1121 if(command == TOCLIENT_REMOVENODE)
1126 p.X = readS16(&data[2]);
1127 p.Y = readS16(&data[4]);
1128 p.Z = readS16(&data[6]);
1131 else if(command == TOCLIENT_ADDNODE)
1133 if(datasize < 8 + MapNode::serializedLength(ser_version))
1137 p.X = readS16(&data[2]);
1138 p.Y = readS16(&data[4]);
1139 p.Z = readS16(&data[6]);
1142 n.deSerialize(&data[8], ser_version);
1144 bool remove_metadata = true;
1145 u32 index = 8 + MapNode::serializedLength(ser_version);
1146 if ((datasize >= index+1) && data[index]){
1147 remove_metadata = false;
1150 addNode(p, n, remove_metadata);
1152 else if(command == TOCLIENT_BLOCKDATA)
1154 // Ignore too small packet
1159 p.X = readS16(&data[2]);
1160 p.Y = readS16(&data[4]);
1161 p.Z = readS16(&data[6]);
1163 std::string datastring((char*)&data[8], datasize-8);
1164 std::istringstream istr(datastring, std::ios_base::binary);
1169 v2s16 p2d(p.X, p.Z);
1170 sector = m_env.getMap().emergeSector(p2d);
1172 assert(sector->getPos() == p2d);
1174 block = sector->getBlockNoCreateNoEx(p.Y);
1178 Update an existing block
1180 block->deSerialize(istr, ser_version, false);
1181 block->deSerializeNetworkSpecific(istr);
1188 block = new MapBlock(&m_env.getMap(), p, this);
1189 block->deSerialize(istr, ser_version, false);
1190 block->deSerializeNetworkSpecific(istr);
1191 sector->insertBlock(block);
1194 if (localdb != NULL) {
1195 ((ServerMap&) localserver->getMap()).saveBlock(block, localdb);
1199 Add it to mesh update queue and set it to be acknowledged after update.
1201 addUpdateMeshTaskWithEdge(p, true);
1203 else if(command == TOCLIENT_INVENTORY)
1208 std::string datastring((char*)&data[2], datasize-2);
1209 std::istringstream is(datastring, std::ios_base::binary);
1211 player->inventory.deSerialize(is);
1213 m_inventory_updated = true;
1215 delete m_inventory_from_server;
1216 m_inventory_from_server = new Inventory(player->inventory);
1217 m_inventory_from_server_age = 0.0;
1220 else if(command == TOCLIENT_TIME_OF_DAY)
1225 u16 time_of_day = readU16(&data[2]);
1226 time_of_day = time_of_day % 24000;
1227 float time_speed = 0;
1229 if(datasize >= 2 + 2 + 4)
1231 time_speed = readF1000(&data[4]);
1234 // Old message; try to approximate speed of time by ourselves
1235 float time_of_day_f = (float)time_of_day / 24000.0;
1236 float tod_diff_f = 0;
1238 if(time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
1239 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0;
1241 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
1243 m_last_time_of_day_f = time_of_day_f;
1244 float time_diff = m_time_of_day_update_timer;
1245 m_time_of_day_update_timer = 0;
1247 if(m_time_of_day_set){
1248 time_speed = (3600.0*24.0) * tod_diff_f / time_diff;
1249 infostream<<"Client: Measured time_of_day speed (old format): "
1250 <<time_speed<<" tod_diff_f="<<tod_diff_f
1251 <<" time_diff="<<time_diff<<std::endl;
1255 // Update environment
1256 m_env.setTimeOfDay(time_of_day);
1257 m_env.setTimeOfDaySpeed(time_speed);
1258 m_time_of_day_set = true;
1260 u32 dr = m_env.getDayNightRatio();
1261 infostream<<"Client: time_of_day="<<time_of_day
1262 <<" time_speed="<<time_speed
1263 <<" dr="<<dr<<std::endl;
1265 else if(command == TOCLIENT_CHAT_MESSAGE)
1273 std::string datastring((char*)&data[2], datasize-2);
1274 std::istringstream is(datastring, std::ios_base::binary);
1277 is.read((char*) buf, 2);
1278 u16 len = readU16(buf);
1280 std::wstring message;
1281 for(unsigned int i=0; i<len; i++)
1283 is.read((char*)buf, 2);
1284 message += (wchar_t)readU16(buf);
1287 m_chat_queue.push_back(message);
1289 else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1293 u16 count of removed objects
1294 for all removed objects {
1297 u16 count of added objects
1298 for all added objects {
1301 u32 initialization data length
1302 string initialization data
1307 // Get all data except the command number
1308 std::string datastring((char*)&data[2], datasize-2);
1309 // Throw them in an istringstream
1310 std::istringstream is(datastring, std::ios_base::binary);
1312 // Read removed objects
1314 u16 removed_count = readU16((u8*)buf);
1315 for(unsigned int i=0; i<removed_count; i++)
1318 u16 id = readU16((u8*)buf);
1319 m_env.removeActiveObject(id);
1322 // Read added objects
1324 u16 added_count = readU16((u8*)buf);
1325 for(unsigned int i=0; i<added_count; i++)
1328 u16 id = readU16((u8*)buf);
1330 u8 type = readU8((u8*)buf);
1331 std::string data = deSerializeLongString(is);
1333 m_env.addActiveObject(id, type, data);
1336 else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1348 // Get all data except the command number
1349 std::string datastring((char*)&data[2], datasize-2);
1350 // Throw them in an istringstream
1351 std::istringstream is(datastring, std::ios_base::binary);
1353 while(is.eof() == false)
1356 u16 id = readU16((u8*)buf);
1360 size_t message_size = readU16((u8*)buf);
1361 std::string message;
1362 message.reserve(message_size);
1363 for(unsigned int i=0; i<message_size; i++)
1366 message.append(buf, 1);
1368 // Pass on to the environment
1369 m_env.processActiveObjectMessage(id, message);
1372 else if(command == TOCLIENT_MOVEMENT)
1374 std::string datastring((char*)&data[2], datasize-2);
1375 std::istringstream is(datastring, std::ios_base::binary);
1377 player->movement_acceleration_default = readF1000(is) * BS;
1378 player->movement_acceleration_air = readF1000(is) * BS;
1379 player->movement_acceleration_fast = readF1000(is) * BS;
1380 player->movement_speed_walk = readF1000(is) * BS;
1381 player->movement_speed_crouch = readF1000(is) * BS;
1382 player->movement_speed_fast = readF1000(is) * BS;
1383 player->movement_speed_climb = readF1000(is) * BS;
1384 player->movement_speed_jump = readF1000(is) * BS;
1385 player->movement_liquid_fluidity = readF1000(is) * BS;
1386 player->movement_liquid_fluidity_smooth = readF1000(is) * BS;
1387 player->movement_liquid_sink = readF1000(is) * BS;
1388 player->movement_gravity = readF1000(is) * BS;
1390 else if(command == TOCLIENT_HP)
1392 std::string datastring((char*)&data[2], datasize-2);
1393 std::istringstream is(datastring, std::ios_base::binary);
1395 u8 oldhp = player->hp;
1401 // Add to ClientEvent queue
1403 event.type = CE_PLAYER_DAMAGE;
1404 event.player_damage.amount = oldhp - hp;
1405 m_client_event_queue.push_back(event);
1408 else if(command == TOCLIENT_BREATH)
1410 std::string datastring((char*)&data[2], datasize-2);
1411 std::istringstream is(datastring, std::ios_base::binary);
1413 player->setBreath(readU16(is));
1415 else if(command == TOCLIENT_MOVE_PLAYER)
1417 std::string datastring((char*)&data[2], datasize-2);
1418 std::istringstream is(datastring, std::ios_base::binary);
1420 v3f pos = readV3F1000(is);
1421 f32 pitch = readF1000(is);
1422 f32 yaw = readF1000(is);
1423 player->setPosition(pos);
1425 infostream<<"Client got TOCLIENT_MOVE_PLAYER"
1426 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1432 Add to ClientEvent queue.
1433 This has to be sent to the main program because otherwise
1434 it would just force the pitch and yaw values to whatever
1435 the camera points to.
1438 event.type = CE_PLAYER_FORCE_MOVE;
1439 event.player_force_move.pitch = pitch;
1440 event.player_force_move.yaw = yaw;
1441 m_client_event_queue.push_back(event);
1443 // Ignore damage for a few seconds, so that the player doesn't
1444 // get damage from falling on ground
1445 m_ignore_damage_timer = 3.0;
1447 else if(command == TOCLIENT_PLAYERITEM)
1449 infostream<<"Client: WARNING: Ignoring TOCLIENT_PLAYERITEM"<<std::endl;
1451 else if(command == TOCLIENT_DEATHSCREEN)
1453 std::string datastring((char*)&data[2], datasize-2);
1454 std::istringstream is(datastring, std::ios_base::binary);
1456 bool set_camera_point_target = readU8(is);
1457 v3f camera_point_target = readV3F1000(is);
1460 event.type = CE_DEATHSCREEN;
1461 event.deathscreen.set_camera_point_target = set_camera_point_target;
1462 event.deathscreen.camera_point_target_x = camera_point_target.X;
1463 event.deathscreen.camera_point_target_y = camera_point_target.Y;
1464 event.deathscreen.camera_point_target_z = camera_point_target.Z;
1465 m_client_event_queue.push_back(event);
1467 else if(command == TOCLIENT_ANNOUNCE_MEDIA)
1469 std::string datastring((char*)&data[2], datasize-2);
1470 std::istringstream is(datastring, std::ios_base::binary);
1472 int num_files = readU16(is);
1474 infostream<<"Client: Received media announcement: packet size: "
1475 <<datasize<<std::endl;
1477 if (m_media_downloader == NULL ||
1478 m_media_downloader->isStarted()) {
1479 const char *problem = m_media_downloader ?
1480 "we already saw another announcement" :
1481 "all media has been received already";
1482 errorstream<<"Client: Received media announcement but "
1484 <<" files="<<num_files
1485 <<" size="<<datasize<<std::endl;
1489 // Mesh update thread must be stopped while
1490 // updating content definitions
1491 assert(!m_mesh_update_thread.IsRunning());
1493 for(int i=0; i<num_files; i++)
1495 std::string name = deSerializeString(is);
1496 std::string sha1_base64 = deSerializeString(is);
1497 std::string sha1_raw = base64_decode(sha1_base64);
1498 m_media_downloader->addFile(name, sha1_raw);
1501 std::vector<std::string> remote_media;
1503 Strfnd sf(deSerializeString(is));
1504 while(!sf.atend()) {
1505 std::string baseurl = trim(sf.next(","));
1507 m_media_downloader->addRemoteServer(baseurl);
1510 catch(SerializationError& e) {
1511 // not supported by server or turned off
1514 m_media_downloader->step(this);
1516 else if(command == TOCLIENT_MEDIA)
1518 std::string datastring((char*)&data[2], datasize-2);
1519 std::istringstream is(datastring, std::ios_base::binary);
1523 u16 total number of file bunches
1524 u16 index of this bunch
1525 u32 number of files in this bunch
1533 int num_bunches = readU16(is);
1534 int bunch_i = readU16(is);
1535 u32 num_files = readU32(is);
1536 infostream<<"Client: Received files: bunch "<<bunch_i<<"/"
1537 <<num_bunches<<" files="<<num_files
1538 <<" size="<<datasize<<std::endl;
1543 if (m_media_downloader == NULL ||
1544 !m_media_downloader->isStarted()) {
1545 const char *problem = m_media_downloader ?
1546 "media has not been requested" :
1547 "all media has been received already";
1548 errorstream<<"Client: Received media but "
1550 <<" bunch "<<bunch_i<<"/"<<num_bunches
1551 <<" files="<<num_files
1552 <<" size="<<datasize<<std::endl;
1556 // Mesh update thread must be stopped while
1557 // updating content definitions
1558 assert(!m_mesh_update_thread.IsRunning());
1560 for(unsigned int i=0; i<num_files; i++){
1561 std::string name = deSerializeString(is);
1562 std::string data = deSerializeLongString(is);
1563 m_media_downloader->conventionalTransferDone(
1567 else if(command == TOCLIENT_TOOLDEF)
1569 infostream<<"Client: WARNING: Ignoring TOCLIENT_TOOLDEF"<<std::endl;
1571 else if(command == TOCLIENT_NODEDEF)
1573 infostream<<"Client: Received node definitions: packet size: "
1574 <<datasize<<std::endl;
1576 // Mesh update thread must be stopped while
1577 // updating content definitions
1578 assert(!m_mesh_update_thread.IsRunning());
1580 // Decompress node definitions
1581 std::string datastring((char*)&data[2], datasize-2);
1582 std::istringstream is(datastring, std::ios_base::binary);
1583 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1584 std::ostringstream tmp_os;
1585 decompressZlib(tmp_is, tmp_os);
1587 // Deserialize node definitions
1588 std::istringstream tmp_is2(tmp_os.str());
1589 m_nodedef->deSerialize(tmp_is2);
1590 m_nodedef_received = true;
1592 else if(command == TOCLIENT_CRAFTITEMDEF)
1594 infostream<<"Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF"<<std::endl;
1596 else if(command == TOCLIENT_ITEMDEF)
1598 infostream<<"Client: Received item definitions: packet size: "
1599 <<datasize<<std::endl;
1601 // Mesh update thread must be stopped while
1602 // updating content definitions
1603 assert(!m_mesh_update_thread.IsRunning());
1605 // Decompress item definitions
1606 std::string datastring((char*)&data[2], datasize-2);
1607 std::istringstream is(datastring, std::ios_base::binary);
1608 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1609 std::ostringstream tmp_os;
1610 decompressZlib(tmp_is, tmp_os);
1612 // Deserialize node definitions
1613 std::istringstream tmp_is2(tmp_os.str());
1614 m_itemdef->deSerialize(tmp_is2);
1615 m_itemdef_received = true;
1617 else if(command == TOCLIENT_PLAY_SOUND)
1619 std::string datastring((char*)&data[2], datasize-2);
1620 std::istringstream is(datastring, std::ios_base::binary);
1622 s32 server_id = readS32(is);
1623 std::string name = deSerializeString(is);
1624 float gain = readF1000(is);
1625 int type = readU8(is); // 0=local, 1=positional, 2=object
1626 v3f pos = readV3F1000(is);
1627 u16 object_id = readU16(is);
1628 bool loop = readU8(is);
1633 client_id = m_sound->playSound(name, loop, gain);
1635 case 1: // positional
1636 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1639 ClientActiveObject *cao = m_env.getActiveObject(object_id);
1641 pos = cao->getPosition();
1642 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1643 // TODO: Set up sound to move with object
1648 if(client_id != -1){
1649 m_sounds_server_to_client[server_id] = client_id;
1650 m_sounds_client_to_server[client_id] = server_id;
1652 m_sounds_to_objects[client_id] = object_id;
1655 else if(command == TOCLIENT_STOP_SOUND)
1657 std::string datastring((char*)&data[2], datasize-2);
1658 std::istringstream is(datastring, std::ios_base::binary);
1660 s32 server_id = readS32(is);
1661 std::map<s32, int>::iterator i =
1662 m_sounds_server_to_client.find(server_id);
1663 if(i != m_sounds_server_to_client.end()){
1664 int client_id = i->second;
1665 m_sound->stopSound(client_id);
1668 else if(command == TOCLIENT_PRIVILEGES)
1670 std::string datastring((char*)&data[2], datasize-2);
1671 std::istringstream is(datastring, std::ios_base::binary);
1673 m_privileges.clear();
1674 infostream<<"Client: Privileges updated: ";
1675 u16 num_privileges = readU16(is);
1676 for(unsigned int i=0; i<num_privileges; i++){
1677 std::string priv = deSerializeString(is);
1678 m_privileges.insert(priv);
1679 infostream<<priv<<" ";
1681 infostream<<std::endl;
1683 else if(command == TOCLIENT_INVENTORY_FORMSPEC)
1685 std::string datastring((char*)&data[2], datasize-2);
1686 std::istringstream is(datastring, std::ios_base::binary);
1688 // Store formspec in LocalPlayer
1689 player->inventory_formspec = deSerializeLongString(is);
1691 else if(command == TOCLIENT_DETACHED_INVENTORY)
1693 std::string datastring((char*)&data[2], datasize-2);
1694 std::istringstream is(datastring, std::ios_base::binary);
1696 std::string name = deSerializeString(is);
1698 infostream<<"Client: Detached inventory update: \""<<name<<"\""<<std::endl;
1700 Inventory *inv = NULL;
1701 if(m_detached_inventories.count(name) > 0)
1702 inv = m_detached_inventories[name];
1704 inv = new Inventory(m_itemdef);
1705 m_detached_inventories[name] = inv;
1707 inv->deSerialize(is);
1709 else if(command == TOCLIENT_SHOW_FORMSPEC)
1711 std::string datastring((char*)&data[2], datasize-2);
1712 std::istringstream is(datastring, std::ios_base::binary);
1714 std::string formspec = deSerializeLongString(is);
1715 std::string formname = deSerializeString(is);
1718 event.type = CE_SHOW_FORMSPEC;
1719 // pointer is required as event is a struct only!
1720 // adding a std:string to a struct isn't possible
1721 event.show_formspec.formspec = new std::string(formspec);
1722 event.show_formspec.formname = new std::string(formname);
1723 m_client_event_queue.push_back(event);
1725 else if(command == TOCLIENT_SPAWN_PARTICLE)
1727 std::string datastring((char*)&data[2], datasize-2);
1728 std::istringstream is(datastring, std::ios_base::binary);
1730 v3f pos = readV3F1000(is);
1731 v3f vel = readV3F1000(is);
1732 v3f acc = readV3F1000(is);
1733 float expirationtime = readF1000(is);
1734 float size = readF1000(is);
1735 bool collisiondetection = readU8(is);
1736 std::string texture = deSerializeLongString(is);
1737 bool vertical = false;
1739 vertical = readU8(is);
1743 event.type = CE_SPAWN_PARTICLE;
1744 event.spawn_particle.pos = new v3f (pos);
1745 event.spawn_particle.vel = new v3f (vel);
1746 event.spawn_particle.acc = new v3f (acc);
1747 event.spawn_particle.expirationtime = expirationtime;
1748 event.spawn_particle.size = size;
1749 event.spawn_particle.collisiondetection = collisiondetection;
1750 event.spawn_particle.vertical = vertical;
1751 event.spawn_particle.texture = new std::string(texture);
1753 m_client_event_queue.push_back(event);
1755 else if(command == TOCLIENT_ADD_PARTICLESPAWNER)
1757 std::string datastring((char*)&data[2], datasize-2);
1758 std::istringstream is(datastring, std::ios_base::binary);
1760 u16 amount = readU16(is);
1761 float spawntime = readF1000(is);
1762 v3f minpos = readV3F1000(is);
1763 v3f maxpos = readV3F1000(is);
1764 v3f minvel = readV3F1000(is);
1765 v3f maxvel = readV3F1000(is);
1766 v3f minacc = readV3F1000(is);
1767 v3f maxacc = readV3F1000(is);
1768 float minexptime = readF1000(is);
1769 float maxexptime = readF1000(is);
1770 float minsize = readF1000(is);
1771 float maxsize = readF1000(is);
1772 bool collisiondetection = readU8(is);
1773 std::string texture = deSerializeLongString(is);
1774 u32 id = readU32(is);
1775 bool vertical = false;
1777 vertical = readU8(is);
1781 event.type = CE_ADD_PARTICLESPAWNER;
1782 event.add_particlespawner.amount = amount;
1783 event.add_particlespawner.spawntime = spawntime;
1784 event.add_particlespawner.minpos = new v3f (minpos);
1785 event.add_particlespawner.maxpos = new v3f (maxpos);
1786 event.add_particlespawner.minvel = new v3f (minvel);
1787 event.add_particlespawner.maxvel = new v3f (maxvel);
1788 event.add_particlespawner.minacc = new v3f (minacc);
1789 event.add_particlespawner.maxacc = new v3f (maxacc);
1790 event.add_particlespawner.minexptime = minexptime;
1791 event.add_particlespawner.maxexptime = maxexptime;
1792 event.add_particlespawner.minsize = minsize;
1793 event.add_particlespawner.maxsize = maxsize;
1794 event.add_particlespawner.collisiondetection = collisiondetection;
1795 event.add_particlespawner.vertical = vertical;
1796 event.add_particlespawner.texture = new std::string(texture);
1797 event.add_particlespawner.id = id;
1799 m_client_event_queue.push_back(event);
1801 else if(command == TOCLIENT_DELETE_PARTICLESPAWNER)
1803 std::string datastring((char*)&data[2], datasize-2);
1804 std::istringstream is(datastring, std::ios_base::binary);
1806 u32 id = readU16(is);
1809 event.type = CE_DELETE_PARTICLESPAWNER;
1810 event.delete_particlespawner.id = id;
1812 m_client_event_queue.push_back(event);
1814 else if(command == TOCLIENT_HUDADD)
1816 std::string datastring((char *)&data[2], datasize - 2);
1817 std::istringstream is(datastring, std::ios_base::binary);
1819 u32 id = readU32(is);
1820 u8 type = readU8(is);
1821 v2f pos = readV2F1000(is);
1822 std::string name = deSerializeString(is);
1823 v2f scale = readV2F1000(is);
1824 std::string text = deSerializeString(is);
1825 u32 number = readU32(is);
1826 u32 item = readU32(is);
1827 u32 dir = readU32(is);
1828 v2f align = readV2F1000(is);
1829 v2f offset = readV2F1000(is);
1833 world_pos = readV3F1000(is);
1834 }catch(SerializationError &e) {};
1836 size = readV2S32(is);
1837 } catch(SerializationError &e) {};
1840 event.type = CE_HUDADD;
1841 event.hudadd.id = id;
1842 event.hudadd.type = type;
1843 event.hudadd.pos = new v2f(pos);
1844 event.hudadd.name = new std::string(name);
1845 event.hudadd.scale = new v2f(scale);
1846 event.hudadd.text = new std::string(text);
1847 event.hudadd.number = number;
1848 event.hudadd.item = item;
1849 event.hudadd.dir = dir;
1850 event.hudadd.align = new v2f(align);
1851 event.hudadd.offset = new v2f(offset);
1852 event.hudadd.world_pos = new v3f(world_pos);
1853 event.hudadd.size = new v2s32(size);
1854 m_client_event_queue.push_back(event);
1856 else if(command == TOCLIENT_HUDRM)
1858 std::string datastring((char *)&data[2], datasize - 2);
1859 std::istringstream is(datastring, std::ios_base::binary);
1861 u32 id = readU32(is);
1864 event.type = CE_HUDRM;
1865 event.hudrm.id = id;
1866 m_client_event_queue.push_back(event);
1868 else if(command == TOCLIENT_HUDCHANGE)
1876 std::string datastring((char *)&data[2], datasize - 2);
1877 std::istringstream is(datastring, std::ios_base::binary);
1879 u32 id = readU32(is);
1880 u8 stat = (HudElementStat)readU8(is);
1882 if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
1883 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
1884 v2fdata = readV2F1000(is);
1885 else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
1886 sdata = deSerializeString(is);
1887 else if (stat == HUD_STAT_WORLD_POS)
1888 v3fdata = readV3F1000(is);
1889 else if (stat == HUD_STAT_SIZE )
1890 v2s32data = readV2S32(is);
1892 intdata = readU32(is);
1895 event.type = CE_HUDCHANGE;
1896 event.hudchange.id = id;
1897 event.hudchange.stat = (HudElementStat)stat;
1898 event.hudchange.v2fdata = new v2f(v2fdata);
1899 event.hudchange.v3fdata = new v3f(v3fdata);
1900 event.hudchange.sdata = new std::string(sdata);
1901 event.hudchange.data = intdata;
1902 event.hudchange.v2s32data = new v2s32(v2s32data);
1903 m_client_event_queue.push_back(event);
1905 else if(command == TOCLIENT_HUD_SET_FLAGS)
1907 std::string datastring((char *)&data[2], datasize - 2);
1908 std::istringstream is(datastring, std::ios_base::binary);
1910 u32 flags = readU32(is);
1911 u32 mask = readU32(is);
1913 player->hud_flags &= ~mask;
1914 player->hud_flags |= flags;
1916 else if(command == TOCLIENT_HUD_SET_PARAM)
1918 std::string datastring((char *)&data[2], datasize - 2);
1919 std::istringstream is(datastring, std::ios_base::binary);
1921 u16 param = readU16(is);
1922 std::string value = deSerializeString(is);
1924 if(param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) {
1925 s32 hotbar_itemcount = readS32((u8*) value.c_str());
1926 if(hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
1927 player->hud_hotbar_itemcount = hotbar_itemcount;
1929 else if (param == HUD_PARAM_HOTBAR_IMAGE) {
1930 ((LocalPlayer *) player)->hotbar_image = value;
1932 else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
1933 ((LocalPlayer *) player)->hotbar_selected_image = value;
1936 else if(command == TOCLIENT_SET_SKY)
1938 std::string datastring((char *)&data[2], datasize - 2);
1939 std::istringstream is(datastring, std::ios_base::binary);
1941 video::SColor *bgcolor = new video::SColor(readARGB8(is));
1942 std::string *type = new std::string(deSerializeString(is));
1943 u16 count = readU16(is);
1944 std::vector<std::string> *params = new std::vector<std::string>;
1946 for(size_t i=0; i<count; i++)
1947 params->push_back(deSerializeString(is));
1950 event.type = CE_SET_SKY;
1951 event.set_sky.bgcolor = bgcolor;
1952 event.set_sky.type = type;
1953 event.set_sky.params = params;
1954 m_client_event_queue.push_back(event);
1956 else if(command == TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO)
1958 std::string datastring((char *)&data[2], datasize - 2);
1959 std::istringstream is(datastring, std::ios_base::binary);
1961 bool do_override = readU8(is);
1962 float day_night_ratio_f = (float)readU16(is) / 65536;
1965 event.type = CE_OVERRIDE_DAY_NIGHT_RATIO;
1966 event.override_day_night_ratio.do_override = do_override;
1967 event.override_day_night_ratio.ratio_f = day_night_ratio_f;
1968 m_client_event_queue.push_back(event);
1970 else if(command == TOCLIENT_LOCAL_PLAYER_ANIMATIONS)
1972 std::string datastring((char *)&data[2], datasize - 2);
1973 std::istringstream is(datastring, std::ios_base::binary);
1975 LocalPlayer *player = m_env.getLocalPlayer();
1976 assert(player != NULL);
1978 player->local_animations[0] = readV2S32(is);
1979 player->local_animations[1] = readV2S32(is);
1980 player->local_animations[2] = readV2S32(is);
1981 player->local_animations[3] = readV2S32(is);
1982 player->local_animation_speed = readF1000(is);
1984 else if(command == TOCLIENT_EYE_OFFSET)
1986 std::string datastring((char *)&data[2], datasize - 2);
1987 std::istringstream is(datastring, std::ios_base::binary);
1989 LocalPlayer *player = m_env.getLocalPlayer();
1990 assert(player != NULL);
1992 player->eye_offset_first = readV3F1000(is);
1993 player->eye_offset_third = readV3F1000(is);
1997 infostream<<"Client: Ignoring unknown command "
1998 <<command<<std::endl;
2002 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
2004 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2005 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
2008 void Client::interact(u8 action, const PointedThing& pointed)
2010 if(m_state != LC_Ready){
2011 infostream<<"Client::interact() "
2012 "cancelled (not connected)"
2017 std::ostringstream os(std::ios_base::binary);
2023 [5] u32 length of the next item
2024 [9] serialized PointedThing
2026 0: start digging (from undersurface) or use
2027 1: stop digging (all parameters ignored)
2028 2: digging completed
2029 3: place block or item (to abovesurface)
2032 writeU16(os, TOSERVER_INTERACT);
2033 writeU8(os, action);
2034 writeU16(os, getPlayerItem());
2035 std::ostringstream tmp_os(std::ios::binary);
2036 pointed.serialize(tmp_os);
2037 os<<serializeLongString(tmp_os.str());
2039 std::string s = os.str();
2040 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2043 Send(0, data, true);
2046 void Client::sendNodemetaFields(v3s16 p, const std::string &formname,
2047 const std::map<std::string, std::string> &fields)
2049 std::ostringstream os(std::ios_base::binary);
2051 writeU16(os, TOSERVER_NODEMETA_FIELDS);
2053 os<<serializeString(formname);
2054 size_t fields_size = fields.size();
2055 assert(fields_size <= 0xFFFF);
2056 writeU16(os, (u16) (fields_size & 0xFFFF));
2057 for(std::map<std::string, std::string>::const_iterator
2058 i = fields.begin(); i != fields.end(); i++){
2059 const std::string &name = i->first;
2060 const std::string &value = i->second;
2061 os<<serializeString(name);
2062 os<<serializeLongString(value);
2066 std::string s = os.str();
2067 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2069 Send(0, data, true);
2072 void Client::sendInventoryFields(const std::string &formname,
2073 const std::map<std::string, std::string> &fields)
2075 std::ostringstream os(std::ios_base::binary);
2077 writeU16(os, TOSERVER_INVENTORY_FIELDS);
2078 os<<serializeString(formname);
2079 size_t fields_size = fields.size();
2080 assert(fields_size <= 0xFFFF);
2081 writeU16(os, (u16) (fields_size & 0xFFFF));
2082 for(std::map<std::string, std::string>::const_iterator
2083 i = fields.begin(); i != fields.end(); i++){
2084 const std::string &name = i->first;
2085 const std::string &value = i->second;
2086 os<<serializeString(name);
2087 os<<serializeLongString(value);
2091 std::string s = os.str();
2092 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2094 Send(0, data, true);
2097 void Client::sendInventoryAction(InventoryAction *a)
2099 std::ostringstream os(std::ios_base::binary);
2103 writeU16(buf, TOSERVER_INVENTORY_ACTION);
2104 os.write((char*)buf, 2);
2109 std::string s = os.str();
2110 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2112 Send(0, data, true);
2115 void Client::sendChatMessage(const std::wstring &message)
2117 std::ostringstream os(std::ios_base::binary);
2121 writeU16(buf, TOSERVER_CHAT_MESSAGE);
2122 os.write((char*)buf, 2);
2125 size_t messagesize = message.size();
2126 if (messagesize > 0xFFFF) {
2127 messagesize = 0xFFFF;
2129 writeU16(buf, (u16) messagesize);
2130 os.write((char*)buf, 2);
2133 for(unsigned int i=0; i<message.size(); i++)
2137 os.write((char*)buf, 2);
2141 std::string s = os.str();
2142 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2144 Send(0, data, true);
2147 void Client::sendChangePassword(const std::wstring &oldpassword,
2148 const std::wstring &newpassword)
2150 Player *player = m_env.getLocalPlayer();
2154 std::string playername = player->getName();
2155 std::string oldpwd = translatePassword(playername, oldpassword);
2156 std::string newpwd = translatePassword(playername, newpassword);
2158 std::ostringstream os(std::ios_base::binary);
2159 u8 buf[2+PASSWORD_SIZE*2];
2161 [0] u16 TOSERVER_PASSWORD
2162 [2] u8[28] old password
2163 [30] u8[28] new password
2166 writeU16(buf, TOSERVER_PASSWORD);
2167 for(unsigned int i=0;i<PASSWORD_SIZE-1;i++)
2169 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
2170 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
2172 buf[2+PASSWORD_SIZE-1] = 0;
2173 buf[30+PASSWORD_SIZE-1] = 0;
2174 os.write((char*)buf, 2+PASSWORD_SIZE*2);
2177 std::string s = os.str();
2178 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2180 Send(0, data, true);
2184 void Client::sendDamage(u8 damage)
2186 DSTACK(__FUNCTION_NAME);
2187 std::ostringstream os(std::ios_base::binary);
2189 writeU16(os, TOSERVER_DAMAGE);
2190 writeU8(os, damage);
2193 std::string s = os.str();
2194 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2196 Send(0, data, true);
2199 void Client::sendBreath(u16 breath)
2201 DSTACK(__FUNCTION_NAME);
2202 std::ostringstream os(std::ios_base::binary);
2204 writeU16(os, TOSERVER_BREATH);
2205 writeU16(os, breath);
2207 std::string s = os.str();
2208 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2210 Send(0, data, true);
2213 void Client::sendRespawn()
2215 DSTACK(__FUNCTION_NAME);
2216 std::ostringstream os(std::ios_base::binary);
2218 writeU16(os, TOSERVER_RESPAWN);
2221 std::string s = os.str();
2222 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2224 Send(0, data, true);
2227 void Client::sendReady()
2229 DSTACK(__FUNCTION_NAME);
2230 std::ostringstream os(std::ios_base::binary);
2232 writeU16(os, TOSERVER_CLIENT_READY);
2233 writeU8(os,VERSION_MAJOR);
2234 writeU8(os,VERSION_MINOR);
2235 writeU8(os,VERSION_PATCH_ORIG);
2238 writeU16(os,strlen(minetest_version_hash));
2239 os.write(minetest_version_hash,strlen(minetest_version_hash));
2242 std::string s = os.str();
2243 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2245 Send(0, data, true);
2248 void Client::sendPlayerPos()
2250 LocalPlayer *myplayer = m_env.getLocalPlayer();
2251 if(myplayer == NULL)
2254 // Save bandwidth by only updating position when something changed
2255 if(myplayer->last_position == myplayer->getPosition() &&
2256 myplayer->last_speed == myplayer->getSpeed() &&
2257 myplayer->last_pitch == myplayer->getPitch() &&
2258 myplayer->last_yaw == myplayer->getYaw() &&
2259 myplayer->last_keyPressed == myplayer->keyPressed)
2262 myplayer->last_position = myplayer->getPosition();
2263 myplayer->last_speed = myplayer->getSpeed();
2264 myplayer->last_pitch = myplayer->getPitch();
2265 myplayer->last_yaw = myplayer->getYaw();
2266 myplayer->last_keyPressed = myplayer->keyPressed;
2270 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2271 our_peer_id = m_con.GetPeerID();
2274 // Set peer id if not set already
2275 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2276 myplayer->peer_id = our_peer_id;
2277 // Check that an existing peer_id is the same as the connection's
2278 assert(myplayer->peer_id == our_peer_id);
2280 v3f pf = myplayer->getPosition();
2281 v3f sf = myplayer->getSpeed();
2282 s32 pitch = myplayer->getPitch() * 100;
2283 s32 yaw = myplayer->getYaw() * 100;
2284 u32 keyPressed = myplayer->keyPressed;
2286 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
2287 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
2291 [2] v3s32 position*100
2292 [2+12] v3s32 speed*100
2293 [2+12+12] s32 pitch*100
2294 [2+12+12+4] s32 yaw*100
2295 [2+12+12+4+4] u32 keyPressed
2297 SharedBuffer<u8> data(2+12+12+4+4+4);
2298 writeU16(&data[0], TOSERVER_PLAYERPOS);
2299 writeV3S32(&data[2], position);
2300 writeV3S32(&data[2+12], speed);
2301 writeS32(&data[2+12+12], pitch);
2302 writeS32(&data[2+12+12+4], yaw);
2303 writeU32(&data[2+12+12+4+4], keyPressed);
2304 // Send as unreliable
2305 Send(0, data, false);
2308 void Client::sendPlayerItem(u16 item)
2310 Player *myplayer = m_env.getLocalPlayer();
2311 if(myplayer == NULL)
2314 u16 our_peer_id = m_con.GetPeerID();
2316 // Set peer id if not set already
2317 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2318 myplayer->peer_id = our_peer_id;
2319 // Check that an existing peer_id is the same as the connection's
2320 assert(myplayer->peer_id == our_peer_id);
2322 SharedBuffer<u8> data(2+2);
2323 writeU16(&data[0], TOSERVER_PLAYERITEM);
2324 writeU16(&data[2], item);
2327 Send(0, data, true);
2330 void Client::removeNode(v3s16 p)
2332 std::map<v3s16, MapBlock*> modified_blocks;
2336 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
2338 catch(InvalidPositionException &e)
2342 for(std::map<v3s16, MapBlock * >::iterator
2343 i = modified_blocks.begin();
2344 i != modified_blocks.end(); ++i)
2346 addUpdateMeshTask(i->first, false, false);
2348 // add urgent task to update the modified node
2349 addUpdateMeshTaskForNode(p, false, true);
2352 void Client::addNode(v3s16 p, MapNode n, bool remove_metadata)
2354 //TimeTaker timer1("Client::addNode()");
2356 std::map<v3s16, MapBlock*> modified_blocks;
2360 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
2361 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
2363 catch(InvalidPositionException &e)
2366 for(std::map<v3s16, MapBlock * >::iterator
2367 i = modified_blocks.begin();
2368 i != modified_blocks.end(); ++i)
2370 addUpdateMeshTask(i->first, false, false);
2374 void Client::setPlayerControl(PlayerControl &control)
2376 LocalPlayer *player = m_env.getLocalPlayer();
2377 assert(player != NULL);
2378 player->control = control;
2381 void Client::selectPlayerItem(u16 item)
2383 m_playeritem = item;
2384 m_inventory_updated = true;
2385 sendPlayerItem(item);
2388 // Returns true if the inventory of the local player has been
2389 // updated from the server. If it is true, it is set to false.
2390 bool Client::getLocalInventoryUpdated()
2392 bool updated = m_inventory_updated;
2393 m_inventory_updated = false;
2397 // Copies the inventory of the local player to parameter
2398 void Client::getLocalInventory(Inventory &dst)
2400 Player *player = m_env.getLocalPlayer();
2401 assert(player != NULL);
2402 dst = player->inventory;
2405 Inventory* Client::getInventory(const InventoryLocation &loc)
2408 case InventoryLocation::UNDEFINED:
2411 case InventoryLocation::CURRENT_PLAYER:
2413 Player *player = m_env.getLocalPlayer();
2414 assert(player != NULL);
2415 return &player->inventory;
2418 case InventoryLocation::PLAYER:
2420 Player *player = m_env.getPlayer(loc.name.c_str());
2423 return &player->inventory;
2426 case InventoryLocation::NODEMETA:
2428 NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p);
2431 return meta->getInventory();
2434 case InventoryLocation::DETACHED:
2436 if(m_detached_inventories.count(loc.name) == 0)
2438 return m_detached_inventories[loc.name];
2447 void Client::inventoryAction(InventoryAction *a)
2450 Send it to the server
2452 sendInventoryAction(a);
2455 Predict some local inventory changes
2457 a->clientApply(this, this);
2463 ClientActiveObject * Client::getSelectedActiveObject(
2465 v3f from_pos_f_on_map,
2466 core::line3d<f32> shootline_on_map
2469 std::vector<DistanceSortedActiveObject> objects;
2471 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
2474 // After this, the closest object is the first in the array.
2475 std::sort(objects.begin(), objects.end());
2477 for(unsigned int i=0; i<objects.size(); i++)
2479 ClientActiveObject *obj = objects[i].obj;
2481 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
2482 if(selection_box == NULL)
2485 v3f pos = obj->getPosition();
2487 core::aabbox3d<f32> offsetted_box(
2488 selection_box->MinEdge + pos,
2489 selection_box->MaxEdge + pos
2492 if(offsetted_box.intersectsWithLine(shootline_on_map))
2501 std::list<std::string> Client::getConnectedPlayerNames()
2503 return m_env.getPlayerNames();
2506 float Client::getAnimationTime()
2508 return m_animation_time;
2511 int Client::getCrackLevel()
2513 return m_crack_level;
2516 void Client::setHighlighted(v3s16 pos, bool show_hud)
2518 m_show_hud = show_hud;
2519 v3s16 old_highlighted_pos = m_highlighted_pos;
2520 m_highlighted_pos = pos;
2521 addUpdateMeshTaskForNode(old_highlighted_pos, false, true);
2522 addUpdateMeshTaskForNode(m_highlighted_pos, false, true);
2525 void Client::setCrack(int level, v3s16 pos)
2527 int old_crack_level = m_crack_level;
2528 v3s16 old_crack_pos = m_crack_pos;
2530 m_crack_level = level;
2533 if(old_crack_level >= 0 && (level < 0 || pos != old_crack_pos))
2536 addUpdateMeshTaskForNode(old_crack_pos, false, true);
2538 if(level >= 0 && (old_crack_level < 0 || pos != old_crack_pos))
2541 addUpdateMeshTaskForNode(pos, false, true);
2547 Player *player = m_env.getLocalPlayer();
2548 assert(player != NULL);
2552 u16 Client::getBreath()
2554 Player *player = m_env.getLocalPlayer();
2555 assert(player != NULL);
2556 return player->getBreath();
2559 bool Client::getChatMessage(std::wstring &message)
2561 if(m_chat_queue.size() == 0)
2563 message = m_chat_queue.pop_front();
2567 void Client::typeChatMessage(const std::wstring &message)
2569 // Discard empty line
2574 sendChatMessage(message);
2577 if (message[0] == L'/')
2579 m_chat_queue.push_back((std::wstring)L"issued command: " + message);
2583 LocalPlayer *player = m_env.getLocalPlayer();
2584 assert(player != NULL);
2585 std::wstring name = narrow_to_wide(player->getName());
2586 m_chat_queue.push_back((std::wstring)L"<" + name + L"> " + message);
2590 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
2592 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2597 Create a task to update the mesh of the block
2600 MeshMakeData *data = new MeshMakeData(this);
2603 //TimeTaker timer("data fill");
2605 // Debug: 1-6ms, avg=2ms
2607 data->setCrack(m_crack_level, m_crack_pos);
2608 data->setHighlighted(m_highlighted_pos, m_show_hud);
2609 data->setSmoothLighting(g_settings->getBool("smooth_lighting"));
2612 // Add task to queue
2613 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server, urgent);
2616 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent)
2619 v3s16 p = blockpos + v3s16(0,0,0);
2620 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2621 addUpdateMeshTask(p, ack_to_server, urgent);
2623 catch(InvalidPositionException &e){}
2626 for (int i=0;i<6;i++)
2629 v3s16 p = blockpos + g_6dirs[i];
2630 addUpdateMeshTask(p, false, urgent);
2632 catch(InvalidPositionException &e){}
2636 void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool urgent)
2640 infostream<<"Client::addUpdateMeshTaskForNode(): "
2641 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2645 v3s16 blockpos = getNodeBlockPos(nodepos);
2646 v3s16 blockpos_relative = blockpos * MAP_BLOCKSIZE;
2649 v3s16 p = blockpos + v3s16(0,0,0);
2650 addUpdateMeshTask(p, ack_to_server, urgent);
2652 catch(InvalidPositionException &e){}
2655 if(nodepos.X == blockpos_relative.X){
2657 v3s16 p = blockpos + v3s16(-1,0,0);
2658 addUpdateMeshTask(p, false, urgent);
2660 catch(InvalidPositionException &e){}
2663 if(nodepos.Y == blockpos_relative.Y){
2665 v3s16 p = blockpos + v3s16(0,-1,0);
2666 addUpdateMeshTask(p, false, urgent);
2668 catch(InvalidPositionException &e){}
2671 if(nodepos.Z == blockpos_relative.Z){
2673 v3s16 p = blockpos + v3s16(0,0,-1);
2674 addUpdateMeshTask(p, false, urgent);
2676 catch(InvalidPositionException &e){}
2680 ClientEvent Client::getClientEvent()
2682 if(m_client_event_queue.size() == 0)
2685 event.type = CE_NONE;
2688 return m_client_event_queue.pop_front();
2691 float Client::mediaReceiveProgress()
2693 if (m_media_downloader)
2694 return m_media_downloader->getProgress();
2696 return 1.0; // downloader only exists when not yet done
2699 void Client::afterContentReceived(IrrlichtDevice *device, gui::IGUIFont* font)
2701 infostream<<"Client::afterContentReceived() started"<<std::endl;
2702 assert(m_itemdef_received);
2703 assert(m_nodedef_received);
2704 assert(mediaReceived());
2706 // Rebuild inherited images and recreate textures
2707 infostream<<"- Rebuilding images and textures"<<std::endl;
2708 m_tsrc->rebuildImagesAndTextures();
2711 infostream<<"- Rebuilding shaders"<<std::endl;
2712 m_shsrc->rebuildShaders();
2714 // Update node aliases
2715 infostream<<"- Updating node aliases"<<std::endl;
2716 m_nodedef->updateAliases(m_itemdef);
2718 // Update node textures and assign shaders to each tile
2719 infostream<<"- Updating node textures"<<std::endl;
2720 m_nodedef->updateTextures(this);
2722 // Preload item textures and meshes if configured to
2723 if(g_settings->getBool("preload_item_visuals"))
2725 verbosestream<<"Updating item textures and meshes"<<std::endl;
2726 wchar_t* text = wgettext("Item textures...");
2727 draw_load_screen(text, device, guienv, font, 0, 0);
2728 std::set<std::string> names = m_itemdef->getAll();
2729 size_t size = names.size();
2732 for(std::set<std::string>::const_iterator
2733 i = names.begin(); i != names.end(); ++i){
2734 // Asking for these caches the result
2735 m_itemdef->getInventoryTexture(*i, this);
2736 m_itemdef->getWieldMesh(*i, this);
2738 percent = count*100/size;
2739 if (count%50 == 0) // only update every 50 item
2740 draw_load_screen(text, device, guienv, font, 0, percent);
2745 // Start mesh update thread after setting up content definitions
2746 infostream<<"- Starting mesh update thread"<<std::endl;
2747 m_mesh_update_thread.Start();
2751 infostream<<"Client::afterContentReceived() done"<<std::endl;
2754 float Client::getRTT(void)
2756 return m_con.getPeerStat(PEER_ID_SERVER,con::AVG_RTT);
2759 float Client::getCurRate(void)
2761 return ( m_con.getLocalStat(con::CUR_INC_RATE) +
2762 m_con.getLocalStat(con::CUR_DL_RATE));
2765 float Client::getAvgRate(void)
2767 return ( m_con.getLocalStat(con::AVG_INC_RATE) +
2768 m_con.getLocalStat(con::AVG_DL_RATE));
2771 void Client::makeScreenshot(IrrlichtDevice *device)
2773 irr::video::IVideoDriver *driver = device->getVideoDriver();
2774 irr::video::IImage* const raw_image = driver->createScreenShot();
2776 irr::video::IImage* const image = driver->createImage(video::ECF_R8G8B8,
2777 raw_image->getDimension());
2780 raw_image->copyTo(image);
2781 irr::c8 filename[256];
2782 snprintf(filename, sizeof(filename), "%s" DIR_DELIM "screenshot_%u.png",
2783 g_settings->get("screenshot_path").c_str(),
2784 device->getTimer()->getRealTime());
2785 std::stringstream sstr;
2786 if (driver->writeImageToFile(image, filename)) {
2787 sstr << "Saved screenshot to '" << filename << "'";
2789 sstr << "Failed to save screenshot '" << filename << "'";
2791 m_chat_queue.push_back(narrow_to_wide(sstr.str()));
2792 infostream << sstr << std::endl;
2799 // IGameDef interface
2801 IItemDefManager* Client::getItemDefManager()
2805 INodeDefManager* Client::getNodeDefManager()
2809 ICraftDefManager* Client::getCraftDefManager()
2812 //return m_craftdef;
2814 ITextureSource* Client::getTextureSource()
2818 IShaderSource* Client::getShaderSource()
2822 scene::ISceneManager* Client::getSceneManager()
2824 return m_device->getSceneManager();
2826 u16 Client::allocateUnknownNodeId(const std::string &name)
2828 errorstream<<"Client::allocateUnknownNodeId(): "
2829 <<"Client cannot allocate node IDs"<<std::endl;
2831 return CONTENT_IGNORE;
2833 ISoundManager* Client::getSoundManager()
2837 MtEventManager* Client::getEventManager()
2842 scene::IAnimatedMesh* Client::getMesh(const std::string &filename)
2844 std::map<std::string, std::string>::const_iterator i =
2845 m_mesh_data.find(filename);
2846 if(i == m_mesh_data.end()){
2847 errorstream<<"Client::getMesh(): Mesh not found: \""<<filename<<"\""
2851 const std::string &data = i->second;
2852 scene::ISceneManager *smgr = m_device->getSceneManager();
2854 // Create the mesh, remove it from cache and return it
2855 // This allows unique vertex colors and other properties for each instance
2856 Buffer<char> data_rw(data.c_str(), data.size()); // Const-incorrect Irrlicht
2857 io::IFileSystem *irrfs = m_device->getFileSystem();
2858 io::IReadFile *rfile = irrfs->createMemoryReadFile(
2859 *data_rw, data_rw.getSize(), filename.c_str());
2862 scene::IAnimatedMesh *mesh = smgr->getMesh(rfile);
2864 // NOTE: By playing with Irrlicht refcounts, maybe we could cache a bunch
2865 // of uniquely named instances and re-use them
2867 smgr->getMeshCache()->removeMesh(mesh);