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 <IFileSystem.h>
24 #include "jthread/jmutexautolock.h"
25 #include "util/directiontables.h"
26 #include "util/pointedthing.h"
27 #include "util/serialize.h"
28 #include "util/string.h"
31 #include "clientserver.h"
35 #include "mapsector.h"
36 #include "mapblock_mesh.h"
42 #include "nodemetadata.h"
47 #include "clientmap.h"
48 #include "clientmedia.h"
50 #include "IMeshCache.h"
51 #include "serialization.h"
54 #include "drawscene.h"
58 #include "database-sqlite3.h"
60 extern gui::IGUIEnvironment* guienv;
66 QueuedMeshUpdate::QueuedMeshUpdate():
69 ack_block_to_server(false)
73 QueuedMeshUpdate::~QueuedMeshUpdate()
83 MeshUpdateQueue::MeshUpdateQueue()
87 MeshUpdateQueue::~MeshUpdateQueue()
89 JMutexAutoLock lock(m_mutex);
91 for(std::vector<QueuedMeshUpdate*>::iterator
93 i != m_queue.end(); i++)
95 QueuedMeshUpdate *q = *i;
101 peer_id=0 adds with nobody to send to
103 void MeshUpdateQueue::addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_server, bool urgent)
105 DSTACK(__FUNCTION_NAME);
109 JMutexAutoLock lock(m_mutex);
115 Find if block is already in queue.
116 If it is, update the data and quit.
118 for(std::vector<QueuedMeshUpdate*>::iterator
120 i != m_queue.end(); i++)
122 QueuedMeshUpdate *q = *i;
128 if(ack_block_to_server)
129 q->ack_block_to_server = true;
137 QueuedMeshUpdate *q = new QueuedMeshUpdate;
140 q->ack_block_to_server = ack_block_to_server;
141 m_queue.push_back(q);
144 // Returned pointer must be deleted
145 // Returns NULL if queue is empty
146 QueuedMeshUpdate * MeshUpdateQueue::pop()
148 JMutexAutoLock lock(m_mutex);
150 bool must_be_urgent = !m_urgents.empty();
151 for(std::vector<QueuedMeshUpdate*>::iterator
153 i != m_queue.end(); i++)
155 QueuedMeshUpdate *q = *i;
156 if(must_be_urgent && m_urgents.count(q->p) == 0)
159 m_urgents.erase(q->p);
169 void * MeshUpdateThread::Thread()
173 log_register_thread("MeshUpdateThread");
175 DSTACK(__FUNCTION_NAME);
177 BEGIN_DEBUG_EXCEPTION_HANDLER
179 porting::setThreadName("MeshUpdateThread");
181 while(!StopRequested())
183 QueuedMeshUpdate *q = m_queue_in.pop();
190 ScopeProfiler sp(g_profiler, "Client: Mesh making");
192 MapBlockMesh *mesh_new = new MapBlockMesh(q->data, m_camera_offset);
193 if(mesh_new->getMesh()->getMeshBufferCount() == 0)
202 r.ack_block_to_server = q->ack_block_to_server;
204 m_queue_out.push_back(r);
209 END_DEBUG_EXCEPTION_HANDLER(errorstream)
219 IrrlichtDevice *device,
220 const char *playername,
221 std::string password,
222 bool is_simple_singleplayer_game,
223 MapDrawControl &control,
224 IWritableTextureSource *tsrc,
225 IWritableShaderSource *shsrc,
226 IWritableItemDefManager *itemdef,
227 IWritableNodeDefManager *nodedef,
228 ISoundManager *sound,
229 MtEventManager *event,
232 m_packetcounter_timer(0.0),
233 m_connection_reinit_timer(0.1),
234 m_avg_rtt_timer(0.0),
235 m_playerpos_send_timer(0.0),
236 m_ignore_damage_timer(0.0),
243 m_mesh_update_thread(this),
245 new ClientMap(this, this, control,
246 device->getSceneManager()->getRootSceneNode(),
247 device->getSceneManager(), 666),
248 device->getSceneManager(),
251 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, ipv6, this),
253 m_server_ser_ver(SER_FMT_VER_INVALID),
255 m_inventory_updated(false),
256 m_inventory_from_server(NULL),
257 m_inventory_from_server_age(0.0),
258 m_show_highlighted(false),
262 m_highlighted_pos(0,0,0),
264 m_password(password),
265 m_access_denied(false),
266 m_itemdef_received(false),
267 m_nodedef_received(false),
268 m_media_downloader(new ClientMediaDownloader()),
269 m_time_of_day_set(false),
270 m_last_time_of_day_f(-1),
271 m_time_of_day_update_timer(0),
272 m_recommended_send_interval(0.1),
273 m_removed_sounds_check_timer(0),
280 Player *player = new LocalPlayer(this, playername);
282 m_env.addPlayer(player);
285 if (g_settings->getBool("enable_local_map_saving")
286 && !is_simple_singleplayer_game) {
287 const std::string world_path = porting::path_user + DIR_DELIM + "worlds"
288 + DIR_DELIM + "server_" + g_settings->get("address")
289 + "_" + g_settings->get("remote_port");
291 SubgameSpec gamespec;
292 if (!getWorldExists(world_path)) {
293 gamespec = findSubgame(g_settings->get("default_game"));
294 if (!gamespec.isValid())
295 gamespec = findSubgame("minimal");
297 std::string world_gameid = getWorldGameId(world_path, false);
298 gamespec = findWorldSubgame(world_path);
300 if (!gamespec.isValid()) {
301 errorstream << "Couldn't find subgame for local map saving." << std::endl;
305 localserver = new Server(world_path, gamespec, false, false);
306 localdb = new Database_SQLite3(&(ServerMap&)localserver->getMap(), world_path);
307 localdb->beginSave();
308 actionstream << "Local map saving started, map will be saved at '" << world_path << "'" << std::endl;
316 //request all client managed threads to stop
317 m_mesh_update_thread.Stop();
318 if (localdb != NULL) {
319 actionstream << "Local map saving ended" << std::endl;
324 bool Client::isShutdown()
327 if (!m_mesh_update_thread.IsRunning()) return true;
336 m_mesh_update_thread.Stop();
337 m_mesh_update_thread.Wait();
338 while(!m_mesh_update_thread.m_queue_out.empty()) {
339 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_frontNoEx();
344 delete m_inventory_from_server;
346 // Delete detached inventories
347 for(std::map<std::string, Inventory*>::iterator
348 i = m_detached_inventories.begin();
349 i != m_detached_inventories.end(); i++){
353 // cleanup 3d model meshes on client shutdown
354 while (m_device->getSceneManager()->getMeshCache()->getMeshCount() != 0) {
355 scene::IAnimatedMesh * mesh =
356 m_device->getSceneManager()->getMeshCache()->getMeshByIndex(0);
359 m_device->getSceneManager()->getMeshCache()->removeMesh(mesh);
363 void Client::connect(Address address)
365 DSTACK(__FUNCTION_NAME);
366 m_con.SetTimeoutMs(0);
367 m_con.Connect(address);
370 void Client::step(float dtime)
372 DSTACK(__FUNCTION_NAME);
378 if(m_ignore_damage_timer > dtime)
379 m_ignore_damage_timer -= dtime;
381 m_ignore_damage_timer = 0.0;
383 m_animation_time += dtime;
384 if(m_animation_time > 60.0)
385 m_animation_time -= 60.0;
387 m_time_of_day_update_timer += dtime;
395 float &counter = m_packetcounter_timer;
401 infostream << "Client packetcounter (" << m_packetcounter_timer
403 m_packetcounter.print(infostream);
404 m_packetcounter.clear();
411 Delete unused sectors
413 NOTE: This jams the game for a while because deleting sectors
417 float &counter = m_delete_unused_sectors_timer;
425 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
427 core::list<v3s16> deleted_blocks;
429 float delete_unused_sectors_timeout =
430 g_settings->getFloat("client_delete_unused_sectors_timeout");
432 // Delete sector blocks
433 /*u32 num = m_env.getMap().unloadUnusedData
434 (delete_unused_sectors_timeout,
435 true, &deleted_blocks);*/
437 // Delete whole sectors
438 m_env.getMap().unloadUnusedData
439 (delete_unused_sectors_timeout,
442 if(deleted_blocks.size() > 0)
444 /*infostream<<"Client: Deleted blocks of "<<num
445 <<" unused sectors"<<std::endl;*/
446 /*infostream<<"Client: Deleted "<<num
447 <<" unused sectors"<<std::endl;*/
453 // Env is locked so con can be locked.
454 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
456 core::list<v3s16>::Iterator i = deleted_blocks.begin();
457 core::list<v3s16> sendlist;
460 if(sendlist.size() == 255 || i == deleted_blocks.end())
462 if(sendlist.size() == 0)
471 u32 replysize = 2+1+6*sendlist.size();
472 SharedBuffer<u8> reply(replysize);
473 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
474 reply[2] = sendlist.size();
476 for(core::list<v3s16>::Iterator
477 j = sendlist.begin();
478 j != sendlist.end(); j++)
480 writeV3S16(&reply[2+1+6*k], *j);
483 m_con.Send(PEER_ID_SERVER, 1, reply, true);
485 if(i == deleted_blocks.end())
491 sendlist.push_back(*i);
498 // UGLY hack to fix 2 second startup delay caused by non existent
499 // server client startup synchronization in local server or singleplayer mode
500 static bool initial_step = true;
502 initial_step = false;
504 else if(m_state == LC_Created)
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);
516 // Send TOSERVER_INIT
517 // [0] u16 TOSERVER_INIT
518 // [2] u8 SER_FMT_VER_HIGHEST_READ
519 // [3] u8[20] player_name
520 // [23] u8[28] password (new in some version)
521 // [51] u16 minimum supported network protocol version (added sometime)
522 // [53] u16 maximum supported network protocol version (added later than the previous one)
523 SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2+2);
524 writeU16(&data[0], TOSERVER_INIT);
525 writeU8(&data[2], SER_FMT_VER_HIGHEST_READ);
527 memset((char*)&data[3], 0, PLAYERNAME_SIZE);
528 snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
530 /*infostream<<"Client: sending initial password hash: \""<<m_password<<"\""
533 memset((char*)&data[23], 0, PASSWORD_SIZE);
534 snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
536 writeU16(&data[51], CLIENT_PROTOCOL_VERSION_MIN);
537 writeU16(&data[53], CLIENT_PROTOCOL_VERSION_MAX);
539 // Send as unreliable
540 Send(1, data, false);
543 // Not connected, return
548 Do stuff if connected
552 Run Map's timers and unload unused data
554 const float map_timer_and_unload_dtime = 5.25;
555 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
557 ScopeProfiler sp(g_profiler, "Client: map timer and unload");
558 std::list<v3s16> deleted_blocks;
559 m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
560 g_settings->getFloat("client_unload_unused_data_timeout"),
563 /*if(deleted_blocks.size() > 0)
564 infostream<<"Client: Unloaded "<<deleted_blocks.size()
565 <<" unused blocks"<<std::endl;*/
569 NOTE: This loop is intentionally iterated the way it is.
572 std::list<v3s16>::iterator i = deleted_blocks.begin();
573 std::list<v3s16> sendlist;
576 if(sendlist.size() == 255 || i == deleted_blocks.end())
578 if(sendlist.size() == 0)
587 u32 replysize = 2+1+6*sendlist.size();
588 SharedBuffer<u8> reply(replysize);
589 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
590 reply[2] = sendlist.size();
592 for(std::list<v3s16>::iterator
593 j = sendlist.begin();
594 j != sendlist.end(); ++j)
596 writeV3S16(&reply[2+1+6*k], *j);
599 m_con.Send(PEER_ID_SERVER, 2, reply, true);
601 if(i == deleted_blocks.end())
607 sendlist.push_back(*i);
616 // Control local player (0ms)
617 LocalPlayer *player = m_env.getLocalPlayer();
618 assert(player != NULL);
619 player->applyControl(dtime);
629 ClientEnvEvent event = m_env.getClientEvent();
630 if(event.type == CEE_NONE)
634 else if(event.type == CEE_PLAYER_DAMAGE)
636 if(m_ignore_damage_timer <= 0)
638 u8 damage = event.player_damage.amount;
640 if(event.player_damage.send_to_server)
643 // Add to ClientEvent queue
645 event.type = CE_PLAYER_DAMAGE;
646 event.player_damage.amount = damage;
647 m_client_event_queue.push_back(event);
650 else if(event.type == CEE_PLAYER_BREATH)
652 u16 breath = event.player_breath.amount;
662 float &counter = m_avg_rtt_timer;
667 // connectedAndInitialized() is true, peer exists.
668 float avg_rtt = getRTT();
669 infostream<<"Client: avg_rtt="<<avg_rtt<<std::endl;
674 Send player position to server
677 float &counter = m_playerpos_send_timer;
679 if((m_state == LC_Ready) && (counter >= m_recommended_send_interval))
687 Replace updated meshes
690 int num_processed_meshes = 0;
691 while(!m_mesh_update_thread.m_queue_out.empty())
693 num_processed_meshes++;
694 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_frontNoEx();
695 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
698 // Delete the old mesh
699 if(block->mesh != NULL)
701 // TODO: Remove hardware buffers of meshbuffers of block->mesh
706 // Replace with the new mesh
707 block->mesh = r.mesh;
711 if(r.ack_block_to_server)
723 u32 replysize = 2+1+6;
724 SharedBuffer<u8> reply(replysize);
725 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
727 writeV3S16(&reply[3], r.p);
729 m_con.Send(PEER_ID_SERVER, 2, reply, true);
732 if(num_processed_meshes > 0)
733 g_profiler->graphAdd("num_processed_meshes", num_processed_meshes);
739 if (m_media_downloader && m_media_downloader->isStarted()) {
740 m_media_downloader->step(this);
741 if (m_media_downloader->isDone()) {
743 delete m_media_downloader;
744 m_media_downloader = NULL;
749 If the server didn't update the inventory in a while, revert
750 the local inventory (so the player notices the lag problem
751 and knows something is wrong).
753 if(m_inventory_from_server)
755 float interval = 10.0;
756 float count_before = floor(m_inventory_from_server_age / interval);
758 m_inventory_from_server_age += dtime;
760 float count_after = floor(m_inventory_from_server_age / interval);
762 if(count_after != count_before)
764 // Do this every <interval> seconds after TOCLIENT_INVENTORY
765 // Reset the locally changed inventory to the authoritative inventory
766 Player *player = m_env.getLocalPlayer();
767 player->inventory = *m_inventory_from_server;
768 m_inventory_updated = true;
773 Update positions of sounds attached to objects
776 for(std::map<int, u16>::iterator
777 i = m_sounds_to_objects.begin();
778 i != m_sounds_to_objects.end(); i++)
780 int client_id = i->first;
781 u16 object_id = i->second;
782 ClientActiveObject *cao = m_env.getActiveObject(object_id);
785 v3f pos = cao->getPosition();
786 m_sound->updateSoundPosition(client_id, pos);
791 Handle removed remotely initiated sounds
793 m_removed_sounds_check_timer += dtime;
794 if(m_removed_sounds_check_timer >= 2.32)
796 m_removed_sounds_check_timer = 0;
797 // Find removed sounds and clear references to them
798 std::set<s32> removed_server_ids;
799 for(std::map<s32, int>::iterator
800 i = m_sounds_server_to_client.begin();
801 i != m_sounds_server_to_client.end();)
803 s32 server_id = i->first;
804 int client_id = i->second;
806 if(!m_sound->soundExists(client_id)){
807 m_sounds_server_to_client.erase(server_id);
808 m_sounds_client_to_server.erase(client_id);
809 m_sounds_to_objects.erase(client_id);
810 removed_server_ids.insert(server_id);
814 if(removed_server_ids.size() != 0)
816 std::ostringstream os(std::ios_base::binary);
817 writeU16(os, TOSERVER_REMOVED_SOUNDS);
818 size_t server_ids = removed_server_ids.size();
819 assert(server_ids <= 0xFFFF);
820 writeU16(os, (u16) (server_ids & 0xFFFF));
821 for(std::set<s32>::iterator i = removed_server_ids.begin();
822 i != removed_server_ids.end(); i++)
824 std::string s = os.str();
825 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
832 bool Client::loadMedia(const std::string &data, const std::string &filename)
834 // Silly irrlicht's const-incorrectness
835 Buffer<char> data_rw(data.c_str(), data.size());
839 const char *image_ext[] = {
840 ".png", ".jpg", ".bmp", ".tga",
841 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
844 name = removeStringEnd(filename, image_ext);
847 verbosestream<<"Client: Attempting to load image "
848 <<"file \""<<filename<<"\""<<std::endl;
850 io::IFileSystem *irrfs = m_device->getFileSystem();
851 video::IVideoDriver *vdrv = m_device->getVideoDriver();
853 // Create an irrlicht memory file
854 io::IReadFile *rfile = irrfs->createMemoryReadFile(
855 *data_rw, data_rw.getSize(), "_tempreadfile");
858 video::IImage *img = vdrv->createImageFromFile(rfile);
860 errorstream<<"Client: Cannot create image from data of "
861 <<"file \""<<filename<<"\""<<std::endl;
866 m_tsrc->insertSourceImage(filename, img);
873 const char *sound_ext[] = {
874 ".0.ogg", ".1.ogg", ".2.ogg", ".3.ogg", ".4.ogg",
875 ".5.ogg", ".6.ogg", ".7.ogg", ".8.ogg", ".9.ogg",
878 name = removeStringEnd(filename, sound_ext);
881 verbosestream<<"Client: Attempting to load sound "
882 <<"file \""<<filename<<"\""<<std::endl;
883 m_sound->loadSoundData(name, data);
887 const char *model_ext[] = {
888 ".x", ".b3d", ".md2", ".obj",
891 name = removeStringEnd(filename, model_ext);
894 verbosestream<<"Client: Storing model into memory: "
895 <<"\""<<filename<<"\""<<std::endl;
896 if(m_mesh_data.count(filename))
897 errorstream<<"Multiple models with name \""<<filename.c_str()
898 <<"\" found; replacing previous model"<<std::endl;
899 m_mesh_data[filename] = data;
903 errorstream<<"Client: Don't know how to load file \""
904 <<filename<<"\""<<std::endl;
908 // Virtual methods from con::PeerHandler
909 void Client::peerAdded(con::Peer *peer)
911 infostream<<"Client::peerAdded(): peer->id="
912 <<peer->id<<std::endl;
914 void Client::deletingPeer(con::Peer *peer, bool timeout)
916 infostream<<"Client::deletingPeer(): "
917 "Server Peer is getting deleted "
918 <<"(timeout="<<timeout<<")"<<std::endl;
923 u16 number of files requested
929 void Client::request_media(const std::list<std::string> &file_requests)
931 std::ostringstream os(std::ios_base::binary);
932 writeU16(os, TOSERVER_REQUEST_MEDIA);
933 size_t file_requests_size = file_requests.size();
934 assert(file_requests_size <= 0xFFFF);
935 writeU16(os, (u16) (file_requests_size & 0xFFFF));
937 for(std::list<std::string>::const_iterator i = file_requests.begin();
938 i != file_requests.end(); ++i) {
939 os<<serializeString(*i);
943 std::string s = os.str();
944 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
947 infostream<<"Client: Sending media request list to server ("
948 <<file_requests.size()<<" files)"<<std::endl;
951 void Client::received_media()
953 // notify server we received everything
954 std::ostringstream os(std::ios_base::binary);
955 writeU16(os, TOSERVER_RECEIVED_MEDIA);
956 std::string s = os.str();
957 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
960 infostream<<"Client: Notifying server that we received all media"
964 void Client::ReceiveAll()
966 DSTACK(__FUNCTION_NAME);
967 u32 start_ms = porting::getTimeMs();
970 // Limit time even if there would be huge amounts of data to
972 if(porting::getTimeMs() > start_ms + 100)
977 g_profiler->graphAdd("client_received_packets", 1);
979 catch(con::NoIncomingDataException &e)
983 catch(con::InvalidIncomingDataException &e)
985 infostream<<"Client::ReceiveAll(): "
986 "InvalidIncomingDataException: what()="
987 <<e.what()<<std::endl;
992 void Client::Receive()
994 DSTACK(__FUNCTION_NAME);
995 SharedBuffer<u8> data;
997 u32 datasize = m_con.Receive(sender_peer_id, data);
998 ProcessData(*data, datasize, sender_peer_id);
1002 sender_peer_id given to this shall be quaranteed to be a valid peer
1004 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
1006 DSTACK(__FUNCTION_NAME);
1008 // Ignore packets that don't even fit a command
1011 m_packetcounter.add(60000);
1015 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
1017 //infostream<<"Client: received command="<<command<<std::endl;
1018 m_packetcounter.add((u16)command);
1021 If this check is removed, be sure to change the queue
1022 system to know the ids
1024 if(sender_peer_id != PEER_ID_SERVER)
1026 infostream<<"Client::ProcessData(): Discarding data not "
1027 "coming from server: peer_id="<<sender_peer_id
1032 u8 ser_version = m_server_ser_ver;
1034 if(command == TOCLIENT_INIT)
1039 u8 deployed = data[2];
1041 infostream<<"Client: TOCLIENT_INIT received with "
1042 "deployed="<<((int)deployed&0xff)<<std::endl;
1044 if(!ser_ver_supported(deployed))
1046 infostream<<"Client: TOCLIENT_INIT: Server sent "
1047 <<"unsupported ser_fmt_ver"<<std::endl;
1051 m_server_ser_ver = deployed;
1053 // Get player position
1054 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
1055 if(datasize >= 2+1+6)
1056 playerpos_s16 = readV3S16(&data[2+1]);
1057 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
1060 // Set player position
1061 Player *player = m_env.getLocalPlayer();
1062 assert(player != NULL);
1063 player->setPosition(playerpos_f);
1065 if(datasize >= 2+1+6+8)
1068 m_map_seed = readU64(&data[2+1+6]);
1069 infostream<<"Client: received map seed: "<<m_map_seed<<std::endl;
1072 if(datasize >= 2+1+6+8+4)
1075 m_recommended_send_interval = readF1000(&data[2+1+6+8]);
1076 infostream<<"Client: received recommended send interval "
1077 <<m_recommended_send_interval<<std::endl;
1082 SharedBuffer<u8> reply(replysize);
1083 writeU16(&reply[0], TOSERVER_INIT2);
1085 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1092 if(command == TOCLIENT_ACCESS_DENIED)
1094 // The server didn't like our password. Note, this needs
1095 // to be processed even if the serialisation format has
1096 // not been agreed yet, the same as TOCLIENT_INIT.
1097 m_access_denied = true;
1098 m_access_denied_reason = L"Unknown";
1101 std::string datastring((char*)&data[2], datasize-2);
1102 std::istringstream is(datastring, std::ios_base::binary);
1103 m_access_denied_reason = deSerializeWideString(is);
1108 if(ser_version == SER_FMT_VER_INVALID)
1110 infostream<<"Client: Server serialization"
1111 " format invalid or not initialized."
1112 " Skipping incoming command="<<command<<std::endl;
1117 Handle runtime commands
1119 // there's no sane reason why we shouldn't have a player and
1120 // almost everyone needs a player reference
1121 Player *player = m_env.getLocalPlayer();
1122 assert(player != NULL);
1124 if(command == TOCLIENT_REMOVENODE)
1129 p.X = readS16(&data[2]);
1130 p.Y = readS16(&data[4]);
1131 p.Z = readS16(&data[6]);
1134 else if(command == TOCLIENT_ADDNODE)
1136 if(datasize < 8 + MapNode::serializedLength(ser_version))
1140 p.X = readS16(&data[2]);
1141 p.Y = readS16(&data[4]);
1142 p.Z = readS16(&data[6]);
1145 n.deSerialize(&data[8], ser_version);
1147 bool remove_metadata = true;
1148 u32 index = 8 + MapNode::serializedLength(ser_version);
1149 if ((datasize >= index+1) && data[index]){
1150 remove_metadata = false;
1153 addNode(p, n, remove_metadata);
1155 else if(command == TOCLIENT_BLOCKDATA)
1157 // Ignore too small packet
1162 p.X = readS16(&data[2]);
1163 p.Y = readS16(&data[4]);
1164 p.Z = readS16(&data[6]);
1166 std::string datastring((char*)&data[8], datasize-8);
1167 std::istringstream istr(datastring, std::ios_base::binary);
1172 v2s16 p2d(p.X, p.Z);
1173 sector = m_env.getMap().emergeSector(p2d);
1175 assert(sector->getPos() == p2d);
1177 block = sector->getBlockNoCreateNoEx(p.Y);
1181 Update an existing block
1183 block->deSerialize(istr, ser_version, false);
1184 block->deSerializeNetworkSpecific(istr);
1191 block = new MapBlock(&m_env.getMap(), p, this);
1192 block->deSerialize(istr, ser_version, false);
1193 block->deSerializeNetworkSpecific(istr);
1194 sector->insertBlock(block);
1197 if (localdb != NULL) {
1198 ((ServerMap&) localserver->getMap()).saveBlock(block, localdb);
1202 Add it to mesh update queue and set it to be acknowledged after update.
1204 addUpdateMeshTaskWithEdge(p, true);
1206 else if(command == TOCLIENT_INVENTORY)
1211 std::string datastring((char*)&data[2], datasize-2);
1212 std::istringstream is(datastring, std::ios_base::binary);
1214 player->inventory.deSerialize(is);
1216 m_inventory_updated = true;
1218 delete m_inventory_from_server;
1219 m_inventory_from_server = new Inventory(player->inventory);
1220 m_inventory_from_server_age = 0.0;
1223 else if(command == TOCLIENT_TIME_OF_DAY)
1228 u16 time_of_day = readU16(&data[2]);
1229 time_of_day = time_of_day % 24000;
1230 float time_speed = 0;
1232 if(datasize >= 2 + 2 + 4)
1234 time_speed = readF1000(&data[4]);
1237 // Old message; try to approximate speed of time by ourselves
1238 float time_of_day_f = (float)time_of_day / 24000.0;
1239 float tod_diff_f = 0;
1241 if(time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
1242 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0;
1244 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
1246 m_last_time_of_day_f = time_of_day_f;
1247 float time_diff = m_time_of_day_update_timer;
1248 m_time_of_day_update_timer = 0;
1250 if(m_time_of_day_set){
1251 time_speed = (3600.0*24.0) * tod_diff_f / time_diff;
1252 infostream<<"Client: Measured time_of_day speed (old format): "
1253 <<time_speed<<" tod_diff_f="<<tod_diff_f
1254 <<" time_diff="<<time_diff<<std::endl;
1258 // Update environment
1259 m_env.setTimeOfDay(time_of_day);
1260 m_env.setTimeOfDaySpeed(time_speed);
1261 m_time_of_day_set = true;
1263 u32 dr = m_env.getDayNightRatio();
1264 infostream<<"Client: time_of_day="<<time_of_day
1265 <<" time_speed="<<time_speed
1266 <<" dr="<<dr<<std::endl;
1268 else if(command == TOCLIENT_CHAT_MESSAGE)
1276 std::string datastring((char*)&data[2], datasize-2);
1277 std::istringstream is(datastring, std::ios_base::binary);
1280 is.read((char*) buf, 2);
1281 u16 len = readU16(buf);
1283 std::wstring message;
1284 for(unsigned int i=0; i<len; i++)
1286 is.read((char*)buf, 2);
1287 message += (wchar_t)readU16(buf);
1290 m_chat_queue.push_back(message);
1292 else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1296 u16 count of removed objects
1297 for all removed objects {
1300 u16 count of added objects
1301 for all added objects {
1304 u32 initialization data length
1305 string initialization data
1310 // Get all data except the command number
1311 std::string datastring((char*)&data[2], datasize-2);
1312 // Throw them in an istringstream
1313 std::istringstream is(datastring, std::ios_base::binary);
1315 // Read removed objects
1317 u16 removed_count = readU16((u8*)buf);
1318 for(unsigned int i=0; i<removed_count; i++)
1321 u16 id = readU16((u8*)buf);
1322 m_env.removeActiveObject(id);
1325 // Read added objects
1327 u16 added_count = readU16((u8*)buf);
1328 for(unsigned int i=0; i<added_count; i++)
1331 u16 id = readU16((u8*)buf);
1333 u8 type = readU8((u8*)buf);
1334 std::string data = deSerializeLongString(is);
1336 m_env.addActiveObject(id, type, data);
1339 else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1351 // Get all data except the command number
1352 std::string datastring((char*)&data[2], datasize-2);
1353 // Throw them in an istringstream
1354 std::istringstream is(datastring, std::ios_base::binary);
1356 while(is.eof() == false)
1359 u16 id = readU16((u8*)buf);
1363 size_t message_size = readU16((u8*)buf);
1364 std::string message;
1365 message.reserve(message_size);
1366 for(unsigned int i=0; i<message_size; i++)
1369 message.append(buf, 1);
1371 // Pass on to the environment
1372 m_env.processActiveObjectMessage(id, message);
1375 else if(command == TOCLIENT_MOVEMENT)
1377 std::string datastring((char*)&data[2], datasize-2);
1378 std::istringstream is(datastring, std::ios_base::binary);
1380 player->movement_acceleration_default = readF1000(is) * BS;
1381 player->movement_acceleration_air = readF1000(is) * BS;
1382 player->movement_acceleration_fast = readF1000(is) * BS;
1383 player->movement_speed_walk = readF1000(is) * BS;
1384 player->movement_speed_crouch = readF1000(is) * BS;
1385 player->movement_speed_fast = readF1000(is) * BS;
1386 player->movement_speed_climb = readF1000(is) * BS;
1387 player->movement_speed_jump = readF1000(is) * BS;
1388 player->movement_liquid_fluidity = readF1000(is) * BS;
1389 player->movement_liquid_fluidity_smooth = readF1000(is) * BS;
1390 player->movement_liquid_sink = readF1000(is) * BS;
1391 player->movement_gravity = readF1000(is) * BS;
1393 else if(command == TOCLIENT_HP)
1395 std::string datastring((char*)&data[2], datasize-2);
1396 std::istringstream is(datastring, std::ios_base::binary);
1398 u8 oldhp = player->hp;
1404 // Add to ClientEvent queue
1406 event.type = CE_PLAYER_DAMAGE;
1407 event.player_damage.amount = oldhp - hp;
1408 m_client_event_queue.push_back(event);
1411 else if(command == TOCLIENT_BREATH)
1413 std::string datastring((char*)&data[2], datasize-2);
1414 std::istringstream is(datastring, std::ios_base::binary);
1416 player->setBreath(readU16(is));
1418 else if(command == TOCLIENT_MOVE_PLAYER)
1420 std::string datastring((char*)&data[2], datasize-2);
1421 std::istringstream is(datastring, std::ios_base::binary);
1423 v3f pos = readV3F1000(is);
1424 f32 pitch = readF1000(is);
1425 f32 yaw = readF1000(is);
1426 player->setPosition(pos);
1428 infostream<<"Client got TOCLIENT_MOVE_PLAYER"
1429 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1435 Add to ClientEvent queue.
1436 This has to be sent to the main program because otherwise
1437 it would just force the pitch and yaw values to whatever
1438 the camera points to.
1441 event.type = CE_PLAYER_FORCE_MOVE;
1442 event.player_force_move.pitch = pitch;
1443 event.player_force_move.yaw = yaw;
1444 m_client_event_queue.push_back(event);
1446 // Ignore damage for a few seconds, so that the player doesn't
1447 // get damage from falling on ground
1448 m_ignore_damage_timer = 3.0;
1450 else if(command == TOCLIENT_PLAYERITEM)
1452 infostream<<"Client: WARNING: Ignoring TOCLIENT_PLAYERITEM"<<std::endl;
1454 else if(command == TOCLIENT_DEATHSCREEN)
1456 std::string datastring((char*)&data[2], datasize-2);
1457 std::istringstream is(datastring, std::ios_base::binary);
1459 bool set_camera_point_target = readU8(is);
1460 v3f camera_point_target = readV3F1000(is);
1463 event.type = CE_DEATHSCREEN;
1464 event.deathscreen.set_camera_point_target = set_camera_point_target;
1465 event.deathscreen.camera_point_target_x = camera_point_target.X;
1466 event.deathscreen.camera_point_target_y = camera_point_target.Y;
1467 event.deathscreen.camera_point_target_z = camera_point_target.Z;
1468 m_client_event_queue.push_back(event);
1470 else if(command == TOCLIENT_ANNOUNCE_MEDIA)
1472 std::string datastring((char*)&data[2], datasize-2);
1473 std::istringstream is(datastring, std::ios_base::binary);
1475 int num_files = readU16(is);
1477 infostream<<"Client: Received media announcement: packet size: "
1478 <<datasize<<std::endl;
1480 if (m_media_downloader == NULL ||
1481 m_media_downloader->isStarted()) {
1482 const char *problem = m_media_downloader ?
1483 "we already saw another announcement" :
1484 "all media has been received already";
1485 errorstream<<"Client: Received media announcement but "
1487 <<" files="<<num_files
1488 <<" size="<<datasize<<std::endl;
1492 // Mesh update thread must be stopped while
1493 // updating content definitions
1494 assert(!m_mesh_update_thread.IsRunning());
1496 for(int i=0; i<num_files; i++)
1498 std::string name = deSerializeString(is);
1499 std::string sha1_base64 = deSerializeString(is);
1500 std::string sha1_raw = base64_decode(sha1_base64);
1501 m_media_downloader->addFile(name, sha1_raw);
1504 std::vector<std::string> remote_media;
1506 Strfnd sf(deSerializeString(is));
1507 while(!sf.atend()) {
1508 std::string baseurl = trim(sf.next(","));
1510 m_media_downloader->addRemoteServer(baseurl);
1513 catch(SerializationError& e) {
1514 // not supported by server or turned off
1517 m_media_downloader->step(this);
1519 else if(command == TOCLIENT_MEDIA)
1521 std::string datastring((char*)&data[2], datasize-2);
1522 std::istringstream is(datastring, std::ios_base::binary);
1526 u16 total number of file bunches
1527 u16 index of this bunch
1528 u32 number of files in this bunch
1536 int num_bunches = readU16(is);
1537 int bunch_i = readU16(is);
1538 u32 num_files = readU32(is);
1539 infostream<<"Client: Received files: bunch "<<bunch_i<<"/"
1540 <<num_bunches<<" files="<<num_files
1541 <<" size="<<datasize<<std::endl;
1546 if (m_media_downloader == NULL ||
1547 !m_media_downloader->isStarted()) {
1548 const char *problem = m_media_downloader ?
1549 "media has not been requested" :
1550 "all media has been received already";
1551 errorstream<<"Client: Received media but "
1553 <<" bunch "<<bunch_i<<"/"<<num_bunches
1554 <<" files="<<num_files
1555 <<" size="<<datasize<<std::endl;
1559 // Mesh update thread must be stopped while
1560 // updating content definitions
1561 assert(!m_mesh_update_thread.IsRunning());
1563 for(unsigned int i=0; i<num_files; i++){
1564 std::string name = deSerializeString(is);
1565 std::string data = deSerializeLongString(is);
1566 m_media_downloader->conventionalTransferDone(
1570 else if(command == TOCLIENT_TOOLDEF)
1572 infostream<<"Client: WARNING: Ignoring TOCLIENT_TOOLDEF"<<std::endl;
1574 else if(command == TOCLIENT_NODEDEF)
1576 infostream<<"Client: Received node definitions: packet size: "
1577 <<datasize<<std::endl;
1579 // Mesh update thread must be stopped while
1580 // updating content definitions
1581 assert(!m_mesh_update_thread.IsRunning());
1583 // Decompress node definitions
1584 std::string datastring((char*)&data[2], datasize-2);
1585 std::istringstream is(datastring, std::ios_base::binary);
1586 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1587 std::ostringstream tmp_os;
1588 decompressZlib(tmp_is, tmp_os);
1590 // Deserialize node definitions
1591 std::istringstream tmp_is2(tmp_os.str());
1592 m_nodedef->deSerialize(tmp_is2);
1593 m_nodedef_received = true;
1595 else if(command == TOCLIENT_CRAFTITEMDEF)
1597 infostream<<"Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF"<<std::endl;
1599 else if(command == TOCLIENT_ITEMDEF)
1601 infostream<<"Client: Received item definitions: packet size: "
1602 <<datasize<<std::endl;
1604 // Mesh update thread must be stopped while
1605 // updating content definitions
1606 assert(!m_mesh_update_thread.IsRunning());
1608 // Decompress item definitions
1609 std::string datastring((char*)&data[2], datasize-2);
1610 std::istringstream is(datastring, std::ios_base::binary);
1611 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1612 std::ostringstream tmp_os;
1613 decompressZlib(tmp_is, tmp_os);
1615 // Deserialize node definitions
1616 std::istringstream tmp_is2(tmp_os.str());
1617 m_itemdef->deSerialize(tmp_is2);
1618 m_itemdef_received = true;
1620 else if(command == TOCLIENT_PLAY_SOUND)
1622 std::string datastring((char*)&data[2], datasize-2);
1623 std::istringstream is(datastring, std::ios_base::binary);
1625 s32 server_id = readS32(is);
1626 std::string name = deSerializeString(is);
1627 float gain = readF1000(is);
1628 int type = readU8(is); // 0=local, 1=positional, 2=object
1629 v3f pos = readV3F1000(is);
1630 u16 object_id = readU16(is);
1631 bool loop = readU8(is);
1636 client_id = m_sound->playSound(name, loop, gain);
1638 case 1: // positional
1639 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1642 ClientActiveObject *cao = m_env.getActiveObject(object_id);
1644 pos = cao->getPosition();
1645 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1646 // TODO: Set up sound to move with object
1651 if(client_id != -1){
1652 m_sounds_server_to_client[server_id] = client_id;
1653 m_sounds_client_to_server[client_id] = server_id;
1655 m_sounds_to_objects[client_id] = object_id;
1658 else if(command == TOCLIENT_STOP_SOUND)
1660 std::string datastring((char*)&data[2], datasize-2);
1661 std::istringstream is(datastring, std::ios_base::binary);
1663 s32 server_id = readS32(is);
1664 std::map<s32, int>::iterator i =
1665 m_sounds_server_to_client.find(server_id);
1666 if(i != m_sounds_server_to_client.end()){
1667 int client_id = i->second;
1668 m_sound->stopSound(client_id);
1671 else if(command == TOCLIENT_PRIVILEGES)
1673 std::string datastring((char*)&data[2], datasize-2);
1674 std::istringstream is(datastring, std::ios_base::binary);
1676 m_privileges.clear();
1677 infostream<<"Client: Privileges updated: ";
1678 u16 num_privileges = readU16(is);
1679 for(unsigned int i=0; i<num_privileges; i++){
1680 std::string priv = deSerializeString(is);
1681 m_privileges.insert(priv);
1682 infostream<<priv<<" ";
1684 infostream<<std::endl;
1686 else if(command == TOCLIENT_INVENTORY_FORMSPEC)
1688 std::string datastring((char*)&data[2], datasize-2);
1689 std::istringstream is(datastring, std::ios_base::binary);
1691 // Store formspec in LocalPlayer
1692 player->inventory_formspec = deSerializeLongString(is);
1694 else if(command == TOCLIENT_DETACHED_INVENTORY)
1696 std::string datastring((char*)&data[2], datasize-2);
1697 std::istringstream is(datastring, std::ios_base::binary);
1699 std::string name = deSerializeString(is);
1701 infostream<<"Client: Detached inventory update: \""<<name<<"\""<<std::endl;
1703 Inventory *inv = NULL;
1704 if(m_detached_inventories.count(name) > 0)
1705 inv = m_detached_inventories[name];
1707 inv = new Inventory(m_itemdef);
1708 m_detached_inventories[name] = inv;
1710 inv->deSerialize(is);
1712 else if(command == TOCLIENT_SHOW_FORMSPEC)
1714 std::string datastring((char*)&data[2], datasize-2);
1715 std::istringstream is(datastring, std::ios_base::binary);
1717 std::string formspec = deSerializeLongString(is);
1718 std::string formname = deSerializeString(is);
1721 event.type = CE_SHOW_FORMSPEC;
1722 // pointer is required as event is a struct only!
1723 // adding a std:string to a struct isn't possible
1724 event.show_formspec.formspec = new std::string(formspec);
1725 event.show_formspec.formname = new std::string(formname);
1726 m_client_event_queue.push_back(event);
1728 else if(command == TOCLIENT_SPAWN_PARTICLE)
1730 std::string datastring((char*)&data[2], datasize-2);
1731 std::istringstream is(datastring, std::ios_base::binary);
1733 v3f pos = readV3F1000(is);
1734 v3f vel = readV3F1000(is);
1735 v3f acc = readV3F1000(is);
1736 float expirationtime = readF1000(is);
1737 float size = readF1000(is);
1738 bool collisiondetection = readU8(is);
1739 std::string texture = deSerializeLongString(is);
1740 bool vertical = false;
1742 vertical = readU8(is);
1746 event.type = CE_SPAWN_PARTICLE;
1747 event.spawn_particle.pos = new v3f (pos);
1748 event.spawn_particle.vel = new v3f (vel);
1749 event.spawn_particle.acc = new v3f (acc);
1750 event.spawn_particle.expirationtime = expirationtime;
1751 event.spawn_particle.size = size;
1752 event.spawn_particle.collisiondetection = collisiondetection;
1753 event.spawn_particle.vertical = vertical;
1754 event.spawn_particle.texture = new std::string(texture);
1756 m_client_event_queue.push_back(event);
1758 else if(command == TOCLIENT_ADD_PARTICLESPAWNER)
1760 std::string datastring((char*)&data[2], datasize-2);
1761 std::istringstream is(datastring, std::ios_base::binary);
1763 u16 amount = readU16(is);
1764 float spawntime = readF1000(is);
1765 v3f minpos = readV3F1000(is);
1766 v3f maxpos = readV3F1000(is);
1767 v3f minvel = readV3F1000(is);
1768 v3f maxvel = readV3F1000(is);
1769 v3f minacc = readV3F1000(is);
1770 v3f maxacc = readV3F1000(is);
1771 float minexptime = readF1000(is);
1772 float maxexptime = readF1000(is);
1773 float minsize = readF1000(is);
1774 float maxsize = readF1000(is);
1775 bool collisiondetection = readU8(is);
1776 std::string texture = deSerializeLongString(is);
1777 u32 id = readU32(is);
1778 bool vertical = false;
1780 vertical = readU8(is);
1784 event.type = CE_ADD_PARTICLESPAWNER;
1785 event.add_particlespawner.amount = amount;
1786 event.add_particlespawner.spawntime = spawntime;
1787 event.add_particlespawner.minpos = new v3f (minpos);
1788 event.add_particlespawner.maxpos = new v3f (maxpos);
1789 event.add_particlespawner.minvel = new v3f (minvel);
1790 event.add_particlespawner.maxvel = new v3f (maxvel);
1791 event.add_particlespawner.minacc = new v3f (minacc);
1792 event.add_particlespawner.maxacc = new v3f (maxacc);
1793 event.add_particlespawner.minexptime = minexptime;
1794 event.add_particlespawner.maxexptime = maxexptime;
1795 event.add_particlespawner.minsize = minsize;
1796 event.add_particlespawner.maxsize = maxsize;
1797 event.add_particlespawner.collisiondetection = collisiondetection;
1798 event.add_particlespawner.vertical = vertical;
1799 event.add_particlespawner.texture = new std::string(texture);
1800 event.add_particlespawner.id = id;
1802 m_client_event_queue.push_back(event);
1804 else if(command == TOCLIENT_DELETE_PARTICLESPAWNER)
1806 std::string datastring((char*)&data[2], datasize-2);
1807 std::istringstream is(datastring, std::ios_base::binary);
1809 u32 id = readU16(is);
1812 event.type = CE_DELETE_PARTICLESPAWNER;
1813 event.delete_particlespawner.id = id;
1815 m_client_event_queue.push_back(event);
1817 else if(command == TOCLIENT_HUDADD)
1819 std::string datastring((char *)&data[2], datasize - 2);
1820 std::istringstream is(datastring, std::ios_base::binary);
1822 u32 id = readU32(is);
1823 u8 type = readU8(is);
1824 v2f pos = readV2F1000(is);
1825 std::string name = deSerializeString(is);
1826 v2f scale = readV2F1000(is);
1827 std::string text = deSerializeString(is);
1828 u32 number = readU32(is);
1829 u32 item = readU32(is);
1830 u32 dir = readU32(is);
1831 v2f align = readV2F1000(is);
1832 v2f offset = readV2F1000(is);
1836 world_pos = readV3F1000(is);
1837 }catch(SerializationError &e) {};
1839 size = readV2S32(is);
1840 } catch(SerializationError &e) {};
1843 event.type = CE_HUDADD;
1844 event.hudadd.id = id;
1845 event.hudadd.type = type;
1846 event.hudadd.pos = new v2f(pos);
1847 event.hudadd.name = new std::string(name);
1848 event.hudadd.scale = new v2f(scale);
1849 event.hudadd.text = new std::string(text);
1850 event.hudadd.number = number;
1851 event.hudadd.item = item;
1852 event.hudadd.dir = dir;
1853 event.hudadd.align = new v2f(align);
1854 event.hudadd.offset = new v2f(offset);
1855 event.hudadd.world_pos = new v3f(world_pos);
1856 event.hudadd.size = new v2s32(size);
1857 m_client_event_queue.push_back(event);
1859 else if(command == TOCLIENT_HUDRM)
1861 std::string datastring((char *)&data[2], datasize - 2);
1862 std::istringstream is(datastring, std::ios_base::binary);
1864 u32 id = readU32(is);
1867 event.type = CE_HUDRM;
1868 event.hudrm.id = id;
1869 m_client_event_queue.push_back(event);
1871 else if(command == TOCLIENT_HUDCHANGE)
1879 std::string datastring((char *)&data[2], datasize - 2);
1880 std::istringstream is(datastring, std::ios_base::binary);
1882 u32 id = readU32(is);
1883 u8 stat = (HudElementStat)readU8(is);
1885 if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
1886 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
1887 v2fdata = readV2F1000(is);
1888 else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
1889 sdata = deSerializeString(is);
1890 else if (stat == HUD_STAT_WORLD_POS)
1891 v3fdata = readV3F1000(is);
1892 else if (stat == HUD_STAT_SIZE )
1893 v2s32data = readV2S32(is);
1895 intdata = readU32(is);
1898 event.type = CE_HUDCHANGE;
1899 event.hudchange.id = id;
1900 event.hudchange.stat = (HudElementStat)stat;
1901 event.hudchange.v2fdata = new v2f(v2fdata);
1902 event.hudchange.v3fdata = new v3f(v3fdata);
1903 event.hudchange.sdata = new std::string(sdata);
1904 event.hudchange.data = intdata;
1905 event.hudchange.v2s32data = new v2s32(v2s32data);
1906 m_client_event_queue.push_back(event);
1908 else if(command == TOCLIENT_HUD_SET_FLAGS)
1910 std::string datastring((char *)&data[2], datasize - 2);
1911 std::istringstream is(datastring, std::ios_base::binary);
1913 u32 flags = readU32(is);
1914 u32 mask = readU32(is);
1916 player->hud_flags &= ~mask;
1917 player->hud_flags |= flags;
1919 else if(command == TOCLIENT_HUD_SET_PARAM)
1921 std::string datastring((char *)&data[2], datasize - 2);
1922 std::istringstream is(datastring, std::ios_base::binary);
1924 u16 param = readU16(is);
1925 std::string value = deSerializeString(is);
1927 if(param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) {
1928 s32 hotbar_itemcount = readS32((u8*) value.c_str());
1929 if(hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
1930 player->hud_hotbar_itemcount = hotbar_itemcount;
1932 else if (param == HUD_PARAM_HOTBAR_IMAGE) {
1933 ((LocalPlayer *) player)->hotbar_image = value;
1935 else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
1936 ((LocalPlayer *) player)->hotbar_selected_image = value;
1939 else if(command == TOCLIENT_SET_SKY)
1941 std::string datastring((char *)&data[2], datasize - 2);
1942 std::istringstream is(datastring, std::ios_base::binary);
1944 video::SColor *bgcolor = new video::SColor(readARGB8(is));
1945 std::string *type = new std::string(deSerializeString(is));
1946 u16 count = readU16(is);
1947 std::vector<std::string> *params = new std::vector<std::string>;
1949 for(size_t i=0; i<count; i++)
1950 params->push_back(deSerializeString(is));
1953 event.type = CE_SET_SKY;
1954 event.set_sky.bgcolor = bgcolor;
1955 event.set_sky.type = type;
1956 event.set_sky.params = params;
1957 m_client_event_queue.push_back(event);
1959 else if(command == TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO)
1961 std::string datastring((char *)&data[2], datasize - 2);
1962 std::istringstream is(datastring, std::ios_base::binary);
1964 bool do_override = readU8(is);
1965 float day_night_ratio_f = (float)readU16(is) / 65536;
1968 event.type = CE_OVERRIDE_DAY_NIGHT_RATIO;
1969 event.override_day_night_ratio.do_override = do_override;
1970 event.override_day_night_ratio.ratio_f = day_night_ratio_f;
1971 m_client_event_queue.push_back(event);
1973 else if(command == TOCLIENT_LOCAL_PLAYER_ANIMATIONS)
1975 std::string datastring((char *)&data[2], datasize - 2);
1976 std::istringstream is(datastring, std::ios_base::binary);
1978 LocalPlayer *player = m_env.getLocalPlayer();
1979 assert(player != NULL);
1981 player->local_animations[0] = readV2S32(is);
1982 player->local_animations[1] = readV2S32(is);
1983 player->local_animations[2] = readV2S32(is);
1984 player->local_animations[3] = readV2S32(is);
1985 player->local_animation_speed = readF1000(is);
1987 else if(command == TOCLIENT_EYE_OFFSET)
1989 std::string datastring((char *)&data[2], datasize - 2);
1990 std::istringstream is(datastring, std::ios_base::binary);
1992 LocalPlayer *player = m_env.getLocalPlayer();
1993 assert(player != NULL);
1995 player->eye_offset_first = readV3F1000(is);
1996 player->eye_offset_third = readV3F1000(is);
2000 infostream<<"Client: Ignoring unknown command "
2001 <<command<<std::endl;
2005 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
2007 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2008 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
2011 void Client::interact(u8 action, const PointedThing& pointed)
2013 if(m_state != LC_Ready){
2014 infostream<<"Client::interact() "
2015 "cancelled (not connected)"
2020 std::ostringstream os(std::ios_base::binary);
2026 [5] u32 length of the next item
2027 [9] serialized PointedThing
2029 0: start digging (from undersurface) or use
2030 1: stop digging (all parameters ignored)
2031 2: digging completed
2032 3: place block or item (to abovesurface)
2035 writeU16(os, TOSERVER_INTERACT);
2036 writeU8(os, action);
2037 writeU16(os, getPlayerItem());
2038 std::ostringstream tmp_os(std::ios::binary);
2039 pointed.serialize(tmp_os);
2040 os<<serializeLongString(tmp_os.str());
2042 std::string s = os.str();
2043 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2046 Send(0, data, true);
2049 void Client::sendNodemetaFields(v3s16 p, const std::string &formname,
2050 const std::map<std::string, std::string> &fields)
2052 std::ostringstream os(std::ios_base::binary);
2054 writeU16(os, TOSERVER_NODEMETA_FIELDS);
2056 os<<serializeString(formname);
2057 size_t fields_size = fields.size();
2058 assert(fields_size <= 0xFFFF);
2059 writeU16(os, (u16) (fields_size & 0xFFFF));
2060 for(std::map<std::string, std::string>::const_iterator
2061 i = fields.begin(); i != fields.end(); i++){
2062 const std::string &name = i->first;
2063 const std::string &value = i->second;
2064 os<<serializeString(name);
2065 os<<serializeLongString(value);
2069 std::string s = os.str();
2070 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2072 Send(0, data, true);
2075 void Client::sendInventoryFields(const std::string &formname,
2076 const std::map<std::string, std::string> &fields)
2078 std::ostringstream os(std::ios_base::binary);
2080 writeU16(os, TOSERVER_INVENTORY_FIELDS);
2081 os<<serializeString(formname);
2082 size_t fields_size = fields.size();
2083 assert(fields_size <= 0xFFFF);
2084 writeU16(os, (u16) (fields_size & 0xFFFF));
2085 for(std::map<std::string, std::string>::const_iterator
2086 i = fields.begin(); i != fields.end(); i++){
2087 const std::string &name = i->first;
2088 const std::string &value = i->second;
2089 os<<serializeString(name);
2090 os<<serializeLongString(value);
2094 std::string s = os.str();
2095 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2097 Send(0, data, true);
2100 void Client::sendInventoryAction(InventoryAction *a)
2102 std::ostringstream os(std::ios_base::binary);
2106 writeU16(buf, TOSERVER_INVENTORY_ACTION);
2107 os.write((char*)buf, 2);
2112 std::string s = os.str();
2113 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2115 Send(0, data, true);
2118 void Client::sendChatMessage(const std::wstring &message)
2120 std::ostringstream os(std::ios_base::binary);
2124 writeU16(buf, TOSERVER_CHAT_MESSAGE);
2125 os.write((char*)buf, 2);
2128 size_t messagesize = message.size();
2129 if (messagesize > 0xFFFF) {
2130 messagesize = 0xFFFF;
2132 writeU16(buf, (u16) messagesize);
2133 os.write((char*)buf, 2);
2136 for(unsigned int i=0; i<message.size(); i++)
2140 os.write((char*)buf, 2);
2144 std::string s = os.str();
2145 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2147 Send(0, data, true);
2150 void Client::sendChangePassword(const std::wstring &oldpassword,
2151 const std::wstring &newpassword)
2153 Player *player = m_env.getLocalPlayer();
2157 std::string playername = player->getName();
2158 std::string oldpwd = translatePassword(playername, oldpassword);
2159 std::string newpwd = translatePassword(playername, newpassword);
2161 std::ostringstream os(std::ios_base::binary);
2162 u8 buf[2+PASSWORD_SIZE*2];
2164 [0] u16 TOSERVER_PASSWORD
2165 [2] u8[28] old password
2166 [30] u8[28] new password
2169 writeU16(buf, TOSERVER_PASSWORD);
2170 for(unsigned int i=0;i<PASSWORD_SIZE-1;i++)
2172 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
2173 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
2175 buf[2+PASSWORD_SIZE-1] = 0;
2176 buf[30+PASSWORD_SIZE-1] = 0;
2177 os.write((char*)buf, 2+PASSWORD_SIZE*2);
2180 std::string s = os.str();
2181 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2183 Send(0, data, true);
2187 void Client::sendDamage(u8 damage)
2189 DSTACK(__FUNCTION_NAME);
2190 std::ostringstream os(std::ios_base::binary);
2192 writeU16(os, TOSERVER_DAMAGE);
2193 writeU8(os, damage);
2196 std::string s = os.str();
2197 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2199 Send(0, data, true);
2202 void Client::sendBreath(u16 breath)
2204 DSTACK(__FUNCTION_NAME);
2205 std::ostringstream os(std::ios_base::binary);
2207 writeU16(os, TOSERVER_BREATH);
2208 writeU16(os, breath);
2210 std::string s = os.str();
2211 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2213 Send(0, data, true);
2216 void Client::sendRespawn()
2218 DSTACK(__FUNCTION_NAME);
2219 std::ostringstream os(std::ios_base::binary);
2221 writeU16(os, TOSERVER_RESPAWN);
2224 std::string s = os.str();
2225 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2227 Send(0, data, true);
2230 void Client::sendReady()
2232 DSTACK(__FUNCTION_NAME);
2233 std::ostringstream os(std::ios_base::binary);
2235 writeU16(os, TOSERVER_CLIENT_READY);
2236 writeU8(os,VERSION_MAJOR);
2237 writeU8(os,VERSION_MINOR);
2238 writeU8(os,VERSION_PATCH_ORIG);
2241 writeU16(os,strlen(minetest_version_hash));
2242 os.write(minetest_version_hash,strlen(minetest_version_hash));
2245 std::string s = os.str();
2246 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2248 Send(0, data, true);
2251 void Client::sendPlayerPos()
2253 LocalPlayer *myplayer = m_env.getLocalPlayer();
2254 if(myplayer == NULL)
2257 // Save bandwidth by only updating position when something changed
2258 if(myplayer->last_position == myplayer->getPosition() &&
2259 myplayer->last_speed == myplayer->getSpeed() &&
2260 myplayer->last_pitch == myplayer->getPitch() &&
2261 myplayer->last_yaw == myplayer->getYaw() &&
2262 myplayer->last_keyPressed == myplayer->keyPressed)
2265 myplayer->last_position = myplayer->getPosition();
2266 myplayer->last_speed = myplayer->getSpeed();
2267 myplayer->last_pitch = myplayer->getPitch();
2268 myplayer->last_yaw = myplayer->getYaw();
2269 myplayer->last_keyPressed = myplayer->keyPressed;
2273 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2274 our_peer_id = m_con.GetPeerID();
2277 // Set peer id if not set already
2278 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2279 myplayer->peer_id = our_peer_id;
2280 // Check that an existing peer_id is the same as the connection's
2281 assert(myplayer->peer_id == our_peer_id);
2283 v3f pf = myplayer->getPosition();
2284 v3f sf = myplayer->getSpeed();
2285 s32 pitch = myplayer->getPitch() * 100;
2286 s32 yaw = myplayer->getYaw() * 100;
2287 u32 keyPressed = myplayer->keyPressed;
2289 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
2290 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
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 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
2341 catch(InvalidPositionException &e)
2345 for(std::map<v3s16, MapBlock * >::iterator
2346 i = modified_blocks.begin();
2347 i != modified_blocks.end(); ++i)
2349 addUpdateMeshTask(i->first, false, false);
2351 // add urgent task to update the modified node
2352 addUpdateMeshTaskForNode(p, false, true);
2355 void Client::addNode(v3s16 p, MapNode n, bool remove_metadata)
2357 //TimeTaker timer1("Client::addNode()");
2359 std::map<v3s16, MapBlock*> modified_blocks;
2363 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
2364 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
2366 catch(InvalidPositionException &e)
2369 for(std::map<v3s16, MapBlock * >::iterator
2370 i = modified_blocks.begin();
2371 i != modified_blocks.end(); ++i)
2373 addUpdateMeshTask(i->first, false, false);
2377 void Client::setPlayerControl(PlayerControl &control)
2379 LocalPlayer *player = m_env.getLocalPlayer();
2380 assert(player != NULL);
2381 player->control = control;
2384 void Client::selectPlayerItem(u16 item)
2386 m_playeritem = item;
2387 m_inventory_updated = true;
2388 sendPlayerItem(item);
2391 // Returns true if the inventory of the local player has been
2392 // updated from the server. If it is true, it is set to false.
2393 bool Client::getLocalInventoryUpdated()
2395 bool updated = m_inventory_updated;
2396 m_inventory_updated = false;
2400 // Copies the inventory of the local player to parameter
2401 void Client::getLocalInventory(Inventory &dst)
2403 Player *player = m_env.getLocalPlayer();
2404 assert(player != NULL);
2405 dst = player->inventory;
2408 Inventory* Client::getInventory(const InventoryLocation &loc)
2411 case InventoryLocation::UNDEFINED:
2414 case InventoryLocation::CURRENT_PLAYER:
2416 Player *player = m_env.getLocalPlayer();
2417 assert(player != NULL);
2418 return &player->inventory;
2421 case InventoryLocation::PLAYER:
2423 Player *player = m_env.getPlayer(loc.name.c_str());
2426 return &player->inventory;
2429 case InventoryLocation::NODEMETA:
2431 NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p);
2434 return meta->getInventory();
2437 case InventoryLocation::DETACHED:
2439 if(m_detached_inventories.count(loc.name) == 0)
2441 return m_detached_inventories[loc.name];
2450 void Client::inventoryAction(InventoryAction *a)
2453 Send it to the server
2455 sendInventoryAction(a);
2458 Predict some local inventory changes
2460 a->clientApply(this, this);
2466 ClientActiveObject * Client::getSelectedActiveObject(
2468 v3f from_pos_f_on_map,
2469 core::line3d<f32> shootline_on_map
2472 std::vector<DistanceSortedActiveObject> objects;
2474 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
2477 // After this, the closest object is the first in the array.
2478 std::sort(objects.begin(), objects.end());
2480 for(unsigned int i=0; i<objects.size(); i++)
2482 ClientActiveObject *obj = objects[i].obj;
2484 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
2485 if(selection_box == NULL)
2488 v3f pos = obj->getPosition();
2490 core::aabbox3d<f32> offsetted_box(
2491 selection_box->MinEdge + pos,
2492 selection_box->MaxEdge + pos
2495 if(offsetted_box.intersectsWithLine(shootline_on_map))
2504 std::list<std::string> Client::getConnectedPlayerNames()
2506 return m_env.getPlayerNames();
2509 float Client::getAnimationTime()
2511 return m_animation_time;
2514 int Client::getCrackLevel()
2516 return m_crack_level;
2519 void Client::setHighlighted(v3s16 pos, bool show_highlighted)
2521 m_show_highlighted = show_highlighted;
2522 v3s16 old_highlighted_pos = m_highlighted_pos;
2523 m_highlighted_pos = pos;
2524 addUpdateMeshTaskForNode(old_highlighted_pos, false, true);
2525 addUpdateMeshTaskForNode(m_highlighted_pos, false, true);
2528 void Client::setCrack(int level, v3s16 pos)
2530 int old_crack_level = m_crack_level;
2531 v3s16 old_crack_pos = m_crack_pos;
2533 m_crack_level = level;
2536 if(old_crack_level >= 0 && (level < 0 || pos != old_crack_pos))
2539 addUpdateMeshTaskForNode(old_crack_pos, false, true);
2541 if(level >= 0 && (old_crack_level < 0 || pos != old_crack_pos))
2544 addUpdateMeshTaskForNode(pos, false, true);
2550 Player *player = m_env.getLocalPlayer();
2551 assert(player != NULL);
2555 u16 Client::getBreath()
2557 Player *player = m_env.getLocalPlayer();
2558 assert(player != NULL);
2559 return player->getBreath();
2562 bool Client::getChatMessage(std::wstring &message)
2564 if(m_chat_queue.size() == 0)
2566 message = m_chat_queue.pop_front();
2570 void Client::typeChatMessage(const std::wstring &message)
2572 // Discard empty line
2577 sendChatMessage(message);
2580 if (message[0] == L'/')
2582 m_chat_queue.push_back((std::wstring)L"issued command: " + message);
2586 LocalPlayer *player = m_env.getLocalPlayer();
2587 assert(player != NULL);
2588 std::wstring name = narrow_to_wide(player->getName());
2589 m_chat_queue.push_back((std::wstring)L"<" + name + L"> " + message);
2593 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
2595 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2600 Create a task to update the mesh of the block
2603 MeshMakeData *data = new MeshMakeData(this);
2606 //TimeTaker timer("data fill");
2608 // Debug: 1-6ms, avg=2ms
2610 data->setCrack(m_crack_level, m_crack_pos);
2611 data->setHighlighted(m_highlighted_pos, m_show_highlighted);
2612 data->setSmoothLighting(g_settings->getBool("smooth_lighting"));
2615 // Add task to queue
2616 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server, urgent);
2619 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent)
2622 v3s16 p = blockpos + v3s16(0,0,0);
2623 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2624 addUpdateMeshTask(p, ack_to_server, urgent);
2626 catch(InvalidPositionException &e){}
2629 for (int i=0;i<6;i++)
2632 v3s16 p = blockpos + g_6dirs[i];
2633 addUpdateMeshTask(p, false, urgent);
2635 catch(InvalidPositionException &e){}
2639 void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool urgent)
2643 infostream<<"Client::addUpdateMeshTaskForNode(): "
2644 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2648 v3s16 blockpos = getNodeBlockPos(nodepos);
2649 v3s16 blockpos_relative = blockpos * MAP_BLOCKSIZE;
2652 v3s16 p = blockpos + v3s16(0,0,0);
2653 addUpdateMeshTask(p, ack_to_server, urgent);
2655 catch(InvalidPositionException &e){}
2658 if(nodepos.X == blockpos_relative.X){
2660 v3s16 p = blockpos + v3s16(-1,0,0);
2661 addUpdateMeshTask(p, false, urgent);
2663 catch(InvalidPositionException &e){}
2666 if(nodepos.Y == blockpos_relative.Y){
2668 v3s16 p = blockpos + v3s16(0,-1,0);
2669 addUpdateMeshTask(p, false, urgent);
2671 catch(InvalidPositionException &e){}
2674 if(nodepos.Z == blockpos_relative.Z){
2676 v3s16 p = blockpos + v3s16(0,0,-1);
2677 addUpdateMeshTask(p, false, urgent);
2679 catch(InvalidPositionException &e){}
2683 ClientEvent Client::getClientEvent()
2685 if(m_client_event_queue.size() == 0)
2688 event.type = CE_NONE;
2691 return m_client_event_queue.pop_front();
2694 float Client::mediaReceiveProgress()
2696 if (m_media_downloader)
2697 return m_media_downloader->getProgress();
2699 return 1.0; // downloader only exists when not yet done
2702 void Client::afterContentReceived(IrrlichtDevice *device, gui::IGUIFont* font)
2704 infostream<<"Client::afterContentReceived() started"<<std::endl;
2705 assert(m_itemdef_received);
2706 assert(m_nodedef_received);
2707 assert(mediaReceived());
2709 // Rebuild inherited images and recreate textures
2710 infostream<<"- Rebuilding images and textures"<<std::endl;
2711 m_tsrc->rebuildImagesAndTextures();
2714 infostream<<"- Rebuilding shaders"<<std::endl;
2715 m_shsrc->rebuildShaders();
2717 // Update node aliases
2718 infostream<<"- Updating node aliases"<<std::endl;
2719 m_nodedef->updateAliases(m_itemdef);
2721 // Update node textures and assign shaders to each tile
2722 infostream<<"- Updating node textures"<<std::endl;
2723 m_nodedef->updateTextures(this);
2725 // Preload item textures and meshes if configured to
2726 if(g_settings->getBool("preload_item_visuals"))
2728 verbosestream<<"Updating item textures and meshes"<<std::endl;
2729 wchar_t* text = wgettext("Item textures...");
2730 draw_load_screen(text, device, guienv, 0, 0);
2731 std::set<std::string> names = m_itemdef->getAll();
2732 size_t size = names.size();
2735 for(std::set<std::string>::const_iterator
2736 i = names.begin(); i != names.end(); ++i){
2737 // Asking for these caches the result
2738 m_itemdef->getInventoryTexture(*i, this);
2739 m_itemdef->getWieldMesh(*i, this);
2741 percent = count*100/size;
2742 if (count%50 == 0) // only update every 50 item
2743 draw_load_screen(text, device, guienv, 0, percent);
2748 // Start mesh update thread after setting up content definitions
2749 infostream<<"- Starting mesh update thread"<<std::endl;
2750 m_mesh_update_thread.Start();
2754 infostream<<"Client::afterContentReceived() done"<<std::endl;
2757 float Client::getRTT(void)
2759 return m_con.getPeerStat(PEER_ID_SERVER,con::AVG_RTT);
2762 float Client::getCurRate(void)
2764 return ( m_con.getLocalStat(con::CUR_INC_RATE) +
2765 m_con.getLocalStat(con::CUR_DL_RATE));
2768 float Client::getAvgRate(void)
2770 return ( m_con.getLocalStat(con::AVG_INC_RATE) +
2771 m_con.getLocalStat(con::AVG_DL_RATE));
2774 void Client::makeScreenshot(IrrlichtDevice *device)
2776 irr::video::IVideoDriver *driver = device->getVideoDriver();
2777 irr::video::IImage* const raw_image = driver->createScreenShot();
2779 irr::video::IImage* const image = driver->createImage(video::ECF_R8G8B8,
2780 raw_image->getDimension());
2783 raw_image->copyTo(image);
2784 irr::c8 filename[256];
2785 snprintf(filename, sizeof(filename), "%s" DIR_DELIM "screenshot_%u.png",
2786 g_settings->get("screenshot_path").c_str(),
2787 device->getTimer()->getRealTime());
2788 std::stringstream sstr;
2789 if (driver->writeImageToFile(image, filename)) {
2790 sstr << "Saved screenshot to '" << filename << "'";
2792 sstr << "Failed to save screenshot '" << filename << "'";
2794 m_chat_queue.push_back(narrow_to_wide(sstr.str()));
2795 infostream << sstr << std::endl;
2802 // IGameDef interface
2804 IItemDefManager* Client::getItemDefManager()
2808 INodeDefManager* Client::getNodeDefManager()
2812 ICraftDefManager* Client::getCraftDefManager()
2815 //return m_craftdef;
2817 ITextureSource* Client::getTextureSource()
2821 IShaderSource* Client::getShaderSource()
2825 scene::ISceneManager* Client::getSceneManager()
2827 return m_device->getSceneManager();
2829 u16 Client::allocateUnknownNodeId(const std::string &name)
2831 errorstream<<"Client::allocateUnknownNodeId(): "
2832 <<"Client cannot allocate node IDs"<<std::endl;
2834 return CONTENT_IGNORE;
2836 ISoundManager* Client::getSoundManager()
2840 MtEventManager* Client::getEventManager()
2845 scene::IAnimatedMesh* Client::getMesh(const std::string &filename)
2847 std::map<std::string, std::string>::const_iterator i =
2848 m_mesh_data.find(filename);
2849 if(i == m_mesh_data.end()){
2850 errorstream<<"Client::getMesh(): Mesh not found: \""<<filename<<"\""
2854 const std::string &data = i->second;
2855 scene::ISceneManager *smgr = m_device->getSceneManager();
2857 // Create the mesh, remove it from cache and return it
2858 // This allows unique vertex colors and other properties for each instance
2859 Buffer<char> data_rw(data.c_str(), data.size()); // Const-incorrect Irrlicht
2860 io::IFileSystem *irrfs = m_device->getFileSystem();
2861 io::IReadFile *rfile = irrfs->createMemoryReadFile(
2862 *data_rw, data_rw.getSize(), filename.c_str());
2865 scene::IAnimatedMesh *mesh = smgr->getMesh(rfile);
2867 // NOTE: By playing with Irrlicht refcounts, maybe we could cache a bunch
2868 // of uniquely named instances and re-use them
2870 smgr->getMeshCache()->removeMesh(mesh);