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),
258 m_show_highlighted(false),
262 m_highlighted_pos(0,0,0),
264 m_password(password),
265 m_access_denied(false),
266 m_itemdef_received(false),
267 m_nodedef_received(false),
268 m_media_downloader(new ClientMediaDownloader()),
269 m_time_of_day_set(false),
270 m_last_time_of_day_f(-1),
271 m_time_of_day_update_timer(0),
272 m_recommended_send_interval(0.1),
273 m_removed_sounds_check_timer(0),
280 Player *player = new LocalPlayer(this, playername);
282 m_env.addPlayer(player);
285 if (g_settings->getBool("enable_local_map_saving")
286 && !is_simple_singleplayer_game) {
287 const std::string world_path = porting::path_user + DIR_DELIM + "worlds"
288 + DIR_DELIM + "server_" + g_settings->get("address")
289 + "_" + g_settings->get("remote_port");
291 SubgameSpec gamespec;
292 if (!getWorldExists(world_path)) {
293 gamespec = findSubgame(g_settings->get("default_game"));
294 if (!gamespec.isValid())
295 gamespec = findSubgame("minimal");
297 std::string world_gameid = getWorldGameId(world_path, false);
298 gamespec = findWorldSubgame(world_path);
300 if (!gamespec.isValid()) {
301 errorstream << "Couldn't find subgame for local map saving." << std::endl;
305 localserver = new Server(world_path, gamespec, false, false);
306 localdb = new Database_SQLite3(&(ServerMap&)localserver->getMap(), world_path);
307 localdb->beginSave();
308 actionstream << "Local map saving started, map will be saved at '" << world_path << "'" << std::endl;
313 m_cache_smooth_lighting = g_settings->getBool("smooth_lighting");
318 //request all client managed threads to stop
319 m_mesh_update_thread.Stop();
320 if (localdb != NULL) {
321 actionstream << "Local map saving ended" << std::endl;
326 bool Client::isShutdown()
329 if (!m_mesh_update_thread.IsRunning()) return true;
338 m_mesh_update_thread.Stop();
339 m_mesh_update_thread.Wait();
340 while(!m_mesh_update_thread.m_queue_out.empty()) {
341 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_frontNoEx();
346 delete m_inventory_from_server;
348 // Delete detached inventories
349 for(std::map<std::string, Inventory*>::iterator
350 i = m_detached_inventories.begin();
351 i != m_detached_inventories.end(); i++){
355 // cleanup 3d model meshes on client shutdown
356 while (m_device->getSceneManager()->getMeshCache()->getMeshCount() != 0) {
357 scene::IAnimatedMesh * mesh =
358 m_device->getSceneManager()->getMeshCache()->getMeshByIndex(0);
361 m_device->getSceneManager()->getMeshCache()->removeMesh(mesh);
365 void Client::connect(Address address)
367 DSTACK(__FUNCTION_NAME);
368 m_con.SetTimeoutMs(0);
369 m_con.Connect(address);
372 void Client::step(float dtime)
374 DSTACK(__FUNCTION_NAME);
380 if(m_ignore_damage_timer > dtime)
381 m_ignore_damage_timer -= dtime;
383 m_ignore_damage_timer = 0.0;
385 m_animation_time += dtime;
386 if(m_animation_time > 60.0)
387 m_animation_time -= 60.0;
389 m_time_of_day_update_timer += dtime;
397 float &counter = m_packetcounter_timer;
403 infostream << "Client packetcounter (" << m_packetcounter_timer
405 m_packetcounter.print(infostream);
406 m_packetcounter.clear();
413 Delete unused sectors
415 NOTE: This jams the game for a while because deleting sectors
419 float &counter = m_delete_unused_sectors_timer;
427 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
429 core::list<v3s16> deleted_blocks;
431 float delete_unused_sectors_timeout =
432 g_settings->getFloat("client_delete_unused_sectors_timeout");
434 // Delete sector blocks
435 /*u32 num = m_env.getMap().unloadUnusedData
436 (delete_unused_sectors_timeout,
437 true, &deleted_blocks);*/
439 // Delete whole sectors
440 m_env.getMap().unloadUnusedData
441 (delete_unused_sectors_timeout,
444 if(deleted_blocks.size() > 0)
446 /*infostream<<"Client: Deleted blocks of "<<num
447 <<" unused sectors"<<std::endl;*/
448 /*infostream<<"Client: Deleted "<<num
449 <<" unused sectors"<<std::endl;*/
455 // Env is locked so con can be locked.
456 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
458 core::list<v3s16>::Iterator i = deleted_blocks.begin();
459 core::list<v3s16> sendlist;
462 if(sendlist.size() == 255 || i == deleted_blocks.end())
464 if(sendlist.size() == 0)
473 u32 replysize = 2+1+6*sendlist.size();
474 SharedBuffer<u8> reply(replysize);
475 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
476 reply[2] = sendlist.size();
478 for(core::list<v3s16>::Iterator
479 j = sendlist.begin();
480 j != sendlist.end(); j++)
482 writeV3S16(&reply[2+1+6*k], *j);
485 m_con.Send(PEER_ID_SERVER, 1, reply, true);
487 if(i == deleted_blocks.end())
493 sendlist.push_back(*i);
500 // UGLY hack to fix 2 second startup delay caused by non existent
501 // server client startup synchronization in local server or singleplayer mode
502 static bool initial_step = true;
504 initial_step = false;
506 else if(m_state == LC_Created)
508 float &counter = m_connection_reinit_timer;
514 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
516 Player *myplayer = m_env.getLocalPlayer();
517 assert(myplayer != NULL);
518 // Send TOSERVER_INIT
519 // [0] u16 TOSERVER_INIT
520 // [2] u8 SER_FMT_VER_HIGHEST_READ
521 // [3] u8[20] player_name
522 // [23] u8[28] password (new in some version)
523 // [51] u16 minimum supported network protocol version (added sometime)
524 // [53] u16 maximum supported network protocol version (added later than the previous one)
525 SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2+2);
526 writeU16(&data[0], TOSERVER_INIT);
527 writeU8(&data[2], SER_FMT_VER_HIGHEST_READ);
529 memset((char*)&data[3], 0, PLAYERNAME_SIZE);
530 snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
532 /*infostream<<"Client: sending initial password hash: \""<<m_password<<"\""
535 memset((char*)&data[23], 0, PASSWORD_SIZE);
536 snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
538 writeU16(&data[51], CLIENT_PROTOCOL_VERSION_MIN);
539 writeU16(&data[53], CLIENT_PROTOCOL_VERSION_MAX);
541 // Send as unreliable
542 Send(1, data, false);
545 // Not connected, return
550 Do stuff if connected
554 Run Map's timers and unload unused data
556 const float map_timer_and_unload_dtime = 5.25;
557 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
559 ScopeProfiler sp(g_profiler, "Client: map timer and unload");
560 std::list<v3s16> deleted_blocks;
561 m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
562 g_settings->getFloat("client_unload_unused_data_timeout"),
565 /*if(deleted_blocks.size() > 0)
566 infostream<<"Client: Unloaded "<<deleted_blocks.size()
567 <<" unused blocks"<<std::endl;*/
571 NOTE: This loop is intentionally iterated the way it is.
574 std::list<v3s16>::iterator i = deleted_blocks.begin();
575 std::list<v3s16> sendlist;
578 if(sendlist.size() == 255 || i == deleted_blocks.end())
589 u32 replysize = 2+1+6*sendlist.size();
590 SharedBuffer<u8> reply(replysize);
591 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
592 reply[2] = sendlist.size();
594 for(std::list<v3s16>::iterator
595 j = sendlist.begin();
596 j != sendlist.end(); ++j)
598 writeV3S16(&reply[2+1+6*k], *j);
601 m_con.Send(PEER_ID_SERVER, 2, reply, true);
603 if(i == deleted_blocks.end())
609 sendlist.push_back(*i);
618 // Control local player (0ms)
619 LocalPlayer *player = m_env.getLocalPlayer();
620 assert(player != NULL);
621 player->applyControl(dtime);
631 ClientEnvEvent event = m_env.getClientEvent();
632 if(event.type == CEE_NONE)
636 else if(event.type == CEE_PLAYER_DAMAGE)
638 if(m_ignore_damage_timer <= 0)
640 u8 damage = event.player_damage.amount;
642 if(event.player_damage.send_to_server)
645 // Add to ClientEvent queue
647 event.type = CE_PLAYER_DAMAGE;
648 event.player_damage.amount = damage;
649 m_client_event_queue.push_back(event);
652 else if(event.type == CEE_PLAYER_BREATH)
654 u16 breath = event.player_breath.amount;
664 float &counter = m_avg_rtt_timer;
669 // connectedAndInitialized() is true, peer exists.
670 float avg_rtt = getRTT();
671 infostream<<"Client: avg_rtt="<<avg_rtt<<std::endl;
676 Send player position to server
679 float &counter = m_playerpos_send_timer;
681 if((m_state == LC_Ready) && (counter >= m_recommended_send_interval))
689 Replace updated meshes
692 int num_processed_meshes = 0;
693 while(!m_mesh_update_thread.m_queue_out.empty())
695 num_processed_meshes++;
696 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_frontNoEx();
697 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
700 // Delete the old mesh
701 if(block->mesh != NULL)
703 // TODO: Remove hardware buffers of meshbuffers of block->mesh
708 // Replace with the new mesh
709 block->mesh = r.mesh;
713 if(r.ack_block_to_server)
725 u32 replysize = 2+1+6;
726 SharedBuffer<u8> reply(replysize);
727 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
729 writeV3S16(&reply[3], r.p);
731 m_con.Send(PEER_ID_SERVER, 2, reply, true);
734 if(num_processed_meshes > 0)
735 g_profiler->graphAdd("num_processed_meshes", num_processed_meshes);
741 if (m_media_downloader && m_media_downloader->isStarted()) {
742 m_media_downloader->step(this);
743 if (m_media_downloader->isDone()) {
745 delete m_media_downloader;
746 m_media_downloader = NULL;
751 If the server didn't update the inventory in a while, revert
752 the local inventory (so the player notices the lag problem
753 and knows something is wrong).
755 if(m_inventory_from_server)
757 float interval = 10.0;
758 float count_before = floor(m_inventory_from_server_age / interval);
760 m_inventory_from_server_age += dtime;
762 float count_after = floor(m_inventory_from_server_age / interval);
764 if(count_after != count_before)
766 // Do this every <interval> seconds after TOCLIENT_INVENTORY
767 // Reset the locally changed inventory to the authoritative inventory
768 Player *player = m_env.getLocalPlayer();
769 player->inventory = *m_inventory_from_server;
770 m_inventory_updated = true;
775 Update positions of sounds attached to objects
778 for(std::map<int, u16>::iterator
779 i = m_sounds_to_objects.begin();
780 i != m_sounds_to_objects.end(); i++)
782 int client_id = i->first;
783 u16 object_id = i->second;
784 ClientActiveObject *cao = m_env.getActiveObject(object_id);
787 v3f pos = cao->getPosition();
788 m_sound->updateSoundPosition(client_id, pos);
793 Handle removed remotely initiated sounds
795 m_removed_sounds_check_timer += dtime;
796 if(m_removed_sounds_check_timer >= 2.32)
798 m_removed_sounds_check_timer = 0;
799 // Find removed sounds and clear references to them
800 std::set<s32> removed_server_ids;
801 for(std::map<s32, int>::iterator
802 i = m_sounds_server_to_client.begin();
803 i != m_sounds_server_to_client.end();)
805 s32 server_id = i->first;
806 int client_id = i->second;
808 if(!m_sound->soundExists(client_id)){
809 m_sounds_server_to_client.erase(server_id);
810 m_sounds_client_to_server.erase(client_id);
811 m_sounds_to_objects.erase(client_id);
812 removed_server_ids.insert(server_id);
816 if(!removed_server_ids.empty())
818 std::ostringstream os(std::ios_base::binary);
819 writeU16(os, TOSERVER_REMOVED_SOUNDS);
820 size_t server_ids = removed_server_ids.size();
821 assert(server_ids <= 0xFFFF);
822 writeU16(os, (u16) (server_ids & 0xFFFF));
823 for(std::set<s32>::iterator i = removed_server_ids.begin();
824 i != removed_server_ids.end(); i++)
826 std::string s = os.str();
827 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
834 bool Client::loadMedia(const std::string &data, const std::string &filename)
836 // Silly irrlicht's const-incorrectness
837 Buffer<char> data_rw(data.c_str(), data.size());
841 const char *image_ext[] = {
842 ".png", ".jpg", ".bmp", ".tga",
843 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
846 name = removeStringEnd(filename, image_ext);
849 verbosestream<<"Client: Attempting to load image "
850 <<"file \""<<filename<<"\""<<std::endl;
852 io::IFileSystem *irrfs = m_device->getFileSystem();
853 video::IVideoDriver *vdrv = m_device->getVideoDriver();
855 // Create an irrlicht memory file
856 io::IReadFile *rfile = irrfs->createMemoryReadFile(
857 *data_rw, data_rw.getSize(), "_tempreadfile");
860 video::IImage *img = vdrv->createImageFromFile(rfile);
862 errorstream<<"Client: Cannot create image from data of "
863 <<"file \""<<filename<<"\""<<std::endl;
868 m_tsrc->insertSourceImage(filename, img);
875 const char *sound_ext[] = {
876 ".0.ogg", ".1.ogg", ".2.ogg", ".3.ogg", ".4.ogg",
877 ".5.ogg", ".6.ogg", ".7.ogg", ".8.ogg", ".9.ogg",
880 name = removeStringEnd(filename, sound_ext);
883 verbosestream<<"Client: Attempting to load sound "
884 <<"file \""<<filename<<"\""<<std::endl;
885 m_sound->loadSoundData(name, data);
889 const char *model_ext[] = {
890 ".x", ".b3d", ".md2", ".obj",
893 name = removeStringEnd(filename, model_ext);
896 verbosestream<<"Client: Storing model into memory: "
897 <<"\""<<filename<<"\""<<std::endl;
898 if(m_mesh_data.count(filename))
899 errorstream<<"Multiple models with name \""<<filename.c_str()
900 <<"\" found; replacing previous model"<<std::endl;
901 m_mesh_data[filename] = data;
905 errorstream<<"Client: Don't know how to load file \""
906 <<filename<<"\""<<std::endl;
910 // Virtual methods from con::PeerHandler
911 void Client::peerAdded(con::Peer *peer)
913 infostream<<"Client::peerAdded(): peer->id="
914 <<peer->id<<std::endl;
916 void Client::deletingPeer(con::Peer *peer, bool timeout)
918 infostream<<"Client::deletingPeer(): "
919 "Server Peer is getting deleted "
920 <<"(timeout="<<timeout<<")"<<std::endl;
925 u16 number of files requested
931 void Client::request_media(const std::list<std::string> &file_requests)
933 std::ostringstream os(std::ios_base::binary);
934 writeU16(os, TOSERVER_REQUEST_MEDIA);
935 size_t file_requests_size = file_requests.size();
936 assert(file_requests_size <= 0xFFFF);
937 writeU16(os, (u16) (file_requests_size & 0xFFFF));
939 for(std::list<std::string>::const_iterator i = file_requests.begin();
940 i != file_requests.end(); ++i) {
941 os<<serializeString(*i);
945 std::string s = os.str();
946 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
949 infostream<<"Client: Sending media request list to server ("
950 <<file_requests.size()<<" files)"<<std::endl;
953 void Client::received_media()
955 // notify server we received everything
956 std::ostringstream os(std::ios_base::binary);
957 writeU16(os, TOSERVER_RECEIVED_MEDIA);
958 std::string s = os.str();
959 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
962 infostream<<"Client: Notifying server that we received all media"
966 void Client::ReceiveAll()
968 DSTACK(__FUNCTION_NAME);
969 u32 start_ms = porting::getTimeMs();
972 // Limit time even if there would be huge amounts of data to
974 if(porting::getTimeMs() > start_ms + 100)
979 g_profiler->graphAdd("client_received_packets", 1);
981 catch(con::NoIncomingDataException &e)
985 catch(con::InvalidIncomingDataException &e)
987 infostream<<"Client::ReceiveAll(): "
988 "InvalidIncomingDataException: what()="
989 <<e.what()<<std::endl;
994 void Client::Receive()
996 DSTACK(__FUNCTION_NAME);
997 SharedBuffer<u8> data;
999 u32 datasize = m_con.Receive(sender_peer_id, data);
1000 ProcessData(*data, datasize, sender_peer_id);
1004 sender_peer_id given to this shall be quaranteed to be a valid peer
1006 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
1008 DSTACK(__FUNCTION_NAME);
1010 // Ignore packets that don't even fit a command
1013 m_packetcounter.add(60000);
1017 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
1019 //infostream<<"Client: received command="<<command<<std::endl;
1020 m_packetcounter.add((u16)command);
1023 If this check is removed, be sure to change the queue
1024 system to know the ids
1026 if(sender_peer_id != PEER_ID_SERVER)
1028 infostream<<"Client::ProcessData(): Discarding data not "
1029 "coming from server: peer_id="<<sender_peer_id
1034 u8 ser_version = m_server_ser_ver;
1036 if(command == TOCLIENT_INIT)
1041 u8 deployed = data[2];
1043 infostream<<"Client: TOCLIENT_INIT received with "
1044 "deployed="<<((int)deployed&0xff)<<std::endl;
1046 if(!ser_ver_supported(deployed))
1048 infostream<<"Client: TOCLIENT_INIT: Server sent "
1049 <<"unsupported ser_fmt_ver"<<std::endl;
1053 m_server_ser_ver = deployed;
1055 // Get player position
1056 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
1057 if(datasize >= 2+1+6)
1058 playerpos_s16 = readV3S16(&data[2+1]);
1059 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
1062 // Set player position
1063 Player *player = m_env.getLocalPlayer();
1064 assert(player != NULL);
1065 player->setPosition(playerpos_f);
1067 if(datasize >= 2+1+6+8)
1070 m_map_seed = readU64(&data[2+1+6]);
1071 infostream<<"Client: received map seed: "<<m_map_seed<<std::endl;
1074 if(datasize >= 2+1+6+8+4)
1077 m_recommended_send_interval = readF1000(&data[2+1+6+8]);
1078 infostream<<"Client: received recommended send interval "
1079 <<m_recommended_send_interval<<std::endl;
1084 SharedBuffer<u8> reply(replysize);
1085 writeU16(&reply[0], TOSERVER_INIT2);
1087 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1094 if(command == TOCLIENT_ACCESS_DENIED)
1096 // The server didn't like our password. Note, this needs
1097 // to be processed even if the serialisation format has
1098 // not been agreed yet, the same as TOCLIENT_INIT.
1099 m_access_denied = true;
1100 m_access_denied_reason = L"Unknown";
1103 std::string datastring((char*)&data[2], datasize-2);
1104 std::istringstream is(datastring, std::ios_base::binary);
1105 m_access_denied_reason = deSerializeWideString(is);
1110 if(ser_version == SER_FMT_VER_INVALID)
1112 infostream<<"Client: Server serialization"
1113 " format invalid or not initialized."
1114 " Skipping incoming command="<<command<<std::endl;
1119 Handle runtime commands
1121 // there's no sane reason why we shouldn't have a player and
1122 // almost everyone needs a player reference
1123 Player *player = m_env.getLocalPlayer();
1124 assert(player != NULL);
1126 if(command == TOCLIENT_REMOVENODE)
1131 p.X = readS16(&data[2]);
1132 p.Y = readS16(&data[4]);
1133 p.Z = readS16(&data[6]);
1136 else if(command == TOCLIENT_ADDNODE)
1138 if(datasize < 8 + MapNode::serializedLength(ser_version))
1142 p.X = readS16(&data[2]);
1143 p.Y = readS16(&data[4]);
1144 p.Z = readS16(&data[6]);
1147 n.deSerialize(&data[8], ser_version);
1149 bool remove_metadata = true;
1150 u32 index = 8 + MapNode::serializedLength(ser_version);
1151 if ((datasize >= index+1) && data[index]){
1152 remove_metadata = false;
1155 addNode(p, n, remove_metadata);
1157 else if(command == TOCLIENT_BLOCKDATA)
1159 // Ignore too small packet
1164 p.X = readS16(&data[2]);
1165 p.Y = readS16(&data[4]);
1166 p.Z = readS16(&data[6]);
1168 std::string datastring((char*)&data[8], datasize-8);
1169 std::istringstream istr(datastring, std::ios_base::binary);
1174 v2s16 p2d(p.X, p.Z);
1175 sector = m_env.getMap().emergeSector(p2d);
1177 assert(sector->getPos() == p2d);
1179 block = sector->getBlockNoCreateNoEx(p.Y);
1183 Update an existing block
1185 block->deSerialize(istr, ser_version, false);
1186 block->deSerializeNetworkSpecific(istr);
1193 block = new MapBlock(&m_env.getMap(), p, this);
1194 block->deSerialize(istr, ser_version, false);
1195 block->deSerializeNetworkSpecific(istr);
1196 sector->insertBlock(block);
1199 if (localdb != NULL) {
1200 ((ServerMap&) localserver->getMap()).saveBlock(block, localdb);
1204 Add it to mesh update queue and set it to be acknowledged after update.
1206 addUpdateMeshTaskWithEdge(p, true);
1208 else if(command == TOCLIENT_INVENTORY)
1213 std::string datastring((char*)&data[2], datasize-2);
1214 std::istringstream is(datastring, std::ios_base::binary);
1216 player->inventory.deSerialize(is);
1218 m_inventory_updated = true;
1220 delete m_inventory_from_server;
1221 m_inventory_from_server = new Inventory(player->inventory);
1222 m_inventory_from_server_age = 0.0;
1225 else if(command == TOCLIENT_TIME_OF_DAY)
1230 u16 time_of_day = readU16(&data[2]);
1231 time_of_day = time_of_day % 24000;
1232 float time_speed = 0;
1234 if(datasize >= 2 + 2 + 4)
1236 time_speed = readF1000(&data[4]);
1239 // Old message; try to approximate speed of time by ourselves
1240 float time_of_day_f = (float)time_of_day / 24000.0;
1241 float tod_diff_f = 0;
1243 if(time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
1244 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0;
1246 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
1248 m_last_time_of_day_f = time_of_day_f;
1249 float time_diff = m_time_of_day_update_timer;
1250 m_time_of_day_update_timer = 0;
1252 if(m_time_of_day_set){
1253 time_speed = (3600.0*24.0) * tod_diff_f / time_diff;
1254 infostream<<"Client: Measured time_of_day speed (old format): "
1255 <<time_speed<<" tod_diff_f="<<tod_diff_f
1256 <<" time_diff="<<time_diff<<std::endl;
1260 // Update environment
1261 m_env.setTimeOfDay(time_of_day);
1262 m_env.setTimeOfDaySpeed(time_speed);
1263 m_time_of_day_set = true;
1265 u32 dr = m_env.getDayNightRatio();
1266 infostream<<"Client: time_of_day="<<time_of_day
1267 <<" time_speed="<<time_speed
1268 <<" dr="<<dr<<std::endl;
1270 else if(command == TOCLIENT_CHAT_MESSAGE)
1278 std::string datastring((char*)&data[2], datasize-2);
1279 std::istringstream is(datastring, std::ios_base::binary);
1282 is.read((char*) buf, 2);
1283 u16 len = readU16(buf);
1285 std::wstring message;
1286 for(unsigned int i=0; i<len; i++)
1288 is.read((char*)buf, 2);
1289 message += (wchar_t)readU16(buf);
1292 m_chat_queue.push_back(message);
1294 else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1298 u16 count of removed objects
1299 for all removed objects {
1302 u16 count of added objects
1303 for all added objects {
1306 u32 initialization data length
1307 string initialization data
1312 // Get all data except the command number
1313 std::string datastring((char*)&data[2], datasize-2);
1314 // Throw them in an istringstream
1315 std::istringstream is(datastring, std::ios_base::binary);
1317 // Read removed objects
1319 u16 removed_count = readU16((u8*)buf);
1320 for(unsigned int i=0; i<removed_count; i++)
1323 u16 id = readU16((u8*)buf);
1324 m_env.removeActiveObject(id);
1327 // Read added objects
1329 u16 added_count = readU16((u8*)buf);
1330 for(unsigned int i=0; i<added_count; i++)
1333 u16 id = readU16((u8*)buf);
1335 u8 type = readU8((u8*)buf);
1336 std::string data = deSerializeLongString(is);
1338 m_env.addActiveObject(id, type, data);
1341 else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1353 // Get all data except the command number
1354 std::string datastring((char*)&data[2], datasize-2);
1355 // Throw them in an istringstream
1356 std::istringstream is(datastring, std::ios_base::binary);
1358 while(is.eof() == false)
1361 u16 id = readU16((u8*)buf);
1365 size_t message_size = readU16((u8*)buf);
1366 std::string message;
1367 message.reserve(message_size);
1368 for(unsigned int i=0; i<message_size; i++)
1371 message.append(buf, 1);
1373 // Pass on to the environment
1374 m_env.processActiveObjectMessage(id, message);
1377 else if(command == TOCLIENT_MOVEMENT)
1379 std::string datastring((char*)&data[2], datasize-2);
1380 std::istringstream is(datastring, std::ios_base::binary);
1382 player->movement_acceleration_default = readF1000(is) * BS;
1383 player->movement_acceleration_air = readF1000(is) * BS;
1384 player->movement_acceleration_fast = readF1000(is) * BS;
1385 player->movement_speed_walk = readF1000(is) * BS;
1386 player->movement_speed_crouch = readF1000(is) * BS;
1387 player->movement_speed_fast = readF1000(is) * BS;
1388 player->movement_speed_climb = readF1000(is) * BS;
1389 player->movement_speed_jump = readF1000(is) * BS;
1390 player->movement_liquid_fluidity = readF1000(is) * BS;
1391 player->movement_liquid_fluidity_smooth = readF1000(is) * BS;
1392 player->movement_liquid_sink = readF1000(is) * BS;
1393 player->movement_gravity = readF1000(is) * BS;
1395 else if(command == TOCLIENT_HP)
1397 std::string datastring((char*)&data[2], datasize-2);
1398 std::istringstream is(datastring, std::ios_base::binary);
1400 u8 oldhp = player->hp;
1406 // Add to ClientEvent queue
1408 event.type = CE_PLAYER_DAMAGE;
1409 event.player_damage.amount = oldhp - hp;
1410 m_client_event_queue.push_back(event);
1413 else if(command == TOCLIENT_BREATH)
1415 std::string datastring((char*)&data[2], datasize-2);
1416 std::istringstream is(datastring, std::ios_base::binary);
1418 player->setBreath(readU16(is));
1420 else if(command == TOCLIENT_MOVE_PLAYER)
1422 std::string datastring((char*)&data[2], datasize-2);
1423 std::istringstream is(datastring, std::ios_base::binary);
1425 v3f pos = readV3F1000(is);
1426 f32 pitch = readF1000(is);
1427 f32 yaw = readF1000(is);
1428 player->setPosition(pos);
1430 infostream<<"Client got TOCLIENT_MOVE_PLAYER"
1431 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1437 Add to ClientEvent queue.
1438 This has to be sent to the main program because otherwise
1439 it would just force the pitch and yaw values to whatever
1440 the camera points to.
1443 event.type = CE_PLAYER_FORCE_MOVE;
1444 event.player_force_move.pitch = pitch;
1445 event.player_force_move.yaw = yaw;
1446 m_client_event_queue.push_back(event);
1448 // Ignore damage for a few seconds, so that the player doesn't
1449 // get damage from falling on ground
1450 m_ignore_damage_timer = 3.0;
1452 else if(command == TOCLIENT_PLAYERITEM)
1454 infostream<<"Client: WARNING: Ignoring TOCLIENT_PLAYERITEM"<<std::endl;
1456 else if(command == TOCLIENT_DEATHSCREEN)
1458 std::string datastring((char*)&data[2], datasize-2);
1459 std::istringstream is(datastring, std::ios_base::binary);
1461 bool set_camera_point_target = readU8(is);
1462 v3f camera_point_target = readV3F1000(is);
1465 event.type = CE_DEATHSCREEN;
1466 event.deathscreen.set_camera_point_target = set_camera_point_target;
1467 event.deathscreen.camera_point_target_x = camera_point_target.X;
1468 event.deathscreen.camera_point_target_y = camera_point_target.Y;
1469 event.deathscreen.camera_point_target_z = camera_point_target.Z;
1470 m_client_event_queue.push_back(event);
1472 else if(command == TOCLIENT_ANNOUNCE_MEDIA)
1474 std::string datastring((char*)&data[2], datasize-2);
1475 std::istringstream is(datastring, std::ios_base::binary);
1477 int num_files = readU16(is);
1479 infostream<<"Client: Received media announcement: packet size: "
1480 <<datasize<<std::endl;
1482 if (m_media_downloader == NULL ||
1483 m_media_downloader->isStarted()) {
1484 const char *problem = m_media_downloader ?
1485 "we already saw another announcement" :
1486 "all media has been received already";
1487 errorstream<<"Client: Received media announcement but "
1489 <<" files="<<num_files
1490 <<" size="<<datasize<<std::endl;
1494 // Mesh update thread must be stopped while
1495 // updating content definitions
1496 assert(!m_mesh_update_thread.IsRunning());
1498 for(int i=0; i<num_files; i++)
1500 std::string name = deSerializeString(is);
1501 std::string sha1_base64 = deSerializeString(is);
1502 std::string sha1_raw = base64_decode(sha1_base64);
1503 m_media_downloader->addFile(name, sha1_raw);
1506 std::vector<std::string> remote_media;
1508 Strfnd sf(deSerializeString(is));
1509 while(!sf.atend()) {
1510 std::string baseurl = trim(sf.next(","));
1512 m_media_downloader->addRemoteServer(baseurl);
1515 catch(SerializationError& e) {
1516 // not supported by server or turned off
1519 m_media_downloader->step(this);
1521 else if(command == TOCLIENT_MEDIA)
1523 std::string datastring((char*)&data[2], datasize-2);
1524 std::istringstream is(datastring, std::ios_base::binary);
1528 u16 total number of file bunches
1529 u16 index of this bunch
1530 u32 number of files in this bunch
1538 int num_bunches = readU16(is);
1539 int bunch_i = readU16(is);
1540 u32 num_files = readU32(is);
1541 infostream<<"Client: Received files: bunch "<<bunch_i<<"/"
1542 <<num_bunches<<" files="<<num_files
1543 <<" size="<<datasize<<std::endl;
1548 if (m_media_downloader == NULL ||
1549 !m_media_downloader->isStarted()) {
1550 const char *problem = m_media_downloader ?
1551 "media has not been requested" :
1552 "all media has been received already";
1553 errorstream<<"Client: Received media but "
1555 <<" bunch "<<bunch_i<<"/"<<num_bunches
1556 <<" files="<<num_files
1557 <<" size="<<datasize<<std::endl;
1561 // Mesh update thread must be stopped while
1562 // updating content definitions
1563 assert(!m_mesh_update_thread.IsRunning());
1565 for(unsigned int i=0; i<num_files; i++){
1566 std::string name = deSerializeString(is);
1567 std::string data = deSerializeLongString(is);
1568 m_media_downloader->conventionalTransferDone(
1572 else if(command == TOCLIENT_TOOLDEF)
1574 infostream<<"Client: WARNING: Ignoring TOCLIENT_TOOLDEF"<<std::endl;
1576 else if(command == TOCLIENT_NODEDEF)
1578 infostream<<"Client: Received node definitions: packet size: "
1579 <<datasize<<std::endl;
1581 // Mesh update thread must be stopped while
1582 // updating content definitions
1583 assert(!m_mesh_update_thread.IsRunning());
1585 // Decompress node definitions
1586 std::string datastring((char*)&data[2], datasize-2);
1587 std::istringstream is(datastring, std::ios_base::binary);
1588 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1589 std::ostringstream tmp_os;
1590 decompressZlib(tmp_is, tmp_os);
1592 // Deserialize node definitions
1593 std::istringstream tmp_is2(tmp_os.str());
1594 m_nodedef->deSerialize(tmp_is2);
1595 m_nodedef_received = true;
1597 else if(command == TOCLIENT_CRAFTITEMDEF)
1599 infostream<<"Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF"<<std::endl;
1601 else if(command == TOCLIENT_ITEMDEF)
1603 infostream<<"Client: Received item definitions: packet size: "
1604 <<datasize<<std::endl;
1606 // Mesh update thread must be stopped while
1607 // updating content definitions
1608 assert(!m_mesh_update_thread.IsRunning());
1610 // Decompress item definitions
1611 std::string datastring((char*)&data[2], datasize-2);
1612 std::istringstream is(datastring, std::ios_base::binary);
1613 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1614 std::ostringstream tmp_os;
1615 decompressZlib(tmp_is, tmp_os);
1617 // Deserialize node definitions
1618 std::istringstream tmp_is2(tmp_os.str());
1619 m_itemdef->deSerialize(tmp_is2);
1620 m_itemdef_received = true;
1622 else if(command == TOCLIENT_PLAY_SOUND)
1624 std::string datastring((char*)&data[2], datasize-2);
1625 std::istringstream is(datastring, std::ios_base::binary);
1627 s32 server_id = readS32(is);
1628 std::string name = deSerializeString(is);
1629 float gain = readF1000(is);
1630 int type = readU8(is); // 0=local, 1=positional, 2=object
1631 v3f pos = readV3F1000(is);
1632 u16 object_id = readU16(is);
1633 bool loop = readU8(is);
1638 client_id = m_sound->playSound(name, loop, gain);
1640 case 1: // positional
1641 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1644 ClientActiveObject *cao = m_env.getActiveObject(object_id);
1646 pos = cao->getPosition();
1647 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1648 // TODO: Set up sound to move with object
1653 if(client_id != -1){
1654 m_sounds_server_to_client[server_id] = client_id;
1655 m_sounds_client_to_server[client_id] = server_id;
1657 m_sounds_to_objects[client_id] = object_id;
1660 else if(command == TOCLIENT_STOP_SOUND)
1662 std::string datastring((char*)&data[2], datasize-2);
1663 std::istringstream is(datastring, std::ios_base::binary);
1665 s32 server_id = readS32(is);
1666 std::map<s32, int>::iterator i =
1667 m_sounds_server_to_client.find(server_id);
1668 if(i != m_sounds_server_to_client.end()){
1669 int client_id = i->second;
1670 m_sound->stopSound(client_id);
1673 else if(command == TOCLIENT_PRIVILEGES)
1675 std::string datastring((char*)&data[2], datasize-2);
1676 std::istringstream is(datastring, std::ios_base::binary);
1678 m_privileges.clear();
1679 infostream<<"Client: Privileges updated: ";
1680 u16 num_privileges = readU16(is);
1681 for(unsigned int i=0; i<num_privileges; i++){
1682 std::string priv = deSerializeString(is);
1683 m_privileges.insert(priv);
1684 infostream<<priv<<" ";
1686 infostream<<std::endl;
1688 else if(command == TOCLIENT_INVENTORY_FORMSPEC)
1690 std::string datastring((char*)&data[2], datasize-2);
1691 std::istringstream is(datastring, std::ios_base::binary);
1693 // Store formspec in LocalPlayer
1694 player->inventory_formspec = deSerializeLongString(is);
1696 else if(command == TOCLIENT_DETACHED_INVENTORY)
1698 std::string datastring((char*)&data[2], datasize-2);
1699 std::istringstream is(datastring, std::ios_base::binary);
1701 std::string name = deSerializeString(is);
1703 infostream<<"Client: Detached inventory update: \""<<name<<"\""<<std::endl;
1705 Inventory *inv = NULL;
1706 if(m_detached_inventories.count(name) > 0)
1707 inv = m_detached_inventories[name];
1709 inv = new Inventory(m_itemdef);
1710 m_detached_inventories[name] = inv;
1712 inv->deSerialize(is);
1714 else if(command == TOCLIENT_SHOW_FORMSPEC)
1716 std::string datastring((char*)&data[2], datasize-2);
1717 std::istringstream is(datastring, std::ios_base::binary);
1719 std::string formspec = deSerializeLongString(is);
1720 std::string formname = deSerializeString(is);
1723 event.type = CE_SHOW_FORMSPEC;
1724 // pointer is required as event is a struct only!
1725 // adding a std:string to a struct isn't possible
1726 event.show_formspec.formspec = new std::string(formspec);
1727 event.show_formspec.formname = new std::string(formname);
1728 m_client_event_queue.push_back(event);
1730 else if(command == TOCLIENT_SPAWN_PARTICLE)
1732 std::string datastring((char*)&data[2], datasize-2);
1733 std::istringstream is(datastring, std::ios_base::binary);
1735 v3f pos = readV3F1000(is);
1736 v3f vel = readV3F1000(is);
1737 v3f acc = readV3F1000(is);
1738 float expirationtime = readF1000(is);
1739 float size = readF1000(is);
1740 bool collisiondetection = readU8(is);
1741 std::string texture = deSerializeLongString(is);
1742 bool vertical = false;
1744 vertical = readU8(is);
1748 event.type = CE_SPAWN_PARTICLE;
1749 event.spawn_particle.pos = new v3f (pos);
1750 event.spawn_particle.vel = new v3f (vel);
1751 event.spawn_particle.acc = new v3f (acc);
1752 event.spawn_particle.expirationtime = expirationtime;
1753 event.spawn_particle.size = size;
1754 event.spawn_particle.collisiondetection = collisiondetection;
1755 event.spawn_particle.vertical = vertical;
1756 event.spawn_particle.texture = new std::string(texture);
1758 m_client_event_queue.push_back(event);
1760 else if(command == TOCLIENT_ADD_PARTICLESPAWNER)
1762 std::string datastring((char*)&data[2], datasize-2);
1763 std::istringstream is(datastring, std::ios_base::binary);
1765 u16 amount = readU16(is);
1766 float spawntime = readF1000(is);
1767 v3f minpos = readV3F1000(is);
1768 v3f maxpos = readV3F1000(is);
1769 v3f minvel = readV3F1000(is);
1770 v3f maxvel = readV3F1000(is);
1771 v3f minacc = readV3F1000(is);
1772 v3f maxacc = readV3F1000(is);
1773 float minexptime = readF1000(is);
1774 float maxexptime = readF1000(is);
1775 float minsize = readF1000(is);
1776 float maxsize = readF1000(is);
1777 bool collisiondetection = readU8(is);
1778 std::string texture = deSerializeLongString(is);
1779 u32 id = readU32(is);
1780 bool vertical = false;
1782 vertical = readU8(is);
1786 event.type = CE_ADD_PARTICLESPAWNER;
1787 event.add_particlespawner.amount = amount;
1788 event.add_particlespawner.spawntime = spawntime;
1789 event.add_particlespawner.minpos = new v3f (minpos);
1790 event.add_particlespawner.maxpos = new v3f (maxpos);
1791 event.add_particlespawner.minvel = new v3f (minvel);
1792 event.add_particlespawner.maxvel = new v3f (maxvel);
1793 event.add_particlespawner.minacc = new v3f (minacc);
1794 event.add_particlespawner.maxacc = new v3f (maxacc);
1795 event.add_particlespawner.minexptime = minexptime;
1796 event.add_particlespawner.maxexptime = maxexptime;
1797 event.add_particlespawner.minsize = minsize;
1798 event.add_particlespawner.maxsize = maxsize;
1799 event.add_particlespawner.collisiondetection = collisiondetection;
1800 event.add_particlespawner.vertical = vertical;
1801 event.add_particlespawner.texture = new std::string(texture);
1802 event.add_particlespawner.id = id;
1804 m_client_event_queue.push_back(event);
1806 else if(command == TOCLIENT_DELETE_PARTICLESPAWNER)
1808 std::string datastring((char*)&data[2], datasize-2);
1809 std::istringstream is(datastring, std::ios_base::binary);
1811 u32 id = readU16(is);
1814 event.type = CE_DELETE_PARTICLESPAWNER;
1815 event.delete_particlespawner.id = id;
1817 m_client_event_queue.push_back(event);
1819 else if(command == TOCLIENT_HUDADD)
1821 std::string datastring((char *)&data[2], datasize - 2);
1822 std::istringstream is(datastring, std::ios_base::binary);
1824 u32 id = readU32(is);
1825 u8 type = readU8(is);
1826 v2f pos = readV2F1000(is);
1827 std::string name = deSerializeString(is);
1828 v2f scale = readV2F1000(is);
1829 std::string text = deSerializeString(is);
1830 u32 number = readU32(is);
1831 u32 item = readU32(is);
1832 u32 dir = readU32(is);
1833 v2f align = readV2F1000(is);
1834 v2f offset = readV2F1000(is);
1838 world_pos = readV3F1000(is);
1839 }catch(SerializationError &e) {};
1841 size = readV2S32(is);
1842 } catch(SerializationError &e) {};
1845 event.type = CE_HUDADD;
1846 event.hudadd.id = id;
1847 event.hudadd.type = type;
1848 event.hudadd.pos = new v2f(pos);
1849 event.hudadd.name = new std::string(name);
1850 event.hudadd.scale = new v2f(scale);
1851 event.hudadd.text = new std::string(text);
1852 event.hudadd.number = number;
1853 event.hudadd.item = item;
1854 event.hudadd.dir = dir;
1855 event.hudadd.align = new v2f(align);
1856 event.hudadd.offset = new v2f(offset);
1857 event.hudadd.world_pos = new v3f(world_pos);
1858 event.hudadd.size = new v2s32(size);
1859 m_client_event_queue.push_back(event);
1861 else if(command == TOCLIENT_HUDRM)
1863 std::string datastring((char *)&data[2], datasize - 2);
1864 std::istringstream is(datastring, std::ios_base::binary);
1866 u32 id = readU32(is);
1869 event.type = CE_HUDRM;
1870 event.hudrm.id = id;
1871 m_client_event_queue.push_back(event);
1873 else if(command == TOCLIENT_HUDCHANGE)
1881 std::string datastring((char *)&data[2], datasize - 2);
1882 std::istringstream is(datastring, std::ios_base::binary);
1884 u32 id = readU32(is);
1885 u8 stat = (HudElementStat)readU8(is);
1887 if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
1888 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
1889 v2fdata = readV2F1000(is);
1890 else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
1891 sdata = deSerializeString(is);
1892 else if (stat == HUD_STAT_WORLD_POS)
1893 v3fdata = readV3F1000(is);
1894 else if (stat == HUD_STAT_SIZE )
1895 v2s32data = readV2S32(is);
1897 intdata = readU32(is);
1900 event.type = CE_HUDCHANGE;
1901 event.hudchange.id = id;
1902 event.hudchange.stat = (HudElementStat)stat;
1903 event.hudchange.v2fdata = new v2f(v2fdata);
1904 event.hudchange.v3fdata = new v3f(v3fdata);
1905 event.hudchange.sdata = new std::string(sdata);
1906 event.hudchange.data = intdata;
1907 event.hudchange.v2s32data = new v2s32(v2s32data);
1908 m_client_event_queue.push_back(event);
1910 else if(command == TOCLIENT_HUD_SET_FLAGS)
1912 std::string datastring((char *)&data[2], datasize - 2);
1913 std::istringstream is(datastring, std::ios_base::binary);
1915 u32 flags = readU32(is);
1916 u32 mask = readU32(is);
1918 player->hud_flags &= ~mask;
1919 player->hud_flags |= flags;
1921 else if(command == TOCLIENT_HUD_SET_PARAM)
1923 std::string datastring((char *)&data[2], datasize - 2);
1924 std::istringstream is(datastring, std::ios_base::binary);
1926 u16 param = readU16(is);
1927 std::string value = deSerializeString(is);
1929 if(param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) {
1930 s32 hotbar_itemcount = readS32((u8*) value.c_str());
1931 if(hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
1932 player->hud_hotbar_itemcount = hotbar_itemcount;
1934 else if (param == HUD_PARAM_HOTBAR_IMAGE) {
1935 ((LocalPlayer *) player)->hotbar_image = value;
1937 else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
1938 ((LocalPlayer *) player)->hotbar_selected_image = value;
1941 else if(command == TOCLIENT_SET_SKY)
1943 std::string datastring((char *)&data[2], datasize - 2);
1944 std::istringstream is(datastring, std::ios_base::binary);
1946 video::SColor *bgcolor = new video::SColor(readARGB8(is));
1947 std::string *type = new std::string(deSerializeString(is));
1948 u16 count = readU16(is);
1949 std::vector<std::string> *params = new std::vector<std::string>;
1951 for(size_t i=0; i<count; i++)
1952 params->push_back(deSerializeString(is));
1955 event.type = CE_SET_SKY;
1956 event.set_sky.bgcolor = bgcolor;
1957 event.set_sky.type = type;
1958 event.set_sky.params = params;
1959 m_client_event_queue.push_back(event);
1961 else if(command == TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO)
1963 std::string datastring((char *)&data[2], datasize - 2);
1964 std::istringstream is(datastring, std::ios_base::binary);
1966 bool do_override = readU8(is);
1967 float day_night_ratio_f = (float)readU16(is) / 65536;
1970 event.type = CE_OVERRIDE_DAY_NIGHT_RATIO;
1971 event.override_day_night_ratio.do_override = do_override;
1972 event.override_day_night_ratio.ratio_f = day_night_ratio_f;
1973 m_client_event_queue.push_back(event);
1975 else if(command == TOCLIENT_LOCAL_PLAYER_ANIMATIONS)
1977 std::string datastring((char *)&data[2], datasize - 2);
1978 std::istringstream is(datastring, std::ios_base::binary);
1980 LocalPlayer *player = m_env.getLocalPlayer();
1981 assert(player != NULL);
1983 player->local_animations[0] = readV2S32(is);
1984 player->local_animations[1] = readV2S32(is);
1985 player->local_animations[2] = readV2S32(is);
1986 player->local_animations[3] = readV2S32(is);
1987 player->local_animation_speed = readF1000(is);
1989 else if(command == TOCLIENT_EYE_OFFSET)
1991 std::string datastring((char *)&data[2], datasize - 2);
1992 std::istringstream is(datastring, std::ios_base::binary);
1994 LocalPlayer *player = m_env.getLocalPlayer();
1995 assert(player != NULL);
1997 player->eye_offset_first = readV3F1000(is);
1998 player->eye_offset_third = readV3F1000(is);
2002 infostream<<"Client: Ignoring unknown command "
2003 <<command<<std::endl;
2007 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
2009 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2010 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
2013 void Client::interact(u8 action, const PointedThing& pointed)
2015 if(m_state != LC_Ready){
2016 infostream<<"Client::interact() "
2017 "cancelled (not connected)"
2022 std::ostringstream os(std::ios_base::binary);
2028 [5] u32 length of the next item
2029 [9] serialized PointedThing
2031 0: start digging (from undersurface) or use
2032 1: stop digging (all parameters ignored)
2033 2: digging completed
2034 3: place block or item (to abovesurface)
2037 writeU16(os, TOSERVER_INTERACT);
2038 writeU8(os, action);
2039 writeU16(os, getPlayerItem());
2040 std::ostringstream tmp_os(std::ios::binary);
2041 pointed.serialize(tmp_os);
2042 os<<serializeLongString(tmp_os.str());
2044 std::string s = os.str();
2045 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2048 Send(0, data, true);
2051 void Client::sendNodemetaFields(v3s16 p, const std::string &formname,
2052 const std::map<std::string, std::string> &fields)
2054 std::ostringstream os(std::ios_base::binary);
2056 writeU16(os, TOSERVER_NODEMETA_FIELDS);
2058 os<<serializeString(formname);
2059 size_t fields_size = fields.size();
2060 assert(fields_size <= 0xFFFF);
2061 writeU16(os, (u16) (fields_size & 0xFFFF));
2062 for(std::map<std::string, std::string>::const_iterator
2063 i = fields.begin(); i != fields.end(); i++){
2064 const std::string &name = i->first;
2065 const std::string &value = i->second;
2066 os<<serializeString(name);
2067 os<<serializeLongString(value);
2071 std::string s = os.str();
2072 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2074 Send(0, data, true);
2077 void Client::sendInventoryFields(const std::string &formname,
2078 const std::map<std::string, std::string> &fields)
2080 std::ostringstream os(std::ios_base::binary);
2082 writeU16(os, TOSERVER_INVENTORY_FIELDS);
2083 os<<serializeString(formname);
2084 size_t fields_size = fields.size();
2085 assert(fields_size <= 0xFFFF);
2086 writeU16(os, (u16) (fields_size & 0xFFFF));
2087 for(std::map<std::string, std::string>::const_iterator
2088 i = fields.begin(); i != fields.end(); i++){
2089 const std::string &name = i->first;
2090 const std::string &value = i->second;
2091 os<<serializeString(name);
2092 os<<serializeLongString(value);
2096 std::string s = os.str();
2097 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2099 Send(0, data, true);
2102 void Client::sendInventoryAction(InventoryAction *a)
2104 std::ostringstream os(std::ios_base::binary);
2108 writeU16(buf, TOSERVER_INVENTORY_ACTION);
2109 os.write((char*)buf, 2);
2114 std::string s = os.str();
2115 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2117 Send(0, data, true);
2120 void Client::sendChatMessage(const std::wstring &message)
2122 std::ostringstream os(std::ios_base::binary);
2126 writeU16(buf, TOSERVER_CHAT_MESSAGE);
2127 os.write((char*)buf, 2);
2130 size_t messagesize = message.size();
2131 if (messagesize > 0xFFFF) {
2132 messagesize = 0xFFFF;
2134 writeU16(buf, (u16) messagesize);
2135 os.write((char*)buf, 2);
2138 for(unsigned int i=0; i<message.size(); i++)
2142 os.write((char*)buf, 2);
2146 std::string s = os.str();
2147 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2149 Send(0, data, true);
2152 void Client::sendChangePassword(const std::wstring &oldpassword,
2153 const std::wstring &newpassword)
2155 Player *player = m_env.getLocalPlayer();
2159 std::string playername = player->getName();
2160 std::string oldpwd = translatePassword(playername, oldpassword);
2161 std::string newpwd = translatePassword(playername, newpassword);
2163 std::ostringstream os(std::ios_base::binary);
2164 u8 buf[2+PASSWORD_SIZE*2];
2166 [0] u16 TOSERVER_PASSWORD
2167 [2] u8[28] old password
2168 [30] u8[28] new password
2171 writeU16(buf, TOSERVER_PASSWORD);
2172 for(unsigned int i=0;i<PASSWORD_SIZE-1;i++)
2174 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
2175 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
2177 buf[2+PASSWORD_SIZE-1] = 0;
2178 buf[30+PASSWORD_SIZE-1] = 0;
2179 os.write((char*)buf, 2+PASSWORD_SIZE*2);
2182 std::string s = os.str();
2183 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2185 Send(0, data, true);
2189 void Client::sendDamage(u8 damage)
2191 DSTACK(__FUNCTION_NAME);
2192 std::ostringstream os(std::ios_base::binary);
2194 writeU16(os, TOSERVER_DAMAGE);
2195 writeU8(os, damage);
2198 std::string s = os.str();
2199 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2201 Send(0, data, true);
2204 void Client::sendBreath(u16 breath)
2206 DSTACK(__FUNCTION_NAME);
2207 std::ostringstream os(std::ios_base::binary);
2209 writeU16(os, TOSERVER_BREATH);
2210 writeU16(os, breath);
2212 std::string s = os.str();
2213 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2215 Send(0, data, true);
2218 void Client::sendRespawn()
2220 DSTACK(__FUNCTION_NAME);
2221 std::ostringstream os(std::ios_base::binary);
2223 writeU16(os, TOSERVER_RESPAWN);
2226 std::string s = os.str();
2227 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2229 Send(0, data, true);
2232 void Client::sendReady()
2234 DSTACK(__FUNCTION_NAME);
2235 std::ostringstream os(std::ios_base::binary);
2237 writeU16(os, TOSERVER_CLIENT_READY);
2238 writeU8(os,VERSION_MAJOR);
2239 writeU8(os,VERSION_MINOR);
2240 writeU8(os,VERSION_PATCH_ORIG);
2243 writeU16(os,strlen(minetest_version_hash));
2244 os.write(minetest_version_hash,strlen(minetest_version_hash));
2247 std::string s = os.str();
2248 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2250 Send(0, data, true);
2253 void Client::sendPlayerPos()
2255 LocalPlayer *myplayer = m_env.getLocalPlayer();
2256 if(myplayer == NULL)
2259 // Save bandwidth by only updating position when something changed
2260 if(myplayer->last_position == myplayer->getPosition() &&
2261 myplayer->last_speed == myplayer->getSpeed() &&
2262 myplayer->last_pitch == myplayer->getPitch() &&
2263 myplayer->last_yaw == myplayer->getYaw() &&
2264 myplayer->last_keyPressed == myplayer->keyPressed)
2267 myplayer->last_position = myplayer->getPosition();
2268 myplayer->last_speed = myplayer->getSpeed();
2269 myplayer->last_pitch = myplayer->getPitch();
2270 myplayer->last_yaw = myplayer->getYaw();
2271 myplayer->last_keyPressed = myplayer->keyPressed;
2275 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2276 our_peer_id = m_con.GetPeerID();
2279 // Set peer id if not set already
2280 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2281 myplayer->peer_id = our_peer_id;
2282 // Check that an existing peer_id is the same as the connection's
2283 assert(myplayer->peer_id == our_peer_id);
2285 v3f pf = myplayer->getPosition();
2286 v3f sf = myplayer->getSpeed();
2287 s32 pitch = myplayer->getPitch() * 100;
2288 s32 yaw = myplayer->getYaw() * 100;
2289 u32 keyPressed = myplayer->keyPressed;
2291 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
2292 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
2296 [2] v3s32 position*100
2297 [2+12] v3s32 speed*100
2298 [2+12+12] s32 pitch*100
2299 [2+12+12+4] s32 yaw*100
2300 [2+12+12+4+4] u32 keyPressed
2302 SharedBuffer<u8> data(2+12+12+4+4+4);
2303 writeU16(&data[0], TOSERVER_PLAYERPOS);
2304 writeV3S32(&data[2], position);
2305 writeV3S32(&data[2+12], speed);
2306 writeS32(&data[2+12+12], pitch);
2307 writeS32(&data[2+12+12+4], yaw);
2308 writeU32(&data[2+12+12+4+4], keyPressed);
2309 // Send as unreliable
2310 Send(0, data, false);
2313 void Client::sendPlayerItem(u16 item)
2315 Player *myplayer = m_env.getLocalPlayer();
2316 if(myplayer == NULL)
2319 u16 our_peer_id = m_con.GetPeerID();
2321 // Set peer id if not set already
2322 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2323 myplayer->peer_id = our_peer_id;
2324 // Check that an existing peer_id is the same as the connection's
2325 assert(myplayer->peer_id == our_peer_id);
2327 SharedBuffer<u8> data(2+2);
2328 writeU16(&data[0], TOSERVER_PLAYERITEM);
2329 writeU16(&data[2], item);
2332 Send(0, data, true);
2335 void Client::removeNode(v3s16 p)
2337 std::map<v3s16, MapBlock*> modified_blocks;
2341 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
2343 catch(InvalidPositionException &e)
2347 for(std::map<v3s16, MapBlock * >::iterator
2348 i = modified_blocks.begin();
2349 i != modified_blocks.end(); ++i)
2351 addUpdateMeshTaskWithEdge(i->first, false, true);
2355 void Client::addNode(v3s16 p, MapNode n, bool remove_metadata)
2357 //TimeTaker timer1("Client::addNode()");
2359 std::map<v3s16, MapBlock*> modified_blocks;
2363 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
2364 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
2366 catch(InvalidPositionException &e)
2369 for(std::map<v3s16, MapBlock * >::iterator
2370 i = modified_blocks.begin();
2371 i != modified_blocks.end(); ++i)
2373 addUpdateMeshTaskWithEdge(i->first, false, true);
2377 void Client::setPlayerControl(PlayerControl &control)
2379 LocalPlayer *player = m_env.getLocalPlayer();
2380 assert(player != NULL);
2381 player->control = control;
2384 void Client::selectPlayerItem(u16 item)
2386 m_playeritem = item;
2387 m_inventory_updated = true;
2388 sendPlayerItem(item);
2391 // Returns true if the inventory of the local player has been
2392 // updated from the server. If it is true, it is set to false.
2393 bool Client::getLocalInventoryUpdated()
2395 bool updated = m_inventory_updated;
2396 m_inventory_updated = false;
2400 // Copies the inventory of the local player to parameter
2401 void Client::getLocalInventory(Inventory &dst)
2403 Player *player = m_env.getLocalPlayer();
2404 assert(player != NULL);
2405 dst = player->inventory;
2408 Inventory* Client::getInventory(const InventoryLocation &loc)
2411 case InventoryLocation::UNDEFINED:
2414 case InventoryLocation::CURRENT_PLAYER:
2416 Player *player = m_env.getLocalPlayer();
2417 assert(player != NULL);
2418 return &player->inventory;
2421 case InventoryLocation::PLAYER:
2423 Player *player = m_env.getPlayer(loc.name.c_str());
2426 return &player->inventory;
2429 case InventoryLocation::NODEMETA:
2431 NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p);
2434 return meta->getInventory();
2437 case InventoryLocation::DETACHED:
2439 if(m_detached_inventories.count(loc.name) == 0)
2441 return m_detached_inventories[loc.name];
2450 void Client::inventoryAction(InventoryAction *a)
2453 Send it to the server
2455 sendInventoryAction(a);
2458 Predict some local inventory changes
2460 a->clientApply(this, this);
2466 ClientActiveObject * Client::getSelectedActiveObject(
2468 v3f from_pos_f_on_map,
2469 core::line3d<f32> shootline_on_map
2472 std::vector<DistanceSortedActiveObject> objects;
2474 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
2477 // After this, the closest object is the first in the array.
2478 std::sort(objects.begin(), objects.end());
2480 for(unsigned int i=0; i<objects.size(); i++)
2482 ClientActiveObject *obj = objects[i].obj;
2484 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
2485 if(selection_box == NULL)
2488 v3f pos = obj->getPosition();
2490 core::aabbox3d<f32> offsetted_box(
2491 selection_box->MinEdge + pos,
2492 selection_box->MaxEdge + pos
2495 if(offsetted_box.intersectsWithLine(shootline_on_map))
2504 std::list<std::string> Client::getConnectedPlayerNames()
2506 return m_env.getPlayerNames();
2509 float Client::getAnimationTime()
2511 return m_animation_time;
2514 int Client::getCrackLevel()
2516 return m_crack_level;
2519 void Client::setHighlighted(v3s16 pos, bool show_highlighted)
2521 m_show_highlighted = show_highlighted;
2522 v3s16 old_highlighted_pos = m_highlighted_pos;
2523 m_highlighted_pos = pos;
2524 addUpdateMeshTaskForNode(old_highlighted_pos, false, true);
2525 addUpdateMeshTaskForNode(m_highlighted_pos, false, true);
2528 void Client::setCrack(int level, v3s16 pos)
2530 int old_crack_level = m_crack_level;
2531 v3s16 old_crack_pos = m_crack_pos;
2533 m_crack_level = level;
2536 if(old_crack_level >= 0 && (level < 0 || pos != old_crack_pos))
2539 addUpdateMeshTaskForNode(old_crack_pos, false, true);
2541 if(level >= 0 && (old_crack_level < 0 || pos != old_crack_pos))
2544 addUpdateMeshTaskForNode(pos, false, true);
2550 Player *player = m_env.getLocalPlayer();
2551 assert(player != NULL);
2555 u16 Client::getBreath()
2557 Player *player = m_env.getLocalPlayer();
2558 assert(player != NULL);
2559 return player->getBreath();
2562 bool Client::getChatMessage(std::wstring &message)
2564 if(m_chat_queue.size() == 0)
2566 message = m_chat_queue.pop_front();
2570 void Client::typeChatMessage(const std::wstring &message)
2572 // Discard empty line
2577 sendChatMessage(message);
2580 if (message[0] == L'/')
2582 m_chat_queue.push_back((std::wstring)L"issued command: " + message);
2586 LocalPlayer *player = m_env.getLocalPlayer();
2587 assert(player != NULL);
2588 std::wstring name = narrow_to_wide(player->getName());
2589 m_chat_queue.push_back((std::wstring)L"<" + name + L"> " + message);
2593 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
2595 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2600 Create a task to update the mesh of the block
2603 MeshMakeData *data = new MeshMakeData(this);
2606 //TimeTaker timer("data fill");
2608 // Debug: 1-6ms, avg=2ms
2610 data->setCrack(m_crack_level, m_crack_pos);
2611 data->setHighlighted(m_highlighted_pos, m_show_highlighted);
2612 data->setSmoothLighting(m_cache_smooth_lighting);
2615 // Add task to queue
2616 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server, urgent);
2619 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent)
2622 addUpdateMeshTask(blockpos, ack_to_server, urgent);
2624 catch(InvalidPositionException &e){}
2627 for (int i=0;i<6;i++)
2630 v3s16 p = blockpos + g_6dirs[i];
2631 addUpdateMeshTask(p, false, urgent);
2633 catch(InvalidPositionException &e){}
2637 void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool urgent)
2641 infostream<<"Client::addUpdateMeshTaskForNode(): "
2642 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2646 v3s16 blockpos = getNodeBlockPos(nodepos);
2647 v3s16 blockpos_relative = blockpos * MAP_BLOCKSIZE;
2650 addUpdateMeshTask(blockpos, 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, 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, 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::ostringstream 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.str() << 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);