3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include "clientserver.h"
24 #include "jthread/jmutexautolock.h"
29 #include "mapsector.h"
30 #include "mapblock_mesh.h"
36 #include "nodemetadata.h"
40 #include <IFileSystem.h>
42 #include "clientmap.h"
43 #include "clientmedia.h"
45 #include "util/string.h"
46 #include "IMeshCache.h"
47 #include "serialization.h"
48 #include "util/serialize.h"
50 #include "cmake_config_githash.h"
51 #include "util/directiontables.h"
52 #include "util/pointedthing.h"
59 QueuedMeshUpdate::QueuedMeshUpdate():
62 ack_block_to_server(false)
66 QueuedMeshUpdate::~QueuedMeshUpdate()
76 MeshUpdateQueue::MeshUpdateQueue()
80 MeshUpdateQueue::~MeshUpdateQueue()
82 JMutexAutoLock lock(m_mutex);
84 for(std::vector<QueuedMeshUpdate*>::iterator
86 i != m_queue.end(); i++)
88 QueuedMeshUpdate *q = *i;
94 peer_id=0 adds with nobody to send to
96 void MeshUpdateQueue::addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_server, bool urgent)
98 DSTACK(__FUNCTION_NAME);
102 JMutexAutoLock lock(m_mutex);
108 Find if block is already in queue.
109 If it is, update the data and quit.
111 for(std::vector<QueuedMeshUpdate*>::iterator
113 i != m_queue.end(); i++)
115 QueuedMeshUpdate *q = *i;
121 if(ack_block_to_server)
122 q->ack_block_to_server = true;
130 QueuedMeshUpdate *q = new QueuedMeshUpdate;
133 q->ack_block_to_server = ack_block_to_server;
134 m_queue.push_back(q);
137 // Returned pointer must be deleted
138 // Returns NULL if queue is empty
139 QueuedMeshUpdate * MeshUpdateQueue::pop()
141 JMutexAutoLock lock(m_mutex);
143 bool must_be_urgent = !m_urgents.empty();
144 for(std::vector<QueuedMeshUpdate*>::iterator
146 i != m_queue.end(); i++)
148 QueuedMeshUpdate *q = *i;
149 if(must_be_urgent && m_urgents.count(q->p) == 0)
152 m_urgents.erase(q->p);
162 void * MeshUpdateThread::Thread()
166 log_register_thread("MeshUpdateThread");
168 DSTACK(__FUNCTION_NAME);
170 BEGIN_DEBUG_EXCEPTION_HANDLER
172 while(!StopRequested())
174 QueuedMeshUpdate *q = m_queue_in.pop();
181 ScopeProfiler sp(g_profiler, "Client: Mesh making");
183 MapBlockMesh *mesh_new = new MapBlockMesh(q->data, m_camera_offset);
184 if(mesh_new->getMesh()->getMeshBufferCount() == 0)
193 r.ack_block_to_server = q->ack_block_to_server;
195 m_queue_out.push_back(r);
200 END_DEBUG_EXCEPTION_HANDLER(errorstream)
210 IrrlichtDevice *device,
211 const char *playername,
212 std::string password,
213 MapDrawControl &control,
214 IWritableTextureSource *tsrc,
215 IWritableShaderSource *shsrc,
216 IWritableItemDefManager *itemdef,
217 IWritableNodeDefManager *nodedef,
218 ISoundManager *sound,
219 MtEventManager *event,
228 m_mesh_update_thread(this),
230 new ClientMap(this, this, control,
231 device->getSceneManager()->getRootSceneNode(),
232 device->getSceneManager(), 666),
233 device->getSceneManager(),
236 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, ipv6, this),
238 m_server_ser_ver(SER_FMT_VER_INVALID),
240 m_inventory_updated(false),
241 m_inventory_from_server(NULL),
242 m_inventory_from_server_age(0.0),
247 m_password(password),
248 m_access_denied(false),
249 m_itemdef_received(false),
250 m_nodedef_received(false),
251 m_media_downloader(new ClientMediaDownloader()),
252 m_time_of_day_set(false),
253 m_last_time_of_day_f(-1),
254 m_time_of_day_update_timer(0),
255 m_recommended_send_interval(0.1),
256 m_removed_sounds_check_timer(0),
259 m_packetcounter_timer = 0.0;
260 //m_delete_unused_sectors_timer = 0.0;
261 m_connection_reinit_timer = 0.0;
262 m_avg_rtt_timer = 0.0;
263 m_playerpos_send_timer = 0.0;
264 m_ignore_damage_timer = 0.0;
270 Player *player = new LocalPlayer(this);
272 player->updateName(playername);
274 m_env.addPlayer(player);
280 //request all client managed threads to stop
281 m_mesh_update_thread.Stop();
284 bool Client::isShutdown()
287 if (!m_mesh_update_thread.IsRunning()) return true;
296 m_mesh_update_thread.Stop();
297 m_mesh_update_thread.Wait();
298 while(!m_mesh_update_thread.m_queue_out.empty()) {
299 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_frontNoEx();
304 delete m_inventory_from_server;
306 // Delete detached inventories
307 for(std::map<std::string, Inventory*>::iterator
308 i = m_detached_inventories.begin();
309 i != m_detached_inventories.end(); i++){
313 // cleanup 3d model meshes on client shutdown
314 while (m_device->getSceneManager()->getMeshCache()->getMeshCount() != 0) {
315 scene::IAnimatedMesh * mesh =
316 m_device->getSceneManager()->getMeshCache()->getMeshByIndex(0);
319 m_device->getSceneManager()->getMeshCache()->removeMesh(mesh);
323 void Client::connect(Address address)
325 DSTACK(__FUNCTION_NAME);
326 m_con.SetTimeoutMs(0);
327 m_con.Connect(address);
330 void Client::step(float dtime)
332 DSTACK(__FUNCTION_NAME);
338 if(m_ignore_damage_timer > dtime)
339 m_ignore_damage_timer -= dtime;
341 m_ignore_damage_timer = 0.0;
343 m_animation_time += dtime;
344 if(m_animation_time > 60.0)
345 m_animation_time -= 60.0;
347 m_time_of_day_update_timer += dtime;
355 float &counter = m_packetcounter_timer;
361 infostream<<"Client packetcounter (20s):"<<std::endl;
362 m_packetcounter.print(infostream);
363 m_packetcounter.clear();
370 Delete unused sectors
372 NOTE: This jams the game for a while because deleting sectors
376 float &counter = m_delete_unused_sectors_timer;
384 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
386 core::list<v3s16> deleted_blocks;
388 float delete_unused_sectors_timeout =
389 g_settings->getFloat("client_delete_unused_sectors_timeout");
391 // Delete sector blocks
392 /*u32 num = m_env.getMap().unloadUnusedData
393 (delete_unused_sectors_timeout,
394 true, &deleted_blocks);*/
396 // Delete whole sectors
397 m_env.getMap().unloadUnusedData
398 (delete_unused_sectors_timeout,
401 if(deleted_blocks.size() > 0)
403 /*infostream<<"Client: Deleted blocks of "<<num
404 <<" unused sectors"<<std::endl;*/
405 /*infostream<<"Client: Deleted "<<num
406 <<" unused sectors"<<std::endl;*/
412 // Env is locked so con can be locked.
413 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
415 core::list<v3s16>::Iterator i = deleted_blocks.begin();
416 core::list<v3s16> sendlist;
419 if(sendlist.size() == 255 || i == deleted_blocks.end())
421 if(sendlist.size() == 0)
430 u32 replysize = 2+1+6*sendlist.size();
431 SharedBuffer<u8> reply(replysize);
432 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
433 reply[2] = sendlist.size();
435 for(core::list<v3s16>::Iterator
436 j = sendlist.begin();
437 j != sendlist.end(); j++)
439 writeV3S16(&reply[2+1+6*k], *j);
442 m_con.Send(PEER_ID_SERVER, 1, reply, true);
444 if(i == deleted_blocks.end())
450 sendlist.push_back(*i);
458 if(m_state == LC_Created)
460 float &counter = m_connection_reinit_timer;
466 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
468 Player *myplayer = m_env.getLocalPlayer();
469 assert(myplayer != NULL);
471 // Send TOSERVER_INIT
472 // [0] u16 TOSERVER_INIT
473 // [2] u8 SER_FMT_VER_HIGHEST_READ
474 // [3] u8[20] player_name
475 // [23] u8[28] password (new in some version)
476 // [51] u16 minimum supported network protocol version (added sometime)
477 // [53] u16 maximum supported network protocol version (added later than the previous one)
478 SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2+2);
479 writeU16(&data[0], TOSERVER_INIT);
480 writeU8(&data[2], SER_FMT_VER_HIGHEST_READ);
482 memset((char*)&data[3], 0, PLAYERNAME_SIZE);
483 snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
485 /*infostream<<"Client: sending initial password hash: \""<<m_password<<"\""
488 memset((char*)&data[23], 0, PASSWORD_SIZE);
489 snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
491 writeU16(&data[51], CLIENT_PROTOCOL_VERSION_MIN);
492 writeU16(&data[53], CLIENT_PROTOCOL_VERSION_MAX);
494 // Send as unreliable
495 Send(1, data, false);
498 // Not connected, return
503 Do stuff if connected
507 Run Map's timers and unload unused data
509 const float map_timer_and_unload_dtime = 5.25;
510 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
512 ScopeProfiler sp(g_profiler, "Client: map timer and unload");
513 std::list<v3s16> deleted_blocks;
514 m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
515 g_settings->getFloat("client_unload_unused_data_timeout"),
518 /*if(deleted_blocks.size() > 0)
519 infostream<<"Client: Unloaded "<<deleted_blocks.size()
520 <<" unused blocks"<<std::endl;*/
524 NOTE: This loop is intentionally iterated the way it is.
527 std::list<v3s16>::iterator i = deleted_blocks.begin();
528 std::list<v3s16> sendlist;
531 if(sendlist.size() == 255 || i == deleted_blocks.end())
533 if(sendlist.size() == 0)
542 u32 replysize = 2+1+6*sendlist.size();
543 SharedBuffer<u8> reply(replysize);
544 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
545 reply[2] = sendlist.size();
547 for(std::list<v3s16>::iterator
548 j = sendlist.begin();
549 j != sendlist.end(); ++j)
551 writeV3S16(&reply[2+1+6*k], *j);
554 m_con.Send(PEER_ID_SERVER, 2, reply, true);
556 if(i == deleted_blocks.end())
562 sendlist.push_back(*i);
571 // Control local player (0ms)
572 LocalPlayer *player = m_env.getLocalPlayer();
573 assert(player != NULL);
574 player->applyControl(dtime);
584 ClientEnvEvent event = m_env.getClientEvent();
585 if(event.type == CEE_NONE)
589 else if(event.type == CEE_PLAYER_DAMAGE)
591 if(m_ignore_damage_timer <= 0)
593 u8 damage = event.player_damage.amount;
595 if(event.player_damage.send_to_server)
598 // Add to ClientEvent queue
600 event.type = CE_PLAYER_DAMAGE;
601 event.player_damage.amount = damage;
602 m_client_event_queue.push_back(event);
605 else if(event.type == CEE_PLAYER_BREATH)
607 u16 breath = event.player_breath.amount;
617 float &counter = m_avg_rtt_timer;
622 // connectedAndInitialized() is true, peer exists.
623 float avg_rtt = getRTT();
624 infostream<<"Client: avg_rtt="<<avg_rtt<<std::endl;
629 Send player position to server
632 float &counter = m_playerpos_send_timer;
634 if((m_state == LC_Ready) && (counter >= m_recommended_send_interval))
642 Replace updated meshes
645 int num_processed_meshes = 0;
646 while(!m_mesh_update_thread.m_queue_out.empty())
648 num_processed_meshes++;
649 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_frontNoEx();
650 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
653 // Delete the old mesh
654 if(block->mesh != NULL)
656 // TODO: Remove hardware buffers of meshbuffers of block->mesh
661 // Replace with the new mesh
662 block->mesh = r.mesh;
666 if(r.ack_block_to_server)
678 u32 replysize = 2+1+6;
679 SharedBuffer<u8> reply(replysize);
680 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
682 writeV3S16(&reply[3], r.p);
684 m_con.Send(PEER_ID_SERVER, 2, reply, true);
687 if(num_processed_meshes > 0)
688 g_profiler->graphAdd("num_processed_meshes", num_processed_meshes);
694 if (m_media_downloader && m_media_downloader->isStarted()) {
695 m_media_downloader->step(this);
696 if (m_media_downloader->isDone()) {
698 delete m_media_downloader;
699 m_media_downloader = NULL;
704 If the server didn't update the inventory in a while, revert
705 the local inventory (so the player notices the lag problem
706 and knows something is wrong).
708 if(m_inventory_from_server)
710 float interval = 10.0;
711 float count_before = floor(m_inventory_from_server_age / interval);
713 m_inventory_from_server_age += dtime;
715 float count_after = floor(m_inventory_from_server_age / interval);
717 if(count_after != count_before)
719 // Do this every <interval> seconds after TOCLIENT_INVENTORY
720 // Reset the locally changed inventory to the authoritative inventory
721 Player *player = m_env.getLocalPlayer();
722 player->inventory = *m_inventory_from_server;
723 m_inventory_updated = true;
728 Update positions of sounds attached to objects
731 for(std::map<int, u16>::iterator
732 i = m_sounds_to_objects.begin();
733 i != m_sounds_to_objects.end(); i++)
735 int client_id = i->first;
736 u16 object_id = i->second;
737 ClientActiveObject *cao = m_env.getActiveObject(object_id);
740 v3f pos = cao->getPosition();
741 m_sound->updateSoundPosition(client_id, pos);
746 Handle removed remotely initiated sounds
748 m_removed_sounds_check_timer += dtime;
749 if(m_removed_sounds_check_timer >= 2.32)
751 m_removed_sounds_check_timer = 0;
752 // Find removed sounds and clear references to them
753 std::set<s32> removed_server_ids;
754 for(std::map<s32, int>::iterator
755 i = m_sounds_server_to_client.begin();
756 i != m_sounds_server_to_client.end();)
758 s32 server_id = i->first;
759 int client_id = i->second;
761 if(!m_sound->soundExists(client_id)){
762 m_sounds_server_to_client.erase(server_id);
763 m_sounds_client_to_server.erase(client_id);
764 m_sounds_to_objects.erase(client_id);
765 removed_server_ids.insert(server_id);
769 if(removed_server_ids.size() != 0)
771 std::ostringstream os(std::ios_base::binary);
772 writeU16(os, TOSERVER_REMOVED_SOUNDS);
773 size_t server_ids = removed_server_ids.size();
774 assert(server_ids <= 0xFFFF);
775 writeU16(os, (u16) (server_ids & 0xFFFF));
776 for(std::set<s32>::iterator i = removed_server_ids.begin();
777 i != removed_server_ids.end(); i++)
779 std::string s = os.str();
780 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
787 bool Client::loadMedia(const std::string &data, const std::string &filename)
789 // Silly irrlicht's const-incorrectness
790 Buffer<char> data_rw(data.c_str(), data.size());
794 const char *image_ext[] = {
795 ".png", ".jpg", ".bmp", ".tga",
796 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
799 name = removeStringEnd(filename, image_ext);
802 verbosestream<<"Client: Attempting to load image "
803 <<"file \""<<filename<<"\""<<std::endl;
805 io::IFileSystem *irrfs = m_device->getFileSystem();
806 video::IVideoDriver *vdrv = m_device->getVideoDriver();
808 // Create an irrlicht memory file
809 io::IReadFile *rfile = irrfs->createMemoryReadFile(
810 *data_rw, data_rw.getSize(), "_tempreadfile");
813 video::IImage *img = vdrv->createImageFromFile(rfile);
815 errorstream<<"Client: Cannot create image from data of "
816 <<"file \""<<filename<<"\""<<std::endl;
821 m_tsrc->insertSourceImage(filename, img);
828 const char *sound_ext[] = {
829 ".0.ogg", ".1.ogg", ".2.ogg", ".3.ogg", ".4.ogg",
830 ".5.ogg", ".6.ogg", ".7.ogg", ".8.ogg", ".9.ogg",
833 name = removeStringEnd(filename, sound_ext);
836 verbosestream<<"Client: Attempting to load sound "
837 <<"file \""<<filename<<"\""<<std::endl;
838 m_sound->loadSoundData(name, data);
842 const char *model_ext[] = {
843 ".x", ".b3d", ".md2", ".obj",
846 name = removeStringEnd(filename, model_ext);
849 verbosestream<<"Client: Storing model into memory: "
850 <<"\""<<filename<<"\""<<std::endl;
851 if(m_mesh_data.count(filename))
852 errorstream<<"Multiple models with name \""<<filename.c_str()
853 <<"\" found; replacing previous model"<<std::endl;
854 m_mesh_data[filename] = data;
858 errorstream<<"Client: Don't know how to load file \""
859 <<filename<<"\""<<std::endl;
863 // Virtual methods from con::PeerHandler
864 void Client::peerAdded(con::Peer *peer)
866 infostream<<"Client::peerAdded(): peer->id="
867 <<peer->id<<std::endl;
869 void Client::deletingPeer(con::Peer *peer, bool timeout)
871 infostream<<"Client::deletingPeer(): "
872 "Server Peer is getting deleted "
873 <<"(timeout="<<timeout<<")"<<std::endl;
878 u16 number of files requested
884 void Client::request_media(const std::list<std::string> &file_requests)
886 std::ostringstream os(std::ios_base::binary);
887 writeU16(os, TOSERVER_REQUEST_MEDIA);
888 size_t file_requests_size = file_requests.size();
889 assert(file_requests_size <= 0xFFFF);
890 writeU16(os, (u16) (file_requests_size & 0xFFFF));
892 for(std::list<std::string>::const_iterator i = file_requests.begin();
893 i != file_requests.end(); ++i) {
894 os<<serializeString(*i);
898 std::string s = os.str();
899 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
902 infostream<<"Client: Sending media request list to server ("
903 <<file_requests.size()<<" files)"<<std::endl;
906 void Client::received_media()
908 // notify server we received everything
909 std::ostringstream os(std::ios_base::binary);
910 writeU16(os, TOSERVER_RECEIVED_MEDIA);
911 std::string s = os.str();
912 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
915 infostream<<"Client: Notifying server that we received all media"
919 void Client::ReceiveAll()
921 DSTACK(__FUNCTION_NAME);
922 u32 start_ms = porting::getTimeMs();
925 // Limit time even if there would be huge amounts of data to
927 if(porting::getTimeMs() > start_ms + 100)
932 g_profiler->graphAdd("client_received_packets", 1);
934 catch(con::NoIncomingDataException &e)
938 catch(con::InvalidIncomingDataException &e)
940 infostream<<"Client::ReceiveAll(): "
941 "InvalidIncomingDataException: what()="
942 <<e.what()<<std::endl;
947 void Client::Receive()
949 DSTACK(__FUNCTION_NAME);
950 SharedBuffer<u8> data;
952 u32 datasize = m_con.Receive(sender_peer_id, data);
953 ProcessData(*data, datasize, sender_peer_id);
957 sender_peer_id given to this shall be quaranteed to be a valid peer
959 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
961 DSTACK(__FUNCTION_NAME);
963 // Ignore packets that don't even fit a command
966 m_packetcounter.add(60000);
970 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
972 //infostream<<"Client: received command="<<command<<std::endl;
973 m_packetcounter.add((u16)command);
976 If this check is removed, be sure to change the queue
977 system to know the ids
979 if(sender_peer_id != PEER_ID_SERVER)
981 infostream<<"Client::ProcessData(): Discarding data not "
982 "coming from server: peer_id="<<sender_peer_id
987 u8 ser_version = m_server_ser_ver;
989 if(command == TOCLIENT_INIT)
994 u8 deployed = data[2];
996 infostream<<"Client: TOCLIENT_INIT received with "
997 "deployed="<<((int)deployed&0xff)<<std::endl;
999 if(!ser_ver_supported(deployed))
1001 infostream<<"Client: TOCLIENT_INIT: Server sent "
1002 <<"unsupported ser_fmt_ver"<<std::endl;
1006 m_server_ser_ver = deployed;
1008 // Get player position
1009 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
1010 if(datasize >= 2+1+6)
1011 playerpos_s16 = readV3S16(&data[2+1]);
1012 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
1015 // Set player position
1016 Player *player = m_env.getLocalPlayer();
1017 assert(player != NULL);
1018 player->setPosition(playerpos_f);
1020 if(datasize >= 2+1+6+8)
1023 m_map_seed = readU64(&data[2+1+6]);
1024 infostream<<"Client: received map seed: "<<m_map_seed<<std::endl;
1027 if(datasize >= 2+1+6+8+4)
1030 m_recommended_send_interval = readF1000(&data[2+1+6+8]);
1031 infostream<<"Client: received recommended send interval "
1032 <<m_recommended_send_interval<<std::endl;
1037 SharedBuffer<u8> reply(replysize);
1038 writeU16(&reply[0], TOSERVER_INIT2);
1040 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1047 if(command == TOCLIENT_ACCESS_DENIED)
1049 // The server didn't like our password. Note, this needs
1050 // to be processed even if the serialisation format has
1051 // not been agreed yet, the same as TOCLIENT_INIT.
1052 m_access_denied = true;
1053 m_access_denied_reason = L"Unknown";
1056 std::string datastring((char*)&data[2], datasize-2);
1057 std::istringstream is(datastring, std::ios_base::binary);
1058 m_access_denied_reason = deSerializeWideString(is);
1063 if(ser_version == SER_FMT_VER_INVALID)
1065 infostream<<"Client: Server serialization"
1066 " format invalid or not initialized."
1067 " Skipping incoming command="<<command<<std::endl;
1072 Handle runtime commands
1074 // there's no sane reason why we shouldn't have a player and
1075 // almost everyone needs a player reference
1076 Player *player = m_env.getLocalPlayer();
1077 assert(player != NULL);
1079 if(command == TOCLIENT_REMOVENODE)
1084 p.X = readS16(&data[2]);
1085 p.Y = readS16(&data[4]);
1086 p.Z = readS16(&data[6]);
1089 else if(command == TOCLIENT_ADDNODE)
1091 if(datasize < 8 + MapNode::serializedLength(ser_version))
1095 p.X = readS16(&data[2]);
1096 p.Y = readS16(&data[4]);
1097 p.Z = readS16(&data[6]);
1100 n.deSerialize(&data[8], ser_version);
1102 bool remove_metadata = true;
1103 u32 index = 8 + MapNode::serializedLength(ser_version);
1104 if ((datasize >= index+1) && data[index]){
1105 remove_metadata = false;
1108 addNode(p, n, remove_metadata);
1110 else if(command == TOCLIENT_BLOCKDATA)
1112 // Ignore too small packet
1117 p.X = readS16(&data[2]);
1118 p.Y = readS16(&data[4]);
1119 p.Z = readS16(&data[6]);
1121 std::string datastring((char*)&data[8], datasize-8);
1122 std::istringstream istr(datastring, std::ios_base::binary);
1127 v2s16 p2d(p.X, p.Z);
1128 sector = m_env.getMap().emergeSector(p2d);
1130 assert(sector->getPos() == p2d);
1132 block = sector->getBlockNoCreateNoEx(p.Y);
1136 Update an existing block
1138 block->deSerialize(istr, ser_version, false);
1139 block->deSerializeNetworkSpecific(istr);
1146 block = new MapBlock(&m_env.getMap(), p, this);
1147 block->deSerialize(istr, ser_version, false);
1148 block->deSerializeNetworkSpecific(istr);
1149 sector->insertBlock(block);
1153 Add it to mesh update queue and set it to be acknowledged after update.
1155 addUpdateMeshTaskWithEdge(p, true);
1157 else if(command == TOCLIENT_INVENTORY)
1162 std::string datastring((char*)&data[2], datasize-2);
1163 std::istringstream is(datastring, std::ios_base::binary);
1165 player->inventory.deSerialize(is);
1167 m_inventory_updated = true;
1169 delete m_inventory_from_server;
1170 m_inventory_from_server = new Inventory(player->inventory);
1171 m_inventory_from_server_age = 0.0;
1174 else if(command == TOCLIENT_TIME_OF_DAY)
1179 u16 time_of_day = readU16(&data[2]);
1180 time_of_day = time_of_day % 24000;
1181 float time_speed = 0;
1183 if(datasize >= 2 + 2 + 4)
1185 time_speed = readF1000(&data[4]);
1188 // Old message; try to approximate speed of time by ourselves
1189 float time_of_day_f = (float)time_of_day / 24000.0;
1190 float tod_diff_f = 0;
1192 if(time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
1193 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0;
1195 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
1197 m_last_time_of_day_f = time_of_day_f;
1198 float time_diff = m_time_of_day_update_timer;
1199 m_time_of_day_update_timer = 0;
1201 if(m_time_of_day_set){
1202 time_speed = (3600.0*24.0) * tod_diff_f / time_diff;
1203 infostream<<"Client: Measured time_of_day speed (old format): "
1204 <<time_speed<<" tod_diff_f="<<tod_diff_f
1205 <<" time_diff="<<time_diff<<std::endl;
1209 // Update environment
1210 m_env.setTimeOfDay(time_of_day);
1211 m_env.setTimeOfDaySpeed(time_speed);
1212 m_time_of_day_set = true;
1214 u32 dr = m_env.getDayNightRatio();
1215 verbosestream<<"Client: time_of_day="<<time_of_day
1216 <<" time_speed="<<time_speed
1217 <<" dr="<<dr<<std::endl;
1219 else if(command == TOCLIENT_CHAT_MESSAGE)
1227 std::string datastring((char*)&data[2], datasize-2);
1228 std::istringstream is(datastring, std::ios_base::binary);
1231 is.read((char*) buf, 2);
1232 u16 len = readU16(buf);
1234 std::wstring message;
1235 for(unsigned int i=0; i<len; i++)
1237 is.read((char*)buf, 2);
1238 message += (wchar_t)readU16(buf);
1241 m_chat_queue.push_back(message);
1243 else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1247 u16 count of removed objects
1248 for all removed objects {
1251 u16 count of added objects
1252 for all added objects {
1255 u32 initialization data length
1256 string initialization data
1261 // Get all data except the command number
1262 std::string datastring((char*)&data[2], datasize-2);
1263 // Throw them in an istringstream
1264 std::istringstream is(datastring, std::ios_base::binary);
1266 // Read removed objects
1268 u16 removed_count = readU16((u8*)buf);
1269 for(unsigned int i=0; i<removed_count; i++)
1272 u16 id = readU16((u8*)buf);
1273 m_env.removeActiveObject(id);
1276 // Read added objects
1278 u16 added_count = readU16((u8*)buf);
1279 for(unsigned int i=0; i<added_count; i++)
1282 u16 id = readU16((u8*)buf);
1284 u8 type = readU8((u8*)buf);
1285 std::string data = deSerializeLongString(is);
1287 m_env.addActiveObject(id, type, data);
1290 else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1302 // Get all data except the command number
1303 std::string datastring((char*)&data[2], datasize-2);
1304 // Throw them in an istringstream
1305 std::istringstream is(datastring, std::ios_base::binary);
1307 while(is.eof() == false)
1310 u16 id = readU16((u8*)buf);
1314 size_t message_size = readU16((u8*)buf);
1315 std::string message;
1316 message.reserve(message_size);
1317 for(unsigned int i=0; i<message_size; i++)
1320 message.append(buf, 1);
1322 // Pass on to the environment
1323 m_env.processActiveObjectMessage(id, message);
1326 else if(command == TOCLIENT_MOVEMENT)
1328 std::string datastring((char*)&data[2], datasize-2);
1329 std::istringstream is(datastring, std::ios_base::binary);
1331 player->movement_acceleration_default = readF1000(is) * BS;
1332 player->movement_acceleration_air = readF1000(is) * BS;
1333 player->movement_acceleration_fast = readF1000(is) * BS;
1334 player->movement_speed_walk = readF1000(is) * BS;
1335 player->movement_speed_crouch = readF1000(is) * BS;
1336 player->movement_speed_fast = readF1000(is) * BS;
1337 player->movement_speed_climb = readF1000(is) * BS;
1338 player->movement_speed_jump = readF1000(is) * BS;
1339 player->movement_liquid_fluidity = readF1000(is) * BS;
1340 player->movement_liquid_fluidity_smooth = readF1000(is) * BS;
1341 player->movement_liquid_sink = readF1000(is) * BS;
1342 player->movement_gravity = readF1000(is) * BS;
1344 else if(command == TOCLIENT_HP)
1346 std::string datastring((char*)&data[2], datasize-2);
1347 std::istringstream is(datastring, std::ios_base::binary);
1349 u8 oldhp = player->hp;
1355 // Add to ClientEvent queue
1357 event.type = CE_PLAYER_DAMAGE;
1358 event.player_damage.amount = oldhp - hp;
1359 m_client_event_queue.push_back(event);
1362 else if(command == TOCLIENT_BREATH)
1364 std::string datastring((char*)&data[2], datasize-2);
1365 std::istringstream is(datastring, std::ios_base::binary);
1367 player->setBreath(readU16(is));
1369 else if(command == TOCLIENT_MOVE_PLAYER)
1371 std::string datastring((char*)&data[2], datasize-2);
1372 std::istringstream is(datastring, std::ios_base::binary);
1374 v3f pos = readV3F1000(is);
1375 f32 pitch = readF1000(is);
1376 f32 yaw = readF1000(is);
1377 player->setPosition(pos);
1379 infostream<<"Client got TOCLIENT_MOVE_PLAYER"
1380 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1386 Add to ClientEvent queue.
1387 This has to be sent to the main program because otherwise
1388 it would just force the pitch and yaw values to whatever
1389 the camera points to.
1392 event.type = CE_PLAYER_FORCE_MOVE;
1393 event.player_force_move.pitch = pitch;
1394 event.player_force_move.yaw = yaw;
1395 m_client_event_queue.push_back(event);
1397 // Ignore damage for a few seconds, so that the player doesn't
1398 // get damage from falling on ground
1399 m_ignore_damage_timer = 3.0;
1401 else if(command == TOCLIENT_PLAYERITEM)
1403 infostream<<"Client: WARNING: Ignoring TOCLIENT_PLAYERITEM"<<std::endl;
1405 else if(command == TOCLIENT_DEATHSCREEN)
1407 std::string datastring((char*)&data[2], datasize-2);
1408 std::istringstream is(datastring, std::ios_base::binary);
1410 bool set_camera_point_target = readU8(is);
1411 v3f camera_point_target = readV3F1000(is);
1414 event.type = CE_DEATHSCREEN;
1415 event.deathscreen.set_camera_point_target = set_camera_point_target;
1416 event.deathscreen.camera_point_target_x = camera_point_target.X;
1417 event.deathscreen.camera_point_target_y = camera_point_target.Y;
1418 event.deathscreen.camera_point_target_z = camera_point_target.Z;
1419 m_client_event_queue.push_back(event);
1421 else if(command == TOCLIENT_ANNOUNCE_MEDIA)
1423 std::string datastring((char*)&data[2], datasize-2);
1424 std::istringstream is(datastring, std::ios_base::binary);
1426 int num_files = readU16(is);
1428 infostream<<"Client: Received media announcement: packet size: "
1429 <<datasize<<std::endl;
1431 if (m_media_downloader == NULL ||
1432 m_media_downloader->isStarted()) {
1433 const char *problem = m_media_downloader ?
1434 "we already saw another announcement" :
1435 "all media has been received already";
1436 errorstream<<"Client: Received media announcement but "
1438 <<" files="<<num_files
1439 <<" size="<<datasize<<std::endl;
1443 // Mesh update thread must be stopped while
1444 // updating content definitions
1445 assert(!m_mesh_update_thread.IsRunning());
1447 for(int i=0; i<num_files; i++)
1449 std::string name = deSerializeString(is);
1450 std::string sha1_base64 = deSerializeString(is);
1451 std::string sha1_raw = base64_decode(sha1_base64);
1452 m_media_downloader->addFile(name, sha1_raw);
1455 std::vector<std::string> remote_media;
1457 Strfnd sf(deSerializeString(is));
1458 while(!sf.atend()) {
1459 std::string baseurl = trim(sf.next(","));
1461 m_media_downloader->addRemoteServer(baseurl);
1464 catch(SerializationError& e) {
1465 // not supported by server or turned off
1468 m_media_downloader->step(this);
1470 else if(command == TOCLIENT_MEDIA)
1472 std::string datastring((char*)&data[2], datasize-2);
1473 std::istringstream is(datastring, std::ios_base::binary);
1477 u16 total number of file bunches
1478 u16 index of this bunch
1479 u32 number of files in this bunch
1487 int num_bunches = readU16(is);
1488 int bunch_i = readU16(is);
1489 u32 num_files = readU32(is);
1490 infostream<<"Client: Received files: bunch "<<bunch_i<<"/"
1491 <<num_bunches<<" files="<<num_files
1492 <<" size="<<datasize<<std::endl;
1497 if (m_media_downloader == NULL ||
1498 !m_media_downloader->isStarted()) {
1499 const char *problem = m_media_downloader ?
1500 "media has not been requested" :
1501 "all media has been received already";
1502 errorstream<<"Client: Received media but "
1504 <<" bunch "<<bunch_i<<"/"<<num_bunches
1505 <<" files="<<num_files
1506 <<" size="<<datasize<<std::endl;
1510 // Mesh update thread must be stopped while
1511 // updating content definitions
1512 assert(!m_mesh_update_thread.IsRunning());
1514 for(unsigned int i=0; i<num_files; i++){
1515 std::string name = deSerializeString(is);
1516 std::string data = deSerializeLongString(is);
1517 m_media_downloader->conventionalTransferDone(
1521 else if(command == TOCLIENT_TOOLDEF)
1523 infostream<<"Client: WARNING: Ignoring TOCLIENT_TOOLDEF"<<std::endl;
1525 else if(command == TOCLIENT_NODEDEF)
1527 infostream<<"Client: Received node definitions: packet size: "
1528 <<datasize<<std::endl;
1530 // Mesh update thread must be stopped while
1531 // updating content definitions
1532 assert(!m_mesh_update_thread.IsRunning());
1534 // Decompress node definitions
1535 std::string datastring((char*)&data[2], datasize-2);
1536 std::istringstream is(datastring, std::ios_base::binary);
1537 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1538 std::ostringstream tmp_os;
1539 decompressZlib(tmp_is, tmp_os);
1541 // Deserialize node definitions
1542 std::istringstream tmp_is2(tmp_os.str());
1543 m_nodedef->deSerialize(tmp_is2);
1544 m_nodedef_received = true;
1546 else if(command == TOCLIENT_CRAFTITEMDEF)
1548 infostream<<"Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF"<<std::endl;
1550 else if(command == TOCLIENT_ITEMDEF)
1552 infostream<<"Client: Received item definitions: packet size: "
1553 <<datasize<<std::endl;
1555 // Mesh update thread must be stopped while
1556 // updating content definitions
1557 assert(!m_mesh_update_thread.IsRunning());
1559 // Decompress item definitions
1560 std::string datastring((char*)&data[2], datasize-2);
1561 std::istringstream is(datastring, std::ios_base::binary);
1562 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1563 std::ostringstream tmp_os;
1564 decompressZlib(tmp_is, tmp_os);
1566 // Deserialize node definitions
1567 std::istringstream tmp_is2(tmp_os.str());
1568 m_itemdef->deSerialize(tmp_is2);
1569 m_itemdef_received = true;
1571 else if(command == TOCLIENT_PLAY_SOUND)
1573 std::string datastring((char*)&data[2], datasize-2);
1574 std::istringstream is(datastring, std::ios_base::binary);
1576 s32 server_id = readS32(is);
1577 std::string name = deSerializeString(is);
1578 float gain = readF1000(is);
1579 int type = readU8(is); // 0=local, 1=positional, 2=object
1580 v3f pos = readV3F1000(is);
1581 u16 object_id = readU16(is);
1582 bool loop = readU8(is);
1587 client_id = m_sound->playSound(name, loop, gain);
1589 case 1: // positional
1590 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1593 ClientActiveObject *cao = m_env.getActiveObject(object_id);
1595 pos = cao->getPosition();
1596 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1597 // TODO: Set up sound to move with object
1602 if(client_id != -1){
1603 m_sounds_server_to_client[server_id] = client_id;
1604 m_sounds_client_to_server[client_id] = server_id;
1606 m_sounds_to_objects[client_id] = object_id;
1609 else if(command == TOCLIENT_STOP_SOUND)
1611 std::string datastring((char*)&data[2], datasize-2);
1612 std::istringstream is(datastring, std::ios_base::binary);
1614 s32 server_id = readS32(is);
1615 std::map<s32, int>::iterator i =
1616 m_sounds_server_to_client.find(server_id);
1617 if(i != m_sounds_server_to_client.end()){
1618 int client_id = i->second;
1619 m_sound->stopSound(client_id);
1622 else if(command == TOCLIENT_PRIVILEGES)
1624 std::string datastring((char*)&data[2], datasize-2);
1625 std::istringstream is(datastring, std::ios_base::binary);
1627 m_privileges.clear();
1628 infostream<<"Client: Privileges updated: ";
1629 u16 num_privileges = readU16(is);
1630 for(unsigned int i=0; i<num_privileges; i++){
1631 std::string priv = deSerializeString(is);
1632 m_privileges.insert(priv);
1633 infostream<<priv<<" ";
1635 infostream<<std::endl;
1637 else if(command == TOCLIENT_INVENTORY_FORMSPEC)
1639 std::string datastring((char*)&data[2], datasize-2);
1640 std::istringstream is(datastring, std::ios_base::binary);
1642 // Store formspec in LocalPlayer
1643 player->inventory_formspec = deSerializeLongString(is);
1645 else if(command == TOCLIENT_DETACHED_INVENTORY)
1647 std::string datastring((char*)&data[2], datasize-2);
1648 std::istringstream is(datastring, std::ios_base::binary);
1650 std::string name = deSerializeString(is);
1652 infostream<<"Client: Detached inventory update: \""<<name<<"\""<<std::endl;
1654 Inventory *inv = NULL;
1655 if(m_detached_inventories.count(name) > 0)
1656 inv = m_detached_inventories[name];
1658 inv = new Inventory(m_itemdef);
1659 m_detached_inventories[name] = inv;
1661 inv->deSerialize(is);
1663 else if(command == TOCLIENT_SHOW_FORMSPEC)
1665 std::string datastring((char*)&data[2], datasize-2);
1666 std::istringstream is(datastring, std::ios_base::binary);
1668 std::string formspec = deSerializeLongString(is);
1669 std::string formname = deSerializeString(is);
1672 event.type = CE_SHOW_FORMSPEC;
1673 // pointer is required as event is a struct only!
1674 // adding a std:string to a struct isn't possible
1675 event.show_formspec.formspec = new std::string(formspec);
1676 event.show_formspec.formname = new std::string(formname);
1677 m_client_event_queue.push_back(event);
1679 else if(command == TOCLIENT_SPAWN_PARTICLE)
1681 std::string datastring((char*)&data[2], datasize-2);
1682 std::istringstream is(datastring, std::ios_base::binary);
1684 v3f pos = readV3F1000(is);
1685 v3f vel = readV3F1000(is);
1686 v3f acc = readV3F1000(is);
1687 float expirationtime = readF1000(is);
1688 float size = readF1000(is);
1689 bool collisiondetection = readU8(is);
1690 std::string texture = deSerializeLongString(is);
1691 bool vertical = false;
1693 vertical = readU8(is);
1697 event.type = CE_SPAWN_PARTICLE;
1698 event.spawn_particle.pos = new v3f (pos);
1699 event.spawn_particle.vel = new v3f (vel);
1700 event.spawn_particle.acc = new v3f (acc);
1701 event.spawn_particle.expirationtime = expirationtime;
1702 event.spawn_particle.size = size;
1703 event.spawn_particle.collisiondetection = collisiondetection;
1704 event.spawn_particle.vertical = vertical;
1705 event.spawn_particle.texture = new std::string(texture);
1707 m_client_event_queue.push_back(event);
1709 else if(command == TOCLIENT_ADD_PARTICLESPAWNER)
1711 std::string datastring((char*)&data[2], datasize-2);
1712 std::istringstream is(datastring, std::ios_base::binary);
1714 u16 amount = readU16(is);
1715 float spawntime = readF1000(is);
1716 v3f minpos = readV3F1000(is);
1717 v3f maxpos = readV3F1000(is);
1718 v3f minvel = readV3F1000(is);
1719 v3f maxvel = readV3F1000(is);
1720 v3f minacc = readV3F1000(is);
1721 v3f maxacc = readV3F1000(is);
1722 float minexptime = readF1000(is);
1723 float maxexptime = readF1000(is);
1724 float minsize = readF1000(is);
1725 float maxsize = readF1000(is);
1726 bool collisiondetection = readU8(is);
1727 std::string texture = deSerializeLongString(is);
1728 u32 id = readU32(is);
1729 bool vertical = false;
1731 vertical = readU8(is);
1735 event.type = CE_ADD_PARTICLESPAWNER;
1736 event.add_particlespawner.amount = amount;
1737 event.add_particlespawner.spawntime = spawntime;
1738 event.add_particlespawner.minpos = new v3f (minpos);
1739 event.add_particlespawner.maxpos = new v3f (maxpos);
1740 event.add_particlespawner.minvel = new v3f (minvel);
1741 event.add_particlespawner.maxvel = new v3f (maxvel);
1742 event.add_particlespawner.minacc = new v3f (minacc);
1743 event.add_particlespawner.maxacc = new v3f (maxacc);
1744 event.add_particlespawner.minexptime = minexptime;
1745 event.add_particlespawner.maxexptime = maxexptime;
1746 event.add_particlespawner.minsize = minsize;
1747 event.add_particlespawner.maxsize = maxsize;
1748 event.add_particlespawner.collisiondetection = collisiondetection;
1749 event.add_particlespawner.vertical = vertical;
1750 event.add_particlespawner.texture = new std::string(texture);
1751 event.add_particlespawner.id = id;
1753 m_client_event_queue.push_back(event);
1755 else if(command == TOCLIENT_DELETE_PARTICLESPAWNER)
1757 std::string datastring((char*)&data[2], datasize-2);
1758 std::istringstream is(datastring, std::ios_base::binary);
1760 u32 id = readU16(is);
1763 event.type = CE_DELETE_PARTICLESPAWNER;
1764 event.delete_particlespawner.id = id;
1766 m_client_event_queue.push_back(event);
1768 else if(command == TOCLIENT_HUDADD)
1770 std::string datastring((char *)&data[2], datasize - 2);
1771 std::istringstream is(datastring, std::ios_base::binary);
1773 u32 id = readU32(is);
1774 u8 type = readU8(is);
1775 v2f pos = readV2F1000(is);
1776 std::string name = deSerializeString(is);
1777 v2f scale = readV2F1000(is);
1778 std::string text = deSerializeString(is);
1779 u32 number = readU32(is);
1780 u32 item = readU32(is);
1781 u32 dir = readU32(is);
1782 v2f align = readV2F1000(is);
1783 v2f offset = readV2F1000(is);
1786 world_pos = readV3F1000(is);
1787 }catch(SerializationError &e) {};
1790 event.type = CE_HUDADD;
1791 event.hudadd.id = id;
1792 event.hudadd.type = type;
1793 event.hudadd.pos = new v2f(pos);
1794 event.hudadd.name = new std::string(name);
1795 event.hudadd.scale = new v2f(scale);
1796 event.hudadd.text = new std::string(text);
1797 event.hudadd.number = number;
1798 event.hudadd.item = item;
1799 event.hudadd.dir = dir;
1800 event.hudadd.align = new v2f(align);
1801 event.hudadd.offset = new v2f(offset);
1802 event.hudadd.world_pos = new v3f(world_pos);
1803 m_client_event_queue.push_back(event);
1805 else if(command == TOCLIENT_HUDRM)
1807 std::string datastring((char *)&data[2], datasize - 2);
1808 std::istringstream is(datastring, std::ios_base::binary);
1810 u32 id = readU32(is);
1813 event.type = CE_HUDRM;
1814 event.hudrm.id = id;
1815 m_client_event_queue.push_back(event);
1817 else if(command == TOCLIENT_HUDCHANGE)
1824 std::string datastring((char *)&data[2], datasize - 2);
1825 std::istringstream is(datastring, std::ios_base::binary);
1827 u32 id = readU32(is);
1828 u8 stat = (HudElementStat)readU8(is);
1830 if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
1831 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
1832 v2fdata = readV2F1000(is);
1833 else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
1834 sdata = deSerializeString(is);
1835 else if (stat == HUD_STAT_WORLD_POS)
1836 v3fdata = readV3F1000(is);
1838 intdata = readU32(is);
1841 event.type = CE_HUDCHANGE;
1842 event.hudchange.id = id;
1843 event.hudchange.stat = (HudElementStat)stat;
1844 event.hudchange.v2fdata = new v2f(v2fdata);
1845 event.hudchange.v3fdata = new v3f(v3fdata);
1846 event.hudchange.sdata = new std::string(sdata);
1847 event.hudchange.data = intdata;
1848 m_client_event_queue.push_back(event);
1850 else if(command == TOCLIENT_HUD_SET_FLAGS)
1852 std::string datastring((char *)&data[2], datasize - 2);
1853 std::istringstream is(datastring, std::ios_base::binary);
1855 u32 flags = readU32(is);
1856 u32 mask = readU32(is);
1858 player->hud_flags &= ~mask;
1859 player->hud_flags |= flags;
1861 else if(command == TOCLIENT_HUD_SET_PARAM)
1863 std::string datastring((char *)&data[2], datasize - 2);
1864 std::istringstream is(datastring, std::ios_base::binary);
1866 u16 param = readU16(is);
1867 std::string value = deSerializeString(is);
1869 if(param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) {
1870 s32 hotbar_itemcount = readS32((u8*) value.c_str());
1871 if(hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
1872 player->hud_hotbar_itemcount = hotbar_itemcount;
1874 else if (param == HUD_PARAM_HOTBAR_IMAGE) {
1875 ((LocalPlayer *) player)->hotbar_image = value;
1877 else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
1878 ((LocalPlayer *) player)->hotbar_selected_image = value;
1881 else if(command == TOCLIENT_SET_SKY)
1883 std::string datastring((char *)&data[2], datasize - 2);
1884 std::istringstream is(datastring, std::ios_base::binary);
1886 video::SColor *bgcolor = new video::SColor(readARGB8(is));
1887 std::string *type = new std::string(deSerializeString(is));
1888 u16 count = readU16(is);
1889 std::vector<std::string> *params = new std::vector<std::string>;
1891 for(size_t i=0; i<count; i++)
1892 params->push_back(deSerializeString(is));
1895 event.type = CE_SET_SKY;
1896 event.set_sky.bgcolor = bgcolor;
1897 event.set_sky.type = type;
1898 event.set_sky.params = params;
1899 m_client_event_queue.push_back(event);
1901 else if(command == TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO)
1903 std::string datastring((char *)&data[2], datasize - 2);
1904 std::istringstream is(datastring, std::ios_base::binary);
1906 bool do_override = readU8(is);
1907 float day_night_ratio_f = (float)readU16(is) / 65536;
1910 event.type = CE_OVERRIDE_DAY_NIGHT_RATIO;
1911 event.override_day_night_ratio.do_override = do_override;
1912 event.override_day_night_ratio.ratio_f = day_night_ratio_f;
1913 m_client_event_queue.push_back(event);
1917 infostream<<"Client: Ignoring unknown command "
1918 <<command<<std::endl;
1922 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
1924 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1925 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
1928 void Client::interact(u8 action, const PointedThing& pointed)
1930 if(m_state != LC_Ready){
1931 infostream<<"Client::interact() "
1932 "cancelled (not connected)"
1937 std::ostringstream os(std::ios_base::binary);
1943 [5] u32 length of the next item
1944 [9] serialized PointedThing
1946 0: start digging (from undersurface) or use
1947 1: stop digging (all parameters ignored)
1948 2: digging completed
1949 3: place block or item (to abovesurface)
1952 writeU16(os, TOSERVER_INTERACT);
1953 writeU8(os, action);
1954 writeU16(os, getPlayerItem());
1955 std::ostringstream tmp_os(std::ios::binary);
1956 pointed.serialize(tmp_os);
1957 os<<serializeLongString(tmp_os.str());
1959 std::string s = os.str();
1960 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1963 Send(0, data, true);
1966 void Client::sendNodemetaFields(v3s16 p, const std::string &formname,
1967 const std::map<std::string, std::string> &fields)
1969 std::ostringstream os(std::ios_base::binary);
1971 writeU16(os, TOSERVER_NODEMETA_FIELDS);
1973 os<<serializeString(formname);
1974 size_t fields_size = fields.size();
1975 assert(fields_size <= 0xFFFF);
1976 writeU16(os, (u16) (fields_size & 0xFFFF));
1977 for(std::map<std::string, std::string>::const_iterator
1978 i = fields.begin(); i != fields.end(); i++){
1979 const std::string &name = i->first;
1980 const std::string &value = i->second;
1981 os<<serializeString(name);
1982 os<<serializeLongString(value);
1986 std::string s = os.str();
1987 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1989 Send(0, data, true);
1992 void Client::sendInventoryFields(const std::string &formname,
1993 const std::map<std::string, std::string> &fields)
1995 std::ostringstream os(std::ios_base::binary);
1997 writeU16(os, TOSERVER_INVENTORY_FIELDS);
1998 os<<serializeString(formname);
1999 size_t fields_size = fields.size();
2000 assert(fields_size <= 0xFFFF);
2001 writeU16(os, (u16) (fields_size & 0xFFFF));
2002 for(std::map<std::string, std::string>::const_iterator
2003 i = fields.begin(); i != fields.end(); i++){
2004 const std::string &name = i->first;
2005 const std::string &value = i->second;
2006 os<<serializeString(name);
2007 os<<serializeLongString(value);
2011 std::string s = os.str();
2012 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2014 Send(0, data, true);
2017 void Client::sendInventoryAction(InventoryAction *a)
2019 std::ostringstream os(std::ios_base::binary);
2023 writeU16(buf, TOSERVER_INVENTORY_ACTION);
2024 os.write((char*)buf, 2);
2029 std::string s = os.str();
2030 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2032 Send(0, data, true);
2035 void Client::sendChatMessage(const std::wstring &message)
2037 std::ostringstream os(std::ios_base::binary);
2041 writeU16(buf, TOSERVER_CHAT_MESSAGE);
2042 os.write((char*)buf, 2);
2045 size_t messagesize = message.size();
2046 assert(messagesize <= 0xFFFF);
2047 writeU16(buf, (u16) (messagesize & 0xFF));
2048 os.write((char*)buf, 2);
2051 for(unsigned int i=0; i<message.size(); i++)
2055 os.write((char*)buf, 2);
2059 std::string s = os.str();
2060 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2062 Send(0, data, true);
2065 void Client::sendChangePassword(const std::wstring &oldpassword,
2066 const std::wstring &newpassword)
2068 Player *player = m_env.getLocalPlayer();
2072 std::string playername = player->getName();
2073 std::string oldpwd = translatePassword(playername, oldpassword);
2074 std::string newpwd = translatePassword(playername, newpassword);
2076 std::ostringstream os(std::ios_base::binary);
2077 u8 buf[2+PASSWORD_SIZE*2];
2079 [0] u16 TOSERVER_PASSWORD
2080 [2] u8[28] old password
2081 [30] u8[28] new password
2084 writeU16(buf, TOSERVER_PASSWORD);
2085 for(unsigned int i=0;i<PASSWORD_SIZE-1;i++)
2087 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
2088 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
2090 buf[2+PASSWORD_SIZE-1] = 0;
2091 buf[30+PASSWORD_SIZE-1] = 0;
2092 os.write((char*)buf, 2+PASSWORD_SIZE*2);
2095 std::string s = os.str();
2096 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2098 Send(0, data, true);
2102 void Client::sendDamage(u8 damage)
2104 DSTACK(__FUNCTION_NAME);
2105 std::ostringstream os(std::ios_base::binary);
2107 writeU16(os, TOSERVER_DAMAGE);
2108 writeU8(os, damage);
2111 std::string s = os.str();
2112 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2114 Send(0, data, true);
2117 void Client::sendBreath(u16 breath)
2119 DSTACK(__FUNCTION_NAME);
2120 std::ostringstream os(std::ios_base::binary);
2122 writeU16(os, TOSERVER_BREATH);
2123 writeU16(os, breath);
2125 std::string s = os.str();
2126 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2128 Send(0, data, true);
2131 void Client::sendRespawn()
2133 DSTACK(__FUNCTION_NAME);
2134 std::ostringstream os(std::ios_base::binary);
2136 writeU16(os, TOSERVER_RESPAWN);
2139 std::string s = os.str();
2140 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2142 Send(0, data, true);
2145 void Client::sendReady()
2147 DSTACK(__FUNCTION_NAME);
2148 std::ostringstream os(std::ios_base::binary);
2150 writeU16(os, TOSERVER_CLIENT_READY);
2151 writeU8(os,VERSION_MAJOR);
2152 writeU8(os,VERSION_MINOR);
2153 writeU8(os,VERSION_PATCH_ORIG);
2156 writeU16(os,strlen(CMAKE_VERSION_GITHASH));
2157 os.write(CMAKE_VERSION_GITHASH,strlen(CMAKE_VERSION_GITHASH));
2160 std::string s = os.str();
2161 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2163 Send(0, data, true);
2166 void Client::sendPlayerPos()
2168 LocalPlayer *myplayer = m_env.getLocalPlayer();
2169 if(myplayer == NULL)
2172 // Save bandwidth by only updating position when something changed
2173 if(myplayer->last_position == myplayer->getPosition() &&
2174 myplayer->last_speed == myplayer->getSpeed() &&
2175 myplayer->last_pitch == myplayer->getPitch() &&
2176 myplayer->last_yaw == myplayer->getYaw() &&
2177 myplayer->last_keyPressed == myplayer->keyPressed)
2180 myplayer->last_position = myplayer->getPosition();
2181 myplayer->last_speed = myplayer->getSpeed();
2182 myplayer->last_pitch = myplayer->getPitch();
2183 myplayer->last_yaw = myplayer->getYaw();
2184 myplayer->last_keyPressed = myplayer->keyPressed;
2188 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2189 our_peer_id = m_con.GetPeerID();
2192 // Set peer id if not set already
2193 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2194 myplayer->peer_id = our_peer_id;
2195 // Check that an existing peer_id is the same as the connection's
2196 assert(myplayer->peer_id == our_peer_id);
2198 v3f pf = myplayer->getPosition();
2199 v3f sf = myplayer->getSpeed();
2200 s32 pitch = myplayer->getPitch() * 100;
2201 s32 yaw = myplayer->getYaw() * 100;
2202 u32 keyPressed = myplayer->keyPressed;
2204 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
2205 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
2209 [2] v3s32 position*100
2210 [2+12] v3s32 speed*100
2211 [2+12+12] s32 pitch*100
2212 [2+12+12+4] s32 yaw*100
2213 [2+12+12+4+4] u32 keyPressed
2215 SharedBuffer<u8> data(2+12+12+4+4+4);
2216 writeU16(&data[0], TOSERVER_PLAYERPOS);
2217 writeV3S32(&data[2], position);
2218 writeV3S32(&data[2+12], speed);
2219 writeS32(&data[2+12+12], pitch);
2220 writeS32(&data[2+12+12+4], yaw);
2221 writeU32(&data[2+12+12+4+4], keyPressed);
2222 // Send as unreliable
2223 Send(0, data, false);
2226 void Client::sendPlayerItem(u16 item)
2228 Player *myplayer = m_env.getLocalPlayer();
2229 if(myplayer == NULL)
2232 u16 our_peer_id = m_con.GetPeerID();
2234 // Set peer id if not set already
2235 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2236 myplayer->peer_id = our_peer_id;
2237 // Check that an existing peer_id is the same as the connection's
2238 assert(myplayer->peer_id == our_peer_id);
2240 SharedBuffer<u8> data(2+2);
2241 writeU16(&data[0], TOSERVER_PLAYERITEM);
2242 writeU16(&data[2], item);
2245 Send(0, data, true);
2248 void Client::removeNode(v3s16 p)
2250 std::map<v3s16, MapBlock*> modified_blocks;
2254 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
2256 catch(InvalidPositionException &e)
2260 // add urgent task to update the modified node
2261 addUpdateMeshTaskForNode(p, false, true);
2263 for(std::map<v3s16, MapBlock * >::iterator
2264 i = modified_blocks.begin();
2265 i != modified_blocks.end(); ++i)
2267 addUpdateMeshTaskWithEdge(i->first);
2271 void Client::addNode(v3s16 p, MapNode n, bool remove_metadata)
2273 TimeTaker timer1("Client::addNode()");
2275 std::map<v3s16, MapBlock*> modified_blocks;
2279 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
2280 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
2282 catch(InvalidPositionException &e)
2285 for(std::map<v3s16, MapBlock * >::iterator
2286 i = modified_blocks.begin();
2287 i != modified_blocks.end(); ++i)
2289 addUpdateMeshTaskWithEdge(i->first);
2293 void Client::setPlayerControl(PlayerControl &control)
2295 LocalPlayer *player = m_env.getLocalPlayer();
2296 assert(player != NULL);
2297 player->control = control;
2300 void Client::selectPlayerItem(u16 item)
2302 m_playeritem = item;
2303 m_inventory_updated = true;
2304 sendPlayerItem(item);
2307 // Returns true if the inventory of the local player has been
2308 // updated from the server. If it is true, it is set to false.
2309 bool Client::getLocalInventoryUpdated()
2311 bool updated = m_inventory_updated;
2312 m_inventory_updated = false;
2316 // Copies the inventory of the local player to parameter
2317 void Client::getLocalInventory(Inventory &dst)
2319 Player *player = m_env.getLocalPlayer();
2320 assert(player != NULL);
2321 dst = player->inventory;
2324 Inventory* Client::getInventory(const InventoryLocation &loc)
2327 case InventoryLocation::UNDEFINED:
2330 case InventoryLocation::CURRENT_PLAYER:
2332 Player *player = m_env.getLocalPlayer();
2333 assert(player != NULL);
2334 return &player->inventory;
2337 case InventoryLocation::PLAYER:
2339 Player *player = m_env.getPlayer(loc.name.c_str());
2342 return &player->inventory;
2345 case InventoryLocation::NODEMETA:
2347 NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p);
2350 return meta->getInventory();
2353 case InventoryLocation::DETACHED:
2355 if(m_detached_inventories.count(loc.name) == 0)
2357 return m_detached_inventories[loc.name];
2366 void Client::inventoryAction(InventoryAction *a)
2369 Send it to the server
2371 sendInventoryAction(a);
2374 Predict some local inventory changes
2376 a->clientApply(this, this);
2382 ClientActiveObject * Client::getSelectedActiveObject(
2384 v3f from_pos_f_on_map,
2385 core::line3d<f32> shootline_on_map
2388 std::vector<DistanceSortedActiveObject> objects;
2390 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
2393 // After this, the closest object is the first in the array.
2394 std::sort(objects.begin(), objects.end());
2396 for(unsigned int i=0; i<objects.size(); i++)
2398 ClientActiveObject *obj = objects[i].obj;
2400 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
2401 if(selection_box == NULL)
2404 v3f pos = obj->getPosition();
2406 core::aabbox3d<f32> offsetted_box(
2407 selection_box->MinEdge + pos,
2408 selection_box->MaxEdge + pos
2411 if(offsetted_box.intersectsWithLine(shootline_on_map))
2420 std::list<std::string> Client::getConnectedPlayerNames()
2422 return m_env.getPlayerNames();
2425 float Client::getAnimationTime()
2427 return m_animation_time;
2430 int Client::getCrackLevel()
2432 return m_crack_level;
2435 void Client::setCrack(int level, v3s16 pos)
2437 int old_crack_level = m_crack_level;
2438 v3s16 old_crack_pos = m_crack_pos;
2440 m_crack_level = level;
2443 if(old_crack_level >= 0 && (level < 0 || pos != old_crack_pos))
2446 addUpdateMeshTaskForNode(old_crack_pos, false, true);
2448 if(level >= 0 && (old_crack_level < 0 || pos != old_crack_pos))
2451 addUpdateMeshTaskForNode(pos, false, true);
2457 Player *player = m_env.getLocalPlayer();
2458 assert(player != NULL);
2462 u16 Client::getBreath()
2464 Player *player = m_env.getLocalPlayer();
2465 assert(player != NULL);
2466 return player->getBreath();
2469 bool Client::getChatMessage(std::wstring &message)
2471 if(m_chat_queue.size() == 0)
2473 message = m_chat_queue.pop_front();
2477 void Client::typeChatMessage(const std::wstring &message)
2479 // Discard empty line
2484 sendChatMessage(message);
2487 if (message[0] == L'/')
2489 m_chat_queue.push_back(
2490 (std::wstring)L"issued command: "+message);
2494 LocalPlayer *player = m_env.getLocalPlayer();
2495 assert(player != NULL);
2496 std::wstring name = narrow_to_wide(player->getName());
2497 m_chat_queue.push_back(
2498 (std::wstring)L"<"+name+L"> "+message);
2502 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
2504 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2509 Create a task to update the mesh of the block
2512 MeshMakeData *data = new MeshMakeData(this);
2515 //TimeTaker timer("data fill");
2517 // Debug: 1-6ms, avg=2ms
2519 data->setCrack(m_crack_level, m_crack_pos);
2520 data->setSmoothLighting(g_settings->getBool("smooth_lighting"));
2523 // Add task to queue
2524 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server, urgent);
2527 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent)
2530 v3s16 p = blockpos + v3s16(0,0,0);
2531 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2532 addUpdateMeshTask(p, ack_to_server, urgent);
2534 catch(InvalidPositionException &e){}
2537 for (int i=0;i<6;i++)
2540 v3s16 p = blockpos + g_6dirs[i];
2541 addUpdateMeshTask(p, false, urgent);
2543 catch(InvalidPositionException &e){}
2547 void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool urgent)
2551 infostream<<"Client::addUpdateMeshTaskForNode(): "
2552 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2556 v3s16 blockpos = getNodeBlockPos(nodepos);
2557 v3s16 blockpos_relative = blockpos * MAP_BLOCKSIZE;
2560 v3s16 p = blockpos + v3s16(0,0,0);
2561 addUpdateMeshTask(p, ack_to_server, urgent);
2563 catch(InvalidPositionException &e){}
2566 if(nodepos.X == blockpos_relative.X){
2568 v3s16 p = blockpos + v3s16(-1,0,0);
2569 addUpdateMeshTask(p, false, urgent);
2571 catch(InvalidPositionException &e){}
2574 if(nodepos.Y == blockpos_relative.Y){
2576 v3s16 p = blockpos + v3s16(0,-1,0);
2577 addUpdateMeshTask(p, false, urgent);
2579 catch(InvalidPositionException &e){}
2582 if(nodepos.Z == blockpos_relative.Z){
2584 v3s16 p = blockpos + v3s16(0,0,-1);
2585 addUpdateMeshTask(p, false, urgent);
2587 catch(InvalidPositionException &e){}
2591 ClientEvent Client::getClientEvent()
2593 if(m_client_event_queue.size() == 0)
2596 event.type = CE_NONE;
2599 return m_client_event_queue.pop_front();
2602 float Client::mediaReceiveProgress()
2604 if (m_media_downloader)
2605 return m_media_downloader->getProgress();
2607 return 1.0; // downloader only exists when not yet done
2610 void draw_load_screen(const std::wstring &text,
2611 IrrlichtDevice* device, gui::IGUIFont* font,
2612 float dtime=0 ,int percent=0, bool clouds=true);
2614 void Client::afterContentReceived(IrrlichtDevice *device, gui::IGUIFont* font)
2616 infostream<<"Client::afterContentReceived() started"<<std::endl;
2617 assert(m_itemdef_received);
2618 assert(m_nodedef_received);
2619 assert(mediaReceived());
2621 // Rebuild inherited images and recreate textures
2622 infostream<<"- Rebuilding images and textures"<<std::endl;
2623 m_tsrc->rebuildImagesAndTextures();
2626 infostream<<"- Rebuilding shaders"<<std::endl;
2627 m_shsrc->rebuildShaders();
2629 // Update node aliases
2630 infostream<<"- Updating node aliases"<<std::endl;
2631 m_nodedef->updateAliases(m_itemdef);
2633 // Update node textures
2634 infostream<<"- Updating node textures"<<std::endl;
2635 m_nodedef->updateTextures(m_tsrc);
2637 // Preload item textures and meshes if configured to
2638 if(g_settings->getBool("preload_item_visuals"))
2640 verbosestream<<"Updating item textures and meshes"<<std::endl;
2641 wchar_t* text = wgettext("Item textures...");
2642 draw_load_screen(text,device,font,0,0);
2643 std::set<std::string> names = m_itemdef->getAll();
2644 size_t size = names.size();
2647 for(std::set<std::string>::const_iterator
2648 i = names.begin(); i != names.end(); ++i){
2649 // Asking for these caches the result
2650 m_itemdef->getInventoryTexture(*i, this);
2651 m_itemdef->getWieldMesh(*i, this);
2653 percent = count*100/size;
2654 if (count%50 == 0) // only update every 50 item
2655 draw_load_screen(text,device,font,0,percent);
2660 // Start mesh update thread after setting up content definitions
2661 infostream<<"- Starting mesh update thread"<<std::endl;
2662 m_mesh_update_thread.Start();
2666 infostream<<"Client::afterContentReceived() done"<<std::endl;
2669 float Client::getRTT(void)
2671 return m_con.getPeerStat(PEER_ID_SERVER,con::AVG_RTT);
2674 // IGameDef interface
2676 IItemDefManager* Client::getItemDefManager()
2680 INodeDefManager* Client::getNodeDefManager()
2684 ICraftDefManager* Client::getCraftDefManager()
2687 //return m_craftdef;
2689 ITextureSource* Client::getTextureSource()
2693 IShaderSource* Client::getShaderSource()
2697 u16 Client::allocateUnknownNodeId(const std::string &name)
2699 errorstream<<"Client::allocateUnknownNodeId(): "
2700 <<"Client cannot allocate node IDs"<<std::endl;
2702 return CONTENT_IGNORE;
2704 ISoundManager* Client::getSoundManager()
2708 MtEventManager* Client::getEventManager()
2713 scene::IAnimatedMesh* Client::getMesh(const std::string &filename)
2715 std::map<std::string, std::string>::const_iterator i =
2716 m_mesh_data.find(filename);
2717 if(i == m_mesh_data.end()){
2718 errorstream<<"Client::getMesh(): Mesh not found: \""<<filename<<"\""
2722 const std::string &data = i->second;
2723 scene::ISceneManager *smgr = m_device->getSceneManager();
2725 // Create the mesh, remove it from cache and return it
2726 // This allows unique vertex colors and other properties for each instance
2727 Buffer<char> data_rw(data.c_str(), data.size()); // Const-incorrect Irrlicht
2728 io::IFileSystem *irrfs = m_device->getFileSystem();
2729 io::IReadFile *rfile = irrfs->createMemoryReadFile(
2730 *data_rw, data_rw.getSize(), filename.c_str());
2733 scene::IAnimatedMesh *mesh = smgr->getMesh(rfile);
2735 // NOTE: By playing with Irrlicht refcounts, maybe we could cache a bunch
2736 // of uniquely named instances and re-use them
2738 smgr->getMeshCache()->removeMesh(mesh);