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 world_pos = readV3F1000(is);
1947 }catch(SerializationError &e) {};
1950 event.type = CE_HUDADD;
1951 event.hudadd.id = id;
1952 event.hudadd.type = type;
1953 event.hudadd.pos = new v2f(pos);
1954 event.hudadd.name = new std::string(name);
1955 event.hudadd.scale = new v2f(scale);
1956 event.hudadd.text = new std::string(text);
1957 event.hudadd.number = number;
1958 event.hudadd.item = item;
1959 event.hudadd.dir = dir;
1960 event.hudadd.align = new v2f(align);
1961 event.hudadd.offset = new v2f(offset);
1962 event.hudadd.world_pos = new v3f(world_pos);
1963 m_client_event_queue.push_back(event);
1965 else if(command == TOCLIENT_HUDRM)
1967 std::string datastring((char *)&data[2], datasize - 2);
1968 std::istringstream is(datastring, std::ios_base::binary);
1970 u32 id = readU32(is);
1973 event.type = CE_HUDRM;
1974 event.hudrm.id = id;
1975 m_client_event_queue.push_back(event);
1977 else if(command == TOCLIENT_HUDCHANGE)
1984 std::string datastring((char *)&data[2], datasize - 2);
1985 std::istringstream is(datastring, std::ios_base::binary);
1987 u32 id = readU32(is);
1988 u8 stat = (HudElementStat)readU8(is);
1990 if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
1991 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
1992 v2fdata = readV2F1000(is);
1993 else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
1994 sdata = deSerializeString(is);
1995 else if (stat == HUD_STAT_WORLD_POS)
1996 v3fdata = readV3F1000(is);
1998 intdata = readU32(is);
2001 event.type = CE_HUDCHANGE;
2002 event.hudchange.id = id;
2003 event.hudchange.stat = (HudElementStat)stat;
2004 event.hudchange.v2fdata = new v2f(v2fdata);
2005 event.hudchange.v3fdata = new v3f(v3fdata);
2006 event.hudchange.sdata = new std::string(sdata);
2007 event.hudchange.data = intdata;
2008 m_client_event_queue.push_back(event);
2010 else if(command == TOCLIENT_HUD_SET_FLAGS)
2012 std::string datastring((char *)&data[2], datasize - 2);
2013 std::istringstream is(datastring, std::ios_base::binary);
2015 Player *player = m_env.getLocalPlayer();
2016 assert(player != NULL);
2018 u32 flags = readU32(is);
2019 u32 mask = readU32(is);
2021 player->hud_flags &= ~mask;
2022 player->hud_flags |= flags;
2024 else if(command == TOCLIENT_HUD_SET_PARAM)
2026 std::string datastring((char *)&data[2], datasize - 2);
2027 std::istringstream is(datastring, std::ios_base::binary);
2029 Player *player = m_env.getLocalPlayer();
2030 assert(player != NULL);
2032 u16 param = readU16(is);
2033 std::string value = deSerializeString(is);
2035 if(param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4){
2036 s32 hotbar_itemcount = readS32((u8*) value.c_str());
2037 if(hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
2038 player->hud_hotbar_itemcount = hotbar_itemcount;
2039 } else if (param == HUD_PARAM_HOTBAR_IMAGE) {
2040 ((LocalPlayer *) player)->hotbar_image = value;
2041 } else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
2042 ((LocalPlayer *) player)->hotbar_selected_image = value;
2047 infostream<<"Client: Ignoring unknown command "
2048 <<command<<std::endl;
2052 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
2054 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2055 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
2058 void Client::interact(u8 action, const PointedThing& pointed)
2060 if(connectedAndInitialized() == false){
2061 infostream<<"Client::interact() "
2062 "cancelled (not connected)"
2067 std::ostringstream os(std::ios_base::binary);
2073 [5] u32 length of the next item
2074 [9] serialized PointedThing
2076 0: start digging (from undersurface) or use
2077 1: stop digging (all parameters ignored)
2078 2: digging completed
2079 3: place block or item (to abovesurface)
2082 writeU16(os, TOSERVER_INTERACT);
2083 writeU8(os, action);
2084 writeU16(os, getPlayerItem());
2085 std::ostringstream tmp_os(std::ios::binary);
2086 pointed.serialize(tmp_os);
2087 os<<serializeLongString(tmp_os.str());
2089 std::string s = os.str();
2090 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2093 Send(0, data, true);
2096 void Client::sendNodemetaFields(v3s16 p, const std::string &formname,
2097 const std::map<std::string, std::string> &fields)
2099 std::ostringstream os(std::ios_base::binary);
2101 writeU16(os, TOSERVER_NODEMETA_FIELDS);
2103 os<<serializeString(formname);
2104 writeU16(os, fields.size());
2105 for(std::map<std::string, std::string>::const_iterator
2106 i = fields.begin(); i != fields.end(); i++){
2107 const std::string &name = i->first;
2108 const std::string &value = i->second;
2109 os<<serializeString(name);
2110 os<<serializeLongString(value);
2114 std::string s = os.str();
2115 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2117 Send(0, data, true);
2120 void Client::sendInventoryFields(const std::string &formname,
2121 const std::map<std::string, std::string> &fields)
2123 std::ostringstream os(std::ios_base::binary);
2125 writeU16(os, TOSERVER_INVENTORY_FIELDS);
2126 os<<serializeString(formname);
2127 writeU16(os, fields.size());
2128 for(std::map<std::string, std::string>::const_iterator
2129 i = fields.begin(); i != fields.end(); i++){
2130 const std::string &name = i->first;
2131 const std::string &value = i->second;
2132 os<<serializeString(name);
2133 os<<serializeLongString(value);
2137 std::string s = os.str();
2138 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2140 Send(0, data, true);
2143 void Client::sendInventoryAction(InventoryAction *a)
2145 std::ostringstream os(std::ios_base::binary);
2149 writeU16(buf, TOSERVER_INVENTORY_ACTION);
2150 os.write((char*)buf, 2);
2155 std::string s = os.str();
2156 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2158 Send(0, data, true);
2161 void Client::sendChatMessage(const std::wstring &message)
2163 std::ostringstream os(std::ios_base::binary);
2167 writeU16(buf, TOSERVER_CHAT_MESSAGE);
2168 os.write((char*)buf, 2);
2171 writeU16(buf, message.size());
2172 os.write((char*)buf, 2);
2175 for(u32 i=0; i<message.size(); i++)
2179 os.write((char*)buf, 2);
2183 std::string s = os.str();
2184 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2186 Send(0, data, true);
2189 void Client::sendChangePassword(const std::wstring oldpassword,
2190 const std::wstring newpassword)
2192 Player *player = m_env.getLocalPlayer();
2196 std::string playername = player->getName();
2197 std::string oldpwd = translatePassword(playername, oldpassword);
2198 std::string newpwd = translatePassword(playername, newpassword);
2200 std::ostringstream os(std::ios_base::binary);
2201 u8 buf[2+PASSWORD_SIZE*2];
2203 [0] u16 TOSERVER_PASSWORD
2204 [2] u8[28] old password
2205 [30] u8[28] new password
2208 writeU16(buf, TOSERVER_PASSWORD);
2209 for(u32 i=0;i<PASSWORD_SIZE-1;i++)
2211 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
2212 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
2214 buf[2+PASSWORD_SIZE-1] = 0;
2215 buf[30+PASSWORD_SIZE-1] = 0;
2216 os.write((char*)buf, 2+PASSWORD_SIZE*2);
2219 std::string s = os.str();
2220 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2222 Send(0, data, true);
2226 void Client::sendDamage(u8 damage)
2228 DSTACK(__FUNCTION_NAME);
2229 std::ostringstream os(std::ios_base::binary);
2231 writeU16(os, TOSERVER_DAMAGE);
2232 writeU8(os, damage);
2235 std::string s = os.str();
2236 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2238 Send(0, data, true);
2241 void Client::sendBreath(u16 breath)
2243 DSTACK(__FUNCTION_NAME);
2244 std::ostringstream os(std::ios_base::binary);
2246 writeU16(os, TOSERVER_BREATH);
2247 writeU16(os, breath);
2249 std::string s = os.str();
2250 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2252 Send(0, data, true);
2255 void Client::sendRespawn()
2257 DSTACK(__FUNCTION_NAME);
2258 std::ostringstream os(std::ios_base::binary);
2260 writeU16(os, TOSERVER_RESPAWN);
2263 std::string s = os.str();
2264 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2266 Send(0, data, true);
2269 void Client::sendPlayerPos()
2271 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2273 LocalPlayer *myplayer = m_env.getLocalPlayer();
2274 if(myplayer == NULL)
2277 // Save bandwidth by only updating position when something changed
2278 if(myplayer->last_position == myplayer->getPosition() &&
2279 myplayer->last_speed == myplayer->getSpeed() &&
2280 myplayer->last_pitch == myplayer->getPitch() &&
2281 myplayer->last_yaw == myplayer->getYaw() &&
2282 myplayer->last_keyPressed == myplayer->keyPressed)
2285 myplayer->last_position = myplayer->getPosition();
2286 myplayer->last_speed = myplayer->getSpeed();
2287 myplayer->last_pitch = myplayer->getPitch();
2288 myplayer->last_yaw = myplayer->getYaw();
2289 myplayer->last_keyPressed = myplayer->keyPressed;
2293 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2294 our_peer_id = m_con.GetPeerID();
2297 // Set peer id if not set already
2298 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2299 myplayer->peer_id = our_peer_id;
2300 // Check that an existing peer_id is the same as the connection's
2301 assert(myplayer->peer_id == our_peer_id);
2303 v3f pf = myplayer->getPosition();
2304 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
2305 v3f sf = myplayer->getSpeed();
2306 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
2307 s32 pitch = myplayer->getPitch() * 100;
2308 s32 yaw = myplayer->getYaw() * 100;
2309 u32 keyPressed=myplayer->keyPressed;
2313 [2] v3s32 position*100
2314 [2+12] v3s32 speed*100
2315 [2+12+12] s32 pitch*100
2316 [2+12+12+4] s32 yaw*100
2317 [2+12+12+4+4] u32 keyPressed
2319 SharedBuffer<u8> data(2+12+12+4+4+4);
2320 writeU16(&data[0], TOSERVER_PLAYERPOS);
2321 writeV3S32(&data[2], position);
2322 writeV3S32(&data[2+12], speed);
2323 writeS32(&data[2+12+12], pitch);
2324 writeS32(&data[2+12+12+4], yaw);
2325 writeU32(&data[2+12+12+4+4], keyPressed);
2326 // Send as unreliable
2327 Send(0, data, false);
2330 void Client::sendPlayerItem(u16 item)
2332 Player *myplayer = m_env.getLocalPlayer();
2333 if(myplayer == NULL)
2336 u16 our_peer_id = m_con.GetPeerID();
2338 // Set peer id if not set already
2339 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2340 myplayer->peer_id = our_peer_id;
2341 // Check that an existing peer_id is the same as the connection's
2342 assert(myplayer->peer_id == our_peer_id);
2344 SharedBuffer<u8> data(2+2);
2345 writeU16(&data[0], TOSERVER_PLAYERITEM);
2346 writeU16(&data[2], item);
2349 Send(0, data, true);
2352 void Client::removeNode(v3s16 p)
2354 std::map<v3s16, MapBlock*> modified_blocks;
2358 //TimeTaker t("removeNodeAndUpdate", m_device);
2359 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
2361 catch(InvalidPositionException &e)
2365 // add urgent task to update the modified node
2366 addUpdateMeshTaskForNode(p, false, true);
2368 for(std::map<v3s16, MapBlock * >::iterator
2369 i = modified_blocks.begin();
2370 i != modified_blocks.end(); ++i)
2372 addUpdateMeshTaskWithEdge(i->first);
2376 void Client::addNode(v3s16 p, MapNode n, bool remove_metadata)
2378 TimeTaker timer1("Client::addNode()");
2380 std::map<v3s16, MapBlock*> modified_blocks;
2384 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
2385 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
2387 catch(InvalidPositionException &e)
2390 for(std::map<v3s16, MapBlock * >::iterator
2391 i = modified_blocks.begin();
2392 i != modified_blocks.end(); ++i)
2394 addUpdateMeshTaskWithEdge(i->first);
2398 void Client::setPlayerControl(PlayerControl &control)
2400 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2401 LocalPlayer *player = m_env.getLocalPlayer();
2402 assert(player != NULL);
2403 player->control = control;
2406 void Client::selectPlayerItem(u16 item)
2408 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2409 m_playeritem = item;
2410 m_inventory_updated = true;
2411 sendPlayerItem(item);
2414 // Returns true if the inventory of the local player has been
2415 // updated from the server. If it is true, it is set to false.
2416 bool Client::getLocalInventoryUpdated()
2418 // m_inventory_updated is behind envlock
2419 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2420 bool updated = m_inventory_updated;
2421 m_inventory_updated = false;
2425 // Copies the inventory of the local player to parameter
2426 void Client::getLocalInventory(Inventory &dst)
2428 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2429 Player *player = m_env.getLocalPlayer();
2430 assert(player != NULL);
2431 dst = player->inventory;
2434 Inventory* Client::getInventory(const InventoryLocation &loc)
2437 case InventoryLocation::UNDEFINED:
2440 case InventoryLocation::CURRENT_PLAYER:
2442 Player *player = m_env.getLocalPlayer();
2443 assert(player != NULL);
2444 return &player->inventory;
2447 case InventoryLocation::PLAYER:
2449 Player *player = m_env.getPlayer(loc.name.c_str());
2452 return &player->inventory;
2455 case InventoryLocation::NODEMETA:
2457 NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p);
2460 return meta->getInventory();
2463 case InventoryLocation::DETACHED:
2465 if(m_detached_inventories.count(loc.name) == 0)
2467 return m_detached_inventories[loc.name];
2475 void Client::inventoryAction(InventoryAction *a)
2478 Send it to the server
2480 sendInventoryAction(a);
2483 Predict some local inventory changes
2485 a->clientApply(this, this);
2491 ClientActiveObject * Client::getSelectedActiveObject(
2493 v3f from_pos_f_on_map,
2494 core::line3d<f32> shootline_on_map
2497 std::vector<DistanceSortedActiveObject> objects;
2499 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
2501 //infostream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
2504 // After this, the closest object is the first in the array.
2505 std::sort(objects.begin(), objects.end());
2507 for(u32 i=0; i<objects.size(); i++)
2509 ClientActiveObject *obj = objects[i].obj;
2511 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
2512 if(selection_box == NULL)
2515 v3f pos = obj->getPosition();
2517 core::aabbox3d<f32> offsetted_box(
2518 selection_box->MinEdge + pos,
2519 selection_box->MaxEdge + pos
2522 if(offsetted_box.intersectsWithLine(shootline_on_map))
2524 //infostream<<"Returning selected object"<<std::endl;
2529 //infostream<<"No object selected; returning NULL."<<std::endl;
2533 void Client::printDebugInfo(std::ostream &os)
2535 //JMutexAutoLock lock1(m_fetchblock_mutex);
2536 /*JMutexAutoLock lock2(m_incoming_queue_mutex);
2538 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
2539 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
2540 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
2544 std::list<std::string> Client::getConnectedPlayerNames()
2546 return m_env.getPlayerNames();
2549 float Client::getAnimationTime()
2551 return m_animation_time;
2554 int Client::getCrackLevel()
2556 return m_crack_level;
2559 void Client::setCrack(int level, v3s16 pos)
2561 int old_crack_level = m_crack_level;
2562 v3s16 old_crack_pos = m_crack_pos;
2564 m_crack_level = level;
2567 if(old_crack_level >= 0 && (level < 0 || pos != old_crack_pos))
2570 addUpdateMeshTaskForNode(old_crack_pos, false, true);
2572 if(level >= 0 && (old_crack_level < 0 || pos != old_crack_pos))
2575 addUpdateMeshTaskForNode(pos, false, true);
2581 Player *player = m_env.getLocalPlayer();
2582 assert(player != NULL);
2586 u16 Client::getBreath()
2588 Player *player = m_env.getLocalPlayer();
2589 assert(player != NULL);
2590 return player->getBreath();
2593 bool Client::getChatMessage(std::wstring &message)
2595 if(m_chat_queue.size() == 0)
2597 message = m_chat_queue.pop_front();
2601 void Client::typeChatMessage(const std::wstring &message)
2603 // Discard empty line
2608 sendChatMessage(message);
2611 if (message[0] == L'/')
2613 m_chat_queue.push_back(
2614 (std::wstring)L"issued command: "+message);
2618 LocalPlayer *player = m_env.getLocalPlayer();
2619 assert(player != NULL);
2620 std::wstring name = narrow_to_wide(player->getName());
2621 m_chat_queue.push_back(
2622 (std::wstring)L"<"+name+L"> "+message);
2626 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
2628 /*infostream<<"Client::addUpdateMeshTask(): "
2629 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2630 <<" ack_to_server="<<ack_to_server
2631 <<" urgent="<<urgent
2634 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2639 Create a task to update the mesh of the block
2642 MeshMakeData *data = new MeshMakeData(this);
2645 //TimeTaker timer("data fill");
2647 // Debug: 1-6ms, avg=2ms
2649 data->setCrack(m_crack_level, m_crack_pos);
2650 data->setSmoothLighting(g_settings->getBool("smooth_lighting"));
2654 //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
2656 // Add task to queue
2657 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server, urgent);
2659 /*infostream<<"Mesh update input queue size is "
2660 <<m_mesh_update_thread.m_queue_in.size()
2664 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent)
2668 infostream<<"Client::addUpdateMeshTaskWithEdge(): "
2669 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2674 v3s16 p = blockpos + v3s16(0,0,0);
2675 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2676 addUpdateMeshTask(p, ack_to_server, urgent);
2678 catch(InvalidPositionException &e){}
2680 for (int i=0;i<6;i++)
2683 v3s16 p = blockpos + g_6dirs[i];
2684 addUpdateMeshTask(p, false, urgent);
2686 catch(InvalidPositionException &e){}
2690 void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool urgent)
2694 infostream<<"Client::addUpdateMeshTaskForNode(): "
2695 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2699 v3s16 blockpos = getNodeBlockPos(nodepos);
2700 v3s16 blockpos_relative = blockpos * MAP_BLOCKSIZE;
2703 v3s16 p = blockpos + v3s16(0,0,0);
2704 addUpdateMeshTask(p, ack_to_server, urgent);
2706 catch(InvalidPositionException &e){}
2708 if(nodepos.X == blockpos_relative.X){
2710 v3s16 p = blockpos + v3s16(-1,0,0);
2711 addUpdateMeshTask(p, false, urgent);
2713 catch(InvalidPositionException &e){}
2715 if(nodepos.Y == blockpos_relative.Y){
2717 v3s16 p = blockpos + v3s16(0,-1,0);
2718 addUpdateMeshTask(p, false, urgent);
2720 catch(InvalidPositionException &e){}
2722 if(nodepos.Z == blockpos_relative.Z){
2724 v3s16 p = blockpos + v3s16(0,0,-1);
2725 addUpdateMeshTask(p, false, urgent);
2727 catch(InvalidPositionException &e){}
2731 ClientEvent Client::getClientEvent()
2733 if(m_client_event_queue.size() == 0)
2736 event.type = CE_NONE;
2739 return m_client_event_queue.pop_front();
2742 float Client::mediaReceiveProgress()
2744 if (m_media_downloader)
2745 return m_media_downloader->getProgress();
2747 return 1.0; // downloader only exists when not yet done
2750 void draw_load_screen(const std::wstring &text,
2751 IrrlichtDevice* device, gui::IGUIFont* font,
2752 float dtime=0 ,int percent=0, bool clouds=true);
2753 void Client::afterContentReceived(IrrlichtDevice *device, gui::IGUIFont* font)
2755 infostream<<"Client::afterContentReceived() started"<<std::endl;
2756 assert(m_itemdef_received);
2757 assert(m_nodedef_received);
2758 assert(mediaReceived());
2760 // Rebuild inherited images and recreate textures
2761 infostream<<"- Rebuilding images and textures"<<std::endl;
2762 m_tsrc->rebuildImagesAndTextures();
2765 infostream<<"- Rebuilding shaders"<<std::endl;
2766 m_shsrc->rebuildShaders();
2768 // Update node aliases
2769 infostream<<"- Updating node aliases"<<std::endl;
2770 m_nodedef->updateAliases(m_itemdef);
2772 // Update node textures
2773 infostream<<"- Updating node textures"<<std::endl;
2774 m_nodedef->updateTextures(m_tsrc);
2776 // Preload item textures and meshes if configured to
2777 if(g_settings->getBool("preload_item_visuals"))
2779 verbosestream<<"Updating item textures and meshes"<<std::endl;
2780 wchar_t* text = wgettext("Item textures...");
2781 draw_load_screen(text,device,font,0,0);
2782 std::set<std::string> names = m_itemdef->getAll();
2783 size_t size = names.size();
2786 for(std::set<std::string>::const_iterator
2787 i = names.begin(); i != names.end(); ++i){
2788 // Asking for these caches the result
2789 m_itemdef->getInventoryTexture(*i, this);
2790 m_itemdef->getWieldMesh(*i, this);
2792 percent = count*100/size;
2793 if (count%50 == 0) // only update every 50 item
2794 draw_load_screen(text,device,font,0,percent);
2799 // Start mesh update thread after setting up content definitions
2800 infostream<<"- Starting mesh update thread"<<std::endl;
2801 m_mesh_update_thread.Start();
2803 infostream<<"Client::afterContentReceived() done"<<std::endl;
2806 float Client::getRTT(void)
2809 return m_con.GetPeerAvgRTT(PEER_ID_SERVER);
2810 } catch(con::PeerNotFoundException &e){
2815 // IGameDef interface
2817 IItemDefManager* Client::getItemDefManager()
2821 INodeDefManager* Client::getNodeDefManager()
2825 ICraftDefManager* Client::getCraftDefManager()
2828 //return m_craftdef;
2830 ITextureSource* Client::getTextureSource()
2834 IShaderSource* Client::getShaderSource()
2838 u16 Client::allocateUnknownNodeId(const std::string &name)
2840 errorstream<<"Client::allocateUnknownNodeId(): "
2841 <<"Client cannot allocate node IDs"<<std::endl;
2843 return CONTENT_IGNORE;
2845 ISoundManager* Client::getSoundManager()
2849 MtEventManager* Client::getEventManager()
2854 scene::IAnimatedMesh* Client::getMesh(const std::string &filename)
2856 std::map<std::string, std::string>::const_iterator i =
2857 m_mesh_data.find(filename);
2858 if(i == m_mesh_data.end()){
2859 errorstream<<"Client::getMesh(): Mesh not found: \""<<filename<<"\""
2863 const std::string &data = i->second;
2864 scene::ISceneManager *smgr = m_device->getSceneManager();
2866 // Create the mesh, remove it from cache and return it
2867 // This allows unique vertex colors and other properties for each instance
2868 Buffer<char> data_rw(data.c_str(), data.size()); // Const-incorrect Irrlicht
2869 io::IFileSystem *irrfs = m_device->getFileSystem();
2870 io::IReadFile *rfile = irrfs->createMemoryReadFile(
2871 *data_rw, data_rw.getSize(), filename.c_str());
2873 scene::IAnimatedMesh *mesh = smgr->getMesh(rfile);
2875 // NOTE: By playing with Irrlicht refcounts, maybe we could cache a bunch
2876 // of uniquely named instances and re-use them
2878 smgr->getMeshCache()->removeMesh(mesh);