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 porting::setThreadName("MeshUpdateThread");
174 while(!StopRequested())
176 QueuedMeshUpdate *q = m_queue_in.pop();
183 ScopeProfiler sp(g_profiler, "Client: Mesh making");
185 MapBlockMesh *mesh_new = new MapBlockMesh(q->data, m_camera_offset);
186 if(mesh_new->getMesh()->getMeshBufferCount() == 0)
195 r.ack_block_to_server = q->ack_block_to_server;
197 m_queue_out.push_back(r);
202 END_DEBUG_EXCEPTION_HANDLER(errorstream)
212 IrrlichtDevice *device,
213 const char *playername,
214 std::string password,
215 MapDrawControl &control,
216 IWritableTextureSource *tsrc,
217 IWritableShaderSource *shsrc,
218 IWritableItemDefManager *itemdef,
219 IWritableNodeDefManager *nodedef,
220 ISoundManager *sound,
221 MtEventManager *event,
230 m_mesh_update_thread(this),
232 new ClientMap(this, this, control,
233 device->getSceneManager()->getRootSceneNode(),
234 device->getSceneManager(), 666),
235 device->getSceneManager(),
238 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, ipv6, this),
240 m_server_ser_ver(SER_FMT_VER_INVALID),
242 m_inventory_updated(false),
243 m_inventory_from_server(NULL),
244 m_inventory_from_server_age(0.0),
249 m_password(password),
250 m_access_denied(false),
251 m_itemdef_received(false),
252 m_nodedef_received(false),
253 m_media_downloader(new ClientMediaDownloader()),
254 m_time_of_day_set(false),
255 m_last_time_of_day_f(-1),
256 m_time_of_day_update_timer(0),
257 m_recommended_send_interval(0.1),
258 m_removed_sounds_check_timer(0),
261 m_packetcounter_timer = 0.0;
262 //m_delete_unused_sectors_timer = 0.0;
263 m_connection_reinit_timer = 0.0;
264 m_avg_rtt_timer = 0.0;
265 m_playerpos_send_timer = 0.0;
266 m_ignore_damage_timer = 0.0;
272 Player *player = new LocalPlayer(this);
274 player->updateName(playername);
276 m_env.addPlayer(player);
282 //request all client managed threads to stop
283 m_mesh_update_thread.Stop();
286 bool Client::isShutdown()
289 if (!m_mesh_update_thread.IsRunning()) return true;
298 m_mesh_update_thread.Stop();
299 m_mesh_update_thread.Wait();
300 while(!m_mesh_update_thread.m_queue_out.empty()) {
301 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_frontNoEx();
306 delete m_inventory_from_server;
308 // Delete detached inventories
309 for(std::map<std::string, Inventory*>::iterator
310 i = m_detached_inventories.begin();
311 i != m_detached_inventories.end(); i++){
315 // cleanup 3d model meshes on client shutdown
316 while (m_device->getSceneManager()->getMeshCache()->getMeshCount() != 0) {
317 scene::IAnimatedMesh * mesh =
318 m_device->getSceneManager()->getMeshCache()->getMeshByIndex(0);
321 m_device->getSceneManager()->getMeshCache()->removeMesh(mesh);
325 void Client::connect(Address address)
327 DSTACK(__FUNCTION_NAME);
328 m_con.SetTimeoutMs(0);
329 m_con.Connect(address);
332 void Client::step(float dtime)
334 DSTACK(__FUNCTION_NAME);
340 if(m_ignore_damage_timer > dtime)
341 m_ignore_damage_timer -= dtime;
343 m_ignore_damage_timer = 0.0;
345 m_animation_time += dtime;
346 if(m_animation_time > 60.0)
347 m_animation_time -= 60.0;
349 m_time_of_day_update_timer += dtime;
357 float &counter = m_packetcounter_timer;
363 infostream<<"Client packetcounter (20s):"<<std::endl;
364 m_packetcounter.print(infostream);
365 m_packetcounter.clear();
372 Delete unused sectors
374 NOTE: This jams the game for a while because deleting sectors
378 float &counter = m_delete_unused_sectors_timer;
386 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
388 core::list<v3s16> deleted_blocks;
390 float delete_unused_sectors_timeout =
391 g_settings->getFloat("client_delete_unused_sectors_timeout");
393 // Delete sector blocks
394 /*u32 num = m_env.getMap().unloadUnusedData
395 (delete_unused_sectors_timeout,
396 true, &deleted_blocks);*/
398 // Delete whole sectors
399 m_env.getMap().unloadUnusedData
400 (delete_unused_sectors_timeout,
403 if(deleted_blocks.size() > 0)
405 /*infostream<<"Client: Deleted blocks of "<<num
406 <<" unused sectors"<<std::endl;*/
407 /*infostream<<"Client: Deleted "<<num
408 <<" unused sectors"<<std::endl;*/
414 // Env is locked so con can be locked.
415 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
417 core::list<v3s16>::Iterator i = deleted_blocks.begin();
418 core::list<v3s16> sendlist;
421 if(sendlist.size() == 255 || i == deleted_blocks.end())
423 if(sendlist.size() == 0)
432 u32 replysize = 2+1+6*sendlist.size();
433 SharedBuffer<u8> reply(replysize);
434 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
435 reply[2] = sendlist.size();
437 for(core::list<v3s16>::Iterator
438 j = sendlist.begin();
439 j != sendlist.end(); j++)
441 writeV3S16(&reply[2+1+6*k], *j);
444 m_con.Send(PEER_ID_SERVER, 1, reply, true);
446 if(i == deleted_blocks.end())
452 sendlist.push_back(*i);
460 if(m_state == LC_Created)
462 float &counter = m_connection_reinit_timer;
468 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
470 Player *myplayer = m_env.getLocalPlayer();
471 assert(myplayer != NULL);
473 // Send TOSERVER_INIT
474 // [0] u16 TOSERVER_INIT
475 // [2] u8 SER_FMT_VER_HIGHEST_READ
476 // [3] u8[20] player_name
477 // [23] u8[28] password (new in some version)
478 // [51] u16 minimum supported network protocol version (added sometime)
479 // [53] u16 maximum supported network protocol version (added later than the previous one)
480 SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2+2);
481 writeU16(&data[0], TOSERVER_INIT);
482 writeU8(&data[2], SER_FMT_VER_HIGHEST_READ);
484 memset((char*)&data[3], 0, PLAYERNAME_SIZE);
485 snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
487 /*infostream<<"Client: sending initial password hash: \""<<m_password<<"\""
490 memset((char*)&data[23], 0, PASSWORD_SIZE);
491 snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
493 writeU16(&data[51], CLIENT_PROTOCOL_VERSION_MIN);
494 writeU16(&data[53], CLIENT_PROTOCOL_VERSION_MAX);
496 // Send as unreliable
497 Send(1, data, false);
500 // Not connected, return
505 Do stuff if connected
509 Run Map's timers and unload unused data
511 const float map_timer_and_unload_dtime = 5.25;
512 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
514 ScopeProfiler sp(g_profiler, "Client: map timer and unload");
515 std::list<v3s16> deleted_blocks;
516 m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
517 g_settings->getFloat("client_unload_unused_data_timeout"),
520 /*if(deleted_blocks.size() > 0)
521 infostream<<"Client: Unloaded "<<deleted_blocks.size()
522 <<" unused blocks"<<std::endl;*/
526 NOTE: This loop is intentionally iterated the way it is.
529 std::list<v3s16>::iterator i = deleted_blocks.begin();
530 std::list<v3s16> sendlist;
533 if(sendlist.size() == 255 || i == deleted_blocks.end())
535 if(sendlist.size() == 0)
544 u32 replysize = 2+1+6*sendlist.size();
545 SharedBuffer<u8> reply(replysize);
546 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
547 reply[2] = sendlist.size();
549 for(std::list<v3s16>::iterator
550 j = sendlist.begin();
551 j != sendlist.end(); ++j)
553 writeV3S16(&reply[2+1+6*k], *j);
556 m_con.Send(PEER_ID_SERVER, 2, reply, true);
558 if(i == deleted_blocks.end())
564 sendlist.push_back(*i);
573 // Control local player (0ms)
574 LocalPlayer *player = m_env.getLocalPlayer();
575 assert(player != NULL);
576 player->applyControl(dtime);
586 ClientEnvEvent event = m_env.getClientEvent();
587 if(event.type == CEE_NONE)
591 else if(event.type == CEE_PLAYER_DAMAGE)
593 if(m_ignore_damage_timer <= 0)
595 u8 damage = event.player_damage.amount;
597 if(event.player_damage.send_to_server)
600 // Add to ClientEvent queue
602 event.type = CE_PLAYER_DAMAGE;
603 event.player_damage.amount = damage;
604 m_client_event_queue.push_back(event);
607 else if(event.type == CEE_PLAYER_BREATH)
609 u16 breath = event.player_breath.amount;
619 float &counter = m_avg_rtt_timer;
624 // connectedAndInitialized() is true, peer exists.
625 float avg_rtt = getRTT();
626 infostream<<"Client: avg_rtt="<<avg_rtt<<std::endl;
631 Send player position to server
634 float &counter = m_playerpos_send_timer;
636 if((m_state == LC_Ready) && (counter >= m_recommended_send_interval))
644 Replace updated meshes
647 int num_processed_meshes = 0;
648 while(!m_mesh_update_thread.m_queue_out.empty())
650 num_processed_meshes++;
651 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_frontNoEx();
652 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
655 // Delete the old mesh
656 if(block->mesh != NULL)
658 // TODO: Remove hardware buffers of meshbuffers of block->mesh
663 // Replace with the new mesh
664 block->mesh = r.mesh;
668 if(r.ack_block_to_server)
680 u32 replysize = 2+1+6;
681 SharedBuffer<u8> reply(replysize);
682 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
684 writeV3S16(&reply[3], r.p);
686 m_con.Send(PEER_ID_SERVER, 2, reply, true);
689 if(num_processed_meshes > 0)
690 g_profiler->graphAdd("num_processed_meshes", num_processed_meshes);
696 if (m_media_downloader && m_media_downloader->isStarted()) {
697 m_media_downloader->step(this);
698 if (m_media_downloader->isDone()) {
700 delete m_media_downloader;
701 m_media_downloader = NULL;
706 If the server didn't update the inventory in a while, revert
707 the local inventory (so the player notices the lag problem
708 and knows something is wrong).
710 if(m_inventory_from_server)
712 float interval = 10.0;
713 float count_before = floor(m_inventory_from_server_age / interval);
715 m_inventory_from_server_age += dtime;
717 float count_after = floor(m_inventory_from_server_age / interval);
719 if(count_after != count_before)
721 // Do this every <interval> seconds after TOCLIENT_INVENTORY
722 // Reset the locally changed inventory to the authoritative inventory
723 Player *player = m_env.getLocalPlayer();
724 player->inventory = *m_inventory_from_server;
725 m_inventory_updated = true;
730 Update positions of sounds attached to objects
733 for(std::map<int, u16>::iterator
734 i = m_sounds_to_objects.begin();
735 i != m_sounds_to_objects.end(); i++)
737 int client_id = i->first;
738 u16 object_id = i->second;
739 ClientActiveObject *cao = m_env.getActiveObject(object_id);
742 v3f pos = cao->getPosition();
743 m_sound->updateSoundPosition(client_id, pos);
748 Handle removed remotely initiated sounds
750 m_removed_sounds_check_timer += dtime;
751 if(m_removed_sounds_check_timer >= 2.32)
753 m_removed_sounds_check_timer = 0;
754 // Find removed sounds and clear references to them
755 std::set<s32> removed_server_ids;
756 for(std::map<s32, int>::iterator
757 i = m_sounds_server_to_client.begin();
758 i != m_sounds_server_to_client.end();)
760 s32 server_id = i->first;
761 int client_id = i->second;
763 if(!m_sound->soundExists(client_id)){
764 m_sounds_server_to_client.erase(server_id);
765 m_sounds_client_to_server.erase(client_id);
766 m_sounds_to_objects.erase(client_id);
767 removed_server_ids.insert(server_id);
771 if(removed_server_ids.size() != 0)
773 std::ostringstream os(std::ios_base::binary);
774 writeU16(os, TOSERVER_REMOVED_SOUNDS);
775 size_t server_ids = removed_server_ids.size();
776 assert(server_ids <= 0xFFFF);
777 writeU16(os, (u16) (server_ids & 0xFFFF));
778 for(std::set<s32>::iterator i = removed_server_ids.begin();
779 i != removed_server_ids.end(); i++)
781 std::string s = os.str();
782 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
789 bool Client::loadMedia(const std::string &data, const std::string &filename)
791 // Silly irrlicht's const-incorrectness
792 Buffer<char> data_rw(data.c_str(), data.size());
796 const char *image_ext[] = {
797 ".png", ".jpg", ".bmp", ".tga",
798 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
801 name = removeStringEnd(filename, image_ext);
804 verbosestream<<"Client: Attempting to load image "
805 <<"file \""<<filename<<"\""<<std::endl;
807 io::IFileSystem *irrfs = m_device->getFileSystem();
808 video::IVideoDriver *vdrv = m_device->getVideoDriver();
810 // Create an irrlicht memory file
811 io::IReadFile *rfile = irrfs->createMemoryReadFile(
812 *data_rw, data_rw.getSize(), "_tempreadfile");
815 video::IImage *img = vdrv->createImageFromFile(rfile);
817 errorstream<<"Client: Cannot create image from data of "
818 <<"file \""<<filename<<"\""<<std::endl;
823 m_tsrc->insertSourceImage(filename, img);
830 const char *sound_ext[] = {
831 ".0.ogg", ".1.ogg", ".2.ogg", ".3.ogg", ".4.ogg",
832 ".5.ogg", ".6.ogg", ".7.ogg", ".8.ogg", ".9.ogg",
835 name = removeStringEnd(filename, sound_ext);
838 verbosestream<<"Client: Attempting to load sound "
839 <<"file \""<<filename<<"\""<<std::endl;
840 m_sound->loadSoundData(name, data);
844 const char *model_ext[] = {
845 ".x", ".b3d", ".md2", ".obj",
848 name = removeStringEnd(filename, model_ext);
851 verbosestream<<"Client: Storing model into memory: "
852 <<"\""<<filename<<"\""<<std::endl;
853 if(m_mesh_data.count(filename))
854 errorstream<<"Multiple models with name \""<<filename.c_str()
855 <<"\" found; replacing previous model"<<std::endl;
856 m_mesh_data[filename] = data;
860 errorstream<<"Client: Don't know how to load file \""
861 <<filename<<"\""<<std::endl;
865 // Virtual methods from con::PeerHandler
866 void Client::peerAdded(con::Peer *peer)
868 infostream<<"Client::peerAdded(): peer->id="
869 <<peer->id<<std::endl;
871 void Client::deletingPeer(con::Peer *peer, bool timeout)
873 infostream<<"Client::deletingPeer(): "
874 "Server Peer is getting deleted "
875 <<"(timeout="<<timeout<<")"<<std::endl;
880 u16 number of files requested
886 void Client::request_media(const std::list<std::string> &file_requests)
888 std::ostringstream os(std::ios_base::binary);
889 writeU16(os, TOSERVER_REQUEST_MEDIA);
890 size_t file_requests_size = file_requests.size();
891 assert(file_requests_size <= 0xFFFF);
892 writeU16(os, (u16) (file_requests_size & 0xFFFF));
894 for(std::list<std::string>::const_iterator i = file_requests.begin();
895 i != file_requests.end(); ++i) {
896 os<<serializeString(*i);
900 std::string s = os.str();
901 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
904 infostream<<"Client: Sending media request list to server ("
905 <<file_requests.size()<<" files)"<<std::endl;
908 void Client::received_media()
910 // notify server we received everything
911 std::ostringstream os(std::ios_base::binary);
912 writeU16(os, TOSERVER_RECEIVED_MEDIA);
913 std::string s = os.str();
914 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
917 infostream<<"Client: Notifying server that we received all media"
921 void Client::ReceiveAll()
923 DSTACK(__FUNCTION_NAME);
924 u32 start_ms = porting::getTimeMs();
927 // Limit time even if there would be huge amounts of data to
929 if(porting::getTimeMs() > start_ms + 100)
934 g_profiler->graphAdd("client_received_packets", 1);
936 catch(con::NoIncomingDataException &e)
940 catch(con::InvalidIncomingDataException &e)
942 infostream<<"Client::ReceiveAll(): "
943 "InvalidIncomingDataException: what()="
944 <<e.what()<<std::endl;
949 void Client::Receive()
951 DSTACK(__FUNCTION_NAME);
952 SharedBuffer<u8> data;
954 u32 datasize = m_con.Receive(sender_peer_id, data);
955 ProcessData(*data, datasize, sender_peer_id);
959 sender_peer_id given to this shall be quaranteed to be a valid peer
961 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
963 DSTACK(__FUNCTION_NAME);
965 // Ignore packets that don't even fit a command
968 m_packetcounter.add(60000);
972 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
974 //infostream<<"Client: received command="<<command<<std::endl;
975 m_packetcounter.add((u16)command);
978 If this check is removed, be sure to change the queue
979 system to know the ids
981 if(sender_peer_id != PEER_ID_SERVER)
983 infostream<<"Client::ProcessData(): Discarding data not "
984 "coming from server: peer_id="<<sender_peer_id
989 u8 ser_version = m_server_ser_ver;
991 if(command == TOCLIENT_INIT)
996 u8 deployed = data[2];
998 infostream<<"Client: TOCLIENT_INIT received with "
999 "deployed="<<((int)deployed&0xff)<<std::endl;
1001 if(!ser_ver_supported(deployed))
1003 infostream<<"Client: TOCLIENT_INIT: Server sent "
1004 <<"unsupported ser_fmt_ver"<<std::endl;
1008 m_server_ser_ver = deployed;
1010 // Get player position
1011 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
1012 if(datasize >= 2+1+6)
1013 playerpos_s16 = readV3S16(&data[2+1]);
1014 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
1017 // Set player position
1018 Player *player = m_env.getLocalPlayer();
1019 assert(player != NULL);
1020 player->setPosition(playerpos_f);
1022 if(datasize >= 2+1+6+8)
1025 m_map_seed = readU64(&data[2+1+6]);
1026 infostream<<"Client: received map seed: "<<m_map_seed<<std::endl;
1029 if(datasize >= 2+1+6+8+4)
1032 m_recommended_send_interval = readF1000(&data[2+1+6+8]);
1033 infostream<<"Client: received recommended send interval "
1034 <<m_recommended_send_interval<<std::endl;
1039 SharedBuffer<u8> reply(replysize);
1040 writeU16(&reply[0], TOSERVER_INIT2);
1042 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1049 if(command == TOCLIENT_ACCESS_DENIED)
1051 // The server didn't like our password. Note, this needs
1052 // to be processed even if the serialisation format has
1053 // not been agreed yet, the same as TOCLIENT_INIT.
1054 m_access_denied = true;
1055 m_access_denied_reason = L"Unknown";
1058 std::string datastring((char*)&data[2], datasize-2);
1059 std::istringstream is(datastring, std::ios_base::binary);
1060 m_access_denied_reason = deSerializeWideString(is);
1065 if(ser_version == SER_FMT_VER_INVALID)
1067 infostream<<"Client: Server serialization"
1068 " format invalid or not initialized."
1069 " Skipping incoming command="<<command<<std::endl;
1074 Handle runtime commands
1076 // there's no sane reason why we shouldn't have a player and
1077 // almost everyone needs a player reference
1078 Player *player = m_env.getLocalPlayer();
1079 assert(player != NULL);
1081 if(command == TOCLIENT_REMOVENODE)
1086 p.X = readS16(&data[2]);
1087 p.Y = readS16(&data[4]);
1088 p.Z = readS16(&data[6]);
1091 else if(command == TOCLIENT_ADDNODE)
1093 if(datasize < 8 + MapNode::serializedLength(ser_version))
1097 p.X = readS16(&data[2]);
1098 p.Y = readS16(&data[4]);
1099 p.Z = readS16(&data[6]);
1102 n.deSerialize(&data[8], ser_version);
1104 bool remove_metadata = true;
1105 u32 index = 8 + MapNode::serializedLength(ser_version);
1106 if ((datasize >= index+1) && data[index]){
1107 remove_metadata = false;
1110 addNode(p, n, remove_metadata);
1112 else if(command == TOCLIENT_BLOCKDATA)
1114 // Ignore too small packet
1119 p.X = readS16(&data[2]);
1120 p.Y = readS16(&data[4]);
1121 p.Z = readS16(&data[6]);
1123 std::string datastring((char*)&data[8], datasize-8);
1124 std::istringstream istr(datastring, std::ios_base::binary);
1129 v2s16 p2d(p.X, p.Z);
1130 sector = m_env.getMap().emergeSector(p2d);
1132 assert(sector->getPos() == p2d);
1134 block = sector->getBlockNoCreateNoEx(p.Y);
1138 Update an existing block
1140 block->deSerialize(istr, ser_version, false);
1141 block->deSerializeNetworkSpecific(istr);
1148 block = new MapBlock(&m_env.getMap(), p, this);
1149 block->deSerialize(istr, ser_version, false);
1150 block->deSerializeNetworkSpecific(istr);
1151 sector->insertBlock(block);
1155 Add it to mesh update queue and set it to be acknowledged after update.
1157 addUpdateMeshTaskWithEdge(p, true);
1159 else if(command == TOCLIENT_INVENTORY)
1164 std::string datastring((char*)&data[2], datasize-2);
1165 std::istringstream is(datastring, std::ios_base::binary);
1167 player->inventory.deSerialize(is);
1169 m_inventory_updated = true;
1171 delete m_inventory_from_server;
1172 m_inventory_from_server = new Inventory(player->inventory);
1173 m_inventory_from_server_age = 0.0;
1176 else if(command == TOCLIENT_TIME_OF_DAY)
1181 u16 time_of_day = readU16(&data[2]);
1182 time_of_day = time_of_day % 24000;
1183 float time_speed = 0;
1185 if(datasize >= 2 + 2 + 4)
1187 time_speed = readF1000(&data[4]);
1190 // Old message; try to approximate speed of time by ourselves
1191 float time_of_day_f = (float)time_of_day / 24000.0;
1192 float tod_diff_f = 0;
1194 if(time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
1195 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0;
1197 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
1199 m_last_time_of_day_f = time_of_day_f;
1200 float time_diff = m_time_of_day_update_timer;
1201 m_time_of_day_update_timer = 0;
1203 if(m_time_of_day_set){
1204 time_speed = (3600.0*24.0) * tod_diff_f / time_diff;
1205 infostream<<"Client: Measured time_of_day speed (old format): "
1206 <<time_speed<<" tod_diff_f="<<tod_diff_f
1207 <<" time_diff="<<time_diff<<std::endl;
1211 // Update environment
1212 m_env.setTimeOfDay(time_of_day);
1213 m_env.setTimeOfDaySpeed(time_speed);
1214 m_time_of_day_set = true;
1216 u32 dr = m_env.getDayNightRatio();
1217 infostream<<"Client: time_of_day="<<time_of_day
1218 <<" time_speed="<<time_speed
1219 <<" dr="<<dr<<std::endl;
1221 else if(command == TOCLIENT_CHAT_MESSAGE)
1229 std::string datastring((char*)&data[2], datasize-2);
1230 std::istringstream is(datastring, std::ios_base::binary);
1233 is.read((char*) buf, 2);
1234 u16 len = readU16(buf);
1236 std::wstring message;
1237 for(unsigned int i=0; i<len; i++)
1239 is.read((char*)buf, 2);
1240 message += (wchar_t)readU16(buf);
1243 m_chat_queue.push_back(message);
1245 else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1249 u16 count of removed objects
1250 for all removed objects {
1253 u16 count of added objects
1254 for all added objects {
1257 u32 initialization data length
1258 string initialization data
1263 // Get all data except the command number
1264 std::string datastring((char*)&data[2], datasize-2);
1265 // Throw them in an istringstream
1266 std::istringstream is(datastring, std::ios_base::binary);
1268 // Read removed objects
1270 u16 removed_count = readU16((u8*)buf);
1271 for(unsigned int i=0; i<removed_count; i++)
1274 u16 id = readU16((u8*)buf);
1275 m_env.removeActiveObject(id);
1278 // Read added objects
1280 u16 added_count = readU16((u8*)buf);
1281 for(unsigned int i=0; i<added_count; i++)
1284 u16 id = readU16((u8*)buf);
1286 u8 type = readU8((u8*)buf);
1287 std::string data = deSerializeLongString(is);
1289 m_env.addActiveObject(id, type, data);
1292 else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1304 // Get all data except the command number
1305 std::string datastring((char*)&data[2], datasize-2);
1306 // Throw them in an istringstream
1307 std::istringstream is(datastring, std::ios_base::binary);
1309 while(is.eof() == false)
1312 u16 id = readU16((u8*)buf);
1316 size_t message_size = readU16((u8*)buf);
1317 std::string message;
1318 message.reserve(message_size);
1319 for(unsigned int i=0; i<message_size; i++)
1322 message.append(buf, 1);
1324 // Pass on to the environment
1325 m_env.processActiveObjectMessage(id, message);
1328 else if(command == TOCLIENT_MOVEMENT)
1330 std::string datastring((char*)&data[2], datasize-2);
1331 std::istringstream is(datastring, std::ios_base::binary);
1333 player->movement_acceleration_default = readF1000(is) * BS;
1334 player->movement_acceleration_air = readF1000(is) * BS;
1335 player->movement_acceleration_fast = readF1000(is) * BS;
1336 player->movement_speed_walk = readF1000(is) * BS;
1337 player->movement_speed_crouch = readF1000(is) * BS;
1338 player->movement_speed_fast = readF1000(is) * BS;
1339 player->movement_speed_climb = readF1000(is) * BS;
1340 player->movement_speed_jump = readF1000(is) * BS;
1341 player->movement_liquid_fluidity = readF1000(is) * BS;
1342 player->movement_liquid_fluidity_smooth = readF1000(is) * BS;
1343 player->movement_liquid_sink = readF1000(is) * BS;
1344 player->movement_gravity = readF1000(is) * BS;
1346 else if(command == TOCLIENT_HP)
1348 std::string datastring((char*)&data[2], datasize-2);
1349 std::istringstream is(datastring, std::ios_base::binary);
1351 u8 oldhp = player->hp;
1357 // Add to ClientEvent queue
1359 event.type = CE_PLAYER_DAMAGE;
1360 event.player_damage.amount = oldhp - hp;
1361 m_client_event_queue.push_back(event);
1364 else if(command == TOCLIENT_BREATH)
1366 std::string datastring((char*)&data[2], datasize-2);
1367 std::istringstream is(datastring, std::ios_base::binary);
1369 player->setBreath(readU16(is));
1371 else if(command == TOCLIENT_MOVE_PLAYER)
1373 std::string datastring((char*)&data[2], datasize-2);
1374 std::istringstream is(datastring, std::ios_base::binary);
1376 v3f pos = readV3F1000(is);
1377 f32 pitch = readF1000(is);
1378 f32 yaw = readF1000(is);
1379 player->setPosition(pos);
1381 infostream<<"Client got TOCLIENT_MOVE_PLAYER"
1382 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1388 Add to ClientEvent queue.
1389 This has to be sent to the main program because otherwise
1390 it would just force the pitch and yaw values to whatever
1391 the camera points to.
1394 event.type = CE_PLAYER_FORCE_MOVE;
1395 event.player_force_move.pitch = pitch;
1396 event.player_force_move.yaw = yaw;
1397 m_client_event_queue.push_back(event);
1399 // Ignore damage for a few seconds, so that the player doesn't
1400 // get damage from falling on ground
1401 m_ignore_damage_timer = 3.0;
1403 else if(command == TOCLIENT_PLAYERITEM)
1405 infostream<<"Client: WARNING: Ignoring TOCLIENT_PLAYERITEM"<<std::endl;
1407 else if(command == TOCLIENT_DEATHSCREEN)
1409 std::string datastring((char*)&data[2], datasize-2);
1410 std::istringstream is(datastring, std::ios_base::binary);
1412 bool set_camera_point_target = readU8(is);
1413 v3f camera_point_target = readV3F1000(is);
1416 event.type = CE_DEATHSCREEN;
1417 event.deathscreen.set_camera_point_target = set_camera_point_target;
1418 event.deathscreen.camera_point_target_x = camera_point_target.X;
1419 event.deathscreen.camera_point_target_y = camera_point_target.Y;
1420 event.deathscreen.camera_point_target_z = camera_point_target.Z;
1421 m_client_event_queue.push_back(event);
1423 else if(command == TOCLIENT_ANNOUNCE_MEDIA)
1425 std::string datastring((char*)&data[2], datasize-2);
1426 std::istringstream is(datastring, std::ios_base::binary);
1428 int num_files = readU16(is);
1430 infostream<<"Client: Received media announcement: packet size: "
1431 <<datasize<<std::endl;
1433 if (m_media_downloader == NULL ||
1434 m_media_downloader->isStarted()) {
1435 const char *problem = m_media_downloader ?
1436 "we already saw another announcement" :
1437 "all media has been received already";
1438 errorstream<<"Client: Received media announcement but "
1440 <<" files="<<num_files
1441 <<" size="<<datasize<<std::endl;
1445 // Mesh update thread must be stopped while
1446 // updating content definitions
1447 assert(!m_mesh_update_thread.IsRunning());
1449 for(int i=0; i<num_files; i++)
1451 std::string name = deSerializeString(is);
1452 std::string sha1_base64 = deSerializeString(is);
1453 std::string sha1_raw = base64_decode(sha1_base64);
1454 m_media_downloader->addFile(name, sha1_raw);
1457 std::vector<std::string> remote_media;
1459 Strfnd sf(deSerializeString(is));
1460 while(!sf.atend()) {
1461 std::string baseurl = trim(sf.next(","));
1463 m_media_downloader->addRemoteServer(baseurl);
1466 catch(SerializationError& e) {
1467 // not supported by server or turned off
1470 m_media_downloader->step(this);
1472 else if(command == TOCLIENT_MEDIA)
1474 std::string datastring((char*)&data[2], datasize-2);
1475 std::istringstream is(datastring, std::ios_base::binary);
1479 u16 total number of file bunches
1480 u16 index of this bunch
1481 u32 number of files in this bunch
1489 int num_bunches = readU16(is);
1490 int bunch_i = readU16(is);
1491 u32 num_files = readU32(is);
1492 infostream<<"Client: Received files: bunch "<<bunch_i<<"/"
1493 <<num_bunches<<" files="<<num_files
1494 <<" size="<<datasize<<std::endl;
1499 if (m_media_downloader == NULL ||
1500 !m_media_downloader->isStarted()) {
1501 const char *problem = m_media_downloader ?
1502 "media has not been requested" :
1503 "all media has been received already";
1504 errorstream<<"Client: Received media but "
1506 <<" bunch "<<bunch_i<<"/"<<num_bunches
1507 <<" files="<<num_files
1508 <<" size="<<datasize<<std::endl;
1512 // Mesh update thread must be stopped while
1513 // updating content definitions
1514 assert(!m_mesh_update_thread.IsRunning());
1516 for(unsigned int i=0; i<num_files; i++){
1517 std::string name = deSerializeString(is);
1518 std::string data = deSerializeLongString(is);
1519 m_media_downloader->conventionalTransferDone(
1523 else if(command == TOCLIENT_TOOLDEF)
1525 infostream<<"Client: WARNING: Ignoring TOCLIENT_TOOLDEF"<<std::endl;
1527 else if(command == TOCLIENT_NODEDEF)
1529 infostream<<"Client: Received node definitions: packet size: "
1530 <<datasize<<std::endl;
1532 // Mesh update thread must be stopped while
1533 // updating content definitions
1534 assert(!m_mesh_update_thread.IsRunning());
1536 // Decompress node definitions
1537 std::string datastring((char*)&data[2], datasize-2);
1538 std::istringstream is(datastring, std::ios_base::binary);
1539 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1540 std::ostringstream tmp_os;
1541 decompressZlib(tmp_is, tmp_os);
1543 // Deserialize node definitions
1544 std::istringstream tmp_is2(tmp_os.str());
1545 m_nodedef->deSerialize(tmp_is2);
1546 m_nodedef_received = true;
1548 else if(command == TOCLIENT_CRAFTITEMDEF)
1550 infostream<<"Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF"<<std::endl;
1552 else if(command == TOCLIENT_ITEMDEF)
1554 infostream<<"Client: Received item definitions: packet size: "
1555 <<datasize<<std::endl;
1557 // Mesh update thread must be stopped while
1558 // updating content definitions
1559 assert(!m_mesh_update_thread.IsRunning());
1561 // Decompress item definitions
1562 std::string datastring((char*)&data[2], datasize-2);
1563 std::istringstream is(datastring, std::ios_base::binary);
1564 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1565 std::ostringstream tmp_os;
1566 decompressZlib(tmp_is, tmp_os);
1568 // Deserialize node definitions
1569 std::istringstream tmp_is2(tmp_os.str());
1570 m_itemdef->deSerialize(tmp_is2);
1571 m_itemdef_received = true;
1573 else if(command == TOCLIENT_PLAY_SOUND)
1575 std::string datastring((char*)&data[2], datasize-2);
1576 std::istringstream is(datastring, std::ios_base::binary);
1578 s32 server_id = readS32(is);
1579 std::string name = deSerializeString(is);
1580 float gain = readF1000(is);
1581 int type = readU8(is); // 0=local, 1=positional, 2=object
1582 v3f pos = readV3F1000(is);
1583 u16 object_id = readU16(is);
1584 bool loop = readU8(is);
1589 client_id = m_sound->playSound(name, loop, gain);
1591 case 1: // positional
1592 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1595 ClientActiveObject *cao = m_env.getActiveObject(object_id);
1597 pos = cao->getPosition();
1598 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1599 // TODO: Set up sound to move with object
1604 if(client_id != -1){
1605 m_sounds_server_to_client[server_id] = client_id;
1606 m_sounds_client_to_server[client_id] = server_id;
1608 m_sounds_to_objects[client_id] = object_id;
1611 else if(command == TOCLIENT_STOP_SOUND)
1613 std::string datastring((char*)&data[2], datasize-2);
1614 std::istringstream is(datastring, std::ios_base::binary);
1616 s32 server_id = readS32(is);
1617 std::map<s32, int>::iterator i =
1618 m_sounds_server_to_client.find(server_id);
1619 if(i != m_sounds_server_to_client.end()){
1620 int client_id = i->second;
1621 m_sound->stopSound(client_id);
1624 else if(command == TOCLIENT_PRIVILEGES)
1626 std::string datastring((char*)&data[2], datasize-2);
1627 std::istringstream is(datastring, std::ios_base::binary);
1629 m_privileges.clear();
1630 infostream<<"Client: Privileges updated: ";
1631 u16 num_privileges = readU16(is);
1632 for(unsigned int i=0; i<num_privileges; i++){
1633 std::string priv = deSerializeString(is);
1634 m_privileges.insert(priv);
1635 infostream<<priv<<" ";
1637 infostream<<std::endl;
1639 else if(command == TOCLIENT_INVENTORY_FORMSPEC)
1641 std::string datastring((char*)&data[2], datasize-2);
1642 std::istringstream is(datastring, std::ios_base::binary);
1644 // Store formspec in LocalPlayer
1645 player->inventory_formspec = deSerializeLongString(is);
1647 else if(command == TOCLIENT_DETACHED_INVENTORY)
1649 std::string datastring((char*)&data[2], datasize-2);
1650 std::istringstream is(datastring, std::ios_base::binary);
1652 std::string name = deSerializeString(is);
1654 infostream<<"Client: Detached inventory update: \""<<name<<"\""<<std::endl;
1656 Inventory *inv = NULL;
1657 if(m_detached_inventories.count(name) > 0)
1658 inv = m_detached_inventories[name];
1660 inv = new Inventory(m_itemdef);
1661 m_detached_inventories[name] = inv;
1663 inv->deSerialize(is);
1665 else if(command == TOCLIENT_SHOW_FORMSPEC)
1667 std::string datastring((char*)&data[2], datasize-2);
1668 std::istringstream is(datastring, std::ios_base::binary);
1670 std::string formspec = deSerializeLongString(is);
1671 std::string formname = deSerializeString(is);
1674 event.type = CE_SHOW_FORMSPEC;
1675 // pointer is required as event is a struct only!
1676 // adding a std:string to a struct isn't possible
1677 event.show_formspec.formspec = new std::string(formspec);
1678 event.show_formspec.formname = new std::string(formname);
1679 m_client_event_queue.push_back(event);
1681 else if(command == TOCLIENT_SPAWN_PARTICLE)
1683 std::string datastring((char*)&data[2], datasize-2);
1684 std::istringstream is(datastring, std::ios_base::binary);
1686 v3f pos = readV3F1000(is);
1687 v3f vel = readV3F1000(is);
1688 v3f acc = readV3F1000(is);
1689 float expirationtime = readF1000(is);
1690 float size = readF1000(is);
1691 bool collisiondetection = readU8(is);
1692 std::string texture = deSerializeLongString(is);
1693 bool vertical = false;
1695 vertical = readU8(is);
1699 event.type = CE_SPAWN_PARTICLE;
1700 event.spawn_particle.pos = new v3f (pos);
1701 event.spawn_particle.vel = new v3f (vel);
1702 event.spawn_particle.acc = new v3f (acc);
1703 event.spawn_particle.expirationtime = expirationtime;
1704 event.spawn_particle.size = size;
1705 event.spawn_particle.collisiondetection = collisiondetection;
1706 event.spawn_particle.vertical = vertical;
1707 event.spawn_particle.texture = new std::string(texture);
1709 m_client_event_queue.push_back(event);
1711 else if(command == TOCLIENT_ADD_PARTICLESPAWNER)
1713 std::string datastring((char*)&data[2], datasize-2);
1714 std::istringstream is(datastring, std::ios_base::binary);
1716 u16 amount = readU16(is);
1717 float spawntime = readF1000(is);
1718 v3f minpos = readV3F1000(is);
1719 v3f maxpos = readV3F1000(is);
1720 v3f minvel = readV3F1000(is);
1721 v3f maxvel = readV3F1000(is);
1722 v3f minacc = readV3F1000(is);
1723 v3f maxacc = readV3F1000(is);
1724 float minexptime = readF1000(is);
1725 float maxexptime = readF1000(is);
1726 float minsize = readF1000(is);
1727 float maxsize = readF1000(is);
1728 bool collisiondetection = readU8(is);
1729 std::string texture = deSerializeLongString(is);
1730 u32 id = readU32(is);
1731 bool vertical = false;
1733 vertical = readU8(is);
1737 event.type = CE_ADD_PARTICLESPAWNER;
1738 event.add_particlespawner.amount = amount;
1739 event.add_particlespawner.spawntime = spawntime;
1740 event.add_particlespawner.minpos = new v3f (minpos);
1741 event.add_particlespawner.maxpos = new v3f (maxpos);
1742 event.add_particlespawner.minvel = new v3f (minvel);
1743 event.add_particlespawner.maxvel = new v3f (maxvel);
1744 event.add_particlespawner.minacc = new v3f (minacc);
1745 event.add_particlespawner.maxacc = new v3f (maxacc);
1746 event.add_particlespawner.minexptime = minexptime;
1747 event.add_particlespawner.maxexptime = maxexptime;
1748 event.add_particlespawner.minsize = minsize;
1749 event.add_particlespawner.maxsize = maxsize;
1750 event.add_particlespawner.collisiondetection = collisiondetection;
1751 event.add_particlespawner.vertical = vertical;
1752 event.add_particlespawner.texture = new std::string(texture);
1753 event.add_particlespawner.id = id;
1755 m_client_event_queue.push_back(event);
1757 else if(command == TOCLIENT_DELETE_PARTICLESPAWNER)
1759 std::string datastring((char*)&data[2], datasize-2);
1760 std::istringstream is(datastring, std::ios_base::binary);
1762 u32 id = readU16(is);
1765 event.type = CE_DELETE_PARTICLESPAWNER;
1766 event.delete_particlespawner.id = id;
1768 m_client_event_queue.push_back(event);
1770 else if(command == TOCLIENT_HUDADD)
1772 std::string datastring((char *)&data[2], datasize - 2);
1773 std::istringstream is(datastring, std::ios_base::binary);
1775 u32 id = readU32(is);
1776 u8 type = readU8(is);
1777 v2f pos = readV2F1000(is);
1778 std::string name = deSerializeString(is);
1779 v2f scale = readV2F1000(is);
1780 std::string text = deSerializeString(is);
1781 u32 number = readU32(is);
1782 u32 item = readU32(is);
1783 u32 dir = readU32(is);
1784 v2f align = readV2F1000(is);
1785 v2f offset = readV2F1000(is);
1788 world_pos = readV3F1000(is);
1789 }catch(SerializationError &e) {};
1792 event.type = CE_HUDADD;
1793 event.hudadd.id = id;
1794 event.hudadd.type = type;
1795 event.hudadd.pos = new v2f(pos);
1796 event.hudadd.name = new std::string(name);
1797 event.hudadd.scale = new v2f(scale);
1798 event.hudadd.text = new std::string(text);
1799 event.hudadd.number = number;
1800 event.hudadd.item = item;
1801 event.hudadd.dir = dir;
1802 event.hudadd.align = new v2f(align);
1803 event.hudadd.offset = new v2f(offset);
1804 event.hudadd.world_pos = new v3f(world_pos);
1805 m_client_event_queue.push_back(event);
1807 else if(command == TOCLIENT_HUDRM)
1809 std::string datastring((char *)&data[2], datasize - 2);
1810 std::istringstream is(datastring, std::ios_base::binary);
1812 u32 id = readU32(is);
1815 event.type = CE_HUDRM;
1816 event.hudrm.id = id;
1817 m_client_event_queue.push_back(event);
1819 else if(command == TOCLIENT_HUDCHANGE)
1826 std::string datastring((char *)&data[2], datasize - 2);
1827 std::istringstream is(datastring, std::ios_base::binary);
1829 u32 id = readU32(is);
1830 u8 stat = (HudElementStat)readU8(is);
1832 if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
1833 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
1834 v2fdata = readV2F1000(is);
1835 else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
1836 sdata = deSerializeString(is);
1837 else if (stat == HUD_STAT_WORLD_POS)
1838 v3fdata = readV3F1000(is);
1840 intdata = readU32(is);
1843 event.type = CE_HUDCHANGE;
1844 event.hudchange.id = id;
1845 event.hudchange.stat = (HudElementStat)stat;
1846 event.hudchange.v2fdata = new v2f(v2fdata);
1847 event.hudchange.v3fdata = new v3f(v3fdata);
1848 event.hudchange.sdata = new std::string(sdata);
1849 event.hudchange.data = intdata;
1850 m_client_event_queue.push_back(event);
1852 else if(command == TOCLIENT_HUD_SET_FLAGS)
1854 std::string datastring((char *)&data[2], datasize - 2);
1855 std::istringstream is(datastring, std::ios_base::binary);
1857 u32 flags = readU32(is);
1858 u32 mask = readU32(is);
1860 player->hud_flags &= ~mask;
1861 player->hud_flags |= flags;
1863 else if(command == TOCLIENT_HUD_SET_PARAM)
1865 std::string datastring((char *)&data[2], datasize - 2);
1866 std::istringstream is(datastring, std::ios_base::binary);
1868 u16 param = readU16(is);
1869 std::string value = deSerializeString(is);
1871 if(param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) {
1872 s32 hotbar_itemcount = readS32((u8*) value.c_str());
1873 if(hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
1874 player->hud_hotbar_itemcount = hotbar_itemcount;
1876 else if (param == HUD_PARAM_HOTBAR_IMAGE) {
1877 ((LocalPlayer *) player)->hotbar_image = value;
1879 else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
1880 ((LocalPlayer *) player)->hotbar_selected_image = value;
1883 else if(command == TOCLIENT_SET_SKY)
1885 std::string datastring((char *)&data[2], datasize - 2);
1886 std::istringstream is(datastring, std::ios_base::binary);
1888 video::SColor *bgcolor = new video::SColor(readARGB8(is));
1889 std::string *type = new std::string(deSerializeString(is));
1890 u16 count = readU16(is);
1891 std::vector<std::string> *params = new std::vector<std::string>;
1893 for(size_t i=0; i<count; i++)
1894 params->push_back(deSerializeString(is));
1897 event.type = CE_SET_SKY;
1898 event.set_sky.bgcolor = bgcolor;
1899 event.set_sky.type = type;
1900 event.set_sky.params = params;
1901 m_client_event_queue.push_back(event);
1903 else if(command == TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO)
1905 std::string datastring((char *)&data[2], datasize - 2);
1906 std::istringstream is(datastring, std::ios_base::binary);
1908 bool do_override = readU8(is);
1909 float day_night_ratio_f = (float)readU16(is) / 65536;
1912 event.type = CE_OVERRIDE_DAY_NIGHT_RATIO;
1913 event.override_day_night_ratio.do_override = do_override;
1914 event.override_day_night_ratio.ratio_f = day_night_ratio_f;
1915 m_client_event_queue.push_back(event);
1919 infostream<<"Client: Ignoring unknown command "
1920 <<command<<std::endl;
1924 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
1926 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1927 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
1930 void Client::interact(u8 action, const PointedThing& pointed)
1932 if(m_state != LC_Ready){
1933 infostream<<"Client::interact() "
1934 "cancelled (not connected)"
1939 std::ostringstream os(std::ios_base::binary);
1945 [5] u32 length of the next item
1946 [9] serialized PointedThing
1948 0: start digging (from undersurface) or use
1949 1: stop digging (all parameters ignored)
1950 2: digging completed
1951 3: place block or item (to abovesurface)
1954 writeU16(os, TOSERVER_INTERACT);
1955 writeU8(os, action);
1956 writeU16(os, getPlayerItem());
1957 std::ostringstream tmp_os(std::ios::binary);
1958 pointed.serialize(tmp_os);
1959 os<<serializeLongString(tmp_os.str());
1961 std::string s = os.str();
1962 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1965 Send(0, data, true);
1968 void Client::sendNodemetaFields(v3s16 p, const std::string &formname,
1969 const std::map<std::string, std::string> &fields)
1971 std::ostringstream os(std::ios_base::binary);
1973 writeU16(os, TOSERVER_NODEMETA_FIELDS);
1975 os<<serializeString(formname);
1976 size_t fields_size = fields.size();
1977 assert(fields_size <= 0xFFFF);
1978 writeU16(os, (u16) (fields_size & 0xFFFF));
1979 for(std::map<std::string, std::string>::const_iterator
1980 i = fields.begin(); i != fields.end(); i++){
1981 const std::string &name = i->first;
1982 const std::string &value = i->second;
1983 os<<serializeString(name);
1984 os<<serializeLongString(value);
1988 std::string s = os.str();
1989 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1991 Send(0, data, true);
1994 void Client::sendInventoryFields(const std::string &formname,
1995 const std::map<std::string, std::string> &fields)
1997 std::ostringstream os(std::ios_base::binary);
1999 writeU16(os, TOSERVER_INVENTORY_FIELDS);
2000 os<<serializeString(formname);
2001 size_t fields_size = fields.size();
2002 assert(fields_size <= 0xFFFF);
2003 writeU16(os, (u16) (fields_size & 0xFFFF));
2004 for(std::map<std::string, std::string>::const_iterator
2005 i = fields.begin(); i != fields.end(); i++){
2006 const std::string &name = i->first;
2007 const std::string &value = i->second;
2008 os<<serializeString(name);
2009 os<<serializeLongString(value);
2013 std::string s = os.str();
2014 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2016 Send(0, data, true);
2019 void Client::sendInventoryAction(InventoryAction *a)
2021 std::ostringstream os(std::ios_base::binary);
2025 writeU16(buf, TOSERVER_INVENTORY_ACTION);
2026 os.write((char*)buf, 2);
2031 std::string s = os.str();
2032 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2034 Send(0, data, true);
2037 void Client::sendChatMessage(const std::wstring &message)
2039 std::ostringstream os(std::ios_base::binary);
2043 writeU16(buf, TOSERVER_CHAT_MESSAGE);
2044 os.write((char*)buf, 2);
2047 size_t messagesize = message.size();
2048 assert(messagesize <= 0xFFFF);
2049 writeU16(buf, (u16) (messagesize & 0xFF));
2050 os.write((char*)buf, 2);
2053 for(unsigned int i=0; i<message.size(); i++)
2057 os.write((char*)buf, 2);
2061 std::string s = os.str();
2062 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2064 Send(0, data, true);
2067 void Client::sendChangePassword(const std::wstring &oldpassword,
2068 const std::wstring &newpassword)
2070 Player *player = m_env.getLocalPlayer();
2074 std::string playername = player->getName();
2075 std::string oldpwd = translatePassword(playername, oldpassword);
2076 std::string newpwd = translatePassword(playername, newpassword);
2078 std::ostringstream os(std::ios_base::binary);
2079 u8 buf[2+PASSWORD_SIZE*2];
2081 [0] u16 TOSERVER_PASSWORD
2082 [2] u8[28] old password
2083 [30] u8[28] new password
2086 writeU16(buf, TOSERVER_PASSWORD);
2087 for(unsigned int i=0;i<PASSWORD_SIZE-1;i++)
2089 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
2090 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
2092 buf[2+PASSWORD_SIZE-1] = 0;
2093 buf[30+PASSWORD_SIZE-1] = 0;
2094 os.write((char*)buf, 2+PASSWORD_SIZE*2);
2097 std::string s = os.str();
2098 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2100 Send(0, data, true);
2104 void Client::sendDamage(u8 damage)
2106 DSTACK(__FUNCTION_NAME);
2107 std::ostringstream os(std::ios_base::binary);
2109 writeU16(os, TOSERVER_DAMAGE);
2110 writeU8(os, damage);
2113 std::string s = os.str();
2114 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2116 Send(0, data, true);
2119 void Client::sendBreath(u16 breath)
2121 DSTACK(__FUNCTION_NAME);
2122 std::ostringstream os(std::ios_base::binary);
2124 writeU16(os, TOSERVER_BREATH);
2125 writeU16(os, breath);
2127 std::string s = os.str();
2128 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2130 Send(0, data, true);
2133 void Client::sendRespawn()
2135 DSTACK(__FUNCTION_NAME);
2136 std::ostringstream os(std::ios_base::binary);
2138 writeU16(os, TOSERVER_RESPAWN);
2141 std::string s = os.str();
2142 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2144 Send(0, data, true);
2147 void Client::sendReady()
2149 DSTACK(__FUNCTION_NAME);
2150 std::ostringstream os(std::ios_base::binary);
2152 writeU16(os, TOSERVER_CLIENT_READY);
2153 writeU8(os,VERSION_MAJOR);
2154 writeU8(os,VERSION_MINOR);
2155 writeU8(os,VERSION_PATCH_ORIG);
2158 writeU16(os,strlen(CMAKE_VERSION_GITHASH));
2159 os.write(CMAKE_VERSION_GITHASH,strlen(CMAKE_VERSION_GITHASH));
2162 std::string s = os.str();
2163 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2165 Send(0, data, true);
2168 void Client::sendPlayerPos()
2170 LocalPlayer *myplayer = m_env.getLocalPlayer();
2171 if(myplayer == NULL)
2174 // Save bandwidth by only updating position when something changed
2175 if(myplayer->last_position == myplayer->getPosition() &&
2176 myplayer->last_speed == myplayer->getSpeed() &&
2177 myplayer->last_pitch == myplayer->getPitch() &&
2178 myplayer->last_yaw == myplayer->getYaw() &&
2179 myplayer->last_keyPressed == myplayer->keyPressed)
2182 myplayer->last_position = myplayer->getPosition();
2183 myplayer->last_speed = myplayer->getSpeed();
2184 myplayer->last_pitch = myplayer->getPitch();
2185 myplayer->last_yaw = myplayer->getYaw();
2186 myplayer->last_keyPressed = myplayer->keyPressed;
2190 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2191 our_peer_id = m_con.GetPeerID();
2194 // Set peer id if not set already
2195 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2196 myplayer->peer_id = our_peer_id;
2197 // Check that an existing peer_id is the same as the connection's
2198 assert(myplayer->peer_id == our_peer_id);
2200 v3f pf = myplayer->getPosition();
2201 v3f sf = myplayer->getSpeed();
2202 s32 pitch = myplayer->getPitch() * 100;
2203 s32 yaw = myplayer->getYaw() * 100;
2204 u32 keyPressed = myplayer->keyPressed;
2206 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
2207 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
2211 [2] v3s32 position*100
2212 [2+12] v3s32 speed*100
2213 [2+12+12] s32 pitch*100
2214 [2+12+12+4] s32 yaw*100
2215 [2+12+12+4+4] u32 keyPressed
2217 SharedBuffer<u8> data(2+12+12+4+4+4);
2218 writeU16(&data[0], TOSERVER_PLAYERPOS);
2219 writeV3S32(&data[2], position);
2220 writeV3S32(&data[2+12], speed);
2221 writeS32(&data[2+12+12], pitch);
2222 writeS32(&data[2+12+12+4], yaw);
2223 writeU32(&data[2+12+12+4+4], keyPressed);
2224 // Send as unreliable
2225 Send(0, data, false);
2228 void Client::sendPlayerItem(u16 item)
2230 Player *myplayer = m_env.getLocalPlayer();
2231 if(myplayer == NULL)
2234 u16 our_peer_id = m_con.GetPeerID();
2236 // Set peer id if not set already
2237 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2238 myplayer->peer_id = our_peer_id;
2239 // Check that an existing peer_id is the same as the connection's
2240 assert(myplayer->peer_id == our_peer_id);
2242 SharedBuffer<u8> data(2+2);
2243 writeU16(&data[0], TOSERVER_PLAYERITEM);
2244 writeU16(&data[2], item);
2247 Send(0, data, true);
2250 void Client::removeNode(v3s16 p)
2252 std::map<v3s16, MapBlock*> modified_blocks;
2256 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
2258 catch(InvalidPositionException &e)
2262 // add urgent task to update the modified node
2263 addUpdateMeshTaskForNode(p, false, true);
2265 for(std::map<v3s16, MapBlock * >::iterator
2266 i = modified_blocks.begin();
2267 i != modified_blocks.end(); ++i)
2269 addUpdateMeshTaskWithEdge(i->first);
2273 void Client::addNode(v3s16 p, MapNode n, bool remove_metadata)
2275 TimeTaker timer1("Client::addNode()");
2277 std::map<v3s16, MapBlock*> modified_blocks;
2281 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
2282 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
2284 catch(InvalidPositionException &e)
2287 for(std::map<v3s16, MapBlock * >::iterator
2288 i = modified_blocks.begin();
2289 i != modified_blocks.end(); ++i)
2291 addUpdateMeshTaskWithEdge(i->first);
2295 void Client::setPlayerControl(PlayerControl &control)
2297 LocalPlayer *player = m_env.getLocalPlayer();
2298 assert(player != NULL);
2299 player->control = control;
2302 void Client::selectPlayerItem(u16 item)
2304 m_playeritem = item;
2305 m_inventory_updated = true;
2306 sendPlayerItem(item);
2309 // Returns true if the inventory of the local player has been
2310 // updated from the server. If it is true, it is set to false.
2311 bool Client::getLocalInventoryUpdated()
2313 bool updated = m_inventory_updated;
2314 m_inventory_updated = false;
2318 // Copies the inventory of the local player to parameter
2319 void Client::getLocalInventory(Inventory &dst)
2321 Player *player = m_env.getLocalPlayer();
2322 assert(player != NULL);
2323 dst = player->inventory;
2326 Inventory* Client::getInventory(const InventoryLocation &loc)
2329 case InventoryLocation::UNDEFINED:
2332 case InventoryLocation::CURRENT_PLAYER:
2334 Player *player = m_env.getLocalPlayer();
2335 assert(player != NULL);
2336 return &player->inventory;
2339 case InventoryLocation::PLAYER:
2341 Player *player = m_env.getPlayer(loc.name.c_str());
2344 return &player->inventory;
2347 case InventoryLocation::NODEMETA:
2349 NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p);
2352 return meta->getInventory();
2355 case InventoryLocation::DETACHED:
2357 if(m_detached_inventories.count(loc.name) == 0)
2359 return m_detached_inventories[loc.name];
2368 void Client::inventoryAction(InventoryAction *a)
2371 Send it to the server
2373 sendInventoryAction(a);
2376 Predict some local inventory changes
2378 a->clientApply(this, this);
2384 ClientActiveObject * Client::getSelectedActiveObject(
2386 v3f from_pos_f_on_map,
2387 core::line3d<f32> shootline_on_map
2390 std::vector<DistanceSortedActiveObject> objects;
2392 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
2395 // After this, the closest object is the first in the array.
2396 std::sort(objects.begin(), objects.end());
2398 for(unsigned int i=0; i<objects.size(); i++)
2400 ClientActiveObject *obj = objects[i].obj;
2402 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
2403 if(selection_box == NULL)
2406 v3f pos = obj->getPosition();
2408 core::aabbox3d<f32> offsetted_box(
2409 selection_box->MinEdge + pos,
2410 selection_box->MaxEdge + pos
2413 if(offsetted_box.intersectsWithLine(shootline_on_map))
2422 std::list<std::string> Client::getConnectedPlayerNames()
2424 return m_env.getPlayerNames();
2427 float Client::getAnimationTime()
2429 return m_animation_time;
2432 int Client::getCrackLevel()
2434 return m_crack_level;
2437 void Client::setCrack(int level, v3s16 pos)
2439 int old_crack_level = m_crack_level;
2440 v3s16 old_crack_pos = m_crack_pos;
2442 m_crack_level = level;
2445 if(old_crack_level >= 0 && (level < 0 || pos != old_crack_pos))
2448 addUpdateMeshTaskForNode(old_crack_pos, false, true);
2450 if(level >= 0 && (old_crack_level < 0 || pos != old_crack_pos))
2453 addUpdateMeshTaskForNode(pos, false, true);
2459 Player *player = m_env.getLocalPlayer();
2460 assert(player != NULL);
2464 u16 Client::getBreath()
2466 Player *player = m_env.getLocalPlayer();
2467 assert(player != NULL);
2468 return player->getBreath();
2471 bool Client::getChatMessage(std::wstring &message)
2473 if(m_chat_queue.size() == 0)
2475 message = m_chat_queue.pop_front();
2479 void Client::typeChatMessage(const std::wstring &message)
2481 // Discard empty line
2486 sendChatMessage(message);
2489 if (message[0] == L'/')
2491 m_chat_queue.push_back(
2492 (std::wstring)L"issued command: "+message);
2496 LocalPlayer *player = m_env.getLocalPlayer();
2497 assert(player != NULL);
2498 std::wstring name = narrow_to_wide(player->getName());
2499 m_chat_queue.push_back(
2500 (std::wstring)L"<"+name+L"> "+message);
2504 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
2506 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2511 Create a task to update the mesh of the block
2514 MeshMakeData *data = new MeshMakeData(this);
2517 //TimeTaker timer("data fill");
2519 // Debug: 1-6ms, avg=2ms
2521 data->setCrack(m_crack_level, m_crack_pos);
2522 data->setSmoothLighting(g_settings->getBool("smooth_lighting"));
2525 // Add task to queue
2526 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server, urgent);
2529 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent)
2532 v3s16 p = blockpos + v3s16(0,0,0);
2533 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2534 addUpdateMeshTask(p, ack_to_server, urgent);
2536 catch(InvalidPositionException &e){}
2539 for (int i=0;i<6;i++)
2542 v3s16 p = blockpos + g_6dirs[i];
2543 addUpdateMeshTask(p, false, urgent);
2545 catch(InvalidPositionException &e){}
2549 void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool urgent)
2553 infostream<<"Client::addUpdateMeshTaskForNode(): "
2554 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2558 v3s16 blockpos = getNodeBlockPos(nodepos);
2559 v3s16 blockpos_relative = blockpos * MAP_BLOCKSIZE;
2562 v3s16 p = blockpos + v3s16(0,0,0);
2563 addUpdateMeshTask(p, ack_to_server, urgent);
2565 catch(InvalidPositionException &e){}
2568 if(nodepos.X == blockpos_relative.X){
2570 v3s16 p = blockpos + v3s16(-1,0,0);
2571 addUpdateMeshTask(p, false, urgent);
2573 catch(InvalidPositionException &e){}
2576 if(nodepos.Y == blockpos_relative.Y){
2578 v3s16 p = blockpos + v3s16(0,-1,0);
2579 addUpdateMeshTask(p, false, urgent);
2581 catch(InvalidPositionException &e){}
2584 if(nodepos.Z == blockpos_relative.Z){
2586 v3s16 p = blockpos + v3s16(0,0,-1);
2587 addUpdateMeshTask(p, false, urgent);
2589 catch(InvalidPositionException &e){}
2593 ClientEvent Client::getClientEvent()
2595 if(m_client_event_queue.size() == 0)
2598 event.type = CE_NONE;
2601 return m_client_event_queue.pop_front();
2604 float Client::mediaReceiveProgress()
2606 if (m_media_downloader)
2607 return m_media_downloader->getProgress();
2609 return 1.0; // downloader only exists when not yet done
2612 void draw_load_screen(const std::wstring &text,
2613 IrrlichtDevice* device, gui::IGUIFont* font,
2614 float dtime=0 ,int percent=0, bool clouds=true);
2616 void Client::afterContentReceived(IrrlichtDevice *device, gui::IGUIFont* font)
2618 infostream<<"Client::afterContentReceived() started"<<std::endl;
2619 assert(m_itemdef_received);
2620 assert(m_nodedef_received);
2621 assert(mediaReceived());
2623 // Rebuild inherited images and recreate textures
2624 infostream<<"- Rebuilding images and textures"<<std::endl;
2625 m_tsrc->rebuildImagesAndTextures();
2628 infostream<<"- Rebuilding shaders"<<std::endl;
2629 m_shsrc->rebuildShaders();
2631 // Update node aliases
2632 infostream<<"- Updating node aliases"<<std::endl;
2633 m_nodedef->updateAliases(m_itemdef);
2635 // Update node textures
2636 infostream<<"- Updating node textures"<<std::endl;
2637 m_nodedef->updateTextures(m_tsrc);
2639 // Preload item textures and meshes if configured to
2640 if(g_settings->getBool("preload_item_visuals"))
2642 verbosestream<<"Updating item textures and meshes"<<std::endl;
2643 wchar_t* text = wgettext("Item textures...");
2644 draw_load_screen(text,device,font,0,0);
2645 std::set<std::string> names = m_itemdef->getAll();
2646 size_t size = names.size();
2649 for(std::set<std::string>::const_iterator
2650 i = names.begin(); i != names.end(); ++i){
2651 // Asking for these caches the result
2652 m_itemdef->getInventoryTexture(*i, this);
2653 m_itemdef->getWieldMesh(*i, this);
2655 percent = count*100/size;
2656 if (count%50 == 0) // only update every 50 item
2657 draw_load_screen(text,device,font,0,percent);
2662 // Start mesh update thread after setting up content definitions
2663 infostream<<"- Starting mesh update thread"<<std::endl;
2664 m_mesh_update_thread.Start();
2668 infostream<<"Client::afterContentReceived() done"<<std::endl;
2671 float Client::getRTT(void)
2673 return m_con.getPeerStat(PEER_ID_SERVER,con::AVG_RTT);
2676 // IGameDef interface
2678 IItemDefManager* Client::getItemDefManager()
2682 INodeDefManager* Client::getNodeDefManager()
2686 ICraftDefManager* Client::getCraftDefManager()
2689 //return m_craftdef;
2691 ITextureSource* Client::getTextureSource()
2695 IShaderSource* Client::getShaderSource()
2699 u16 Client::allocateUnknownNodeId(const std::string &name)
2701 errorstream<<"Client::allocateUnknownNodeId(): "
2702 <<"Client cannot allocate node IDs"<<std::endl;
2704 return CONTENT_IGNORE;
2706 ISoundManager* Client::getSoundManager()
2710 MtEventManager* Client::getEventManager()
2715 scene::IAnimatedMesh* Client::getMesh(const std::string &filename)
2717 std::map<std::string, std::string>::const_iterator i =
2718 m_mesh_data.find(filename);
2719 if(i == m_mesh_data.end()){
2720 errorstream<<"Client::getMesh(): Mesh not found: \""<<filename<<"\""
2724 const std::string &data = i->second;
2725 scene::ISceneManager *smgr = m_device->getSceneManager();
2727 // Create the mesh, remove it from cache and return it
2728 // This allows unique vertex colors and other properties for each instance
2729 Buffer<char> data_rw(data.c_str(), data.size()); // Const-incorrect Irrlicht
2730 io::IFileSystem *irrfs = m_device->getFileSystem();
2731 io::IReadFile *rfile = irrfs->createMemoryReadFile(
2732 *data_rw, data_rw.getSize(), filename.c_str());
2735 scene::IAnimatedMesh *mesh = smgr->getMesh(rfile);
2737 // NOTE: By playing with Irrlicht refcounts, maybe we could cache a bunch
2738 // of uniquely named instances and re-use them
2740 smgr->getMeshCache()->removeMesh(mesh);