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);
388 //TimeTaker timer("m_con_mutex + m_con.RunTimeouts()", m_device);
390 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
391 m_con.RunTimeouts(dtime);
398 float &counter = m_packetcounter_timer;
404 infostream<<"Client packetcounter (20s):"<<std::endl;
405 m_packetcounter.print(infostream);
406 m_packetcounter.clear();
410 // Get connection status
411 bool connected = connectedAndInitialized();
416 Delete unused sectors
418 NOTE: This jams the game for a while because deleting sectors
422 float &counter = m_delete_unused_sectors_timer;
430 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
432 core::list<v3s16> deleted_blocks;
434 float delete_unused_sectors_timeout =
435 g_settings->getFloat("client_delete_unused_sectors_timeout");
437 // Delete sector blocks
438 /*u32 num = m_env.getMap().unloadUnusedData
439 (delete_unused_sectors_timeout,
440 true, &deleted_blocks);*/
442 // Delete whole sectors
443 m_env.getMap().unloadUnusedData
444 (delete_unused_sectors_timeout,
447 if(deleted_blocks.size() > 0)
449 /*infostream<<"Client: Deleted blocks of "<<num
450 <<" unused sectors"<<std::endl;*/
451 /*infostream<<"Client: Deleted "<<num
452 <<" unused sectors"<<std::endl;*/
458 // Env is locked so con can be locked.
459 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
461 core::list<v3s16>::Iterator i = deleted_blocks.begin();
462 core::list<v3s16> sendlist;
465 if(sendlist.size() == 255 || i == deleted_blocks.end())
467 if(sendlist.size() == 0)
476 u32 replysize = 2+1+6*sendlist.size();
477 SharedBuffer<u8> reply(replysize);
478 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
479 reply[2] = sendlist.size();
481 for(core::list<v3s16>::Iterator
482 j = sendlist.begin();
483 j != sendlist.end(); j++)
485 writeV3S16(&reply[2+1+6*k], *j);
488 m_con.Send(PEER_ID_SERVER, 1, reply, true);
490 if(i == deleted_blocks.end())
496 sendlist.push_back(*i);
504 if(connected == false)
506 float &counter = m_connection_reinit_timer;
512 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
514 Player *myplayer = m_env.getLocalPlayer();
515 assert(myplayer != NULL);
517 // Send TOSERVER_INIT
518 // [0] u16 TOSERVER_INIT
519 // [2] u8 SER_FMT_VER_HIGHEST_READ
520 // [3] u8[20] player_name
521 // [23] u8[28] password (new in some version)
522 // [51] u16 minimum supported network protocol version (added sometime)
523 // [53] u16 maximum supported network protocol version (added later than the previous one)
524 SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2+2);
525 writeU16(&data[0], TOSERVER_INIT);
526 writeU8(&data[2], SER_FMT_VER_HIGHEST_READ);
528 memset((char*)&data[3], 0, PLAYERNAME_SIZE);
529 snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
531 /*infostream<<"Client: sending initial password hash: \""<<m_password<<"\""
534 memset((char*)&data[23], 0, PASSWORD_SIZE);
535 snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
537 writeU16(&data[51], CLIENT_PROTOCOL_VERSION_MIN);
538 writeU16(&data[53], CLIENT_PROTOCOL_VERSION_MAX);
540 // Send as unreliable
541 Send(1, data, false);
544 // Not connected, return
549 Do stuff if connected
553 Run Map's timers and unload unused data
555 const float map_timer_and_unload_dtime = 5.25;
556 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
558 ScopeProfiler sp(g_profiler, "Client: map timer and unload");
559 std::list<v3s16> deleted_blocks;
560 m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
561 g_settings->getFloat("client_unload_unused_data_timeout"),
564 /*if(deleted_blocks.size() > 0)
565 infostream<<"Client: Unloaded "<<deleted_blocks.size()
566 <<" unused blocks"<<std::endl;*/
570 NOTE: This loop is intentionally iterated the way it is.
573 std::list<v3s16>::iterator i = deleted_blocks.begin();
574 std::list<v3s16> sendlist;
577 if(sendlist.size() == 255 || i == deleted_blocks.end())
579 if(sendlist.size() == 0)
588 u32 replysize = 2+1+6*sendlist.size();
589 SharedBuffer<u8> reply(replysize);
590 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
591 reply[2] = sendlist.size();
593 for(std::list<v3s16>::iterator
594 j = sendlist.begin();
595 j != sendlist.end(); ++j)
597 writeV3S16(&reply[2+1+6*k], *j);
600 m_con.Send(PEER_ID_SERVER, 2, reply, true);
602 if(i == deleted_blocks.end())
608 sendlist.push_back(*i);
618 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
620 // Control local player (0ms)
621 LocalPlayer *player = m_env.getLocalPlayer();
622 assert(player != NULL);
623 player->applyControl(dtime);
625 //TimeTaker envtimer("env step", m_device);
634 ClientEnvEvent event = m_env.getClientEvent();
635 if(event.type == CEE_NONE)
639 else if(event.type == CEE_PLAYER_DAMAGE)
641 if(m_ignore_damage_timer <= 0)
643 u8 damage = event.player_damage.amount;
645 if(event.player_damage.send_to_server)
648 // Add to ClientEvent queue
650 event.type = CE_PLAYER_DAMAGE;
651 event.player_damage.amount = damage;
652 m_client_event_queue.push_back(event);
655 else if(event.type == CEE_PLAYER_BREATH)
657 u16 breath = event.player_breath.amount;
667 float &counter = m_avg_rtt_timer;
672 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
673 // connectedAndInitialized() is true, peer exists.
674 float avg_rtt = m_con.GetPeerAvgRTT(PEER_ID_SERVER);
675 infostream<<"Client: avg_rtt="<<avg_rtt<<std::endl;
680 Send player position to server
683 float &counter = m_playerpos_send_timer;
685 if(counter >= m_recommended_send_interval)
693 Replace updated meshes
696 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
698 //TimeTaker timer("** Processing mesh update result queue");
701 /*infostream<<"Mesh update result queue size is "
702 <<m_mesh_update_thread.m_queue_out.size()
705 int num_processed_meshes = 0;
706 while(!m_mesh_update_thread.m_queue_out.empty())
708 num_processed_meshes++;
709 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_frontNoEx();
710 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
713 //JMutexAutoLock lock(block->mesh_mutex);
715 // Delete the old mesh
716 if(block->mesh != NULL)
718 // TODO: Remove hardware buffers of meshbuffers of block->mesh
723 // Replace with the new mesh
724 block->mesh = r.mesh;
728 if(r.ack_block_to_server)
730 /*infostream<<"Client: ACK block ("<<r.p.X<<","<<r.p.Y
731 <<","<<r.p.Z<<")"<<std::endl;*/
742 u32 replysize = 2+1+6;
743 SharedBuffer<u8> reply(replysize);
744 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
746 writeV3S16(&reply[3], r.p);
748 m_con.Send(PEER_ID_SERVER, 2, reply, true);
751 if(num_processed_meshes > 0)
752 g_profiler->graphAdd("num_processed_meshes", num_processed_meshes);
758 if (m_media_downloader && m_media_downloader->isStarted()) {
759 m_media_downloader->step(this);
760 if (m_media_downloader->isDone()) {
761 delete m_media_downloader;
762 m_media_downloader = NULL;
767 If the server didn't update the inventory in a while, revert
768 the local inventory (so the player notices the lag problem
769 and knows something is wrong).
771 if(m_inventory_from_server)
773 float interval = 10.0;
774 float count_before = floor(m_inventory_from_server_age / interval);
776 m_inventory_from_server_age += dtime;
778 float count_after = floor(m_inventory_from_server_age / interval);
780 if(count_after != count_before)
782 // Do this every <interval> seconds after TOCLIENT_INVENTORY
783 // Reset the locally changed inventory to the authoritative inventory
784 Player *player = m_env.getLocalPlayer();
785 player->inventory = *m_inventory_from_server;
786 m_inventory_updated = true;
791 Update positions of sounds attached to objects
794 for(std::map<int, u16>::iterator
795 i = m_sounds_to_objects.begin();
796 i != m_sounds_to_objects.end(); i++)
798 int client_id = i->first;
799 u16 object_id = i->second;
800 ClientActiveObject *cao = m_env.getActiveObject(object_id);
803 v3f pos = cao->getPosition();
804 m_sound->updateSoundPosition(client_id, pos);
809 Handle removed remotely initiated sounds
811 m_removed_sounds_check_timer += dtime;
812 if(m_removed_sounds_check_timer >= 2.32)
814 m_removed_sounds_check_timer = 0;
815 // Find removed sounds and clear references to them
816 std::set<s32> removed_server_ids;
817 for(std::map<s32, int>::iterator
818 i = m_sounds_server_to_client.begin();
819 i != m_sounds_server_to_client.end();)
821 s32 server_id = i->first;
822 int client_id = i->second;
824 if(!m_sound->soundExists(client_id)){
825 m_sounds_server_to_client.erase(server_id);
826 m_sounds_client_to_server.erase(client_id);
827 m_sounds_to_objects.erase(client_id);
828 removed_server_ids.insert(server_id);
832 if(removed_server_ids.size() != 0)
834 std::ostringstream os(std::ios_base::binary);
835 writeU16(os, TOSERVER_REMOVED_SOUNDS);
836 writeU16(os, removed_server_ids.size());
837 for(std::set<s32>::iterator i = removed_server_ids.begin();
838 i != removed_server_ids.end(); i++)
840 std::string s = os.str();
841 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
848 bool Client::loadMedia(const std::string &data, const std::string &filename)
850 // Silly irrlicht's const-incorrectness
851 Buffer<char> data_rw(data.c_str(), data.size());
855 const char *image_ext[] = {
856 ".png", ".jpg", ".bmp", ".tga",
857 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
860 name = removeStringEnd(filename, image_ext);
863 verbosestream<<"Client: Attempting to load image "
864 <<"file \""<<filename<<"\""<<std::endl;
866 io::IFileSystem *irrfs = m_device->getFileSystem();
867 video::IVideoDriver *vdrv = m_device->getVideoDriver();
869 // Create an irrlicht memory file
870 io::IReadFile *rfile = irrfs->createMemoryReadFile(
871 *data_rw, data_rw.getSize(), "_tempreadfile");
874 video::IImage *img = vdrv->createImageFromFile(rfile);
876 errorstream<<"Client: Cannot create image from data of "
877 <<"file \""<<filename<<"\""<<std::endl;
882 m_tsrc->insertSourceImage(filename, img);
889 const char *sound_ext[] = {
890 ".0.ogg", ".1.ogg", ".2.ogg", ".3.ogg", ".4.ogg",
891 ".5.ogg", ".6.ogg", ".7.ogg", ".8.ogg", ".9.ogg",
894 name = removeStringEnd(filename, sound_ext);
897 verbosestream<<"Client: Attempting to load sound "
898 <<"file \""<<filename<<"\""<<std::endl;
899 m_sound->loadSoundData(name, data);
903 const char *model_ext[] = {
904 ".x", ".b3d", ".md2", ".obj",
907 name = removeStringEnd(filename, model_ext);
910 verbosestream<<"Client: Storing model into memory: "
911 <<"\""<<filename<<"\""<<std::endl;
912 if(m_mesh_data.count(filename))
913 errorstream<<"Multiple models with name \""<<filename.c_str()
914 <<"\" found; replacing previous model"<<std::endl;
915 m_mesh_data[filename] = data;
919 errorstream<<"Client: Don't know how to load file \""
920 <<filename<<"\""<<std::endl;
924 // Virtual methods from con::PeerHandler
925 void Client::peerAdded(con::Peer *peer)
927 infostream<<"Client::peerAdded(): peer->id="
928 <<peer->id<<std::endl;
930 void Client::deletingPeer(con::Peer *peer, bool timeout)
932 infostream<<"Client::deletingPeer(): "
933 "Server Peer is getting deleted "
934 <<"(timeout="<<timeout<<")"<<std::endl;
939 u16 number of files requested
945 void Client::request_media(const std::list<std::string> &file_requests)
947 std::ostringstream os(std::ios_base::binary);
948 writeU16(os, TOSERVER_REQUEST_MEDIA);
949 writeU16(os, file_requests.size());
951 for(std::list<std::string>::const_iterator i = file_requests.begin();
952 i != file_requests.end(); ++i) {
953 os<<serializeString(*i);
957 std::string s = os.str();
958 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
961 infostream<<"Client: Sending media request list to server ("
962 <<file_requests.size()<<" files)"<<std::endl;
965 void Client::received_media()
967 // notify server we received everything
968 std::ostringstream os(std::ios_base::binary);
969 writeU16(os, TOSERVER_RECEIVED_MEDIA);
970 std::string s = os.str();
971 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
974 infostream<<"Client: Notifying server that we received all media"
978 void Client::ReceiveAll()
980 DSTACK(__FUNCTION_NAME);
981 u32 start_ms = porting::getTimeMs();
984 // Limit time even if there would be huge amounts of data to
986 if(porting::getTimeMs() > start_ms + 100)
991 g_profiler->graphAdd("client_received_packets", 1);
993 catch(con::NoIncomingDataException &e)
997 catch(con::InvalidIncomingDataException &e)
999 infostream<<"Client::ReceiveAll(): "
1000 "InvalidIncomingDataException: what()="
1001 <<e.what()<<std::endl;
1006 void Client::Receive()
1008 DSTACK(__FUNCTION_NAME);
1009 SharedBuffer<u8> data;
1013 //TimeTaker t1("con mutex and receive", m_device);
1014 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1015 datasize = m_con.Receive(sender_peer_id, data);
1017 //TimeTaker t1("ProcessData", m_device);
1018 ProcessData(*data, datasize, sender_peer_id);
1022 sender_peer_id given to this shall be quaranteed to be a valid peer
1024 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
1026 DSTACK(__FUNCTION_NAME);
1028 // Ignore packets that don't even fit a command
1031 m_packetcounter.add(60000);
1035 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
1037 //infostream<<"Client: received command="<<command<<std::endl;
1038 m_packetcounter.add((u16)command);
1041 If this check is removed, be sure to change the queue
1042 system to know the ids
1044 if(sender_peer_id != PEER_ID_SERVER)
1046 infostream<<"Client::ProcessData(): Discarding data not "
1047 "coming from server: peer_id="<<sender_peer_id
1052 u8 ser_version = m_server_ser_ver;
1054 //infostream<<"Client received command="<<(int)command<<std::endl;
1056 if(command == TOCLIENT_INIT)
1061 u8 deployed = data[2];
1063 infostream<<"Client: TOCLIENT_INIT received with "
1064 "deployed="<<((int)deployed&0xff)<<std::endl;
1066 if(!ser_ver_supported(deployed))
1068 infostream<<"Client: TOCLIENT_INIT: Server sent "
1069 <<"unsupported ser_fmt_ver"<<std::endl;
1073 m_server_ser_ver = deployed;
1075 // Get player position
1076 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
1077 if(datasize >= 2+1+6)
1078 playerpos_s16 = readV3S16(&data[2+1]);
1079 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
1082 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1084 // Set player position
1085 Player *player = m_env.getLocalPlayer();
1086 assert(player != NULL);
1087 player->setPosition(playerpos_f);
1090 if(datasize >= 2+1+6+8)
1093 m_map_seed = readU64(&data[2+1+6]);
1094 infostream<<"Client: received map seed: "<<m_map_seed<<std::endl;
1097 if(datasize >= 2+1+6+8+4)
1100 m_recommended_send_interval = readF1000(&data[2+1+6+8]);
1101 infostream<<"Client: received recommended send interval "
1102 <<m_recommended_send_interval<<std::endl;
1107 SharedBuffer<u8> reply(replysize);
1108 writeU16(&reply[0], TOSERVER_INIT2);
1110 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1115 if(command == TOCLIENT_ACCESS_DENIED)
1117 // The server didn't like our password. Note, this needs
1118 // to be processed even if the serialisation format has
1119 // not been agreed yet, the same as TOCLIENT_INIT.
1120 m_access_denied = true;
1121 m_access_denied_reason = L"Unknown";
1124 std::string datastring((char*)&data[2], datasize-2);
1125 std::istringstream is(datastring, std::ios_base::binary);
1126 m_access_denied_reason = deSerializeWideString(is);
1131 if(ser_version == SER_FMT_VER_INVALID)
1133 infostream<<"Client: Server serialization"
1134 " format invalid or not initialized."
1135 " Skipping incoming command="<<command<<std::endl;
1139 // Just here to avoid putting the two if's together when
1140 // making some copypasta
1143 if(command == TOCLIENT_REMOVENODE)
1148 p.X = readS16(&data[2]);
1149 p.Y = readS16(&data[4]);
1150 p.Z = readS16(&data[6]);
1152 //TimeTaker t1("TOCLIENT_REMOVENODE");
1156 else if(command == TOCLIENT_ADDNODE)
1158 if(datasize < 8 + MapNode::serializedLength(ser_version))
1162 p.X = readS16(&data[2]);
1163 p.Y = readS16(&data[4]);
1164 p.Z = readS16(&data[6]);
1166 //TimeTaker t1("TOCLIENT_ADDNODE");
1169 n.deSerialize(&data[8], ser_version);
1171 bool remove_metadata = true;
1172 u32 index = 8 + MapNode::serializedLength(ser_version);
1173 if ((datasize >= index+1) && data[index]){
1174 remove_metadata = false;
1177 addNode(p, n, remove_metadata);
1179 else if(command == TOCLIENT_BLOCKDATA)
1181 // Ignore too small packet
1186 p.X = readS16(&data[2]);
1187 p.Y = readS16(&data[4]);
1188 p.Z = readS16(&data[6]);
1190 /*infostream<<"Client: Thread: BLOCKDATA for ("
1191 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1192 /*infostream<<"Client: Thread: BLOCKDATA for ("
1193 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1195 std::string datastring((char*)&data[8], datasize-8);
1196 std::istringstream istr(datastring, std::ios_base::binary);
1201 v2s16 p2d(p.X, p.Z);
1202 sector = m_env.getMap().emergeSector(p2d);
1204 assert(sector->getPos() == p2d);
1206 //TimeTaker timer("MapBlock deSerialize");
1209 block = sector->getBlockNoCreateNoEx(p.Y);
1213 Update an existing block
1215 //infostream<<"Updating"<<std::endl;
1216 block->deSerialize(istr, ser_version, false);
1217 block->deSerializeNetworkSpecific(istr);
1224 //infostream<<"Creating new"<<std::endl;
1225 block = new MapBlock(&m_env.getMap(), p, this);
1226 block->deSerialize(istr, ser_version, false);
1227 block->deSerializeNetworkSpecific(istr);
1228 sector->insertBlock(block);
1242 u32 replysize = 2+1+6;
1243 SharedBuffer<u8> reply(replysize);
1244 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
1246 writeV3S16(&reply[3], p);
1248 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1252 Add it to mesh update queue and set it to be acknowledged after update.
1254 //infostream<<"Adding mesh update task for received block"<<std::endl;
1255 addUpdateMeshTaskWithEdge(p, true);
1257 else if(command == TOCLIENT_INVENTORY)
1262 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
1265 //TimeTaker t2("mutex locking", m_device);
1266 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1269 //TimeTaker t3("istringstream init", m_device);
1270 std::string datastring((char*)&data[2], datasize-2);
1271 std::istringstream is(datastring, std::ios_base::binary);
1274 //TimeTaker t4("player get", m_device);
1275 Player *player = m_env.getLocalPlayer();
1276 assert(player != NULL);
1279 //TimeTaker t1("inventory.deSerialize()", m_device);
1280 player->inventory.deSerialize(is);
1283 m_inventory_updated = true;
1285 delete m_inventory_from_server;
1286 m_inventory_from_server = new Inventory(player->inventory);
1287 m_inventory_from_server_age = 0.0;
1289 //infostream<<"Client got player inventory:"<<std::endl;
1290 //player->inventory.print(infostream);
1293 else if(command == TOCLIENT_TIME_OF_DAY)
1298 u16 time_of_day = readU16(&data[2]);
1299 time_of_day = time_of_day % 24000;
1300 //infostream<<"Client: time_of_day="<<time_of_day<<std::endl;
1301 float time_speed = 0;
1302 if(datasize >= 2 + 2 + 4){
1303 time_speed = readF1000(&data[4]);
1305 // Old message; try to approximate speed of time by ourselves
1306 float time_of_day_f = (float)time_of_day / 24000.0;
1307 float tod_diff_f = 0;
1308 if(time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
1309 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0;
1311 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
1312 m_last_time_of_day_f = time_of_day_f;
1313 float time_diff = m_time_of_day_update_timer;
1314 m_time_of_day_update_timer = 0;
1315 if(m_time_of_day_set){
1316 time_speed = 3600.0*24.0 * tod_diff_f / time_diff;
1317 infostream<<"Client: Measured time_of_day speed (old format): "
1318 <<time_speed<<" tod_diff_f="<<tod_diff_f
1319 <<" time_diff="<<time_diff<<std::endl;
1323 // Update environment
1324 m_env.setTimeOfDay(time_of_day);
1325 m_env.setTimeOfDaySpeed(time_speed);
1326 m_time_of_day_set = true;
1328 u32 dr = m_env.getDayNightRatio();
1329 verbosestream<<"Client: time_of_day="<<time_of_day
1330 <<" time_speed="<<time_speed
1331 <<" dr="<<dr<<std::endl;
1333 else if(command == TOCLIENT_CHAT_MESSAGE)
1341 std::string datastring((char*)&data[2], datasize-2);
1342 std::istringstream is(datastring, std::ios_base::binary);
1345 is.read((char*)buf, 2);
1346 u16 len = readU16(buf);
1348 std::wstring message;
1349 for(u16 i=0; i<len; i++)
1351 is.read((char*)buf, 2);
1352 message += (wchar_t)readU16(buf);
1355 /*infostream<<"Client received chat message: "
1356 <<wide_to_narrow(message)<<std::endl;*/
1358 m_chat_queue.push_back(message);
1360 else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1362 //if(g_settings->getBool("enable_experimental"))
1366 u16 count of removed objects
1367 for all removed objects {
1370 u16 count of added objects
1371 for all added objects {
1374 u32 initialization data length
1375 string initialization data
1380 // Get all data except the command number
1381 std::string datastring((char*)&data[2], datasize-2);
1382 // Throw them in an istringstream
1383 std::istringstream is(datastring, std::ios_base::binary);
1387 // Read removed objects
1389 u16 removed_count = readU16((u8*)buf);
1390 for(u16 i=0; i<removed_count; i++)
1393 u16 id = readU16((u8*)buf);
1396 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1397 m_env.removeActiveObject(id);
1401 // Read added objects
1403 u16 added_count = readU16((u8*)buf);
1404 for(u16 i=0; i<added_count; i++)
1407 u16 id = readU16((u8*)buf);
1409 u8 type = readU8((u8*)buf);
1410 std::string data = deSerializeLongString(is);
1413 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1414 m_env.addActiveObject(id, type, data);
1419 else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1421 //if(g_settings->getBool("enable_experimental"))
1433 // Get all data except the command number
1434 std::string datastring((char*)&data[2], datasize-2);
1435 // Throw them in an istringstream
1436 std::istringstream is(datastring, std::ios_base::binary);
1438 while(is.eof() == false)
1442 u16 id = readU16((u8*)buf);
1446 u16 message_size = readU16((u8*)buf);
1447 std::string message;
1448 message.reserve(message_size);
1449 for(u16 i=0; i<message_size; i++)
1452 message.append(buf, 1);
1454 // Pass on to the environment
1456 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1457 m_env.processActiveObjectMessage(id, message);
1462 else if(command == TOCLIENT_MOVEMENT)
1464 std::string datastring((char*)&data[2], datasize-2);
1465 std::istringstream is(datastring, std::ios_base::binary);
1466 Player *player = m_env.getLocalPlayer();
1467 assert(player != NULL);
1469 player->movement_acceleration_default = readF1000(is) * BS;
1470 player->movement_acceleration_air = readF1000(is) * BS;
1471 player->movement_acceleration_fast = readF1000(is) * BS;
1472 player->movement_speed_walk = readF1000(is) * BS;
1473 player->movement_speed_crouch = readF1000(is) * BS;
1474 player->movement_speed_fast = readF1000(is) * BS;
1475 player->movement_speed_climb = readF1000(is) * BS;
1476 player->movement_speed_jump = readF1000(is) * BS;
1477 player->movement_liquid_fluidity = readF1000(is) * BS;
1478 player->movement_liquid_fluidity_smooth = readF1000(is) * BS;
1479 player->movement_liquid_sink = readF1000(is) * BS;
1480 player->movement_gravity = readF1000(is) * BS;
1482 else if(command == TOCLIENT_HP)
1484 std::string datastring((char*)&data[2], datasize-2);
1485 std::istringstream is(datastring, std::ios_base::binary);
1486 Player *player = m_env.getLocalPlayer();
1487 assert(player != NULL);
1488 u8 oldhp = player->hp;
1494 // Add to ClientEvent queue
1496 event.type = CE_PLAYER_DAMAGE;
1497 event.player_damage.amount = oldhp - hp;
1498 m_client_event_queue.push_back(event);
1501 else if(command == TOCLIENT_BREATH)
1503 std::string datastring((char*)&data[2], datasize-2);
1504 std::istringstream is(datastring, std::ios_base::binary);
1505 Player *player = m_env.getLocalPlayer();
1506 assert(player != NULL);
1507 u16 breath = readU16(is);
1508 player->setBreath(breath) ;
1510 else if(command == TOCLIENT_MOVE_PLAYER)
1512 std::string datastring((char*)&data[2], datasize-2);
1513 std::istringstream is(datastring, std::ios_base::binary);
1514 Player *player = m_env.getLocalPlayer();
1515 assert(player != NULL);
1516 v3f pos = readV3F1000(is);
1517 f32 pitch = readF1000(is);
1518 f32 yaw = readF1000(is);
1519 player->setPosition(pos);
1520 /*player->setPitch(pitch);
1521 player->setYaw(yaw);*/
1523 infostream<<"Client got TOCLIENT_MOVE_PLAYER"
1524 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1530 Add to ClientEvent queue.
1531 This has to be sent to the main program because otherwise
1532 it would just force the pitch and yaw values to whatever
1533 the camera points to.
1536 event.type = CE_PLAYER_FORCE_MOVE;
1537 event.player_force_move.pitch = pitch;
1538 event.player_force_move.yaw = yaw;
1539 m_client_event_queue.push_back(event);
1541 // Ignore damage for a few seconds, so that the player doesn't
1542 // get damage from falling on ground
1543 m_ignore_damage_timer = 3.0;
1545 else if(command == TOCLIENT_PLAYERITEM)
1547 infostream<<"Client: WARNING: Ignoring TOCLIENT_PLAYERITEM"<<std::endl;
1549 else if(command == TOCLIENT_DEATHSCREEN)
1551 std::string datastring((char*)&data[2], datasize-2);
1552 std::istringstream is(datastring, std::ios_base::binary);
1554 bool set_camera_point_target = readU8(is);
1555 v3f camera_point_target = readV3F1000(is);
1558 event.type = CE_DEATHSCREEN;
1559 event.deathscreen.set_camera_point_target = set_camera_point_target;
1560 event.deathscreen.camera_point_target_x = camera_point_target.X;
1561 event.deathscreen.camera_point_target_y = camera_point_target.Y;
1562 event.deathscreen.camera_point_target_z = camera_point_target.Z;
1563 m_client_event_queue.push_back(event);
1565 else if(command == TOCLIENT_ANNOUNCE_MEDIA)
1567 std::string datastring((char*)&data[2], datasize-2);
1568 std::istringstream is(datastring, std::ios_base::binary);
1570 int num_files = readU16(is);
1572 infostream<<"Client: Received media announcement: packet size: "
1573 <<datasize<<std::endl;
1575 if (m_media_downloader == NULL ||
1576 m_media_downloader->isStarted()) {
1577 const char *problem = m_media_downloader ?
1578 "we already saw another announcement" :
1579 "all media has been received already";
1580 errorstream<<"Client: Received media announcement but "
1582 <<" files="<<num_files
1583 <<" size="<<datasize<<std::endl;
1587 // Mesh update thread must be stopped while
1588 // updating content definitions
1589 assert(!m_mesh_update_thread.IsRunning());
1591 for(int i=0; i<num_files; i++)
1593 std::string name = deSerializeString(is);
1594 std::string sha1_base64 = deSerializeString(is);
1595 std::string sha1_raw = base64_decode(sha1_base64);
1596 m_media_downloader->addFile(name, sha1_raw);
1599 std::vector<std::string> remote_media;
1601 Strfnd sf(deSerializeString(is));
1602 while(!sf.atend()) {
1603 std::string baseurl = trim(sf.next(","));
1605 m_media_downloader->addRemoteServer(baseurl);
1608 catch(SerializationError) {
1609 // not supported by server or turned off
1612 m_media_downloader->step(this);
1613 if (m_media_downloader->isDone()) {
1614 // might be done already if all media is in the cache
1615 delete m_media_downloader;
1616 m_media_downloader = NULL;
1619 else if(command == TOCLIENT_MEDIA)
1621 std::string datastring((char*)&data[2], datasize-2);
1622 std::istringstream is(datastring, std::ios_base::binary);
1626 u16 total number of file bunches
1627 u16 index of this bunch
1628 u32 number of files in this bunch
1636 int num_bunches = readU16(is);
1637 int bunch_i = readU16(is);
1638 u32 num_files = readU32(is);
1639 infostream<<"Client: Received files: bunch "<<bunch_i<<"/"
1640 <<num_bunches<<" files="<<num_files
1641 <<" size="<<datasize<<std::endl;
1646 if (m_media_downloader == NULL ||
1647 !m_media_downloader->isStarted()) {
1648 const char *problem = m_media_downloader ?
1649 "media has not been requested" :
1650 "all media has been received already";
1651 errorstream<<"Client: Received media but "
1653 <<" bunch "<<bunch_i<<"/"<<num_bunches
1654 <<" files="<<num_files
1655 <<" size="<<datasize<<std::endl;
1659 // Mesh update thread must be stopped while
1660 // updating content definitions
1661 assert(!m_mesh_update_thread.IsRunning());
1663 for(u32 i=0; i<num_files; i++){
1664 std::string name = deSerializeString(is);
1665 std::string data = deSerializeLongString(is);
1666 m_media_downloader->conventionalTransferDone(
1670 if (m_media_downloader->isDone()) {
1671 delete m_media_downloader;
1672 m_media_downloader = NULL;
1675 else if(command == TOCLIENT_TOOLDEF)
1677 infostream<<"Client: WARNING: Ignoring TOCLIENT_TOOLDEF"<<std::endl;
1679 else if(command == TOCLIENT_NODEDEF)
1681 infostream<<"Client: Received node definitions: packet size: "
1682 <<datasize<<std::endl;
1684 // Mesh update thread must be stopped while
1685 // updating content definitions
1686 assert(!m_mesh_update_thread.IsRunning());
1688 // Decompress node definitions
1689 std::string datastring((char*)&data[2], datasize-2);
1690 std::istringstream is(datastring, std::ios_base::binary);
1691 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1692 std::ostringstream tmp_os;
1693 decompressZlib(tmp_is, tmp_os);
1695 // Deserialize node definitions
1696 std::istringstream tmp_is2(tmp_os.str());
1697 m_nodedef->deSerialize(tmp_is2);
1698 m_nodedef_received = true;
1700 else if(command == TOCLIENT_CRAFTITEMDEF)
1702 infostream<<"Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF"<<std::endl;
1704 else if(command == TOCLIENT_ITEMDEF)
1706 infostream<<"Client: Received item definitions: packet size: "
1707 <<datasize<<std::endl;
1709 // Mesh update thread must be stopped while
1710 // updating content definitions
1711 assert(!m_mesh_update_thread.IsRunning());
1713 // Decompress item definitions
1714 std::string datastring((char*)&data[2], datasize-2);
1715 std::istringstream is(datastring, std::ios_base::binary);
1716 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1717 std::ostringstream tmp_os;
1718 decompressZlib(tmp_is, tmp_os);
1720 // Deserialize node definitions
1721 std::istringstream tmp_is2(tmp_os.str());
1722 m_itemdef->deSerialize(tmp_is2);
1723 m_itemdef_received = true;
1725 else if(command == TOCLIENT_PLAY_SOUND)
1727 std::string datastring((char*)&data[2], datasize-2);
1728 std::istringstream is(datastring, std::ios_base::binary);
1730 s32 server_id = readS32(is);
1731 std::string name = deSerializeString(is);
1732 float gain = readF1000(is);
1733 int type = readU8(is); // 0=local, 1=positional, 2=object
1734 v3f pos = readV3F1000(is);
1735 u16 object_id = readU16(is);
1736 bool loop = readU8(is);
1741 client_id = m_sound->playSound(name, loop, gain);
1743 case 1: // positional
1744 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1747 ClientActiveObject *cao = m_env.getActiveObject(object_id);
1749 pos = cao->getPosition();
1750 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1751 // TODO: Set up sound to move with object
1756 if(client_id != -1){
1757 m_sounds_server_to_client[server_id] = client_id;
1758 m_sounds_client_to_server[client_id] = server_id;
1760 m_sounds_to_objects[client_id] = object_id;
1763 else if(command == TOCLIENT_STOP_SOUND)
1765 std::string datastring((char*)&data[2], datasize-2);
1766 std::istringstream is(datastring, std::ios_base::binary);
1768 s32 server_id = readS32(is);
1769 std::map<s32, int>::iterator i =
1770 m_sounds_server_to_client.find(server_id);
1771 if(i != m_sounds_server_to_client.end()){
1772 int client_id = i->second;
1773 m_sound->stopSound(client_id);
1776 else if(command == TOCLIENT_PRIVILEGES)
1778 std::string datastring((char*)&data[2], datasize-2);
1779 std::istringstream is(datastring, std::ios_base::binary);
1781 m_privileges.clear();
1782 infostream<<"Client: Privileges updated: ";
1783 u16 num_privileges = readU16(is);
1784 for(u16 i=0; i<num_privileges; i++){
1785 std::string priv = deSerializeString(is);
1786 m_privileges.insert(priv);
1787 infostream<<priv<<" ";
1789 infostream<<std::endl;
1791 else if(command == TOCLIENT_INVENTORY_FORMSPEC)
1793 std::string datastring((char*)&data[2], datasize-2);
1794 std::istringstream is(datastring, std::ios_base::binary);
1796 // Store formspec in LocalPlayer
1797 Player *player = m_env.getLocalPlayer();
1798 assert(player != NULL);
1799 player->inventory_formspec = deSerializeLongString(is);
1801 else if(command == TOCLIENT_DETACHED_INVENTORY)
1803 std::string datastring((char*)&data[2], datasize-2);
1804 std::istringstream is(datastring, std::ios_base::binary);
1806 std::string name = deSerializeString(is);
1808 infostream<<"Client: Detached inventory update: \""<<name<<"\""<<std::endl;
1810 Inventory *inv = NULL;
1811 if(m_detached_inventories.count(name) > 0)
1812 inv = m_detached_inventories[name];
1814 inv = new Inventory(m_itemdef);
1815 m_detached_inventories[name] = inv;
1817 inv->deSerialize(is);
1819 else if(command == TOCLIENT_SHOW_FORMSPEC)
1821 std::string datastring((char*)&data[2], datasize-2);
1822 std::istringstream is(datastring, std::ios_base::binary);
1824 std::string formspec = deSerializeLongString(is);
1825 std::string formname = deSerializeString(is);
1828 event.type = CE_SHOW_FORMSPEC;
1829 // pointer is required as event is a struct only!
1830 // adding a std:string to a struct isn't possible
1831 event.show_formspec.formspec = new std::string(formspec);
1832 event.show_formspec.formname = new std::string(formname);
1833 m_client_event_queue.push_back(event);
1835 else if(command == TOCLIENT_SPAWN_PARTICLE)
1837 std::string datastring((char*)&data[2], datasize-2);
1838 std::istringstream is(datastring, std::ios_base::binary);
1840 v3f pos = readV3F1000(is);
1841 v3f vel = readV3F1000(is);
1842 v3f acc = readV3F1000(is);
1843 float expirationtime = readF1000(is);
1844 float size = readF1000(is);
1845 bool collisiondetection = readU8(is);
1846 std::string texture = deSerializeLongString(is);
1847 bool vertical = false;
1849 vertical = readU8(is);
1853 event.type = CE_SPAWN_PARTICLE;
1854 event.spawn_particle.pos = new v3f (pos);
1855 event.spawn_particle.vel = new v3f (vel);
1856 event.spawn_particle.acc = new v3f (acc);
1858 event.spawn_particle.expirationtime = expirationtime;
1859 event.spawn_particle.size = size;
1860 event.spawn_particle.collisiondetection =
1862 event.spawn_particle.vertical = vertical;
1863 event.spawn_particle.texture = new std::string(texture);
1865 m_client_event_queue.push_back(event);
1867 else if(command == TOCLIENT_ADD_PARTICLESPAWNER)
1869 std::string datastring((char*)&data[2], datasize-2);
1870 std::istringstream is(datastring, std::ios_base::binary);
1872 u16 amount = readU16(is);
1873 float spawntime = readF1000(is);
1874 v3f minpos = readV3F1000(is);
1875 v3f maxpos = readV3F1000(is);
1876 v3f minvel = readV3F1000(is);
1877 v3f maxvel = readV3F1000(is);
1878 v3f minacc = readV3F1000(is);
1879 v3f maxacc = readV3F1000(is);
1880 float minexptime = readF1000(is);
1881 float maxexptime = readF1000(is);
1882 float minsize = readF1000(is);
1883 float maxsize = readF1000(is);
1884 bool collisiondetection = readU8(is);
1885 std::string texture = deSerializeLongString(is);
1886 u32 id = readU32(is);
1887 bool vertical = false;
1889 vertical = readU8(is);
1893 event.type = CE_ADD_PARTICLESPAWNER;
1894 event.add_particlespawner.amount = amount;
1895 event.add_particlespawner.spawntime = spawntime;
1897 event.add_particlespawner.minpos = new v3f (minpos);
1898 event.add_particlespawner.maxpos = new v3f (maxpos);
1899 event.add_particlespawner.minvel = new v3f (minvel);
1900 event.add_particlespawner.maxvel = new v3f (maxvel);
1901 event.add_particlespawner.minacc = new v3f (minacc);
1902 event.add_particlespawner.maxacc = new v3f (maxacc);
1904 event.add_particlespawner.minexptime = minexptime;
1905 event.add_particlespawner.maxexptime = maxexptime;
1906 event.add_particlespawner.minsize = minsize;
1907 event.add_particlespawner.maxsize = maxsize;
1908 event.add_particlespawner.collisiondetection = collisiondetection;
1909 event.add_particlespawner.vertical = vertical;
1910 event.add_particlespawner.texture = new std::string(texture);
1911 event.add_particlespawner.id = id;
1913 m_client_event_queue.push_back(event);
1915 else if(command == TOCLIENT_DELETE_PARTICLESPAWNER)
1917 std::string datastring((char*)&data[2], datasize-2);
1918 std::istringstream is(datastring, std::ios_base::binary);
1920 u32 id = readU16(is);
1923 event.type = CE_DELETE_PARTICLESPAWNER;
1924 event.delete_particlespawner.id = id;
1926 m_client_event_queue.push_back(event);
1928 else if(command == TOCLIENT_HUDADD)
1930 std::string datastring((char *)&data[2], datasize - 2);
1931 std::istringstream is(datastring, std::ios_base::binary);
1933 u32 id = readU32(is);
1934 u8 type = readU8(is);
1935 v2f pos = readV2F1000(is);
1936 std::string name = deSerializeString(is);
1937 v2f scale = readV2F1000(is);
1938 std::string text = deSerializeString(is);
1939 u32 number = readU32(is);
1940 u32 item = readU32(is);
1941 u32 dir = readU32(is);
1942 v2f align = readV2F1000(is);
1943 v2f offset = readV2F1000(is);
1946 event.type = CE_HUDADD;
1947 event.hudadd.id = id;
1948 event.hudadd.type = type;
1949 event.hudadd.pos = new v2f(pos);
1950 event.hudadd.name = new std::string(name);
1951 event.hudadd.scale = new v2f(scale);
1952 event.hudadd.text = new std::string(text);
1953 event.hudadd.number = number;
1954 event.hudadd.item = item;
1955 event.hudadd.dir = dir;
1956 event.hudadd.align = new v2f(align);
1957 event.hudadd.offset = new v2f(offset);
1958 m_client_event_queue.push_back(event);
1960 else if(command == TOCLIENT_HUDRM)
1962 std::string datastring((char *)&data[2], datasize - 2);
1963 std::istringstream is(datastring, std::ios_base::binary);
1965 u32 id = readU32(is);
1968 event.type = CE_HUDRM;
1969 event.hudrm.id = id;
1970 m_client_event_queue.push_back(event);
1972 else if(command == TOCLIENT_HUDCHANGE)
1978 std::string datastring((char *)&data[2], datasize - 2);
1979 std::istringstream is(datastring, std::ios_base::binary);
1981 u32 id = readU32(is);
1982 u8 stat = (HudElementStat)readU8(is);
1984 if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
1985 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
1986 v2fdata = readV2F1000(is);
1987 else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
1988 sdata = deSerializeString(is);
1990 intdata = readU32(is);
1993 event.type = CE_HUDCHANGE;
1994 event.hudchange.id = id;
1995 event.hudchange.stat = (HudElementStat)stat;
1996 event.hudchange.v2fdata = new v2f(v2fdata);
1997 event.hudchange.sdata = new std::string(sdata);
1998 event.hudchange.data = intdata;
1999 m_client_event_queue.push_back(event);
2001 else if(command == TOCLIENT_HUD_SET_FLAGS)
2003 std::string datastring((char *)&data[2], datasize - 2);
2004 std::istringstream is(datastring, std::ios_base::binary);
2006 Player *player = m_env.getLocalPlayer();
2007 assert(player != NULL);
2009 u32 flags = readU32(is);
2010 u32 mask = readU32(is);
2012 player->hud_flags &= ~mask;
2013 player->hud_flags |= flags;
2015 else if(command == TOCLIENT_HUD_SET_PARAM)
2017 std::string datastring((char *)&data[2], datasize - 2);
2018 std::istringstream is(datastring, std::ios_base::binary);
2020 Player *player = m_env.getLocalPlayer();
2021 assert(player != NULL);
2023 u16 param = readU16(is);
2024 std::string value = deSerializeString(is);
2026 if(param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4){
2027 s32 hotbar_itemcount = readS32((u8*) value.c_str());
2028 if(hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
2029 player->hud_hotbar_itemcount = hotbar_itemcount;
2030 } else if (param == HUD_PARAM_HOTBAR_IMAGE) {
2031 ((LocalPlayer *) player)->hotbar_image = value;
2032 } else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
2033 ((LocalPlayer *) player)->hotbar_selected_image = value;
2038 infostream<<"Client: Ignoring unknown command "
2039 <<command<<std::endl;
2043 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
2045 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2046 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
2049 void Client::interact(u8 action, const PointedThing& pointed)
2051 if(connectedAndInitialized() == false){
2052 infostream<<"Client::interact() "
2053 "cancelled (not connected)"
2058 std::ostringstream os(std::ios_base::binary);
2064 [5] u32 length of the next item
2065 [9] serialized PointedThing
2067 0: start digging (from undersurface) or use
2068 1: stop digging (all parameters ignored)
2069 2: digging completed
2070 3: place block or item (to abovesurface)
2073 writeU16(os, TOSERVER_INTERACT);
2074 writeU8(os, action);
2075 writeU16(os, getPlayerItem());
2076 std::ostringstream tmp_os(std::ios::binary);
2077 pointed.serialize(tmp_os);
2078 os<<serializeLongString(tmp_os.str());
2080 std::string s = os.str();
2081 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2084 Send(0, data, true);
2087 void Client::sendNodemetaFields(v3s16 p, const std::string &formname,
2088 const std::map<std::string, std::string> &fields)
2090 std::ostringstream os(std::ios_base::binary);
2092 writeU16(os, TOSERVER_NODEMETA_FIELDS);
2094 os<<serializeString(formname);
2095 writeU16(os, fields.size());
2096 for(std::map<std::string, std::string>::const_iterator
2097 i = fields.begin(); i != fields.end(); i++){
2098 const std::string &name = i->first;
2099 const std::string &value = i->second;
2100 os<<serializeString(name);
2101 os<<serializeLongString(value);
2105 std::string s = os.str();
2106 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2108 Send(0, data, true);
2111 void Client::sendInventoryFields(const std::string &formname,
2112 const std::map<std::string, std::string> &fields)
2114 std::ostringstream os(std::ios_base::binary);
2116 writeU16(os, TOSERVER_INVENTORY_FIELDS);
2117 os<<serializeString(formname);
2118 writeU16(os, fields.size());
2119 for(std::map<std::string, std::string>::const_iterator
2120 i = fields.begin(); i != fields.end(); i++){
2121 const std::string &name = i->first;
2122 const std::string &value = i->second;
2123 os<<serializeString(name);
2124 os<<serializeLongString(value);
2128 std::string s = os.str();
2129 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2131 Send(0, data, true);
2134 void Client::sendInventoryAction(InventoryAction *a)
2136 std::ostringstream os(std::ios_base::binary);
2140 writeU16(buf, TOSERVER_INVENTORY_ACTION);
2141 os.write((char*)buf, 2);
2146 std::string s = os.str();
2147 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2149 Send(0, data, true);
2152 void Client::sendChatMessage(const std::wstring &message)
2154 std::ostringstream os(std::ios_base::binary);
2158 writeU16(buf, TOSERVER_CHAT_MESSAGE);
2159 os.write((char*)buf, 2);
2162 writeU16(buf, message.size());
2163 os.write((char*)buf, 2);
2166 for(u32 i=0; i<message.size(); i++)
2170 os.write((char*)buf, 2);
2174 std::string s = os.str();
2175 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2177 Send(0, data, true);
2180 void Client::sendChangePassword(const std::wstring oldpassword,
2181 const std::wstring newpassword)
2183 Player *player = m_env.getLocalPlayer();
2187 std::string playername = player->getName();
2188 std::string oldpwd = translatePassword(playername, oldpassword);
2189 std::string newpwd = translatePassword(playername, newpassword);
2191 std::ostringstream os(std::ios_base::binary);
2192 u8 buf[2+PASSWORD_SIZE*2];
2194 [0] u16 TOSERVER_PASSWORD
2195 [2] u8[28] old password
2196 [30] u8[28] new password
2199 writeU16(buf, TOSERVER_PASSWORD);
2200 for(u32 i=0;i<PASSWORD_SIZE-1;i++)
2202 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
2203 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
2205 buf[2+PASSWORD_SIZE-1] = 0;
2206 buf[30+PASSWORD_SIZE-1] = 0;
2207 os.write((char*)buf, 2+PASSWORD_SIZE*2);
2210 std::string s = os.str();
2211 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2213 Send(0, data, true);
2217 void Client::sendDamage(u8 damage)
2219 DSTACK(__FUNCTION_NAME);
2220 std::ostringstream os(std::ios_base::binary);
2222 writeU16(os, TOSERVER_DAMAGE);
2223 writeU8(os, damage);
2226 std::string s = os.str();
2227 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2229 Send(0, data, true);
2232 void Client::sendBreath(u16 breath)
2234 DSTACK(__FUNCTION_NAME);
2235 std::ostringstream os(std::ios_base::binary);
2237 writeU16(os, TOSERVER_BREATH);
2238 writeU16(os, breath);
2240 std::string s = os.str();
2241 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2243 Send(0, data, true);
2246 void Client::sendRespawn()
2248 DSTACK(__FUNCTION_NAME);
2249 std::ostringstream os(std::ios_base::binary);
2251 writeU16(os, TOSERVER_RESPAWN);
2254 std::string s = os.str();
2255 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2257 Send(0, data, true);
2260 void Client::sendPlayerPos()
2262 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2264 LocalPlayer *myplayer = m_env.getLocalPlayer();
2265 if(myplayer == NULL)
2268 // Save bandwidth by only updating position when something changed
2269 if(myplayer->last_position == myplayer->getPosition() &&
2270 myplayer->last_speed == myplayer->getSpeed() &&
2271 myplayer->last_pitch == myplayer->getPitch() &&
2272 myplayer->last_yaw == myplayer->getYaw() &&
2273 myplayer->last_keyPressed == myplayer->keyPressed)
2276 myplayer->last_position = myplayer->getPosition();
2277 myplayer->last_speed = myplayer->getSpeed();
2278 myplayer->last_pitch = myplayer->getPitch();
2279 myplayer->last_yaw = myplayer->getYaw();
2280 myplayer->last_keyPressed = myplayer->keyPressed;
2284 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2285 our_peer_id = m_con.GetPeerID();
2288 // Set peer id if not set already
2289 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2290 myplayer->peer_id = our_peer_id;
2291 // Check that an existing peer_id is the same as the connection's
2292 assert(myplayer->peer_id == our_peer_id);
2294 v3f pf = myplayer->getPosition();
2295 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
2296 v3f sf = myplayer->getSpeed();
2297 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
2298 s32 pitch = myplayer->getPitch() * 100;
2299 s32 yaw = myplayer->getYaw() * 100;
2300 u32 keyPressed=myplayer->keyPressed;
2304 [2] v3s32 position*100
2305 [2+12] v3s32 speed*100
2306 [2+12+12] s32 pitch*100
2307 [2+12+12+4] s32 yaw*100
2308 [2+12+12+4+4] u32 keyPressed
2310 SharedBuffer<u8> data(2+12+12+4+4+4);
2311 writeU16(&data[0], TOSERVER_PLAYERPOS);
2312 writeV3S32(&data[2], position);
2313 writeV3S32(&data[2+12], speed);
2314 writeS32(&data[2+12+12], pitch);
2315 writeS32(&data[2+12+12+4], yaw);
2316 writeU32(&data[2+12+12+4+4], keyPressed);
2317 // Send as unreliable
2318 Send(0, data, false);
2321 void Client::sendPlayerItem(u16 item)
2323 Player *myplayer = m_env.getLocalPlayer();
2324 if(myplayer == NULL)
2327 u16 our_peer_id = m_con.GetPeerID();
2329 // Set peer id if not set already
2330 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2331 myplayer->peer_id = our_peer_id;
2332 // Check that an existing peer_id is the same as the connection's
2333 assert(myplayer->peer_id == our_peer_id);
2335 SharedBuffer<u8> data(2+2);
2336 writeU16(&data[0], TOSERVER_PLAYERITEM);
2337 writeU16(&data[2], item);
2340 Send(0, data, true);
2343 void Client::removeNode(v3s16 p)
2345 std::map<v3s16, MapBlock*> modified_blocks;
2349 //TimeTaker t("removeNodeAndUpdate", m_device);
2350 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
2352 catch(InvalidPositionException &e)
2356 // add urgent task to update the modified node
2357 addUpdateMeshTaskForNode(p, false, true);
2359 for(std::map<v3s16, MapBlock * >::iterator
2360 i = modified_blocks.begin();
2361 i != modified_blocks.end(); ++i)
2363 addUpdateMeshTaskWithEdge(i->first);
2367 void Client::addNode(v3s16 p, MapNode n, bool remove_metadata)
2369 TimeTaker timer1("Client::addNode()");
2371 std::map<v3s16, MapBlock*> modified_blocks;
2375 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
2376 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
2378 catch(InvalidPositionException &e)
2381 for(std::map<v3s16, MapBlock * >::iterator
2382 i = modified_blocks.begin();
2383 i != modified_blocks.end(); ++i)
2385 addUpdateMeshTaskWithEdge(i->first);
2389 void Client::setPlayerControl(PlayerControl &control)
2391 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2392 LocalPlayer *player = m_env.getLocalPlayer();
2393 assert(player != NULL);
2394 player->control = control;
2397 void Client::selectPlayerItem(u16 item)
2399 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2400 m_playeritem = item;
2401 m_inventory_updated = true;
2402 sendPlayerItem(item);
2405 // Returns true if the inventory of the local player has been
2406 // updated from the server. If it is true, it is set to false.
2407 bool Client::getLocalInventoryUpdated()
2409 // m_inventory_updated is behind envlock
2410 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2411 bool updated = m_inventory_updated;
2412 m_inventory_updated = false;
2416 // Copies the inventory of the local player to parameter
2417 void Client::getLocalInventory(Inventory &dst)
2419 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2420 Player *player = m_env.getLocalPlayer();
2421 assert(player != NULL);
2422 dst = player->inventory;
2425 Inventory* Client::getInventory(const InventoryLocation &loc)
2428 case InventoryLocation::UNDEFINED:
2431 case InventoryLocation::CURRENT_PLAYER:
2433 Player *player = m_env.getLocalPlayer();
2434 assert(player != NULL);
2435 return &player->inventory;
2438 case InventoryLocation::PLAYER:
2440 Player *player = m_env.getPlayer(loc.name.c_str());
2443 return &player->inventory;
2446 case InventoryLocation::NODEMETA:
2448 NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p);
2451 return meta->getInventory();
2454 case InventoryLocation::DETACHED:
2456 if(m_detached_inventories.count(loc.name) == 0)
2458 return m_detached_inventories[loc.name];
2466 void Client::inventoryAction(InventoryAction *a)
2469 Send it to the server
2471 sendInventoryAction(a);
2474 Predict some local inventory changes
2476 a->clientApply(this, this);
2482 ClientActiveObject * Client::getSelectedActiveObject(
2484 v3f from_pos_f_on_map,
2485 core::line3d<f32> shootline_on_map
2488 std::vector<DistanceSortedActiveObject> objects;
2490 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
2492 //infostream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
2495 // After this, the closest object is the first in the array.
2496 std::sort(objects.begin(), objects.end());
2498 for(u32 i=0; i<objects.size(); i++)
2500 ClientActiveObject *obj = objects[i].obj;
2502 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
2503 if(selection_box == NULL)
2506 v3f pos = obj->getPosition();
2508 core::aabbox3d<f32> offsetted_box(
2509 selection_box->MinEdge + pos,
2510 selection_box->MaxEdge + pos
2513 if(offsetted_box.intersectsWithLine(shootline_on_map))
2515 //infostream<<"Returning selected object"<<std::endl;
2520 //infostream<<"No object selected; returning NULL."<<std::endl;
2524 void Client::printDebugInfo(std::ostream &os)
2526 //JMutexAutoLock lock1(m_fetchblock_mutex);
2527 /*JMutexAutoLock lock2(m_incoming_queue_mutex);
2529 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
2530 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
2531 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
2535 std::list<std::string> Client::getConnectedPlayerNames()
2537 return m_env.getPlayerNames();
2540 float Client::getAnimationTime()
2542 return m_animation_time;
2545 int Client::getCrackLevel()
2547 return m_crack_level;
2550 void Client::setCrack(int level, v3s16 pos)
2552 int old_crack_level = m_crack_level;
2553 v3s16 old_crack_pos = m_crack_pos;
2555 m_crack_level = level;
2558 if(old_crack_level >= 0 && (level < 0 || pos != old_crack_pos))
2561 addUpdateMeshTaskForNode(old_crack_pos, false, true);
2563 if(level >= 0 && (old_crack_level < 0 || pos != old_crack_pos))
2566 addUpdateMeshTaskForNode(pos, false, true);
2572 Player *player = m_env.getLocalPlayer();
2573 assert(player != NULL);
2577 u16 Client::getBreath()
2579 Player *player = m_env.getLocalPlayer();
2580 assert(player != NULL);
2581 return player->getBreath();
2584 bool Client::getChatMessage(std::wstring &message)
2586 if(m_chat_queue.size() == 0)
2588 message = m_chat_queue.pop_front();
2592 void Client::typeChatMessage(const std::wstring &message)
2594 // Discard empty line
2599 sendChatMessage(message);
2602 if (message[0] == L'/')
2604 m_chat_queue.push_back(
2605 (std::wstring)L"issued command: "+message);
2609 LocalPlayer *player = m_env.getLocalPlayer();
2610 assert(player != NULL);
2611 std::wstring name = narrow_to_wide(player->getName());
2612 m_chat_queue.push_back(
2613 (std::wstring)L"<"+name+L"> "+message);
2617 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
2619 /*infostream<<"Client::addUpdateMeshTask(): "
2620 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2621 <<" ack_to_server="<<ack_to_server
2622 <<" urgent="<<urgent
2625 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2630 Create a task to update the mesh of the block
2633 MeshMakeData *data = new MeshMakeData(this);
2636 //TimeTaker timer("data fill");
2638 // Debug: 1-6ms, avg=2ms
2640 data->setCrack(m_crack_level, m_crack_pos);
2641 data->setSmoothLighting(g_settings->getBool("smooth_lighting"));
2645 //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
2647 // Add task to queue
2648 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server, urgent);
2650 /*infostream<<"Mesh update input queue size is "
2651 <<m_mesh_update_thread.m_queue_in.size()
2655 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent)
2659 infostream<<"Client::addUpdateMeshTaskWithEdge(): "
2660 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2665 v3s16 p = blockpos + v3s16(0,0,0);
2666 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2667 addUpdateMeshTask(p, ack_to_server, urgent);
2669 catch(InvalidPositionException &e){}
2671 for (int i=0;i<6;i++)
2674 v3s16 p = blockpos + g_6dirs[i];
2675 addUpdateMeshTask(p, false, urgent);
2677 catch(InvalidPositionException &e){}
2681 void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool urgent)
2685 infostream<<"Client::addUpdateMeshTaskForNode(): "
2686 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2690 v3s16 blockpos = getNodeBlockPos(nodepos);
2691 v3s16 blockpos_relative = blockpos * MAP_BLOCKSIZE;
2694 v3s16 p = blockpos + v3s16(0,0,0);
2695 addUpdateMeshTask(p, ack_to_server, urgent);
2697 catch(InvalidPositionException &e){}
2699 if(nodepos.X == blockpos_relative.X){
2701 v3s16 p = blockpos + v3s16(-1,0,0);
2702 addUpdateMeshTask(p, false, urgent);
2704 catch(InvalidPositionException &e){}
2706 if(nodepos.Y == blockpos_relative.Y){
2708 v3s16 p = blockpos + v3s16(0,-1,0);
2709 addUpdateMeshTask(p, false, urgent);
2711 catch(InvalidPositionException &e){}
2713 if(nodepos.Z == blockpos_relative.Z){
2715 v3s16 p = blockpos + v3s16(0,0,-1);
2716 addUpdateMeshTask(p, false, urgent);
2718 catch(InvalidPositionException &e){}
2722 ClientEvent Client::getClientEvent()
2724 if(m_client_event_queue.size() == 0)
2727 event.type = CE_NONE;
2730 return m_client_event_queue.pop_front();
2733 float Client::mediaReceiveProgress()
2735 if (m_media_downloader)
2736 return m_media_downloader->getProgress();
2738 return 1.0; // downloader only exists when not yet done
2741 void draw_load_screen(const std::wstring &text,
2742 IrrlichtDevice* device, gui::IGUIFont* font,
2743 float dtime=0 ,int percent=0, bool clouds=true);
2744 void Client::afterContentReceived(IrrlichtDevice *device, gui::IGUIFont* font)
2746 infostream<<"Client::afterContentReceived() started"<<std::endl;
2747 assert(m_itemdef_received);
2748 assert(m_nodedef_received);
2749 assert(mediaReceived());
2751 // Rebuild inherited images and recreate textures
2752 infostream<<"- Rebuilding images and textures"<<std::endl;
2753 m_tsrc->rebuildImagesAndTextures();
2756 infostream<<"- Rebuilding shaders"<<std::endl;
2757 m_shsrc->rebuildShaders();
2759 // Update node aliases
2760 infostream<<"- Updating node aliases"<<std::endl;
2761 m_nodedef->updateAliases(m_itemdef);
2763 // Update node textures
2764 infostream<<"- Updating node textures"<<std::endl;
2765 m_nodedef->updateTextures(m_tsrc);
2767 // Preload item textures and meshes if configured to
2768 if(g_settings->getBool("preload_item_visuals"))
2770 verbosestream<<"Updating item textures and meshes"<<std::endl;
2771 wchar_t* text = wgettext("Item textures...");
2772 draw_load_screen(text,device,font,0,0);
2773 std::set<std::string> names = m_itemdef->getAll();
2774 size_t size = names.size();
2777 for(std::set<std::string>::const_iterator
2778 i = names.begin(); i != names.end(); ++i){
2779 // Asking for these caches the result
2780 m_itemdef->getInventoryTexture(*i, this);
2781 m_itemdef->getWieldMesh(*i, this);
2783 percent = count*100/size;
2784 if (count%50 == 0) // only update every 50 item
2785 draw_load_screen(text,device,font,0,percent);
2790 // Start mesh update thread after setting up content definitions
2791 infostream<<"- Starting mesh update thread"<<std::endl;
2792 m_mesh_update_thread.Start();
2794 infostream<<"Client::afterContentReceived() done"<<std::endl;
2797 float Client::getRTT(void)
2800 return m_con.GetPeerAvgRTT(PEER_ID_SERVER);
2801 } catch(con::PeerNotFoundException &e){
2806 // IGameDef interface
2808 IItemDefManager* Client::getItemDefManager()
2812 INodeDefManager* Client::getNodeDefManager()
2816 ICraftDefManager* Client::getCraftDefManager()
2819 //return m_craftdef;
2821 ITextureSource* Client::getTextureSource()
2825 IShaderSource* Client::getShaderSource()
2829 u16 Client::allocateUnknownNodeId(const std::string &name)
2831 errorstream<<"Client::allocateUnknownNodeId(): "
2832 <<"Client cannot allocate node IDs"<<std::endl;
2834 return CONTENT_IGNORE;
2836 ISoundManager* Client::getSoundManager()
2840 MtEventManager* Client::getEventManager()
2845 scene::IAnimatedMesh* Client::getMesh(const std::string &filename)
2847 std::map<std::string, std::string>::const_iterator i =
2848 m_mesh_data.find(filename);
2849 if(i == m_mesh_data.end()){
2850 errorstream<<"Client::getMesh(): Mesh not found: \""<<filename<<"\""
2854 const std::string &data = i->second;
2855 scene::ISceneManager *smgr = m_device->getSceneManager();
2857 // Create the mesh, remove it from cache and return it
2858 // This allows unique vertex colors and other properties for each instance
2859 Buffer<char> data_rw(data.c_str(), data.size()); // Const-incorrect Irrlicht
2860 io::IFileSystem *irrfs = m_device->getFileSystem();
2861 io::IReadFile *rfile = irrfs->createMemoryReadFile(
2862 *data_rw, data_rw.getSize(), filename.c_str());
2864 scene::IAnimatedMesh *mesh = smgr->getMesh(rfile);
2866 // NOTE: By playing with Irrlicht refcounts, maybe we could cache a bunch
2867 // of uniquely named instances and re-use them
2869 smgr->getMeshCache()->removeMesh(mesh);