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 Irrlicht: "
897 <<"\""<<filename<<"\""<<std::endl;
898 scene::ISceneManager *smgr = m_device->getSceneManager();
900 //check if mesh was already cached
901 scene::IAnimatedMesh *mesh =
902 smgr->getMeshCache()->getMeshByName(filename.c_str());
905 errorstream << "Multiple models with name: " << filename.c_str() <<
906 " found replacing previous model!" << std::endl;
908 smgr->getMeshCache()->removeMesh(mesh);
912 io::IFileSystem *irrfs = m_device->getFileSystem();
913 io::IReadFile *rfile = irrfs->createMemoryReadFile(
914 *data_rw, data_rw.getSize(), filename.c_str());
917 mesh = smgr->getMesh(rfile);
918 smgr->getMeshCache()->addMesh(filename.c_str(), mesh);
923 errorstream<<"Client: Don't know how to load file \""
924 <<filename<<"\""<<std::endl;
928 // Virtual methods from con::PeerHandler
929 void Client::peerAdded(con::Peer *peer)
931 infostream<<"Client::peerAdded(): peer->id="
932 <<peer->id<<std::endl;
934 void Client::deletingPeer(con::Peer *peer, bool timeout)
936 infostream<<"Client::deletingPeer(): "
937 "Server Peer is getting deleted "
938 <<"(timeout="<<timeout<<")"<<std::endl;
943 u16 number of files requested
949 void Client::request_media(const std::list<std::string> &file_requests)
951 std::ostringstream os(std::ios_base::binary);
952 writeU16(os, TOSERVER_REQUEST_MEDIA);
953 writeU16(os, file_requests.size());
955 for(std::list<std::string>::const_iterator i = file_requests.begin();
956 i != file_requests.end(); ++i) {
957 os<<serializeString(*i);
961 std::string s = os.str();
962 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
965 infostream<<"Client: Sending media request list to server ("
966 <<file_requests.size()<<" files)"<<std::endl;
969 void Client::received_media()
971 // notify server we received everything
972 std::ostringstream os(std::ios_base::binary);
973 writeU16(os, TOSERVER_RECEIVED_MEDIA);
974 std::string s = os.str();
975 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
978 infostream<<"Client: Notifying server that we received all media"
982 void Client::ReceiveAll()
984 DSTACK(__FUNCTION_NAME);
985 u32 start_ms = porting::getTimeMs();
988 // Limit time even if there would be huge amounts of data to
990 if(porting::getTimeMs() > start_ms + 100)
995 g_profiler->graphAdd("client_received_packets", 1);
997 catch(con::NoIncomingDataException &e)
1001 catch(con::InvalidIncomingDataException &e)
1003 infostream<<"Client::ReceiveAll(): "
1004 "InvalidIncomingDataException: what()="
1005 <<e.what()<<std::endl;
1010 void Client::Receive()
1012 DSTACK(__FUNCTION_NAME);
1013 SharedBuffer<u8> data;
1017 //TimeTaker t1("con mutex and receive", m_device);
1018 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1019 datasize = m_con.Receive(sender_peer_id, data);
1021 //TimeTaker t1("ProcessData", m_device);
1022 ProcessData(*data, datasize, sender_peer_id);
1026 sender_peer_id given to this shall be quaranteed to be a valid peer
1028 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
1030 DSTACK(__FUNCTION_NAME);
1032 // Ignore packets that don't even fit a command
1035 m_packetcounter.add(60000);
1039 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
1041 //infostream<<"Client: received command="<<command<<std::endl;
1042 m_packetcounter.add((u16)command);
1045 If this check is removed, be sure to change the queue
1046 system to know the ids
1048 if(sender_peer_id != PEER_ID_SERVER)
1050 infostream<<"Client::ProcessData(): Discarding data not "
1051 "coming from server: peer_id="<<sender_peer_id
1056 u8 ser_version = m_server_ser_ver;
1058 //infostream<<"Client received command="<<(int)command<<std::endl;
1060 if(command == TOCLIENT_INIT)
1065 u8 deployed = data[2];
1067 infostream<<"Client: TOCLIENT_INIT received with "
1068 "deployed="<<((int)deployed&0xff)<<std::endl;
1070 if(!ser_ver_supported(deployed))
1072 infostream<<"Client: TOCLIENT_INIT: Server sent "
1073 <<"unsupported ser_fmt_ver"<<std::endl;
1077 m_server_ser_ver = deployed;
1079 // Get player position
1080 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
1081 if(datasize >= 2+1+6)
1082 playerpos_s16 = readV3S16(&data[2+1]);
1083 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
1086 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1088 // Set player position
1089 Player *player = m_env.getLocalPlayer();
1090 assert(player != NULL);
1091 player->setPosition(playerpos_f);
1094 if(datasize >= 2+1+6+8)
1097 m_map_seed = readU64(&data[2+1+6]);
1098 infostream<<"Client: received map seed: "<<m_map_seed<<std::endl;
1101 if(datasize >= 2+1+6+8+4)
1104 m_recommended_send_interval = readF1000(&data[2+1+6+8]);
1105 infostream<<"Client: received recommended send interval "
1106 <<m_recommended_send_interval<<std::endl;
1111 SharedBuffer<u8> reply(replysize);
1112 writeU16(&reply[0], TOSERVER_INIT2);
1114 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1119 if(command == TOCLIENT_ACCESS_DENIED)
1121 // The server didn't like our password. Note, this needs
1122 // to be processed even if the serialisation format has
1123 // not been agreed yet, the same as TOCLIENT_INIT.
1124 m_access_denied = true;
1125 m_access_denied_reason = L"Unknown";
1128 std::string datastring((char*)&data[2], datasize-2);
1129 std::istringstream is(datastring, std::ios_base::binary);
1130 m_access_denied_reason = deSerializeWideString(is);
1135 if(ser_version == SER_FMT_VER_INVALID)
1137 infostream<<"Client: Server serialization"
1138 " format invalid or not initialized."
1139 " Skipping incoming command="<<command<<std::endl;
1143 // Just here to avoid putting the two if's together when
1144 // making some copypasta
1147 if(command == TOCLIENT_REMOVENODE)
1152 p.X = readS16(&data[2]);
1153 p.Y = readS16(&data[4]);
1154 p.Z = readS16(&data[6]);
1156 //TimeTaker t1("TOCLIENT_REMOVENODE");
1160 else if(command == TOCLIENT_ADDNODE)
1162 if(datasize < 8 + MapNode::serializedLength(ser_version))
1166 p.X = readS16(&data[2]);
1167 p.Y = readS16(&data[4]);
1168 p.Z = readS16(&data[6]);
1170 //TimeTaker t1("TOCLIENT_ADDNODE");
1173 n.deSerialize(&data[8], ser_version);
1175 bool remove_metadata = true;
1176 u32 index = 8 + MapNode::serializedLength(ser_version);
1177 if ((datasize >= index+1) && data[index]){
1178 remove_metadata = false;
1181 addNode(p, n, remove_metadata);
1183 else if(command == TOCLIENT_BLOCKDATA)
1185 // Ignore too small packet
1190 p.X = readS16(&data[2]);
1191 p.Y = readS16(&data[4]);
1192 p.Z = readS16(&data[6]);
1194 /*infostream<<"Client: Thread: BLOCKDATA for ("
1195 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1196 /*infostream<<"Client: Thread: BLOCKDATA for ("
1197 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1199 std::string datastring((char*)&data[8], datasize-8);
1200 std::istringstream istr(datastring, std::ios_base::binary);
1205 v2s16 p2d(p.X, p.Z);
1206 sector = m_env.getMap().emergeSector(p2d);
1208 assert(sector->getPos() == p2d);
1210 //TimeTaker timer("MapBlock deSerialize");
1213 block = sector->getBlockNoCreateNoEx(p.Y);
1217 Update an existing block
1219 //infostream<<"Updating"<<std::endl;
1220 block->deSerialize(istr, ser_version, false);
1221 block->deSerializeNetworkSpecific(istr);
1228 //infostream<<"Creating new"<<std::endl;
1229 block = new MapBlock(&m_env.getMap(), p, this);
1230 block->deSerialize(istr, ser_version, false);
1231 block->deSerializeNetworkSpecific(istr);
1232 sector->insertBlock(block);
1246 u32 replysize = 2+1+6;
1247 SharedBuffer<u8> reply(replysize);
1248 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
1250 writeV3S16(&reply[3], p);
1252 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1256 Add it to mesh update queue and set it to be acknowledged after update.
1258 //infostream<<"Adding mesh update task for received block"<<std::endl;
1259 addUpdateMeshTaskWithEdge(p, true);
1261 else if(command == TOCLIENT_INVENTORY)
1266 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
1269 //TimeTaker t2("mutex locking", m_device);
1270 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1273 //TimeTaker t3("istringstream init", m_device);
1274 std::string datastring((char*)&data[2], datasize-2);
1275 std::istringstream is(datastring, std::ios_base::binary);
1278 //TimeTaker t4("player get", m_device);
1279 Player *player = m_env.getLocalPlayer();
1280 assert(player != NULL);
1283 //TimeTaker t1("inventory.deSerialize()", m_device);
1284 player->inventory.deSerialize(is);
1287 m_inventory_updated = true;
1289 delete m_inventory_from_server;
1290 m_inventory_from_server = new Inventory(player->inventory);
1291 m_inventory_from_server_age = 0.0;
1293 //infostream<<"Client got player inventory:"<<std::endl;
1294 //player->inventory.print(infostream);
1297 else if(command == TOCLIENT_TIME_OF_DAY)
1302 u16 time_of_day = readU16(&data[2]);
1303 time_of_day = time_of_day % 24000;
1304 //infostream<<"Client: time_of_day="<<time_of_day<<std::endl;
1305 float time_speed = 0;
1306 if(datasize >= 2 + 2 + 4){
1307 time_speed = readF1000(&data[4]);
1309 // Old message; try to approximate speed of time by ourselves
1310 float time_of_day_f = (float)time_of_day / 24000.0;
1311 float tod_diff_f = 0;
1312 if(time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
1313 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0;
1315 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
1316 m_last_time_of_day_f = time_of_day_f;
1317 float time_diff = m_time_of_day_update_timer;
1318 m_time_of_day_update_timer = 0;
1319 if(m_time_of_day_set){
1320 time_speed = 3600.0*24.0 * tod_diff_f / time_diff;
1321 infostream<<"Client: Measured time_of_day speed (old format): "
1322 <<time_speed<<" tod_diff_f="<<tod_diff_f
1323 <<" time_diff="<<time_diff<<std::endl;
1327 // Update environment
1328 m_env.setTimeOfDay(time_of_day);
1329 m_env.setTimeOfDaySpeed(time_speed);
1330 m_time_of_day_set = true;
1332 u32 dr = m_env.getDayNightRatio();
1333 verbosestream<<"Client: time_of_day="<<time_of_day
1334 <<" time_speed="<<time_speed
1335 <<" dr="<<dr<<std::endl;
1337 else if(command == TOCLIENT_CHAT_MESSAGE)
1345 std::string datastring((char*)&data[2], datasize-2);
1346 std::istringstream is(datastring, std::ios_base::binary);
1349 is.read((char*)buf, 2);
1350 u16 len = readU16(buf);
1352 std::wstring message;
1353 for(u16 i=0; i<len; i++)
1355 is.read((char*)buf, 2);
1356 message += (wchar_t)readU16(buf);
1359 /*infostream<<"Client received chat message: "
1360 <<wide_to_narrow(message)<<std::endl;*/
1362 m_chat_queue.push_back(message);
1364 else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1366 //if(g_settings->getBool("enable_experimental"))
1370 u16 count of removed objects
1371 for all removed objects {
1374 u16 count of added objects
1375 for all added objects {
1378 u32 initialization data length
1379 string initialization data
1384 // Get all data except the command number
1385 std::string datastring((char*)&data[2], datasize-2);
1386 // Throw them in an istringstream
1387 std::istringstream is(datastring, std::ios_base::binary);
1391 // Read removed objects
1393 u16 removed_count = readU16((u8*)buf);
1394 for(u16 i=0; i<removed_count; i++)
1397 u16 id = readU16((u8*)buf);
1400 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1401 m_env.removeActiveObject(id);
1405 // Read added objects
1407 u16 added_count = readU16((u8*)buf);
1408 for(u16 i=0; i<added_count; i++)
1411 u16 id = readU16((u8*)buf);
1413 u8 type = readU8((u8*)buf);
1414 std::string data = deSerializeLongString(is);
1417 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1418 m_env.addActiveObject(id, type, data);
1423 else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1425 //if(g_settings->getBool("enable_experimental"))
1437 // Get all data except the command number
1438 std::string datastring((char*)&data[2], datasize-2);
1439 // Throw them in an istringstream
1440 std::istringstream is(datastring, std::ios_base::binary);
1442 while(is.eof() == false)
1446 u16 id = readU16((u8*)buf);
1450 u16 message_size = readU16((u8*)buf);
1451 std::string message;
1452 message.reserve(message_size);
1453 for(u16 i=0; i<message_size; i++)
1456 message.append(buf, 1);
1458 // Pass on to the environment
1460 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1461 m_env.processActiveObjectMessage(id, message);
1466 else if(command == TOCLIENT_MOVEMENT)
1468 std::string datastring((char*)&data[2], datasize-2);
1469 std::istringstream is(datastring, std::ios_base::binary);
1470 Player *player = m_env.getLocalPlayer();
1471 assert(player != NULL);
1473 player->movement_acceleration_default = readF1000(is) * BS;
1474 player->movement_acceleration_air = readF1000(is) * BS;
1475 player->movement_acceleration_fast = readF1000(is) * BS;
1476 player->movement_speed_walk = readF1000(is) * BS;
1477 player->movement_speed_crouch = readF1000(is) * BS;
1478 player->movement_speed_fast = readF1000(is) * BS;
1479 player->movement_speed_climb = readF1000(is) * BS;
1480 player->movement_speed_jump = readF1000(is) * BS;
1481 player->movement_liquid_fluidity = readF1000(is) * BS;
1482 player->movement_liquid_fluidity_smooth = readF1000(is) * BS;
1483 player->movement_liquid_sink = readF1000(is) * BS;
1484 player->movement_gravity = readF1000(is) * BS;
1486 else if(command == TOCLIENT_HP)
1488 std::string datastring((char*)&data[2], datasize-2);
1489 std::istringstream is(datastring, std::ios_base::binary);
1490 Player *player = m_env.getLocalPlayer();
1491 assert(player != NULL);
1492 u8 oldhp = player->hp;
1498 // Add to ClientEvent queue
1500 event.type = CE_PLAYER_DAMAGE;
1501 event.player_damage.amount = oldhp - hp;
1502 m_client_event_queue.push_back(event);
1505 else if(command == TOCLIENT_BREATH)
1507 std::string datastring((char*)&data[2], datasize-2);
1508 std::istringstream is(datastring, std::ios_base::binary);
1509 Player *player = m_env.getLocalPlayer();
1510 assert(player != NULL);
1511 u16 breath = readU16(is);
1512 player->setBreath(breath) ;
1514 else if(command == TOCLIENT_MOVE_PLAYER)
1516 std::string datastring((char*)&data[2], datasize-2);
1517 std::istringstream is(datastring, std::ios_base::binary);
1518 Player *player = m_env.getLocalPlayer();
1519 assert(player != NULL);
1520 v3f pos = readV3F1000(is);
1521 f32 pitch = readF1000(is);
1522 f32 yaw = readF1000(is);
1523 player->setPosition(pos);
1524 /*player->setPitch(pitch);
1525 player->setYaw(yaw);*/
1527 infostream<<"Client got TOCLIENT_MOVE_PLAYER"
1528 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1534 Add to ClientEvent queue.
1535 This has to be sent to the main program because otherwise
1536 it would just force the pitch and yaw values to whatever
1537 the camera points to.
1540 event.type = CE_PLAYER_FORCE_MOVE;
1541 event.player_force_move.pitch = pitch;
1542 event.player_force_move.yaw = yaw;
1543 m_client_event_queue.push_back(event);
1545 // Ignore damage for a few seconds, so that the player doesn't
1546 // get damage from falling on ground
1547 m_ignore_damage_timer = 3.0;
1549 else if(command == TOCLIENT_PLAYERITEM)
1551 infostream<<"Client: WARNING: Ignoring TOCLIENT_PLAYERITEM"<<std::endl;
1553 else if(command == TOCLIENT_DEATHSCREEN)
1555 std::string datastring((char*)&data[2], datasize-2);
1556 std::istringstream is(datastring, std::ios_base::binary);
1558 bool set_camera_point_target = readU8(is);
1559 v3f camera_point_target = readV3F1000(is);
1562 event.type = CE_DEATHSCREEN;
1563 event.deathscreen.set_camera_point_target = set_camera_point_target;
1564 event.deathscreen.camera_point_target_x = camera_point_target.X;
1565 event.deathscreen.camera_point_target_y = camera_point_target.Y;
1566 event.deathscreen.camera_point_target_z = camera_point_target.Z;
1567 m_client_event_queue.push_back(event);
1569 else if(command == TOCLIENT_ANNOUNCE_MEDIA)
1571 std::string datastring((char*)&data[2], datasize-2);
1572 std::istringstream is(datastring, std::ios_base::binary);
1574 int num_files = readU16(is);
1576 infostream<<"Client: Received media announcement: packet size: "
1577 <<datasize<<std::endl;
1579 if (m_media_downloader == NULL ||
1580 m_media_downloader->isStarted()) {
1581 const char *problem = m_media_downloader ?
1582 "we already saw another announcement" :
1583 "all media has been received already";
1584 errorstream<<"Client: Received media announcement but "
1586 <<" files="<<num_files
1587 <<" size="<<datasize<<std::endl;
1591 // Mesh update thread must be stopped while
1592 // updating content definitions
1593 assert(!m_mesh_update_thread.IsRunning());
1595 for(int i=0; i<num_files; i++)
1597 std::string name = deSerializeString(is);
1598 std::string sha1_base64 = deSerializeString(is);
1599 std::string sha1_raw = base64_decode(sha1_base64);
1600 m_media_downloader->addFile(name, sha1_raw);
1603 std::vector<std::string> remote_media;
1605 Strfnd sf(deSerializeString(is));
1606 while(!sf.atend()) {
1607 std::string baseurl = trim(sf.next(","));
1609 m_media_downloader->addRemoteServer(baseurl);
1612 catch(SerializationError) {
1613 // not supported by server or turned off
1616 m_media_downloader->step(this);
1617 if (m_media_downloader->isDone()) {
1618 // might be done already if all media is in the cache
1619 delete m_media_downloader;
1620 m_media_downloader = NULL;
1623 else if(command == TOCLIENT_MEDIA)
1625 std::string datastring((char*)&data[2], datasize-2);
1626 std::istringstream is(datastring, std::ios_base::binary);
1630 u16 total number of file bunches
1631 u16 index of this bunch
1632 u32 number of files in this bunch
1640 int num_bunches = readU16(is);
1641 int bunch_i = readU16(is);
1642 u32 num_files = readU32(is);
1643 infostream<<"Client: Received files: bunch "<<bunch_i<<"/"
1644 <<num_bunches<<" files="<<num_files
1645 <<" size="<<datasize<<std::endl;
1650 if (m_media_downloader == NULL ||
1651 !m_media_downloader->isStarted()) {
1652 const char *problem = m_media_downloader ?
1653 "media has not been requested" :
1654 "all media has been received already";
1655 errorstream<<"Client: Received media but "
1657 <<" bunch "<<bunch_i<<"/"<<num_bunches
1658 <<" files="<<num_files
1659 <<" size="<<datasize<<std::endl;
1663 // Mesh update thread must be stopped while
1664 // updating content definitions
1665 assert(!m_mesh_update_thread.IsRunning());
1667 for(u32 i=0; i<num_files; i++){
1668 std::string name = deSerializeString(is);
1669 std::string data = deSerializeLongString(is);
1670 m_media_downloader->conventionalTransferDone(
1674 if (m_media_downloader->isDone()) {
1675 delete m_media_downloader;
1676 m_media_downloader = NULL;
1679 else if(command == TOCLIENT_TOOLDEF)
1681 infostream<<"Client: WARNING: Ignoring TOCLIENT_TOOLDEF"<<std::endl;
1683 else if(command == TOCLIENT_NODEDEF)
1685 infostream<<"Client: Received node definitions: packet size: "
1686 <<datasize<<std::endl;
1688 // Mesh update thread must be stopped while
1689 // updating content definitions
1690 assert(!m_mesh_update_thread.IsRunning());
1692 // Decompress node definitions
1693 std::string datastring((char*)&data[2], datasize-2);
1694 std::istringstream is(datastring, std::ios_base::binary);
1695 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1696 std::ostringstream tmp_os;
1697 decompressZlib(tmp_is, tmp_os);
1699 // Deserialize node definitions
1700 std::istringstream tmp_is2(tmp_os.str());
1701 m_nodedef->deSerialize(tmp_is2);
1702 m_nodedef_received = true;
1704 else if(command == TOCLIENT_CRAFTITEMDEF)
1706 infostream<<"Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF"<<std::endl;
1708 else if(command == TOCLIENT_ITEMDEF)
1710 infostream<<"Client: Received item definitions: packet size: "
1711 <<datasize<<std::endl;
1713 // Mesh update thread must be stopped while
1714 // updating content definitions
1715 assert(!m_mesh_update_thread.IsRunning());
1717 // Decompress item definitions
1718 std::string datastring((char*)&data[2], datasize-2);
1719 std::istringstream is(datastring, std::ios_base::binary);
1720 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1721 std::ostringstream tmp_os;
1722 decompressZlib(tmp_is, tmp_os);
1724 // Deserialize node definitions
1725 std::istringstream tmp_is2(tmp_os.str());
1726 m_itemdef->deSerialize(tmp_is2);
1727 m_itemdef_received = true;
1729 else if(command == TOCLIENT_PLAY_SOUND)
1731 std::string datastring((char*)&data[2], datasize-2);
1732 std::istringstream is(datastring, std::ios_base::binary);
1734 s32 server_id = readS32(is);
1735 std::string name = deSerializeString(is);
1736 float gain = readF1000(is);
1737 int type = readU8(is); // 0=local, 1=positional, 2=object
1738 v3f pos = readV3F1000(is);
1739 u16 object_id = readU16(is);
1740 bool loop = readU8(is);
1745 client_id = m_sound->playSound(name, loop, gain);
1747 case 1: // positional
1748 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1751 ClientActiveObject *cao = m_env.getActiveObject(object_id);
1753 pos = cao->getPosition();
1754 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1755 // TODO: Set up sound to move with object
1760 if(client_id != -1){
1761 m_sounds_server_to_client[server_id] = client_id;
1762 m_sounds_client_to_server[client_id] = server_id;
1764 m_sounds_to_objects[client_id] = object_id;
1767 else if(command == TOCLIENT_STOP_SOUND)
1769 std::string datastring((char*)&data[2], datasize-2);
1770 std::istringstream is(datastring, std::ios_base::binary);
1772 s32 server_id = readS32(is);
1773 std::map<s32, int>::iterator i =
1774 m_sounds_server_to_client.find(server_id);
1775 if(i != m_sounds_server_to_client.end()){
1776 int client_id = i->second;
1777 m_sound->stopSound(client_id);
1780 else if(command == TOCLIENT_PRIVILEGES)
1782 std::string datastring((char*)&data[2], datasize-2);
1783 std::istringstream is(datastring, std::ios_base::binary);
1785 m_privileges.clear();
1786 infostream<<"Client: Privileges updated: ";
1787 u16 num_privileges = readU16(is);
1788 for(u16 i=0; i<num_privileges; i++){
1789 std::string priv = deSerializeString(is);
1790 m_privileges.insert(priv);
1791 infostream<<priv<<" ";
1793 infostream<<std::endl;
1795 else if(command == TOCLIENT_INVENTORY_FORMSPEC)
1797 std::string datastring((char*)&data[2], datasize-2);
1798 std::istringstream is(datastring, std::ios_base::binary);
1800 // Store formspec in LocalPlayer
1801 Player *player = m_env.getLocalPlayer();
1802 assert(player != NULL);
1803 player->inventory_formspec = deSerializeLongString(is);
1805 else if(command == TOCLIENT_DETACHED_INVENTORY)
1807 std::string datastring((char*)&data[2], datasize-2);
1808 std::istringstream is(datastring, std::ios_base::binary);
1810 std::string name = deSerializeString(is);
1812 infostream<<"Client: Detached inventory update: \""<<name<<"\""<<std::endl;
1814 Inventory *inv = NULL;
1815 if(m_detached_inventories.count(name) > 0)
1816 inv = m_detached_inventories[name];
1818 inv = new Inventory(m_itemdef);
1819 m_detached_inventories[name] = inv;
1821 inv->deSerialize(is);
1823 else if(command == TOCLIENT_SHOW_FORMSPEC)
1825 std::string datastring((char*)&data[2], datasize-2);
1826 std::istringstream is(datastring, std::ios_base::binary);
1828 std::string formspec = deSerializeLongString(is);
1829 std::string formname = deSerializeString(is);
1832 event.type = CE_SHOW_FORMSPEC;
1833 // pointer is required as event is a struct only!
1834 // adding a std:string to a struct isn't possible
1835 event.show_formspec.formspec = new std::string(formspec);
1836 event.show_formspec.formname = new std::string(formname);
1837 m_client_event_queue.push_back(event);
1839 else if(command == TOCLIENT_SPAWN_PARTICLE)
1841 std::string datastring((char*)&data[2], datasize-2);
1842 std::istringstream is(datastring, std::ios_base::binary);
1844 v3f pos = readV3F1000(is);
1845 v3f vel = readV3F1000(is);
1846 v3f acc = readV3F1000(is);
1847 float expirationtime = readF1000(is);
1848 float size = readF1000(is);
1849 bool collisiondetection = readU8(is);
1850 std::string texture = deSerializeLongString(is);
1853 event.type = CE_SPAWN_PARTICLE;
1854 event.spawn_particle.pos = new v3f (pos);
1855 event.spawn_particle.vel = new v3f (vel);
1856 event.spawn_particle.acc = new v3f (acc);
1858 event.spawn_particle.expirationtime = expirationtime;
1859 event.spawn_particle.size = size;
1860 event.spawn_particle.collisiondetection =
1862 event.spawn_particle.texture = new std::string(texture);
1864 m_client_event_queue.push_back(event);
1866 else if(command == TOCLIENT_ADD_PARTICLESPAWNER)
1868 std::string datastring((char*)&data[2], datasize-2);
1869 std::istringstream is(datastring, std::ios_base::binary);
1871 u16 amount = readU16(is);
1872 float spawntime = readF1000(is);
1873 v3f minpos = readV3F1000(is);
1874 v3f maxpos = readV3F1000(is);
1875 v3f minvel = readV3F1000(is);
1876 v3f maxvel = readV3F1000(is);
1877 v3f minacc = readV3F1000(is);
1878 v3f maxacc = readV3F1000(is);
1879 float minexptime = readF1000(is);
1880 float maxexptime = readF1000(is);
1881 float minsize = readF1000(is);
1882 float maxsize = readF1000(is);
1883 bool collisiondetection = readU8(is);
1884 std::string texture = deSerializeLongString(is);
1885 u32 id = readU32(is);
1888 event.type = CE_ADD_PARTICLESPAWNER;
1889 event.add_particlespawner.amount = amount;
1890 event.add_particlespawner.spawntime = spawntime;
1892 event.add_particlespawner.minpos = new v3f (minpos);
1893 event.add_particlespawner.maxpos = new v3f (maxpos);
1894 event.add_particlespawner.minvel = new v3f (minvel);
1895 event.add_particlespawner.maxvel = new v3f (maxvel);
1896 event.add_particlespawner.minacc = new v3f (minacc);
1897 event.add_particlespawner.maxacc = new v3f (maxacc);
1899 event.add_particlespawner.minexptime = minexptime;
1900 event.add_particlespawner.maxexptime = maxexptime;
1901 event.add_particlespawner.minsize = minsize;
1902 event.add_particlespawner.maxsize = maxsize;
1903 event.add_particlespawner.collisiondetection = collisiondetection;
1904 event.add_particlespawner.texture = new std::string(texture);
1905 event.add_particlespawner.id = id;
1907 m_client_event_queue.push_back(event);
1909 else if(command == TOCLIENT_DELETE_PARTICLESPAWNER)
1911 std::string datastring((char*)&data[2], datasize-2);
1912 std::istringstream is(datastring, std::ios_base::binary);
1914 u32 id = readU16(is);
1917 event.type = CE_DELETE_PARTICLESPAWNER;
1918 event.delete_particlespawner.id = id;
1920 m_client_event_queue.push_back(event);
1922 else if(command == TOCLIENT_HUDADD)
1924 std::string datastring((char *)&data[2], datasize - 2);
1925 std::istringstream is(datastring, std::ios_base::binary);
1927 u32 id = readU32(is);
1928 u8 type = readU8(is);
1929 v2f pos = readV2F1000(is);
1930 std::string name = deSerializeString(is);
1931 v2f scale = readV2F1000(is);
1932 std::string text = deSerializeString(is);
1933 u32 number = readU32(is);
1934 u32 item = readU32(is);
1935 u32 dir = readU32(is);
1936 v2f align = readV2F1000(is);
1937 v2f offset = readV2F1000(is);
1940 event.type = CE_HUDADD;
1941 event.hudadd.id = id;
1942 event.hudadd.type = type;
1943 event.hudadd.pos = new v2f(pos);
1944 event.hudadd.name = new std::string(name);
1945 event.hudadd.scale = new v2f(scale);
1946 event.hudadd.text = new std::string(text);
1947 event.hudadd.number = number;
1948 event.hudadd.item = item;
1949 event.hudadd.dir = dir;
1950 event.hudadd.align = new v2f(align);
1951 event.hudadd.offset = new v2f(offset);
1952 m_client_event_queue.push_back(event);
1954 else if(command == TOCLIENT_HUDRM)
1956 std::string datastring((char *)&data[2], datasize - 2);
1957 std::istringstream is(datastring, std::ios_base::binary);
1959 u32 id = readU32(is);
1962 event.type = CE_HUDRM;
1963 event.hudrm.id = id;
1964 m_client_event_queue.push_back(event);
1966 else if(command == TOCLIENT_HUDCHANGE)
1972 std::string datastring((char *)&data[2], datasize - 2);
1973 std::istringstream is(datastring, std::ios_base::binary);
1975 u32 id = readU32(is);
1976 u8 stat = (HudElementStat)readU8(is);
1978 if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
1979 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
1980 v2fdata = readV2F1000(is);
1981 else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
1982 sdata = deSerializeString(is);
1984 intdata = readU32(is);
1987 event.type = CE_HUDCHANGE;
1988 event.hudchange.id = id;
1989 event.hudchange.stat = (HudElementStat)stat;
1990 event.hudchange.v2fdata = new v2f(v2fdata);
1991 event.hudchange.sdata = new std::string(sdata);
1992 event.hudchange.data = intdata;
1993 m_client_event_queue.push_back(event);
1995 else if(command == TOCLIENT_HUD_SET_FLAGS)
1997 std::string datastring((char *)&data[2], datasize - 2);
1998 std::istringstream is(datastring, std::ios_base::binary);
2000 Player *player = m_env.getLocalPlayer();
2001 assert(player != NULL);
2003 u32 flags = readU32(is);
2004 u32 mask = readU32(is);
2006 player->hud_flags &= ~mask;
2007 player->hud_flags |= flags;
2009 else if(command == TOCLIENT_HUD_SET_PARAM)
2011 std::string datastring((char *)&data[2], datasize - 2);
2012 std::istringstream is(datastring, std::ios_base::binary);
2014 Player *player = m_env.getLocalPlayer();
2015 assert(player != NULL);
2017 u16 param = readU16(is);
2018 std::string value = deSerializeString(is);
2020 if(param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4){
2021 s32 hotbar_itemcount = readS32((u8*) value.c_str());
2022 if(hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
2023 player->hud_hotbar_itemcount = hotbar_itemcount;
2024 } else if (param == HUD_PARAM_HOTBAR_IMAGE) {
2025 ((LocalPlayer *) player)->hotbar_image = value;
2026 } else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
2027 ((LocalPlayer *) player)->hotbar_selected_image = value;
2032 infostream<<"Client: Ignoring unknown command "
2033 <<command<<std::endl;
2037 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
2039 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2040 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
2043 void Client::interact(u8 action, const PointedThing& pointed)
2045 if(connectedAndInitialized() == false){
2046 infostream<<"Client::interact() "
2047 "cancelled (not connected)"
2052 std::ostringstream os(std::ios_base::binary);
2058 [5] u32 length of the next item
2059 [9] serialized PointedThing
2061 0: start digging (from undersurface) or use
2062 1: stop digging (all parameters ignored)
2063 2: digging completed
2064 3: place block or item (to abovesurface)
2067 writeU16(os, TOSERVER_INTERACT);
2068 writeU8(os, action);
2069 writeU16(os, getPlayerItem());
2070 std::ostringstream tmp_os(std::ios::binary);
2071 pointed.serialize(tmp_os);
2072 os<<serializeLongString(tmp_os.str());
2074 std::string s = os.str();
2075 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2078 Send(0, data, true);
2081 void Client::sendNodemetaFields(v3s16 p, const std::string &formname,
2082 const std::map<std::string, std::string> &fields)
2084 std::ostringstream os(std::ios_base::binary);
2086 writeU16(os, TOSERVER_NODEMETA_FIELDS);
2088 os<<serializeString(formname);
2089 writeU16(os, fields.size());
2090 for(std::map<std::string, std::string>::const_iterator
2091 i = fields.begin(); i != fields.end(); i++){
2092 const std::string &name = i->first;
2093 const std::string &value = i->second;
2094 os<<serializeString(name);
2095 os<<serializeLongString(value);
2099 std::string s = os.str();
2100 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2102 Send(0, data, true);
2105 void Client::sendInventoryFields(const std::string &formname,
2106 const std::map<std::string, std::string> &fields)
2108 std::ostringstream os(std::ios_base::binary);
2110 writeU16(os, TOSERVER_INVENTORY_FIELDS);
2111 os<<serializeString(formname);
2112 writeU16(os, fields.size());
2113 for(std::map<std::string, std::string>::const_iterator
2114 i = fields.begin(); i != fields.end(); i++){
2115 const std::string &name = i->first;
2116 const std::string &value = i->second;
2117 os<<serializeString(name);
2118 os<<serializeLongString(value);
2122 std::string s = os.str();
2123 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2125 Send(0, data, true);
2128 void Client::sendInventoryAction(InventoryAction *a)
2130 std::ostringstream os(std::ios_base::binary);
2134 writeU16(buf, TOSERVER_INVENTORY_ACTION);
2135 os.write((char*)buf, 2);
2140 std::string s = os.str();
2141 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2143 Send(0, data, true);
2146 void Client::sendChatMessage(const std::wstring &message)
2148 std::ostringstream os(std::ios_base::binary);
2152 writeU16(buf, TOSERVER_CHAT_MESSAGE);
2153 os.write((char*)buf, 2);
2156 writeU16(buf, message.size());
2157 os.write((char*)buf, 2);
2160 for(u32 i=0; i<message.size(); i++)
2164 os.write((char*)buf, 2);
2168 std::string s = os.str();
2169 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2171 Send(0, data, true);
2174 void Client::sendChangePassword(const std::wstring oldpassword,
2175 const std::wstring newpassword)
2177 Player *player = m_env.getLocalPlayer();
2181 std::string playername = player->getName();
2182 std::string oldpwd = translatePassword(playername, oldpassword);
2183 std::string newpwd = translatePassword(playername, newpassword);
2185 std::ostringstream os(std::ios_base::binary);
2186 u8 buf[2+PASSWORD_SIZE*2];
2188 [0] u16 TOSERVER_PASSWORD
2189 [2] u8[28] old password
2190 [30] u8[28] new password
2193 writeU16(buf, TOSERVER_PASSWORD);
2194 for(u32 i=0;i<PASSWORD_SIZE-1;i++)
2196 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
2197 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
2199 buf[2+PASSWORD_SIZE-1] = 0;
2200 buf[30+PASSWORD_SIZE-1] = 0;
2201 os.write((char*)buf, 2+PASSWORD_SIZE*2);
2204 std::string s = os.str();
2205 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2207 Send(0, data, true);
2211 void Client::sendDamage(u8 damage)
2213 DSTACK(__FUNCTION_NAME);
2214 std::ostringstream os(std::ios_base::binary);
2216 writeU16(os, TOSERVER_DAMAGE);
2217 writeU8(os, damage);
2220 std::string s = os.str();
2221 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2223 Send(0, data, true);
2226 void Client::sendBreath(u16 breath)
2228 DSTACK(__FUNCTION_NAME);
2229 std::ostringstream os(std::ios_base::binary);
2231 writeU16(os, TOSERVER_BREATH);
2232 writeU16(os, breath);
2234 std::string s = os.str();
2235 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2237 Send(0, data, true);
2240 void Client::sendRespawn()
2242 DSTACK(__FUNCTION_NAME);
2243 std::ostringstream os(std::ios_base::binary);
2245 writeU16(os, TOSERVER_RESPAWN);
2248 std::string s = os.str();
2249 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2251 Send(0, data, true);
2254 void Client::sendPlayerPos()
2256 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2258 LocalPlayer *myplayer = m_env.getLocalPlayer();
2259 if(myplayer == NULL)
2262 // Save bandwidth by only updating position when something changed
2263 if(myplayer->last_position == myplayer->getPosition() &&
2264 myplayer->last_speed == myplayer->getSpeed() &&
2265 myplayer->last_pitch == myplayer->getPitch() &&
2266 myplayer->last_yaw == myplayer->getYaw() &&
2267 myplayer->last_keyPressed == myplayer->keyPressed)
2270 myplayer->last_position = myplayer->getPosition();
2271 myplayer->last_speed = myplayer->getSpeed();
2272 myplayer->last_pitch = myplayer->getPitch();
2273 myplayer->last_yaw = myplayer->getYaw();
2274 myplayer->last_keyPressed = myplayer->keyPressed;
2278 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2279 our_peer_id = m_con.GetPeerID();
2282 // Set peer id if not set already
2283 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2284 myplayer->peer_id = our_peer_id;
2285 // Check that an existing peer_id is the same as the connection's
2286 assert(myplayer->peer_id == our_peer_id);
2288 v3f pf = myplayer->getPosition();
2289 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
2290 v3f sf = myplayer->getSpeed();
2291 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
2292 s32 pitch = myplayer->getPitch() * 100;
2293 s32 yaw = myplayer->getYaw() * 100;
2294 u32 keyPressed=myplayer->keyPressed;
2298 [2] v3s32 position*100
2299 [2+12] v3s32 speed*100
2300 [2+12+12] s32 pitch*100
2301 [2+12+12+4] s32 yaw*100
2302 [2+12+12+4+4] u32 keyPressed
2304 SharedBuffer<u8> data(2+12+12+4+4+4);
2305 writeU16(&data[0], TOSERVER_PLAYERPOS);
2306 writeV3S32(&data[2], position);
2307 writeV3S32(&data[2+12], speed);
2308 writeS32(&data[2+12+12], pitch);
2309 writeS32(&data[2+12+12+4], yaw);
2310 writeU32(&data[2+12+12+4+4], keyPressed);
2311 // Send as unreliable
2312 Send(0, data, false);
2315 void Client::sendPlayerItem(u16 item)
2317 Player *myplayer = m_env.getLocalPlayer();
2318 if(myplayer == NULL)
2321 u16 our_peer_id = m_con.GetPeerID();
2323 // Set peer id if not set already
2324 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2325 myplayer->peer_id = our_peer_id;
2326 // Check that an existing peer_id is the same as the connection's
2327 assert(myplayer->peer_id == our_peer_id);
2329 SharedBuffer<u8> data(2+2);
2330 writeU16(&data[0], TOSERVER_PLAYERITEM);
2331 writeU16(&data[2], item);
2334 Send(0, data, true);
2337 void Client::removeNode(v3s16 p)
2339 std::map<v3s16, MapBlock*> modified_blocks;
2343 //TimeTaker t("removeNodeAndUpdate", m_device);
2344 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
2346 catch(InvalidPositionException &e)
2350 // add urgent task to update the modified node
2351 addUpdateMeshTaskForNode(p, false, true);
2353 for(std::map<v3s16, MapBlock * >::iterator
2354 i = modified_blocks.begin();
2355 i != modified_blocks.end(); ++i)
2357 addUpdateMeshTaskWithEdge(i->first);
2361 void Client::addNode(v3s16 p, MapNode n, bool remove_metadata)
2363 TimeTaker timer1("Client::addNode()");
2365 std::map<v3s16, MapBlock*> modified_blocks;
2369 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
2370 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
2372 catch(InvalidPositionException &e)
2375 for(std::map<v3s16, MapBlock * >::iterator
2376 i = modified_blocks.begin();
2377 i != modified_blocks.end(); ++i)
2379 addUpdateMeshTaskWithEdge(i->first);
2383 void Client::setPlayerControl(PlayerControl &control)
2385 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2386 LocalPlayer *player = m_env.getLocalPlayer();
2387 assert(player != NULL);
2388 player->control = control;
2391 void Client::selectPlayerItem(u16 item)
2393 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2394 m_playeritem = item;
2395 m_inventory_updated = true;
2396 sendPlayerItem(item);
2399 // Returns true if the inventory of the local player has been
2400 // updated from the server. If it is true, it is set to false.
2401 bool Client::getLocalInventoryUpdated()
2403 // m_inventory_updated is behind envlock
2404 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2405 bool updated = m_inventory_updated;
2406 m_inventory_updated = false;
2410 // Copies the inventory of the local player to parameter
2411 void Client::getLocalInventory(Inventory &dst)
2413 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2414 Player *player = m_env.getLocalPlayer();
2415 assert(player != NULL);
2416 dst = player->inventory;
2419 Inventory* Client::getInventory(const InventoryLocation &loc)
2422 case InventoryLocation::UNDEFINED:
2425 case InventoryLocation::CURRENT_PLAYER:
2427 Player *player = m_env.getLocalPlayer();
2428 assert(player != NULL);
2429 return &player->inventory;
2432 case InventoryLocation::PLAYER:
2434 Player *player = m_env.getPlayer(loc.name.c_str());
2437 return &player->inventory;
2440 case InventoryLocation::NODEMETA:
2442 NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p);
2445 return meta->getInventory();
2448 case InventoryLocation::DETACHED:
2450 if(m_detached_inventories.count(loc.name) == 0)
2452 return m_detached_inventories[loc.name];
2460 void Client::inventoryAction(InventoryAction *a)
2463 Send it to the server
2465 sendInventoryAction(a);
2468 Predict some local inventory changes
2470 a->clientApply(this, this);
2476 ClientActiveObject * Client::getSelectedActiveObject(
2478 v3f from_pos_f_on_map,
2479 core::line3d<f32> shootline_on_map
2482 std::vector<DistanceSortedActiveObject> objects;
2484 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
2486 //infostream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
2489 // After this, the closest object is the first in the array.
2490 std::sort(objects.begin(), objects.end());
2492 for(u32 i=0; i<objects.size(); i++)
2494 ClientActiveObject *obj = objects[i].obj;
2496 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
2497 if(selection_box == NULL)
2500 v3f pos = obj->getPosition();
2502 core::aabbox3d<f32> offsetted_box(
2503 selection_box->MinEdge + pos,
2504 selection_box->MaxEdge + pos
2507 if(offsetted_box.intersectsWithLine(shootline_on_map))
2509 //infostream<<"Returning selected object"<<std::endl;
2514 //infostream<<"No object selected; returning NULL."<<std::endl;
2518 void Client::printDebugInfo(std::ostream &os)
2520 //JMutexAutoLock lock1(m_fetchblock_mutex);
2521 /*JMutexAutoLock lock2(m_incoming_queue_mutex);
2523 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
2524 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
2525 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
2529 std::list<std::string> Client::getConnectedPlayerNames()
2531 return m_env.getPlayerNames();
2534 float Client::getAnimationTime()
2536 return m_animation_time;
2539 int Client::getCrackLevel()
2541 return m_crack_level;
2544 void Client::setCrack(int level, v3s16 pos)
2546 int old_crack_level = m_crack_level;
2547 v3s16 old_crack_pos = m_crack_pos;
2549 m_crack_level = level;
2552 if(old_crack_level >= 0 && (level < 0 || pos != old_crack_pos))
2555 addUpdateMeshTaskForNode(old_crack_pos, false, true);
2557 if(level >= 0 && (old_crack_level < 0 || pos != old_crack_pos))
2560 addUpdateMeshTaskForNode(pos, false, true);
2566 Player *player = m_env.getLocalPlayer();
2567 assert(player != NULL);
2571 u16 Client::getBreath()
2573 Player *player = m_env.getLocalPlayer();
2574 assert(player != NULL);
2575 return player->getBreath();
2578 bool Client::getChatMessage(std::wstring &message)
2580 if(m_chat_queue.size() == 0)
2582 message = m_chat_queue.pop_front();
2586 void Client::typeChatMessage(const std::wstring &message)
2588 // Discard empty line
2593 sendChatMessage(message);
2596 if (message[0] == L'/')
2598 m_chat_queue.push_back(
2599 (std::wstring)L"issued command: "+message);
2603 LocalPlayer *player = m_env.getLocalPlayer();
2604 assert(player != NULL);
2605 std::wstring name = narrow_to_wide(player->getName());
2606 m_chat_queue.push_back(
2607 (std::wstring)L"<"+name+L"> "+message);
2611 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
2613 /*infostream<<"Client::addUpdateMeshTask(): "
2614 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2615 <<" ack_to_server="<<ack_to_server
2616 <<" urgent="<<urgent
2619 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2624 Create a task to update the mesh of the block
2627 MeshMakeData *data = new MeshMakeData(this);
2630 //TimeTaker timer("data fill");
2632 // Debug: 1-6ms, avg=2ms
2634 data->setCrack(m_crack_level, m_crack_pos);
2635 data->setSmoothLighting(g_settings->getBool("smooth_lighting"));
2639 //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
2641 // Add task to queue
2642 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server, urgent);
2644 /*infostream<<"Mesh update input queue size is "
2645 <<m_mesh_update_thread.m_queue_in.size()
2649 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent)
2653 infostream<<"Client::addUpdateMeshTaskWithEdge(): "
2654 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2659 v3s16 p = blockpos + v3s16(0,0,0);
2660 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2661 addUpdateMeshTask(p, ack_to_server, urgent);
2663 catch(InvalidPositionException &e){}
2665 for (int i=0;i<6;i++)
2668 v3s16 p = blockpos + g_6dirs[i];
2669 addUpdateMeshTask(p, false, urgent);
2671 catch(InvalidPositionException &e){}
2675 void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool urgent)
2679 infostream<<"Client::addUpdateMeshTaskForNode(): "
2680 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2684 v3s16 blockpos = getNodeBlockPos(nodepos);
2685 v3s16 blockpos_relative = blockpos * MAP_BLOCKSIZE;
2688 v3s16 p = blockpos + v3s16(0,0,0);
2689 addUpdateMeshTask(p, ack_to_server, urgent);
2691 catch(InvalidPositionException &e){}
2693 if(nodepos.X == blockpos_relative.X){
2695 v3s16 p = blockpos + v3s16(-1,0,0);
2696 addUpdateMeshTask(p, false, urgent);
2698 catch(InvalidPositionException &e){}
2700 if(nodepos.Y == blockpos_relative.Y){
2702 v3s16 p = blockpos + v3s16(0,-1,0);
2703 addUpdateMeshTask(p, false, urgent);
2705 catch(InvalidPositionException &e){}
2707 if(nodepos.Z == blockpos_relative.Z){
2709 v3s16 p = blockpos + v3s16(0,0,-1);
2710 addUpdateMeshTask(p, false, urgent);
2712 catch(InvalidPositionException &e){}
2716 ClientEvent Client::getClientEvent()
2718 if(m_client_event_queue.size() == 0)
2721 event.type = CE_NONE;
2724 return m_client_event_queue.pop_front();
2727 float Client::mediaReceiveProgress()
2729 if (m_media_downloader)
2730 return m_media_downloader->getProgress();
2732 return 1.0; // downloader only exists when not yet done
2735 void draw_load_screen(const std::wstring &text,
2736 IrrlichtDevice* device, gui::IGUIFont* font,
2737 float dtime=0 ,int percent=0, bool clouds=true);
2738 void Client::afterContentReceived(IrrlichtDevice *device, gui::IGUIFont* font)
2740 infostream<<"Client::afterContentReceived() started"<<std::endl;
2741 assert(m_itemdef_received);
2742 assert(m_nodedef_received);
2743 assert(mediaReceived());
2745 // Rebuild inherited images and recreate textures
2746 infostream<<"- Rebuilding images and textures"<<std::endl;
2747 m_tsrc->rebuildImagesAndTextures();
2750 infostream<<"- Rebuilding shaders"<<std::endl;
2751 m_shsrc->rebuildShaders();
2753 // Update node aliases
2754 infostream<<"- Updating node aliases"<<std::endl;
2755 m_nodedef->updateAliases(m_itemdef);
2757 // Update node textures
2758 infostream<<"- Updating node textures"<<std::endl;
2759 m_nodedef->updateTextures(m_tsrc);
2761 // Preload item textures and meshes if configured to
2762 if(g_settings->getBool("preload_item_visuals"))
2764 verbosestream<<"Updating item textures and meshes"<<std::endl;
2765 wchar_t* text = wgettext("Item textures...");
2766 draw_load_screen(text,device,font,0,0);
2767 std::set<std::string> names = m_itemdef->getAll();
2768 size_t size = names.size();
2771 for(std::set<std::string>::const_iterator
2772 i = names.begin(); i != names.end(); ++i){
2773 // Asking for these caches the result
2774 m_itemdef->getInventoryTexture(*i, this);
2775 m_itemdef->getWieldMesh(*i, this);
2777 percent = count*100/size;
2778 if (count%50 == 0) // only update every 50 item
2779 draw_load_screen(text,device,font,0,percent);
2784 // Start mesh update thread after setting up content definitions
2785 infostream<<"- Starting mesh update thread"<<std::endl;
2786 m_mesh_update_thread.Start();
2788 infostream<<"Client::afterContentReceived() done"<<std::endl;
2791 float Client::getRTT(void)
2794 return m_con.GetPeerAvgRTT(PEER_ID_SERVER);
2795 } catch(con::PeerNotFoundException &e){
2800 // IGameDef interface
2802 IItemDefManager* Client::getItemDefManager()
2806 INodeDefManager* Client::getNodeDefManager()
2810 ICraftDefManager* Client::getCraftDefManager()
2813 //return m_craftdef;
2815 ITextureSource* Client::getTextureSource()
2819 IShaderSource* Client::getShaderSource()
2823 u16 Client::allocateUnknownNodeId(const std::string &name)
2825 errorstream<<"Client::allocateUnknownNodeId(): "
2826 <<"Client cannot allocate node IDs"<<std::endl;
2828 return CONTENT_IGNORE;
2830 ISoundManager* Client::getSoundManager()
2834 MtEventManager* Client::getEventManager()