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(0, 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, 1, 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, 1, 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);
1849 event.type = CE_SPAWN_PARTICLE;
1850 event.spawn_particle.pos = new v3f (pos);
1851 event.spawn_particle.vel = new v3f (vel);
1852 event.spawn_particle.acc = new v3f (acc);
1854 event.spawn_particle.expirationtime = expirationtime;
1855 event.spawn_particle.size = size;
1856 event.spawn_particle.collisiondetection =
1858 event.spawn_particle.texture = new std::string(texture);
1860 m_client_event_queue.push_back(event);
1862 else if(command == TOCLIENT_ADD_PARTICLESPAWNER)
1864 std::string datastring((char*)&data[2], datasize-2);
1865 std::istringstream is(datastring, std::ios_base::binary);
1867 u16 amount = readU16(is);
1868 float spawntime = readF1000(is);
1869 v3f minpos = readV3F1000(is);
1870 v3f maxpos = readV3F1000(is);
1871 v3f minvel = readV3F1000(is);
1872 v3f maxvel = readV3F1000(is);
1873 v3f minacc = readV3F1000(is);
1874 v3f maxacc = readV3F1000(is);
1875 float minexptime = readF1000(is);
1876 float maxexptime = readF1000(is);
1877 float minsize = readF1000(is);
1878 float maxsize = readF1000(is);
1879 bool collisiondetection = readU8(is);
1880 std::string texture = deSerializeLongString(is);
1881 u32 id = readU32(is);
1884 event.type = CE_ADD_PARTICLESPAWNER;
1885 event.add_particlespawner.amount = amount;
1886 event.add_particlespawner.spawntime = spawntime;
1888 event.add_particlespawner.minpos = new v3f (minpos);
1889 event.add_particlespawner.maxpos = new v3f (maxpos);
1890 event.add_particlespawner.minvel = new v3f (minvel);
1891 event.add_particlespawner.maxvel = new v3f (maxvel);
1892 event.add_particlespawner.minacc = new v3f (minacc);
1893 event.add_particlespawner.maxacc = new v3f (maxacc);
1895 event.add_particlespawner.minexptime = minexptime;
1896 event.add_particlespawner.maxexptime = maxexptime;
1897 event.add_particlespawner.minsize = minsize;
1898 event.add_particlespawner.maxsize = maxsize;
1899 event.add_particlespawner.collisiondetection = collisiondetection;
1900 event.add_particlespawner.texture = new std::string(texture);
1901 event.add_particlespawner.id = id;
1903 m_client_event_queue.push_back(event);
1905 else if(command == TOCLIENT_DELETE_PARTICLESPAWNER)
1907 std::string datastring((char*)&data[2], datasize-2);
1908 std::istringstream is(datastring, std::ios_base::binary);
1910 u32 id = readU16(is);
1913 event.type = CE_DELETE_PARTICLESPAWNER;
1914 event.delete_particlespawner.id = id;
1916 m_client_event_queue.push_back(event);
1918 else if(command == TOCLIENT_HUDADD)
1920 std::string datastring((char *)&data[2], datasize - 2);
1921 std::istringstream is(datastring, std::ios_base::binary);
1923 u32 id = readU32(is);
1924 u8 type = readU8(is);
1925 v2f pos = readV2F1000(is);
1926 std::string name = deSerializeString(is);
1927 v2f scale = readV2F1000(is);
1928 std::string text = deSerializeString(is);
1929 u32 number = readU32(is);
1930 u32 item = readU32(is);
1931 u32 dir = readU32(is);
1932 v2f align = readV2F1000(is);
1933 v2f offset = readV2F1000(is);
1936 event.type = CE_HUDADD;
1937 event.hudadd.id = id;
1938 event.hudadd.type = type;
1939 event.hudadd.pos = new v2f(pos);
1940 event.hudadd.name = new std::string(name);
1941 event.hudadd.scale = new v2f(scale);
1942 event.hudadd.text = new std::string(text);
1943 event.hudadd.number = number;
1944 event.hudadd.item = item;
1945 event.hudadd.dir = dir;
1946 event.hudadd.align = new v2f(align);
1947 event.hudadd.offset = new v2f(offset);
1948 m_client_event_queue.push_back(event);
1950 else if(command == TOCLIENT_HUDRM)
1952 std::string datastring((char *)&data[2], datasize - 2);
1953 std::istringstream is(datastring, std::ios_base::binary);
1955 u32 id = readU32(is);
1958 event.type = CE_HUDRM;
1959 event.hudrm.id = id;
1960 m_client_event_queue.push_back(event);
1962 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);
1980 intdata = readU32(is);
1983 event.type = CE_HUDCHANGE;
1984 event.hudchange.id = id;
1985 event.hudchange.stat = (HudElementStat)stat;
1986 event.hudchange.v2fdata = new v2f(v2fdata);
1987 event.hudchange.sdata = new std::string(sdata);
1988 event.hudchange.data = intdata;
1989 m_client_event_queue.push_back(event);
1991 else if(command == TOCLIENT_HUD_SET_FLAGS)
1993 std::string datastring((char *)&data[2], datasize - 2);
1994 std::istringstream is(datastring, std::ios_base::binary);
1996 Player *player = m_env.getLocalPlayer();
1997 assert(player != NULL);
1999 u32 flags = readU32(is);
2000 u32 mask = readU32(is);
2002 player->hud_flags &= ~mask;
2003 player->hud_flags |= flags;
2005 else if(command == TOCLIENT_HUD_SET_PARAM)
2007 std::string datastring((char *)&data[2], datasize - 2);
2008 std::istringstream is(datastring, std::ios_base::binary);
2010 Player *player = m_env.getLocalPlayer();
2011 assert(player != NULL);
2013 u16 param = readU16(is);
2014 std::string value = deSerializeString(is);
2016 if(param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4){
2017 s32 hotbar_itemcount = readS32((u8*) value.c_str());
2018 if(hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
2019 player->hud_hotbar_itemcount = hotbar_itemcount;
2020 } else if (param == HUD_PARAM_HOTBAR_IMAGE) {
2021 ((LocalPlayer *) player)->hotbar_image = value;
2022 } else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
2023 ((LocalPlayer *) player)->hotbar_selected_image = value;
2028 infostream<<"Client: Ignoring unknown command "
2029 <<command<<std::endl;
2033 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
2035 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2036 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
2039 void Client::interact(u8 action, const PointedThing& pointed)
2041 if(connectedAndInitialized() == false){
2042 infostream<<"Client::interact() "
2043 "cancelled (not connected)"
2048 std::ostringstream os(std::ios_base::binary);
2054 [5] u32 length of the next item
2055 [9] serialized PointedThing
2057 0: start digging (from undersurface) or use
2058 1: stop digging (all parameters ignored)
2059 2: digging completed
2060 3: place block or item (to abovesurface)
2063 writeU16(os, TOSERVER_INTERACT);
2064 writeU8(os, action);
2065 writeU16(os, getPlayerItem());
2066 std::ostringstream tmp_os(std::ios::binary);
2067 pointed.serialize(tmp_os);
2068 os<<serializeLongString(tmp_os.str());
2070 std::string s = os.str();
2071 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2074 Send(0, data, true);
2077 void Client::sendNodemetaFields(v3s16 p, const std::string &formname,
2078 const std::map<std::string, std::string> &fields)
2080 std::ostringstream os(std::ios_base::binary);
2082 writeU16(os, TOSERVER_NODEMETA_FIELDS);
2084 os<<serializeString(formname);
2085 writeU16(os, fields.size());
2086 for(std::map<std::string, std::string>::const_iterator
2087 i = fields.begin(); i != fields.end(); i++){
2088 const std::string &name = i->first;
2089 const std::string &value = i->second;
2090 os<<serializeString(name);
2091 os<<serializeLongString(value);
2095 std::string s = os.str();
2096 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2098 Send(0, data, true);
2101 void Client::sendInventoryFields(const std::string &formname,
2102 const std::map<std::string, std::string> &fields)
2104 std::ostringstream os(std::ios_base::binary);
2106 writeU16(os, TOSERVER_INVENTORY_FIELDS);
2107 os<<serializeString(formname);
2108 writeU16(os, fields.size());
2109 for(std::map<std::string, std::string>::const_iterator
2110 i = fields.begin(); i != fields.end(); i++){
2111 const std::string &name = i->first;
2112 const std::string &value = i->second;
2113 os<<serializeString(name);
2114 os<<serializeLongString(value);
2118 std::string s = os.str();
2119 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2121 Send(0, data, true);
2124 void Client::sendInventoryAction(InventoryAction *a)
2126 std::ostringstream os(std::ios_base::binary);
2130 writeU16(buf, TOSERVER_INVENTORY_ACTION);
2131 os.write((char*)buf, 2);
2136 std::string s = os.str();
2137 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2139 Send(0, data, true);
2142 void Client::sendChatMessage(const std::wstring &message)
2144 std::ostringstream os(std::ios_base::binary);
2148 writeU16(buf, TOSERVER_CHAT_MESSAGE);
2149 os.write((char*)buf, 2);
2152 writeU16(buf, message.size());
2153 os.write((char*)buf, 2);
2156 for(u32 i=0; i<message.size(); i++)
2160 os.write((char*)buf, 2);
2164 std::string s = os.str();
2165 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2167 Send(0, data, true);
2170 void Client::sendChangePassword(const std::wstring oldpassword,
2171 const std::wstring newpassword)
2173 Player *player = m_env.getLocalPlayer();
2177 std::string playername = player->getName();
2178 std::string oldpwd = translatePassword(playername, oldpassword);
2179 std::string newpwd = translatePassword(playername, newpassword);
2181 std::ostringstream os(std::ios_base::binary);
2182 u8 buf[2+PASSWORD_SIZE*2];
2184 [0] u16 TOSERVER_PASSWORD
2185 [2] u8[28] old password
2186 [30] u8[28] new password
2189 writeU16(buf, TOSERVER_PASSWORD);
2190 for(u32 i=0;i<PASSWORD_SIZE-1;i++)
2192 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
2193 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
2195 buf[2+PASSWORD_SIZE-1] = 0;
2196 buf[30+PASSWORD_SIZE-1] = 0;
2197 os.write((char*)buf, 2+PASSWORD_SIZE*2);
2200 std::string s = os.str();
2201 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2203 Send(0, data, true);
2207 void Client::sendDamage(u8 damage)
2209 DSTACK(__FUNCTION_NAME);
2210 std::ostringstream os(std::ios_base::binary);
2212 writeU16(os, TOSERVER_DAMAGE);
2213 writeU8(os, damage);
2216 std::string s = os.str();
2217 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2219 Send(0, data, true);
2222 void Client::sendBreath(u16 breath)
2224 DSTACK(__FUNCTION_NAME);
2225 std::ostringstream os(std::ios_base::binary);
2227 writeU16(os, TOSERVER_BREATH);
2228 writeU16(os, breath);
2230 std::string s = os.str();
2231 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2233 Send(0, data, true);
2236 void Client::sendRespawn()
2238 DSTACK(__FUNCTION_NAME);
2239 std::ostringstream os(std::ios_base::binary);
2241 writeU16(os, TOSERVER_RESPAWN);
2244 std::string s = os.str();
2245 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2247 Send(0, data, true);
2250 void Client::sendPlayerPos()
2252 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2254 LocalPlayer *myplayer = m_env.getLocalPlayer();
2255 if(myplayer == NULL)
2258 // Save bandwidth by only updating position when something changed
2259 if(myplayer->last_position == myplayer->getPosition() &&
2260 myplayer->last_speed == myplayer->getSpeed() &&
2261 myplayer->last_pitch == myplayer->getPitch() &&
2262 myplayer->last_yaw == myplayer->getYaw() &&
2263 myplayer->last_keyPressed == myplayer->keyPressed)
2266 myplayer->last_position = myplayer->getPosition();
2267 myplayer->last_speed = myplayer->getSpeed();
2268 myplayer->last_pitch = myplayer->getPitch();
2269 myplayer->last_yaw = myplayer->getYaw();
2270 myplayer->last_keyPressed = myplayer->keyPressed;
2274 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2275 our_peer_id = m_con.GetPeerID();
2278 // Set peer id if not set already
2279 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2280 myplayer->peer_id = our_peer_id;
2281 // Check that an existing peer_id is the same as the connection's
2282 assert(myplayer->peer_id == our_peer_id);
2284 v3f pf = myplayer->getPosition();
2285 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
2286 v3f sf = myplayer->getSpeed();
2287 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
2288 s32 pitch = myplayer->getPitch() * 100;
2289 s32 yaw = myplayer->getYaw() * 100;
2290 u32 keyPressed=myplayer->keyPressed;
2294 [2] v3s32 position*100
2295 [2+12] v3s32 speed*100
2296 [2+12+12] s32 pitch*100
2297 [2+12+12+4] s32 yaw*100
2298 [2+12+12+4+4] u32 keyPressed
2300 SharedBuffer<u8> data(2+12+12+4+4+4);
2301 writeU16(&data[0], TOSERVER_PLAYERPOS);
2302 writeV3S32(&data[2], position);
2303 writeV3S32(&data[2+12], speed);
2304 writeS32(&data[2+12+12], pitch);
2305 writeS32(&data[2+12+12+4], yaw);
2306 writeU32(&data[2+12+12+4+4], keyPressed);
2307 // Send as unreliable
2308 Send(0, data, false);
2311 void Client::sendPlayerItem(u16 item)
2313 Player *myplayer = m_env.getLocalPlayer();
2314 if(myplayer == NULL)
2317 u16 our_peer_id = m_con.GetPeerID();
2319 // Set peer id if not set already
2320 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2321 myplayer->peer_id = our_peer_id;
2322 // Check that an existing peer_id is the same as the connection's
2323 assert(myplayer->peer_id == our_peer_id);
2325 SharedBuffer<u8> data(2+2);
2326 writeU16(&data[0], TOSERVER_PLAYERITEM);
2327 writeU16(&data[2], item);
2330 Send(0, data, true);
2333 void Client::removeNode(v3s16 p)
2335 std::map<v3s16, MapBlock*> modified_blocks;
2339 //TimeTaker t("removeNodeAndUpdate", m_device);
2340 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
2342 catch(InvalidPositionException &e)
2346 // add urgent task to update the modified node
2347 addUpdateMeshTaskForNode(p, false, true);
2349 for(std::map<v3s16, MapBlock * >::iterator
2350 i = modified_blocks.begin();
2351 i != modified_blocks.end(); ++i)
2353 addUpdateMeshTaskWithEdge(i->first);
2357 void Client::addNode(v3s16 p, MapNode n, bool remove_metadata)
2359 TimeTaker timer1("Client::addNode()");
2361 std::map<v3s16, MapBlock*> modified_blocks;
2365 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
2366 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
2368 catch(InvalidPositionException &e)
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::setPlayerControl(PlayerControl &control)
2381 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2382 LocalPlayer *player = m_env.getLocalPlayer();
2383 assert(player != NULL);
2384 player->control = control;
2387 void Client::selectPlayerItem(u16 item)
2389 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2390 m_playeritem = item;
2391 m_inventory_updated = true;
2392 sendPlayerItem(item);
2395 // Returns true if the inventory of the local player has been
2396 // updated from the server. If it is true, it is set to false.
2397 bool Client::getLocalInventoryUpdated()
2399 // m_inventory_updated is behind envlock
2400 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2401 bool updated = m_inventory_updated;
2402 m_inventory_updated = false;
2406 // Copies the inventory of the local player to parameter
2407 void Client::getLocalInventory(Inventory &dst)
2409 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2410 Player *player = m_env.getLocalPlayer();
2411 assert(player != NULL);
2412 dst = player->inventory;
2415 Inventory* Client::getInventory(const InventoryLocation &loc)
2418 case InventoryLocation::UNDEFINED:
2421 case InventoryLocation::CURRENT_PLAYER:
2423 Player *player = m_env.getLocalPlayer();
2424 assert(player != NULL);
2425 return &player->inventory;
2428 case InventoryLocation::PLAYER:
2430 Player *player = m_env.getPlayer(loc.name.c_str());
2433 return &player->inventory;
2436 case InventoryLocation::NODEMETA:
2438 NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p);
2441 return meta->getInventory();
2444 case InventoryLocation::DETACHED:
2446 if(m_detached_inventories.count(loc.name) == 0)
2448 return m_detached_inventories[loc.name];
2456 void Client::inventoryAction(InventoryAction *a)
2459 Send it to the server
2461 sendInventoryAction(a);
2464 Predict some local inventory changes
2466 a->clientApply(this, this);
2472 ClientActiveObject * Client::getSelectedActiveObject(
2474 v3f from_pos_f_on_map,
2475 core::line3d<f32> shootline_on_map
2478 std::vector<DistanceSortedActiveObject> objects;
2480 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
2482 //infostream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
2485 // After this, the closest object is the first in the array.
2486 std::sort(objects.begin(), objects.end());
2488 for(u32 i=0; i<objects.size(); i++)
2490 ClientActiveObject *obj = objects[i].obj;
2492 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
2493 if(selection_box == NULL)
2496 v3f pos = obj->getPosition();
2498 core::aabbox3d<f32> offsetted_box(
2499 selection_box->MinEdge + pos,
2500 selection_box->MaxEdge + pos
2503 if(offsetted_box.intersectsWithLine(shootline_on_map))
2505 //infostream<<"Returning selected object"<<std::endl;
2510 //infostream<<"No object selected; returning NULL."<<std::endl;
2514 void Client::printDebugInfo(std::ostream &os)
2516 //JMutexAutoLock lock1(m_fetchblock_mutex);
2517 /*JMutexAutoLock lock2(m_incoming_queue_mutex);
2519 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
2520 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
2521 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
2525 std::list<std::string> Client::getConnectedPlayerNames()
2527 return m_env.getPlayerNames();
2530 float Client::getAnimationTime()
2532 return m_animation_time;
2535 int Client::getCrackLevel()
2537 return m_crack_level;
2540 void Client::setCrack(int level, v3s16 pos)
2542 int old_crack_level = m_crack_level;
2543 v3s16 old_crack_pos = m_crack_pos;
2545 m_crack_level = level;
2548 if(old_crack_level >= 0 && (level < 0 || pos != old_crack_pos))
2551 addUpdateMeshTaskForNode(old_crack_pos, false, true);
2553 if(level >= 0 && (old_crack_level < 0 || pos != old_crack_pos))
2556 addUpdateMeshTaskForNode(pos, false, true);
2562 Player *player = m_env.getLocalPlayer();
2563 assert(player != NULL);
2567 u16 Client::getBreath()
2569 Player *player = m_env.getLocalPlayer();
2570 assert(player != NULL);
2571 return player->getBreath();
2574 bool Client::getChatMessage(std::wstring &message)
2576 if(m_chat_queue.size() == 0)
2578 message = m_chat_queue.pop_front();
2582 void Client::typeChatMessage(const std::wstring &message)
2584 // Discard empty line
2589 sendChatMessage(message);
2592 if (message[0] == L'/')
2594 m_chat_queue.push_back(
2595 (std::wstring)L"issued command: "+message);
2599 LocalPlayer *player = m_env.getLocalPlayer();
2600 assert(player != NULL);
2601 std::wstring name = narrow_to_wide(player->getName());
2602 m_chat_queue.push_back(
2603 (std::wstring)L"<"+name+L"> "+message);
2607 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
2609 /*infostream<<"Client::addUpdateMeshTask(): "
2610 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2611 <<" ack_to_server="<<ack_to_server
2612 <<" urgent="<<urgent
2615 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2620 Create a task to update the mesh of the block
2623 MeshMakeData *data = new MeshMakeData(this);
2626 //TimeTaker timer("data fill");
2628 // Debug: 1-6ms, avg=2ms
2630 data->setCrack(m_crack_level, m_crack_pos);
2631 data->setSmoothLighting(g_settings->getBool("smooth_lighting"));
2635 //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
2637 // Add task to queue
2638 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server, urgent);
2640 /*infostream<<"Mesh update input queue size is "
2641 <<m_mesh_update_thread.m_queue_in.size()
2645 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent)
2649 infostream<<"Client::addUpdateMeshTaskWithEdge(): "
2650 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2655 v3s16 p = blockpos + v3s16(0,0,0);
2656 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2657 addUpdateMeshTask(p, ack_to_server, urgent);
2659 catch(InvalidPositionException &e){}
2661 for (int i=0;i<6;i++)
2664 v3s16 p = blockpos + g_6dirs[i];
2665 addUpdateMeshTask(p, false, urgent);
2667 catch(InvalidPositionException &e){}
2671 void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool urgent)
2675 infostream<<"Client::addUpdateMeshTaskForNode(): "
2676 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2680 v3s16 blockpos = getNodeBlockPos(nodepos);
2681 v3s16 blockpos_relative = blockpos * MAP_BLOCKSIZE;
2684 v3s16 p = blockpos + v3s16(0,0,0);
2685 addUpdateMeshTask(p, ack_to_server, urgent);
2687 catch(InvalidPositionException &e){}
2689 if(nodepos.X == blockpos_relative.X){
2691 v3s16 p = blockpos + v3s16(-1,0,0);
2692 addUpdateMeshTask(p, false, urgent);
2694 catch(InvalidPositionException &e){}
2696 if(nodepos.Y == blockpos_relative.Y){
2698 v3s16 p = blockpos + v3s16(0,-1,0);
2699 addUpdateMeshTask(p, false, urgent);
2701 catch(InvalidPositionException &e){}
2703 if(nodepos.Z == blockpos_relative.Z){
2705 v3s16 p = blockpos + v3s16(0,0,-1);
2706 addUpdateMeshTask(p, false, urgent);
2708 catch(InvalidPositionException &e){}
2712 ClientEvent Client::getClientEvent()
2714 if(m_client_event_queue.size() == 0)
2717 event.type = CE_NONE;
2720 return m_client_event_queue.pop_front();
2723 float Client::mediaReceiveProgress()
2725 if (m_media_downloader)
2726 return m_media_downloader->getProgress();
2728 return 1.0; // downloader only exists when not yet done
2731 void draw_load_screen(const std::wstring &text,
2732 IrrlichtDevice* device, gui::IGUIFont* font,
2733 float dtime=0 ,int percent=0, bool clouds=true);
2734 void Client::afterContentReceived(IrrlichtDevice *device, gui::IGUIFont* font)
2736 infostream<<"Client::afterContentReceived() started"<<std::endl;
2737 assert(m_itemdef_received);
2738 assert(m_nodedef_received);
2739 assert(mediaReceived());
2741 // Rebuild inherited images and recreate textures
2742 infostream<<"- Rebuilding images and textures"<<std::endl;
2743 m_tsrc->rebuildImagesAndTextures();
2746 infostream<<"- Rebuilding shaders"<<std::endl;
2747 m_shsrc->rebuildShaders();
2749 // Update node aliases
2750 infostream<<"- Updating node aliases"<<std::endl;
2751 m_nodedef->updateAliases(m_itemdef);
2753 // Update node textures
2754 infostream<<"- Updating node textures"<<std::endl;
2755 m_nodedef->updateTextures(m_tsrc);
2757 // Preload item textures and meshes if configured to
2758 if(g_settings->getBool("preload_item_visuals"))
2760 verbosestream<<"Updating item textures and meshes"<<std::endl;
2761 wchar_t* text = wgettext("Item textures...");
2762 draw_load_screen(text,device,font,0,0);
2763 std::set<std::string> names = m_itemdef->getAll();
2764 size_t size = names.size();
2767 for(std::set<std::string>::const_iterator
2768 i = names.begin(); i != names.end(); ++i){
2769 // Asking for these caches the result
2770 m_itemdef->getInventoryTexture(*i, this);
2771 m_itemdef->getWieldMesh(*i, this);
2773 percent = count*100/size;
2774 if (count%50 == 0) // only update every 50 item
2775 draw_load_screen(text,device,font,0,percent);
2780 // Start mesh update thread after setting up content definitions
2781 infostream<<"- Starting mesh update thread"<<std::endl;
2782 m_mesh_update_thread.Start();
2784 infostream<<"Client::afterContentReceived() done"<<std::endl;
2787 float Client::getRTT(void)
2790 return m_con.GetPeerAvgRTT(PEER_ID_SERVER);
2791 } catch(con::PeerNotFoundException &e){
2796 // IGameDef interface
2798 IItemDefManager* Client::getItemDefManager()
2802 INodeDefManager* Client::getNodeDefManager()
2806 ICraftDefManager* Client::getCraftDefManager()
2809 //return m_craftdef;
2811 ITextureSource* Client::getTextureSource()
2815 IShaderSource* Client::getShaderSource()
2819 u16 Client::allocateUnknownNodeId(const std::string &name)
2821 errorstream<<"Client::allocateUnknownNodeId(): "
2822 <<"Client cannot allocate node IDs"<<std::endl;
2824 return CONTENT_IGNORE;
2826 ISoundManager* Client::getSoundManager()
2830 MtEventManager* Client::getEventManager()
2835 scene::IAnimatedMesh* Client::getMesh(const std::string &filename)
2837 std::map<std::string, std::string>::const_iterator i =
2838 m_mesh_data.find(filename);
2839 if(i == m_mesh_data.end()){
2840 errorstream<<"Client::getMesh(): Mesh not found: \""<<filename<<"\""
2844 const std::string &data = i->second;
2845 scene::ISceneManager *smgr = m_device->getSceneManager();
2847 // Create the mesh, remove it from cache and return it
2848 // This allows unique vertex colors and other properties for each instance
2849 Buffer<char> data_rw(data.c_str(), data.size()); // Const-incorrect Irrlicht
2850 io::IFileSystem *irrfs = m_device->getFileSystem();
2851 io::IReadFile *rfile = irrfs->createMemoryReadFile(
2852 *data_rw, data_rw.getSize(), filename.c_str());
2854 scene::IAnimatedMesh *mesh = smgr->getMesh(rfile);
2856 // NOTE: By playing with Irrlicht refcounts, maybe we could cache a bunch
2857 // of uniquely named instances and re-use them
2859 smgr->getMeshCache()->removeMesh(mesh);