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);
291 //request all client managed threads to stop
292 m_mesh_update_thread.Stop();
295 bool Client::isShutdown()
298 if (!m_mesh_update_thread.IsRunning()) return true;
306 //JMutexAutoLock conlock(m_con_mutex); //bulk comment-out
310 m_mesh_update_thread.Stop();
311 m_mesh_update_thread.Wait();
312 while(!m_mesh_update_thread.m_queue_out.empty()) {
313 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_frontNoEx();
318 delete m_inventory_from_server;
320 // Delete detached inventories
322 for(std::map<std::string, Inventory*>::iterator
323 i = m_detached_inventories.begin();
324 i != m_detached_inventories.end(); i++){
329 // cleanup 3d model meshes on client shutdown
330 while (m_device->getSceneManager()->getMeshCache()->getMeshCount() != 0) {
331 scene::IAnimatedMesh * mesh =
332 m_device->getSceneManager()->getMeshCache()->getMeshByIndex(0);
335 m_device->getSceneManager()->getMeshCache()->removeMesh(mesh);
339 void Client::connect(Address address)
341 DSTACK(__FUNCTION_NAME);
342 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
343 m_con.SetTimeoutMs(0);
344 m_con.Connect(address);
347 bool Client::connectedAndInitialized()
349 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
351 if(m_con.Connected() == false)
354 if(m_server_ser_ver == SER_FMT_VER_INVALID)
360 void Client::step(float dtime)
362 DSTACK(__FUNCTION_NAME);
368 if(m_ignore_damage_timer > dtime)
369 m_ignore_damage_timer -= dtime;
371 m_ignore_damage_timer = 0.0;
373 m_animation_time += dtime;
374 if(m_animation_time > 60.0)
375 m_animation_time -= 60.0;
377 m_time_of_day_update_timer += dtime;
379 //infostream<<"Client steps "<<dtime<<std::endl;
382 //TimeTaker timer("ReceiveAll()", m_device);
391 float &counter = m_packetcounter_timer;
397 infostream<<"Client packetcounter (20s):"<<std::endl;
398 m_packetcounter.print(infostream);
399 m_packetcounter.clear();
403 // Get connection status
404 bool connected = connectedAndInitialized();
409 Delete unused sectors
411 NOTE: This jams the game for a while because deleting sectors
415 float &counter = m_delete_unused_sectors_timer;
423 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
425 core::list<v3s16> deleted_blocks;
427 float delete_unused_sectors_timeout =
428 g_settings->getFloat("client_delete_unused_sectors_timeout");
430 // Delete sector blocks
431 /*u32 num = m_env.getMap().unloadUnusedData
432 (delete_unused_sectors_timeout,
433 true, &deleted_blocks);*/
435 // Delete whole sectors
436 m_env.getMap().unloadUnusedData
437 (delete_unused_sectors_timeout,
440 if(deleted_blocks.size() > 0)
442 /*infostream<<"Client: Deleted blocks of "<<num
443 <<" unused sectors"<<std::endl;*/
444 /*infostream<<"Client: Deleted "<<num
445 <<" unused sectors"<<std::endl;*/
451 // Env is locked so con can be locked.
452 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
454 core::list<v3s16>::Iterator i = deleted_blocks.begin();
455 core::list<v3s16> sendlist;
458 if(sendlist.size() == 255 || i == deleted_blocks.end())
460 if(sendlist.size() == 0)
469 u32 replysize = 2+1+6*sendlist.size();
470 SharedBuffer<u8> reply(replysize);
471 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
472 reply[2] = sendlist.size();
474 for(core::list<v3s16>::Iterator
475 j = sendlist.begin();
476 j != sendlist.end(); j++)
478 writeV3S16(&reply[2+1+6*k], *j);
481 m_con.Send(PEER_ID_SERVER, 1, reply, true);
483 if(i == deleted_blocks.end())
489 sendlist.push_back(*i);
497 if(connected == false)
499 float &counter = m_connection_reinit_timer;
505 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
507 Player *myplayer = m_env.getLocalPlayer();
508 assert(myplayer != NULL);
510 // Send TOSERVER_INIT
511 // [0] u16 TOSERVER_INIT
512 // [2] u8 SER_FMT_VER_HIGHEST_READ
513 // [3] u8[20] player_name
514 // [23] u8[28] password (new in some version)
515 // [51] u16 minimum supported network protocol version (added sometime)
516 // [53] u16 maximum supported network protocol version (added later than the previous one)
517 SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2+2);
518 writeU16(&data[0], TOSERVER_INIT);
519 writeU8(&data[2], SER_FMT_VER_HIGHEST_READ);
521 memset((char*)&data[3], 0, PLAYERNAME_SIZE);
522 snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
524 /*infostream<<"Client: sending initial password hash: \""<<m_password<<"\""
527 memset((char*)&data[23], 0, PASSWORD_SIZE);
528 snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
530 writeU16(&data[51], CLIENT_PROTOCOL_VERSION_MIN);
531 writeU16(&data[53], CLIENT_PROTOCOL_VERSION_MAX);
533 // Send as unreliable
534 Send(1, data, false);
537 // Not connected, return
542 Do stuff if connected
546 Run Map's timers and unload unused data
548 const float map_timer_and_unload_dtime = 5.25;
549 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
551 ScopeProfiler sp(g_profiler, "Client: map timer and unload");
552 std::list<v3s16> deleted_blocks;
553 m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
554 g_settings->getFloat("client_unload_unused_data_timeout"),
557 /*if(deleted_blocks.size() > 0)
558 infostream<<"Client: Unloaded "<<deleted_blocks.size()
559 <<" unused blocks"<<std::endl;*/
563 NOTE: This loop is intentionally iterated the way it is.
566 std::list<v3s16>::iterator i = deleted_blocks.begin();
567 std::list<v3s16> sendlist;
570 if(sendlist.size() == 255 || i == deleted_blocks.end())
572 if(sendlist.size() == 0)
581 u32 replysize = 2+1+6*sendlist.size();
582 SharedBuffer<u8> reply(replysize);
583 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
584 reply[2] = sendlist.size();
586 for(std::list<v3s16>::iterator
587 j = sendlist.begin();
588 j != sendlist.end(); ++j)
590 writeV3S16(&reply[2+1+6*k], *j);
593 m_con.Send(PEER_ID_SERVER, 2, reply, true);
595 if(i == deleted_blocks.end())
601 sendlist.push_back(*i);
611 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
613 // Control local player (0ms)
614 LocalPlayer *player = m_env.getLocalPlayer();
615 assert(player != NULL);
616 player->applyControl(dtime);
618 //TimeTaker envtimer("env step", m_device);
627 ClientEnvEvent event = m_env.getClientEvent();
628 if(event.type == CEE_NONE)
632 else if(event.type == CEE_PLAYER_DAMAGE)
634 if(m_ignore_damage_timer <= 0)
636 u8 damage = event.player_damage.amount;
638 if(event.player_damage.send_to_server)
641 // Add to ClientEvent queue
643 event.type = CE_PLAYER_DAMAGE;
644 event.player_damage.amount = damage;
645 m_client_event_queue.push_back(event);
648 else if(event.type == CEE_PLAYER_BREATH)
650 u16 breath = event.player_breath.amount;
660 float &counter = m_avg_rtt_timer;
665 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
666 // connectedAndInitialized() is true, peer exists.
667 float avg_rtt = m_con.GetPeerAvgRTT(PEER_ID_SERVER);
668 infostream<<"Client: avg_rtt="<<avg_rtt<<std::endl;
673 Send player position to server
676 float &counter = m_playerpos_send_timer;
678 if(counter >= m_recommended_send_interval)
686 Replace updated meshes
689 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
691 //TimeTaker timer("** Processing mesh update result queue");
694 /*infostream<<"Mesh update result queue size is "
695 <<m_mesh_update_thread.m_queue_out.size()
698 int num_processed_meshes = 0;
699 while(!m_mesh_update_thread.m_queue_out.empty())
701 num_processed_meshes++;
702 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_frontNoEx();
703 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
706 //JMutexAutoLock lock(block->mesh_mutex);
708 // Delete the old mesh
709 if(block->mesh != NULL)
711 // TODO: Remove hardware buffers of meshbuffers of block->mesh
716 // Replace with the new mesh
717 block->mesh = r.mesh;
721 if(r.ack_block_to_server)
723 /*infostream<<"Client: ACK block ("<<r.p.X<<","<<r.p.Y
724 <<","<<r.p.Z<<")"<<std::endl;*/
735 u32 replysize = 2+1+6;
736 SharedBuffer<u8> reply(replysize);
737 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
739 writeV3S16(&reply[3], r.p);
741 m_con.Send(PEER_ID_SERVER, 2, reply, true);
744 if(num_processed_meshes > 0)
745 g_profiler->graphAdd("num_processed_meshes", num_processed_meshes);
751 if (m_media_downloader && m_media_downloader->isStarted()) {
752 m_media_downloader->step(this);
753 if (m_media_downloader->isDone()) {
755 delete m_media_downloader;
756 m_media_downloader = NULL;
761 If the server didn't update the inventory in a while, revert
762 the local inventory (so the player notices the lag problem
763 and knows something is wrong).
765 if(m_inventory_from_server)
767 float interval = 10.0;
768 float count_before = floor(m_inventory_from_server_age / interval);
770 m_inventory_from_server_age += dtime;
772 float count_after = floor(m_inventory_from_server_age / interval);
774 if(count_after != count_before)
776 // Do this every <interval> seconds after TOCLIENT_INVENTORY
777 // Reset the locally changed inventory to the authoritative inventory
778 Player *player = m_env.getLocalPlayer();
779 player->inventory = *m_inventory_from_server;
780 m_inventory_updated = true;
785 Update positions of sounds attached to objects
788 for(std::map<int, u16>::iterator
789 i = m_sounds_to_objects.begin();
790 i != m_sounds_to_objects.end(); i++)
792 int client_id = i->first;
793 u16 object_id = i->second;
794 ClientActiveObject *cao = m_env.getActiveObject(object_id);
797 v3f pos = cao->getPosition();
798 m_sound->updateSoundPosition(client_id, pos);
803 Handle removed remotely initiated sounds
805 m_removed_sounds_check_timer += dtime;
806 if(m_removed_sounds_check_timer >= 2.32)
808 m_removed_sounds_check_timer = 0;
809 // Find removed sounds and clear references to them
810 std::set<s32> removed_server_ids;
811 for(std::map<s32, int>::iterator
812 i = m_sounds_server_to_client.begin();
813 i != m_sounds_server_to_client.end();)
815 s32 server_id = i->first;
816 int client_id = i->second;
818 if(!m_sound->soundExists(client_id)){
819 m_sounds_server_to_client.erase(server_id);
820 m_sounds_client_to_server.erase(client_id);
821 m_sounds_to_objects.erase(client_id);
822 removed_server_ids.insert(server_id);
826 if(removed_server_ids.size() != 0)
828 std::ostringstream os(std::ios_base::binary);
829 writeU16(os, TOSERVER_REMOVED_SOUNDS);
830 writeU16(os, removed_server_ids.size());
831 for(std::set<s32>::iterator i = removed_server_ids.begin();
832 i != removed_server_ids.end(); i++)
834 std::string s = os.str();
835 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
842 bool Client::loadMedia(const std::string &data, const std::string &filename)
844 // Silly irrlicht's const-incorrectness
845 Buffer<char> data_rw(data.c_str(), data.size());
849 const char *image_ext[] = {
850 ".png", ".jpg", ".bmp", ".tga",
851 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
854 name = removeStringEnd(filename, image_ext);
857 verbosestream<<"Client: Attempting to load image "
858 <<"file \""<<filename<<"\""<<std::endl;
860 io::IFileSystem *irrfs = m_device->getFileSystem();
861 video::IVideoDriver *vdrv = m_device->getVideoDriver();
863 // Create an irrlicht memory file
864 io::IReadFile *rfile = irrfs->createMemoryReadFile(
865 *data_rw, data_rw.getSize(), "_tempreadfile");
868 video::IImage *img = vdrv->createImageFromFile(rfile);
870 errorstream<<"Client: Cannot create image from data of "
871 <<"file \""<<filename<<"\""<<std::endl;
876 m_tsrc->insertSourceImage(filename, img);
883 const char *sound_ext[] = {
884 ".0.ogg", ".1.ogg", ".2.ogg", ".3.ogg", ".4.ogg",
885 ".5.ogg", ".6.ogg", ".7.ogg", ".8.ogg", ".9.ogg",
888 name = removeStringEnd(filename, sound_ext);
891 verbosestream<<"Client: Attempting to load sound "
892 <<"file \""<<filename<<"\""<<std::endl;
893 m_sound->loadSoundData(name, data);
897 const char *model_ext[] = {
898 ".x", ".b3d", ".md2", ".obj",
901 name = removeStringEnd(filename, model_ext);
904 verbosestream<<"Client: Storing model into memory: "
905 <<"\""<<filename<<"\""<<std::endl;
906 if(m_mesh_data.count(filename))
907 errorstream<<"Multiple models with name \""<<filename.c_str()
908 <<"\" found; replacing previous model"<<std::endl;
909 m_mesh_data[filename] = data;
913 errorstream<<"Client: Don't know how to load file \""
914 <<filename<<"\""<<std::endl;
918 // Virtual methods from con::PeerHandler
919 void Client::peerAdded(con::Peer *peer)
921 infostream<<"Client::peerAdded(): peer->id="
922 <<peer->id<<std::endl;
924 void Client::deletingPeer(con::Peer *peer, bool timeout)
926 infostream<<"Client::deletingPeer(): "
927 "Server Peer is getting deleted "
928 <<"(timeout="<<timeout<<")"<<std::endl;
933 u16 number of files requested
939 void Client::request_media(const std::list<std::string> &file_requests)
941 std::ostringstream os(std::ios_base::binary);
942 writeU16(os, TOSERVER_REQUEST_MEDIA);
943 writeU16(os, file_requests.size());
945 for(std::list<std::string>::const_iterator i = file_requests.begin();
946 i != file_requests.end(); ++i) {
947 os<<serializeString(*i);
951 std::string s = os.str();
952 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
955 infostream<<"Client: Sending media request list to server ("
956 <<file_requests.size()<<" files)"<<std::endl;
959 void Client::received_media()
961 // notify server we received everything
962 std::ostringstream os(std::ios_base::binary);
963 writeU16(os, TOSERVER_RECEIVED_MEDIA);
964 std::string s = os.str();
965 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
968 infostream<<"Client: Notifying server that we received all media"
972 void Client::ReceiveAll()
974 DSTACK(__FUNCTION_NAME);
975 u32 start_ms = porting::getTimeMs();
978 // Limit time even if there would be huge amounts of data to
980 if(porting::getTimeMs() > start_ms + 100)
985 g_profiler->graphAdd("client_received_packets", 1);
987 catch(con::NoIncomingDataException &e)
991 catch(con::InvalidIncomingDataException &e)
993 infostream<<"Client::ReceiveAll(): "
994 "InvalidIncomingDataException: what()="
995 <<e.what()<<std::endl;
1000 void Client::Receive()
1002 DSTACK(__FUNCTION_NAME);
1003 SharedBuffer<u8> data;
1007 //TimeTaker t1("con mutex and receive", m_device);
1008 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1009 datasize = m_con.Receive(sender_peer_id, data);
1011 //TimeTaker t1("ProcessData", m_device);
1012 ProcessData(*data, datasize, sender_peer_id);
1016 sender_peer_id given to this shall be quaranteed to be a valid peer
1018 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
1020 DSTACK(__FUNCTION_NAME);
1022 // Ignore packets that don't even fit a command
1025 m_packetcounter.add(60000);
1029 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
1031 //infostream<<"Client: received command="<<command<<std::endl;
1032 m_packetcounter.add((u16)command);
1035 If this check is removed, be sure to change the queue
1036 system to know the ids
1038 if(sender_peer_id != PEER_ID_SERVER)
1040 infostream<<"Client::ProcessData(): Discarding data not "
1041 "coming from server: peer_id="<<sender_peer_id
1046 u8 ser_version = m_server_ser_ver;
1048 //infostream<<"Client received command="<<(int)command<<std::endl;
1050 if(command == TOCLIENT_INIT)
1055 u8 deployed = data[2];
1057 infostream<<"Client: TOCLIENT_INIT received with "
1058 "deployed="<<((int)deployed&0xff)<<std::endl;
1060 if(!ser_ver_supported(deployed))
1062 infostream<<"Client: TOCLIENT_INIT: Server sent "
1063 <<"unsupported ser_fmt_ver"<<std::endl;
1067 m_server_ser_ver = deployed;
1069 // Get player position
1070 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
1071 if(datasize >= 2+1+6)
1072 playerpos_s16 = readV3S16(&data[2+1]);
1073 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
1076 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1078 // Set player position
1079 Player *player = m_env.getLocalPlayer();
1080 assert(player != NULL);
1081 player->setPosition(playerpos_f);
1084 if(datasize >= 2+1+6+8)
1087 m_map_seed = readU64(&data[2+1+6]);
1088 infostream<<"Client: received map seed: "<<m_map_seed<<std::endl;
1091 if(datasize >= 2+1+6+8+4)
1094 m_recommended_send_interval = readF1000(&data[2+1+6+8]);
1095 infostream<<"Client: received recommended send interval "
1096 <<m_recommended_send_interval<<std::endl;
1101 SharedBuffer<u8> reply(replysize);
1102 writeU16(&reply[0], TOSERVER_INIT2);
1104 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1109 if(command == TOCLIENT_ACCESS_DENIED)
1111 // The server didn't like our password. Note, this needs
1112 // to be processed even if the serialisation format has
1113 // not been agreed yet, the same as TOCLIENT_INIT.
1114 m_access_denied = true;
1115 m_access_denied_reason = L"Unknown";
1118 std::string datastring((char*)&data[2], datasize-2);
1119 std::istringstream is(datastring, std::ios_base::binary);
1120 m_access_denied_reason = deSerializeWideString(is);
1125 if(ser_version == SER_FMT_VER_INVALID)
1127 infostream<<"Client: Server serialization"
1128 " format invalid or not initialized."
1129 " Skipping incoming command="<<command<<std::endl;
1133 // Just here to avoid putting the two if's together when
1134 // making some copypasta
1137 if(command == TOCLIENT_REMOVENODE)
1142 p.X = readS16(&data[2]);
1143 p.Y = readS16(&data[4]);
1144 p.Z = readS16(&data[6]);
1146 //TimeTaker t1("TOCLIENT_REMOVENODE");
1150 else if(command == TOCLIENT_ADDNODE)
1152 if(datasize < 8 + MapNode::serializedLength(ser_version))
1156 p.X = readS16(&data[2]);
1157 p.Y = readS16(&data[4]);
1158 p.Z = readS16(&data[6]);
1160 //TimeTaker t1("TOCLIENT_ADDNODE");
1163 n.deSerialize(&data[8], ser_version);
1165 bool remove_metadata = true;
1166 u32 index = 8 + MapNode::serializedLength(ser_version);
1167 if ((datasize >= index+1) && data[index]){
1168 remove_metadata = false;
1171 addNode(p, n, remove_metadata);
1173 else if(command == TOCLIENT_BLOCKDATA)
1175 // Ignore too small packet
1180 p.X = readS16(&data[2]);
1181 p.Y = readS16(&data[4]);
1182 p.Z = readS16(&data[6]);
1184 /*infostream<<"Client: Thread: BLOCKDATA for ("
1185 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1186 /*infostream<<"Client: Thread: BLOCKDATA for ("
1187 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1189 std::string datastring((char*)&data[8], datasize-8);
1190 std::istringstream istr(datastring, std::ios_base::binary);
1195 v2s16 p2d(p.X, p.Z);
1196 sector = m_env.getMap().emergeSector(p2d);
1198 assert(sector->getPos() == p2d);
1200 //TimeTaker timer("MapBlock deSerialize");
1203 block = sector->getBlockNoCreateNoEx(p.Y);
1207 Update an existing block
1209 //infostream<<"Updating"<<std::endl;
1210 block->deSerialize(istr, ser_version, false);
1211 block->deSerializeNetworkSpecific(istr);
1218 //infostream<<"Creating new"<<std::endl;
1219 block = new MapBlock(&m_env.getMap(), p, this);
1220 block->deSerialize(istr, ser_version, false);
1221 block->deSerializeNetworkSpecific(istr);
1222 sector->insertBlock(block);
1236 u32 replysize = 2+1+6;
1237 SharedBuffer<u8> reply(replysize);
1238 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
1240 writeV3S16(&reply[3], p);
1242 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1246 Add it to mesh update queue and set it to be acknowledged after update.
1248 //infostream<<"Adding mesh update task for received block"<<std::endl;
1249 addUpdateMeshTaskWithEdge(p, true);
1251 else if(command == TOCLIENT_INVENTORY)
1256 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
1259 //TimeTaker t2("mutex locking", m_device);
1260 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1263 //TimeTaker t3("istringstream init", m_device);
1264 std::string datastring((char*)&data[2], datasize-2);
1265 std::istringstream is(datastring, std::ios_base::binary);
1268 //TimeTaker t4("player get", m_device);
1269 Player *player = m_env.getLocalPlayer();
1270 assert(player != NULL);
1273 //TimeTaker t1("inventory.deSerialize()", m_device);
1274 player->inventory.deSerialize(is);
1277 m_inventory_updated = true;
1279 delete m_inventory_from_server;
1280 m_inventory_from_server = new Inventory(player->inventory);
1281 m_inventory_from_server_age = 0.0;
1283 //infostream<<"Client got player inventory:"<<std::endl;
1284 //player->inventory.print(infostream);
1287 else if(command == TOCLIENT_TIME_OF_DAY)
1292 u16 time_of_day = readU16(&data[2]);
1293 time_of_day = time_of_day % 24000;
1294 //infostream<<"Client: time_of_day="<<time_of_day<<std::endl;
1295 float time_speed = 0;
1296 if(datasize >= 2 + 2 + 4){
1297 time_speed = readF1000(&data[4]);
1299 // Old message; try to approximate speed of time by ourselves
1300 float time_of_day_f = (float)time_of_day / 24000.0;
1301 float tod_diff_f = 0;
1302 if(time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
1303 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0;
1305 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
1306 m_last_time_of_day_f = time_of_day_f;
1307 float time_diff = m_time_of_day_update_timer;
1308 m_time_of_day_update_timer = 0;
1309 if(m_time_of_day_set){
1310 time_speed = 3600.0*24.0 * tod_diff_f / time_diff;
1311 infostream<<"Client: Measured time_of_day speed (old format): "
1312 <<time_speed<<" tod_diff_f="<<tod_diff_f
1313 <<" time_diff="<<time_diff<<std::endl;
1317 // Update environment
1318 m_env.setTimeOfDay(time_of_day);
1319 m_env.setTimeOfDaySpeed(time_speed);
1320 m_time_of_day_set = true;
1322 u32 dr = m_env.getDayNightRatio();
1323 verbosestream<<"Client: time_of_day="<<time_of_day
1324 <<" time_speed="<<time_speed
1325 <<" dr="<<dr<<std::endl;
1327 else if(command == TOCLIENT_CHAT_MESSAGE)
1335 std::string datastring((char*)&data[2], datasize-2);
1336 std::istringstream is(datastring, std::ios_base::binary);
1339 is.read((char*)buf, 2);
1340 u16 len = readU16(buf);
1342 std::wstring message;
1343 for(u16 i=0; i<len; i++)
1345 is.read((char*)buf, 2);
1346 message += (wchar_t)readU16(buf);
1349 /*infostream<<"Client received chat message: "
1350 <<wide_to_narrow(message)<<std::endl;*/
1352 m_chat_queue.push_back(message);
1354 else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1356 //if(g_settings->getBool("enable_experimental"))
1360 u16 count of removed objects
1361 for all removed objects {
1364 u16 count of added objects
1365 for all added objects {
1368 u32 initialization data length
1369 string initialization data
1374 // Get all data except the command number
1375 std::string datastring((char*)&data[2], datasize-2);
1376 // Throw them in an istringstream
1377 std::istringstream is(datastring, std::ios_base::binary);
1381 // Read removed objects
1383 u16 removed_count = readU16((u8*)buf);
1384 for(u16 i=0; i<removed_count; i++)
1387 u16 id = readU16((u8*)buf);
1390 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1391 m_env.removeActiveObject(id);
1395 // Read added objects
1397 u16 added_count = readU16((u8*)buf);
1398 for(u16 i=0; i<added_count; i++)
1401 u16 id = readU16((u8*)buf);
1403 u8 type = readU8((u8*)buf);
1404 std::string data = deSerializeLongString(is);
1407 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1408 m_env.addActiveObject(id, type, data);
1413 else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1415 //if(g_settings->getBool("enable_experimental"))
1427 // Get all data except the command number
1428 std::string datastring((char*)&data[2], datasize-2);
1429 // Throw them in an istringstream
1430 std::istringstream is(datastring, std::ios_base::binary);
1432 while(is.eof() == false)
1436 u16 id = readU16((u8*)buf);
1440 u16 message_size = readU16((u8*)buf);
1441 std::string message;
1442 message.reserve(message_size);
1443 for(u16 i=0; i<message_size; i++)
1446 message.append(buf, 1);
1448 // Pass on to the environment
1450 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1451 m_env.processActiveObjectMessage(id, message);
1456 else if(command == TOCLIENT_MOVEMENT)
1458 std::string datastring((char*)&data[2], datasize-2);
1459 std::istringstream is(datastring, std::ios_base::binary);
1460 Player *player = m_env.getLocalPlayer();
1461 assert(player != NULL);
1463 player->movement_acceleration_default = readF1000(is) * BS;
1464 player->movement_acceleration_air = readF1000(is) * BS;
1465 player->movement_acceleration_fast = readF1000(is) * BS;
1466 player->movement_speed_walk = readF1000(is) * BS;
1467 player->movement_speed_crouch = readF1000(is) * BS;
1468 player->movement_speed_fast = readF1000(is) * BS;
1469 player->movement_speed_climb = readF1000(is) * BS;
1470 player->movement_speed_jump = readF1000(is) * BS;
1471 player->movement_liquid_fluidity = readF1000(is) * BS;
1472 player->movement_liquid_fluidity_smooth = readF1000(is) * BS;
1473 player->movement_liquid_sink = readF1000(is) * BS;
1474 player->movement_gravity = readF1000(is) * BS;
1476 else if(command == TOCLIENT_HP)
1478 std::string datastring((char*)&data[2], datasize-2);
1479 std::istringstream is(datastring, std::ios_base::binary);
1480 Player *player = m_env.getLocalPlayer();
1481 assert(player != NULL);
1482 u8 oldhp = player->hp;
1488 // Add to ClientEvent queue
1490 event.type = CE_PLAYER_DAMAGE;
1491 event.player_damage.amount = oldhp - hp;
1492 m_client_event_queue.push_back(event);
1495 else if(command == TOCLIENT_BREATH)
1497 std::string datastring((char*)&data[2], datasize-2);
1498 std::istringstream is(datastring, std::ios_base::binary);
1499 Player *player = m_env.getLocalPlayer();
1500 assert(player != NULL);
1501 u16 breath = readU16(is);
1502 player->setBreath(breath) ;
1504 else if(command == TOCLIENT_MOVE_PLAYER)
1506 std::string datastring((char*)&data[2], datasize-2);
1507 std::istringstream is(datastring, std::ios_base::binary);
1508 Player *player = m_env.getLocalPlayer();
1509 assert(player != NULL);
1510 v3f pos = readV3F1000(is);
1511 f32 pitch = readF1000(is);
1512 f32 yaw = readF1000(is);
1513 player->setPosition(pos);
1514 /*player->setPitch(pitch);
1515 player->setYaw(yaw);*/
1517 infostream<<"Client got TOCLIENT_MOVE_PLAYER"
1518 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1524 Add to ClientEvent queue.
1525 This has to be sent to the main program because otherwise
1526 it would just force the pitch and yaw values to whatever
1527 the camera points to.
1530 event.type = CE_PLAYER_FORCE_MOVE;
1531 event.player_force_move.pitch = pitch;
1532 event.player_force_move.yaw = yaw;
1533 m_client_event_queue.push_back(event);
1535 // Ignore damage for a few seconds, so that the player doesn't
1536 // get damage from falling on ground
1537 m_ignore_damage_timer = 3.0;
1539 else if(command == TOCLIENT_PLAYERITEM)
1541 infostream<<"Client: WARNING: Ignoring TOCLIENT_PLAYERITEM"<<std::endl;
1543 else if(command == TOCLIENT_DEATHSCREEN)
1545 std::string datastring((char*)&data[2], datasize-2);
1546 std::istringstream is(datastring, std::ios_base::binary);
1548 bool set_camera_point_target = readU8(is);
1549 v3f camera_point_target = readV3F1000(is);
1552 event.type = CE_DEATHSCREEN;
1553 event.deathscreen.set_camera_point_target = set_camera_point_target;
1554 event.deathscreen.camera_point_target_x = camera_point_target.X;
1555 event.deathscreen.camera_point_target_y = camera_point_target.Y;
1556 event.deathscreen.camera_point_target_z = camera_point_target.Z;
1557 m_client_event_queue.push_back(event);
1559 else if(command == TOCLIENT_ANNOUNCE_MEDIA)
1561 std::string datastring((char*)&data[2], datasize-2);
1562 std::istringstream is(datastring, std::ios_base::binary);
1564 int num_files = readU16(is);
1566 infostream<<"Client: Received media announcement: packet size: "
1567 <<datasize<<std::endl;
1569 if (m_media_downloader == NULL ||
1570 m_media_downloader->isStarted()) {
1571 const char *problem = m_media_downloader ?
1572 "we already saw another announcement" :
1573 "all media has been received already";
1574 errorstream<<"Client: Received media announcement but "
1576 <<" files="<<num_files
1577 <<" size="<<datasize<<std::endl;
1581 // Mesh update thread must be stopped while
1582 // updating content definitions
1583 assert(!m_mesh_update_thread.IsRunning());
1585 for(int i=0; i<num_files; i++)
1587 std::string name = deSerializeString(is);
1588 std::string sha1_base64 = deSerializeString(is);
1589 std::string sha1_raw = base64_decode(sha1_base64);
1590 m_media_downloader->addFile(name, sha1_raw);
1593 std::vector<std::string> remote_media;
1595 Strfnd sf(deSerializeString(is));
1596 while(!sf.atend()) {
1597 std::string baseurl = trim(sf.next(","));
1599 m_media_downloader->addRemoteServer(baseurl);
1602 catch(SerializationError) {
1603 // not supported by server or turned off
1606 m_media_downloader->step(this);
1608 else if(command == TOCLIENT_MEDIA)
1610 std::string datastring((char*)&data[2], datasize-2);
1611 std::istringstream is(datastring, std::ios_base::binary);
1615 u16 total number of file bunches
1616 u16 index of this bunch
1617 u32 number of files in this bunch
1625 int num_bunches = readU16(is);
1626 int bunch_i = readU16(is);
1627 u32 num_files = readU32(is);
1628 infostream<<"Client: Received files: bunch "<<bunch_i<<"/"
1629 <<num_bunches<<" files="<<num_files
1630 <<" size="<<datasize<<std::endl;
1635 if (m_media_downloader == NULL ||
1636 !m_media_downloader->isStarted()) {
1637 const char *problem = m_media_downloader ?
1638 "media has not been requested" :
1639 "all media has been received already";
1640 errorstream<<"Client: Received media but "
1642 <<" bunch "<<bunch_i<<"/"<<num_bunches
1643 <<" files="<<num_files
1644 <<" size="<<datasize<<std::endl;
1648 // Mesh update thread must be stopped while
1649 // updating content definitions
1650 assert(!m_mesh_update_thread.IsRunning());
1652 for(u32 i=0; i<num_files; i++){
1653 std::string name = deSerializeString(is);
1654 std::string data = deSerializeLongString(is);
1655 m_media_downloader->conventionalTransferDone(
1659 else if(command == TOCLIENT_TOOLDEF)
1661 infostream<<"Client: WARNING: Ignoring TOCLIENT_TOOLDEF"<<std::endl;
1663 else if(command == TOCLIENT_NODEDEF)
1665 infostream<<"Client: Received node definitions: packet size: "
1666 <<datasize<<std::endl;
1668 // Mesh update thread must be stopped while
1669 // updating content definitions
1670 assert(!m_mesh_update_thread.IsRunning());
1672 // Decompress node definitions
1673 std::string datastring((char*)&data[2], datasize-2);
1674 std::istringstream is(datastring, std::ios_base::binary);
1675 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1676 std::ostringstream tmp_os;
1677 decompressZlib(tmp_is, tmp_os);
1679 // Deserialize node definitions
1680 std::istringstream tmp_is2(tmp_os.str());
1681 m_nodedef->deSerialize(tmp_is2);
1682 m_nodedef_received = true;
1684 else if(command == TOCLIENT_CRAFTITEMDEF)
1686 infostream<<"Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF"<<std::endl;
1688 else if(command == TOCLIENT_ITEMDEF)
1690 infostream<<"Client: Received item definitions: packet size: "
1691 <<datasize<<std::endl;
1693 // Mesh update thread must be stopped while
1694 // updating content definitions
1695 assert(!m_mesh_update_thread.IsRunning());
1697 // Decompress item definitions
1698 std::string datastring((char*)&data[2], datasize-2);
1699 std::istringstream is(datastring, std::ios_base::binary);
1700 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1701 std::ostringstream tmp_os;
1702 decompressZlib(tmp_is, tmp_os);
1704 // Deserialize node definitions
1705 std::istringstream tmp_is2(tmp_os.str());
1706 m_itemdef->deSerialize(tmp_is2);
1707 m_itemdef_received = true;
1709 else if(command == TOCLIENT_PLAY_SOUND)
1711 std::string datastring((char*)&data[2], datasize-2);
1712 std::istringstream is(datastring, std::ios_base::binary);
1714 s32 server_id = readS32(is);
1715 std::string name = deSerializeString(is);
1716 float gain = readF1000(is);
1717 int type = readU8(is); // 0=local, 1=positional, 2=object
1718 v3f pos = readV3F1000(is);
1719 u16 object_id = readU16(is);
1720 bool loop = readU8(is);
1725 client_id = m_sound->playSound(name, loop, gain);
1727 case 1: // positional
1728 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1731 ClientActiveObject *cao = m_env.getActiveObject(object_id);
1733 pos = cao->getPosition();
1734 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1735 // TODO: Set up sound to move with object
1740 if(client_id != -1){
1741 m_sounds_server_to_client[server_id] = client_id;
1742 m_sounds_client_to_server[client_id] = server_id;
1744 m_sounds_to_objects[client_id] = object_id;
1747 else if(command == TOCLIENT_STOP_SOUND)
1749 std::string datastring((char*)&data[2], datasize-2);
1750 std::istringstream is(datastring, std::ios_base::binary);
1752 s32 server_id = readS32(is);
1753 std::map<s32, int>::iterator i =
1754 m_sounds_server_to_client.find(server_id);
1755 if(i != m_sounds_server_to_client.end()){
1756 int client_id = i->second;
1757 m_sound->stopSound(client_id);
1760 else if(command == TOCLIENT_PRIVILEGES)
1762 std::string datastring((char*)&data[2], datasize-2);
1763 std::istringstream is(datastring, std::ios_base::binary);
1765 m_privileges.clear();
1766 infostream<<"Client: Privileges updated: ";
1767 u16 num_privileges = readU16(is);
1768 for(u16 i=0; i<num_privileges; i++){
1769 std::string priv = deSerializeString(is);
1770 m_privileges.insert(priv);
1771 infostream<<priv<<" ";
1773 infostream<<std::endl;
1775 else if(command == TOCLIENT_INVENTORY_FORMSPEC)
1777 std::string datastring((char*)&data[2], datasize-2);
1778 std::istringstream is(datastring, std::ios_base::binary);
1780 // Store formspec in LocalPlayer
1781 Player *player = m_env.getLocalPlayer();
1782 assert(player != NULL);
1783 player->inventory_formspec = deSerializeLongString(is);
1785 else if(command == TOCLIENT_DETACHED_INVENTORY)
1787 std::string datastring((char*)&data[2], datasize-2);
1788 std::istringstream is(datastring, std::ios_base::binary);
1790 std::string name = deSerializeString(is);
1792 infostream<<"Client: Detached inventory update: \""<<name<<"\""<<std::endl;
1794 Inventory *inv = NULL;
1795 if(m_detached_inventories.count(name) > 0)
1796 inv = m_detached_inventories[name];
1798 inv = new Inventory(m_itemdef);
1799 m_detached_inventories[name] = inv;
1801 inv->deSerialize(is);
1803 else if(command == TOCLIENT_SHOW_FORMSPEC)
1805 std::string datastring((char*)&data[2], datasize-2);
1806 std::istringstream is(datastring, std::ios_base::binary);
1808 std::string formspec = deSerializeLongString(is);
1809 std::string formname = deSerializeString(is);
1812 event.type = CE_SHOW_FORMSPEC;
1813 // pointer is required as event is a struct only!
1814 // adding a std:string to a struct isn't possible
1815 event.show_formspec.formspec = new std::string(formspec);
1816 event.show_formspec.formname = new std::string(formname);
1817 m_client_event_queue.push_back(event);
1819 else if(command == TOCLIENT_SPAWN_PARTICLE)
1821 std::string datastring((char*)&data[2], datasize-2);
1822 std::istringstream is(datastring, std::ios_base::binary);
1824 v3f pos = readV3F1000(is);
1825 v3f vel = readV3F1000(is);
1826 v3f acc = readV3F1000(is);
1827 float expirationtime = readF1000(is);
1828 float size = readF1000(is);
1829 bool collisiondetection = readU8(is);
1830 std::string texture = deSerializeLongString(is);
1831 bool vertical = false;
1833 vertical = readU8(is);
1837 event.type = CE_SPAWN_PARTICLE;
1838 event.spawn_particle.pos = new v3f (pos);
1839 event.spawn_particle.vel = new v3f (vel);
1840 event.spawn_particle.acc = new v3f (acc);
1842 event.spawn_particle.expirationtime = expirationtime;
1843 event.spawn_particle.size = size;
1844 event.spawn_particle.collisiondetection =
1846 event.spawn_particle.vertical = vertical;
1847 event.spawn_particle.texture = new std::string(texture);
1849 m_client_event_queue.push_back(event);
1851 else if(command == TOCLIENT_ADD_PARTICLESPAWNER)
1853 std::string datastring((char*)&data[2], datasize-2);
1854 std::istringstream is(datastring, std::ios_base::binary);
1856 u16 amount = readU16(is);
1857 float spawntime = readF1000(is);
1858 v3f minpos = readV3F1000(is);
1859 v3f maxpos = readV3F1000(is);
1860 v3f minvel = readV3F1000(is);
1861 v3f maxvel = readV3F1000(is);
1862 v3f minacc = readV3F1000(is);
1863 v3f maxacc = readV3F1000(is);
1864 float minexptime = readF1000(is);
1865 float maxexptime = readF1000(is);
1866 float minsize = readF1000(is);
1867 float maxsize = readF1000(is);
1868 bool collisiondetection = readU8(is);
1869 std::string texture = deSerializeLongString(is);
1870 u32 id = readU32(is);
1871 bool vertical = false;
1873 vertical = readU8(is);
1877 event.type = CE_ADD_PARTICLESPAWNER;
1878 event.add_particlespawner.amount = amount;
1879 event.add_particlespawner.spawntime = spawntime;
1881 event.add_particlespawner.minpos = new v3f (minpos);
1882 event.add_particlespawner.maxpos = new v3f (maxpos);
1883 event.add_particlespawner.minvel = new v3f (minvel);
1884 event.add_particlespawner.maxvel = new v3f (maxvel);
1885 event.add_particlespawner.minacc = new v3f (minacc);
1886 event.add_particlespawner.maxacc = new v3f (maxacc);
1888 event.add_particlespawner.minexptime = minexptime;
1889 event.add_particlespawner.maxexptime = maxexptime;
1890 event.add_particlespawner.minsize = minsize;
1891 event.add_particlespawner.maxsize = maxsize;
1892 event.add_particlespawner.collisiondetection = collisiondetection;
1893 event.add_particlespawner.vertical = vertical;
1894 event.add_particlespawner.texture = new std::string(texture);
1895 event.add_particlespawner.id = id;
1897 m_client_event_queue.push_back(event);
1899 else if(command == TOCLIENT_DELETE_PARTICLESPAWNER)
1901 std::string datastring((char*)&data[2], datasize-2);
1902 std::istringstream is(datastring, std::ios_base::binary);
1904 u32 id = readU16(is);
1907 event.type = CE_DELETE_PARTICLESPAWNER;
1908 event.delete_particlespawner.id = id;
1910 m_client_event_queue.push_back(event);
1912 else if(command == TOCLIENT_HUDADD)
1914 std::string datastring((char *)&data[2], datasize - 2);
1915 std::istringstream is(datastring, std::ios_base::binary);
1917 u32 id = readU32(is);
1918 u8 type = readU8(is);
1919 v2f pos = readV2F1000(is);
1920 std::string name = deSerializeString(is);
1921 v2f scale = readV2F1000(is);
1922 std::string text = deSerializeString(is);
1923 u32 number = readU32(is);
1924 u32 item = readU32(is);
1925 u32 dir = readU32(is);
1926 v2f align = readV2F1000(is);
1927 v2f offset = readV2F1000(is);
1930 world_pos = readV3F1000(is);
1931 }catch(SerializationError &e) {};
1934 event.type = CE_HUDADD;
1935 event.hudadd.id = id;
1936 event.hudadd.type = type;
1937 event.hudadd.pos = new v2f(pos);
1938 event.hudadd.name = new std::string(name);
1939 event.hudadd.scale = new v2f(scale);
1940 event.hudadd.text = new std::string(text);
1941 event.hudadd.number = number;
1942 event.hudadd.item = item;
1943 event.hudadd.dir = dir;
1944 event.hudadd.align = new v2f(align);
1945 event.hudadd.offset = new v2f(offset);
1946 event.hudadd.world_pos = new v3f(world_pos);
1947 m_client_event_queue.push_back(event);
1949 else if(command == TOCLIENT_HUDRM)
1951 std::string datastring((char *)&data[2], datasize - 2);
1952 std::istringstream is(datastring, std::ios_base::binary);
1954 u32 id = readU32(is);
1957 event.type = CE_HUDRM;
1958 event.hudrm.id = id;
1959 m_client_event_queue.push_back(event);
1961 else if(command == TOCLIENT_HUDCHANGE)
1968 std::string datastring((char *)&data[2], datasize - 2);
1969 std::istringstream is(datastring, std::ios_base::binary);
1971 u32 id = readU32(is);
1972 u8 stat = (HudElementStat)readU8(is);
1974 if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
1975 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
1976 v2fdata = readV2F1000(is);
1977 else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
1978 sdata = deSerializeString(is);
1979 else if (stat == HUD_STAT_WORLD_POS)
1980 v3fdata = readV3F1000(is);
1982 intdata = readU32(is);
1985 event.type = CE_HUDCHANGE;
1986 event.hudchange.id = id;
1987 event.hudchange.stat = (HudElementStat)stat;
1988 event.hudchange.v2fdata = new v2f(v2fdata);
1989 event.hudchange.v3fdata = new v3f(v3fdata);
1990 event.hudchange.sdata = new std::string(sdata);
1991 event.hudchange.data = intdata;
1992 m_client_event_queue.push_back(event);
1994 else if(command == TOCLIENT_HUD_SET_FLAGS)
1996 std::string datastring((char *)&data[2], datasize - 2);
1997 std::istringstream is(datastring, std::ios_base::binary);
1999 Player *player = m_env.getLocalPlayer();
2000 assert(player != NULL);
2002 u32 flags = readU32(is);
2003 u32 mask = readU32(is);
2005 player->hud_flags &= ~mask;
2006 player->hud_flags |= flags;
2008 else if(command == TOCLIENT_HUD_SET_PARAM)
2010 std::string datastring((char *)&data[2], datasize - 2);
2011 std::istringstream is(datastring, std::ios_base::binary);
2013 Player *player = m_env.getLocalPlayer();
2014 assert(player != NULL);
2016 u16 param = readU16(is);
2017 std::string value = deSerializeString(is);
2019 if(param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4){
2020 s32 hotbar_itemcount = readS32((u8*) value.c_str());
2021 if(hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
2022 player->hud_hotbar_itemcount = hotbar_itemcount;
2023 } else if (param == HUD_PARAM_HOTBAR_IMAGE) {
2024 ((LocalPlayer *) player)->hotbar_image = value;
2025 } else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
2026 ((LocalPlayer *) player)->hotbar_selected_image = value;
2031 infostream<<"Client: Ignoring unknown command "
2032 <<command<<std::endl;
2036 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
2038 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2039 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
2042 void Client::interact(u8 action, const PointedThing& pointed)
2044 if(connectedAndInitialized() == false){
2045 infostream<<"Client::interact() "
2046 "cancelled (not connected)"
2051 std::ostringstream os(std::ios_base::binary);
2057 [5] u32 length of the next item
2058 [9] serialized PointedThing
2060 0: start digging (from undersurface) or use
2061 1: stop digging (all parameters ignored)
2062 2: digging completed
2063 3: place block or item (to abovesurface)
2066 writeU16(os, TOSERVER_INTERACT);
2067 writeU8(os, action);
2068 writeU16(os, getPlayerItem());
2069 std::ostringstream tmp_os(std::ios::binary);
2070 pointed.serialize(tmp_os);
2071 os<<serializeLongString(tmp_os.str());
2073 std::string s = os.str();
2074 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2077 Send(0, data, true);
2080 void Client::sendNodemetaFields(v3s16 p, const std::string &formname,
2081 const std::map<std::string, std::string> &fields)
2083 std::ostringstream os(std::ios_base::binary);
2085 writeU16(os, TOSERVER_NODEMETA_FIELDS);
2087 os<<serializeString(formname);
2088 writeU16(os, fields.size());
2089 for(std::map<std::string, std::string>::const_iterator
2090 i = fields.begin(); i != fields.end(); i++){
2091 const std::string &name = i->first;
2092 const std::string &value = i->second;
2093 os<<serializeString(name);
2094 os<<serializeLongString(value);
2098 std::string s = os.str();
2099 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2101 Send(0, data, true);
2104 void Client::sendInventoryFields(const std::string &formname,
2105 const std::map<std::string, std::string> &fields)
2107 std::ostringstream os(std::ios_base::binary);
2109 writeU16(os, TOSERVER_INVENTORY_FIELDS);
2110 os<<serializeString(formname);
2111 writeU16(os, fields.size());
2112 for(std::map<std::string, std::string>::const_iterator
2113 i = fields.begin(); i != fields.end(); i++){
2114 const std::string &name = i->first;
2115 const std::string &value = i->second;
2116 os<<serializeString(name);
2117 os<<serializeLongString(value);
2121 std::string s = os.str();
2122 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2124 Send(0, data, true);
2127 void Client::sendInventoryAction(InventoryAction *a)
2129 std::ostringstream os(std::ios_base::binary);
2133 writeU16(buf, TOSERVER_INVENTORY_ACTION);
2134 os.write((char*)buf, 2);
2139 std::string s = os.str();
2140 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2142 Send(0, data, true);
2145 void Client::sendChatMessage(const std::wstring &message)
2147 std::ostringstream os(std::ios_base::binary);
2151 writeU16(buf, TOSERVER_CHAT_MESSAGE);
2152 os.write((char*)buf, 2);
2155 writeU16(buf, message.size());
2156 os.write((char*)buf, 2);
2159 for(u32 i=0; i<message.size(); i++)
2163 os.write((char*)buf, 2);
2167 std::string s = os.str();
2168 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2170 Send(0, data, true);
2173 void Client::sendChangePassword(const std::wstring oldpassword,
2174 const std::wstring newpassword)
2176 Player *player = m_env.getLocalPlayer();
2180 std::string playername = player->getName();
2181 std::string oldpwd = translatePassword(playername, oldpassword);
2182 std::string newpwd = translatePassword(playername, newpassword);
2184 std::ostringstream os(std::ios_base::binary);
2185 u8 buf[2+PASSWORD_SIZE*2];
2187 [0] u16 TOSERVER_PASSWORD
2188 [2] u8[28] old password
2189 [30] u8[28] new password
2192 writeU16(buf, TOSERVER_PASSWORD);
2193 for(u32 i=0;i<PASSWORD_SIZE-1;i++)
2195 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
2196 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
2198 buf[2+PASSWORD_SIZE-1] = 0;
2199 buf[30+PASSWORD_SIZE-1] = 0;
2200 os.write((char*)buf, 2+PASSWORD_SIZE*2);
2203 std::string s = os.str();
2204 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2206 Send(0, data, true);
2210 void Client::sendDamage(u8 damage)
2212 DSTACK(__FUNCTION_NAME);
2213 std::ostringstream os(std::ios_base::binary);
2215 writeU16(os, TOSERVER_DAMAGE);
2216 writeU8(os, damage);
2219 std::string s = os.str();
2220 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2222 Send(0, data, true);
2225 void Client::sendBreath(u16 breath)
2227 DSTACK(__FUNCTION_NAME);
2228 std::ostringstream os(std::ios_base::binary);
2230 writeU16(os, TOSERVER_BREATH);
2231 writeU16(os, breath);
2233 std::string s = os.str();
2234 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2236 Send(0, data, true);
2239 void Client::sendRespawn()
2241 DSTACK(__FUNCTION_NAME);
2242 std::ostringstream os(std::ios_base::binary);
2244 writeU16(os, TOSERVER_RESPAWN);
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 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2257 LocalPlayer *myplayer = m_env.getLocalPlayer();
2258 if(myplayer == NULL)
2261 // Save bandwidth by only updating position when something changed
2262 if(myplayer->last_position == myplayer->getPosition() &&
2263 myplayer->last_speed == myplayer->getSpeed() &&
2264 myplayer->last_pitch == myplayer->getPitch() &&
2265 myplayer->last_yaw == myplayer->getYaw() &&
2266 myplayer->last_keyPressed == myplayer->keyPressed)
2269 myplayer->last_position = myplayer->getPosition();
2270 myplayer->last_speed = myplayer->getSpeed();
2271 myplayer->last_pitch = myplayer->getPitch();
2272 myplayer->last_yaw = myplayer->getYaw();
2273 myplayer->last_keyPressed = myplayer->keyPressed;
2277 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2278 our_peer_id = m_con.GetPeerID();
2281 // Set peer id if not set already
2282 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2283 myplayer->peer_id = our_peer_id;
2284 // Check that an existing peer_id is the same as the connection's
2285 assert(myplayer->peer_id == our_peer_id);
2287 v3f pf = myplayer->getPosition();
2288 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
2289 v3f sf = myplayer->getSpeed();
2290 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
2291 s32 pitch = myplayer->getPitch() * 100;
2292 s32 yaw = myplayer->getYaw() * 100;
2293 u32 keyPressed=myplayer->keyPressed;
2297 [2] v3s32 position*100
2298 [2+12] v3s32 speed*100
2299 [2+12+12] s32 pitch*100
2300 [2+12+12+4] s32 yaw*100
2301 [2+12+12+4+4] u32 keyPressed
2303 SharedBuffer<u8> data(2+12+12+4+4+4);
2304 writeU16(&data[0], TOSERVER_PLAYERPOS);
2305 writeV3S32(&data[2], position);
2306 writeV3S32(&data[2+12], speed);
2307 writeS32(&data[2+12+12], pitch);
2308 writeS32(&data[2+12+12+4], yaw);
2309 writeU32(&data[2+12+12+4+4], keyPressed);
2310 // Send as unreliable
2311 Send(0, data, false);
2314 void Client::sendPlayerItem(u16 item)
2316 Player *myplayer = m_env.getLocalPlayer();
2317 if(myplayer == NULL)
2320 u16 our_peer_id = m_con.GetPeerID();
2322 // Set peer id if not set already
2323 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2324 myplayer->peer_id = our_peer_id;
2325 // Check that an existing peer_id is the same as the connection's
2326 assert(myplayer->peer_id == our_peer_id);
2328 SharedBuffer<u8> data(2+2);
2329 writeU16(&data[0], TOSERVER_PLAYERITEM);
2330 writeU16(&data[2], item);
2333 Send(0, data, true);
2336 void Client::removeNode(v3s16 p)
2338 std::map<v3s16, MapBlock*> modified_blocks;
2342 //TimeTaker t("removeNodeAndUpdate", m_device);
2343 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
2345 catch(InvalidPositionException &e)
2349 // add urgent task to update the modified node
2350 addUpdateMeshTaskForNode(p, false, true);
2352 for(std::map<v3s16, MapBlock * >::iterator
2353 i = modified_blocks.begin();
2354 i != modified_blocks.end(); ++i)
2356 addUpdateMeshTaskWithEdge(i->first);
2360 void Client::addNode(v3s16 p, MapNode n, bool remove_metadata)
2362 TimeTaker timer1("Client::addNode()");
2364 std::map<v3s16, MapBlock*> modified_blocks;
2368 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
2369 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
2371 catch(InvalidPositionException &e)
2374 for(std::map<v3s16, MapBlock * >::iterator
2375 i = modified_blocks.begin();
2376 i != modified_blocks.end(); ++i)
2378 addUpdateMeshTaskWithEdge(i->first);
2382 void Client::setPlayerControl(PlayerControl &control)
2384 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2385 LocalPlayer *player = m_env.getLocalPlayer();
2386 assert(player != NULL);
2387 player->control = control;
2390 void Client::selectPlayerItem(u16 item)
2392 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2393 m_playeritem = item;
2394 m_inventory_updated = true;
2395 sendPlayerItem(item);
2398 // Returns true if the inventory of the local player has been
2399 // updated from the server. If it is true, it is set to false.
2400 bool Client::getLocalInventoryUpdated()
2402 // m_inventory_updated is behind envlock
2403 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2404 bool updated = m_inventory_updated;
2405 m_inventory_updated = false;
2409 // Copies the inventory of the local player to parameter
2410 void Client::getLocalInventory(Inventory &dst)
2412 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2413 Player *player = m_env.getLocalPlayer();
2414 assert(player != NULL);
2415 dst = player->inventory;
2418 Inventory* Client::getInventory(const InventoryLocation &loc)
2421 case InventoryLocation::UNDEFINED:
2424 case InventoryLocation::CURRENT_PLAYER:
2426 Player *player = m_env.getLocalPlayer();
2427 assert(player != NULL);
2428 return &player->inventory;
2431 case InventoryLocation::PLAYER:
2433 Player *player = m_env.getPlayer(loc.name.c_str());
2436 return &player->inventory;
2439 case InventoryLocation::NODEMETA:
2441 NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p);
2444 return meta->getInventory();
2447 case InventoryLocation::DETACHED:
2449 if(m_detached_inventories.count(loc.name) == 0)
2451 return m_detached_inventories[loc.name];
2459 void Client::inventoryAction(InventoryAction *a)
2462 Send it to the server
2464 sendInventoryAction(a);
2467 Predict some local inventory changes
2469 a->clientApply(this, this);
2475 ClientActiveObject * Client::getSelectedActiveObject(
2477 v3f from_pos_f_on_map,
2478 core::line3d<f32> shootline_on_map
2481 std::vector<DistanceSortedActiveObject> objects;
2483 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
2485 //infostream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
2488 // After this, the closest object is the first in the array.
2489 std::sort(objects.begin(), objects.end());
2491 for(u32 i=0; i<objects.size(); i++)
2493 ClientActiveObject *obj = objects[i].obj;
2495 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
2496 if(selection_box == NULL)
2499 v3f pos = obj->getPosition();
2501 core::aabbox3d<f32> offsetted_box(
2502 selection_box->MinEdge + pos,
2503 selection_box->MaxEdge + pos
2506 if(offsetted_box.intersectsWithLine(shootline_on_map))
2508 //infostream<<"Returning selected object"<<std::endl;
2513 //infostream<<"No object selected; returning NULL."<<std::endl;
2517 void Client::printDebugInfo(std::ostream &os)
2519 //JMutexAutoLock lock1(m_fetchblock_mutex);
2520 /*JMutexAutoLock lock2(m_incoming_queue_mutex);
2522 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
2523 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
2524 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
2528 std::list<std::string> Client::getConnectedPlayerNames()
2530 return m_env.getPlayerNames();
2533 float Client::getAnimationTime()
2535 return m_animation_time;
2538 int Client::getCrackLevel()
2540 return m_crack_level;
2543 void Client::setCrack(int level, v3s16 pos)
2545 int old_crack_level = m_crack_level;
2546 v3s16 old_crack_pos = m_crack_pos;
2548 m_crack_level = level;
2551 if(old_crack_level >= 0 && (level < 0 || pos != old_crack_pos))
2554 addUpdateMeshTaskForNode(old_crack_pos, false, true);
2556 if(level >= 0 && (old_crack_level < 0 || pos != old_crack_pos))
2559 addUpdateMeshTaskForNode(pos, false, true);
2565 Player *player = m_env.getLocalPlayer();
2566 assert(player != NULL);
2570 u16 Client::getBreath()
2572 Player *player = m_env.getLocalPlayer();
2573 assert(player != NULL);
2574 return player->getBreath();
2577 bool Client::getChatMessage(std::wstring &message)
2579 if(m_chat_queue.size() == 0)
2581 message = m_chat_queue.pop_front();
2585 void Client::typeChatMessage(const std::wstring &message)
2587 // Discard empty line
2592 sendChatMessage(message);
2595 if (message[0] == L'/')
2597 m_chat_queue.push_back(
2598 (std::wstring)L"issued command: "+message);
2602 LocalPlayer *player = m_env.getLocalPlayer();
2603 assert(player != NULL);
2604 std::wstring name = narrow_to_wide(player->getName());
2605 m_chat_queue.push_back(
2606 (std::wstring)L"<"+name+L"> "+message);
2610 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
2612 /*infostream<<"Client::addUpdateMeshTask(): "
2613 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2614 <<" ack_to_server="<<ack_to_server
2615 <<" urgent="<<urgent
2618 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2623 Create a task to update the mesh of the block
2626 MeshMakeData *data = new MeshMakeData(this);
2629 //TimeTaker timer("data fill");
2631 // Debug: 1-6ms, avg=2ms
2633 data->setCrack(m_crack_level, m_crack_pos);
2634 data->setSmoothLighting(g_settings->getBool("smooth_lighting"));
2638 //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
2640 // Add task to queue
2641 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server, urgent);
2643 /*infostream<<"Mesh update input queue size is "
2644 <<m_mesh_update_thread.m_queue_in.size()
2648 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent)
2652 infostream<<"Client::addUpdateMeshTaskWithEdge(): "
2653 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2658 v3s16 p = blockpos + v3s16(0,0,0);
2659 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2660 addUpdateMeshTask(p, ack_to_server, urgent);
2662 catch(InvalidPositionException &e){}
2664 for (int i=0;i<6;i++)
2667 v3s16 p = blockpos + g_6dirs[i];
2668 addUpdateMeshTask(p, false, urgent);
2670 catch(InvalidPositionException &e){}
2674 void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool urgent)
2678 infostream<<"Client::addUpdateMeshTaskForNode(): "
2679 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2683 v3s16 blockpos = getNodeBlockPos(nodepos);
2684 v3s16 blockpos_relative = blockpos * MAP_BLOCKSIZE;
2687 v3s16 p = blockpos + v3s16(0,0,0);
2688 addUpdateMeshTask(p, ack_to_server, urgent);
2690 catch(InvalidPositionException &e){}
2692 if(nodepos.X == blockpos_relative.X){
2694 v3s16 p = blockpos + v3s16(-1,0,0);
2695 addUpdateMeshTask(p, false, urgent);
2697 catch(InvalidPositionException &e){}
2699 if(nodepos.Y == blockpos_relative.Y){
2701 v3s16 p = blockpos + v3s16(0,-1,0);
2702 addUpdateMeshTask(p, false, urgent);
2704 catch(InvalidPositionException &e){}
2706 if(nodepos.Z == blockpos_relative.Z){
2708 v3s16 p = blockpos + v3s16(0,0,-1);
2709 addUpdateMeshTask(p, false, urgent);
2711 catch(InvalidPositionException &e){}
2715 ClientEvent Client::getClientEvent()
2717 if(m_client_event_queue.size() == 0)
2720 event.type = CE_NONE;
2723 return m_client_event_queue.pop_front();
2726 float Client::mediaReceiveProgress()
2728 if (m_media_downloader)
2729 return m_media_downloader->getProgress();
2731 return 1.0; // downloader only exists when not yet done
2734 void draw_load_screen(const std::wstring &text,
2735 IrrlichtDevice* device, gui::IGUIFont* font,
2736 float dtime=0 ,int percent=0, bool clouds=true);
2737 void Client::afterContentReceived(IrrlichtDevice *device, gui::IGUIFont* font)
2739 infostream<<"Client::afterContentReceived() started"<<std::endl;
2740 assert(m_itemdef_received);
2741 assert(m_nodedef_received);
2742 assert(mediaReceived());
2744 // Rebuild inherited images and recreate textures
2745 infostream<<"- Rebuilding images and textures"<<std::endl;
2746 m_tsrc->rebuildImagesAndTextures();
2749 infostream<<"- Rebuilding shaders"<<std::endl;
2750 m_shsrc->rebuildShaders();
2752 // Update node aliases
2753 infostream<<"- Updating node aliases"<<std::endl;
2754 m_nodedef->updateAliases(m_itemdef);
2756 // Update node textures
2757 infostream<<"- Updating node textures"<<std::endl;
2758 m_nodedef->updateTextures(m_tsrc);
2760 // Preload item textures and meshes if configured to
2761 if(g_settings->getBool("preload_item_visuals"))
2763 verbosestream<<"Updating item textures and meshes"<<std::endl;
2764 wchar_t* text = wgettext("Item textures...");
2765 draw_load_screen(text,device,font,0,0);
2766 std::set<std::string> names = m_itemdef->getAll();
2767 size_t size = names.size();
2770 for(std::set<std::string>::const_iterator
2771 i = names.begin(); i != names.end(); ++i){
2772 // Asking for these caches the result
2773 m_itemdef->getInventoryTexture(*i, this);
2774 m_itemdef->getWieldMesh(*i, this);
2776 percent = count*100/size;
2777 if (count%50 == 0) // only update every 50 item
2778 draw_load_screen(text,device,font,0,percent);
2783 // Start mesh update thread after setting up content definitions
2784 infostream<<"- Starting mesh update thread"<<std::endl;
2785 m_mesh_update_thread.Start();
2787 infostream<<"Client::afterContentReceived() done"<<std::endl;
2790 float Client::getRTT(void)
2793 return m_con.GetPeerAvgRTT(PEER_ID_SERVER);
2794 } catch(con::PeerNotFoundException &e){
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 u16 Client::allocateUnknownNodeId(const std::string &name)
2824 errorstream<<"Client::allocateUnknownNodeId(): "
2825 <<"Client cannot allocate node IDs"<<std::endl;
2827 return CONTENT_IGNORE;
2829 ISoundManager* Client::getSoundManager()
2833 MtEventManager* Client::getEventManager()
2838 scene::IAnimatedMesh* Client::getMesh(const std::string &filename)
2840 std::map<std::string, std::string>::const_iterator i =
2841 m_mesh_data.find(filename);
2842 if(i == m_mesh_data.end()){
2843 errorstream<<"Client::getMesh(): Mesh not found: \""<<filename<<"\""
2847 const std::string &data = i->second;
2848 scene::ISceneManager *smgr = m_device->getSceneManager();
2850 // Create the mesh, remove it from cache and return it
2851 // This allows unique vertex colors and other properties for each instance
2852 Buffer<char> data_rw(data.c_str(), data.size()); // Const-incorrect Irrlicht
2853 io::IFileSystem *irrfs = m_device->getFileSystem();
2854 io::IReadFile *rfile = irrfs->createMemoryReadFile(
2855 *data_rw, data_rw.getSize(), filename.c_str());
2857 scene::IAnimatedMesh *mesh = smgr->getMesh(rfile);
2859 // NOTE: By playing with Irrlicht refcounts, maybe we could cache a bunch
2860 // of uniquely named instances and re-use them
2862 smgr->getMeshCache()->removeMesh(mesh);