3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include "clientserver.h"
24 #include "jthread/jmutexautolock.h"
29 #include "mapsector.h"
30 #include "mapblock_mesh.h"
36 #include "nodemetadata.h"
40 #include <IFileSystem.h>
42 #include "clientmap.h"
43 #include "clientmedia.h"
45 #include "util/string.h"
46 #include "IMeshCache.h"
47 #include "serialization.h"
48 #include "util/serialize.h"
50 #include "util/directiontables.h"
51 #include "util/pointedthing.h"
58 QueuedMeshUpdate::QueuedMeshUpdate():
61 ack_block_to_server(false)
65 QueuedMeshUpdate::~QueuedMeshUpdate()
75 MeshUpdateQueue::MeshUpdateQueue()
79 MeshUpdateQueue::~MeshUpdateQueue()
81 JMutexAutoLock lock(m_mutex);
83 for(std::vector<QueuedMeshUpdate*>::iterator
85 i != m_queue.end(); i++)
87 QueuedMeshUpdate *q = *i;
93 peer_id=0 adds with nobody to send to
95 void MeshUpdateQueue::addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_server, bool urgent)
97 DSTACK(__FUNCTION_NAME);
101 JMutexAutoLock lock(m_mutex);
107 Find if block is already in queue.
108 If it is, update the data and quit.
110 for(std::vector<QueuedMeshUpdate*>::iterator
112 i != m_queue.end(); i++)
114 QueuedMeshUpdate *q = *i;
120 if(ack_block_to_server)
121 q->ack_block_to_server = true;
129 QueuedMeshUpdate *q = new QueuedMeshUpdate;
132 q->ack_block_to_server = ack_block_to_server;
133 m_queue.push_back(q);
136 // Returned pointer must be deleted
137 // Returns NULL if queue is empty
138 QueuedMeshUpdate * MeshUpdateQueue::pop()
140 JMutexAutoLock lock(m_mutex);
142 bool must_be_urgent = !m_urgents.empty();
143 for(std::vector<QueuedMeshUpdate*>::iterator
145 i != m_queue.end(); i++)
147 QueuedMeshUpdate *q = *i;
148 if(must_be_urgent && m_urgents.count(q->p) == 0)
151 m_urgents.erase(q->p);
161 void * MeshUpdateThread::Thread()
165 log_register_thread("MeshUpdateThread");
167 DSTACK(__FUNCTION_NAME);
169 BEGIN_DEBUG_EXCEPTION_HANDLER
171 while(!StopRequested())
173 /*// Wait for output queue to flush.
174 // Allow 2 in queue, this makes less frametime jitter.
175 // Umm actually, there is no much difference
176 if(m_queue_out.size() >= 2)
182 QueuedMeshUpdate *q = m_queue_in.pop();
189 ScopeProfiler sp(g_profiler, "Client: Mesh making");
191 MapBlockMesh *mesh_new = new MapBlockMesh(q->data);
192 if(mesh_new->getMesh()->getMeshBufferCount() == 0)
201 r.ack_block_to_server = q->ack_block_to_server;
203 /*infostream<<"MeshUpdateThread: Processed "
204 <<"("<<q->p.X<<","<<q->p.Y<<","<<q->p.Z<<")"
207 m_queue_out.push_back(r);
212 END_DEBUG_EXCEPTION_HANDLER(errorstream)
222 IrrlichtDevice *device,
223 const char *playername,
224 std::string password,
225 MapDrawControl &control,
226 IWritableTextureSource *tsrc,
227 IWritableShaderSource *shsrc,
228 IWritableItemDefManager *itemdef,
229 IWritableNodeDefManager *nodedef,
230 ISoundManager *sound,
231 MtEventManager *event,
240 m_mesh_update_thread(this),
242 new ClientMap(this, this, control,
243 device->getSceneManager()->getRootSceneNode(),
244 device->getSceneManager(), 666),
245 device->getSceneManager(),
248 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, ipv6, this),
250 m_server_ser_ver(SER_FMT_VER_INVALID),
252 m_inventory_updated(false),
253 m_inventory_from_server(NULL),
254 m_inventory_from_server_age(0.0),
259 m_password(password),
260 m_access_denied(false),
261 m_itemdef_received(false),
262 m_nodedef_received(false),
263 m_media_downloader(new ClientMediaDownloader()),
264 m_time_of_day_set(false),
265 m_last_time_of_day_f(-1),
266 m_time_of_day_update_timer(0),
267 m_recommended_send_interval(0.1),
268 m_removed_sounds_check_timer(0)
270 m_packetcounter_timer = 0.0;
271 //m_delete_unused_sectors_timer = 0.0;
272 m_connection_reinit_timer = 0.0;
273 m_avg_rtt_timer = 0.0;
274 m_playerpos_send_timer = 0.0;
275 m_ignore_damage_timer = 0.0;
281 Player *player = new LocalPlayer(this);
283 player->updateName(playername);
285 m_env.addPlayer(player);
291 //request all client managed threads to stop
292 m_mesh_update_thread.Stop();
295 bool Client::isShutdown()
298 if (!m_mesh_update_thread.IsRunning()) return true;
306 //JMutexAutoLock conlock(m_con_mutex); //bulk comment-out
310 m_mesh_update_thread.Stop();
311 m_mesh_update_thread.Wait();
312 while(!m_mesh_update_thread.m_queue_out.empty()) {
313 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_frontNoEx();
318 delete m_inventory_from_server;
320 // Delete detached inventories
322 for(std::map<std::string, Inventory*>::iterator
323 i = m_detached_inventories.begin();
324 i != m_detached_inventories.end(); i++){
329 // cleanup 3d model meshes on client shutdown
330 while (m_device->getSceneManager()->getMeshCache()->getMeshCount() != 0) {
331 scene::IAnimatedMesh * mesh =
332 m_device->getSceneManager()->getMeshCache()->getMeshByIndex(0);
335 m_device->getSceneManager()->getMeshCache()->removeMesh(mesh);
339 void Client::connect(Address address)
341 DSTACK(__FUNCTION_NAME);
342 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
343 m_con.SetTimeoutMs(0);
344 m_con.Connect(address);
347 bool Client::connectedAndInitialized()
349 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
351 if(m_con.Connected() == false)
354 if(m_server_ser_ver == SER_FMT_VER_INVALID)
360 void Client::step(float dtime)
362 DSTACK(__FUNCTION_NAME);
368 if(m_ignore_damage_timer > dtime)
369 m_ignore_damage_timer -= dtime;
371 m_ignore_damage_timer = 0.0;
373 m_animation_time += dtime;
374 if(m_animation_time > 60.0)
375 m_animation_time -= 60.0;
377 m_time_of_day_update_timer += dtime;
379 //infostream<<"Client steps "<<dtime<<std::endl;
382 //TimeTaker timer("ReceiveAll()", m_device);
391 float &counter = m_packetcounter_timer;
397 infostream<<"Client packetcounter (20s):"<<std::endl;
398 m_packetcounter.print(infostream);
399 m_packetcounter.clear();
403 // Get connection status
404 bool connected = connectedAndInitialized();
409 Delete unused sectors
411 NOTE: This jams the game for a while because deleting sectors
415 float &counter = m_delete_unused_sectors_timer;
423 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
425 core::list<v3s16> deleted_blocks;
427 float delete_unused_sectors_timeout =
428 g_settings->getFloat("client_delete_unused_sectors_timeout");
430 // Delete sector blocks
431 /*u32 num = m_env.getMap().unloadUnusedData
432 (delete_unused_sectors_timeout,
433 true, &deleted_blocks);*/
435 // Delete whole sectors
436 m_env.getMap().unloadUnusedData
437 (delete_unused_sectors_timeout,
440 if(deleted_blocks.size() > 0)
442 /*infostream<<"Client: Deleted blocks of "<<num
443 <<" unused sectors"<<std::endl;*/
444 /*infostream<<"Client: Deleted "<<num
445 <<" unused sectors"<<std::endl;*/
451 // Env is locked so con can be locked.
452 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
454 core::list<v3s16>::Iterator i = deleted_blocks.begin();
455 core::list<v3s16> sendlist;
458 if(sendlist.size() == 255 || i == deleted_blocks.end())
460 if(sendlist.size() == 0)
469 u32 replysize = 2+1+6*sendlist.size();
470 SharedBuffer<u8> reply(replysize);
471 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
472 reply[2] = sendlist.size();
474 for(core::list<v3s16>::Iterator
475 j = sendlist.begin();
476 j != sendlist.end(); j++)
478 writeV3S16(&reply[2+1+6*k], *j);
481 m_con.Send(PEER_ID_SERVER, 1, reply, true);
483 if(i == deleted_blocks.end())
489 sendlist.push_back(*i);
497 if(connected == false)
499 float &counter = m_connection_reinit_timer;
505 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
507 Player *myplayer = m_env.getLocalPlayer();
508 assert(myplayer != NULL);
510 // Send TOSERVER_INIT
511 // [0] u16 TOSERVER_INIT
512 // [2] u8 SER_FMT_VER_HIGHEST_READ
513 // [3] u8[20] player_name
514 // [23] u8[28] password (new in some version)
515 // [51] u16 minimum supported network protocol version (added sometime)
516 // [53] u16 maximum supported network protocol version (added later than the previous one)
517 SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2+2);
518 writeU16(&data[0], TOSERVER_INIT);
519 writeU8(&data[2], SER_FMT_VER_HIGHEST_READ);
521 memset((char*)&data[3], 0, PLAYERNAME_SIZE);
522 snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
524 /*infostream<<"Client: sending initial password hash: \""<<m_password<<"\""
527 memset((char*)&data[23], 0, PASSWORD_SIZE);
528 snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
530 writeU16(&data[51], CLIENT_PROTOCOL_VERSION_MIN);
531 writeU16(&data[53], CLIENT_PROTOCOL_VERSION_MAX);
533 // Send as unreliable
534 Send(1, data, false);
537 // Not connected, return
542 Do stuff if connected
546 Run Map's timers and unload unused data
548 const float map_timer_and_unload_dtime = 5.25;
549 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
551 ScopeProfiler sp(g_profiler, "Client: map timer and unload");
552 std::list<v3s16> deleted_blocks;
553 m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
554 g_settings->getFloat("client_unload_unused_data_timeout"),
557 /*if(deleted_blocks.size() > 0)
558 infostream<<"Client: Unloaded "<<deleted_blocks.size()
559 <<" unused blocks"<<std::endl;*/
563 NOTE: This loop is intentionally iterated the way it is.
566 std::list<v3s16>::iterator i = deleted_blocks.begin();
567 std::list<v3s16> sendlist;
570 if(sendlist.size() == 255 || i == deleted_blocks.end())
572 if(sendlist.size() == 0)
581 u32 replysize = 2+1+6*sendlist.size();
582 SharedBuffer<u8> reply(replysize);
583 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
584 reply[2] = sendlist.size();
586 for(std::list<v3s16>::iterator
587 j = sendlist.begin();
588 j != sendlist.end(); ++j)
590 writeV3S16(&reply[2+1+6*k], *j);
593 m_con.Send(PEER_ID_SERVER, 2, reply, true);
595 if(i == deleted_blocks.end())
601 sendlist.push_back(*i);
611 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
613 // Control local player (0ms)
614 LocalPlayer *player = m_env.getLocalPlayer();
615 assert(player != NULL);
616 player->applyControl(dtime);
618 //TimeTaker envtimer("env step", m_device);
627 ClientEnvEvent event = m_env.getClientEvent();
628 if(event.type == CEE_NONE)
632 else if(event.type == CEE_PLAYER_DAMAGE)
634 if(m_ignore_damage_timer <= 0)
636 u8 damage = event.player_damage.amount;
638 if(event.player_damage.send_to_server)
641 // Add to ClientEvent queue
643 event.type = CE_PLAYER_DAMAGE;
644 event.player_damage.amount = damage;
645 m_client_event_queue.push_back(event);
648 else if(event.type == CEE_PLAYER_BREATH)
650 u16 breath = event.player_breath.amount;
660 float &counter = m_avg_rtt_timer;
665 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
666 // connectedAndInitialized() is true, peer exists.
667 float avg_rtt = m_con.GetPeerAvgRTT(PEER_ID_SERVER);
668 infostream<<"Client: avg_rtt="<<avg_rtt<<std::endl;
673 Send player position to server
676 float &counter = m_playerpos_send_timer;
678 if(counter >= m_recommended_send_interval)
686 Replace updated meshes
689 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
691 //TimeTaker timer("** Processing mesh update result queue");
694 /*infostream<<"Mesh update result queue size is "
695 <<m_mesh_update_thread.m_queue_out.size()
698 int num_processed_meshes = 0;
699 while(!m_mesh_update_thread.m_queue_out.empty())
701 num_processed_meshes++;
702 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_frontNoEx();
703 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
706 //JMutexAutoLock lock(block->mesh_mutex);
708 // Delete the old mesh
709 if(block->mesh != NULL)
711 // TODO: Remove hardware buffers of meshbuffers of block->mesh
716 // Replace with the new mesh
717 block->mesh = r.mesh;
721 if(r.ack_block_to_server)
723 /*infostream<<"Client: ACK block ("<<r.p.X<<","<<r.p.Y
724 <<","<<r.p.Z<<")"<<std::endl;*/
735 u32 replysize = 2+1+6;
736 SharedBuffer<u8> reply(replysize);
737 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
739 writeV3S16(&reply[3], r.p);
741 m_con.Send(PEER_ID_SERVER, 2, reply, true);
744 if(num_processed_meshes > 0)
745 g_profiler->graphAdd("num_processed_meshes", num_processed_meshes);
751 if (m_media_downloader && m_media_downloader->isStarted()) {
752 m_media_downloader->step(this);
753 if (m_media_downloader->isDone()) {
755 delete m_media_downloader;
756 m_media_downloader = NULL;
761 If the server didn't update the inventory in a while, revert
762 the local inventory (so the player notices the lag problem
763 and knows something is wrong).
765 if(m_inventory_from_server)
767 float interval = 10.0;
768 float count_before = floor(m_inventory_from_server_age / interval);
770 m_inventory_from_server_age += dtime;
772 float count_after = floor(m_inventory_from_server_age / interval);
774 if(count_after != count_before)
776 // Do this every <interval> seconds after TOCLIENT_INVENTORY
777 // Reset the locally changed inventory to the authoritative inventory
778 Player *player = m_env.getLocalPlayer();
779 player->inventory = *m_inventory_from_server;
780 m_inventory_updated = true;
785 Update positions of sounds attached to objects
788 for(std::map<int, u16>::iterator
789 i = m_sounds_to_objects.begin();
790 i != m_sounds_to_objects.end(); i++)
792 int client_id = i->first;
793 u16 object_id = i->second;
794 ClientActiveObject *cao = m_env.getActiveObject(object_id);
797 v3f pos = cao->getPosition();
798 m_sound->updateSoundPosition(client_id, pos);
803 Handle removed remotely initiated sounds
805 m_removed_sounds_check_timer += dtime;
806 if(m_removed_sounds_check_timer >= 2.32)
808 m_removed_sounds_check_timer = 0;
809 // Find removed sounds and clear references to them
810 std::set<s32> removed_server_ids;
811 for(std::map<s32, int>::iterator
812 i = m_sounds_server_to_client.begin();
813 i != m_sounds_server_to_client.end();)
815 s32 server_id = i->first;
816 int client_id = i->second;
818 if(!m_sound->soundExists(client_id)){
819 m_sounds_server_to_client.erase(server_id);
820 m_sounds_client_to_server.erase(client_id);
821 m_sounds_to_objects.erase(client_id);
822 removed_server_ids.insert(server_id);
826 if(removed_server_ids.size() != 0)
828 std::ostringstream os(std::ios_base::binary);
829 writeU16(os, TOSERVER_REMOVED_SOUNDS);
830 writeU16(os, removed_server_ids.size());
831 for(std::set<s32>::iterator i = removed_server_ids.begin();
832 i != removed_server_ids.end(); i++)
834 std::string s = os.str();
835 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
842 bool Client::loadMedia(const std::string &data, const std::string &filename)
844 // Silly irrlicht's const-incorrectness
845 Buffer<char> data_rw(data.c_str(), data.size());
849 const char *image_ext[] = {
850 ".png", ".jpg", ".bmp", ".tga",
851 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
854 name = removeStringEnd(filename, image_ext);
857 verbosestream<<"Client: Attempting to load image "
858 <<"file \""<<filename<<"\""<<std::endl;
860 io::IFileSystem *irrfs = m_device->getFileSystem();
861 video::IVideoDriver *vdrv = m_device->getVideoDriver();
863 // Create an irrlicht memory file
864 io::IReadFile *rfile = irrfs->createMemoryReadFile(
865 *data_rw, data_rw.getSize(), "_tempreadfile");
868 video::IImage *img = vdrv->createImageFromFile(rfile);
870 errorstream<<"Client: Cannot create image from data of "
871 <<"file \""<<filename<<"\""<<std::endl;
876 m_tsrc->insertSourceImage(filename, img);
883 const char *sound_ext[] = {
884 ".0.ogg", ".1.ogg", ".2.ogg", ".3.ogg", ".4.ogg",
885 ".5.ogg", ".6.ogg", ".7.ogg", ".8.ogg", ".9.ogg",
888 name = removeStringEnd(filename, sound_ext);
891 verbosestream<<"Client: Attempting to load sound "
892 <<"file \""<<filename<<"\""<<std::endl;
893 m_sound->loadSoundData(name, data);
897 const char *model_ext[] = {
898 ".x", ".b3d", ".md2", ".obj",
901 name = removeStringEnd(filename, model_ext);
904 verbosestream<<"Client: Storing model into memory: "
905 <<"\""<<filename<<"\""<<std::endl;
906 if(m_mesh_data.count(filename))
907 errorstream<<"Multiple models with name \""<<filename.c_str()
908 <<"\" found; replacing previous model"<<std::endl;
909 m_mesh_data[filename] = data;
913 errorstream<<"Client: Don't know how to load file \""
914 <<filename<<"\""<<std::endl;
918 // Virtual methods from con::PeerHandler
919 void Client::peerAdded(con::Peer *peer)
921 infostream<<"Client::peerAdded(): peer->id="
922 <<peer->id<<std::endl;
924 void Client::deletingPeer(con::Peer *peer, bool timeout)
926 infostream<<"Client::deletingPeer(): "
927 "Server Peer is getting deleted "
928 <<"(timeout="<<timeout<<")"<<std::endl;
933 u16 number of files requested
939 void Client::request_media(const std::list<std::string> &file_requests)
941 std::ostringstream os(std::ios_base::binary);
942 writeU16(os, TOSERVER_REQUEST_MEDIA);
943 writeU16(os, file_requests.size());
945 for(std::list<std::string>::const_iterator i = file_requests.begin();
946 i != file_requests.end(); ++i) {
947 os<<serializeString(*i);
951 std::string s = os.str();
952 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
955 infostream<<"Client: Sending media request list to server ("
956 <<file_requests.size()<<" files)"<<std::endl;
959 void Client::received_media()
961 // notify server we received everything
962 std::ostringstream os(std::ios_base::binary);
963 writeU16(os, TOSERVER_RECEIVED_MEDIA);
964 std::string s = os.str();
965 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
968 infostream<<"Client: Notifying server that we received all media"
972 void Client::ReceiveAll()
974 DSTACK(__FUNCTION_NAME);
975 u32 start_ms = porting::getTimeMs();
978 // Limit time even if there would be huge amounts of data to
980 if(porting::getTimeMs() > start_ms + 100)
985 g_profiler->graphAdd("client_received_packets", 1);
987 catch(con::NoIncomingDataException &e)
991 catch(con::InvalidIncomingDataException &e)
993 infostream<<"Client::ReceiveAll(): "
994 "InvalidIncomingDataException: what()="
995 <<e.what()<<std::endl;
1000 void Client::Receive()
1002 DSTACK(__FUNCTION_NAME);
1003 SharedBuffer<u8> data;
1007 //TimeTaker t1("con mutex and receive", m_device);
1008 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1009 datasize = m_con.Receive(sender_peer_id, data);
1011 //TimeTaker t1("ProcessData", m_device);
1012 ProcessData(*data, datasize, sender_peer_id);
1016 sender_peer_id given to this shall be quaranteed to be a valid peer
1018 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
1020 DSTACK(__FUNCTION_NAME);
1022 // Ignore packets that don't even fit a command
1025 m_packetcounter.add(60000);
1029 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
1031 //infostream<<"Client: received command="<<command<<std::endl;
1032 m_packetcounter.add((u16)command);
1035 If this check is removed, be sure to change the queue
1036 system to know the ids
1038 if(sender_peer_id != PEER_ID_SERVER)
1040 infostream<<"Client::ProcessData(): Discarding data not "
1041 "coming from server: peer_id="<<sender_peer_id
1046 u8 ser_version = m_server_ser_ver;
1048 //infostream<<"Client received command="<<(int)command<<std::endl;
1050 if(command == TOCLIENT_INIT)
1055 u8 deployed = data[2];
1057 infostream<<"Client: TOCLIENT_INIT received with "
1058 "deployed="<<((int)deployed&0xff)<<std::endl;
1060 if(!ser_ver_supported(deployed))
1062 infostream<<"Client: TOCLIENT_INIT: Server sent "
1063 <<"unsupported ser_fmt_ver"<<std::endl;
1067 m_server_ser_ver = deployed;
1069 // Get player position
1070 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
1071 if(datasize >= 2+1+6)
1072 playerpos_s16 = readV3S16(&data[2+1]);
1073 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
1076 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1078 // Set player position
1079 Player *player = m_env.getLocalPlayer();
1080 assert(player != NULL);
1081 player->setPosition(playerpos_f);
1084 if(datasize >= 2+1+6+8)
1087 m_map_seed = readU64(&data[2+1+6]);
1088 infostream<<"Client: received map seed: "<<m_map_seed<<std::endl;
1091 if(datasize >= 2+1+6+8+4)
1094 m_recommended_send_interval = readF1000(&data[2+1+6+8]);
1095 infostream<<"Client: received recommended send interval "
1096 <<m_recommended_send_interval<<std::endl;
1101 SharedBuffer<u8> reply(replysize);
1102 writeU16(&reply[0], TOSERVER_INIT2);
1104 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1109 if(command == TOCLIENT_ACCESS_DENIED)
1111 // The server didn't like our password. Note, this needs
1112 // to be processed even if the serialisation format has
1113 // not been agreed yet, the same as TOCLIENT_INIT.
1114 m_access_denied = true;
1115 m_access_denied_reason = L"Unknown";
1118 std::string datastring((char*)&data[2], datasize-2);
1119 std::istringstream is(datastring, std::ios_base::binary);
1120 m_access_denied_reason = deSerializeWideString(is);
1125 if(ser_version == SER_FMT_VER_INVALID)
1127 infostream<<"Client: Server serialization"
1128 " format invalid or not initialized."
1129 " Skipping incoming command="<<command<<std::endl;
1133 // Just here to avoid putting the two if's together when
1134 // making some copypasta
1137 if(command == TOCLIENT_REMOVENODE)
1142 p.X = readS16(&data[2]);
1143 p.Y = readS16(&data[4]);
1144 p.Z = readS16(&data[6]);
1146 //TimeTaker t1("TOCLIENT_REMOVENODE");
1150 else if(command == TOCLIENT_ADDNODE)
1152 if(datasize < 8 + MapNode::serializedLength(ser_version))
1156 p.X = readS16(&data[2]);
1157 p.Y = readS16(&data[4]);
1158 p.Z = readS16(&data[6]);
1160 //TimeTaker t1("TOCLIENT_ADDNODE");
1163 n.deSerialize(&data[8], ser_version);
1165 bool remove_metadata = true;
1166 u32 index = 8 + MapNode::serializedLength(ser_version);
1167 if ((datasize >= index+1) && data[index]){
1168 remove_metadata = false;
1171 addNode(p, n, remove_metadata);
1173 else if(command == TOCLIENT_BLOCKDATA)
1175 // Ignore too small packet
1180 p.X = readS16(&data[2]);
1181 p.Y = readS16(&data[4]);
1182 p.Z = readS16(&data[6]);
1184 /*infostream<<"Client: Thread: BLOCKDATA for ("
1185 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1186 /*infostream<<"Client: Thread: BLOCKDATA for ("
1187 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1189 std::string datastring((char*)&data[8], datasize-8);
1190 std::istringstream istr(datastring, std::ios_base::binary);
1195 v2s16 p2d(p.X, p.Z);
1196 sector = m_env.getMap().emergeSector(p2d);
1198 assert(sector->getPos() == p2d);
1200 //TimeTaker timer("MapBlock deSerialize");
1203 block = sector->getBlockNoCreateNoEx(p.Y);
1207 Update an existing block
1209 //infostream<<"Updating"<<std::endl;
1210 block->deSerialize(istr, ser_version, false);
1211 block->deSerializeNetworkSpecific(istr);
1218 //infostream<<"Creating new"<<std::endl;
1219 block = new MapBlock(&m_env.getMap(), p, this);
1220 block->deSerialize(istr, ser_version, false);
1221 block->deSerializeNetworkSpecific(istr);
1222 sector->insertBlock(block);
1236 u32 replysize = 2+1+6;
1237 SharedBuffer<u8> reply(replysize);
1238 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
1240 writeV3S16(&reply[3], p);
1242 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1246 Add it to mesh update queue and set it to be acknowledged after update.
1248 //infostream<<"Adding mesh update task for received block"<<std::endl;
1249 addUpdateMeshTaskWithEdge(p, true);
1251 else if(command == TOCLIENT_INVENTORY)
1256 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
1259 //TimeTaker t2("mutex locking", m_device);
1260 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1263 //TimeTaker t3("istringstream init", m_device);
1264 std::string datastring((char*)&data[2], datasize-2);
1265 std::istringstream is(datastring, std::ios_base::binary);
1268 //TimeTaker t4("player get", m_device);
1269 Player *player = m_env.getLocalPlayer();
1270 assert(player != NULL);
1273 //TimeTaker t1("inventory.deSerialize()", m_device);
1274 player->inventory.deSerialize(is);
1277 m_inventory_updated = true;
1279 delete m_inventory_from_server;
1280 m_inventory_from_server = new Inventory(player->inventory);
1281 m_inventory_from_server_age = 0.0;
1283 //infostream<<"Client got player inventory:"<<std::endl;
1284 //player->inventory.print(infostream);
1287 else if(command == TOCLIENT_TIME_OF_DAY)
1292 u16 time_of_day = readU16(&data[2]);
1293 time_of_day = time_of_day % 24000;
1294 //infostream<<"Client: time_of_day="<<time_of_day<<std::endl;
1295 float time_speed = 0;
1296 if(datasize >= 2 + 2 + 4){
1297 time_speed = readF1000(&data[4]);
1299 // Old message; try to approximate speed of time by ourselves
1300 float time_of_day_f = (float)time_of_day / 24000.0;
1301 float tod_diff_f = 0;
1302 if(time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
1303 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0;
1305 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
1306 m_last_time_of_day_f = time_of_day_f;
1307 float time_diff = m_time_of_day_update_timer;
1308 m_time_of_day_update_timer = 0;
1309 if(m_time_of_day_set){
1310 time_speed = 3600.0*24.0 * tod_diff_f / time_diff;
1311 infostream<<"Client: Measured time_of_day speed (old format): "
1312 <<time_speed<<" tod_diff_f="<<tod_diff_f
1313 <<" time_diff="<<time_diff<<std::endl;
1317 // Update environment
1318 m_env.setTimeOfDay(time_of_day);
1319 m_env.setTimeOfDaySpeed(time_speed);
1320 m_time_of_day_set = true;
1322 u32 dr = m_env.getDayNightRatio();
1323 verbosestream<<"Client: time_of_day="<<time_of_day
1324 <<" time_speed="<<time_speed
1325 <<" dr="<<dr<<std::endl;
1327 else if(command == TOCLIENT_CHAT_MESSAGE)
1335 std::string datastring((char*)&data[2], datasize-2);
1336 std::istringstream is(datastring, std::ios_base::binary);
1339 is.read((char*)buf, 2);
1340 u16 len = readU16(buf);
1342 std::wstring message;
1343 for(u16 i=0; i<len; i++)
1345 is.read((char*)buf, 2);
1346 message += (wchar_t)readU16(buf);
1349 /*infostream<<"Client received chat message: "
1350 <<wide_to_narrow(message)<<std::endl;*/
1352 m_chat_queue.push_back(message);
1354 else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1356 //if(g_settings->getBool("enable_experimental"))
1360 u16 count of removed objects
1361 for all removed objects {
1364 u16 count of added objects
1365 for all added objects {
1368 u32 initialization data length
1369 string initialization data
1374 // Get all data except the command number
1375 std::string datastring((char*)&data[2], datasize-2);
1376 // Throw them in an istringstream
1377 std::istringstream is(datastring, std::ios_base::binary);
1381 // Read removed objects
1383 u16 removed_count = readU16((u8*)buf);
1384 for(u16 i=0; i<removed_count; i++)
1387 u16 id = readU16((u8*)buf);
1390 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1391 m_env.removeActiveObject(id);
1395 // Read added objects
1397 u16 added_count = readU16((u8*)buf);
1398 for(u16 i=0; i<added_count; i++)
1401 u16 id = readU16((u8*)buf);
1403 u8 type = readU8((u8*)buf);
1404 std::string data = deSerializeLongString(is);
1407 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1408 m_env.addActiveObject(id, type, data);
1413 else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1415 //if(g_settings->getBool("enable_experimental"))
1427 // Get all data except the command number
1428 std::string datastring((char*)&data[2], datasize-2);
1429 // Throw them in an istringstream
1430 std::istringstream is(datastring, std::ios_base::binary);
1432 while(is.eof() == false)
1436 u16 id = readU16((u8*)buf);
1440 u16 message_size = readU16((u8*)buf);
1441 std::string message;
1442 message.reserve(message_size);
1443 for(u16 i=0; i<message_size; i++)
1446 message.append(buf, 1);
1448 // Pass on to the environment
1450 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1451 m_env.processActiveObjectMessage(id, message);
1456 else if(command == TOCLIENT_MOVEMENT)
1458 std::string datastring((char*)&data[2], datasize-2);
1459 std::istringstream is(datastring, std::ios_base::binary);
1460 Player *player = m_env.getLocalPlayer();
1461 assert(player != NULL);
1463 player->movement_acceleration_default = readF1000(is) * BS;
1464 player->movement_acceleration_air = readF1000(is) * BS;
1465 player->movement_acceleration_fast = readF1000(is) * BS;
1466 player->movement_speed_walk = readF1000(is) * BS;
1467 player->movement_speed_crouch = readF1000(is) * BS;
1468 player->movement_speed_fast = readF1000(is) * BS;
1469 player->movement_speed_climb = readF1000(is) * BS;
1470 player->movement_speed_jump = readF1000(is) * BS;
1471 player->movement_liquid_fluidity = readF1000(is) * BS;
1472 player->movement_liquid_fluidity_smooth = readF1000(is) * BS;
1473 player->movement_liquid_sink = readF1000(is) * BS;
1474 player->movement_gravity = readF1000(is) * BS;
1476 else if(command == TOCLIENT_HP)
1478 std::string datastring((char*)&data[2], datasize-2);
1479 std::istringstream is(datastring, std::ios_base::binary);
1480 Player *player = m_env.getLocalPlayer();
1481 assert(player != NULL);
1482 u8 oldhp = player->hp;
1488 // Add to ClientEvent queue
1490 event.type = CE_PLAYER_DAMAGE;
1491 event.player_damage.amount = oldhp - hp;
1492 m_client_event_queue.push_back(event);
1495 else if(command == TOCLIENT_BREATH)
1497 std::string datastring((char*)&data[2], datasize-2);
1498 std::istringstream is(datastring, std::ios_base::binary);
1499 Player *player = m_env.getLocalPlayer();
1500 assert(player != NULL);
1501 u16 breath = readU16(is);
1502 player->setBreath(breath) ;
1504 else if(command == TOCLIENT_MOVE_PLAYER)
1506 std::string datastring((char*)&data[2], datasize-2);
1507 std::istringstream is(datastring, std::ios_base::binary);
1508 Player *player = m_env.getLocalPlayer();
1509 assert(player != NULL);
1510 v3f pos = readV3F1000(is);
1511 f32 pitch = readF1000(is);
1512 f32 yaw = readF1000(is);
1513 player->setPosition(pos);
1514 /*player->setPitch(pitch);
1515 player->setYaw(yaw);*/
1517 infostream<<"Client got TOCLIENT_MOVE_PLAYER"
1518 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1524 Add to ClientEvent queue.
1525 This has to be sent to the main program because otherwise
1526 it would just force the pitch and yaw values to whatever
1527 the camera points to.
1530 event.type = CE_PLAYER_FORCE_MOVE;
1531 event.player_force_move.pitch = pitch;
1532 event.player_force_move.yaw = yaw;
1533 m_client_event_queue.push_back(event);
1535 // Ignore damage for a few seconds, so that the player doesn't
1536 // get damage from falling on ground
1537 m_ignore_damage_timer = 3.0;
1539 else if(command == TOCLIENT_PLAYERITEM)
1541 infostream<<"Client: WARNING: Ignoring TOCLIENT_PLAYERITEM"<<std::endl;
1543 else if(command == TOCLIENT_DEATHSCREEN)
1545 std::string datastring((char*)&data[2], datasize-2);
1546 std::istringstream is(datastring, std::ios_base::binary);
1548 bool set_camera_point_target = readU8(is);
1549 v3f camera_point_target = readV3F1000(is);
1552 event.type = CE_DEATHSCREEN;
1553 event.deathscreen.set_camera_point_target = set_camera_point_target;
1554 event.deathscreen.camera_point_target_x = camera_point_target.X;
1555 event.deathscreen.camera_point_target_y = camera_point_target.Y;
1556 event.deathscreen.camera_point_target_z = camera_point_target.Z;
1557 m_client_event_queue.push_back(event);
1559 else if(command == TOCLIENT_ANNOUNCE_MEDIA)
1561 std::string datastring((char*)&data[2], datasize-2);
1562 std::istringstream is(datastring, std::ios_base::binary);
1564 int num_files = readU16(is);
1566 infostream<<"Client: Received media announcement: packet size: "
1567 <<datasize<<std::endl;
1569 if (m_media_downloader == NULL ||
1570 m_media_downloader->isStarted()) {
1571 const char *problem = m_media_downloader ?
1572 "we already saw another announcement" :
1573 "all media has been received already";
1574 errorstream<<"Client: Received media announcement but "
1576 <<" files="<<num_files
1577 <<" size="<<datasize<<std::endl;
1581 // Mesh update thread must be stopped while
1582 // updating content definitions
1583 assert(!m_mesh_update_thread.IsRunning());
1585 for(int i=0; i<num_files; i++)
1587 std::string name = deSerializeString(is);
1588 std::string sha1_base64 = deSerializeString(is);
1589 std::string sha1_raw = base64_decode(sha1_base64);
1590 m_media_downloader->addFile(name, sha1_raw);
1593 std::vector<std::string> remote_media;
1595 Strfnd sf(deSerializeString(is));
1596 while(!sf.atend()) {
1597 std::string baseurl = trim(sf.next(","));
1599 m_media_downloader->addRemoteServer(baseurl);
1602 catch(SerializationError) {
1603 // not supported by server or turned off
1606 m_media_downloader->step(this);
1608 else if(command == TOCLIENT_MEDIA)
1610 std::string datastring((char*)&data[2], datasize-2);
1611 std::istringstream is(datastring, std::ios_base::binary);
1615 u16 total number of file bunches
1616 u16 index of this bunch
1617 u32 number of files in this bunch
1625 int num_bunches = readU16(is);
1626 int bunch_i = readU16(is);
1627 u32 num_files = readU32(is);
1628 infostream<<"Client: Received files: bunch "<<bunch_i<<"/"
1629 <<num_bunches<<" files="<<num_files
1630 <<" size="<<datasize<<std::endl;
1635 if (m_media_downloader == NULL ||
1636 !m_media_downloader->isStarted()) {
1637 const char *problem = m_media_downloader ?
1638 "media has not been requested" :
1639 "all media has been received already";
1640 errorstream<<"Client: Received media but "
1642 <<" bunch "<<bunch_i<<"/"<<num_bunches
1643 <<" files="<<num_files
1644 <<" size="<<datasize<<std::endl;
1648 // Mesh update thread must be stopped while
1649 // updating content definitions
1650 assert(!m_mesh_update_thread.IsRunning());
1652 for(u32 i=0; i<num_files; i++){
1653 std::string name = deSerializeString(is);
1654 std::string data = deSerializeLongString(is);
1655 m_media_downloader->conventionalTransferDone(
1659 else if(command == TOCLIENT_TOOLDEF)
1661 infostream<<"Client: WARNING: Ignoring TOCLIENT_TOOLDEF"<<std::endl;
1663 else if(command == TOCLIENT_NODEDEF)
1665 infostream<<"Client: Received node definitions: packet size: "
1666 <<datasize<<std::endl;
1668 // Mesh update thread must be stopped while
1669 // updating content definitions
1670 assert(!m_mesh_update_thread.IsRunning());
1672 // Decompress node definitions
1673 std::string datastring((char*)&data[2], datasize-2);
1674 std::istringstream is(datastring, std::ios_base::binary);
1675 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1676 std::ostringstream tmp_os;
1677 decompressZlib(tmp_is, tmp_os);
1679 // Deserialize node definitions
1680 std::istringstream tmp_is2(tmp_os.str());
1681 m_nodedef->deSerialize(tmp_is2);
1682 m_nodedef_received = true;
1684 else if(command == TOCLIENT_CRAFTITEMDEF)
1686 infostream<<"Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF"<<std::endl;
1688 else if(command == TOCLIENT_ITEMDEF)
1690 infostream<<"Client: Received item definitions: packet size: "
1691 <<datasize<<std::endl;
1693 // Mesh update thread must be stopped while
1694 // updating content definitions
1695 assert(!m_mesh_update_thread.IsRunning());
1697 // Decompress item definitions
1698 std::string datastring((char*)&data[2], datasize-2);
1699 std::istringstream is(datastring, std::ios_base::binary);
1700 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1701 std::ostringstream tmp_os;
1702 decompressZlib(tmp_is, tmp_os);
1704 // Deserialize node definitions
1705 std::istringstream tmp_is2(tmp_os.str());
1706 m_itemdef->deSerialize(tmp_is2);
1707 m_itemdef_received = true;
1709 else if(command == TOCLIENT_PLAY_SOUND)
1711 std::string datastring((char*)&data[2], datasize-2);
1712 std::istringstream is(datastring, std::ios_base::binary);
1714 s32 server_id = readS32(is);
1715 std::string name = deSerializeString(is);
1716 float gain = readF1000(is);
1717 int type = readU8(is); // 0=local, 1=positional, 2=object
1718 v3f pos = readV3F1000(is);
1719 u16 object_id = readU16(is);
1720 bool loop = readU8(is);
1725 client_id = m_sound->playSound(name, loop, gain);
1727 case 1: // positional
1728 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1731 ClientActiveObject *cao = m_env.getActiveObject(object_id);
1733 pos = cao->getPosition();
1734 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1735 // TODO: Set up sound to move with object
1740 if(client_id != -1){
1741 m_sounds_server_to_client[server_id] = client_id;
1742 m_sounds_client_to_server[client_id] = server_id;
1744 m_sounds_to_objects[client_id] = object_id;
1747 else if(command == TOCLIENT_STOP_SOUND)
1749 std::string datastring((char*)&data[2], datasize-2);
1750 std::istringstream is(datastring, std::ios_base::binary);
1752 s32 server_id = readS32(is);
1753 std::map<s32, int>::iterator i =
1754 m_sounds_server_to_client.find(server_id);
1755 if(i != m_sounds_server_to_client.end()){
1756 int client_id = i->second;
1757 m_sound->stopSound(client_id);
1760 else if(command == TOCLIENT_PRIVILEGES)
1762 std::string datastring((char*)&data[2], datasize-2);
1763 std::istringstream is(datastring, std::ios_base::binary);
1765 m_privileges.clear();
1766 infostream<<"Client: Privileges updated: ";
1767 u16 num_privileges = readU16(is);
1768 for(u16 i=0; i<num_privileges; i++){
1769 std::string priv = deSerializeString(is);
1770 m_privileges.insert(priv);
1771 infostream<<priv<<" ";
1773 infostream<<std::endl;
1775 else if(command == TOCLIENT_INVENTORY_FORMSPEC)
1777 std::string datastring((char*)&data[2], datasize-2);
1778 std::istringstream is(datastring, std::ios_base::binary);
1780 // Store formspec in LocalPlayer
1781 Player *player = m_env.getLocalPlayer();
1782 assert(player != NULL);
1783 player->inventory_formspec = deSerializeLongString(is);
1785 else if(command == TOCLIENT_DETACHED_INVENTORY)
1787 std::string datastring((char*)&data[2], datasize-2);
1788 std::istringstream is(datastring, std::ios_base::binary);
1790 std::string name = deSerializeString(is);
1792 infostream<<"Client: Detached inventory update: \""<<name<<"\""<<std::endl;
1794 Inventory *inv = NULL;
1795 if(m_detached_inventories.count(name) > 0)
1796 inv = m_detached_inventories[name];
1798 inv = new Inventory(m_itemdef);
1799 m_detached_inventories[name] = inv;
1801 inv->deSerialize(is);
1803 else if(command == TOCLIENT_SHOW_FORMSPEC)
1805 std::string datastring((char*)&data[2], datasize-2);
1806 std::istringstream is(datastring, std::ios_base::binary);
1808 std::string formspec = deSerializeLongString(is);
1809 std::string formname = deSerializeString(is);
1812 event.type = CE_SHOW_FORMSPEC;
1813 // pointer is required as event is a struct only!
1814 // adding a std:string to a struct isn't possible
1815 event.show_formspec.formspec = new std::string(formspec);
1816 event.show_formspec.formname = new std::string(formname);
1817 m_client_event_queue.push_back(event);
1819 else if(command == TOCLIENT_SPAWN_PARTICLE)
1821 std::string datastring((char*)&data[2], datasize-2);
1822 std::istringstream is(datastring, std::ios_base::binary);
1824 v3f pos = readV3F1000(is);
1825 v3f vel = readV3F1000(is);
1826 v3f acc = readV3F1000(is);
1827 float expirationtime = readF1000(is);
1828 float size = readF1000(is);
1829 bool collisiondetection = readU8(is);
1830 std::string texture = deSerializeLongString(is);
1831 bool vertical = false;
1833 vertical = readU8(is);
1837 event.type = CE_SPAWN_PARTICLE;
1838 event.spawn_particle.pos = new v3f (pos);
1839 event.spawn_particle.vel = new v3f (vel);
1840 event.spawn_particle.acc = new v3f (acc);
1842 event.spawn_particle.expirationtime = expirationtime;
1843 event.spawn_particle.size = size;
1844 event.spawn_particle.collisiondetection =
1846 event.spawn_particle.vertical = vertical;
1847 event.spawn_particle.texture = new std::string(texture);
1849 m_client_event_queue.push_back(event);
1851 else if(command == TOCLIENT_ADD_PARTICLESPAWNER)
1853 std::string datastring((char*)&data[2], datasize-2);
1854 std::istringstream is(datastring, std::ios_base::binary);
1856 u16 amount = readU16(is);
1857 float spawntime = readF1000(is);
1858 v3f minpos = readV3F1000(is);
1859 v3f maxpos = readV3F1000(is);
1860 v3f minvel = readV3F1000(is);
1861 v3f maxvel = readV3F1000(is);
1862 v3f minacc = readV3F1000(is);
1863 v3f maxacc = readV3F1000(is);
1864 float minexptime = readF1000(is);
1865 float maxexptime = readF1000(is);
1866 float minsize = readF1000(is);
1867 float maxsize = readF1000(is);
1868 bool collisiondetection = readU8(is);
1869 std::string texture = deSerializeLongString(is);
1870 u32 id = readU32(is);
1871 bool vertical = false;
1873 vertical = readU8(is);
1877 event.type = CE_ADD_PARTICLESPAWNER;
1878 event.add_particlespawner.amount = amount;
1879 event.add_particlespawner.spawntime = spawntime;
1881 event.add_particlespawner.minpos = new v3f (minpos);
1882 event.add_particlespawner.maxpos = new v3f (maxpos);
1883 event.add_particlespawner.minvel = new v3f (minvel);
1884 event.add_particlespawner.maxvel = new v3f (maxvel);
1885 event.add_particlespawner.minacc = new v3f (minacc);
1886 event.add_particlespawner.maxacc = new v3f (maxacc);
1888 event.add_particlespawner.minexptime = minexptime;
1889 event.add_particlespawner.maxexptime = maxexptime;
1890 event.add_particlespawner.minsize = minsize;
1891 event.add_particlespawner.maxsize = maxsize;
1892 event.add_particlespawner.collisiondetection = collisiondetection;
1893 event.add_particlespawner.vertical = vertical;
1894 event.add_particlespawner.texture = new std::string(texture);
1895 event.add_particlespawner.id = id;
1897 m_client_event_queue.push_back(event);
1899 else if(command == TOCLIENT_DELETE_PARTICLESPAWNER)
1901 std::string datastring((char*)&data[2], datasize-2);
1902 std::istringstream is(datastring, std::ios_base::binary);
1904 u32 id = readU16(is);
1907 event.type = CE_DELETE_PARTICLESPAWNER;
1908 event.delete_particlespawner.id = id;
1910 m_client_event_queue.push_back(event);
1912 else if(command == TOCLIENT_HUDADD)
1914 std::string datastring((char *)&data[2], datasize - 2);
1915 std::istringstream is(datastring, std::ios_base::binary);
1917 u32 id = readU32(is);
1918 u8 type = readU8(is);
1919 v2f pos = readV2F1000(is);
1920 std::string name = deSerializeString(is);
1921 v2f scale = readV2F1000(is);
1922 std::string text = deSerializeString(is);
1923 u32 number = readU32(is);
1924 u32 item = readU32(is);
1925 u32 dir = readU32(is);
1926 v2f align = readV2F1000(is);
1927 v2f offset = readV2F1000(is);
1930 world_pos = readV3F1000(is);
1931 }catch(SerializationError &e) {};
1934 event.type = CE_HUDADD;
1935 event.hudadd.id = id;
1936 event.hudadd.type = type;
1937 event.hudadd.pos = new v2f(pos);
1938 event.hudadd.name = new std::string(name);
1939 event.hudadd.scale = new v2f(scale);
1940 event.hudadd.text = new std::string(text);
1941 event.hudadd.number = number;
1942 event.hudadd.item = item;
1943 event.hudadd.dir = dir;
1944 event.hudadd.align = new v2f(align);
1945 event.hudadd.offset = new v2f(offset);
1946 event.hudadd.world_pos = new v3f(world_pos);
1947 m_client_event_queue.push_back(event);
1949 else if(command == TOCLIENT_HUDRM)
1951 std::string datastring((char *)&data[2], datasize - 2);
1952 std::istringstream is(datastring, std::ios_base::binary);
1954 u32 id = readU32(is);
1957 event.type = CE_HUDRM;
1958 event.hudrm.id = id;
1959 m_client_event_queue.push_back(event);
1961 else if(command == TOCLIENT_HUDCHANGE)
1968 std::string datastring((char *)&data[2], datasize - 2);
1969 std::istringstream is(datastring, std::ios_base::binary);
1971 u32 id = readU32(is);
1972 u8 stat = (HudElementStat)readU8(is);
1974 if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
1975 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
1976 v2fdata = readV2F1000(is);
1977 else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
1978 sdata = deSerializeString(is);
1979 else if (stat == HUD_STAT_WORLD_POS)
1980 v3fdata = readV3F1000(is);
1982 intdata = readU32(is);
1985 event.type = CE_HUDCHANGE;
1986 event.hudchange.id = id;
1987 event.hudchange.stat = (HudElementStat)stat;
1988 event.hudchange.v2fdata = new v2f(v2fdata);
1989 event.hudchange.v3fdata = new v3f(v3fdata);
1990 event.hudchange.sdata = new std::string(sdata);
1991 event.hudchange.data = intdata;
1992 m_client_event_queue.push_back(event);
1994 else if(command == TOCLIENT_HUD_SET_FLAGS)
1996 std::string datastring((char *)&data[2], datasize - 2);
1997 std::istringstream is(datastring, std::ios_base::binary);
1999 Player *player = m_env.getLocalPlayer();
2000 assert(player != NULL);
2002 u32 flags = readU32(is);
2003 u32 mask = readU32(is);
2005 player->hud_flags &= ~mask;
2006 player->hud_flags |= flags;
2008 else if(command == TOCLIENT_HUD_SET_PARAM)
2010 std::string datastring((char *)&data[2], datasize - 2);
2011 std::istringstream is(datastring, std::ios_base::binary);
2013 Player *player = m_env.getLocalPlayer();
2014 assert(player != NULL);
2016 u16 param = readU16(is);
2017 std::string value = deSerializeString(is);
2019 if(param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4){
2020 s32 hotbar_itemcount = readS32((u8*) value.c_str());
2021 if(hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
2022 player->hud_hotbar_itemcount = hotbar_itemcount;
2023 } else if (param == HUD_PARAM_HOTBAR_IMAGE) {
2024 ((LocalPlayer *) player)->hotbar_image = value;
2025 } else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
2026 ((LocalPlayer *) player)->hotbar_selected_image = value;
2029 else if(command == TOCLIENT_SET_SKY)
2031 std::string datastring((char *)&data[2], datasize - 2);
2032 std::istringstream is(datastring, std::ios_base::binary);
2034 video::SColor *bgcolor = new video::SColor(readARGB8(is));
2035 std::string *type = new std::string(deSerializeString(is));
2036 u16 count = readU16(is);
2037 std::vector<std::string> *params = new std::vector<std::string>;
2038 for(size_t i=0; i<count; i++)
2039 params->push_back(deSerializeString(is));
2042 event.type = CE_SET_SKY;
2043 event.set_sky.bgcolor = bgcolor;
2044 event.set_sky.type = type;
2045 event.set_sky.params = params;
2046 m_client_event_queue.push_back(event);
2050 infostream<<"Client: Ignoring unknown command "
2051 <<command<<std::endl;
2055 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
2057 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2058 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
2061 void Client::interact(u8 action, const PointedThing& pointed)
2063 if(connectedAndInitialized() == false){
2064 infostream<<"Client::interact() "
2065 "cancelled (not connected)"
2070 std::ostringstream os(std::ios_base::binary);
2076 [5] u32 length of the next item
2077 [9] serialized PointedThing
2079 0: start digging (from undersurface) or use
2080 1: stop digging (all parameters ignored)
2081 2: digging completed
2082 3: place block or item (to abovesurface)
2085 writeU16(os, TOSERVER_INTERACT);
2086 writeU8(os, action);
2087 writeU16(os, getPlayerItem());
2088 std::ostringstream tmp_os(std::ios::binary);
2089 pointed.serialize(tmp_os);
2090 os<<serializeLongString(tmp_os.str());
2092 std::string s = os.str();
2093 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2096 Send(0, data, true);
2099 void Client::sendNodemetaFields(v3s16 p, const std::string &formname,
2100 const std::map<std::string, std::string> &fields)
2102 std::ostringstream os(std::ios_base::binary);
2104 writeU16(os, TOSERVER_NODEMETA_FIELDS);
2106 os<<serializeString(formname);
2107 writeU16(os, fields.size());
2108 for(std::map<std::string, std::string>::const_iterator
2109 i = fields.begin(); i != fields.end(); i++){
2110 const std::string &name = i->first;
2111 const std::string &value = i->second;
2112 os<<serializeString(name);
2113 os<<serializeLongString(value);
2117 std::string s = os.str();
2118 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2120 Send(0, data, true);
2123 void Client::sendInventoryFields(const std::string &formname,
2124 const std::map<std::string, std::string> &fields)
2126 std::ostringstream os(std::ios_base::binary);
2128 writeU16(os, TOSERVER_INVENTORY_FIELDS);
2129 os<<serializeString(formname);
2130 writeU16(os, fields.size());
2131 for(std::map<std::string, std::string>::const_iterator
2132 i = fields.begin(); i != fields.end(); i++){
2133 const std::string &name = i->first;
2134 const std::string &value = i->second;
2135 os<<serializeString(name);
2136 os<<serializeLongString(value);
2140 std::string s = os.str();
2141 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2143 Send(0, data, true);
2146 void Client::sendInventoryAction(InventoryAction *a)
2148 std::ostringstream os(std::ios_base::binary);
2152 writeU16(buf, TOSERVER_INVENTORY_ACTION);
2153 os.write((char*)buf, 2);
2158 std::string s = os.str();
2159 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2161 Send(0, data, true);
2164 void Client::sendChatMessage(const std::wstring &message)
2166 std::ostringstream os(std::ios_base::binary);
2170 writeU16(buf, TOSERVER_CHAT_MESSAGE);
2171 os.write((char*)buf, 2);
2174 writeU16(buf, message.size());
2175 os.write((char*)buf, 2);
2178 for(u32 i=0; i<message.size(); i++)
2182 os.write((char*)buf, 2);
2186 std::string s = os.str();
2187 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2189 Send(0, data, true);
2192 void Client::sendChangePassword(const std::wstring oldpassword,
2193 const std::wstring newpassword)
2195 Player *player = m_env.getLocalPlayer();
2199 std::string playername = player->getName();
2200 std::string oldpwd = translatePassword(playername, oldpassword);
2201 std::string newpwd = translatePassword(playername, newpassword);
2203 std::ostringstream os(std::ios_base::binary);
2204 u8 buf[2+PASSWORD_SIZE*2];
2206 [0] u16 TOSERVER_PASSWORD
2207 [2] u8[28] old password
2208 [30] u8[28] new password
2211 writeU16(buf, TOSERVER_PASSWORD);
2212 for(u32 i=0;i<PASSWORD_SIZE-1;i++)
2214 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
2215 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
2217 buf[2+PASSWORD_SIZE-1] = 0;
2218 buf[30+PASSWORD_SIZE-1] = 0;
2219 os.write((char*)buf, 2+PASSWORD_SIZE*2);
2222 std::string s = os.str();
2223 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2225 Send(0, data, true);
2229 void Client::sendDamage(u8 damage)
2231 DSTACK(__FUNCTION_NAME);
2232 std::ostringstream os(std::ios_base::binary);
2234 writeU16(os, TOSERVER_DAMAGE);
2235 writeU8(os, damage);
2238 std::string s = os.str();
2239 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2241 Send(0, data, true);
2244 void Client::sendBreath(u16 breath)
2246 DSTACK(__FUNCTION_NAME);
2247 std::ostringstream os(std::ios_base::binary);
2249 writeU16(os, TOSERVER_BREATH);
2250 writeU16(os, breath);
2252 std::string s = os.str();
2253 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2255 Send(0, data, true);
2258 void Client::sendRespawn()
2260 DSTACK(__FUNCTION_NAME);
2261 std::ostringstream os(std::ios_base::binary);
2263 writeU16(os, TOSERVER_RESPAWN);
2266 std::string s = os.str();
2267 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2269 Send(0, data, true);
2272 void Client::sendPlayerPos()
2274 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2276 LocalPlayer *myplayer = m_env.getLocalPlayer();
2277 if(myplayer == NULL)
2280 // Save bandwidth by only updating position when something changed
2281 if(myplayer->last_position == myplayer->getPosition() &&
2282 myplayer->last_speed == myplayer->getSpeed() &&
2283 myplayer->last_pitch == myplayer->getPitch() &&
2284 myplayer->last_yaw == myplayer->getYaw() &&
2285 myplayer->last_keyPressed == myplayer->keyPressed)
2288 myplayer->last_position = myplayer->getPosition();
2289 myplayer->last_speed = myplayer->getSpeed();
2290 myplayer->last_pitch = myplayer->getPitch();
2291 myplayer->last_yaw = myplayer->getYaw();
2292 myplayer->last_keyPressed = myplayer->keyPressed;
2296 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2297 our_peer_id = m_con.GetPeerID();
2300 // Set peer id if not set already
2301 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2302 myplayer->peer_id = our_peer_id;
2303 // Check that an existing peer_id is the same as the connection's
2304 assert(myplayer->peer_id == our_peer_id);
2306 v3f pf = myplayer->getPosition();
2307 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
2308 v3f sf = myplayer->getSpeed();
2309 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
2310 s32 pitch = myplayer->getPitch() * 100;
2311 s32 yaw = myplayer->getYaw() * 100;
2312 u32 keyPressed=myplayer->keyPressed;
2316 [2] v3s32 position*100
2317 [2+12] v3s32 speed*100
2318 [2+12+12] s32 pitch*100
2319 [2+12+12+4] s32 yaw*100
2320 [2+12+12+4+4] u32 keyPressed
2322 SharedBuffer<u8> data(2+12+12+4+4+4);
2323 writeU16(&data[0], TOSERVER_PLAYERPOS);
2324 writeV3S32(&data[2], position);
2325 writeV3S32(&data[2+12], speed);
2326 writeS32(&data[2+12+12], pitch);
2327 writeS32(&data[2+12+12+4], yaw);
2328 writeU32(&data[2+12+12+4+4], keyPressed);
2329 // Send as unreliable
2330 Send(0, data, false);
2333 void Client::sendPlayerItem(u16 item)
2335 Player *myplayer = m_env.getLocalPlayer();
2336 if(myplayer == NULL)
2339 u16 our_peer_id = m_con.GetPeerID();
2341 // Set peer id if not set already
2342 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2343 myplayer->peer_id = our_peer_id;
2344 // Check that an existing peer_id is the same as the connection's
2345 assert(myplayer->peer_id == our_peer_id);
2347 SharedBuffer<u8> data(2+2);
2348 writeU16(&data[0], TOSERVER_PLAYERITEM);
2349 writeU16(&data[2], item);
2352 Send(0, data, true);
2355 void Client::removeNode(v3s16 p)
2357 std::map<v3s16, MapBlock*> modified_blocks;
2361 //TimeTaker t("removeNodeAndUpdate", m_device);
2362 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
2364 catch(InvalidPositionException &e)
2368 // add urgent task to update the modified node
2369 addUpdateMeshTaskForNode(p, false, true);
2371 for(std::map<v3s16, MapBlock * >::iterator
2372 i = modified_blocks.begin();
2373 i != modified_blocks.end(); ++i)
2375 addUpdateMeshTaskWithEdge(i->first);
2379 void Client::addNode(v3s16 p, MapNode n, bool remove_metadata)
2381 TimeTaker timer1("Client::addNode()");
2383 std::map<v3s16, MapBlock*> modified_blocks;
2387 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
2388 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
2390 catch(InvalidPositionException &e)
2393 for(std::map<v3s16, MapBlock * >::iterator
2394 i = modified_blocks.begin();
2395 i != modified_blocks.end(); ++i)
2397 addUpdateMeshTaskWithEdge(i->first);
2401 void Client::setPlayerControl(PlayerControl &control)
2403 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2404 LocalPlayer *player = m_env.getLocalPlayer();
2405 assert(player != NULL);
2406 player->control = control;
2409 void Client::selectPlayerItem(u16 item)
2411 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2412 m_playeritem = item;
2413 m_inventory_updated = true;
2414 sendPlayerItem(item);
2417 // Returns true if the inventory of the local player has been
2418 // updated from the server. If it is true, it is set to false.
2419 bool Client::getLocalInventoryUpdated()
2421 // m_inventory_updated is behind envlock
2422 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2423 bool updated = m_inventory_updated;
2424 m_inventory_updated = false;
2428 // Copies the inventory of the local player to parameter
2429 void Client::getLocalInventory(Inventory &dst)
2431 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2432 Player *player = m_env.getLocalPlayer();
2433 assert(player != NULL);
2434 dst = player->inventory;
2437 Inventory* Client::getInventory(const InventoryLocation &loc)
2440 case InventoryLocation::UNDEFINED:
2443 case InventoryLocation::CURRENT_PLAYER:
2445 Player *player = m_env.getLocalPlayer();
2446 assert(player != NULL);
2447 return &player->inventory;
2450 case InventoryLocation::PLAYER:
2452 Player *player = m_env.getPlayer(loc.name.c_str());
2455 return &player->inventory;
2458 case InventoryLocation::NODEMETA:
2460 NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p);
2463 return meta->getInventory();
2466 case InventoryLocation::DETACHED:
2468 if(m_detached_inventories.count(loc.name) == 0)
2470 return m_detached_inventories[loc.name];
2478 void Client::inventoryAction(InventoryAction *a)
2481 Send it to the server
2483 sendInventoryAction(a);
2486 Predict some local inventory changes
2488 a->clientApply(this, this);
2494 ClientActiveObject * Client::getSelectedActiveObject(
2496 v3f from_pos_f_on_map,
2497 core::line3d<f32> shootline_on_map
2500 std::vector<DistanceSortedActiveObject> objects;
2502 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
2504 //infostream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
2507 // After this, the closest object is the first in the array.
2508 std::sort(objects.begin(), objects.end());
2510 for(u32 i=0; i<objects.size(); i++)
2512 ClientActiveObject *obj = objects[i].obj;
2514 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
2515 if(selection_box == NULL)
2518 v3f pos = obj->getPosition();
2520 core::aabbox3d<f32> offsetted_box(
2521 selection_box->MinEdge + pos,
2522 selection_box->MaxEdge + pos
2525 if(offsetted_box.intersectsWithLine(shootline_on_map))
2527 //infostream<<"Returning selected object"<<std::endl;
2532 //infostream<<"No object selected; returning NULL."<<std::endl;
2536 void Client::printDebugInfo(std::ostream &os)
2538 //JMutexAutoLock lock1(m_fetchblock_mutex);
2539 /*JMutexAutoLock lock2(m_incoming_queue_mutex);
2541 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
2542 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
2543 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
2547 std::list<std::string> Client::getConnectedPlayerNames()
2549 return m_env.getPlayerNames();
2552 float Client::getAnimationTime()
2554 return m_animation_time;
2557 int Client::getCrackLevel()
2559 return m_crack_level;
2562 void Client::setCrack(int level, v3s16 pos)
2564 int old_crack_level = m_crack_level;
2565 v3s16 old_crack_pos = m_crack_pos;
2567 m_crack_level = level;
2570 if(old_crack_level >= 0 && (level < 0 || pos != old_crack_pos))
2573 addUpdateMeshTaskForNode(old_crack_pos, false, true);
2575 if(level >= 0 && (old_crack_level < 0 || pos != old_crack_pos))
2578 addUpdateMeshTaskForNode(pos, false, true);
2584 Player *player = m_env.getLocalPlayer();
2585 assert(player != NULL);
2589 u16 Client::getBreath()
2591 Player *player = m_env.getLocalPlayer();
2592 assert(player != NULL);
2593 return player->getBreath();
2596 bool Client::getChatMessage(std::wstring &message)
2598 if(m_chat_queue.size() == 0)
2600 message = m_chat_queue.pop_front();
2604 void Client::typeChatMessage(const std::wstring &message)
2606 // Discard empty line
2611 sendChatMessage(message);
2614 if (message[0] == L'/')
2616 m_chat_queue.push_back(
2617 (std::wstring)L"issued command: "+message);
2621 LocalPlayer *player = m_env.getLocalPlayer();
2622 assert(player != NULL);
2623 std::wstring name = narrow_to_wide(player->getName());
2624 m_chat_queue.push_back(
2625 (std::wstring)L"<"+name+L"> "+message);
2629 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
2631 /*infostream<<"Client::addUpdateMeshTask(): "
2632 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2633 <<" ack_to_server="<<ack_to_server
2634 <<" urgent="<<urgent
2637 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2642 Create a task to update the mesh of the block
2645 MeshMakeData *data = new MeshMakeData(this);
2648 //TimeTaker timer("data fill");
2650 // Debug: 1-6ms, avg=2ms
2652 data->setCrack(m_crack_level, m_crack_pos);
2653 data->setSmoothLighting(g_settings->getBool("smooth_lighting"));
2657 //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
2659 // Add task to queue
2660 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server, urgent);
2662 /*infostream<<"Mesh update input queue size is "
2663 <<m_mesh_update_thread.m_queue_in.size()
2667 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent)
2671 infostream<<"Client::addUpdateMeshTaskWithEdge(): "
2672 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2677 v3s16 p = blockpos + v3s16(0,0,0);
2678 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2679 addUpdateMeshTask(p, ack_to_server, urgent);
2681 catch(InvalidPositionException &e){}
2683 for (int i=0;i<6;i++)
2686 v3s16 p = blockpos + g_6dirs[i];
2687 addUpdateMeshTask(p, false, urgent);
2689 catch(InvalidPositionException &e){}
2693 void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool urgent)
2697 infostream<<"Client::addUpdateMeshTaskForNode(): "
2698 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2702 v3s16 blockpos = getNodeBlockPos(nodepos);
2703 v3s16 blockpos_relative = blockpos * MAP_BLOCKSIZE;
2706 v3s16 p = blockpos + v3s16(0,0,0);
2707 addUpdateMeshTask(p, ack_to_server, urgent);
2709 catch(InvalidPositionException &e){}
2711 if(nodepos.X == blockpos_relative.X){
2713 v3s16 p = blockpos + v3s16(-1,0,0);
2714 addUpdateMeshTask(p, false, urgent);
2716 catch(InvalidPositionException &e){}
2718 if(nodepos.Y == blockpos_relative.Y){
2720 v3s16 p = blockpos + v3s16(0,-1,0);
2721 addUpdateMeshTask(p, false, urgent);
2723 catch(InvalidPositionException &e){}
2725 if(nodepos.Z == blockpos_relative.Z){
2727 v3s16 p = blockpos + v3s16(0,0,-1);
2728 addUpdateMeshTask(p, false, urgent);
2730 catch(InvalidPositionException &e){}
2734 ClientEvent Client::getClientEvent()
2736 if(m_client_event_queue.size() == 0)
2739 event.type = CE_NONE;
2742 return m_client_event_queue.pop_front();
2745 float Client::mediaReceiveProgress()
2747 if (m_media_downloader)
2748 return m_media_downloader->getProgress();
2750 return 1.0; // downloader only exists when not yet done
2753 void draw_load_screen(const std::wstring &text,
2754 IrrlichtDevice* device, gui::IGUIFont* font,
2755 float dtime=0 ,int percent=0, bool clouds=true);
2756 void Client::afterContentReceived(IrrlichtDevice *device, gui::IGUIFont* font)
2758 infostream<<"Client::afterContentReceived() started"<<std::endl;
2759 assert(m_itemdef_received);
2760 assert(m_nodedef_received);
2761 assert(mediaReceived());
2763 // Rebuild inherited images and recreate textures
2764 infostream<<"- Rebuilding images and textures"<<std::endl;
2765 m_tsrc->rebuildImagesAndTextures();
2768 infostream<<"- Rebuilding shaders"<<std::endl;
2769 m_shsrc->rebuildShaders();
2771 // Update node aliases
2772 infostream<<"- Updating node aliases"<<std::endl;
2773 m_nodedef->updateAliases(m_itemdef);
2775 // Update node textures
2776 infostream<<"- Updating node textures"<<std::endl;
2777 m_nodedef->updateTextures(m_tsrc);
2779 // Preload item textures and meshes if configured to
2780 if(g_settings->getBool("preload_item_visuals"))
2782 verbosestream<<"Updating item textures and meshes"<<std::endl;
2783 wchar_t* text = wgettext("Item textures...");
2784 draw_load_screen(text,device,font,0,0);
2785 std::set<std::string> names = m_itemdef->getAll();
2786 size_t size = names.size();
2789 for(std::set<std::string>::const_iterator
2790 i = names.begin(); i != names.end(); ++i){
2791 // Asking for these caches the result
2792 m_itemdef->getInventoryTexture(*i, this);
2793 m_itemdef->getWieldMesh(*i, this);
2795 percent = count*100/size;
2796 if (count%50 == 0) // only update every 50 item
2797 draw_load_screen(text,device,font,0,percent);
2802 // Start mesh update thread after setting up content definitions
2803 infostream<<"- Starting mesh update thread"<<std::endl;
2804 m_mesh_update_thread.Start();
2806 infostream<<"Client::afterContentReceived() done"<<std::endl;
2809 float Client::getRTT(void)
2812 return m_con.GetPeerAvgRTT(PEER_ID_SERVER);
2813 } catch(con::PeerNotFoundException &e){
2818 // IGameDef interface
2820 IItemDefManager* Client::getItemDefManager()
2824 INodeDefManager* Client::getNodeDefManager()
2828 ICraftDefManager* Client::getCraftDefManager()
2831 //return m_craftdef;
2833 ITextureSource* Client::getTextureSource()
2837 IShaderSource* Client::getShaderSource()
2841 u16 Client::allocateUnknownNodeId(const std::string &name)
2843 errorstream<<"Client::allocateUnknownNodeId(): "
2844 <<"Client cannot allocate node IDs"<<std::endl;
2846 return CONTENT_IGNORE;
2848 ISoundManager* Client::getSoundManager()
2852 MtEventManager* Client::getEventManager()
2857 scene::IAnimatedMesh* Client::getMesh(const std::string &filename)
2859 std::map<std::string, std::string>::const_iterator i =
2860 m_mesh_data.find(filename);
2861 if(i == m_mesh_data.end()){
2862 errorstream<<"Client::getMesh(): Mesh not found: \""<<filename<<"\""
2866 const std::string &data = i->second;
2867 scene::ISceneManager *smgr = m_device->getSceneManager();
2869 // Create the mesh, remove it from cache and return it
2870 // This allows unique vertex colors and other properties for each instance
2871 Buffer<char> data_rw(data.c_str(), data.size()); // Const-incorrect Irrlicht
2872 io::IFileSystem *irrfs = m_device->getFileSystem();
2873 io::IReadFile *rfile = irrfs->createMemoryReadFile(
2874 *data_rw, data_rw.getSize(), filename.c_str());
2876 scene::IAnimatedMesh *mesh = smgr->getMesh(rfile);
2878 // NOTE: By playing with Irrlicht refcounts, maybe we could cache a bunch
2879 // of uniquely named instances and re-use them
2881 smgr->getMeshCache()->removeMesh(mesh);