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 "clientserver.h"
24 #include "jthread/jmutexautolock.h"
29 #include "mapsector.h"
30 #include "mapblock_mesh.h"
36 #include "nodemetadata.h"
40 #include <IFileSystem.h>
42 #include "clientmap.h"
43 #include "clientmedia.h"
45 #include "util/string.h"
46 #include "IMeshCache.h"
47 #include "serialization.h"
48 #include "util/serialize.h"
50 #include "util/directiontables.h"
51 #include "util/pointedthing.h"
58 QueuedMeshUpdate::QueuedMeshUpdate():
61 ack_block_to_server(false)
65 QueuedMeshUpdate::~QueuedMeshUpdate()
75 MeshUpdateQueue::MeshUpdateQueue()
79 MeshUpdateQueue::~MeshUpdateQueue()
81 JMutexAutoLock lock(m_mutex);
83 for(std::vector<QueuedMeshUpdate*>::iterator
85 i != m_queue.end(); i++)
87 QueuedMeshUpdate *q = *i;
93 peer_id=0 adds with nobody to send to
95 void MeshUpdateQueue::addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_server, bool urgent)
97 DSTACK(__FUNCTION_NAME);
101 JMutexAutoLock lock(m_mutex);
107 Find if block is already in queue.
108 If it is, update the data and quit.
110 for(std::vector<QueuedMeshUpdate*>::iterator
112 i != m_queue.end(); i++)
114 QueuedMeshUpdate *q = *i;
120 if(ack_block_to_server)
121 q->ack_block_to_server = true;
129 QueuedMeshUpdate *q = new QueuedMeshUpdate;
132 q->ack_block_to_server = ack_block_to_server;
133 m_queue.push_back(q);
136 // Returned pointer must be deleted
137 // Returns NULL if queue is empty
138 QueuedMeshUpdate * MeshUpdateQueue::pop()
140 JMutexAutoLock lock(m_mutex);
142 bool must_be_urgent = !m_urgents.empty();
143 for(std::vector<QueuedMeshUpdate*>::iterator
145 i != m_queue.end(); i++)
147 QueuedMeshUpdate *q = *i;
148 if(must_be_urgent && m_urgents.count(q->p) == 0)
151 m_urgents.erase(q->p);
161 void * MeshUpdateThread::Thread()
165 log_register_thread("MeshUpdateThread");
167 DSTACK(__FUNCTION_NAME);
169 BEGIN_DEBUG_EXCEPTION_HANDLER
171 while(!StopRequested())
173 /*// Wait for output queue to flush.
174 // Allow 2 in queue, this makes less frametime jitter.
175 // Umm actually, there is no much difference
176 if(m_queue_out.size() >= 2)
182 QueuedMeshUpdate *q = m_queue_in.pop();
189 ScopeProfiler sp(g_profiler, "Client: Mesh making");
191 MapBlockMesh *mesh_new = new MapBlockMesh(q->data);
192 if(mesh_new->getMesh()->getMeshBufferCount() == 0)
201 r.ack_block_to_server = q->ack_block_to_server;
203 /*infostream<<"MeshUpdateThread: Processed "
204 <<"("<<q->p.X<<","<<q->p.Y<<","<<q->p.Z<<")"
207 m_queue_out.push_back(r);
212 END_DEBUG_EXCEPTION_HANDLER(errorstream)
222 IrrlichtDevice *device,
223 const char *playername,
224 std::string password,
225 MapDrawControl &control,
226 IWritableTextureSource *tsrc,
227 IWritableShaderSource *shsrc,
228 IWritableItemDefManager *itemdef,
229 IWritableNodeDefManager *nodedef,
230 ISoundManager *sound,
231 MtEventManager *event,
240 m_mesh_update_thread(this),
242 new ClientMap(this, this, control,
243 device->getSceneManager()->getRootSceneNode(),
244 device->getSceneManager(), 666),
245 device->getSceneManager(),
248 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, ipv6, this),
250 m_server_ser_ver(SER_FMT_VER_INVALID),
252 m_inventory_updated(false),
253 m_inventory_from_server(NULL),
254 m_inventory_from_server_age(0.0),
259 m_password(password),
260 m_access_denied(false),
261 m_itemdef_received(false),
262 m_nodedef_received(false),
263 m_media_downloader(new ClientMediaDownloader()),
264 m_time_of_day_set(false),
265 m_last_time_of_day_f(-1),
266 m_time_of_day_update_timer(0),
267 m_recommended_send_interval(0.1),
268 m_removed_sounds_check_timer(0)
270 m_packetcounter_timer = 0.0;
271 //m_delete_unused_sectors_timer = 0.0;
272 m_connection_reinit_timer = 0.0;
273 m_avg_rtt_timer = 0.0;
274 m_playerpos_send_timer = 0.0;
275 m_ignore_damage_timer = 0.0;
281 Player *player = new LocalPlayer(this);
283 player->updateName(playername);
285 m_env.addPlayer(player);
292 //JMutexAutoLock conlock(m_con_mutex); //bulk comment-out
296 m_mesh_update_thread.Stop();
297 m_mesh_update_thread.Wait();
298 while(!m_mesh_update_thread.m_queue_out.empty()) {
299 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_front();
304 delete m_inventory_from_server;
306 // Delete detached inventories
308 for(std::map<std::string, Inventory*>::iterator
309 i = m_detached_inventories.begin();
310 i != m_detached_inventories.end(); i++){
315 // cleanup 3d model meshes on client shutdown
316 while (m_device->getSceneManager()->getMeshCache()->getMeshCount() != 0) {
317 scene::IAnimatedMesh * mesh =
318 m_device->getSceneManager()->getMeshCache()->getMeshByIndex(0);
321 m_device->getSceneManager()->getMeshCache()->removeMesh(mesh);
325 void Client::connect(Address address)
327 DSTACK(__FUNCTION_NAME);
328 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
329 m_con.SetTimeoutMs(0);
330 m_con.Connect(address);
333 bool Client::connectedAndInitialized()
335 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
337 if(m_con.Connected() == false)
340 if(m_server_ser_ver == SER_FMT_VER_INVALID)
346 void Client::step(float dtime)
348 DSTACK(__FUNCTION_NAME);
354 if(m_ignore_damage_timer > dtime)
355 m_ignore_damage_timer -= dtime;
357 m_ignore_damage_timer = 0.0;
359 m_animation_time += dtime;
360 if(m_animation_time > 60.0)
361 m_animation_time -= 60.0;
363 m_time_of_day_update_timer += dtime;
365 //infostream<<"Client steps "<<dtime<<std::endl;
368 //TimeTaker timer("ReceiveAll()", m_device);
374 //TimeTaker timer("m_con_mutex + m_con.RunTimeouts()", m_device);
376 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
377 m_con.RunTimeouts(dtime);
384 float &counter = m_packetcounter_timer;
390 infostream<<"Client packetcounter (20s):"<<std::endl;
391 m_packetcounter.print(infostream);
392 m_packetcounter.clear();
396 // Get connection status
397 bool connected = connectedAndInitialized();
402 Delete unused sectors
404 NOTE: This jams the game for a while because deleting sectors
408 float &counter = m_delete_unused_sectors_timer;
416 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
418 core::list<v3s16> deleted_blocks;
420 float delete_unused_sectors_timeout =
421 g_settings->getFloat("client_delete_unused_sectors_timeout");
423 // Delete sector blocks
424 /*u32 num = m_env.getMap().unloadUnusedData
425 (delete_unused_sectors_timeout,
426 true, &deleted_blocks);*/
428 // Delete whole sectors
429 m_env.getMap().unloadUnusedData
430 (delete_unused_sectors_timeout,
433 if(deleted_blocks.size() > 0)
435 /*infostream<<"Client: Deleted blocks of "<<num
436 <<" unused sectors"<<std::endl;*/
437 /*infostream<<"Client: Deleted "<<num
438 <<" unused sectors"<<std::endl;*/
444 // Env is locked so con can be locked.
445 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
447 core::list<v3s16>::Iterator i = deleted_blocks.begin();
448 core::list<v3s16> sendlist;
451 if(sendlist.size() == 255 || i == deleted_blocks.end())
453 if(sendlist.size() == 0)
462 u32 replysize = 2+1+6*sendlist.size();
463 SharedBuffer<u8> reply(replysize);
464 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
465 reply[2] = sendlist.size();
467 for(core::list<v3s16>::Iterator
468 j = sendlist.begin();
469 j != sendlist.end(); j++)
471 writeV3S16(&reply[2+1+6*k], *j);
474 m_con.Send(PEER_ID_SERVER, 1, reply, true);
476 if(i == deleted_blocks.end())
482 sendlist.push_back(*i);
490 if(connected == false)
492 float &counter = m_connection_reinit_timer;
498 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
500 Player *myplayer = m_env.getLocalPlayer();
501 assert(myplayer != NULL);
503 // Send TOSERVER_INIT
504 // [0] u16 TOSERVER_INIT
505 // [2] u8 SER_FMT_VER_HIGHEST_READ
506 // [3] u8[20] player_name
507 // [23] u8[28] password (new in some version)
508 // [51] u16 minimum supported network protocol version (added sometime)
509 // [53] u16 maximum supported network protocol version (added later than the previous one)
510 SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2+2);
511 writeU16(&data[0], TOSERVER_INIT);
512 writeU8(&data[2], SER_FMT_VER_HIGHEST_READ);
514 memset((char*)&data[3], 0, PLAYERNAME_SIZE);
515 snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
517 /*infostream<<"Client: sending initial password hash: \""<<m_password<<"\""
520 memset((char*)&data[23], 0, PASSWORD_SIZE);
521 snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
523 writeU16(&data[51], CLIENT_PROTOCOL_VERSION_MIN);
524 writeU16(&data[53], CLIENT_PROTOCOL_VERSION_MAX);
526 // Send as unreliable
527 Send(0, data, false);
530 // Not connected, return
535 Do stuff if connected
539 Run Map's timers and unload unused data
541 const float map_timer_and_unload_dtime = 5.25;
542 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
544 ScopeProfiler sp(g_profiler, "Client: map timer and unload");
545 std::list<v3s16> deleted_blocks;
546 m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
547 g_settings->getFloat("client_unload_unused_data_timeout"),
550 /*if(deleted_blocks.size() > 0)
551 infostream<<"Client: Unloaded "<<deleted_blocks.size()
552 <<" unused blocks"<<std::endl;*/
556 NOTE: This loop is intentionally iterated the way it is.
559 std::list<v3s16>::iterator i = deleted_blocks.begin();
560 std::list<v3s16> sendlist;
563 if(sendlist.size() == 255 || i == deleted_blocks.end())
565 if(sendlist.size() == 0)
574 u32 replysize = 2+1+6*sendlist.size();
575 SharedBuffer<u8> reply(replysize);
576 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
577 reply[2] = sendlist.size();
579 for(std::list<v3s16>::iterator
580 j = sendlist.begin();
581 j != sendlist.end(); ++j)
583 writeV3S16(&reply[2+1+6*k], *j);
586 m_con.Send(PEER_ID_SERVER, 1, reply, true);
588 if(i == deleted_blocks.end())
594 sendlist.push_back(*i);
604 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
606 // Control local player (0ms)
607 LocalPlayer *player = m_env.getLocalPlayer();
608 assert(player != NULL);
609 player->applyControl(dtime);
611 //TimeTaker envtimer("env step", m_device);
620 ClientEnvEvent event = m_env.getClientEvent();
621 if(event.type == CEE_NONE)
625 else if(event.type == CEE_PLAYER_DAMAGE)
627 if(m_ignore_damage_timer <= 0)
629 u8 damage = event.player_damage.amount;
631 if(event.player_damage.send_to_server)
634 // Add to ClientEvent queue
636 event.type = CE_PLAYER_DAMAGE;
637 event.player_damage.amount = damage;
638 m_client_event_queue.push_back(event);
641 else if(event.type == CEE_PLAYER_BREATH)
643 u16 breath = event.player_breath.amount;
653 float &counter = m_avg_rtt_timer;
658 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
659 // connectedAndInitialized() is true, peer exists.
660 float avg_rtt = m_con.GetPeerAvgRTT(PEER_ID_SERVER);
661 infostream<<"Client: avg_rtt="<<avg_rtt<<std::endl;
666 Send player position to server
669 float &counter = m_playerpos_send_timer;
671 if(counter >= m_recommended_send_interval)
679 Replace updated meshes
682 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
684 //TimeTaker timer("** Processing mesh update result queue");
687 /*infostream<<"Mesh update result queue size is "
688 <<m_mesh_update_thread.m_queue_out.size()
691 int num_processed_meshes = 0;
692 while(!m_mesh_update_thread.m_queue_out.empty())
694 num_processed_meshes++;
695 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_front();
696 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
699 //JMutexAutoLock lock(block->mesh_mutex);
701 // Delete the old mesh
702 if(block->mesh != NULL)
704 // TODO: Remove hardware buffers of meshbuffers of block->mesh
709 // Replace with the new mesh
710 block->mesh = r.mesh;
714 if(r.ack_block_to_server)
716 /*infostream<<"Client: ACK block ("<<r.p.X<<","<<r.p.Y
717 <<","<<r.p.Z<<")"<<std::endl;*/
728 u32 replysize = 2+1+6;
729 SharedBuffer<u8> reply(replysize);
730 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
732 writeV3S16(&reply[3], r.p);
734 m_con.Send(PEER_ID_SERVER, 1, reply, true);
737 if(num_processed_meshes > 0)
738 g_profiler->graphAdd("num_processed_meshes", num_processed_meshes);
744 if (m_media_downloader && m_media_downloader->isStarted()) {
745 m_media_downloader->step(this);
746 if (m_media_downloader->isDone()) {
747 delete m_media_downloader;
748 m_media_downloader = NULL;
753 If the server didn't update the inventory in a while, revert
754 the local inventory (so the player notices the lag problem
755 and knows something is wrong).
757 if(m_inventory_from_server)
759 float interval = 10.0;
760 float count_before = floor(m_inventory_from_server_age / interval);
762 m_inventory_from_server_age += dtime;
764 float count_after = floor(m_inventory_from_server_age / interval);
766 if(count_after != count_before)
768 // Do this every <interval> seconds after TOCLIENT_INVENTORY
769 // Reset the locally changed inventory to the authoritative inventory
770 Player *player = m_env.getLocalPlayer();
771 player->inventory = *m_inventory_from_server;
772 m_inventory_updated = true;
777 Update positions of sounds attached to objects
780 for(std::map<int, u16>::iterator
781 i = m_sounds_to_objects.begin();
782 i != m_sounds_to_objects.end(); i++)
784 int client_id = i->first;
785 u16 object_id = i->second;
786 ClientActiveObject *cao = m_env.getActiveObject(object_id);
789 v3f pos = cao->getPosition();
790 m_sound->updateSoundPosition(client_id, pos);
795 Handle removed remotely initiated sounds
797 m_removed_sounds_check_timer += dtime;
798 if(m_removed_sounds_check_timer >= 2.32)
800 m_removed_sounds_check_timer = 0;
801 // Find removed sounds and clear references to them
802 std::set<s32> removed_server_ids;
803 for(std::map<s32, int>::iterator
804 i = m_sounds_server_to_client.begin();
805 i != m_sounds_server_to_client.end();)
807 s32 server_id = i->first;
808 int client_id = i->second;
810 if(!m_sound->soundExists(client_id)){
811 m_sounds_server_to_client.erase(server_id);
812 m_sounds_client_to_server.erase(client_id);
813 m_sounds_to_objects.erase(client_id);
814 removed_server_ids.insert(server_id);
818 if(removed_server_ids.size() != 0)
820 std::ostringstream os(std::ios_base::binary);
821 writeU16(os, TOSERVER_REMOVED_SOUNDS);
822 writeU16(os, removed_server_ids.size());
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 writeU16(os, file_requests.size());
937 for(std::list<std::string>::const_iterator i = file_requests.begin();
938 i != file_requests.end(); ++i) {
939 os<<serializeString(*i);
943 std::string s = os.str();
944 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
947 infostream<<"Client: Sending media request list to server ("
948 <<file_requests.size()<<" files)"<<std::endl;
951 void Client::received_media()
953 // notify server we received everything
954 std::ostringstream os(std::ios_base::binary);
955 writeU16(os, TOSERVER_RECEIVED_MEDIA);
956 std::string s = os.str();
957 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
960 infostream<<"Client: Notifying server that we received all media"
964 void Client::ReceiveAll()
966 DSTACK(__FUNCTION_NAME);
967 u32 start_ms = porting::getTimeMs();
970 // Limit time even if there would be huge amounts of data to
972 if(porting::getTimeMs() > start_ms + 100)
977 g_profiler->graphAdd("client_received_packets", 1);
979 catch(con::NoIncomingDataException &e)
983 catch(con::InvalidIncomingDataException &e)
985 infostream<<"Client::ReceiveAll(): "
986 "InvalidIncomingDataException: what()="
987 <<e.what()<<std::endl;
992 void Client::Receive()
994 DSTACK(__FUNCTION_NAME);
995 SharedBuffer<u8> data;
999 //TimeTaker t1("con mutex and receive", m_device);
1000 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1001 datasize = m_con.Receive(sender_peer_id, data);
1003 //TimeTaker t1("ProcessData", m_device);
1004 ProcessData(*data, datasize, sender_peer_id);
1008 sender_peer_id given to this shall be quaranteed to be a valid peer
1010 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
1012 DSTACK(__FUNCTION_NAME);
1014 // Ignore packets that don't even fit a command
1017 m_packetcounter.add(60000);
1021 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
1023 //infostream<<"Client: received command="<<command<<std::endl;
1024 m_packetcounter.add((u16)command);
1027 If this check is removed, be sure to change the queue
1028 system to know the ids
1030 if(sender_peer_id != PEER_ID_SERVER)
1032 infostream<<"Client::ProcessData(): Discarding data not "
1033 "coming from server: peer_id="<<sender_peer_id
1038 u8 ser_version = m_server_ser_ver;
1040 //infostream<<"Client received command="<<(int)command<<std::endl;
1042 if(command == TOCLIENT_INIT)
1047 u8 deployed = data[2];
1049 infostream<<"Client: TOCLIENT_INIT received with "
1050 "deployed="<<((int)deployed&0xff)<<std::endl;
1052 if(!ser_ver_supported(deployed))
1054 infostream<<"Client: TOCLIENT_INIT: Server sent "
1055 <<"unsupported ser_fmt_ver"<<std::endl;
1059 m_server_ser_ver = deployed;
1061 // Get player position
1062 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
1063 if(datasize >= 2+1+6)
1064 playerpos_s16 = readV3S16(&data[2+1]);
1065 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
1068 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1070 // Set player position
1071 Player *player = m_env.getLocalPlayer();
1072 assert(player != NULL);
1073 player->setPosition(playerpos_f);
1076 if(datasize >= 2+1+6+8)
1079 m_map_seed = readU64(&data[2+1+6]);
1080 infostream<<"Client: received map seed: "<<m_map_seed<<std::endl;
1083 if(datasize >= 2+1+6+8+4)
1086 m_recommended_send_interval = readF1000(&data[2+1+6+8]);
1087 infostream<<"Client: received recommended send interval "
1088 <<m_recommended_send_interval<<std::endl;
1093 SharedBuffer<u8> reply(replysize);
1094 writeU16(&reply[0], TOSERVER_INIT2);
1096 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1101 if(command == TOCLIENT_ACCESS_DENIED)
1103 // The server didn't like our password. Note, this needs
1104 // to be processed even if the serialisation format has
1105 // not been agreed yet, the same as TOCLIENT_INIT.
1106 m_access_denied = true;
1107 m_access_denied_reason = L"Unknown";
1110 std::string datastring((char*)&data[2], datasize-2);
1111 std::istringstream is(datastring, std::ios_base::binary);
1112 m_access_denied_reason = deSerializeWideString(is);
1117 if(ser_version == SER_FMT_VER_INVALID)
1119 infostream<<"Client: Server serialization"
1120 " format invalid or not initialized."
1121 " Skipping incoming command="<<command<<std::endl;
1125 // Just here to avoid putting the two if's together when
1126 // making some copypasta
1129 if(command == TOCLIENT_REMOVENODE)
1134 p.X = readS16(&data[2]);
1135 p.Y = readS16(&data[4]);
1136 p.Z = readS16(&data[6]);
1138 //TimeTaker t1("TOCLIENT_REMOVENODE");
1142 else if(command == TOCLIENT_ADDNODE)
1144 if(datasize < 8 + MapNode::serializedLength(ser_version))
1148 p.X = readS16(&data[2]);
1149 p.Y = readS16(&data[4]);
1150 p.Z = readS16(&data[6]);
1152 //TimeTaker t1("TOCLIENT_ADDNODE");
1155 n.deSerialize(&data[8], ser_version);
1157 bool remove_metadata = true;
1158 u32 index = 8 + MapNode::serializedLength(ser_version);
1159 if ((datasize >= index+1) && data[index]){
1160 remove_metadata = false;
1163 addNode(p, n, remove_metadata);
1165 else if(command == TOCLIENT_BLOCKDATA)
1167 // Ignore too small packet
1172 p.X = readS16(&data[2]);
1173 p.Y = readS16(&data[4]);
1174 p.Z = readS16(&data[6]);
1176 /*infostream<<"Client: Thread: BLOCKDATA for ("
1177 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1178 /*infostream<<"Client: Thread: BLOCKDATA for ("
1179 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1181 std::string datastring((char*)&data[8], datasize-8);
1182 std::istringstream istr(datastring, std::ios_base::binary);
1187 v2s16 p2d(p.X, p.Z);
1188 sector = m_env.getMap().emergeSector(p2d);
1190 assert(sector->getPos() == p2d);
1192 //TimeTaker timer("MapBlock deSerialize");
1195 block = sector->getBlockNoCreateNoEx(p.Y);
1199 Update an existing block
1201 //infostream<<"Updating"<<std::endl;
1202 block->deSerialize(istr, ser_version, false);
1203 block->deSerializeNetworkSpecific(istr);
1210 //infostream<<"Creating new"<<std::endl;
1211 block = new MapBlock(&m_env.getMap(), p, this);
1212 block->deSerialize(istr, ser_version, false);
1213 block->deSerializeNetworkSpecific(istr);
1214 sector->insertBlock(block);
1228 u32 replysize = 2+1+6;
1229 SharedBuffer<u8> reply(replysize);
1230 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
1232 writeV3S16(&reply[3], p);
1234 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1238 Add it to mesh update queue and set it to be acknowledged after update.
1240 //infostream<<"Adding mesh update task for received block"<<std::endl;
1241 addUpdateMeshTaskWithEdge(p, true);
1243 else if(command == TOCLIENT_INVENTORY)
1248 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
1251 //TimeTaker t2("mutex locking", m_device);
1252 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1255 //TimeTaker t3("istringstream init", m_device);
1256 std::string datastring((char*)&data[2], datasize-2);
1257 std::istringstream is(datastring, std::ios_base::binary);
1260 //TimeTaker t4("player get", m_device);
1261 Player *player = m_env.getLocalPlayer();
1262 assert(player != NULL);
1265 //TimeTaker t1("inventory.deSerialize()", m_device);
1266 player->inventory.deSerialize(is);
1269 m_inventory_updated = true;
1271 delete m_inventory_from_server;
1272 m_inventory_from_server = new Inventory(player->inventory);
1273 m_inventory_from_server_age = 0.0;
1275 //infostream<<"Client got player inventory:"<<std::endl;
1276 //player->inventory.print(infostream);
1279 else if(command == TOCLIENT_TIME_OF_DAY)
1284 u16 time_of_day = readU16(&data[2]);
1285 time_of_day = time_of_day % 24000;
1286 //infostream<<"Client: time_of_day="<<time_of_day<<std::endl;
1287 float time_speed = 0;
1288 if(datasize >= 2 + 2 + 4){
1289 time_speed = readF1000(&data[4]);
1291 // Old message; try to approximate speed of time by ourselves
1292 float time_of_day_f = (float)time_of_day / 24000.0;
1293 float tod_diff_f = 0;
1294 if(time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
1295 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0;
1297 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
1298 m_last_time_of_day_f = time_of_day_f;
1299 float time_diff = m_time_of_day_update_timer;
1300 m_time_of_day_update_timer = 0;
1301 if(m_time_of_day_set){
1302 time_speed = 3600.0*24.0 * tod_diff_f / time_diff;
1303 infostream<<"Client: Measured time_of_day speed (old format): "
1304 <<time_speed<<" tod_diff_f="<<tod_diff_f
1305 <<" time_diff="<<time_diff<<std::endl;
1309 // Update environment
1310 m_env.setTimeOfDay(time_of_day);
1311 m_env.setTimeOfDaySpeed(time_speed);
1312 m_time_of_day_set = true;
1314 u32 dr = m_env.getDayNightRatio();
1315 verbosestream<<"Client: time_of_day="<<time_of_day
1316 <<" time_speed="<<time_speed
1317 <<" dr="<<dr<<std::endl;
1319 else if(command == TOCLIENT_CHAT_MESSAGE)
1327 std::string datastring((char*)&data[2], datasize-2);
1328 std::istringstream is(datastring, std::ios_base::binary);
1331 is.read((char*)buf, 2);
1332 u16 len = readU16(buf);
1334 std::wstring message;
1335 for(u16 i=0; i<len; i++)
1337 is.read((char*)buf, 2);
1338 message += (wchar_t)readU16(buf);
1341 /*infostream<<"Client received chat message: "
1342 <<wide_to_narrow(message)<<std::endl;*/
1344 m_chat_queue.push_back(message);
1346 else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1348 //if(g_settings->getBool("enable_experimental"))
1352 u16 count of removed objects
1353 for all removed objects {
1356 u16 count of added objects
1357 for all added objects {
1360 u32 initialization data length
1361 string initialization data
1366 // Get all data except the command number
1367 std::string datastring((char*)&data[2], datasize-2);
1368 // Throw them in an istringstream
1369 std::istringstream is(datastring, std::ios_base::binary);
1373 // Read removed objects
1375 u16 removed_count = readU16((u8*)buf);
1376 for(u16 i=0; i<removed_count; i++)
1379 u16 id = readU16((u8*)buf);
1382 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1383 m_env.removeActiveObject(id);
1387 // Read added objects
1389 u16 added_count = readU16((u8*)buf);
1390 for(u16 i=0; i<added_count; i++)
1393 u16 id = readU16((u8*)buf);
1395 u8 type = readU8((u8*)buf);
1396 std::string data = deSerializeLongString(is);
1399 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1400 m_env.addActiveObject(id, type, data);
1405 else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1407 //if(g_settings->getBool("enable_experimental"))
1419 // Get all data except the command number
1420 std::string datastring((char*)&data[2], datasize-2);
1421 // Throw them in an istringstream
1422 std::istringstream is(datastring, std::ios_base::binary);
1424 while(is.eof() == false)
1428 u16 id = readU16((u8*)buf);
1432 u16 message_size = readU16((u8*)buf);
1433 std::string message;
1434 message.reserve(message_size);
1435 for(u16 i=0; i<message_size; i++)
1438 message.append(buf, 1);
1440 // Pass on to the environment
1442 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1443 m_env.processActiveObjectMessage(id, message);
1448 else if(command == TOCLIENT_MOVEMENT)
1450 std::string datastring((char*)&data[2], datasize-2);
1451 std::istringstream is(datastring, std::ios_base::binary);
1452 Player *player = m_env.getLocalPlayer();
1453 assert(player != NULL);
1455 player->movement_acceleration_default = readF1000(is) * BS;
1456 player->movement_acceleration_air = readF1000(is) * BS;
1457 player->movement_acceleration_fast = readF1000(is) * BS;
1458 player->movement_speed_walk = readF1000(is) * BS;
1459 player->movement_speed_crouch = readF1000(is) * BS;
1460 player->movement_speed_fast = readF1000(is) * BS;
1461 player->movement_speed_climb = readF1000(is) * BS;
1462 player->movement_speed_jump = readF1000(is) * BS;
1463 player->movement_liquid_fluidity = readF1000(is) * BS;
1464 player->movement_liquid_fluidity_smooth = readF1000(is) * BS;
1465 player->movement_liquid_sink = readF1000(is) * BS;
1466 player->movement_gravity = readF1000(is) * BS;
1468 else if(command == TOCLIENT_HP)
1470 std::string datastring((char*)&data[2], datasize-2);
1471 std::istringstream is(datastring, std::ios_base::binary);
1472 Player *player = m_env.getLocalPlayer();
1473 assert(player != NULL);
1474 u8 oldhp = player->hp;
1480 // Add to ClientEvent queue
1482 event.type = CE_PLAYER_DAMAGE;
1483 event.player_damage.amount = oldhp - hp;
1484 m_client_event_queue.push_back(event);
1487 else if(command == TOCLIENT_BREATH)
1489 std::string datastring((char*)&data[2], datasize-2);
1490 std::istringstream is(datastring, std::ios_base::binary);
1491 Player *player = m_env.getLocalPlayer();
1492 assert(player != NULL);
1493 u16 breath = readU16(is);
1494 player->setBreath(breath) ;
1496 else if(command == TOCLIENT_MOVE_PLAYER)
1498 std::string datastring((char*)&data[2], datasize-2);
1499 std::istringstream is(datastring, std::ios_base::binary);
1500 Player *player = m_env.getLocalPlayer();
1501 assert(player != NULL);
1502 v3f pos = readV3F1000(is);
1503 f32 pitch = readF1000(is);
1504 f32 yaw = readF1000(is);
1505 player->setPosition(pos);
1506 /*player->setPitch(pitch);
1507 player->setYaw(yaw);*/
1509 infostream<<"Client got TOCLIENT_MOVE_PLAYER"
1510 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1516 Add to ClientEvent queue.
1517 This has to be sent to the main program because otherwise
1518 it would just force the pitch and yaw values to whatever
1519 the camera points to.
1522 event.type = CE_PLAYER_FORCE_MOVE;
1523 event.player_force_move.pitch = pitch;
1524 event.player_force_move.yaw = yaw;
1525 m_client_event_queue.push_back(event);
1527 // Ignore damage for a few seconds, so that the player doesn't
1528 // get damage from falling on ground
1529 m_ignore_damage_timer = 3.0;
1531 else if(command == TOCLIENT_PLAYERITEM)
1533 infostream<<"Client: WARNING: Ignoring TOCLIENT_PLAYERITEM"<<std::endl;
1535 else if(command == TOCLIENT_DEATHSCREEN)
1537 std::string datastring((char*)&data[2], datasize-2);
1538 std::istringstream is(datastring, std::ios_base::binary);
1540 bool set_camera_point_target = readU8(is);
1541 v3f camera_point_target = readV3F1000(is);
1544 event.type = CE_DEATHSCREEN;
1545 event.deathscreen.set_camera_point_target = set_camera_point_target;
1546 event.deathscreen.camera_point_target_x = camera_point_target.X;
1547 event.deathscreen.camera_point_target_y = camera_point_target.Y;
1548 event.deathscreen.camera_point_target_z = camera_point_target.Z;
1549 m_client_event_queue.push_back(event);
1551 else if(command == TOCLIENT_ANNOUNCE_MEDIA)
1553 std::string datastring((char*)&data[2], datasize-2);
1554 std::istringstream is(datastring, std::ios_base::binary);
1556 int num_files = readU16(is);
1558 infostream<<"Client: Received media announcement: packet size: "
1559 <<datasize<<std::endl;
1561 if (m_media_downloader == NULL ||
1562 m_media_downloader->isStarted()) {
1563 const char *problem = m_media_downloader ?
1564 "we already saw another announcement" :
1565 "all media has been received already";
1566 errorstream<<"Client: Received media announcement but "
1568 <<" files="<<num_files
1569 <<" size="<<datasize<<std::endl;
1573 // Mesh update thread must be stopped while
1574 // updating content definitions
1575 assert(!m_mesh_update_thread.IsRunning());
1577 for(int i=0; i<num_files; i++)
1579 std::string name = deSerializeString(is);
1580 std::string sha1_base64 = deSerializeString(is);
1581 std::string sha1_raw = base64_decode(sha1_base64);
1582 m_media_downloader->addFile(name, sha1_raw);
1585 std::vector<std::string> remote_media;
1587 Strfnd sf(deSerializeString(is));
1588 while(!sf.atend()) {
1589 std::string baseurl = trim(sf.next(","));
1591 m_media_downloader->addRemoteServer(baseurl);
1594 catch(SerializationError) {
1595 // not supported by server or turned off
1598 m_media_downloader->step(this);
1599 if (m_media_downloader->isDone()) {
1600 // might be done already if all media is in the cache
1601 delete m_media_downloader;
1602 m_media_downloader = NULL;
1605 else if(command == TOCLIENT_MEDIA)
1607 std::string datastring((char*)&data[2], datasize-2);
1608 std::istringstream is(datastring, std::ios_base::binary);
1612 u16 total number of file bunches
1613 u16 index of this bunch
1614 u32 number of files in this bunch
1622 int num_bunches = readU16(is);
1623 int bunch_i = readU16(is);
1624 u32 num_files = readU32(is);
1625 infostream<<"Client: Received files: bunch "<<bunch_i<<"/"
1626 <<num_bunches<<" files="<<num_files
1627 <<" size="<<datasize<<std::endl;
1632 if (m_media_downloader == NULL ||
1633 !m_media_downloader->isStarted()) {
1634 const char *problem = m_media_downloader ?
1635 "media has not been requested" :
1636 "all media has been received already";
1637 errorstream<<"Client: Received media but "
1639 <<" bunch "<<bunch_i<<"/"<<num_bunches
1640 <<" files="<<num_files
1641 <<" size="<<datasize<<std::endl;
1645 // Mesh update thread must be stopped while
1646 // updating content definitions
1647 assert(!m_mesh_update_thread.IsRunning());
1649 for(u32 i=0; i<num_files; i++){
1650 std::string name = deSerializeString(is);
1651 std::string data = deSerializeLongString(is);
1652 m_media_downloader->conventionalTransferDone(
1656 if (m_media_downloader->isDone()) {
1657 delete m_media_downloader;
1658 m_media_downloader = NULL;
1661 else if(command == TOCLIENT_TOOLDEF)
1663 infostream<<"Client: WARNING: Ignoring TOCLIENT_TOOLDEF"<<std::endl;
1665 else if(command == TOCLIENT_NODEDEF)
1667 infostream<<"Client: Received node definitions: packet size: "
1668 <<datasize<<std::endl;
1670 // Mesh update thread must be stopped while
1671 // updating content definitions
1672 assert(!m_mesh_update_thread.IsRunning());
1674 // Decompress node definitions
1675 std::string datastring((char*)&data[2], datasize-2);
1676 std::istringstream is(datastring, std::ios_base::binary);
1677 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1678 std::ostringstream tmp_os;
1679 decompressZlib(tmp_is, tmp_os);
1681 // Deserialize node definitions
1682 std::istringstream tmp_is2(tmp_os.str());
1683 m_nodedef->deSerialize(tmp_is2);
1684 m_nodedef_received = true;
1686 else if(command == TOCLIENT_CRAFTITEMDEF)
1688 infostream<<"Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF"<<std::endl;
1690 else if(command == TOCLIENT_ITEMDEF)
1692 infostream<<"Client: Received item definitions: packet size: "
1693 <<datasize<<std::endl;
1695 // Mesh update thread must be stopped while
1696 // updating content definitions
1697 assert(!m_mesh_update_thread.IsRunning());
1699 // Decompress item definitions
1700 std::string datastring((char*)&data[2], datasize-2);
1701 std::istringstream is(datastring, std::ios_base::binary);
1702 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1703 std::ostringstream tmp_os;
1704 decompressZlib(tmp_is, tmp_os);
1706 // Deserialize node definitions
1707 std::istringstream tmp_is2(tmp_os.str());
1708 m_itemdef->deSerialize(tmp_is2);
1709 m_itemdef_received = true;
1711 else if(command == TOCLIENT_PLAY_SOUND)
1713 std::string datastring((char*)&data[2], datasize-2);
1714 std::istringstream is(datastring, std::ios_base::binary);
1716 s32 server_id = readS32(is);
1717 std::string name = deSerializeString(is);
1718 float gain = readF1000(is);
1719 int type = readU8(is); // 0=local, 1=positional, 2=object
1720 v3f pos = readV3F1000(is);
1721 u16 object_id = readU16(is);
1722 bool loop = readU8(is);
1727 client_id = m_sound->playSound(name, loop, gain);
1729 case 1: // positional
1730 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1733 ClientActiveObject *cao = m_env.getActiveObject(object_id);
1735 pos = cao->getPosition();
1736 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1737 // TODO: Set up sound to move with object
1742 if(client_id != -1){
1743 m_sounds_server_to_client[server_id] = client_id;
1744 m_sounds_client_to_server[client_id] = server_id;
1746 m_sounds_to_objects[client_id] = object_id;
1749 else if(command == TOCLIENT_STOP_SOUND)
1751 std::string datastring((char*)&data[2], datasize-2);
1752 std::istringstream is(datastring, std::ios_base::binary);
1754 s32 server_id = readS32(is);
1755 std::map<s32, int>::iterator i =
1756 m_sounds_server_to_client.find(server_id);
1757 if(i != m_sounds_server_to_client.end()){
1758 int client_id = i->second;
1759 m_sound->stopSound(client_id);
1762 else if(command == TOCLIENT_PRIVILEGES)
1764 std::string datastring((char*)&data[2], datasize-2);
1765 std::istringstream is(datastring, std::ios_base::binary);
1767 m_privileges.clear();
1768 infostream<<"Client: Privileges updated: ";
1769 u16 num_privileges = readU16(is);
1770 for(u16 i=0; i<num_privileges; i++){
1771 std::string priv = deSerializeString(is);
1772 m_privileges.insert(priv);
1773 infostream<<priv<<" ";
1775 infostream<<std::endl;
1777 else if(command == TOCLIENT_INVENTORY_FORMSPEC)
1779 std::string datastring((char*)&data[2], datasize-2);
1780 std::istringstream is(datastring, std::ios_base::binary);
1782 // Store formspec in LocalPlayer
1783 Player *player = m_env.getLocalPlayer();
1784 assert(player != NULL);
1785 player->inventory_formspec = deSerializeLongString(is);
1787 else if(command == TOCLIENT_DETACHED_INVENTORY)
1789 std::string datastring((char*)&data[2], datasize-2);
1790 std::istringstream is(datastring, std::ios_base::binary);
1792 std::string name = deSerializeString(is);
1794 infostream<<"Client: Detached inventory update: \""<<name<<"\""<<std::endl;
1796 Inventory *inv = NULL;
1797 if(m_detached_inventories.count(name) > 0)
1798 inv = m_detached_inventories[name];
1800 inv = new Inventory(m_itemdef);
1801 m_detached_inventories[name] = inv;
1803 inv->deSerialize(is);
1805 else if(command == TOCLIENT_SHOW_FORMSPEC)
1807 std::string datastring((char*)&data[2], datasize-2);
1808 std::istringstream is(datastring, std::ios_base::binary);
1810 std::string formspec = deSerializeLongString(is);
1811 std::string formname = deSerializeString(is);
1814 event.type = CE_SHOW_FORMSPEC;
1815 // pointer is required as event is a struct only!
1816 // adding a std:string to a struct isn't possible
1817 event.show_formspec.formspec = new std::string(formspec);
1818 event.show_formspec.formname = new std::string(formname);
1819 m_client_event_queue.push_back(event);
1821 else if(command == TOCLIENT_SPAWN_PARTICLE)
1823 std::string datastring((char*)&data[2], datasize-2);
1824 std::istringstream is(datastring, std::ios_base::binary);
1826 v3f pos = readV3F1000(is);
1827 v3f vel = readV3F1000(is);
1828 v3f acc = readV3F1000(is);
1829 float expirationtime = readF1000(is);
1830 float size = readF1000(is);
1831 bool collisiondetection = readU8(is);
1832 std::string texture = deSerializeLongString(is);
1835 event.type = CE_SPAWN_PARTICLE;
1836 event.spawn_particle.pos = new v3f (pos);
1837 event.spawn_particle.vel = new v3f (vel);
1838 event.spawn_particle.acc = new v3f (acc);
1840 event.spawn_particle.expirationtime = expirationtime;
1841 event.spawn_particle.size = size;
1842 event.spawn_particle.collisiondetection =
1844 event.spawn_particle.texture = new std::string(texture);
1846 m_client_event_queue.push_back(event);
1848 else if(command == TOCLIENT_ADD_PARTICLESPAWNER)
1850 std::string datastring((char*)&data[2], datasize-2);
1851 std::istringstream is(datastring, std::ios_base::binary);
1853 u16 amount = readU16(is);
1854 float spawntime = readF1000(is);
1855 v3f minpos = readV3F1000(is);
1856 v3f maxpos = readV3F1000(is);
1857 v3f minvel = readV3F1000(is);
1858 v3f maxvel = readV3F1000(is);
1859 v3f minacc = readV3F1000(is);
1860 v3f maxacc = readV3F1000(is);
1861 float minexptime = readF1000(is);
1862 float maxexptime = readF1000(is);
1863 float minsize = readF1000(is);
1864 float maxsize = readF1000(is);
1865 bool collisiondetection = readU8(is);
1866 std::string texture = deSerializeLongString(is);
1867 u32 id = readU32(is);
1870 event.type = CE_ADD_PARTICLESPAWNER;
1871 event.add_particlespawner.amount = amount;
1872 event.add_particlespawner.spawntime = spawntime;
1874 event.add_particlespawner.minpos = new v3f (minpos);
1875 event.add_particlespawner.maxpos = new v3f (maxpos);
1876 event.add_particlespawner.minvel = new v3f (minvel);
1877 event.add_particlespawner.maxvel = new v3f (maxvel);
1878 event.add_particlespawner.minacc = new v3f (minacc);
1879 event.add_particlespawner.maxacc = new v3f (maxacc);
1881 event.add_particlespawner.minexptime = minexptime;
1882 event.add_particlespawner.maxexptime = maxexptime;
1883 event.add_particlespawner.minsize = minsize;
1884 event.add_particlespawner.maxsize = maxsize;
1885 event.add_particlespawner.collisiondetection = collisiondetection;
1886 event.add_particlespawner.texture = new std::string(texture);
1887 event.add_particlespawner.id = id;
1889 m_client_event_queue.push_back(event);
1891 else if(command == TOCLIENT_DELETE_PARTICLESPAWNER)
1893 std::string datastring((char*)&data[2], datasize-2);
1894 std::istringstream is(datastring, std::ios_base::binary);
1896 u32 id = readU16(is);
1899 event.type = CE_DELETE_PARTICLESPAWNER;
1900 event.delete_particlespawner.id = id;
1902 m_client_event_queue.push_back(event);
1904 else if(command == TOCLIENT_HUDADD)
1906 std::string datastring((char *)&data[2], datasize - 2);
1907 std::istringstream is(datastring, std::ios_base::binary);
1909 u32 id = readU32(is);
1910 u8 type = readU8(is);
1911 v2f pos = readV2F1000(is);
1912 std::string name = deSerializeString(is);
1913 v2f scale = readV2F1000(is);
1914 std::string text = deSerializeString(is);
1915 u32 number = readU32(is);
1916 u32 item = readU32(is);
1917 u32 dir = readU32(is);
1918 v2f align = readV2F1000(is);
1919 v2f offset = readV2F1000(is);
1922 event.type = CE_HUDADD;
1923 event.hudadd.id = id;
1924 event.hudadd.type = type;
1925 event.hudadd.pos = new v2f(pos);
1926 event.hudadd.name = new std::string(name);
1927 event.hudadd.scale = new v2f(scale);
1928 event.hudadd.text = new std::string(text);
1929 event.hudadd.number = number;
1930 event.hudadd.item = item;
1931 event.hudadd.dir = dir;
1932 event.hudadd.align = new v2f(align);
1933 event.hudadd.offset = new v2f(offset);
1934 m_client_event_queue.push_back(event);
1936 else if(command == TOCLIENT_HUDRM)
1938 std::string datastring((char *)&data[2], datasize - 2);
1939 std::istringstream is(datastring, std::ios_base::binary);
1941 u32 id = readU32(is);
1944 event.type = CE_HUDRM;
1945 event.hudrm.id = id;
1946 m_client_event_queue.push_back(event);
1948 else if(command == TOCLIENT_HUDCHANGE)
1954 std::string datastring((char *)&data[2], datasize - 2);
1955 std::istringstream is(datastring, std::ios_base::binary);
1957 u32 id = readU32(is);
1958 u8 stat = (HudElementStat)readU8(is);
1960 if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
1961 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
1962 v2fdata = readV2F1000(is);
1963 else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
1964 sdata = deSerializeString(is);
1966 intdata = readU32(is);
1969 event.type = CE_HUDCHANGE;
1970 event.hudchange.id = id;
1971 event.hudchange.stat = (HudElementStat)stat;
1972 event.hudchange.v2fdata = new v2f(v2fdata);
1973 event.hudchange.sdata = new std::string(sdata);
1974 event.hudchange.data = intdata;
1975 m_client_event_queue.push_back(event);
1977 else if(command == TOCLIENT_HUD_SET_FLAGS)
1979 std::string datastring((char *)&data[2], datasize - 2);
1980 std::istringstream is(datastring, std::ios_base::binary);
1982 Player *player = m_env.getLocalPlayer();
1983 assert(player != NULL);
1985 u32 flags = readU32(is);
1986 u32 mask = readU32(is);
1988 player->hud_flags &= ~mask;
1989 player->hud_flags |= flags;
1991 else if(command == TOCLIENT_HUD_SET_PARAM)
1993 std::string datastring((char *)&data[2], datasize - 2);
1994 std::istringstream is(datastring, std::ios_base::binary);
1996 Player *player = m_env.getLocalPlayer();
1997 assert(player != NULL);
1999 u16 param = readU16(is);
2000 std::string value = deSerializeString(is);
2002 if(param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4){
2003 s32 hotbar_itemcount = readS32((u8*) value.c_str());
2004 if(hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
2005 player->hud_hotbar_itemcount = hotbar_itemcount;
2006 } else if (param == HUD_PARAM_HOTBAR_IMAGE) {
2007 ((LocalPlayer *) player)->hotbar_image = value;
2008 } else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
2009 ((LocalPlayer *) player)->hotbar_selected_image = value;
2014 infostream<<"Client: Ignoring unknown command "
2015 <<command<<std::endl;
2019 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
2021 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2022 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
2025 void Client::interact(u8 action, const PointedThing& pointed)
2027 if(connectedAndInitialized() == false){
2028 infostream<<"Client::interact() "
2029 "cancelled (not connected)"
2034 std::ostringstream os(std::ios_base::binary);
2040 [5] u32 length of the next item
2041 [9] serialized PointedThing
2043 0: start digging (from undersurface) or use
2044 1: stop digging (all parameters ignored)
2045 2: digging completed
2046 3: place block or item (to abovesurface)
2049 writeU16(os, TOSERVER_INTERACT);
2050 writeU8(os, action);
2051 writeU16(os, getPlayerItem());
2052 std::ostringstream tmp_os(std::ios::binary);
2053 pointed.serialize(tmp_os);
2054 os<<serializeLongString(tmp_os.str());
2056 std::string s = os.str();
2057 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2060 Send(0, data, true);
2063 void Client::sendNodemetaFields(v3s16 p, const std::string &formname,
2064 const std::map<std::string, std::string> &fields)
2066 std::ostringstream os(std::ios_base::binary);
2068 writeU16(os, TOSERVER_NODEMETA_FIELDS);
2070 os<<serializeString(formname);
2071 writeU16(os, fields.size());
2072 for(std::map<std::string, std::string>::const_iterator
2073 i = fields.begin(); i != fields.end(); i++){
2074 const std::string &name = i->first;
2075 const std::string &value = i->second;
2076 os<<serializeString(name);
2077 os<<serializeLongString(value);
2081 std::string s = os.str();
2082 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2084 Send(0, data, true);
2087 void Client::sendInventoryFields(const std::string &formname,
2088 const std::map<std::string, std::string> &fields)
2090 std::ostringstream os(std::ios_base::binary);
2092 writeU16(os, TOSERVER_INVENTORY_FIELDS);
2093 os<<serializeString(formname);
2094 writeU16(os, fields.size());
2095 for(std::map<std::string, std::string>::const_iterator
2096 i = fields.begin(); i != fields.end(); i++){
2097 const std::string &name = i->first;
2098 const std::string &value = i->second;
2099 os<<serializeString(name);
2100 os<<serializeLongString(value);
2104 std::string s = os.str();
2105 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2107 Send(0, data, true);
2110 void Client::sendInventoryAction(InventoryAction *a)
2112 std::ostringstream os(std::ios_base::binary);
2116 writeU16(buf, TOSERVER_INVENTORY_ACTION);
2117 os.write((char*)buf, 2);
2122 std::string s = os.str();
2123 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2125 Send(0, data, true);
2128 void Client::sendChatMessage(const std::wstring &message)
2130 std::ostringstream os(std::ios_base::binary);
2134 writeU16(buf, TOSERVER_CHAT_MESSAGE);
2135 os.write((char*)buf, 2);
2138 writeU16(buf, message.size());
2139 os.write((char*)buf, 2);
2142 for(u32 i=0; i<message.size(); i++)
2146 os.write((char*)buf, 2);
2150 std::string s = os.str();
2151 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2153 Send(0, data, true);
2156 void Client::sendChangePassword(const std::wstring oldpassword,
2157 const std::wstring newpassword)
2159 Player *player = m_env.getLocalPlayer();
2163 std::string playername = player->getName();
2164 std::string oldpwd = translatePassword(playername, oldpassword);
2165 std::string newpwd = translatePassword(playername, newpassword);
2167 std::ostringstream os(std::ios_base::binary);
2168 u8 buf[2+PASSWORD_SIZE*2];
2170 [0] u16 TOSERVER_PASSWORD
2171 [2] u8[28] old password
2172 [30] u8[28] new password
2175 writeU16(buf, TOSERVER_PASSWORD);
2176 for(u32 i=0;i<PASSWORD_SIZE-1;i++)
2178 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
2179 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
2181 buf[2+PASSWORD_SIZE-1] = 0;
2182 buf[30+PASSWORD_SIZE-1] = 0;
2183 os.write((char*)buf, 2+PASSWORD_SIZE*2);
2186 std::string s = os.str();
2187 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2189 Send(0, data, true);
2193 void Client::sendDamage(u8 damage)
2195 DSTACK(__FUNCTION_NAME);
2196 std::ostringstream os(std::ios_base::binary);
2198 writeU16(os, TOSERVER_DAMAGE);
2199 writeU8(os, damage);
2202 std::string s = os.str();
2203 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2205 Send(0, data, true);
2208 void Client::sendBreath(u16 breath)
2210 DSTACK(__FUNCTION_NAME);
2211 std::ostringstream os(std::ios_base::binary);
2213 writeU16(os, TOSERVER_BREATH);
2214 writeU16(os, breath);
2216 std::string s = os.str();
2217 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2219 Send(0, data, true);
2222 void Client::sendRespawn()
2224 DSTACK(__FUNCTION_NAME);
2225 std::ostringstream os(std::ios_base::binary);
2227 writeU16(os, TOSERVER_RESPAWN);
2230 std::string s = os.str();
2231 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2233 Send(0, data, true);
2236 void Client::sendPlayerPos()
2238 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2240 LocalPlayer *myplayer = m_env.getLocalPlayer();
2241 if(myplayer == NULL)
2244 // Save bandwidth by only updating position when something changed
2245 if(myplayer->last_position == myplayer->getPosition() &&
2246 myplayer->last_speed == myplayer->getSpeed() &&
2247 myplayer->last_pitch == myplayer->getPitch() &&
2248 myplayer->last_yaw == myplayer->getYaw() &&
2249 myplayer->last_keyPressed == myplayer->keyPressed)
2252 myplayer->last_position = myplayer->getPosition();
2253 myplayer->last_speed = myplayer->getSpeed();
2254 myplayer->last_pitch = myplayer->getPitch();
2255 myplayer->last_yaw = myplayer->getYaw();
2256 myplayer->last_keyPressed = myplayer->keyPressed;
2260 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2261 our_peer_id = m_con.GetPeerID();
2264 // Set peer id if not set already
2265 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2266 myplayer->peer_id = our_peer_id;
2267 // Check that an existing peer_id is the same as the connection's
2268 assert(myplayer->peer_id == our_peer_id);
2270 v3f pf = myplayer->getPosition();
2271 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
2272 v3f sf = myplayer->getSpeed();
2273 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
2274 s32 pitch = myplayer->getPitch() * 100;
2275 s32 yaw = myplayer->getYaw() * 100;
2276 u32 keyPressed=myplayer->keyPressed;
2280 [2] v3s32 position*100
2281 [2+12] v3s32 speed*100
2282 [2+12+12] s32 pitch*100
2283 [2+12+12+4] s32 yaw*100
2284 [2+12+12+4+4] u32 keyPressed
2286 SharedBuffer<u8> data(2+12+12+4+4+4);
2287 writeU16(&data[0], TOSERVER_PLAYERPOS);
2288 writeV3S32(&data[2], position);
2289 writeV3S32(&data[2+12], speed);
2290 writeS32(&data[2+12+12], pitch);
2291 writeS32(&data[2+12+12+4], yaw);
2292 writeU32(&data[2+12+12+4+4], keyPressed);
2293 // Send as unreliable
2294 Send(0, data, false);
2297 void Client::sendPlayerItem(u16 item)
2299 Player *myplayer = m_env.getLocalPlayer();
2300 if(myplayer == NULL)
2303 u16 our_peer_id = m_con.GetPeerID();
2305 // Set peer id if not set already
2306 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2307 myplayer->peer_id = our_peer_id;
2308 // Check that an existing peer_id is the same as the connection's
2309 assert(myplayer->peer_id == our_peer_id);
2311 SharedBuffer<u8> data(2+2);
2312 writeU16(&data[0], TOSERVER_PLAYERITEM);
2313 writeU16(&data[2], item);
2316 Send(0, data, true);
2319 void Client::removeNode(v3s16 p)
2321 std::map<v3s16, MapBlock*> modified_blocks;
2325 //TimeTaker t("removeNodeAndUpdate", m_device);
2326 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
2328 catch(InvalidPositionException &e)
2332 // add urgent task to update the modified node
2333 addUpdateMeshTaskForNode(p, false, true);
2335 for(std::map<v3s16, MapBlock * >::iterator
2336 i = modified_blocks.begin();
2337 i != modified_blocks.end(); ++i)
2339 addUpdateMeshTaskWithEdge(i->first);
2343 void Client::addNode(v3s16 p, MapNode n, bool remove_metadata)
2345 TimeTaker timer1("Client::addNode()");
2347 std::map<v3s16, MapBlock*> modified_blocks;
2351 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
2352 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
2354 catch(InvalidPositionException &e)
2357 for(std::map<v3s16, MapBlock * >::iterator
2358 i = modified_blocks.begin();
2359 i != modified_blocks.end(); ++i)
2361 addUpdateMeshTaskWithEdge(i->first);
2365 void Client::setPlayerControl(PlayerControl &control)
2367 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2368 LocalPlayer *player = m_env.getLocalPlayer();
2369 assert(player != NULL);
2370 player->control = control;
2373 void Client::selectPlayerItem(u16 item)
2375 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2376 m_playeritem = item;
2377 m_inventory_updated = true;
2378 sendPlayerItem(item);
2381 // Returns true if the inventory of the local player has been
2382 // updated from the server. If it is true, it is set to false.
2383 bool Client::getLocalInventoryUpdated()
2385 // m_inventory_updated is behind envlock
2386 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2387 bool updated = m_inventory_updated;
2388 m_inventory_updated = false;
2392 // Copies the inventory of the local player to parameter
2393 void Client::getLocalInventory(Inventory &dst)
2395 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2396 Player *player = m_env.getLocalPlayer();
2397 assert(player != NULL);
2398 dst = player->inventory;
2401 Inventory* Client::getInventory(const InventoryLocation &loc)
2404 case InventoryLocation::UNDEFINED:
2407 case InventoryLocation::CURRENT_PLAYER:
2409 Player *player = m_env.getLocalPlayer();
2410 assert(player != NULL);
2411 return &player->inventory;
2414 case InventoryLocation::PLAYER:
2416 Player *player = m_env.getPlayer(loc.name.c_str());
2419 return &player->inventory;
2422 case InventoryLocation::NODEMETA:
2424 NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p);
2427 return meta->getInventory();
2430 case InventoryLocation::DETACHED:
2432 if(m_detached_inventories.count(loc.name) == 0)
2434 return m_detached_inventories[loc.name];
2442 void Client::inventoryAction(InventoryAction *a)
2445 Send it to the server
2447 sendInventoryAction(a);
2450 Predict some local inventory changes
2452 a->clientApply(this, this);
2458 ClientActiveObject * Client::getSelectedActiveObject(
2460 v3f from_pos_f_on_map,
2461 core::line3d<f32> shootline_on_map
2464 std::vector<DistanceSortedActiveObject> objects;
2466 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
2468 //infostream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
2471 // After this, the closest object is the first in the array.
2472 std::sort(objects.begin(), objects.end());
2474 for(u32 i=0; i<objects.size(); i++)
2476 ClientActiveObject *obj = objects[i].obj;
2478 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
2479 if(selection_box == NULL)
2482 v3f pos = obj->getPosition();
2484 core::aabbox3d<f32> offsetted_box(
2485 selection_box->MinEdge + pos,
2486 selection_box->MaxEdge + pos
2489 if(offsetted_box.intersectsWithLine(shootline_on_map))
2491 //infostream<<"Returning selected object"<<std::endl;
2496 //infostream<<"No object selected; returning NULL."<<std::endl;
2500 void Client::printDebugInfo(std::ostream &os)
2502 //JMutexAutoLock lock1(m_fetchblock_mutex);
2503 /*JMutexAutoLock lock2(m_incoming_queue_mutex);
2505 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
2506 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
2507 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
2511 std::list<std::string> Client::getConnectedPlayerNames()
2513 return m_env.getPlayerNames();
2516 float Client::getAnimationTime()
2518 return m_animation_time;
2521 int Client::getCrackLevel()
2523 return m_crack_level;
2526 void Client::setCrack(int level, v3s16 pos)
2528 int old_crack_level = m_crack_level;
2529 v3s16 old_crack_pos = m_crack_pos;
2531 m_crack_level = level;
2534 if(old_crack_level >= 0 && (level < 0 || pos != old_crack_pos))
2537 addUpdateMeshTaskForNode(old_crack_pos, false, true);
2539 if(level >= 0 && (old_crack_level < 0 || pos != old_crack_pos))
2542 addUpdateMeshTaskForNode(pos, false, true);
2548 Player *player = m_env.getLocalPlayer();
2549 assert(player != NULL);
2553 u16 Client::getBreath()
2555 Player *player = m_env.getLocalPlayer();
2556 assert(player != NULL);
2557 return player->getBreath();
2560 bool Client::getChatMessage(std::wstring &message)
2562 if(m_chat_queue.size() == 0)
2564 message = m_chat_queue.pop_front();
2568 void Client::typeChatMessage(const std::wstring &message)
2570 // Discard empty line
2575 sendChatMessage(message);
2578 if (message[0] == L'/')
2580 m_chat_queue.push_back(
2581 (std::wstring)L"issued command: "+message);
2585 LocalPlayer *player = m_env.getLocalPlayer();
2586 assert(player != NULL);
2587 std::wstring name = narrow_to_wide(player->getName());
2588 m_chat_queue.push_back(
2589 (std::wstring)L"<"+name+L"> "+message);
2593 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
2595 /*infostream<<"Client::addUpdateMeshTask(): "
2596 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2597 <<" ack_to_server="<<ack_to_server
2598 <<" urgent="<<urgent
2601 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2606 Create a task to update the mesh of the block
2609 MeshMakeData *data = new MeshMakeData(this);
2612 //TimeTaker timer("data fill");
2614 // Debug: 1-6ms, avg=2ms
2616 data->setCrack(m_crack_level, m_crack_pos);
2617 data->setSmoothLighting(g_settings->getBool("smooth_lighting"));
2621 //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
2623 // Add task to queue
2624 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server, urgent);
2626 /*infostream<<"Mesh update input queue size is "
2627 <<m_mesh_update_thread.m_queue_in.size()
2631 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent)
2635 infostream<<"Client::addUpdateMeshTaskWithEdge(): "
2636 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2641 v3s16 p = blockpos + v3s16(0,0,0);
2642 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2643 addUpdateMeshTask(p, ack_to_server, urgent);
2645 catch(InvalidPositionException &e){}
2647 for (int i=0;i<6;i++)
2650 v3s16 p = blockpos + g_6dirs[i];
2651 addUpdateMeshTask(p, false, urgent);
2653 catch(InvalidPositionException &e){}
2657 void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool urgent)
2661 infostream<<"Client::addUpdateMeshTaskForNode(): "
2662 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2666 v3s16 blockpos = getNodeBlockPos(nodepos);
2667 v3s16 blockpos_relative = blockpos * MAP_BLOCKSIZE;
2670 v3s16 p = blockpos + v3s16(0,0,0);
2671 addUpdateMeshTask(p, ack_to_server, urgent);
2673 catch(InvalidPositionException &e){}
2675 if(nodepos.X == blockpos_relative.X){
2677 v3s16 p = blockpos + v3s16(-1,0,0);
2678 addUpdateMeshTask(p, false, urgent);
2680 catch(InvalidPositionException &e){}
2682 if(nodepos.Y == blockpos_relative.Y){
2684 v3s16 p = blockpos + v3s16(0,-1,0);
2685 addUpdateMeshTask(p, false, urgent);
2687 catch(InvalidPositionException &e){}
2689 if(nodepos.Z == blockpos_relative.Z){
2691 v3s16 p = blockpos + v3s16(0,0,-1);
2692 addUpdateMeshTask(p, false, urgent);
2694 catch(InvalidPositionException &e){}
2698 ClientEvent Client::getClientEvent()
2700 if(m_client_event_queue.size() == 0)
2703 event.type = CE_NONE;
2706 return m_client_event_queue.pop_front();
2709 float Client::mediaReceiveProgress()
2711 if (m_media_downloader)
2712 return m_media_downloader->getProgress();
2714 return 1.0; // downloader only exists when not yet done
2717 void draw_load_screen(const std::wstring &text,
2718 IrrlichtDevice* device, gui::IGUIFont* font,
2719 float dtime=0 ,int percent=0, bool clouds=true);
2720 void Client::afterContentReceived(IrrlichtDevice *device, gui::IGUIFont* font)
2722 infostream<<"Client::afterContentReceived() started"<<std::endl;
2723 assert(m_itemdef_received);
2724 assert(m_nodedef_received);
2725 assert(mediaReceived());
2727 // Rebuild inherited images and recreate textures
2728 infostream<<"- Rebuilding images and textures"<<std::endl;
2729 m_tsrc->rebuildImagesAndTextures();
2732 infostream<<"- Rebuilding shaders"<<std::endl;
2733 m_shsrc->rebuildShaders();
2735 // Update node aliases
2736 infostream<<"- Updating node aliases"<<std::endl;
2737 m_nodedef->updateAliases(m_itemdef);
2739 // Update node textures
2740 infostream<<"- Updating node textures"<<std::endl;
2741 m_nodedef->updateTextures(m_tsrc);
2743 // Preload item textures and meshes if configured to
2744 if(g_settings->getBool("preload_item_visuals"))
2746 verbosestream<<"Updating item textures and meshes"<<std::endl;
2747 wchar_t* text = wgettext("Item textures...");
2748 draw_load_screen(text,device,font,0,0);
2749 std::set<std::string> names = m_itemdef->getAll();
2750 size_t size = names.size();
2753 for(std::set<std::string>::const_iterator
2754 i = names.begin(); i != names.end(); ++i){
2755 // Asking for these caches the result
2756 m_itemdef->getInventoryTexture(*i, this);
2757 m_itemdef->getWieldMesh(*i, this);
2759 percent = count*100/size;
2760 if (count%50 == 0) // only update every 50 item
2761 draw_load_screen(text,device,font,0,percent);
2766 // Start mesh update thread after setting up content definitions
2767 infostream<<"- Starting mesh update thread"<<std::endl;
2768 m_mesh_update_thread.Start();
2770 infostream<<"Client::afterContentReceived() done"<<std::endl;
2773 float Client::getRTT(void)
2776 return m_con.GetPeerAvgRTT(PEER_ID_SERVER);
2777 } catch(con::PeerNotFoundException &e){
2782 // IGameDef interface
2784 IItemDefManager* Client::getItemDefManager()
2788 INodeDefManager* Client::getNodeDefManager()
2792 ICraftDefManager* Client::getCraftDefManager()
2795 //return m_craftdef;
2797 ITextureSource* Client::getTextureSource()
2801 IShaderSource* Client::getShaderSource()
2805 u16 Client::allocateUnknownNodeId(const std::string &name)
2807 errorstream<<"Client::allocateUnknownNodeId(): "
2808 <<"Client cannot allocate node IDs"<<std::endl;
2810 return CONTENT_IGNORE;
2812 ISoundManager* Client::getSoundManager()
2816 MtEventManager* Client::getEventManager()
2821 scene::IAnimatedMesh* Client::getMesh(const std::string &filename)
2823 std::map<std::string, std::string>::const_iterator i =
2824 m_mesh_data.find(filename);
2825 if(i == m_mesh_data.end()){
2826 errorstream<<"Client::getMesh(): Mesh not found: \""<<filename<<"\""
2830 const std::string &data = i->second;
2831 scene::ISceneManager *smgr = m_device->getSceneManager();
2833 // Create the mesh, remove it from cache and return it
2834 // This allows unique vertex colors and other properties for each instance
2835 Buffer<char> data_rw(data.c_str(), data.size()); // Const-incorrect Irrlicht
2836 io::IFileSystem *irrfs = m_device->getFileSystem();
2837 io::IReadFile *rfile = irrfs->createMemoryReadFile(
2838 *data_rw, data_rw.getSize(), filename.c_str());
2840 scene::IAnimatedMesh *mesh = smgr->getMesh(rfile);
2842 // NOTE: By playing with Irrlicht refcounts, maybe we could cache a bunch
2843 // of uniquely named instances and re-use them
2845 smgr->getMeshCache()->removeMesh(mesh);