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"
55 #include <curl/curl.h>
58 static std::string getMediaCacheDir()
60 return porting::path_user + DIR_DELIM + "cache" + DIR_DELIM + "media";
67 QueuedMeshUpdate::QueuedMeshUpdate():
70 ack_block_to_server(false)
74 QueuedMeshUpdate::~QueuedMeshUpdate()
84 MeshUpdateQueue::MeshUpdateQueue()
88 MeshUpdateQueue::~MeshUpdateQueue()
90 JMutexAutoLock lock(m_mutex);
92 for(std::vector<QueuedMeshUpdate*>::iterator
94 i != m_queue.end(); i++)
96 QueuedMeshUpdate *q = *i;
102 peer_id=0 adds with nobody to send to
104 void MeshUpdateQueue::addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_server, bool urgent)
106 DSTACK(__FUNCTION_NAME);
110 JMutexAutoLock lock(m_mutex);
116 Find if block is already in queue.
117 If it is, update the data and quit.
119 for(std::vector<QueuedMeshUpdate*>::iterator
121 i != m_queue.end(); i++)
123 QueuedMeshUpdate *q = *i;
129 if(ack_block_to_server)
130 q->ack_block_to_server = true;
138 QueuedMeshUpdate *q = new QueuedMeshUpdate;
141 q->ack_block_to_server = ack_block_to_server;
142 m_queue.push_back(q);
145 // Returned pointer must be deleted
146 // Returns NULL if queue is empty
147 QueuedMeshUpdate * MeshUpdateQueue::pop()
149 JMutexAutoLock lock(m_mutex);
151 bool must_be_urgent = !m_urgents.empty();
152 for(std::vector<QueuedMeshUpdate*>::iterator
154 i != m_queue.end(); i++)
156 QueuedMeshUpdate *q = *i;
157 if(must_be_urgent && m_urgents.count(q->p) == 0)
160 m_urgents.erase(q->p);
170 void * MeshUpdateThread::Thread()
174 log_register_thread("MeshUpdateThread");
176 DSTACK(__FUNCTION_NAME);
178 BEGIN_DEBUG_EXCEPTION_HANDLER
182 /*// Wait for output queue to flush.
183 // Allow 2 in queue, this makes less frametime jitter.
184 // Umm actually, there is no much difference
185 if(m_queue_out.size() >= 2)
191 QueuedMeshUpdate *q = m_queue_in.pop();
198 ScopeProfiler sp(g_profiler, "Client: Mesh making");
200 MapBlockMesh *mesh_new = new MapBlockMesh(q->data);
201 if(mesh_new->getMesh()->getMeshBufferCount() == 0)
210 r.ack_block_to_server = q->ack_block_to_server;
212 /*infostream<<"MeshUpdateThread: Processed "
213 <<"("<<q->p.X<<","<<q->p.Y<<","<<q->p.Z<<")"
216 m_queue_out.push_back(r);
221 END_DEBUG_EXCEPTION_HANDLER(errorstream)
231 IrrlichtDevice *device,
232 const char *playername,
233 std::string password,
234 MapDrawControl &control,
235 IWritableTextureSource *tsrc,
236 IWritableShaderSource *shsrc,
237 IWritableItemDefManager *itemdef,
238 IWritableNodeDefManager *nodedef,
239 ISoundManager *sound,
240 MtEventManager *event,
249 m_mesh_update_thread(this),
251 new ClientMap(this, this, control,
252 device->getSceneManager()->getRootSceneNode(),
253 device->getSceneManager(), 666),
254 device->getSceneManager(),
257 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, ipv6, this),
259 m_server_ser_ver(SER_FMT_VER_INVALID),
261 m_inventory_updated(false),
262 m_inventory_from_server(NULL),
263 m_inventory_from_server_age(0.0),
268 m_password(password),
269 m_access_denied(false),
270 m_itemdef_received(false),
271 m_nodedef_received(false),
272 m_media_downloader(new ClientMediaDownloader()),
273 m_time_of_day_set(false),
274 m_last_time_of_day_f(-1),
275 m_time_of_day_update_timer(0),
276 m_recommended_send_interval(0.1),
277 m_removed_sounds_check_timer(0)
279 m_packetcounter_timer = 0.0;
280 //m_delete_unused_sectors_timer = 0.0;
281 m_connection_reinit_timer = 0.0;
282 m_avg_rtt_timer = 0.0;
283 m_playerpos_send_timer = 0.0;
284 m_ignore_damage_timer = 0.0;
290 Player *player = new LocalPlayer(this);
292 player->updateName(playername);
294 m_env.addPlayer(player);
301 //JMutexAutoLock conlock(m_con_mutex); //bulk comment-out
305 m_mesh_update_thread.setRun(false);
306 while(m_mesh_update_thread.IsRunning())
308 while(!m_mesh_update_thread.m_queue_out.empty()) {
309 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_front();
314 delete m_inventory_from_server;
316 // Delete detached inventories
318 for(std::map<std::string, Inventory*>::iterator
319 i = m_detached_inventories.begin();
320 i != m_detached_inventories.end(); i++){
325 // cleanup 3d model meshes on client shutdown
326 while (m_device->getSceneManager()->getMeshCache()->getMeshCount() != 0) {
327 scene::IAnimatedMesh * mesh =
328 m_device->getSceneManager()->getMeshCache()->getMeshByIndex(0);
331 m_device->getSceneManager()->getMeshCache()->removeMesh(mesh);
335 void Client::connect(Address address)
337 DSTACK(__FUNCTION_NAME);
338 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
339 m_con.SetTimeoutMs(0);
340 m_con.Connect(address);
343 bool Client::connectedAndInitialized()
345 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
347 if(m_con.Connected() == false)
350 if(m_server_ser_ver == SER_FMT_VER_INVALID)
356 void Client::step(float dtime)
358 DSTACK(__FUNCTION_NAME);
364 if(m_ignore_damage_timer > dtime)
365 m_ignore_damage_timer -= dtime;
367 m_ignore_damage_timer = 0.0;
369 m_animation_time += dtime;
370 if(m_animation_time > 60.0)
371 m_animation_time -= 60.0;
373 m_time_of_day_update_timer += dtime;
375 //infostream<<"Client steps "<<dtime<<std::endl;
378 //TimeTaker timer("ReceiveAll()", m_device);
384 //TimeTaker timer("m_con_mutex + m_con.RunTimeouts()", m_device);
386 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
387 m_con.RunTimeouts(dtime);
394 float &counter = m_packetcounter_timer;
400 infostream<<"Client packetcounter (20s):"<<std::endl;
401 m_packetcounter.print(infostream);
402 m_packetcounter.clear();
406 // Get connection status
407 bool connected = connectedAndInitialized();
412 Delete unused sectors
414 NOTE: This jams the game for a while because deleting sectors
418 float &counter = m_delete_unused_sectors_timer;
426 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
428 core::list<v3s16> deleted_blocks;
430 float delete_unused_sectors_timeout =
431 g_settings->getFloat("client_delete_unused_sectors_timeout");
433 // Delete sector blocks
434 /*u32 num = m_env.getMap().unloadUnusedData
435 (delete_unused_sectors_timeout,
436 true, &deleted_blocks);*/
438 // Delete whole sectors
439 m_env.getMap().unloadUnusedData
440 (delete_unused_sectors_timeout,
443 if(deleted_blocks.size() > 0)
445 /*infostream<<"Client: Deleted blocks of "<<num
446 <<" unused sectors"<<std::endl;*/
447 /*infostream<<"Client: Deleted "<<num
448 <<" unused sectors"<<std::endl;*/
454 // Env is locked so con can be locked.
455 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
457 core::list<v3s16>::Iterator i = deleted_blocks.begin();
458 core::list<v3s16> sendlist;
461 if(sendlist.size() == 255 || i == deleted_blocks.end())
463 if(sendlist.size() == 0)
472 u32 replysize = 2+1+6*sendlist.size();
473 SharedBuffer<u8> reply(replysize);
474 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
475 reply[2] = sendlist.size();
477 for(core::list<v3s16>::Iterator
478 j = sendlist.begin();
479 j != sendlist.end(); j++)
481 writeV3S16(&reply[2+1+6*k], *j);
484 m_con.Send(PEER_ID_SERVER, 1, reply, true);
486 if(i == deleted_blocks.end())
492 sendlist.push_back(*i);
500 if(connected == false)
502 float &counter = m_connection_reinit_timer;
508 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
510 Player *myplayer = m_env.getLocalPlayer();
511 assert(myplayer != NULL);
513 // Send TOSERVER_INIT
514 // [0] u16 TOSERVER_INIT
515 // [2] u8 SER_FMT_VER_HIGHEST_READ
516 // [3] u8[20] player_name
517 // [23] u8[28] password (new in some version)
518 // [51] u16 minimum supported network protocol version (added sometime)
519 // [53] u16 maximum supported network protocol version (added later than the previous one)
520 SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2+2);
521 writeU16(&data[0], TOSERVER_INIT);
522 writeU8(&data[2], SER_FMT_VER_HIGHEST_READ);
524 memset((char*)&data[3], 0, PLAYERNAME_SIZE);
525 snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
527 /*infostream<<"Client: sending initial password hash: \""<<m_password<<"\""
530 memset((char*)&data[23], 0, PASSWORD_SIZE);
531 snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
533 writeU16(&data[51], CLIENT_PROTOCOL_VERSION_MIN);
534 writeU16(&data[53], CLIENT_PROTOCOL_VERSION_MAX);
536 // Send as unreliable
537 Send(0, data, false);
540 // Not connected, return
545 Do stuff if connected
549 Run Map's timers and unload unused data
551 const float map_timer_and_unload_dtime = 5.25;
552 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
554 ScopeProfiler sp(g_profiler, "Client: map timer and unload");
555 std::list<v3s16> deleted_blocks;
556 m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
557 g_settings->getFloat("client_unload_unused_data_timeout"),
560 /*if(deleted_blocks.size() > 0)
561 infostream<<"Client: Unloaded "<<deleted_blocks.size()
562 <<" unused blocks"<<std::endl;*/
566 NOTE: This loop is intentionally iterated the way it is.
569 std::list<v3s16>::iterator i = deleted_blocks.begin();
570 std::list<v3s16> sendlist;
573 if(sendlist.size() == 255 || i == deleted_blocks.end())
575 if(sendlist.size() == 0)
584 u32 replysize = 2+1+6*sendlist.size();
585 SharedBuffer<u8> reply(replysize);
586 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
587 reply[2] = sendlist.size();
589 for(std::list<v3s16>::iterator
590 j = sendlist.begin();
591 j != sendlist.end(); ++j)
593 writeV3S16(&reply[2+1+6*k], *j);
596 m_con.Send(PEER_ID_SERVER, 1, reply, true);
598 if(i == deleted_blocks.end())
604 sendlist.push_back(*i);
614 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
616 // Control local player (0ms)
617 LocalPlayer *player = m_env.getLocalPlayer();
618 assert(player != NULL);
619 player->applyControl(dtime);
621 //TimeTaker envtimer("env step", m_device);
630 ClientEnvEvent event = m_env.getClientEvent();
631 if(event.type == CEE_NONE)
635 else if(event.type == CEE_PLAYER_DAMAGE)
637 if(m_ignore_damage_timer <= 0)
639 u8 damage = event.player_damage.amount;
641 if(event.player_damage.send_to_server)
644 // Add to ClientEvent queue
646 event.type = CE_PLAYER_DAMAGE;
647 event.player_damage.amount = damage;
648 m_client_event_queue.push_back(event);
651 else if(event.type == CEE_PLAYER_BREATH)
653 u16 breath = event.player_breath.amount;
663 float &counter = m_avg_rtt_timer;
668 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
669 // connectedAndInitialized() is true, peer exists.
670 float avg_rtt = m_con.GetPeerAvgRTT(PEER_ID_SERVER);
671 infostream<<"Client: avg_rtt="<<avg_rtt<<std::endl;
676 Send player position to server
679 float &counter = m_playerpos_send_timer;
681 if(counter >= m_recommended_send_interval)
689 Replace updated meshes
692 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
694 //TimeTaker timer("** Processing mesh update result queue");
697 /*infostream<<"Mesh update result queue size is "
698 <<m_mesh_update_thread.m_queue_out.size()
701 int num_processed_meshes = 0;
702 while(!m_mesh_update_thread.m_queue_out.empty())
704 num_processed_meshes++;
705 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_front();
706 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
709 //JMutexAutoLock lock(block->mesh_mutex);
711 // Delete the old mesh
712 if(block->mesh != NULL)
714 // TODO: Remove hardware buffers of meshbuffers of block->mesh
719 // Replace with the new mesh
720 block->mesh = r.mesh;
724 if(r.ack_block_to_server)
726 /*infostream<<"Client: ACK block ("<<r.p.X<<","<<r.p.Y
727 <<","<<r.p.Z<<")"<<std::endl;*/
738 u32 replysize = 2+1+6;
739 SharedBuffer<u8> reply(replysize);
740 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
742 writeV3S16(&reply[3], r.p);
744 m_con.Send(PEER_ID_SERVER, 1, reply, true);
747 if(num_processed_meshes > 0)
748 g_profiler->graphAdd("num_processed_meshes", num_processed_meshes);
754 if (m_media_downloader && m_media_downloader->isStarted()) {
755 m_media_downloader->step(this);
756 if (m_media_downloader->isDone()) {
757 delete m_media_downloader;
758 m_media_downloader = NULL;
763 If the server didn't update the inventory in a while, revert
764 the local inventory (so the player notices the lag problem
765 and knows something is wrong).
767 if(m_inventory_from_server)
769 float interval = 10.0;
770 float count_before = floor(m_inventory_from_server_age / interval);
772 m_inventory_from_server_age += dtime;
774 float count_after = floor(m_inventory_from_server_age / interval);
776 if(count_after != count_before)
778 // Do this every <interval> seconds after TOCLIENT_INVENTORY
779 // Reset the locally changed inventory to the authoritative inventory
780 Player *player = m_env.getLocalPlayer();
781 player->inventory = *m_inventory_from_server;
782 m_inventory_updated = true;
787 Update positions of sounds attached to objects
790 for(std::map<int, u16>::iterator
791 i = m_sounds_to_objects.begin();
792 i != m_sounds_to_objects.end(); i++)
794 int client_id = i->first;
795 u16 object_id = i->second;
796 ClientActiveObject *cao = m_env.getActiveObject(object_id);
799 v3f pos = cao->getPosition();
800 m_sound->updateSoundPosition(client_id, pos);
805 Handle removed remotely initiated sounds
807 m_removed_sounds_check_timer += dtime;
808 if(m_removed_sounds_check_timer >= 2.32)
810 m_removed_sounds_check_timer = 0;
811 // Find removed sounds and clear references to them
812 std::set<s32> removed_server_ids;
813 for(std::map<s32, int>::iterator
814 i = m_sounds_server_to_client.begin();
815 i != m_sounds_server_to_client.end();)
817 s32 server_id = i->first;
818 int client_id = i->second;
820 if(!m_sound->soundExists(client_id)){
821 m_sounds_server_to_client.erase(server_id);
822 m_sounds_client_to_server.erase(client_id);
823 m_sounds_to_objects.erase(client_id);
824 removed_server_ids.insert(server_id);
828 if(removed_server_ids.size() != 0)
830 std::ostringstream os(std::ios_base::binary);
831 writeU16(os, TOSERVER_REMOVED_SOUNDS);
832 writeU16(os, removed_server_ids.size());
833 for(std::set<s32>::iterator i = removed_server_ids.begin();
834 i != removed_server_ids.end(); i++)
836 std::string s = os.str();
837 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
844 bool Client::loadMedia(const std::string &data, const std::string &filename)
846 // Silly irrlicht's const-incorrectness
847 Buffer<char> data_rw(data.c_str(), data.size());
851 const char *image_ext[] = {
852 ".png", ".jpg", ".bmp", ".tga",
853 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
856 name = removeStringEnd(filename, image_ext);
859 verbosestream<<"Client: Attempting to load image "
860 <<"file \""<<filename<<"\""<<std::endl;
862 io::IFileSystem *irrfs = m_device->getFileSystem();
863 video::IVideoDriver *vdrv = m_device->getVideoDriver();
865 // Create an irrlicht memory file
866 io::IReadFile *rfile = irrfs->createMemoryReadFile(
867 *data_rw, data_rw.getSize(), "_tempreadfile");
870 video::IImage *img = vdrv->createImageFromFile(rfile);
872 errorstream<<"Client: Cannot create image from data of "
873 <<"file \""<<filename<<"\""<<std::endl;
878 m_tsrc->insertSourceImage(filename, img);
885 const char *sound_ext[] = {
886 ".0.ogg", ".1.ogg", ".2.ogg", ".3.ogg", ".4.ogg",
887 ".5.ogg", ".6.ogg", ".7.ogg", ".8.ogg", ".9.ogg",
890 name = removeStringEnd(filename, sound_ext);
893 verbosestream<<"Client: Attempting to load sound "
894 <<"file \""<<filename<<"\""<<std::endl;
895 m_sound->loadSoundData(name, data);
899 const char *model_ext[] = {
900 ".x", ".b3d", ".md2", ".obj",
903 name = removeStringEnd(filename, model_ext);
906 verbosestream<<"Client: Storing model into Irrlicht: "
907 <<"\""<<filename<<"\""<<std::endl;
908 scene::ISceneManager *smgr = m_device->getSceneManager();
910 //check if mesh was already cached
911 scene::IAnimatedMesh *mesh =
912 smgr->getMeshCache()->getMeshByName(filename.c_str());
915 errorstream << "Multiple models with name: " << filename.c_str() <<
916 " found replacing previous model!" << std::endl;
918 smgr->getMeshCache()->removeMesh(mesh);
922 io::IFileSystem *irrfs = m_device->getFileSystem();
923 io::IReadFile *rfile = irrfs->createMemoryReadFile(
924 *data_rw, data_rw.getSize(), filename.c_str());
927 mesh = smgr->getMesh(rfile);
928 smgr->getMeshCache()->addMesh(filename.c_str(), mesh);
933 errorstream<<"Client: Don't know how to load file \""
934 <<filename<<"\""<<std::endl;
938 // Virtual methods from con::PeerHandler
939 void Client::peerAdded(con::Peer *peer)
941 infostream<<"Client::peerAdded(): peer->id="
942 <<peer->id<<std::endl;
944 void Client::deletingPeer(con::Peer *peer, bool timeout)
946 infostream<<"Client::deletingPeer(): "
947 "Server Peer is getting deleted "
948 <<"(timeout="<<timeout<<")"<<std::endl;
953 u16 number of files requested
959 void Client::request_media(const std::list<std::string> &file_requests)
961 std::ostringstream os(std::ios_base::binary);
962 writeU16(os, TOSERVER_REQUEST_MEDIA);
963 writeU16(os, file_requests.size());
965 for(std::list<std::string>::const_iterator i = file_requests.begin();
966 i != file_requests.end(); ++i) {
967 os<<serializeString(*i);
971 std::string s = os.str();
972 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
975 infostream<<"Client: Sending media request list to server ("
976 <<file_requests.size()<<" files)"<<std::endl;
979 void Client::received_media()
981 // notify server we received everything
982 std::ostringstream os(std::ios_base::binary);
983 writeU16(os, TOSERVER_RECEIVED_MEDIA);
984 std::string s = os.str();
985 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
988 infostream<<"Client: Notifying server that we received all media"
992 void Client::ReceiveAll()
994 DSTACK(__FUNCTION_NAME);
995 u32 start_ms = porting::getTimeMs();
998 // Limit time even if there would be huge amounts of data to
1000 if(porting::getTimeMs() > start_ms + 100)
1005 g_profiler->graphAdd("client_received_packets", 1);
1007 catch(con::NoIncomingDataException &e)
1011 catch(con::InvalidIncomingDataException &e)
1013 infostream<<"Client::ReceiveAll(): "
1014 "InvalidIncomingDataException: what()="
1015 <<e.what()<<std::endl;
1020 void Client::Receive()
1022 DSTACK(__FUNCTION_NAME);
1023 SharedBuffer<u8> data;
1027 //TimeTaker t1("con mutex and receive", m_device);
1028 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1029 datasize = m_con.Receive(sender_peer_id, data);
1031 //TimeTaker t1("ProcessData", m_device);
1032 ProcessData(*data, datasize, sender_peer_id);
1036 sender_peer_id given to this shall be quaranteed to be a valid peer
1038 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
1040 DSTACK(__FUNCTION_NAME);
1042 // Ignore packets that don't even fit a command
1045 m_packetcounter.add(60000);
1049 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
1051 //infostream<<"Client: received command="<<command<<std::endl;
1052 m_packetcounter.add((u16)command);
1055 If this check is removed, be sure to change the queue
1056 system to know the ids
1058 if(sender_peer_id != PEER_ID_SERVER)
1060 infostream<<"Client::ProcessData(): Discarding data not "
1061 "coming from server: peer_id="<<sender_peer_id
1066 u8 ser_version = m_server_ser_ver;
1068 //infostream<<"Client received command="<<(int)command<<std::endl;
1070 if(command == TOCLIENT_INIT)
1075 u8 deployed = data[2];
1077 infostream<<"Client: TOCLIENT_INIT received with "
1078 "deployed="<<((int)deployed&0xff)<<std::endl;
1080 if(!ser_ver_supported(deployed))
1082 infostream<<"Client: TOCLIENT_INIT: Server sent "
1083 <<"unsupported ser_fmt_ver"<<std::endl;
1087 m_server_ser_ver = deployed;
1089 // Get player position
1090 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
1091 if(datasize >= 2+1+6)
1092 playerpos_s16 = readV3S16(&data[2+1]);
1093 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
1096 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1098 // Set player position
1099 Player *player = m_env.getLocalPlayer();
1100 assert(player != NULL);
1101 player->setPosition(playerpos_f);
1104 if(datasize >= 2+1+6+8)
1107 m_map_seed = readU64(&data[2+1+6]);
1108 infostream<<"Client: received map seed: "<<m_map_seed<<std::endl;
1111 if(datasize >= 2+1+6+8+4)
1114 m_recommended_send_interval = readF1000(&data[2+1+6+8]);
1115 infostream<<"Client: received recommended send interval "
1116 <<m_recommended_send_interval<<std::endl;
1121 SharedBuffer<u8> reply(replysize);
1122 writeU16(&reply[0], TOSERVER_INIT2);
1124 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1129 if(command == TOCLIENT_ACCESS_DENIED)
1131 // The server didn't like our password. Note, this needs
1132 // to be processed even if the serialisation format has
1133 // not been agreed yet, the same as TOCLIENT_INIT.
1134 m_access_denied = true;
1135 m_access_denied_reason = L"Unknown";
1138 std::string datastring((char*)&data[2], datasize-2);
1139 std::istringstream is(datastring, std::ios_base::binary);
1140 m_access_denied_reason = deSerializeWideString(is);
1145 if(ser_version == SER_FMT_VER_INVALID)
1147 infostream<<"Client: Server serialization"
1148 " format invalid or not initialized."
1149 " Skipping incoming command="<<command<<std::endl;
1153 // Just here to avoid putting the two if's together when
1154 // making some copypasta
1157 if(command == TOCLIENT_REMOVENODE)
1162 p.X = readS16(&data[2]);
1163 p.Y = readS16(&data[4]);
1164 p.Z = readS16(&data[6]);
1166 //TimeTaker t1("TOCLIENT_REMOVENODE");
1170 else if(command == TOCLIENT_ADDNODE)
1172 if(datasize < 8 + MapNode::serializedLength(ser_version))
1176 p.X = readS16(&data[2]);
1177 p.Y = readS16(&data[4]);
1178 p.Z = readS16(&data[6]);
1180 //TimeTaker t1("TOCLIENT_ADDNODE");
1183 n.deSerialize(&data[8], ser_version);
1185 bool remove_metadata = true;
1186 u32 index = 8 + MapNode::serializedLength(ser_version);
1187 if ((datasize >= index+1) && data[index]){
1188 remove_metadata = false;
1191 addNode(p, n, remove_metadata);
1193 else if(command == TOCLIENT_BLOCKDATA)
1195 // Ignore too small packet
1200 p.X = readS16(&data[2]);
1201 p.Y = readS16(&data[4]);
1202 p.Z = readS16(&data[6]);
1204 /*infostream<<"Client: Thread: BLOCKDATA for ("
1205 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1206 /*infostream<<"Client: Thread: BLOCKDATA for ("
1207 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1209 std::string datastring((char*)&data[8], datasize-8);
1210 std::istringstream istr(datastring, std::ios_base::binary);
1215 v2s16 p2d(p.X, p.Z);
1216 sector = m_env.getMap().emergeSector(p2d);
1218 assert(sector->getPos() == p2d);
1220 //TimeTaker timer("MapBlock deSerialize");
1223 block = sector->getBlockNoCreateNoEx(p.Y);
1227 Update an existing block
1229 //infostream<<"Updating"<<std::endl;
1230 block->deSerialize(istr, ser_version, false);
1231 block->deSerializeNetworkSpecific(istr);
1238 //infostream<<"Creating new"<<std::endl;
1239 block = new MapBlock(&m_env.getMap(), p, this);
1240 block->deSerialize(istr, ser_version, false);
1241 block->deSerializeNetworkSpecific(istr);
1242 sector->insertBlock(block);
1256 u32 replysize = 2+1+6;
1257 SharedBuffer<u8> reply(replysize);
1258 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
1260 writeV3S16(&reply[3], p);
1262 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1266 Add it to mesh update queue and set it to be acknowledged after update.
1268 //infostream<<"Adding mesh update task for received block"<<std::endl;
1269 addUpdateMeshTaskWithEdge(p, true);
1271 else if(command == TOCLIENT_INVENTORY)
1276 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
1279 //TimeTaker t2("mutex locking", m_device);
1280 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1283 //TimeTaker t3("istringstream init", m_device);
1284 std::string datastring((char*)&data[2], datasize-2);
1285 std::istringstream is(datastring, std::ios_base::binary);
1288 //TimeTaker t4("player get", m_device);
1289 Player *player = m_env.getLocalPlayer();
1290 assert(player != NULL);
1293 //TimeTaker t1("inventory.deSerialize()", m_device);
1294 player->inventory.deSerialize(is);
1297 m_inventory_updated = true;
1299 delete m_inventory_from_server;
1300 m_inventory_from_server = new Inventory(player->inventory);
1301 m_inventory_from_server_age = 0.0;
1303 //infostream<<"Client got player inventory:"<<std::endl;
1304 //player->inventory.print(infostream);
1307 else if(command == TOCLIENT_TIME_OF_DAY)
1312 u16 time_of_day = readU16(&data[2]);
1313 time_of_day = time_of_day % 24000;
1314 //infostream<<"Client: time_of_day="<<time_of_day<<std::endl;
1315 float time_speed = 0;
1316 if(datasize >= 2 + 2 + 4){
1317 time_speed = readF1000(&data[4]);
1319 // Old message; try to approximate speed of time by ourselves
1320 float time_of_day_f = (float)time_of_day / 24000.0;
1321 float tod_diff_f = 0;
1322 if(time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
1323 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0;
1325 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
1326 m_last_time_of_day_f = time_of_day_f;
1327 float time_diff = m_time_of_day_update_timer;
1328 m_time_of_day_update_timer = 0;
1329 if(m_time_of_day_set){
1330 time_speed = 3600.0*24.0 * tod_diff_f / time_diff;
1331 infostream<<"Client: Measured time_of_day speed (old format): "
1332 <<time_speed<<" tod_diff_f="<<tod_diff_f
1333 <<" time_diff="<<time_diff<<std::endl;
1337 // Update environment
1338 m_env.setTimeOfDay(time_of_day);
1339 m_env.setTimeOfDaySpeed(time_speed);
1340 m_time_of_day_set = true;
1342 u32 dr = m_env.getDayNightRatio();
1343 verbosestream<<"Client: time_of_day="<<time_of_day
1344 <<" time_speed="<<time_speed
1345 <<" dr="<<dr<<std::endl;
1347 else if(command == TOCLIENT_CHAT_MESSAGE)
1355 std::string datastring((char*)&data[2], datasize-2);
1356 std::istringstream is(datastring, std::ios_base::binary);
1359 is.read((char*)buf, 2);
1360 u16 len = readU16(buf);
1362 std::wstring message;
1363 for(u16 i=0; i<len; i++)
1365 is.read((char*)buf, 2);
1366 message += (wchar_t)readU16(buf);
1369 /*infostream<<"Client received chat message: "
1370 <<wide_to_narrow(message)<<std::endl;*/
1372 m_chat_queue.push_back(message);
1374 else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1376 //if(g_settings->getBool("enable_experimental"))
1380 u16 count of removed objects
1381 for all removed objects {
1384 u16 count of added objects
1385 for all added objects {
1388 u32 initialization data length
1389 string initialization data
1394 // Get all data except the command number
1395 std::string datastring((char*)&data[2], datasize-2);
1396 // Throw them in an istringstream
1397 std::istringstream is(datastring, std::ios_base::binary);
1401 // Read removed objects
1403 u16 removed_count = readU16((u8*)buf);
1404 for(u16 i=0; i<removed_count; i++)
1407 u16 id = readU16((u8*)buf);
1410 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1411 m_env.removeActiveObject(id);
1415 // Read added objects
1417 u16 added_count = readU16((u8*)buf);
1418 for(u16 i=0; i<added_count; i++)
1421 u16 id = readU16((u8*)buf);
1423 u8 type = readU8((u8*)buf);
1424 std::string data = deSerializeLongString(is);
1427 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1428 m_env.addActiveObject(id, type, data);
1433 else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1435 //if(g_settings->getBool("enable_experimental"))
1447 // Get all data except the command number
1448 std::string datastring((char*)&data[2], datasize-2);
1449 // Throw them in an istringstream
1450 std::istringstream is(datastring, std::ios_base::binary);
1452 while(is.eof() == false)
1456 u16 id = readU16((u8*)buf);
1460 u16 message_size = readU16((u8*)buf);
1461 std::string message;
1462 message.reserve(message_size);
1463 for(u16 i=0; i<message_size; i++)
1466 message.append(buf, 1);
1468 // Pass on to the environment
1470 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1471 m_env.processActiveObjectMessage(id, message);
1476 else if(command == TOCLIENT_MOVEMENT)
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);
1483 player->movement_acceleration_default = readF1000(is) * BS;
1484 player->movement_acceleration_air = readF1000(is) * BS;
1485 player->movement_acceleration_fast = readF1000(is) * BS;
1486 player->movement_speed_walk = readF1000(is) * BS;
1487 player->movement_speed_crouch = readF1000(is) * BS;
1488 player->movement_speed_fast = readF1000(is) * BS;
1489 player->movement_speed_climb = readF1000(is) * BS;
1490 player->movement_speed_jump = readF1000(is) * BS;
1491 player->movement_liquid_fluidity = readF1000(is) * BS;
1492 player->movement_liquid_fluidity_smooth = readF1000(is) * BS;
1493 player->movement_liquid_sink = readF1000(is) * BS;
1494 player->movement_gravity = readF1000(is) * BS;
1496 else if(command == TOCLIENT_HP)
1498 std::string datastring((char*)&data[2], datasize-2);
1499 std::istringstream is(datastring, std::ios_base::binary);
1500 Player *player = m_env.getLocalPlayer();
1501 assert(player != NULL);
1502 u8 oldhp = player->hp;
1508 // Add to ClientEvent queue
1510 event.type = CE_PLAYER_DAMAGE;
1511 event.player_damage.amount = oldhp - hp;
1512 m_client_event_queue.push_back(event);
1515 else if(command == TOCLIENT_BREATH)
1517 std::string datastring((char*)&data[2], datasize-2);
1518 std::istringstream is(datastring, std::ios_base::binary);
1519 Player *player = m_env.getLocalPlayer();
1520 assert(player != NULL);
1521 u16 breath = readU16(is);
1522 player->setBreath(breath) ;
1524 else if(command == TOCLIENT_MOVE_PLAYER)
1526 std::string datastring((char*)&data[2], datasize-2);
1527 std::istringstream is(datastring, std::ios_base::binary);
1528 Player *player = m_env.getLocalPlayer();
1529 assert(player != NULL);
1530 v3f pos = readV3F1000(is);
1531 f32 pitch = readF1000(is);
1532 f32 yaw = readF1000(is);
1533 player->setPosition(pos);
1534 /*player->setPitch(pitch);
1535 player->setYaw(yaw);*/
1537 infostream<<"Client got TOCLIENT_MOVE_PLAYER"
1538 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1544 Add to ClientEvent queue.
1545 This has to be sent to the main program because otherwise
1546 it would just force the pitch and yaw values to whatever
1547 the camera points to.
1550 event.type = CE_PLAYER_FORCE_MOVE;
1551 event.player_force_move.pitch = pitch;
1552 event.player_force_move.yaw = yaw;
1553 m_client_event_queue.push_back(event);
1555 // Ignore damage for a few seconds, so that the player doesn't
1556 // get damage from falling on ground
1557 m_ignore_damage_timer = 3.0;
1559 else if(command == TOCLIENT_PLAYERITEM)
1561 infostream<<"Client: WARNING: Ignoring TOCLIENT_PLAYERITEM"<<std::endl;
1563 else if(command == TOCLIENT_DEATHSCREEN)
1565 std::string datastring((char*)&data[2], datasize-2);
1566 std::istringstream is(datastring, std::ios_base::binary);
1568 bool set_camera_point_target = readU8(is);
1569 v3f camera_point_target = readV3F1000(is);
1572 event.type = CE_DEATHSCREEN;
1573 event.deathscreen.set_camera_point_target = set_camera_point_target;
1574 event.deathscreen.camera_point_target_x = camera_point_target.X;
1575 event.deathscreen.camera_point_target_y = camera_point_target.Y;
1576 event.deathscreen.camera_point_target_z = camera_point_target.Z;
1577 m_client_event_queue.push_back(event);
1579 else if(command == TOCLIENT_ANNOUNCE_MEDIA)
1581 std::string datastring((char*)&data[2], datasize-2);
1582 std::istringstream is(datastring, std::ios_base::binary);
1584 int num_files = readU16(is);
1586 infostream<<"Client: Received media announcement: packet size: "
1587 <<datasize<<std::endl;
1589 if (m_media_downloader == NULL ||
1590 m_media_downloader->isStarted()) {
1591 const char *problem = m_media_downloader ?
1592 "we already saw another announcement" :
1593 "all media has been received already";
1594 errorstream<<"Client: Received media announcement but "
1596 <<" files="<<num_files
1597 <<" size="<<datasize<<std::endl;
1601 // Mesh update thread must be stopped while
1602 // updating content definitions
1603 assert(!m_mesh_update_thread.IsRunning());
1605 for(int i=0; i<num_files; i++)
1607 std::string name = deSerializeString(is);
1608 std::string sha1_base64 = deSerializeString(is);
1609 std::string sha1_raw = base64_decode(sha1_base64);
1610 m_media_downloader->addFile(name, sha1_raw);
1613 std::vector<std::string> remote_media;
1615 Strfnd sf(deSerializeString(is));
1616 while(!sf.atend()) {
1617 std::string baseurl = trim(sf.next(","));
1619 m_media_downloader->addRemoteServer(baseurl);
1622 catch(SerializationError) {
1623 // not supported by server or turned off
1626 m_media_downloader->step(this);
1627 if (m_media_downloader->isDone()) {
1628 // might be done already if all media is in the cache
1629 delete m_media_downloader;
1630 m_media_downloader = NULL;
1633 else if(command == TOCLIENT_MEDIA)
1635 std::string datastring((char*)&data[2], datasize-2);
1636 std::istringstream is(datastring, std::ios_base::binary);
1640 u16 total number of file bunches
1641 u16 index of this bunch
1642 u32 number of files in this bunch
1650 int num_bunches = readU16(is);
1651 int bunch_i = readU16(is);
1652 u32 num_files = readU32(is);
1653 infostream<<"Client: Received files: bunch "<<bunch_i<<"/"
1654 <<num_bunches<<" files="<<num_files
1655 <<" size="<<datasize<<std::endl;
1660 if (m_media_downloader == NULL ||
1661 !m_media_downloader->isStarted()) {
1662 const char *problem = m_media_downloader ?
1663 "media has not been requested" :
1664 "all media has been received already";
1665 errorstream<<"Client: Received media but "
1667 <<" bunch "<<bunch_i<<"/"<<num_bunches
1668 <<" files="<<num_files
1669 <<" size="<<datasize<<std::endl;
1673 // Mesh update thread must be stopped while
1674 // updating content definitions
1675 assert(!m_mesh_update_thread.IsRunning());
1677 for(u32 i=0; i<num_files; i++){
1678 std::string name = deSerializeString(is);
1679 std::string data = deSerializeLongString(is);
1680 m_media_downloader->conventionalTransferDone(
1684 if (m_media_downloader->isDone()) {
1685 delete m_media_downloader;
1686 m_media_downloader = NULL;
1689 else if(command == TOCLIENT_TOOLDEF)
1691 infostream<<"Client: WARNING: Ignoring TOCLIENT_TOOLDEF"<<std::endl;
1693 else if(command == TOCLIENT_NODEDEF)
1695 infostream<<"Client: Received node definitions: packet size: "
1696 <<datasize<<std::endl;
1698 // Mesh update thread must be stopped while
1699 // updating content definitions
1700 assert(!m_mesh_update_thread.IsRunning());
1702 // Decompress node definitions
1703 std::string datastring((char*)&data[2], datasize-2);
1704 std::istringstream is(datastring, std::ios_base::binary);
1705 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1706 std::ostringstream tmp_os;
1707 decompressZlib(tmp_is, tmp_os);
1709 // Deserialize node definitions
1710 std::istringstream tmp_is2(tmp_os.str());
1711 m_nodedef->deSerialize(tmp_is2);
1712 m_nodedef_received = true;
1714 else if(command == TOCLIENT_CRAFTITEMDEF)
1716 infostream<<"Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF"<<std::endl;
1718 else if(command == TOCLIENT_ITEMDEF)
1720 infostream<<"Client: Received item definitions: packet size: "
1721 <<datasize<<std::endl;
1723 // Mesh update thread must be stopped while
1724 // updating content definitions
1725 assert(!m_mesh_update_thread.IsRunning());
1727 // Decompress item definitions
1728 std::string datastring((char*)&data[2], datasize-2);
1729 std::istringstream is(datastring, std::ios_base::binary);
1730 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1731 std::ostringstream tmp_os;
1732 decompressZlib(tmp_is, tmp_os);
1734 // Deserialize node definitions
1735 std::istringstream tmp_is2(tmp_os.str());
1736 m_itemdef->deSerialize(tmp_is2);
1737 m_itemdef_received = true;
1739 else if(command == TOCLIENT_PLAY_SOUND)
1741 std::string datastring((char*)&data[2], datasize-2);
1742 std::istringstream is(datastring, std::ios_base::binary);
1744 s32 server_id = readS32(is);
1745 std::string name = deSerializeString(is);
1746 float gain = readF1000(is);
1747 int type = readU8(is); // 0=local, 1=positional, 2=object
1748 v3f pos = readV3F1000(is);
1749 u16 object_id = readU16(is);
1750 bool loop = readU8(is);
1755 client_id = m_sound->playSound(name, loop, gain);
1757 case 1: // positional
1758 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1761 ClientActiveObject *cao = m_env.getActiveObject(object_id);
1763 pos = cao->getPosition();
1764 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1765 // TODO: Set up sound to move with object
1770 if(client_id != -1){
1771 m_sounds_server_to_client[server_id] = client_id;
1772 m_sounds_client_to_server[client_id] = server_id;
1774 m_sounds_to_objects[client_id] = object_id;
1777 else if(command == TOCLIENT_STOP_SOUND)
1779 std::string datastring((char*)&data[2], datasize-2);
1780 std::istringstream is(datastring, std::ios_base::binary);
1782 s32 server_id = readS32(is);
1783 std::map<s32, int>::iterator i =
1784 m_sounds_server_to_client.find(server_id);
1785 if(i != m_sounds_server_to_client.end()){
1786 int client_id = i->second;
1787 m_sound->stopSound(client_id);
1790 else if(command == TOCLIENT_PRIVILEGES)
1792 std::string datastring((char*)&data[2], datasize-2);
1793 std::istringstream is(datastring, std::ios_base::binary);
1795 m_privileges.clear();
1796 infostream<<"Client: Privileges updated: ";
1797 u16 num_privileges = readU16(is);
1798 for(u16 i=0; i<num_privileges; i++){
1799 std::string priv = deSerializeString(is);
1800 m_privileges.insert(priv);
1801 infostream<<priv<<" ";
1803 infostream<<std::endl;
1805 else if(command == TOCLIENT_INVENTORY_FORMSPEC)
1807 std::string datastring((char*)&data[2], datasize-2);
1808 std::istringstream is(datastring, std::ios_base::binary);
1810 // Store formspec in LocalPlayer
1811 Player *player = m_env.getLocalPlayer();
1812 assert(player != NULL);
1813 player->inventory_formspec = deSerializeLongString(is);
1815 else if(command == TOCLIENT_DETACHED_INVENTORY)
1817 std::string datastring((char*)&data[2], datasize-2);
1818 std::istringstream is(datastring, std::ios_base::binary);
1820 std::string name = deSerializeString(is);
1822 infostream<<"Client: Detached inventory update: \""<<name<<"\""<<std::endl;
1824 Inventory *inv = NULL;
1825 if(m_detached_inventories.count(name) > 0)
1826 inv = m_detached_inventories[name];
1828 inv = new Inventory(m_itemdef);
1829 m_detached_inventories[name] = inv;
1831 inv->deSerialize(is);
1833 else if(command == TOCLIENT_SHOW_FORMSPEC)
1835 std::string datastring((char*)&data[2], datasize-2);
1836 std::istringstream is(datastring, std::ios_base::binary);
1838 std::string formspec = deSerializeLongString(is);
1839 std::string formname = deSerializeString(is);
1842 event.type = CE_SHOW_FORMSPEC;
1843 // pointer is required as event is a struct only!
1844 // adding a std:string to a struct isn't possible
1845 event.show_formspec.formspec = new std::string(formspec);
1846 event.show_formspec.formname = new std::string(formname);
1847 m_client_event_queue.push_back(event);
1849 else if(command == TOCLIENT_SPAWN_PARTICLE)
1851 std::string datastring((char*)&data[2], datasize-2);
1852 std::istringstream is(datastring, std::ios_base::binary);
1854 v3f pos = readV3F1000(is);
1855 v3f vel = readV3F1000(is);
1856 v3f acc = readV3F1000(is);
1857 float expirationtime = readF1000(is);
1858 float size = readF1000(is);
1859 bool collisiondetection = readU8(is);
1860 std::string texture = deSerializeLongString(is);
1863 event.type = CE_SPAWN_PARTICLE;
1864 event.spawn_particle.pos = new v3f (pos);
1865 event.spawn_particle.vel = new v3f (vel);
1866 event.spawn_particle.acc = new v3f (acc);
1868 event.spawn_particle.expirationtime = expirationtime;
1869 event.spawn_particle.size = size;
1870 event.spawn_particle.collisiondetection =
1872 event.spawn_particle.texture = new std::string(texture);
1874 m_client_event_queue.push_back(event);
1876 else if(command == TOCLIENT_ADD_PARTICLESPAWNER)
1878 std::string datastring((char*)&data[2], datasize-2);
1879 std::istringstream is(datastring, std::ios_base::binary);
1881 u16 amount = readU16(is);
1882 float spawntime = readF1000(is);
1883 v3f minpos = readV3F1000(is);
1884 v3f maxpos = readV3F1000(is);
1885 v3f minvel = readV3F1000(is);
1886 v3f maxvel = readV3F1000(is);
1887 v3f minacc = readV3F1000(is);
1888 v3f maxacc = readV3F1000(is);
1889 float minexptime = readF1000(is);
1890 float maxexptime = readF1000(is);
1891 float minsize = readF1000(is);
1892 float maxsize = readF1000(is);
1893 bool collisiondetection = readU8(is);
1894 std::string texture = deSerializeLongString(is);
1895 u32 id = readU32(is);
1898 event.type = CE_ADD_PARTICLESPAWNER;
1899 event.add_particlespawner.amount = amount;
1900 event.add_particlespawner.spawntime = spawntime;
1902 event.add_particlespawner.minpos = new v3f (minpos);
1903 event.add_particlespawner.maxpos = new v3f (maxpos);
1904 event.add_particlespawner.minvel = new v3f (minvel);
1905 event.add_particlespawner.maxvel = new v3f (maxvel);
1906 event.add_particlespawner.minacc = new v3f (minacc);
1907 event.add_particlespawner.maxacc = new v3f (maxacc);
1909 event.add_particlespawner.minexptime = minexptime;
1910 event.add_particlespawner.maxexptime = maxexptime;
1911 event.add_particlespawner.minsize = minsize;
1912 event.add_particlespawner.maxsize = maxsize;
1913 event.add_particlespawner.collisiondetection = collisiondetection;
1914 event.add_particlespawner.texture = new std::string(texture);
1915 event.add_particlespawner.id = id;
1917 m_client_event_queue.push_back(event);
1919 else if(command == TOCLIENT_DELETE_PARTICLESPAWNER)
1921 std::string datastring((char*)&data[2], datasize-2);
1922 std::istringstream is(datastring, std::ios_base::binary);
1924 u32 id = readU16(is);
1927 event.type = CE_DELETE_PARTICLESPAWNER;
1928 event.delete_particlespawner.id = id;
1930 m_client_event_queue.push_back(event);
1932 else if(command == TOCLIENT_HUDADD)
1934 std::string datastring((char *)&data[2], datasize - 2);
1935 std::istringstream is(datastring, std::ios_base::binary);
1937 u32 id = readU32(is);
1938 u8 type = readU8(is);
1939 v2f pos = readV2F1000(is);
1940 std::string name = deSerializeString(is);
1941 v2f scale = readV2F1000(is);
1942 std::string text = deSerializeString(is);
1943 u32 number = readU32(is);
1944 u32 item = readU32(is);
1945 u32 dir = readU32(is);
1946 v2f align = readV2F1000(is);
1947 v2f offset = readV2F1000(is);
1950 event.type = CE_HUDADD;
1951 event.hudadd.id = id;
1952 event.hudadd.type = type;
1953 event.hudadd.pos = new v2f(pos);
1954 event.hudadd.name = new std::string(name);
1955 event.hudadd.scale = new v2f(scale);
1956 event.hudadd.text = new std::string(text);
1957 event.hudadd.number = number;
1958 event.hudadd.item = item;
1959 event.hudadd.dir = dir;
1960 event.hudadd.align = new v2f(align);
1961 event.hudadd.offset = new v2f(offset);
1962 m_client_event_queue.push_back(event);
1964 else if(command == TOCLIENT_HUDRM)
1966 std::string datastring((char *)&data[2], datasize - 2);
1967 std::istringstream is(datastring, std::ios_base::binary);
1969 u32 id = readU32(is);
1972 event.type = CE_HUDRM;
1973 event.hudrm.id = id;
1974 m_client_event_queue.push_back(event);
1976 else if(command == TOCLIENT_HUDCHANGE)
1982 std::string datastring((char *)&data[2], datasize - 2);
1983 std::istringstream is(datastring, std::ios_base::binary);
1985 u32 id = readU32(is);
1986 u8 stat = (HudElementStat)readU8(is);
1988 if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
1989 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
1990 v2fdata = readV2F1000(is);
1991 else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
1992 sdata = deSerializeString(is);
1994 intdata = readU32(is);
1997 event.type = CE_HUDCHANGE;
1998 event.hudchange.id = id;
1999 event.hudchange.stat = (HudElementStat)stat;
2000 event.hudchange.v2fdata = new v2f(v2fdata);
2001 event.hudchange.sdata = new std::string(sdata);
2002 event.hudchange.data = intdata;
2003 m_client_event_queue.push_back(event);
2005 else if(command == TOCLIENT_HUD_SET_FLAGS)
2007 std::string datastring((char *)&data[2], datasize - 2);
2008 std::istringstream is(datastring, std::ios_base::binary);
2010 Player *player = m_env.getLocalPlayer();
2011 assert(player != NULL);
2013 u32 flags = readU32(is);
2014 u32 mask = readU32(is);
2016 player->hud_flags &= ~mask;
2017 player->hud_flags |= flags;
2019 else if(command == TOCLIENT_HUD_SET_PARAM)
2021 std::string datastring((char *)&data[2], datasize - 2);
2022 std::istringstream is(datastring, std::ios_base::binary);
2024 Player *player = m_env.getLocalPlayer();
2025 assert(player != NULL);
2027 u16 param = readU16(is);
2028 std::string value = deSerializeString(is);
2030 if(param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4){
2031 s32 hotbar_itemcount = readS32((u8*) value.c_str());
2032 if(hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
2033 player->hud_hotbar_itemcount = hotbar_itemcount;
2034 } else if (param == HUD_PARAM_HOTBAR_IMAGE) {
2035 ((LocalPlayer *) player)->hotbar_image = value;
2036 } else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
2037 ((LocalPlayer *) player)->hotbar_selected_image = value;
2042 infostream<<"Client: Ignoring unknown command "
2043 <<command<<std::endl;
2047 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
2049 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2050 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
2053 void Client::interact(u8 action, const PointedThing& pointed)
2055 if(connectedAndInitialized() == false){
2056 infostream<<"Client::interact() "
2057 "cancelled (not connected)"
2062 std::ostringstream os(std::ios_base::binary);
2068 [5] u32 length of the next item
2069 [9] serialized PointedThing
2071 0: start digging (from undersurface) or use
2072 1: stop digging (all parameters ignored)
2073 2: digging completed
2074 3: place block or item (to abovesurface)
2077 writeU16(os, TOSERVER_INTERACT);
2078 writeU8(os, action);
2079 writeU16(os, getPlayerItem());
2080 std::ostringstream tmp_os(std::ios::binary);
2081 pointed.serialize(tmp_os);
2082 os<<serializeLongString(tmp_os.str());
2084 std::string s = os.str();
2085 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2088 Send(0, data, true);
2091 void Client::sendNodemetaFields(v3s16 p, const std::string &formname,
2092 const std::map<std::string, std::string> &fields)
2094 std::ostringstream os(std::ios_base::binary);
2096 writeU16(os, TOSERVER_NODEMETA_FIELDS);
2098 os<<serializeString(formname);
2099 writeU16(os, fields.size());
2100 for(std::map<std::string, std::string>::const_iterator
2101 i = fields.begin(); i != fields.end(); i++){
2102 const std::string &name = i->first;
2103 const std::string &value = i->second;
2104 os<<serializeString(name);
2105 os<<serializeLongString(value);
2109 std::string s = os.str();
2110 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2112 Send(0, data, true);
2115 void Client::sendInventoryFields(const std::string &formname,
2116 const std::map<std::string, std::string> &fields)
2118 std::ostringstream os(std::ios_base::binary);
2120 writeU16(os, TOSERVER_INVENTORY_FIELDS);
2121 os<<serializeString(formname);
2122 writeU16(os, fields.size());
2123 for(std::map<std::string, std::string>::const_iterator
2124 i = fields.begin(); i != fields.end(); i++){
2125 const std::string &name = i->first;
2126 const std::string &value = i->second;
2127 os<<serializeString(name);
2128 os<<serializeLongString(value);
2132 std::string s = os.str();
2133 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2135 Send(0, data, true);
2138 void Client::sendInventoryAction(InventoryAction *a)
2140 std::ostringstream os(std::ios_base::binary);
2144 writeU16(buf, TOSERVER_INVENTORY_ACTION);
2145 os.write((char*)buf, 2);
2150 std::string s = os.str();
2151 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2153 Send(0, data, true);
2156 void Client::sendChatMessage(const std::wstring &message)
2158 std::ostringstream os(std::ios_base::binary);
2162 writeU16(buf, TOSERVER_CHAT_MESSAGE);
2163 os.write((char*)buf, 2);
2166 writeU16(buf, message.size());
2167 os.write((char*)buf, 2);
2170 for(u32 i=0; i<message.size(); i++)
2174 os.write((char*)buf, 2);
2178 std::string s = os.str();
2179 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2181 Send(0, data, true);
2184 void Client::sendChangePassword(const std::wstring oldpassword,
2185 const std::wstring newpassword)
2187 Player *player = m_env.getLocalPlayer();
2191 std::string playername = player->getName();
2192 std::string oldpwd = translatePassword(playername, oldpassword);
2193 std::string newpwd = translatePassword(playername, newpassword);
2195 std::ostringstream os(std::ios_base::binary);
2196 u8 buf[2+PASSWORD_SIZE*2];
2198 [0] u16 TOSERVER_PASSWORD
2199 [2] u8[28] old password
2200 [30] u8[28] new password
2203 writeU16(buf, TOSERVER_PASSWORD);
2204 for(u32 i=0;i<PASSWORD_SIZE-1;i++)
2206 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
2207 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
2209 buf[2+PASSWORD_SIZE-1] = 0;
2210 buf[30+PASSWORD_SIZE-1] = 0;
2211 os.write((char*)buf, 2+PASSWORD_SIZE*2);
2214 std::string s = os.str();
2215 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2217 Send(0, data, true);
2221 void Client::sendDamage(u8 damage)
2223 DSTACK(__FUNCTION_NAME);
2224 std::ostringstream os(std::ios_base::binary);
2226 writeU16(os, TOSERVER_DAMAGE);
2227 writeU8(os, damage);
2230 std::string s = os.str();
2231 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2233 Send(0, data, true);
2236 void Client::sendBreath(u16 breath)
2238 DSTACK(__FUNCTION_NAME);
2239 std::ostringstream os(std::ios_base::binary);
2241 writeU16(os, TOSERVER_BREATH);
2242 writeU16(os, breath);
2244 std::string s = os.str();
2245 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2247 Send(0, data, true);
2250 void Client::sendRespawn()
2252 DSTACK(__FUNCTION_NAME);
2253 std::ostringstream os(std::ios_base::binary);
2255 writeU16(os, TOSERVER_RESPAWN);
2258 std::string s = os.str();
2259 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2261 Send(0, data, true);
2264 void Client::sendPlayerPos()
2266 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2268 LocalPlayer *myplayer = m_env.getLocalPlayer();
2269 if(myplayer == NULL)
2272 // Save bandwidth by only updating position when something changed
2273 if(myplayer->last_position == myplayer->getPosition() &&
2274 myplayer->last_speed == myplayer->getSpeed() &&
2275 myplayer->last_pitch == myplayer->getPitch() &&
2276 myplayer->last_yaw == myplayer->getYaw() &&
2277 myplayer->last_keyPressed == myplayer->keyPressed)
2280 myplayer->last_position = myplayer->getPosition();
2281 myplayer->last_speed = myplayer->getSpeed();
2282 myplayer->last_pitch = myplayer->getPitch();
2283 myplayer->last_yaw = myplayer->getYaw();
2284 myplayer->last_keyPressed = myplayer->keyPressed;
2288 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2289 our_peer_id = m_con.GetPeerID();
2292 // Set peer id if not set already
2293 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2294 myplayer->peer_id = our_peer_id;
2295 // Check that an existing peer_id is the same as the connection's
2296 assert(myplayer->peer_id == our_peer_id);
2298 v3f pf = myplayer->getPosition();
2299 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
2300 v3f sf = myplayer->getSpeed();
2301 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
2302 s32 pitch = myplayer->getPitch() * 100;
2303 s32 yaw = myplayer->getYaw() * 100;
2304 u32 keyPressed=myplayer->keyPressed;
2308 [2] v3s32 position*100
2309 [2+12] v3s32 speed*100
2310 [2+12+12] s32 pitch*100
2311 [2+12+12+4] s32 yaw*100
2312 [2+12+12+4+4] u32 keyPressed
2314 SharedBuffer<u8> data(2+12+12+4+4+4);
2315 writeU16(&data[0], TOSERVER_PLAYERPOS);
2316 writeV3S32(&data[2], position);
2317 writeV3S32(&data[2+12], speed);
2318 writeS32(&data[2+12+12], pitch);
2319 writeS32(&data[2+12+12+4], yaw);
2320 writeU32(&data[2+12+12+4+4], keyPressed);
2321 // Send as unreliable
2322 Send(0, data, false);
2325 void Client::sendPlayerItem(u16 item)
2327 Player *myplayer = m_env.getLocalPlayer();
2328 if(myplayer == NULL)
2331 u16 our_peer_id = m_con.GetPeerID();
2333 // Set peer id if not set already
2334 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2335 myplayer->peer_id = our_peer_id;
2336 // Check that an existing peer_id is the same as the connection's
2337 assert(myplayer->peer_id == our_peer_id);
2339 SharedBuffer<u8> data(2+2);
2340 writeU16(&data[0], TOSERVER_PLAYERITEM);
2341 writeU16(&data[2], item);
2344 Send(0, data, true);
2347 void Client::removeNode(v3s16 p)
2349 std::map<v3s16, MapBlock*> modified_blocks;
2353 //TimeTaker t("removeNodeAndUpdate", m_device);
2354 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
2356 catch(InvalidPositionException &e)
2360 // add urgent task to update the modified node
2361 addUpdateMeshTaskForNode(p, false, true);
2363 for(std::map<v3s16, MapBlock * >::iterator
2364 i = modified_blocks.begin();
2365 i != modified_blocks.end(); ++i)
2367 addUpdateMeshTaskWithEdge(i->first);
2371 void Client::addNode(v3s16 p, MapNode n, bool remove_metadata)
2373 TimeTaker timer1("Client::addNode()");
2375 std::map<v3s16, MapBlock*> modified_blocks;
2379 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
2380 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
2382 catch(InvalidPositionException &e)
2385 for(std::map<v3s16, MapBlock * >::iterator
2386 i = modified_blocks.begin();
2387 i != modified_blocks.end(); ++i)
2389 addUpdateMeshTaskWithEdge(i->first);
2393 void Client::setPlayerControl(PlayerControl &control)
2395 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2396 LocalPlayer *player = m_env.getLocalPlayer();
2397 assert(player != NULL);
2398 player->control = control;
2401 void Client::selectPlayerItem(u16 item)
2403 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2404 m_playeritem = item;
2405 m_inventory_updated = true;
2406 sendPlayerItem(item);
2409 // Returns true if the inventory of the local player has been
2410 // updated from the server. If it is true, it is set to false.
2411 bool Client::getLocalInventoryUpdated()
2413 // m_inventory_updated is behind envlock
2414 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2415 bool updated = m_inventory_updated;
2416 m_inventory_updated = false;
2420 // Copies the inventory of the local player to parameter
2421 void Client::getLocalInventory(Inventory &dst)
2423 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2424 Player *player = m_env.getLocalPlayer();
2425 assert(player != NULL);
2426 dst = player->inventory;
2429 Inventory* Client::getInventory(const InventoryLocation &loc)
2432 case InventoryLocation::UNDEFINED:
2435 case InventoryLocation::CURRENT_PLAYER:
2437 Player *player = m_env.getLocalPlayer();
2438 assert(player != NULL);
2439 return &player->inventory;
2442 case InventoryLocation::PLAYER:
2444 Player *player = m_env.getPlayer(loc.name.c_str());
2447 return &player->inventory;
2450 case InventoryLocation::NODEMETA:
2452 NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p);
2455 return meta->getInventory();
2458 case InventoryLocation::DETACHED:
2460 if(m_detached_inventories.count(loc.name) == 0)
2462 return m_detached_inventories[loc.name];
2470 void Client::inventoryAction(InventoryAction *a)
2473 Send it to the server
2475 sendInventoryAction(a);
2478 Predict some local inventory changes
2480 a->clientApply(this, this);
2486 ClientActiveObject * Client::getSelectedActiveObject(
2488 v3f from_pos_f_on_map,
2489 core::line3d<f32> shootline_on_map
2492 std::vector<DistanceSortedActiveObject> objects;
2494 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
2496 //infostream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
2499 // After this, the closest object is the first in the array.
2500 std::sort(objects.begin(), objects.end());
2502 for(u32 i=0; i<objects.size(); i++)
2504 ClientActiveObject *obj = objects[i].obj;
2506 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
2507 if(selection_box == NULL)
2510 v3f pos = obj->getPosition();
2512 core::aabbox3d<f32> offsetted_box(
2513 selection_box->MinEdge + pos,
2514 selection_box->MaxEdge + pos
2517 if(offsetted_box.intersectsWithLine(shootline_on_map))
2519 //infostream<<"Returning selected object"<<std::endl;
2524 //infostream<<"No object selected; returning NULL."<<std::endl;
2528 void Client::printDebugInfo(std::ostream &os)
2530 //JMutexAutoLock lock1(m_fetchblock_mutex);
2531 /*JMutexAutoLock lock2(m_incoming_queue_mutex);
2533 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
2534 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
2535 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
2539 std::list<std::string> Client::getConnectedPlayerNames()
2541 return m_env.getPlayerNames();
2544 float Client::getAnimationTime()
2546 return m_animation_time;
2549 int Client::getCrackLevel()
2551 return m_crack_level;
2554 void Client::setCrack(int level, v3s16 pos)
2556 int old_crack_level = m_crack_level;
2557 v3s16 old_crack_pos = m_crack_pos;
2559 m_crack_level = level;
2562 if(old_crack_level >= 0 && (level < 0 || pos != old_crack_pos))
2565 addUpdateMeshTaskForNode(old_crack_pos, false, true);
2567 if(level >= 0 && (old_crack_level < 0 || pos != old_crack_pos))
2570 addUpdateMeshTaskForNode(pos, false, true);
2576 Player *player = m_env.getLocalPlayer();
2577 assert(player != NULL);
2581 u16 Client::getBreath()
2583 Player *player = m_env.getLocalPlayer();
2584 assert(player != NULL);
2585 return player->getBreath();
2588 bool Client::getChatMessage(std::wstring &message)
2590 if(m_chat_queue.size() == 0)
2592 message = m_chat_queue.pop_front();
2596 void Client::typeChatMessage(const std::wstring &message)
2598 // Discard empty line
2603 sendChatMessage(message);
2606 if (message[0] == L'/')
2608 m_chat_queue.push_back(
2609 (std::wstring)L"issued command: "+message);
2613 LocalPlayer *player = m_env.getLocalPlayer();
2614 assert(player != NULL);
2615 std::wstring name = narrow_to_wide(player->getName());
2616 m_chat_queue.push_back(
2617 (std::wstring)L"<"+name+L"> "+message);
2621 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
2623 /*infostream<<"Client::addUpdateMeshTask(): "
2624 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2625 <<" ack_to_server="<<ack_to_server
2626 <<" urgent="<<urgent
2629 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2634 Create a task to update the mesh of the block
2637 MeshMakeData *data = new MeshMakeData(this);
2640 //TimeTaker timer("data fill");
2642 // Debug: 1-6ms, avg=2ms
2644 data->setCrack(m_crack_level, m_crack_pos);
2645 data->setSmoothLighting(g_settings->getBool("smooth_lighting"));
2649 //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
2651 // Add task to queue
2652 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server, urgent);
2654 /*infostream<<"Mesh update input queue size is "
2655 <<m_mesh_update_thread.m_queue_in.size()
2659 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent)
2663 infostream<<"Client::addUpdateMeshTaskWithEdge(): "
2664 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2669 v3s16 p = blockpos + v3s16(0,0,0);
2670 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2671 addUpdateMeshTask(p, ack_to_server, urgent);
2673 catch(InvalidPositionException &e){}
2675 for (int i=0;i<6;i++)
2678 v3s16 p = blockpos + g_6dirs[i];
2679 addUpdateMeshTask(p, false, urgent);
2681 catch(InvalidPositionException &e){}
2685 void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool urgent)
2689 infostream<<"Client::addUpdateMeshTaskForNode(): "
2690 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2694 v3s16 blockpos = getNodeBlockPos(nodepos);
2695 v3s16 blockpos_relative = blockpos * MAP_BLOCKSIZE;
2698 v3s16 p = blockpos + v3s16(0,0,0);
2699 addUpdateMeshTask(p, ack_to_server, urgent);
2701 catch(InvalidPositionException &e){}
2703 if(nodepos.X == blockpos_relative.X){
2705 v3s16 p = blockpos + v3s16(-1,0,0);
2706 addUpdateMeshTask(p, false, urgent);
2708 catch(InvalidPositionException &e){}
2710 if(nodepos.Y == blockpos_relative.Y){
2712 v3s16 p = blockpos + v3s16(0,-1,0);
2713 addUpdateMeshTask(p, false, urgent);
2715 catch(InvalidPositionException &e){}
2717 if(nodepos.Z == blockpos_relative.Z){
2719 v3s16 p = blockpos + v3s16(0,0,-1);
2720 addUpdateMeshTask(p, false, urgent);
2722 catch(InvalidPositionException &e){}
2726 ClientEvent Client::getClientEvent()
2728 if(m_client_event_queue.size() == 0)
2731 event.type = CE_NONE;
2734 return m_client_event_queue.pop_front();
2737 float Client::mediaReceiveProgress()
2739 if (m_media_downloader)
2740 return m_media_downloader->getProgress();
2742 return 1.0; // downloader only exists when not yet done
2745 void draw_load_screen(const std::wstring &text,
2746 IrrlichtDevice* device, gui::IGUIFont* font,
2747 float dtime=0 ,int percent=0, bool clouds=true);
2748 void Client::afterContentReceived(IrrlichtDevice *device, gui::IGUIFont* font)
2750 infostream<<"Client::afterContentReceived() started"<<std::endl;
2751 assert(m_itemdef_received);
2752 assert(m_nodedef_received);
2753 assert(mediaReceived());
2755 // Rebuild inherited images and recreate textures
2756 infostream<<"- Rebuilding images and textures"<<std::endl;
2757 m_tsrc->rebuildImagesAndTextures();
2760 infostream<<"- Rebuilding shaders"<<std::endl;
2761 m_shsrc->rebuildShaders();
2763 // Update node aliases
2764 infostream<<"- Updating node aliases"<<std::endl;
2765 m_nodedef->updateAliases(m_itemdef);
2767 // Update node textures
2768 infostream<<"- Updating node textures"<<std::endl;
2769 m_nodedef->updateTextures(m_tsrc);
2771 // Preload item textures and meshes if configured to
2772 if(g_settings->getBool("preload_item_visuals"))
2774 verbosestream<<"Updating item textures and meshes"<<std::endl;
2775 wchar_t* text = wgettext("Item textures...");
2776 draw_load_screen(text,device,font,0,0);
2777 std::set<std::string> names = m_itemdef->getAll();
2778 size_t size = names.size();
2781 for(std::set<std::string>::const_iterator
2782 i = names.begin(); i != names.end(); ++i){
2783 // Asking for these caches the result
2784 m_itemdef->getInventoryTexture(*i, this);
2785 m_itemdef->getWieldMesh(*i, this);
2787 percent = count*100/size;
2788 if (count%50 == 0) // only update every 50 item
2789 draw_load_screen(text,device,font,0,percent);
2794 // Start mesh update thread after setting up content definitions
2795 infostream<<"- Starting mesh update thread"<<std::endl;
2796 m_mesh_update_thread.Start();
2798 infostream<<"Client::afterContentReceived() done"<<std::endl;
2801 float Client::getRTT(void)
2804 return m_con.GetPeerAvgRTT(PEER_ID_SERVER);
2805 } catch(con::PeerNotFoundException &e){
2810 // IGameDef interface
2812 IItemDefManager* Client::getItemDefManager()
2816 INodeDefManager* Client::getNodeDefManager()
2820 ICraftDefManager* Client::getCraftDefManager()
2823 //return m_craftdef;
2825 ITextureSource* Client::getTextureSource()
2829 IShaderSource* Client::getShaderSource()
2833 u16 Client::allocateUnknownNodeId(const std::string &name)
2835 errorstream<<"Client::allocateUnknownNodeId(): "
2836 <<"Client cannot allocate node IDs"<<std::endl;
2838 return CONTENT_IGNORE;
2840 ISoundManager* Client::getSoundManager()
2844 MtEventManager* Client::getEventManager()