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_particle_manager(&m_env),
252 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, ipv6, this),
254 m_server_ser_ver(SER_FMT_VER_INVALID),
256 m_inventory_updated(false),
257 m_inventory_from_server(NULL),
258 m_inventory_from_server_age(0.0),
259 m_show_highlighted(false),
263 m_highlighted_pos(0,0,0),
265 m_password(password),
266 m_access_denied(false),
267 m_itemdef_received(false),
268 m_nodedef_received(false),
269 m_media_downloader(new ClientMediaDownloader()),
270 m_time_of_day_set(false),
271 m_last_time_of_day_f(-1),
272 m_time_of_day_update_timer(0),
273 m_recommended_send_interval(0.1),
274 m_removed_sounds_check_timer(0),
281 Player *player = new LocalPlayer(this, playername);
283 m_env.addPlayer(player);
286 if (g_settings->getBool("enable_local_map_saving")
287 && !is_simple_singleplayer_game) {
288 const std::string world_path = porting::path_user + DIR_DELIM + "worlds"
289 + DIR_DELIM + "server_" + g_settings->get("address")
290 + "_" + g_settings->get("remote_port");
292 SubgameSpec gamespec;
293 if (!getWorldExists(world_path)) {
294 gamespec = findSubgame(g_settings->get("default_game"));
295 if (!gamespec.isValid())
296 gamespec = findSubgame("minimal");
298 std::string world_gameid = getWorldGameId(world_path, false);
299 gamespec = findWorldSubgame(world_path);
301 if (!gamespec.isValid()) {
302 errorstream << "Couldn't find subgame for local map saving." << std::endl;
306 localserver = new Server(world_path, gamespec, false, false);
307 localdb = new Database_SQLite3(&(ServerMap&)localserver->getMap(), world_path);
308 localdb->beginSave();
309 actionstream << "Local map saving started, map will be saved at '" << world_path << "'" << std::endl;
314 m_cache_smooth_lighting = g_settings->getBool("smooth_lighting");
319 //request all client managed threads to stop
320 m_mesh_update_thread.Stop();
321 if (localdb != NULL) {
322 actionstream << "Local map saving ended" << std::endl;
327 bool Client::isShutdown()
330 if (!m_mesh_update_thread.IsRunning()) return true;
339 m_mesh_update_thread.Stop();
340 m_mesh_update_thread.Wait();
341 while(!m_mesh_update_thread.m_queue_out.empty()) {
342 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_frontNoEx();
347 delete m_inventory_from_server;
349 // Delete detached inventories
350 for(std::map<std::string, Inventory*>::iterator
351 i = m_detached_inventories.begin();
352 i != m_detached_inventories.end(); i++){
356 // cleanup 3d model meshes on client shutdown
357 while (m_device->getSceneManager()->getMeshCache()->getMeshCount() != 0) {
358 scene::IAnimatedMesh * mesh =
359 m_device->getSceneManager()->getMeshCache()->getMeshByIndex(0);
362 m_device->getSceneManager()->getMeshCache()->removeMesh(mesh);
366 void Client::connect(Address address)
368 DSTACK(__FUNCTION_NAME);
369 m_con.SetTimeoutMs(0);
370 m_con.Connect(address);
373 void Client::step(float dtime)
375 DSTACK(__FUNCTION_NAME);
381 if(m_ignore_damage_timer > dtime)
382 m_ignore_damage_timer -= dtime;
384 m_ignore_damage_timer = 0.0;
386 m_animation_time += dtime;
387 if(m_animation_time > 60.0)
388 m_animation_time -= 60.0;
390 m_time_of_day_update_timer += dtime;
398 float &counter = m_packetcounter_timer;
404 infostream << "Client packetcounter (" << m_packetcounter_timer
406 m_packetcounter.print(infostream);
407 m_packetcounter.clear();
414 Delete unused sectors
416 NOTE: This jams the game for a while because deleting sectors
420 float &counter = m_delete_unused_sectors_timer;
428 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
430 core::list<v3s16> deleted_blocks;
432 float delete_unused_sectors_timeout =
433 g_settings->getFloat("client_delete_unused_sectors_timeout");
435 // Delete sector blocks
436 /*u32 num = m_env.getMap().unloadUnusedData
437 (delete_unused_sectors_timeout,
438 true, &deleted_blocks);*/
440 // Delete whole sectors
441 m_env.getMap().unloadUnusedData
442 (delete_unused_sectors_timeout,
445 if(deleted_blocks.size() > 0)
447 /*infostream<<"Client: Deleted blocks of "<<num
448 <<" unused sectors"<<std::endl;*/
449 /*infostream<<"Client: Deleted "<<num
450 <<" unused sectors"<<std::endl;*/
456 // Env is locked so con can be locked.
457 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
459 core::list<v3s16>::Iterator i = deleted_blocks.begin();
460 core::list<v3s16> sendlist;
463 if(sendlist.size() == 255 || i == deleted_blocks.end())
465 if(sendlist.size() == 0)
474 u32 replysize = 2+1+6*sendlist.size();
475 SharedBuffer<u8> reply(replysize);
476 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
477 reply[2] = sendlist.size();
479 for(core::list<v3s16>::Iterator
480 j = sendlist.begin();
481 j != sendlist.end(); j++)
483 writeV3S16(&reply[2+1+6*k], *j);
486 m_con.Send(PEER_ID_SERVER, 1, reply, true);
488 if(i == deleted_blocks.end())
494 sendlist.push_back(*i);
501 // UGLY hack to fix 2 second startup delay caused by non existent
502 // server client startup synchronization in local server or singleplayer mode
503 static bool initial_step = true;
505 initial_step = false;
507 else if(m_state == LC_Created)
509 float &counter = m_connection_reinit_timer;
515 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
517 Player *myplayer = m_env.getLocalPlayer();
518 assert(myplayer != NULL);
519 // Send TOSERVER_INIT
520 // [0] u16 TOSERVER_INIT
521 // [2] u8 SER_FMT_VER_HIGHEST_READ
522 // [3] u8[20] player_name
523 // [23] u8[28] password (new in some version)
524 // [51] u16 minimum supported network protocol version (added sometime)
525 // [53] u16 maximum supported network protocol version (added later than the previous one)
526 SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2+2);
527 writeU16(&data[0], TOSERVER_INIT);
528 writeU8(&data[2], SER_FMT_VER_HIGHEST_READ);
530 memset((char*)&data[3], 0, PLAYERNAME_SIZE);
531 snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
533 /*infostream<<"Client: sending initial password hash: \""<<m_password<<"\""
536 memset((char*)&data[23], 0, PASSWORD_SIZE);
537 snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
539 writeU16(&data[51], CLIENT_PROTOCOL_VERSION_MIN);
540 writeU16(&data[53], CLIENT_PROTOCOL_VERSION_MAX);
542 // Send as unreliable
543 Send(1, data, false);
546 // Not connected, return
551 Do stuff if connected
555 Run Map's timers and unload unused data
557 const float map_timer_and_unload_dtime = 5.25;
558 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
560 ScopeProfiler sp(g_profiler, "Client: map timer and unload");
561 std::list<v3s16> deleted_blocks;
562 m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
563 g_settings->getFloat("client_unload_unused_data_timeout"),
566 /*if(deleted_blocks.size() > 0)
567 infostream<<"Client: Unloaded "<<deleted_blocks.size()
568 <<" unused blocks"<<std::endl;*/
572 NOTE: This loop is intentionally iterated the way it is.
575 std::list<v3s16>::iterator i = deleted_blocks.begin();
576 std::list<v3s16> sendlist;
579 if(sendlist.size() == 255 || i == deleted_blocks.end())
590 u32 replysize = 2+1+6*sendlist.size();
591 SharedBuffer<u8> reply(replysize);
592 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
593 reply[2] = sendlist.size();
595 for(std::list<v3s16>::iterator
596 j = sendlist.begin();
597 j != sendlist.end(); ++j)
599 writeV3S16(&reply[2+1+6*k], *j);
602 m_con.Send(PEER_ID_SERVER, 2, reply, true);
604 if(i == deleted_blocks.end())
610 sendlist.push_back(*i);
619 // Control local player (0ms)
620 LocalPlayer *player = m_env.getLocalPlayer();
621 assert(player != NULL);
622 player->applyControl(dtime);
632 ClientEnvEvent event = m_env.getClientEvent();
633 if(event.type == CEE_NONE)
637 else if(event.type == CEE_PLAYER_DAMAGE)
639 if(m_ignore_damage_timer <= 0)
641 u8 damage = event.player_damage.amount;
643 if(event.player_damage.send_to_server)
646 // Add to ClientEvent queue
648 event.type = CE_PLAYER_DAMAGE;
649 event.player_damage.amount = damage;
650 m_client_event_queue.push_back(event);
653 else if(event.type == CEE_PLAYER_BREATH)
655 u16 breath = event.player_breath.amount;
665 float &counter = m_avg_rtt_timer;
670 // connectedAndInitialized() is true, peer exists.
671 float avg_rtt = getRTT();
672 infostream<<"Client: avg_rtt="<<avg_rtt<<std::endl;
677 Send player position to server
680 float &counter = m_playerpos_send_timer;
682 if((m_state == LC_Ready) && (counter >= m_recommended_send_interval))
690 Replace updated meshes
693 int num_processed_meshes = 0;
694 while(!m_mesh_update_thread.m_queue_out.empty())
696 num_processed_meshes++;
697 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_frontNoEx();
698 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
701 // Delete the old mesh
702 if(block->mesh != NULL)
704 // TODO: Remove hardware buffers of meshbuffers of block->mesh
709 // Replace with the new mesh
710 block->mesh = r.mesh;
714 if(r.ack_block_to_server)
726 u32 replysize = 2+1+6;
727 SharedBuffer<u8> reply(replysize);
728 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
730 writeV3S16(&reply[3], r.p);
732 m_con.Send(PEER_ID_SERVER, 2, reply, true);
735 if(num_processed_meshes > 0)
736 g_profiler->graphAdd("num_processed_meshes", num_processed_meshes);
742 if (m_media_downloader && m_media_downloader->isStarted()) {
743 m_media_downloader->step(this);
744 if (m_media_downloader->isDone()) {
746 delete m_media_downloader;
747 m_media_downloader = NULL;
752 If the server didn't update the inventory in a while, revert
753 the local inventory (so the player notices the lag problem
754 and knows something is wrong).
756 if(m_inventory_from_server)
758 float interval = 10.0;
759 float count_before = floor(m_inventory_from_server_age / interval);
761 m_inventory_from_server_age += dtime;
763 float count_after = floor(m_inventory_from_server_age / interval);
765 if(count_after != count_before)
767 // Do this every <interval> seconds after TOCLIENT_INVENTORY
768 // Reset the locally changed inventory to the authoritative inventory
769 Player *player = m_env.getLocalPlayer();
770 player->inventory = *m_inventory_from_server;
771 m_inventory_updated = true;
776 Update positions of sounds attached to objects
779 for(std::map<int, u16>::iterator
780 i = m_sounds_to_objects.begin();
781 i != m_sounds_to_objects.end(); i++)
783 int client_id = i->first;
784 u16 object_id = i->second;
785 ClientActiveObject *cao = m_env.getActiveObject(object_id);
788 v3f pos = cao->getPosition();
789 m_sound->updateSoundPosition(client_id, pos);
794 Handle removed remotely initiated sounds
796 m_removed_sounds_check_timer += dtime;
797 if(m_removed_sounds_check_timer >= 2.32)
799 m_removed_sounds_check_timer = 0;
800 // Find removed sounds and clear references to them
801 std::set<s32> removed_server_ids;
802 for(std::map<s32, int>::iterator
803 i = m_sounds_server_to_client.begin();
804 i != m_sounds_server_to_client.end();)
806 s32 server_id = i->first;
807 int client_id = i->second;
809 if(!m_sound->soundExists(client_id)){
810 m_sounds_server_to_client.erase(server_id);
811 m_sounds_client_to_server.erase(client_id);
812 m_sounds_to_objects.erase(client_id);
813 removed_server_ids.insert(server_id);
817 if(!removed_server_ids.empty())
819 std::ostringstream os(std::ios_base::binary);
820 writeU16(os, TOSERVER_REMOVED_SOUNDS);
821 size_t server_ids = removed_server_ids.size();
822 assert(server_ids <= 0xFFFF);
823 writeU16(os, (u16) (server_ids & 0xFFFF));
824 for(std::set<s32>::iterator i = removed_server_ids.begin();
825 i != removed_server_ids.end(); i++)
827 std::string s = os.str();
828 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
835 bool Client::loadMedia(const std::string &data, const std::string &filename)
837 // Silly irrlicht's const-incorrectness
838 Buffer<char> data_rw(data.c_str(), data.size());
842 const char *image_ext[] = {
843 ".png", ".jpg", ".bmp", ".tga",
844 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
847 name = removeStringEnd(filename, image_ext);
850 verbosestream<<"Client: Attempting to load image "
851 <<"file \""<<filename<<"\""<<std::endl;
853 io::IFileSystem *irrfs = m_device->getFileSystem();
854 video::IVideoDriver *vdrv = m_device->getVideoDriver();
856 // Create an irrlicht memory file
857 io::IReadFile *rfile = irrfs->createMemoryReadFile(
858 *data_rw, data_rw.getSize(), "_tempreadfile");
861 video::IImage *img = vdrv->createImageFromFile(rfile);
863 errorstream<<"Client: Cannot create image from data of "
864 <<"file \""<<filename<<"\""<<std::endl;
869 m_tsrc->insertSourceImage(filename, img);
876 const char *sound_ext[] = {
877 ".0.ogg", ".1.ogg", ".2.ogg", ".3.ogg", ".4.ogg",
878 ".5.ogg", ".6.ogg", ".7.ogg", ".8.ogg", ".9.ogg",
881 name = removeStringEnd(filename, sound_ext);
884 verbosestream<<"Client: Attempting to load sound "
885 <<"file \""<<filename<<"\""<<std::endl;
886 m_sound->loadSoundData(name, data);
890 const char *model_ext[] = {
891 ".x", ".b3d", ".md2", ".obj",
894 name = removeStringEnd(filename, model_ext);
897 verbosestream<<"Client: Storing model into memory: "
898 <<"\""<<filename<<"\""<<std::endl;
899 if(m_mesh_data.count(filename))
900 errorstream<<"Multiple models with name \""<<filename.c_str()
901 <<"\" found; replacing previous model"<<std::endl;
902 m_mesh_data[filename] = data;
906 errorstream<<"Client: Don't know how to load file \""
907 <<filename<<"\""<<std::endl;
911 // Virtual methods from con::PeerHandler
912 void Client::peerAdded(con::Peer *peer)
914 infostream<<"Client::peerAdded(): peer->id="
915 <<peer->id<<std::endl;
917 void Client::deletingPeer(con::Peer *peer, bool timeout)
919 infostream<<"Client::deletingPeer(): "
920 "Server Peer is getting deleted "
921 <<"(timeout="<<timeout<<")"<<std::endl;
926 u16 number of files requested
932 void Client::request_media(const std::list<std::string> &file_requests)
934 std::ostringstream os(std::ios_base::binary);
935 writeU16(os, TOSERVER_REQUEST_MEDIA);
936 size_t file_requests_size = file_requests.size();
937 assert(file_requests_size <= 0xFFFF);
938 writeU16(os, (u16) (file_requests_size & 0xFFFF));
940 for(std::list<std::string>::const_iterator i = file_requests.begin();
941 i != file_requests.end(); ++i) {
942 os<<serializeString(*i);
946 std::string s = os.str();
947 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
950 infostream<<"Client: Sending media request list to server ("
951 <<file_requests.size()<<" files)"<<std::endl;
954 void Client::received_media()
956 // notify server we received everything
957 std::ostringstream os(std::ios_base::binary);
958 writeU16(os, TOSERVER_RECEIVED_MEDIA);
959 std::string s = os.str();
960 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
963 infostream<<"Client: Notifying server that we received all media"
967 void Client::ReceiveAll()
969 DSTACK(__FUNCTION_NAME);
970 u32 start_ms = porting::getTimeMs();
973 // Limit time even if there would be huge amounts of data to
975 if(porting::getTimeMs() > start_ms + 100)
980 g_profiler->graphAdd("client_received_packets", 1);
982 catch(con::NoIncomingDataException &e)
986 catch(con::InvalidIncomingDataException &e)
988 infostream<<"Client::ReceiveAll(): "
989 "InvalidIncomingDataException: what()="
990 <<e.what()<<std::endl;
995 void Client::Receive()
997 DSTACK(__FUNCTION_NAME);
998 SharedBuffer<u8> data;
1000 u32 datasize = m_con.Receive(sender_peer_id, data);
1001 ProcessData(*data, datasize, sender_peer_id);
1005 sender_peer_id given to this shall be quaranteed to be a valid peer
1007 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
1009 DSTACK(__FUNCTION_NAME);
1011 // Ignore packets that don't even fit a command
1014 m_packetcounter.add(60000);
1018 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
1020 //infostream<<"Client: received command="<<command<<std::endl;
1021 m_packetcounter.add((u16)command);
1024 If this check is removed, be sure to change the queue
1025 system to know the ids
1027 if(sender_peer_id != PEER_ID_SERVER)
1029 infostream<<"Client::ProcessData(): Discarding data not "
1030 "coming from server: peer_id="<<sender_peer_id
1035 u8 ser_version = m_server_ser_ver;
1037 if(command == TOCLIENT_INIT)
1042 u8 deployed = data[2];
1044 infostream<<"Client: TOCLIENT_INIT received with "
1045 "deployed="<<((int)deployed&0xff)<<std::endl;
1047 if(!ser_ver_supported(deployed))
1049 infostream<<"Client: TOCLIENT_INIT: Server sent "
1050 <<"unsupported ser_fmt_ver"<<std::endl;
1054 m_server_ser_ver = deployed;
1056 // Get player position
1057 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
1058 if(datasize >= 2+1+6)
1059 playerpos_s16 = readV3S16(&data[2+1]);
1060 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
1063 // Set player position
1064 Player *player = m_env.getLocalPlayer();
1065 assert(player != NULL);
1066 player->setPosition(playerpos_f);
1068 if(datasize >= 2+1+6+8)
1071 m_map_seed = readU64(&data[2+1+6]);
1072 infostream<<"Client: received map seed: "<<m_map_seed<<std::endl;
1075 if(datasize >= 2+1+6+8+4)
1078 m_recommended_send_interval = readF1000(&data[2+1+6+8]);
1079 infostream<<"Client: received recommended send interval "
1080 <<m_recommended_send_interval<<std::endl;
1085 SharedBuffer<u8> reply(replysize);
1086 writeU16(&reply[0], TOSERVER_INIT2);
1088 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1095 if(command == TOCLIENT_ACCESS_DENIED)
1097 // The server didn't like our password. Note, this needs
1098 // to be processed even if the serialisation format has
1099 // not been agreed yet, the same as TOCLIENT_INIT.
1100 m_access_denied = true;
1101 m_access_denied_reason = L"Unknown";
1104 std::string datastring((char*)&data[2], datasize-2);
1105 std::istringstream is(datastring, std::ios_base::binary);
1106 m_access_denied_reason = deSerializeWideString(is);
1111 if(ser_version == SER_FMT_VER_INVALID)
1113 infostream<<"Client: Server serialization"
1114 " format invalid or not initialized."
1115 " Skipping incoming command="<<command<<std::endl;
1120 Handle runtime commands
1122 // there's no sane reason why we shouldn't have a player and
1123 // almost everyone needs a player reference
1124 Player *player = m_env.getLocalPlayer();
1125 assert(player != NULL);
1127 if(command == TOCLIENT_REMOVENODE)
1132 p.X = readS16(&data[2]);
1133 p.Y = readS16(&data[4]);
1134 p.Z = readS16(&data[6]);
1137 else if(command == TOCLIENT_ADDNODE)
1139 if(datasize < 8 + MapNode::serializedLength(ser_version))
1143 p.X = readS16(&data[2]);
1144 p.Y = readS16(&data[4]);
1145 p.Z = readS16(&data[6]);
1148 n.deSerialize(&data[8], ser_version);
1150 bool remove_metadata = true;
1151 u32 index = 8 + MapNode::serializedLength(ser_version);
1152 if ((datasize >= index+1) && data[index]){
1153 remove_metadata = false;
1156 addNode(p, n, remove_metadata);
1158 else if(command == TOCLIENT_BLOCKDATA)
1160 // Ignore too small packet
1165 p.X = readS16(&data[2]);
1166 p.Y = readS16(&data[4]);
1167 p.Z = readS16(&data[6]);
1169 std::string datastring((char*)&data[8], datasize-8);
1170 std::istringstream istr(datastring, std::ios_base::binary);
1175 v2s16 p2d(p.X, p.Z);
1176 sector = m_env.getMap().emergeSector(p2d);
1178 assert(sector->getPos() == p2d);
1180 block = sector->getBlockNoCreateNoEx(p.Y);
1184 Update an existing block
1186 block->deSerialize(istr, ser_version, false);
1187 block->deSerializeNetworkSpecific(istr);
1194 block = new MapBlock(&m_env.getMap(), p, this);
1195 block->deSerialize(istr, ser_version, false);
1196 block->deSerializeNetworkSpecific(istr);
1197 sector->insertBlock(block);
1200 if (localdb != NULL) {
1201 ((ServerMap&) localserver->getMap()).saveBlock(block, localdb);
1205 Add it to mesh update queue and set it to be acknowledged after update.
1207 addUpdateMeshTaskWithEdge(p, true);
1209 else if(command == TOCLIENT_INVENTORY)
1214 std::string datastring((char*)&data[2], datasize-2);
1215 std::istringstream is(datastring, std::ios_base::binary);
1217 player->inventory.deSerialize(is);
1219 m_inventory_updated = true;
1221 delete m_inventory_from_server;
1222 m_inventory_from_server = new Inventory(player->inventory);
1223 m_inventory_from_server_age = 0.0;
1226 else if(command == TOCLIENT_TIME_OF_DAY)
1231 u16 time_of_day = readU16(&data[2]);
1232 time_of_day = time_of_day % 24000;
1233 float time_speed = 0;
1235 if(datasize >= 2 + 2 + 4)
1237 time_speed = readF1000(&data[4]);
1240 // Old message; try to approximate speed of time by ourselves
1241 float time_of_day_f = (float)time_of_day / 24000.0;
1242 float tod_diff_f = 0;
1244 if(time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
1245 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0;
1247 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
1249 m_last_time_of_day_f = time_of_day_f;
1250 float time_diff = m_time_of_day_update_timer;
1251 m_time_of_day_update_timer = 0;
1253 if(m_time_of_day_set){
1254 time_speed = (3600.0*24.0) * tod_diff_f / time_diff;
1255 infostream<<"Client: Measured time_of_day speed (old format): "
1256 <<time_speed<<" tod_diff_f="<<tod_diff_f
1257 <<" time_diff="<<time_diff<<std::endl;
1261 // Update environment
1262 m_env.setTimeOfDay(time_of_day);
1263 m_env.setTimeOfDaySpeed(time_speed);
1264 m_time_of_day_set = true;
1266 u32 dr = m_env.getDayNightRatio();
1267 infostream<<"Client: time_of_day="<<time_of_day
1268 <<" time_speed="<<time_speed
1269 <<" dr="<<dr<<std::endl;
1271 else if(command == TOCLIENT_CHAT_MESSAGE)
1279 std::string datastring((char*)&data[2], datasize-2);
1280 std::istringstream is(datastring, std::ios_base::binary);
1283 is.read((char*) buf, 2);
1284 u16 len = readU16(buf);
1286 std::wstring message;
1287 for(unsigned int i=0; i<len; i++)
1289 is.read((char*)buf, 2);
1290 message += (wchar_t)readU16(buf);
1293 m_chat_queue.push_back(message);
1295 else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1299 u16 count of removed objects
1300 for all removed objects {
1303 u16 count of added objects
1304 for all added objects {
1307 u32 initialization data length
1308 string initialization data
1313 // Get all data except the command number
1314 std::string datastring((char*)&data[2], datasize-2);
1315 // Throw them in an istringstream
1316 std::istringstream is(datastring, std::ios_base::binary);
1318 // Read removed objects
1320 u16 removed_count = readU16((u8*)buf);
1321 for(unsigned int i=0; i<removed_count; i++)
1324 u16 id = readU16((u8*)buf);
1325 m_env.removeActiveObject(id);
1328 // Read added objects
1330 u16 added_count = readU16((u8*)buf);
1331 for(unsigned int i=0; i<added_count; i++)
1334 u16 id = readU16((u8*)buf);
1336 u8 type = readU8((u8*)buf);
1337 std::string data = deSerializeLongString(is);
1339 m_env.addActiveObject(id, type, data);
1342 else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1354 // Get all data except the command number
1355 std::string datastring((char*)&data[2], datasize-2);
1356 // Throw them in an istringstream
1357 std::istringstream is(datastring, std::ios_base::binary);
1359 while(is.eof() == false)
1362 u16 id = readU16((u8*)buf);
1366 size_t message_size = readU16((u8*)buf);
1367 std::string message;
1368 message.reserve(message_size);
1369 for(unsigned int i=0; i<message_size; i++)
1372 message.append(buf, 1);
1374 // Pass on to the environment
1375 m_env.processActiveObjectMessage(id, message);
1378 else if(command == TOCLIENT_MOVEMENT)
1380 std::string datastring((char*)&data[2], datasize-2);
1381 std::istringstream is(datastring, std::ios_base::binary);
1383 player->movement_acceleration_default = readF1000(is) * BS;
1384 player->movement_acceleration_air = readF1000(is) * BS;
1385 player->movement_acceleration_fast = readF1000(is) * BS;
1386 player->movement_speed_walk = readF1000(is) * BS;
1387 player->movement_speed_crouch = readF1000(is) * BS;
1388 player->movement_speed_fast = readF1000(is) * BS;
1389 player->movement_speed_climb = readF1000(is) * BS;
1390 player->movement_speed_jump = readF1000(is) * BS;
1391 player->movement_liquid_fluidity = readF1000(is) * BS;
1392 player->movement_liquid_fluidity_smooth = readF1000(is) * BS;
1393 player->movement_liquid_sink = readF1000(is) * BS;
1394 player->movement_gravity = readF1000(is) * BS;
1396 else if(command == TOCLIENT_HP)
1398 std::string datastring((char*)&data[2], datasize-2);
1399 std::istringstream is(datastring, std::ios_base::binary);
1401 u8 oldhp = player->hp;
1407 // Add to ClientEvent queue
1409 event.type = CE_PLAYER_DAMAGE;
1410 event.player_damage.amount = oldhp - hp;
1411 m_client_event_queue.push_back(event);
1414 else if(command == TOCLIENT_BREATH)
1416 std::string datastring((char*)&data[2], datasize-2);
1417 std::istringstream is(datastring, std::ios_base::binary);
1419 player->setBreath(readU16(is));
1421 else if(command == TOCLIENT_MOVE_PLAYER)
1423 std::string datastring((char*)&data[2], datasize-2);
1424 std::istringstream is(datastring, std::ios_base::binary);
1426 v3f pos = readV3F1000(is);
1427 f32 pitch = readF1000(is);
1428 f32 yaw = readF1000(is);
1429 player->setPosition(pos);
1431 infostream<<"Client got TOCLIENT_MOVE_PLAYER"
1432 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1438 Add to ClientEvent queue.
1439 This has to be sent to the main program because otherwise
1440 it would just force the pitch and yaw values to whatever
1441 the camera points to.
1444 event.type = CE_PLAYER_FORCE_MOVE;
1445 event.player_force_move.pitch = pitch;
1446 event.player_force_move.yaw = yaw;
1447 m_client_event_queue.push_back(event);
1449 // Ignore damage for a few seconds, so that the player doesn't
1450 // get damage from falling on ground
1451 m_ignore_damage_timer = 3.0;
1453 else if(command == TOCLIENT_PLAYERITEM)
1455 infostream<<"Client: WARNING: Ignoring TOCLIENT_PLAYERITEM"<<std::endl;
1457 else if(command == TOCLIENT_DEATHSCREEN)
1459 std::string datastring((char*)&data[2], datasize-2);
1460 std::istringstream is(datastring, std::ios_base::binary);
1462 bool set_camera_point_target = readU8(is);
1463 v3f camera_point_target = readV3F1000(is);
1466 event.type = CE_DEATHSCREEN;
1467 event.deathscreen.set_camera_point_target = set_camera_point_target;
1468 event.deathscreen.camera_point_target_x = camera_point_target.X;
1469 event.deathscreen.camera_point_target_y = camera_point_target.Y;
1470 event.deathscreen.camera_point_target_z = camera_point_target.Z;
1471 m_client_event_queue.push_back(event);
1473 else if(command == TOCLIENT_ANNOUNCE_MEDIA)
1475 std::string datastring((char*)&data[2], datasize-2);
1476 std::istringstream is(datastring, std::ios_base::binary);
1478 int num_files = readU16(is);
1480 infostream<<"Client: Received media announcement: packet size: "
1481 <<datasize<<std::endl;
1483 if (m_media_downloader == NULL ||
1484 m_media_downloader->isStarted()) {
1485 const char *problem = m_media_downloader ?
1486 "we already saw another announcement" :
1487 "all media has been received already";
1488 errorstream<<"Client: Received media announcement but "
1490 <<" files="<<num_files
1491 <<" size="<<datasize<<std::endl;
1495 // Mesh update thread must be stopped while
1496 // updating content definitions
1497 assert(!m_mesh_update_thread.IsRunning());
1499 for(int i=0; i<num_files; i++)
1501 std::string name = deSerializeString(is);
1502 std::string sha1_base64 = deSerializeString(is);
1503 std::string sha1_raw = base64_decode(sha1_base64);
1504 m_media_downloader->addFile(name, sha1_raw);
1507 std::vector<std::string> remote_media;
1509 Strfnd sf(deSerializeString(is));
1510 while(!sf.atend()) {
1511 std::string baseurl = trim(sf.next(","));
1513 m_media_downloader->addRemoteServer(baseurl);
1516 catch(SerializationError& e) {
1517 // not supported by server or turned off
1520 m_media_downloader->step(this);
1522 else if(command == TOCLIENT_MEDIA)
1524 std::string datastring((char*)&data[2], datasize-2);
1525 std::istringstream is(datastring, std::ios_base::binary);
1529 u16 total number of file bunches
1530 u16 index of this bunch
1531 u32 number of files in this bunch
1539 int num_bunches = readU16(is);
1540 int bunch_i = readU16(is);
1541 u32 num_files = readU32(is);
1542 infostream<<"Client: Received files: bunch "<<bunch_i<<"/"
1543 <<num_bunches<<" files="<<num_files
1544 <<" size="<<datasize<<std::endl;
1549 if (m_media_downloader == NULL ||
1550 !m_media_downloader->isStarted()) {
1551 const char *problem = m_media_downloader ?
1552 "media has not been requested" :
1553 "all media has been received already";
1554 errorstream<<"Client: Received media but "
1556 <<" bunch "<<bunch_i<<"/"<<num_bunches
1557 <<" files="<<num_files
1558 <<" size="<<datasize<<std::endl;
1562 // Mesh update thread must be stopped while
1563 // updating content definitions
1564 assert(!m_mesh_update_thread.IsRunning());
1566 for(unsigned int i=0; i<num_files; i++){
1567 std::string name = deSerializeString(is);
1568 std::string data = deSerializeLongString(is);
1569 m_media_downloader->conventionalTransferDone(
1573 else if(command == TOCLIENT_TOOLDEF)
1575 infostream<<"Client: WARNING: Ignoring TOCLIENT_TOOLDEF"<<std::endl;
1577 else if(command == TOCLIENT_NODEDEF)
1579 infostream<<"Client: Received node definitions: packet size: "
1580 <<datasize<<std::endl;
1582 // Mesh update thread must be stopped while
1583 // updating content definitions
1584 assert(!m_mesh_update_thread.IsRunning());
1586 // Decompress node definitions
1587 std::string datastring((char*)&data[2], datasize-2);
1588 std::istringstream is(datastring, std::ios_base::binary);
1589 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1590 std::ostringstream tmp_os;
1591 decompressZlib(tmp_is, tmp_os);
1593 // Deserialize node definitions
1594 std::istringstream tmp_is2(tmp_os.str());
1595 m_nodedef->deSerialize(tmp_is2);
1596 m_nodedef_received = true;
1598 else if(command == TOCLIENT_CRAFTITEMDEF)
1600 infostream<<"Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF"<<std::endl;
1602 else if(command == TOCLIENT_ITEMDEF)
1604 infostream<<"Client: Received item definitions: packet size: "
1605 <<datasize<<std::endl;
1607 // Mesh update thread must be stopped while
1608 // updating content definitions
1609 assert(!m_mesh_update_thread.IsRunning());
1611 // Decompress item definitions
1612 std::string datastring((char*)&data[2], datasize-2);
1613 std::istringstream is(datastring, std::ios_base::binary);
1614 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1615 std::ostringstream tmp_os;
1616 decompressZlib(tmp_is, tmp_os);
1618 // Deserialize node definitions
1619 std::istringstream tmp_is2(tmp_os.str());
1620 m_itemdef->deSerialize(tmp_is2);
1621 m_itemdef_received = true;
1623 else if(command == TOCLIENT_PLAY_SOUND)
1625 std::string datastring((char*)&data[2], datasize-2);
1626 std::istringstream is(datastring, std::ios_base::binary);
1628 s32 server_id = readS32(is);
1629 std::string name = deSerializeString(is);
1630 float gain = readF1000(is);
1631 int type = readU8(is); // 0=local, 1=positional, 2=object
1632 v3f pos = readV3F1000(is);
1633 u16 object_id = readU16(is);
1634 bool loop = readU8(is);
1639 client_id = m_sound->playSound(name, loop, gain);
1641 case 1: // positional
1642 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1645 ClientActiveObject *cao = m_env.getActiveObject(object_id);
1647 pos = cao->getPosition();
1648 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1649 // TODO: Set up sound to move with object
1654 if(client_id != -1){
1655 m_sounds_server_to_client[server_id] = client_id;
1656 m_sounds_client_to_server[client_id] = server_id;
1658 m_sounds_to_objects[client_id] = object_id;
1661 else if(command == TOCLIENT_STOP_SOUND)
1663 std::string datastring((char*)&data[2], datasize-2);
1664 std::istringstream is(datastring, std::ios_base::binary);
1666 s32 server_id = readS32(is);
1667 std::map<s32, int>::iterator i =
1668 m_sounds_server_to_client.find(server_id);
1669 if(i != m_sounds_server_to_client.end()){
1670 int client_id = i->second;
1671 m_sound->stopSound(client_id);
1674 else if(command == TOCLIENT_PRIVILEGES)
1676 std::string datastring((char*)&data[2], datasize-2);
1677 std::istringstream is(datastring, std::ios_base::binary);
1679 m_privileges.clear();
1680 infostream<<"Client: Privileges updated: ";
1681 u16 num_privileges = readU16(is);
1682 for(unsigned int i=0; i<num_privileges; i++){
1683 std::string priv = deSerializeString(is);
1684 m_privileges.insert(priv);
1685 infostream<<priv<<" ";
1687 infostream<<std::endl;
1689 else if(command == TOCLIENT_INVENTORY_FORMSPEC)
1691 std::string datastring((char*)&data[2], datasize-2);
1692 std::istringstream is(datastring, std::ios_base::binary);
1694 // Store formspec in LocalPlayer
1695 player->inventory_formspec = deSerializeLongString(is);
1697 else if(command == TOCLIENT_DETACHED_INVENTORY)
1699 std::string datastring((char*)&data[2], datasize-2);
1700 std::istringstream is(datastring, std::ios_base::binary);
1702 std::string name = deSerializeString(is);
1704 infostream<<"Client: Detached inventory update: \""<<name<<"\""<<std::endl;
1706 Inventory *inv = NULL;
1707 if(m_detached_inventories.count(name) > 0)
1708 inv = m_detached_inventories[name];
1710 inv = new Inventory(m_itemdef);
1711 m_detached_inventories[name] = inv;
1713 inv->deSerialize(is);
1715 else if(command == TOCLIENT_SHOW_FORMSPEC)
1717 std::string datastring((char*)&data[2], datasize-2);
1718 std::istringstream is(datastring, std::ios_base::binary);
1720 std::string formspec = deSerializeLongString(is);
1721 std::string formname = deSerializeString(is);
1724 event.type = CE_SHOW_FORMSPEC;
1725 // pointer is required as event is a struct only!
1726 // adding a std:string to a struct isn't possible
1727 event.show_formspec.formspec = new std::string(formspec);
1728 event.show_formspec.formname = new std::string(formname);
1729 m_client_event_queue.push_back(event);
1731 else if(command == TOCLIENT_SPAWN_PARTICLE)
1733 std::string datastring((char*)&data[2], datasize-2);
1734 std::istringstream is(datastring, std::ios_base::binary);
1736 v3f pos = readV3F1000(is);
1737 v3f vel = readV3F1000(is);
1738 v3f acc = readV3F1000(is);
1739 float expirationtime = readF1000(is);
1740 float size = readF1000(is);
1741 bool collisiondetection = readU8(is);
1742 std::string texture = deSerializeLongString(is);
1743 bool vertical = false;
1745 vertical = readU8(is);
1749 event.type = CE_SPAWN_PARTICLE;
1750 event.spawn_particle.pos = new v3f (pos);
1751 event.spawn_particle.vel = new v3f (vel);
1752 event.spawn_particle.acc = new v3f (acc);
1753 event.spawn_particle.expirationtime = expirationtime;
1754 event.spawn_particle.size = size;
1755 event.spawn_particle.collisiondetection = collisiondetection;
1756 event.spawn_particle.vertical = vertical;
1757 event.spawn_particle.texture = new std::string(texture);
1759 m_client_event_queue.push_back(event);
1761 else if(command == TOCLIENT_ADD_PARTICLESPAWNER)
1763 std::string datastring((char*)&data[2], datasize-2);
1764 std::istringstream is(datastring, std::ios_base::binary);
1766 u16 amount = readU16(is);
1767 float spawntime = readF1000(is);
1768 v3f minpos = readV3F1000(is);
1769 v3f maxpos = readV3F1000(is);
1770 v3f minvel = readV3F1000(is);
1771 v3f maxvel = readV3F1000(is);
1772 v3f minacc = readV3F1000(is);
1773 v3f maxacc = readV3F1000(is);
1774 float minexptime = readF1000(is);
1775 float maxexptime = readF1000(is);
1776 float minsize = readF1000(is);
1777 float maxsize = readF1000(is);
1778 bool collisiondetection = readU8(is);
1779 std::string texture = deSerializeLongString(is);
1780 u32 id = readU32(is);
1781 bool vertical = false;
1783 vertical = readU8(is);
1787 event.type = CE_ADD_PARTICLESPAWNER;
1788 event.add_particlespawner.amount = amount;
1789 event.add_particlespawner.spawntime = spawntime;
1790 event.add_particlespawner.minpos = new v3f (minpos);
1791 event.add_particlespawner.maxpos = new v3f (maxpos);
1792 event.add_particlespawner.minvel = new v3f (minvel);
1793 event.add_particlespawner.maxvel = new v3f (maxvel);
1794 event.add_particlespawner.minacc = new v3f (minacc);
1795 event.add_particlespawner.maxacc = new v3f (maxacc);
1796 event.add_particlespawner.minexptime = minexptime;
1797 event.add_particlespawner.maxexptime = maxexptime;
1798 event.add_particlespawner.minsize = minsize;
1799 event.add_particlespawner.maxsize = maxsize;
1800 event.add_particlespawner.collisiondetection = collisiondetection;
1801 event.add_particlespawner.vertical = vertical;
1802 event.add_particlespawner.texture = new std::string(texture);
1803 event.add_particlespawner.id = id;
1805 m_client_event_queue.push_back(event);
1807 else if(command == TOCLIENT_DELETE_PARTICLESPAWNER)
1809 std::string datastring((char*)&data[2], datasize-2);
1810 std::istringstream is(datastring, std::ios_base::binary);
1812 u32 id = readU16(is);
1815 event.type = CE_DELETE_PARTICLESPAWNER;
1816 event.delete_particlespawner.id = id;
1818 m_client_event_queue.push_back(event);
1820 else if(command == TOCLIENT_HUDADD)
1822 std::string datastring((char *)&data[2], datasize - 2);
1823 std::istringstream is(datastring, std::ios_base::binary);
1825 u32 id = readU32(is);
1826 u8 type = readU8(is);
1827 v2f pos = readV2F1000(is);
1828 std::string name = deSerializeString(is);
1829 v2f scale = readV2F1000(is);
1830 std::string text = deSerializeString(is);
1831 u32 number = readU32(is);
1832 u32 item = readU32(is);
1833 u32 dir = readU32(is);
1834 v2f align = readV2F1000(is);
1835 v2f offset = readV2F1000(is);
1839 world_pos = readV3F1000(is);
1840 }catch(SerializationError &e) {};
1842 size = readV2S32(is);
1843 } catch(SerializationError &e) {};
1846 event.type = CE_HUDADD;
1847 event.hudadd.id = id;
1848 event.hudadd.type = type;
1849 event.hudadd.pos = new v2f(pos);
1850 event.hudadd.name = new std::string(name);
1851 event.hudadd.scale = new v2f(scale);
1852 event.hudadd.text = new std::string(text);
1853 event.hudadd.number = number;
1854 event.hudadd.item = item;
1855 event.hudadd.dir = dir;
1856 event.hudadd.align = new v2f(align);
1857 event.hudadd.offset = new v2f(offset);
1858 event.hudadd.world_pos = new v3f(world_pos);
1859 event.hudadd.size = new v2s32(size);
1860 m_client_event_queue.push_back(event);
1862 else if(command == TOCLIENT_HUDRM)
1864 std::string datastring((char *)&data[2], datasize - 2);
1865 std::istringstream is(datastring, std::ios_base::binary);
1867 u32 id = readU32(is);
1870 event.type = CE_HUDRM;
1871 event.hudrm.id = id;
1872 m_client_event_queue.push_back(event);
1874 else if(command == TOCLIENT_HUDCHANGE)
1882 std::string datastring((char *)&data[2], datasize - 2);
1883 std::istringstream is(datastring, std::ios_base::binary);
1885 u32 id = readU32(is);
1886 u8 stat = (HudElementStat)readU8(is);
1888 if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
1889 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
1890 v2fdata = readV2F1000(is);
1891 else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
1892 sdata = deSerializeString(is);
1893 else if (stat == HUD_STAT_WORLD_POS)
1894 v3fdata = readV3F1000(is);
1895 else if (stat == HUD_STAT_SIZE )
1896 v2s32data = readV2S32(is);
1898 intdata = readU32(is);
1901 event.type = CE_HUDCHANGE;
1902 event.hudchange.id = id;
1903 event.hudchange.stat = (HudElementStat)stat;
1904 event.hudchange.v2fdata = new v2f(v2fdata);
1905 event.hudchange.v3fdata = new v3f(v3fdata);
1906 event.hudchange.sdata = new std::string(sdata);
1907 event.hudchange.data = intdata;
1908 event.hudchange.v2s32data = new v2s32(v2s32data);
1909 m_client_event_queue.push_back(event);
1911 else if(command == TOCLIENT_HUD_SET_FLAGS)
1913 std::string datastring((char *)&data[2], datasize - 2);
1914 std::istringstream is(datastring, std::ios_base::binary);
1916 u32 flags = readU32(is);
1917 u32 mask = readU32(is);
1919 player->hud_flags &= ~mask;
1920 player->hud_flags |= flags;
1922 else if(command == TOCLIENT_HUD_SET_PARAM)
1924 std::string datastring((char *)&data[2], datasize - 2);
1925 std::istringstream is(datastring, std::ios_base::binary);
1927 u16 param = readU16(is);
1928 std::string value = deSerializeString(is);
1930 if(param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) {
1931 s32 hotbar_itemcount = readS32((u8*) value.c_str());
1932 if(hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
1933 player->hud_hotbar_itemcount = hotbar_itemcount;
1935 else if (param == HUD_PARAM_HOTBAR_IMAGE) {
1936 ((LocalPlayer *) player)->hotbar_image = value;
1938 else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
1939 ((LocalPlayer *) player)->hotbar_selected_image = value;
1942 else if(command == TOCLIENT_SET_SKY)
1944 std::string datastring((char *)&data[2], datasize - 2);
1945 std::istringstream is(datastring, std::ios_base::binary);
1947 video::SColor *bgcolor = new video::SColor(readARGB8(is));
1948 std::string *type = new std::string(deSerializeString(is));
1949 u16 count = readU16(is);
1950 std::vector<std::string> *params = new std::vector<std::string>;
1952 for(size_t i=0; i<count; i++)
1953 params->push_back(deSerializeString(is));
1956 event.type = CE_SET_SKY;
1957 event.set_sky.bgcolor = bgcolor;
1958 event.set_sky.type = type;
1959 event.set_sky.params = params;
1960 m_client_event_queue.push_back(event);
1962 else if(command == TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO)
1964 std::string datastring((char *)&data[2], datasize - 2);
1965 std::istringstream is(datastring, std::ios_base::binary);
1967 bool do_override = readU8(is);
1968 float day_night_ratio_f = (float)readU16(is) / 65536;
1971 event.type = CE_OVERRIDE_DAY_NIGHT_RATIO;
1972 event.override_day_night_ratio.do_override = do_override;
1973 event.override_day_night_ratio.ratio_f = day_night_ratio_f;
1974 m_client_event_queue.push_back(event);
1976 else if(command == TOCLIENT_LOCAL_PLAYER_ANIMATIONS)
1978 std::string datastring((char *)&data[2], datasize - 2);
1979 std::istringstream is(datastring, std::ios_base::binary);
1981 LocalPlayer *player = m_env.getLocalPlayer();
1982 assert(player != NULL);
1984 player->local_animations[0] = readV2S32(is);
1985 player->local_animations[1] = readV2S32(is);
1986 player->local_animations[2] = readV2S32(is);
1987 player->local_animations[3] = readV2S32(is);
1988 player->local_animation_speed = readF1000(is);
1990 else if(command == TOCLIENT_EYE_OFFSET)
1992 std::string datastring((char *)&data[2], datasize - 2);
1993 std::istringstream is(datastring, std::ios_base::binary);
1995 LocalPlayer *player = m_env.getLocalPlayer();
1996 assert(player != NULL);
1998 player->eye_offset_first = readV3F1000(is);
1999 player->eye_offset_third = readV3F1000(is);
2003 infostream<<"Client: Ignoring unknown command "
2004 <<command<<std::endl;
2008 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
2010 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2011 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
2014 void Client::interact(u8 action, const PointedThing& pointed)
2016 if(m_state != LC_Ready){
2017 infostream<<"Client::interact() "
2018 "cancelled (not connected)"
2023 std::ostringstream os(std::ios_base::binary);
2029 [5] u32 length of the next item
2030 [9] serialized PointedThing
2032 0: start digging (from undersurface) or use
2033 1: stop digging (all parameters ignored)
2034 2: digging completed
2035 3: place block or item (to abovesurface)
2038 writeU16(os, TOSERVER_INTERACT);
2039 writeU8(os, action);
2040 writeU16(os, getPlayerItem());
2041 std::ostringstream tmp_os(std::ios::binary);
2042 pointed.serialize(tmp_os);
2043 os<<serializeLongString(tmp_os.str());
2045 std::string s = os.str();
2046 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2049 Send(0, data, true);
2052 void Client::sendNodemetaFields(v3s16 p, const std::string &formname,
2053 const std::map<std::string, std::string> &fields)
2055 std::ostringstream os(std::ios_base::binary);
2057 writeU16(os, TOSERVER_NODEMETA_FIELDS);
2059 os<<serializeString(formname);
2060 size_t fields_size = fields.size();
2061 assert(fields_size <= 0xFFFF);
2062 writeU16(os, (u16) (fields_size & 0xFFFF));
2063 for(std::map<std::string, std::string>::const_iterator
2064 i = fields.begin(); i != fields.end(); i++){
2065 const std::string &name = i->first;
2066 const std::string &value = i->second;
2067 os<<serializeString(name);
2068 os<<serializeLongString(value);
2072 std::string s = os.str();
2073 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2075 Send(0, data, true);
2078 void Client::sendInventoryFields(const std::string &formname,
2079 const std::map<std::string, std::string> &fields)
2081 std::ostringstream os(std::ios_base::binary);
2083 writeU16(os, TOSERVER_INVENTORY_FIELDS);
2084 os<<serializeString(formname);
2085 size_t fields_size = fields.size();
2086 assert(fields_size <= 0xFFFF);
2087 writeU16(os, (u16) (fields_size & 0xFFFF));
2088 for(std::map<std::string, std::string>::const_iterator
2089 i = fields.begin(); i != fields.end(); i++){
2090 const std::string &name = i->first;
2091 const std::string &value = i->second;
2092 os<<serializeString(name);
2093 os<<serializeLongString(value);
2097 std::string s = os.str();
2098 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2100 Send(0, data, true);
2103 void Client::sendInventoryAction(InventoryAction *a)
2105 std::ostringstream os(std::ios_base::binary);
2109 writeU16(buf, TOSERVER_INVENTORY_ACTION);
2110 os.write((char*)buf, 2);
2115 std::string s = os.str();
2116 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2118 Send(0, data, true);
2121 void Client::sendChatMessage(const std::wstring &message)
2123 std::ostringstream os(std::ios_base::binary);
2127 writeU16(buf, TOSERVER_CHAT_MESSAGE);
2128 os.write((char*)buf, 2);
2131 size_t messagesize = message.size();
2132 if (messagesize > 0xFFFF) {
2133 messagesize = 0xFFFF;
2135 writeU16(buf, (u16) messagesize);
2136 os.write((char*)buf, 2);
2139 for(unsigned int i=0; i<message.size(); i++)
2143 os.write((char*)buf, 2);
2147 std::string s = os.str();
2148 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2150 Send(0, data, true);
2153 void Client::sendChangePassword(const std::wstring &oldpassword,
2154 const std::wstring &newpassword)
2156 Player *player = m_env.getLocalPlayer();
2160 std::string playername = player->getName();
2161 std::string oldpwd = translatePassword(playername, oldpassword);
2162 std::string newpwd = translatePassword(playername, newpassword);
2164 std::ostringstream os(std::ios_base::binary);
2165 u8 buf[2+PASSWORD_SIZE*2];
2167 [0] u16 TOSERVER_PASSWORD
2168 [2] u8[28] old password
2169 [30] u8[28] new password
2172 writeU16(buf, TOSERVER_PASSWORD);
2173 for(unsigned int i=0;i<PASSWORD_SIZE-1;i++)
2175 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
2176 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
2178 buf[2+PASSWORD_SIZE-1] = 0;
2179 buf[30+PASSWORD_SIZE-1] = 0;
2180 os.write((char*)buf, 2+PASSWORD_SIZE*2);
2183 std::string s = os.str();
2184 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2186 Send(0, data, true);
2190 void Client::sendDamage(u8 damage)
2192 DSTACK(__FUNCTION_NAME);
2193 std::ostringstream os(std::ios_base::binary);
2195 writeU16(os, TOSERVER_DAMAGE);
2196 writeU8(os, damage);
2199 std::string s = os.str();
2200 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2202 Send(0, data, true);
2205 void Client::sendBreath(u16 breath)
2207 DSTACK(__FUNCTION_NAME);
2208 std::ostringstream os(std::ios_base::binary);
2210 writeU16(os, TOSERVER_BREATH);
2211 writeU16(os, breath);
2213 std::string s = os.str();
2214 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2216 Send(0, data, true);
2219 void Client::sendRespawn()
2221 DSTACK(__FUNCTION_NAME);
2222 std::ostringstream os(std::ios_base::binary);
2224 writeU16(os, TOSERVER_RESPAWN);
2227 std::string s = os.str();
2228 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2230 Send(0, data, true);
2233 void Client::sendReady()
2235 DSTACK(__FUNCTION_NAME);
2236 std::ostringstream os(std::ios_base::binary);
2238 writeU16(os, TOSERVER_CLIENT_READY);
2239 writeU8(os,VERSION_MAJOR);
2240 writeU8(os,VERSION_MINOR);
2241 writeU8(os,VERSION_PATCH_ORIG);
2244 writeU16(os,strlen(minetest_version_hash));
2245 os.write(minetest_version_hash,strlen(minetest_version_hash));
2248 std::string s = os.str();
2249 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2251 Send(0, data, true);
2254 void Client::sendPlayerPos()
2256 LocalPlayer *myplayer = m_env.getLocalPlayer();
2257 if(myplayer == NULL)
2260 // Save bandwidth by only updating position when something changed
2261 if(myplayer->last_position == myplayer->getPosition() &&
2262 myplayer->last_speed == myplayer->getSpeed() &&
2263 myplayer->last_pitch == myplayer->getPitch() &&
2264 myplayer->last_yaw == myplayer->getYaw() &&
2265 myplayer->last_keyPressed == myplayer->keyPressed)
2268 myplayer->last_position = myplayer->getPosition();
2269 myplayer->last_speed = myplayer->getSpeed();
2270 myplayer->last_pitch = myplayer->getPitch();
2271 myplayer->last_yaw = myplayer->getYaw();
2272 myplayer->last_keyPressed = myplayer->keyPressed;
2276 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2277 our_peer_id = m_con.GetPeerID();
2280 // Set peer id if not set already
2281 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2282 myplayer->peer_id = our_peer_id;
2283 // Check that an existing peer_id is the same as the connection's
2284 assert(myplayer->peer_id == our_peer_id);
2286 v3f pf = myplayer->getPosition();
2287 v3f sf = myplayer->getSpeed();
2288 s32 pitch = myplayer->getPitch() * 100;
2289 s32 yaw = myplayer->getYaw() * 100;
2290 u32 keyPressed = myplayer->keyPressed;
2292 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
2293 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
2297 [2] v3s32 position*100
2298 [2+12] v3s32 speed*100
2299 [2+12+12] s32 pitch*100
2300 [2+12+12+4] s32 yaw*100
2301 [2+12+12+4+4] u32 keyPressed
2303 SharedBuffer<u8> data(2+12+12+4+4+4);
2304 writeU16(&data[0], TOSERVER_PLAYERPOS);
2305 writeV3S32(&data[2], position);
2306 writeV3S32(&data[2+12], speed);
2307 writeS32(&data[2+12+12], pitch);
2308 writeS32(&data[2+12+12+4], yaw);
2309 writeU32(&data[2+12+12+4+4], keyPressed);
2310 // Send as unreliable
2311 Send(0, data, false);
2314 void Client::sendPlayerItem(u16 item)
2316 Player *myplayer = m_env.getLocalPlayer();
2317 if(myplayer == NULL)
2320 u16 our_peer_id = m_con.GetPeerID();
2322 // Set peer id if not set already
2323 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2324 myplayer->peer_id = our_peer_id;
2325 // Check that an existing peer_id is the same as the connection's
2326 assert(myplayer->peer_id == our_peer_id);
2328 SharedBuffer<u8> data(2+2);
2329 writeU16(&data[0], TOSERVER_PLAYERITEM);
2330 writeU16(&data[2], item);
2333 Send(0, data, true);
2336 void Client::removeNode(v3s16 p)
2338 std::map<v3s16, MapBlock*> modified_blocks;
2342 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
2344 catch(InvalidPositionException &e)
2348 for(std::map<v3s16, MapBlock * >::iterator
2349 i = modified_blocks.begin();
2350 i != modified_blocks.end(); ++i)
2352 addUpdateMeshTaskWithEdge(i->first, false, true);
2356 void Client::addNode(v3s16 p, MapNode n, bool remove_metadata)
2358 //TimeTaker timer1("Client::addNode()");
2360 std::map<v3s16, MapBlock*> modified_blocks;
2364 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
2365 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
2367 catch(InvalidPositionException &e)
2370 for(std::map<v3s16, MapBlock * >::iterator
2371 i = modified_blocks.begin();
2372 i != modified_blocks.end(); ++i)
2374 addUpdateMeshTaskWithEdge(i->first, false, true);
2378 void Client::setPlayerControl(PlayerControl &control)
2380 LocalPlayer *player = m_env.getLocalPlayer();
2381 assert(player != NULL);
2382 player->control = control;
2385 void Client::selectPlayerItem(u16 item)
2387 m_playeritem = item;
2388 m_inventory_updated = true;
2389 sendPlayerItem(item);
2392 // Returns true if the inventory of the local player has been
2393 // updated from the server. If it is true, it is set to false.
2394 bool Client::getLocalInventoryUpdated()
2396 bool updated = m_inventory_updated;
2397 m_inventory_updated = false;
2401 // Copies the inventory of the local player to parameter
2402 void Client::getLocalInventory(Inventory &dst)
2404 Player *player = m_env.getLocalPlayer();
2405 assert(player != NULL);
2406 dst = player->inventory;
2409 Inventory* Client::getInventory(const InventoryLocation &loc)
2412 case InventoryLocation::UNDEFINED:
2415 case InventoryLocation::CURRENT_PLAYER:
2417 Player *player = m_env.getLocalPlayer();
2418 assert(player != NULL);
2419 return &player->inventory;
2422 case InventoryLocation::PLAYER:
2424 Player *player = m_env.getPlayer(loc.name.c_str());
2427 return &player->inventory;
2430 case InventoryLocation::NODEMETA:
2432 NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p);
2435 return meta->getInventory();
2438 case InventoryLocation::DETACHED:
2440 if(m_detached_inventories.count(loc.name) == 0)
2442 return m_detached_inventories[loc.name];
2451 void Client::inventoryAction(InventoryAction *a)
2454 Send it to the server
2456 sendInventoryAction(a);
2459 Predict some local inventory changes
2461 a->clientApply(this, this);
2467 ClientActiveObject * Client::getSelectedActiveObject(
2469 v3f from_pos_f_on_map,
2470 core::line3d<f32> shootline_on_map
2473 std::vector<DistanceSortedActiveObject> objects;
2475 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
2478 // After this, the closest object is the first in the array.
2479 std::sort(objects.begin(), objects.end());
2481 for(unsigned int i=0; i<objects.size(); i++)
2483 ClientActiveObject *obj = objects[i].obj;
2485 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
2486 if(selection_box == NULL)
2489 v3f pos = obj->getPosition();
2491 core::aabbox3d<f32> offsetted_box(
2492 selection_box->MinEdge + pos,
2493 selection_box->MaxEdge + pos
2496 if(offsetted_box.intersectsWithLine(shootline_on_map))
2505 std::list<std::string> Client::getConnectedPlayerNames()
2507 return m_env.getPlayerNames();
2510 float Client::getAnimationTime()
2512 return m_animation_time;
2515 int Client::getCrackLevel()
2517 return m_crack_level;
2520 void Client::setHighlighted(v3s16 pos, bool show_highlighted)
2522 m_show_highlighted = show_highlighted;
2523 v3s16 old_highlighted_pos = m_highlighted_pos;
2524 m_highlighted_pos = pos;
2525 addUpdateMeshTaskForNode(old_highlighted_pos, false, true);
2526 addUpdateMeshTaskForNode(m_highlighted_pos, false, true);
2529 void Client::setCrack(int level, v3s16 pos)
2531 int old_crack_level = m_crack_level;
2532 v3s16 old_crack_pos = m_crack_pos;
2534 m_crack_level = level;
2537 if(old_crack_level >= 0 && (level < 0 || pos != old_crack_pos))
2540 addUpdateMeshTaskForNode(old_crack_pos, false, true);
2542 if(level >= 0 && (old_crack_level < 0 || pos != old_crack_pos))
2545 addUpdateMeshTaskForNode(pos, false, true);
2551 Player *player = m_env.getLocalPlayer();
2552 assert(player != NULL);
2556 u16 Client::getBreath()
2558 Player *player = m_env.getLocalPlayer();
2559 assert(player != NULL);
2560 return player->getBreath();
2563 bool Client::getChatMessage(std::wstring &message)
2565 if(m_chat_queue.size() == 0)
2567 message = m_chat_queue.pop_front();
2571 void Client::typeChatMessage(const std::wstring &message)
2573 // Discard empty line
2578 sendChatMessage(message);
2581 if (message[0] == L'/')
2583 m_chat_queue.push_back((std::wstring)L"issued command: " + message);
2587 LocalPlayer *player = m_env.getLocalPlayer();
2588 assert(player != NULL);
2589 std::wstring name = narrow_to_wide(player->getName());
2590 m_chat_queue.push_back((std::wstring)L"<" + name + L"> " + message);
2594 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
2596 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2601 Create a task to update the mesh of the block
2604 MeshMakeData *data = new MeshMakeData(this);
2607 //TimeTaker timer("data fill");
2609 // Debug: 1-6ms, avg=2ms
2611 data->setCrack(m_crack_level, m_crack_pos);
2612 data->setHighlighted(m_highlighted_pos, m_show_highlighted);
2613 data->setSmoothLighting(m_cache_smooth_lighting);
2616 // Add task to queue
2617 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server, urgent);
2620 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent)
2623 addUpdateMeshTask(blockpos, ack_to_server, urgent);
2625 catch(InvalidPositionException &e){}
2628 for (int i=0;i<6;i++)
2631 v3s16 p = blockpos + g_6dirs[i];
2632 addUpdateMeshTask(p, false, urgent);
2634 catch(InvalidPositionException &e){}
2638 void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool urgent)
2642 infostream<<"Client::addUpdateMeshTaskForNode(): "
2643 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2647 v3s16 blockpos = getNodeBlockPos(nodepos);
2648 v3s16 blockpos_relative = blockpos * MAP_BLOCKSIZE;
2651 addUpdateMeshTask(blockpos, ack_to_server, urgent);
2653 catch(InvalidPositionException &e){}
2656 if(nodepos.X == blockpos_relative.X){
2658 v3s16 p = blockpos + v3s16(-1,0,0);
2659 addUpdateMeshTask(p, false, urgent);
2661 catch(InvalidPositionException &e){}
2664 if(nodepos.Y == blockpos_relative.Y){
2666 v3s16 p = blockpos + v3s16(0,-1,0);
2667 addUpdateMeshTask(p, false, urgent);
2669 catch(InvalidPositionException &e){}
2672 if(nodepos.Z == blockpos_relative.Z){
2674 v3s16 p = blockpos + v3s16(0,0,-1);
2675 addUpdateMeshTask(p, false, urgent);
2677 catch(InvalidPositionException &e){}
2681 ClientEvent Client::getClientEvent()
2683 if(m_client_event_queue.size() == 0)
2686 event.type = CE_NONE;
2689 return m_client_event_queue.pop_front();
2692 float Client::mediaReceiveProgress()
2694 if (m_media_downloader)
2695 return m_media_downloader->getProgress();
2697 return 1.0; // downloader only exists when not yet done
2700 void Client::afterContentReceived(IrrlichtDevice *device, gui::IGUIFont* font)
2702 infostream<<"Client::afterContentReceived() started"<<std::endl;
2703 assert(m_itemdef_received);
2704 assert(m_nodedef_received);
2705 assert(mediaReceived());
2707 wchar_t* text = wgettext("Loading textures...");
2709 // Rebuild inherited images and recreate textures
2710 infostream<<"- Rebuilding images and textures"<<std::endl;
2711 draw_load_screen(text,device, guienv, 0, 70);
2712 m_tsrc->rebuildImagesAndTextures();
2716 infostream<<"- Rebuilding shaders"<<std::endl;
2717 text = wgettext("Rebuilding shaders...");
2718 draw_load_screen(text, device, guienv, 0, 75);
2719 m_shsrc->rebuildShaders();
2722 // Update node aliases
2723 infostream<<"- Updating node aliases"<<std::endl;
2724 text = wgettext("Initializing nodes...");
2725 draw_load_screen(text, device, guienv, 0, 80);
2726 m_nodedef->updateAliases(m_itemdef);
2727 m_nodedef->setNodeRegistrationStatus(true);
2728 m_nodedef->runNodeResolverCallbacks();
2731 // Update node textures and assign shaders to each tile
2732 infostream<<"- Updating node textures"<<std::endl;
2733 m_nodedef->updateTextures(this);
2735 // Preload item textures and meshes if configured to
2736 if(g_settings->getBool("preload_item_visuals"))
2738 verbosestream<<"Updating item textures and meshes"<<std::endl;
2739 text = wgettext("Item textures...");
2740 draw_load_screen(text, device, guienv, 0, 0);
2741 std::set<std::string> names = m_itemdef->getAll();
2742 size_t size = names.size();
2745 for(std::set<std::string>::const_iterator
2746 i = names.begin(); i != names.end(); ++i)
2748 // Asking for these caches the result
2749 m_itemdef->getInventoryTexture(*i, this);
2750 m_itemdef->getWieldMesh(*i, this);
2752 percent = (count * 100 / size * 0.2) + 80;
2753 draw_load_screen(text, device, guienv, 0, percent);
2758 // Start mesh update thread after setting up content definitions
2759 infostream<<"- Starting mesh update thread"<<std::endl;
2760 m_mesh_update_thread.Start();
2764 text = wgettext("Done!");
2765 draw_load_screen(text, device, guienv, 0, 100);
2766 infostream<<"Client::afterContentReceived() done"<<std::endl;
2770 float Client::getRTT(void)
2772 return m_con.getPeerStat(PEER_ID_SERVER,con::AVG_RTT);
2775 float Client::getCurRate(void)
2777 return ( m_con.getLocalStat(con::CUR_INC_RATE) +
2778 m_con.getLocalStat(con::CUR_DL_RATE));
2781 float Client::getAvgRate(void)
2783 return ( m_con.getLocalStat(con::AVG_INC_RATE) +
2784 m_con.getLocalStat(con::AVG_DL_RATE));
2787 void Client::makeScreenshot(IrrlichtDevice *device)
2789 irr::video::IVideoDriver *driver = device->getVideoDriver();
2790 irr::video::IImage* const raw_image = driver->createScreenShot();
2792 irr::video::IImage* const image = driver->createImage(video::ECF_R8G8B8,
2793 raw_image->getDimension());
2796 raw_image->copyTo(image);
2797 irr::c8 filename[256];
2798 snprintf(filename, sizeof(filename), "%s" DIR_DELIM "screenshot_%u.png",
2799 g_settings->get("screenshot_path").c_str(),
2800 device->getTimer()->getRealTime());
2801 std::ostringstream sstr;
2802 if (driver->writeImageToFile(image, filename)) {
2803 sstr << "Saved screenshot to '" << filename << "'";
2805 sstr << "Failed to save screenshot '" << filename << "'";
2807 m_chat_queue.push_back(narrow_to_wide(sstr.str()));
2808 infostream << sstr.str() << std::endl;
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 scene::ISceneManager* Client::getSceneManager()
2840 return m_device->getSceneManager();
2842 u16 Client::allocateUnknownNodeId(const std::string &name)
2844 errorstream<<"Client::allocateUnknownNodeId(): "
2845 <<"Client cannot allocate node IDs"<<std::endl;
2847 return CONTENT_IGNORE;
2849 ISoundManager* Client::getSoundManager()
2853 MtEventManager* Client::getEventManager()
2858 ParticleManager* Client::getParticleManager()
2860 return &m_particle_manager;
2863 scene::IAnimatedMesh* Client::getMesh(const std::string &filename)
2865 std::map<std::string, std::string>::const_iterator i =
2866 m_mesh_data.find(filename);
2867 if(i == m_mesh_data.end()){
2868 errorstream<<"Client::getMesh(): Mesh not found: \""<<filename<<"\""
2872 const std::string &data = i->second;
2873 scene::ISceneManager *smgr = m_device->getSceneManager();
2875 // Create the mesh, remove it from cache and return it
2876 // This allows unique vertex colors and other properties for each instance
2877 Buffer<char> data_rw(data.c_str(), data.size()); // Const-incorrect Irrlicht
2878 io::IFileSystem *irrfs = m_device->getFileSystem();
2879 io::IReadFile *rfile = irrfs->createMemoryReadFile(
2880 *data_rw, data_rw.getSize(), filename.c_str());
2883 scene::IAnimatedMesh *mesh = smgr->getMesh(rfile);
2885 // NOTE: By playing with Irrlicht refcounts, maybe we could cache a bunch
2886 // of uniquely named instances and re-use them
2888 smgr->getMeshCache()->removeMesh(mesh);