3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include "clientserver.h"
24 #include "jthread/jmutexautolock.h"
29 #include "mapsector.h"
30 #include "mapblock_mesh.h"
36 #include "nodemetadata.h"
40 #include <IFileSystem.h>
42 #include "clientmap.h"
43 #include "clientmedia.h"
45 #include "util/string.h"
46 #include "IMeshCache.h"
47 #include "serialization.h"
48 #include "util/serialize.h"
50 #include "util/directiontables.h"
51 #include "util/pointedthing.h"
58 QueuedMeshUpdate::QueuedMeshUpdate():
61 ack_block_to_server(false)
65 QueuedMeshUpdate::~QueuedMeshUpdate()
75 MeshUpdateQueue::MeshUpdateQueue()
79 MeshUpdateQueue::~MeshUpdateQueue()
81 JMutexAutoLock lock(m_mutex);
83 for(std::vector<QueuedMeshUpdate*>::iterator
85 i != m_queue.end(); i++)
87 QueuedMeshUpdate *q = *i;
93 peer_id=0 adds with nobody to send to
95 void MeshUpdateQueue::addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_server, bool urgent)
97 DSTACK(__FUNCTION_NAME);
101 JMutexAutoLock lock(m_mutex);
107 Find if block is already in queue.
108 If it is, update the data and quit.
110 for(std::vector<QueuedMeshUpdate*>::iterator
112 i != m_queue.end(); i++)
114 QueuedMeshUpdate *q = *i;
120 if(ack_block_to_server)
121 q->ack_block_to_server = true;
129 QueuedMeshUpdate *q = new QueuedMeshUpdate;
132 q->ack_block_to_server = ack_block_to_server;
133 m_queue.push_back(q);
136 // Returned pointer must be deleted
137 // Returns NULL if queue is empty
138 QueuedMeshUpdate * MeshUpdateQueue::pop()
140 JMutexAutoLock lock(m_mutex);
142 bool must_be_urgent = !m_urgents.empty();
143 for(std::vector<QueuedMeshUpdate*>::iterator
145 i != m_queue.end(); i++)
147 QueuedMeshUpdate *q = *i;
148 if(must_be_urgent && m_urgents.count(q->p) == 0)
151 m_urgents.erase(q->p);
161 void * MeshUpdateThread::Thread()
165 log_register_thread("MeshUpdateThread");
167 DSTACK(__FUNCTION_NAME);
169 BEGIN_DEBUG_EXCEPTION_HANDLER
171 while(!StopRequested())
173 QueuedMeshUpdate *q = m_queue_in.pop();
180 ScopeProfiler sp(g_profiler, "Client: Mesh making");
182 MapBlockMesh *mesh_new = new MapBlockMesh(q->data, m_camera_offset);
183 if(mesh_new->getMesh()->getMeshBufferCount() == 0)
192 r.ack_block_to_server = q->ack_block_to_server;
194 m_queue_out.push_back(r);
199 END_DEBUG_EXCEPTION_HANDLER(errorstream)
209 IrrlichtDevice *device,
210 const char *playername,
211 std::string password,
212 MapDrawControl &control,
213 IWritableTextureSource *tsrc,
214 IWritableShaderSource *shsrc,
215 IWritableItemDefManager *itemdef,
216 IWritableNodeDefManager *nodedef,
217 ISoundManager *sound,
218 MtEventManager *event,
227 m_mesh_update_thread(this),
229 new ClientMap(this, this, control,
230 device->getSceneManager()->getRootSceneNode(),
231 device->getSceneManager(), 666),
232 device->getSceneManager(),
235 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, ipv6, this),
237 m_server_ser_ver(SER_FMT_VER_INVALID),
239 m_inventory_updated(false),
240 m_inventory_from_server(NULL),
241 m_inventory_from_server_age(0.0),
246 m_password(password),
247 m_access_denied(false),
248 m_itemdef_received(false),
249 m_nodedef_received(false),
250 m_media_downloader(new ClientMediaDownloader()),
251 m_time_of_day_set(false),
252 m_last_time_of_day_f(-1),
253 m_time_of_day_update_timer(0),
254 m_recommended_send_interval(0.1),
255 m_removed_sounds_check_timer(0)
257 m_packetcounter_timer = 0.0;
258 //m_delete_unused_sectors_timer = 0.0;
259 m_connection_reinit_timer = 0.0;
260 m_avg_rtt_timer = 0.0;
261 m_playerpos_send_timer = 0.0;
262 m_ignore_damage_timer = 0.0;
268 Player *player = new LocalPlayer(this);
270 player->updateName(playername);
272 m_env.addPlayer(player);
278 //request all client managed threads to stop
279 m_mesh_update_thread.Stop();
282 bool Client::isShutdown()
285 if (!m_mesh_update_thread.IsRunning()) return true;
294 m_mesh_update_thread.Stop();
295 m_mesh_update_thread.Wait();
296 while(!m_mesh_update_thread.m_queue_out.empty()) {
297 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_frontNoEx();
302 delete m_inventory_from_server;
304 // Delete detached inventories
305 for(std::map<std::string, Inventory*>::iterator
306 i = m_detached_inventories.begin();
307 i != m_detached_inventories.end(); i++){
311 // cleanup 3d model meshes on client shutdown
312 while (m_device->getSceneManager()->getMeshCache()->getMeshCount() != 0) {
313 scene::IAnimatedMesh * mesh =
314 m_device->getSceneManager()->getMeshCache()->getMeshByIndex(0);
317 m_device->getSceneManager()->getMeshCache()->removeMesh(mesh);
321 void Client::connect(Address address)
323 DSTACK(__FUNCTION_NAME);
324 m_con.SetTimeoutMs(0);
325 m_con.Connect(address);
328 bool Client::connectedAndInitialized()
330 if(m_con.Connected() == false)
333 if(m_server_ser_ver == SER_FMT_VER_INVALID)
339 void Client::step(float dtime)
341 DSTACK(__FUNCTION_NAME);
347 if(m_ignore_damage_timer > dtime)
348 m_ignore_damage_timer -= dtime;
350 m_ignore_damage_timer = 0.0;
352 m_animation_time += dtime;
353 if(m_animation_time > 60.0)
354 m_animation_time -= 60.0;
356 m_time_of_day_update_timer += dtime;
364 float &counter = m_packetcounter_timer;
370 infostream<<"Client packetcounter (20s):"<<std::endl;
371 m_packetcounter.print(infostream);
372 m_packetcounter.clear();
376 // Get connection status
377 bool connected = connectedAndInitialized();
382 Delete unused sectors
384 NOTE: This jams the game for a while because deleting sectors
388 float &counter = m_delete_unused_sectors_timer;
396 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
398 core::list<v3s16> deleted_blocks;
400 float delete_unused_sectors_timeout =
401 g_settings->getFloat("client_delete_unused_sectors_timeout");
403 // Delete sector blocks
404 /*u32 num = m_env.getMap().unloadUnusedData
405 (delete_unused_sectors_timeout,
406 true, &deleted_blocks);*/
408 // Delete whole sectors
409 m_env.getMap().unloadUnusedData
410 (delete_unused_sectors_timeout,
413 if(deleted_blocks.size() > 0)
415 /*infostream<<"Client: Deleted blocks of "<<num
416 <<" unused sectors"<<std::endl;*/
417 /*infostream<<"Client: Deleted "<<num
418 <<" unused sectors"<<std::endl;*/
424 // Env is locked so con can be locked.
425 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
427 core::list<v3s16>::Iterator i = deleted_blocks.begin();
428 core::list<v3s16> sendlist;
431 if(sendlist.size() == 255 || i == deleted_blocks.end())
433 if(sendlist.size() == 0)
442 u32 replysize = 2+1+6*sendlist.size();
443 SharedBuffer<u8> reply(replysize);
444 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
445 reply[2] = sendlist.size();
447 for(core::list<v3s16>::Iterator
448 j = sendlist.begin();
449 j != sendlist.end(); j++)
451 writeV3S16(&reply[2+1+6*k], *j);
454 m_con.Send(PEER_ID_SERVER, 1, reply, true);
456 if(i == deleted_blocks.end())
462 sendlist.push_back(*i);
470 if(connected == false)
472 float &counter = m_connection_reinit_timer;
478 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
480 Player *myplayer = m_env.getLocalPlayer();
481 assert(myplayer != NULL);
483 // Send TOSERVER_INIT
484 // [0] u16 TOSERVER_INIT
485 // [2] u8 SER_FMT_VER_HIGHEST_READ
486 // [3] u8[20] player_name
487 // [23] u8[28] password (new in some version)
488 // [51] u16 minimum supported network protocol version (added sometime)
489 // [53] u16 maximum supported network protocol version (added later than the previous one)
490 SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2+2);
491 writeU16(&data[0], TOSERVER_INIT);
492 writeU8(&data[2], SER_FMT_VER_HIGHEST_READ);
494 memset((char*)&data[3], 0, PLAYERNAME_SIZE);
495 snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
497 /*infostream<<"Client: sending initial password hash: \""<<m_password<<"\""
500 memset((char*)&data[23], 0, PASSWORD_SIZE);
501 snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
503 writeU16(&data[51], CLIENT_PROTOCOL_VERSION_MIN);
504 writeU16(&data[53], CLIENT_PROTOCOL_VERSION_MAX);
506 // Send as unreliable
507 Send(1, data, false);
510 // Not connected, return
515 Do stuff if connected
519 Run Map's timers and unload unused data
521 const float map_timer_and_unload_dtime = 5.25;
522 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
524 ScopeProfiler sp(g_profiler, "Client: map timer and unload");
525 std::list<v3s16> deleted_blocks;
526 m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
527 g_settings->getFloat("client_unload_unused_data_timeout"),
530 /*if(deleted_blocks.size() > 0)
531 infostream<<"Client: Unloaded "<<deleted_blocks.size()
532 <<" unused blocks"<<std::endl;*/
536 NOTE: This loop is intentionally iterated the way it is.
539 std::list<v3s16>::iterator i = deleted_blocks.begin();
540 std::list<v3s16> sendlist;
543 if(sendlist.size() == 255 || i == deleted_blocks.end())
545 if(sendlist.size() == 0)
554 u32 replysize = 2+1+6*sendlist.size();
555 SharedBuffer<u8> reply(replysize);
556 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
557 reply[2] = sendlist.size();
559 for(std::list<v3s16>::iterator
560 j = sendlist.begin();
561 j != sendlist.end(); ++j)
563 writeV3S16(&reply[2+1+6*k], *j);
566 m_con.Send(PEER_ID_SERVER, 2, reply, true);
568 if(i == deleted_blocks.end())
574 sendlist.push_back(*i);
583 // Control local player (0ms)
584 LocalPlayer *player = m_env.getLocalPlayer();
585 assert(player != NULL);
586 player->applyControl(dtime);
596 ClientEnvEvent event = m_env.getClientEvent();
597 if(event.type == CEE_NONE)
601 else if(event.type == CEE_PLAYER_DAMAGE)
603 if(m_ignore_damage_timer <= 0)
605 u8 damage = event.player_damage.amount;
607 if(event.player_damage.send_to_server)
610 // Add to ClientEvent queue
612 event.type = CE_PLAYER_DAMAGE;
613 event.player_damage.amount = damage;
614 m_client_event_queue.push_back(event);
617 else if(event.type == CEE_PLAYER_BREATH)
619 u16 breath = event.player_breath.amount;
629 float &counter = m_avg_rtt_timer;
634 // connectedAndInitialized() is true, peer exists.
635 float avg_rtt = m_con.GetPeerAvgRTT(PEER_ID_SERVER);
636 infostream<<"Client: avg_rtt="<<avg_rtt<<std::endl;
641 Send player position to server
644 float &counter = m_playerpos_send_timer;
646 if(counter >= m_recommended_send_interval)
654 Replace updated meshes
657 int num_processed_meshes = 0;
658 while(!m_mesh_update_thread.m_queue_out.empty())
660 num_processed_meshes++;
661 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_frontNoEx();
662 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
665 // Delete the old mesh
666 if(block->mesh != NULL)
668 // TODO: Remove hardware buffers of meshbuffers of block->mesh
673 // Replace with the new mesh
674 block->mesh = r.mesh;
678 if(r.ack_block_to_server)
690 u32 replysize = 2+1+6;
691 SharedBuffer<u8> reply(replysize);
692 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
694 writeV3S16(&reply[3], r.p);
696 m_con.Send(PEER_ID_SERVER, 2, reply, true);
699 if(num_processed_meshes > 0)
700 g_profiler->graphAdd("num_processed_meshes", num_processed_meshes);
706 if (m_media_downloader && m_media_downloader->isStarted()) {
707 m_media_downloader->step(this);
708 if (m_media_downloader->isDone()) {
710 delete m_media_downloader;
711 m_media_downloader = NULL;
716 If the server didn't update the inventory in a while, revert
717 the local inventory (so the player notices the lag problem
718 and knows something is wrong).
720 if(m_inventory_from_server)
722 float interval = 10.0;
723 float count_before = floor(m_inventory_from_server_age / interval);
725 m_inventory_from_server_age += dtime;
727 float count_after = floor(m_inventory_from_server_age / interval);
729 if(count_after != count_before)
731 // Do this every <interval> seconds after TOCLIENT_INVENTORY
732 // Reset the locally changed inventory to the authoritative inventory
733 Player *player = m_env.getLocalPlayer();
734 player->inventory = *m_inventory_from_server;
735 m_inventory_updated = true;
740 Update positions of sounds attached to objects
743 for(std::map<int, u16>::iterator
744 i = m_sounds_to_objects.begin();
745 i != m_sounds_to_objects.end(); i++)
747 int client_id = i->first;
748 u16 object_id = i->second;
749 ClientActiveObject *cao = m_env.getActiveObject(object_id);
752 v3f pos = cao->getPosition();
753 m_sound->updateSoundPosition(client_id, pos);
758 Handle removed remotely initiated sounds
760 m_removed_sounds_check_timer += dtime;
761 if(m_removed_sounds_check_timer >= 2.32)
763 m_removed_sounds_check_timer = 0;
764 // Find removed sounds and clear references to them
765 std::set<s32> removed_server_ids;
766 for(std::map<s32, int>::iterator
767 i = m_sounds_server_to_client.begin();
768 i != m_sounds_server_to_client.end();)
770 s32 server_id = i->first;
771 int client_id = i->second;
773 if(!m_sound->soundExists(client_id)){
774 m_sounds_server_to_client.erase(server_id);
775 m_sounds_client_to_server.erase(client_id);
776 m_sounds_to_objects.erase(client_id);
777 removed_server_ids.insert(server_id);
781 if(removed_server_ids.size() != 0)
783 std::ostringstream os(std::ios_base::binary);
784 writeU16(os, TOSERVER_REMOVED_SOUNDS);
785 size_t server_ids = removed_server_ids.size();
786 assert(server_ids <= 0xFFFF);
787 writeU16(os, (u16) (server_ids & 0xFFFF));
788 for(std::set<s32>::iterator i = removed_server_ids.begin();
789 i != removed_server_ids.end(); i++)
791 std::string s = os.str();
792 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
799 bool Client::loadMedia(const std::string &data, const std::string &filename)
801 // Silly irrlicht's const-incorrectness
802 Buffer<char> data_rw(data.c_str(), data.size());
806 const char *image_ext[] = {
807 ".png", ".jpg", ".bmp", ".tga",
808 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
811 name = removeStringEnd(filename, image_ext);
814 verbosestream<<"Client: Attempting to load image "
815 <<"file \""<<filename<<"\""<<std::endl;
817 io::IFileSystem *irrfs = m_device->getFileSystem();
818 video::IVideoDriver *vdrv = m_device->getVideoDriver();
820 // Create an irrlicht memory file
821 io::IReadFile *rfile = irrfs->createMemoryReadFile(
822 *data_rw, data_rw.getSize(), "_tempreadfile");
825 video::IImage *img = vdrv->createImageFromFile(rfile);
827 errorstream<<"Client: Cannot create image from data of "
828 <<"file \""<<filename<<"\""<<std::endl;
833 m_tsrc->insertSourceImage(filename, img);
840 const char *sound_ext[] = {
841 ".0.ogg", ".1.ogg", ".2.ogg", ".3.ogg", ".4.ogg",
842 ".5.ogg", ".6.ogg", ".7.ogg", ".8.ogg", ".9.ogg",
845 name = removeStringEnd(filename, sound_ext);
848 verbosestream<<"Client: Attempting to load sound "
849 <<"file \""<<filename<<"\""<<std::endl;
850 m_sound->loadSoundData(name, data);
854 const char *model_ext[] = {
855 ".x", ".b3d", ".md2", ".obj",
858 name = removeStringEnd(filename, model_ext);
861 verbosestream<<"Client: Storing model into memory: "
862 <<"\""<<filename<<"\""<<std::endl;
863 if(m_mesh_data.count(filename))
864 errorstream<<"Multiple models with name \""<<filename.c_str()
865 <<"\" found; replacing previous model"<<std::endl;
866 m_mesh_data[filename] = data;
870 errorstream<<"Client: Don't know how to load file \""
871 <<filename<<"\""<<std::endl;
875 // Virtual methods from con::PeerHandler
876 void Client::peerAdded(con::Peer *peer)
878 infostream<<"Client::peerAdded(): peer->id="
879 <<peer->id<<std::endl;
881 void Client::deletingPeer(con::Peer *peer, bool timeout)
883 infostream<<"Client::deletingPeer(): "
884 "Server Peer is getting deleted "
885 <<"(timeout="<<timeout<<")"<<std::endl;
890 u16 number of files requested
896 void Client::request_media(const std::list<std::string> &file_requests)
898 std::ostringstream os(std::ios_base::binary);
899 writeU16(os, TOSERVER_REQUEST_MEDIA);
900 size_t file_requests_size = file_requests.size();
901 assert(file_requests_size <= 0xFFFF);
902 writeU16(os, (u16) (file_requests_size & 0xFFFF));
904 for(std::list<std::string>::const_iterator i = file_requests.begin();
905 i != file_requests.end(); ++i) {
906 os<<serializeString(*i);
910 std::string s = os.str();
911 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
914 infostream<<"Client: Sending media request list to server ("
915 <<file_requests.size()<<" files)"<<std::endl;
918 void Client::received_media()
920 // notify server we received everything
921 std::ostringstream os(std::ios_base::binary);
922 writeU16(os, TOSERVER_RECEIVED_MEDIA);
923 std::string s = os.str();
924 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
927 infostream<<"Client: Notifying server that we received all media"
931 void Client::ReceiveAll()
933 DSTACK(__FUNCTION_NAME);
934 u32 start_ms = porting::getTimeMs();
937 // Limit time even if there would be huge amounts of data to
939 if(porting::getTimeMs() > start_ms + 100)
944 g_profiler->graphAdd("client_received_packets", 1);
946 catch(con::NoIncomingDataException &e)
950 catch(con::InvalidIncomingDataException &e)
952 infostream<<"Client::ReceiveAll(): "
953 "InvalidIncomingDataException: what()="
954 <<e.what()<<std::endl;
959 void Client::Receive()
961 DSTACK(__FUNCTION_NAME);
962 SharedBuffer<u8> data;
964 u32 datasize = m_con.Receive(sender_peer_id, data);
965 ProcessData(*data, datasize, sender_peer_id);
969 sender_peer_id given to this shall be quaranteed to be a valid peer
971 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
973 DSTACK(__FUNCTION_NAME);
975 // Ignore packets that don't even fit a command
978 m_packetcounter.add(60000);
982 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
984 //infostream<<"Client: received command="<<command<<std::endl;
985 m_packetcounter.add((u16)command);
988 If this check is removed, be sure to change the queue
989 system to know the ids
991 if(sender_peer_id != PEER_ID_SERVER)
993 infostream<<"Client::ProcessData(): Discarding data not "
994 "coming from server: peer_id="<<sender_peer_id
999 u8 ser_version = m_server_ser_ver;
1001 if(command == TOCLIENT_INIT)
1006 u8 deployed = data[2];
1008 infostream<<"Client: TOCLIENT_INIT received with "
1009 "deployed="<<((int)deployed&0xff)<<std::endl;
1011 if(!ser_ver_supported(deployed))
1013 infostream<<"Client: TOCLIENT_INIT: Server sent "
1014 <<"unsupported ser_fmt_ver"<<std::endl;
1018 m_server_ser_ver = deployed;
1020 // Get player position
1021 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
1022 if(datasize >= 2+1+6)
1023 playerpos_s16 = readV3S16(&data[2+1]);
1024 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
1027 // Set player position
1028 Player *player = m_env.getLocalPlayer();
1029 assert(player != NULL);
1030 player->setPosition(playerpos_f);
1032 if(datasize >= 2+1+6+8)
1035 m_map_seed = readU64(&data[2+1+6]);
1036 infostream<<"Client: received map seed: "<<m_map_seed<<std::endl;
1039 if(datasize >= 2+1+6+8+4)
1042 m_recommended_send_interval = readF1000(&data[2+1+6+8]);
1043 infostream<<"Client: received recommended send interval "
1044 <<m_recommended_send_interval<<std::endl;
1049 SharedBuffer<u8> reply(replysize);
1050 writeU16(&reply[0], TOSERVER_INIT2);
1052 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1057 if(command == TOCLIENT_ACCESS_DENIED)
1059 // The server didn't like our password. Note, this needs
1060 // to be processed even if the serialisation format has
1061 // not been agreed yet, the same as TOCLIENT_INIT.
1062 m_access_denied = true;
1063 m_access_denied_reason = L"Unknown";
1066 std::string datastring((char*)&data[2], datasize-2);
1067 std::istringstream is(datastring, std::ios_base::binary);
1068 m_access_denied_reason = deSerializeWideString(is);
1073 if(ser_version == SER_FMT_VER_INVALID)
1075 infostream<<"Client: Server serialization"
1076 " format invalid or not initialized."
1077 " Skipping incoming command="<<command<<std::endl;
1082 Handle runtime commands
1084 // there's no sane reason why we shouldn't have a player and
1085 // almost everyone needs a player reference
1086 Player *player = m_env.getLocalPlayer();
1087 assert(player != NULL);
1089 if(command == TOCLIENT_REMOVENODE)
1094 p.X = readS16(&data[2]);
1095 p.Y = readS16(&data[4]);
1096 p.Z = readS16(&data[6]);
1099 else if(command == TOCLIENT_ADDNODE)
1101 if(datasize < 8 + MapNode::serializedLength(ser_version))
1105 p.X = readS16(&data[2]);
1106 p.Y = readS16(&data[4]);
1107 p.Z = readS16(&data[6]);
1110 n.deSerialize(&data[8], ser_version);
1112 bool remove_metadata = true;
1113 u32 index = 8 + MapNode::serializedLength(ser_version);
1114 if ((datasize >= index+1) && data[index]){
1115 remove_metadata = false;
1118 addNode(p, n, remove_metadata);
1120 else if(command == TOCLIENT_BLOCKDATA)
1122 // Ignore too small packet
1127 p.X = readS16(&data[2]);
1128 p.Y = readS16(&data[4]);
1129 p.Z = readS16(&data[6]);
1131 std::string datastring((char*)&data[8], datasize-8);
1132 std::istringstream istr(datastring, std::ios_base::binary);
1137 v2s16 p2d(p.X, p.Z);
1138 sector = m_env.getMap().emergeSector(p2d);
1140 assert(sector->getPos() == p2d);
1142 block = sector->getBlockNoCreateNoEx(p.Y);
1146 Update an existing block
1148 block->deSerialize(istr, ser_version, false);
1149 block->deSerializeNetworkSpecific(istr);
1156 block = new MapBlock(&m_env.getMap(), p, this);
1157 block->deSerialize(istr, ser_version, false);
1158 block->deSerializeNetworkSpecific(istr);
1159 sector->insertBlock(block);
1163 Add it to mesh update queue and set it to be acknowledged after update.
1165 addUpdateMeshTaskWithEdge(p, true);
1167 else if(command == TOCLIENT_INVENTORY)
1172 std::string datastring((char*)&data[2], datasize-2);
1173 std::istringstream is(datastring, std::ios_base::binary);
1175 player->inventory.deSerialize(is);
1177 m_inventory_updated = true;
1179 delete m_inventory_from_server;
1180 m_inventory_from_server = new Inventory(player->inventory);
1181 m_inventory_from_server_age = 0.0;
1184 else if(command == TOCLIENT_TIME_OF_DAY)
1189 u16 time_of_day = readU16(&data[2]);
1190 time_of_day = time_of_day % 24000;
1191 float time_speed = 0;
1193 if(datasize >= 2 + 2 + 4)
1195 time_speed = readF1000(&data[4]);
1198 // Old message; try to approximate speed of time by ourselves
1199 float time_of_day_f = (float)time_of_day / 24000.0;
1200 float tod_diff_f = 0;
1202 if(time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
1203 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0;
1205 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
1207 m_last_time_of_day_f = time_of_day_f;
1208 float time_diff = m_time_of_day_update_timer;
1209 m_time_of_day_update_timer = 0;
1211 if(m_time_of_day_set){
1212 time_speed = (3600.0*24.0) * tod_diff_f / time_diff;
1213 infostream<<"Client: Measured time_of_day speed (old format): "
1214 <<time_speed<<" tod_diff_f="<<tod_diff_f
1215 <<" time_diff="<<time_diff<<std::endl;
1219 // Update environment
1220 m_env.setTimeOfDay(time_of_day);
1221 m_env.setTimeOfDaySpeed(time_speed);
1222 m_time_of_day_set = true;
1224 u32 dr = m_env.getDayNightRatio();
1225 verbosestream<<"Client: time_of_day="<<time_of_day
1226 <<" time_speed="<<time_speed
1227 <<" dr="<<dr<<std::endl;
1229 else if(command == TOCLIENT_CHAT_MESSAGE)
1237 std::string datastring((char*)&data[2], datasize-2);
1238 std::istringstream is(datastring, std::ios_base::binary);
1241 is.read((char*) buf, 2);
1242 u16 len = readU16(buf);
1244 std::wstring message;
1245 for(unsigned int i=0; i<len; i++)
1247 is.read((char*)buf, 2);
1248 message += (wchar_t)readU16(buf);
1251 m_chat_queue.push_back(message);
1253 else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1257 u16 count of removed objects
1258 for all removed objects {
1261 u16 count of added objects
1262 for all added objects {
1265 u32 initialization data length
1266 string initialization data
1271 // Get all data except the command number
1272 std::string datastring((char*)&data[2], datasize-2);
1273 // Throw them in an istringstream
1274 std::istringstream is(datastring, std::ios_base::binary);
1276 // Read removed objects
1278 u16 removed_count = readU16((u8*)buf);
1279 for(unsigned int i=0; i<removed_count; i++)
1282 u16 id = readU16((u8*)buf);
1283 m_env.removeActiveObject(id);
1286 // Read added objects
1288 u16 added_count = readU16((u8*)buf);
1289 for(unsigned int i=0; i<added_count; i++)
1292 u16 id = readU16((u8*)buf);
1294 u8 type = readU8((u8*)buf);
1295 std::string data = deSerializeLongString(is);
1297 m_env.addActiveObject(id, type, data);
1300 else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1312 // Get all data except the command number
1313 std::string datastring((char*)&data[2], datasize-2);
1314 // Throw them in an istringstream
1315 std::istringstream is(datastring, std::ios_base::binary);
1317 while(is.eof() == false)
1320 u16 id = readU16((u8*)buf);
1324 size_t message_size = readU16((u8*)buf);
1325 std::string message;
1326 message.reserve(message_size);
1327 for(unsigned int i=0; i<message_size; i++)
1330 message.append(buf, 1);
1332 // Pass on to the environment
1333 m_env.processActiveObjectMessage(id, message);
1336 else if(command == TOCLIENT_MOVEMENT)
1338 std::string datastring((char*)&data[2], datasize-2);
1339 std::istringstream is(datastring, std::ios_base::binary);
1341 player->movement_acceleration_default = readF1000(is) * BS;
1342 player->movement_acceleration_air = readF1000(is) * BS;
1343 player->movement_acceleration_fast = readF1000(is) * BS;
1344 player->movement_speed_walk = readF1000(is) * BS;
1345 player->movement_speed_crouch = readF1000(is) * BS;
1346 player->movement_speed_fast = readF1000(is) * BS;
1347 player->movement_speed_climb = readF1000(is) * BS;
1348 player->movement_speed_jump = readF1000(is) * BS;
1349 player->movement_liquid_fluidity = readF1000(is) * BS;
1350 player->movement_liquid_fluidity_smooth = readF1000(is) * BS;
1351 player->movement_liquid_sink = readF1000(is) * BS;
1352 player->movement_gravity = readF1000(is) * BS;
1354 else if(command == TOCLIENT_HP)
1356 std::string datastring((char*)&data[2], datasize-2);
1357 std::istringstream is(datastring, std::ios_base::binary);
1359 u8 oldhp = player->hp;
1365 // Add to ClientEvent queue
1367 event.type = CE_PLAYER_DAMAGE;
1368 event.player_damage.amount = oldhp - hp;
1369 m_client_event_queue.push_back(event);
1372 else if(command == TOCLIENT_BREATH)
1374 std::string datastring((char*)&data[2], datasize-2);
1375 std::istringstream is(datastring, std::ios_base::binary);
1377 player->setBreath(readU16(is));
1379 else if(command == TOCLIENT_MOVE_PLAYER)
1381 std::string datastring((char*)&data[2], datasize-2);
1382 std::istringstream is(datastring, std::ios_base::binary);
1384 v3f pos = readV3F1000(is);
1385 f32 pitch = readF1000(is);
1386 f32 yaw = readF1000(is);
1387 player->setPosition(pos);
1389 infostream<<"Client got TOCLIENT_MOVE_PLAYER"
1390 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1396 Add to ClientEvent queue.
1397 This has to be sent to the main program because otherwise
1398 it would just force the pitch and yaw values to whatever
1399 the camera points to.
1402 event.type = CE_PLAYER_FORCE_MOVE;
1403 event.player_force_move.pitch = pitch;
1404 event.player_force_move.yaw = yaw;
1405 m_client_event_queue.push_back(event);
1407 // Ignore damage for a few seconds, so that the player doesn't
1408 // get damage from falling on ground
1409 m_ignore_damage_timer = 3.0;
1411 else if(command == TOCLIENT_PLAYERITEM)
1413 infostream<<"Client: WARNING: Ignoring TOCLIENT_PLAYERITEM"<<std::endl;
1415 else if(command == TOCLIENT_DEATHSCREEN)
1417 std::string datastring((char*)&data[2], datasize-2);
1418 std::istringstream is(datastring, std::ios_base::binary);
1420 bool set_camera_point_target = readU8(is);
1421 v3f camera_point_target = readV3F1000(is);
1424 event.type = CE_DEATHSCREEN;
1425 event.deathscreen.set_camera_point_target = set_camera_point_target;
1426 event.deathscreen.camera_point_target_x = camera_point_target.X;
1427 event.deathscreen.camera_point_target_y = camera_point_target.Y;
1428 event.deathscreen.camera_point_target_z = camera_point_target.Z;
1429 m_client_event_queue.push_back(event);
1431 else if(command == TOCLIENT_ANNOUNCE_MEDIA)
1433 std::string datastring((char*)&data[2], datasize-2);
1434 std::istringstream is(datastring, std::ios_base::binary);
1436 int num_files = readU16(is);
1438 infostream<<"Client: Received media announcement: packet size: "
1439 <<datasize<<std::endl;
1441 if (m_media_downloader == NULL ||
1442 m_media_downloader->isStarted()) {
1443 const char *problem = m_media_downloader ?
1444 "we already saw another announcement" :
1445 "all media has been received already";
1446 errorstream<<"Client: Received media announcement but "
1448 <<" files="<<num_files
1449 <<" size="<<datasize<<std::endl;
1453 // Mesh update thread must be stopped while
1454 // updating content definitions
1455 assert(!m_mesh_update_thread.IsRunning());
1457 for(int i=0; i<num_files; i++)
1459 std::string name = deSerializeString(is);
1460 std::string sha1_base64 = deSerializeString(is);
1461 std::string sha1_raw = base64_decode(sha1_base64);
1462 m_media_downloader->addFile(name, sha1_raw);
1465 std::vector<std::string> remote_media;
1467 Strfnd sf(deSerializeString(is));
1468 while(!sf.atend()) {
1469 std::string baseurl = trim(sf.next(","));
1471 m_media_downloader->addRemoteServer(baseurl);
1474 catch(SerializationError& e) {
1475 // not supported by server or turned off
1478 m_media_downloader->step(this);
1480 else if(command == TOCLIENT_MEDIA)
1482 std::string datastring((char*)&data[2], datasize-2);
1483 std::istringstream is(datastring, std::ios_base::binary);
1487 u16 total number of file bunches
1488 u16 index of this bunch
1489 u32 number of files in this bunch
1497 int num_bunches = readU16(is);
1498 int bunch_i = readU16(is);
1499 u32 num_files = readU32(is);
1500 infostream<<"Client: Received files: bunch "<<bunch_i<<"/"
1501 <<num_bunches<<" files="<<num_files
1502 <<" size="<<datasize<<std::endl;
1507 if (m_media_downloader == NULL ||
1508 !m_media_downloader->isStarted()) {
1509 const char *problem = m_media_downloader ?
1510 "media has not been requested" :
1511 "all media has been received already";
1512 errorstream<<"Client: Received media but "
1514 <<" bunch "<<bunch_i<<"/"<<num_bunches
1515 <<" files="<<num_files
1516 <<" size="<<datasize<<std::endl;
1520 // Mesh update thread must be stopped while
1521 // updating content definitions
1522 assert(!m_mesh_update_thread.IsRunning());
1524 for(unsigned int i=0; i<num_files; i++){
1525 std::string name = deSerializeString(is);
1526 std::string data = deSerializeLongString(is);
1527 m_media_downloader->conventionalTransferDone(
1531 else if(command == TOCLIENT_TOOLDEF)
1533 infostream<<"Client: WARNING: Ignoring TOCLIENT_TOOLDEF"<<std::endl;
1535 else if(command == TOCLIENT_NODEDEF)
1537 infostream<<"Client: Received node definitions: packet size: "
1538 <<datasize<<std::endl;
1540 // Mesh update thread must be stopped while
1541 // updating content definitions
1542 assert(!m_mesh_update_thread.IsRunning());
1544 // Decompress node definitions
1545 std::string datastring((char*)&data[2], datasize-2);
1546 std::istringstream is(datastring, std::ios_base::binary);
1547 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1548 std::ostringstream tmp_os;
1549 decompressZlib(tmp_is, tmp_os);
1551 // Deserialize node definitions
1552 std::istringstream tmp_is2(tmp_os.str());
1553 m_nodedef->deSerialize(tmp_is2);
1554 m_nodedef_received = true;
1556 else if(command == TOCLIENT_CRAFTITEMDEF)
1558 infostream<<"Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF"<<std::endl;
1560 else if(command == TOCLIENT_ITEMDEF)
1562 infostream<<"Client: Received item definitions: packet size: "
1563 <<datasize<<std::endl;
1565 // Mesh update thread must be stopped while
1566 // updating content definitions
1567 assert(!m_mesh_update_thread.IsRunning());
1569 // Decompress item definitions
1570 std::string datastring((char*)&data[2], datasize-2);
1571 std::istringstream is(datastring, std::ios_base::binary);
1572 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1573 std::ostringstream tmp_os;
1574 decompressZlib(tmp_is, tmp_os);
1576 // Deserialize node definitions
1577 std::istringstream tmp_is2(tmp_os.str());
1578 m_itemdef->deSerialize(tmp_is2);
1579 m_itemdef_received = true;
1581 else if(command == TOCLIENT_PLAY_SOUND)
1583 std::string datastring((char*)&data[2], datasize-2);
1584 std::istringstream is(datastring, std::ios_base::binary);
1586 s32 server_id = readS32(is);
1587 std::string name = deSerializeString(is);
1588 float gain = readF1000(is);
1589 int type = readU8(is); // 0=local, 1=positional, 2=object
1590 v3f pos = readV3F1000(is);
1591 u16 object_id = readU16(is);
1592 bool loop = readU8(is);
1597 client_id = m_sound->playSound(name, loop, gain);
1599 case 1: // positional
1600 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1603 ClientActiveObject *cao = m_env.getActiveObject(object_id);
1605 pos = cao->getPosition();
1606 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1607 // TODO: Set up sound to move with object
1612 if(client_id != -1){
1613 m_sounds_server_to_client[server_id] = client_id;
1614 m_sounds_client_to_server[client_id] = server_id;
1616 m_sounds_to_objects[client_id] = object_id;
1619 else if(command == TOCLIENT_STOP_SOUND)
1621 std::string datastring((char*)&data[2], datasize-2);
1622 std::istringstream is(datastring, std::ios_base::binary);
1624 s32 server_id = readS32(is);
1625 std::map<s32, int>::iterator i =
1626 m_sounds_server_to_client.find(server_id);
1627 if(i != m_sounds_server_to_client.end()){
1628 int client_id = i->second;
1629 m_sound->stopSound(client_id);
1632 else if(command == TOCLIENT_PRIVILEGES)
1634 std::string datastring((char*)&data[2], datasize-2);
1635 std::istringstream is(datastring, std::ios_base::binary);
1637 m_privileges.clear();
1638 infostream<<"Client: Privileges updated: ";
1639 u16 num_privileges = readU16(is);
1640 for(unsigned int i=0; i<num_privileges; i++){
1641 std::string priv = deSerializeString(is);
1642 m_privileges.insert(priv);
1643 infostream<<priv<<" ";
1645 infostream<<std::endl;
1647 else if(command == TOCLIENT_INVENTORY_FORMSPEC)
1649 std::string datastring((char*)&data[2], datasize-2);
1650 std::istringstream is(datastring, std::ios_base::binary);
1652 // Store formspec in LocalPlayer
1653 player->inventory_formspec = deSerializeLongString(is);
1655 else if(command == TOCLIENT_DETACHED_INVENTORY)
1657 std::string datastring((char*)&data[2], datasize-2);
1658 std::istringstream is(datastring, std::ios_base::binary);
1660 std::string name = deSerializeString(is);
1662 infostream<<"Client: Detached inventory update: \""<<name<<"\""<<std::endl;
1664 Inventory *inv = NULL;
1665 if(m_detached_inventories.count(name) > 0)
1666 inv = m_detached_inventories[name];
1668 inv = new Inventory(m_itemdef);
1669 m_detached_inventories[name] = inv;
1671 inv->deSerialize(is);
1673 else if(command == TOCLIENT_SHOW_FORMSPEC)
1675 std::string datastring((char*)&data[2], datasize-2);
1676 std::istringstream is(datastring, std::ios_base::binary);
1678 std::string formspec = deSerializeLongString(is);
1679 std::string formname = deSerializeString(is);
1682 event.type = CE_SHOW_FORMSPEC;
1683 // pointer is required as event is a struct only!
1684 // adding a std:string to a struct isn't possible
1685 event.show_formspec.formspec = new std::string(formspec);
1686 event.show_formspec.formname = new std::string(formname);
1687 m_client_event_queue.push_back(event);
1689 else if(command == TOCLIENT_SPAWN_PARTICLE)
1691 std::string datastring((char*)&data[2], datasize-2);
1692 std::istringstream is(datastring, std::ios_base::binary);
1694 v3f pos = readV3F1000(is);
1695 v3f vel = readV3F1000(is);
1696 v3f acc = readV3F1000(is);
1697 float expirationtime = readF1000(is);
1698 float size = readF1000(is);
1699 bool collisiondetection = readU8(is);
1700 std::string texture = deSerializeLongString(is);
1701 bool vertical = false;
1703 vertical = readU8(is);
1707 event.type = CE_SPAWN_PARTICLE;
1708 event.spawn_particle.pos = new v3f (pos);
1709 event.spawn_particle.vel = new v3f (vel);
1710 event.spawn_particle.acc = new v3f (acc);
1711 event.spawn_particle.expirationtime = expirationtime;
1712 event.spawn_particle.size = size;
1713 event.spawn_particle.collisiondetection = collisiondetection;
1714 event.spawn_particle.vertical = vertical;
1715 event.spawn_particle.texture = new std::string(texture);
1717 m_client_event_queue.push_back(event);
1719 else if(command == TOCLIENT_ADD_PARTICLESPAWNER)
1721 std::string datastring((char*)&data[2], datasize-2);
1722 std::istringstream is(datastring, std::ios_base::binary);
1724 u16 amount = readU16(is);
1725 float spawntime = readF1000(is);
1726 v3f minpos = readV3F1000(is);
1727 v3f maxpos = readV3F1000(is);
1728 v3f minvel = readV3F1000(is);
1729 v3f maxvel = readV3F1000(is);
1730 v3f minacc = readV3F1000(is);
1731 v3f maxacc = readV3F1000(is);
1732 float minexptime = readF1000(is);
1733 float maxexptime = readF1000(is);
1734 float minsize = readF1000(is);
1735 float maxsize = readF1000(is);
1736 bool collisiondetection = readU8(is);
1737 std::string texture = deSerializeLongString(is);
1738 u32 id = readU32(is);
1739 bool vertical = false;
1741 vertical = readU8(is);
1745 event.type = CE_ADD_PARTICLESPAWNER;
1746 event.add_particlespawner.amount = amount;
1747 event.add_particlespawner.spawntime = spawntime;
1748 event.add_particlespawner.minpos = new v3f (minpos);
1749 event.add_particlespawner.maxpos = new v3f (maxpos);
1750 event.add_particlespawner.minvel = new v3f (minvel);
1751 event.add_particlespawner.maxvel = new v3f (maxvel);
1752 event.add_particlespawner.minacc = new v3f (minacc);
1753 event.add_particlespawner.maxacc = new v3f (maxacc);
1754 event.add_particlespawner.minexptime = minexptime;
1755 event.add_particlespawner.maxexptime = maxexptime;
1756 event.add_particlespawner.minsize = minsize;
1757 event.add_particlespawner.maxsize = maxsize;
1758 event.add_particlespawner.collisiondetection = collisiondetection;
1759 event.add_particlespawner.vertical = vertical;
1760 event.add_particlespawner.texture = new std::string(texture);
1761 event.add_particlespawner.id = id;
1763 m_client_event_queue.push_back(event);
1765 else if(command == TOCLIENT_DELETE_PARTICLESPAWNER)
1767 std::string datastring((char*)&data[2], datasize-2);
1768 std::istringstream is(datastring, std::ios_base::binary);
1770 u32 id = readU16(is);
1773 event.type = CE_DELETE_PARTICLESPAWNER;
1774 event.delete_particlespawner.id = id;
1776 m_client_event_queue.push_back(event);
1778 else if(command == TOCLIENT_HUDADD)
1780 std::string datastring((char *)&data[2], datasize - 2);
1781 std::istringstream is(datastring, std::ios_base::binary);
1783 u32 id = readU32(is);
1784 u8 type = readU8(is);
1785 v2f pos = readV2F1000(is);
1786 std::string name = deSerializeString(is);
1787 v2f scale = readV2F1000(is);
1788 std::string text = deSerializeString(is);
1789 u32 number = readU32(is);
1790 u32 item = readU32(is);
1791 u32 dir = readU32(is);
1792 v2f align = readV2F1000(is);
1793 v2f offset = readV2F1000(is);
1796 world_pos = readV3F1000(is);
1797 }catch(SerializationError &e) {};
1800 event.type = CE_HUDADD;
1801 event.hudadd.id = id;
1802 event.hudadd.type = type;
1803 event.hudadd.pos = new v2f(pos);
1804 event.hudadd.name = new std::string(name);
1805 event.hudadd.scale = new v2f(scale);
1806 event.hudadd.text = new std::string(text);
1807 event.hudadd.number = number;
1808 event.hudadd.item = item;
1809 event.hudadd.dir = dir;
1810 event.hudadd.align = new v2f(align);
1811 event.hudadd.offset = new v2f(offset);
1812 event.hudadd.world_pos = new v3f(world_pos);
1813 m_client_event_queue.push_back(event);
1815 else if(command == TOCLIENT_HUDRM)
1817 std::string datastring((char *)&data[2], datasize - 2);
1818 std::istringstream is(datastring, std::ios_base::binary);
1820 u32 id = readU32(is);
1823 event.type = CE_HUDRM;
1824 event.hudrm.id = id;
1825 m_client_event_queue.push_back(event);
1827 else if(command == TOCLIENT_HUDCHANGE)
1834 std::string datastring((char *)&data[2], datasize - 2);
1835 std::istringstream is(datastring, std::ios_base::binary);
1837 u32 id = readU32(is);
1838 u8 stat = (HudElementStat)readU8(is);
1840 if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
1841 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
1842 v2fdata = readV2F1000(is);
1843 else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
1844 sdata = deSerializeString(is);
1845 else if (stat == HUD_STAT_WORLD_POS)
1846 v3fdata = readV3F1000(is);
1848 intdata = readU32(is);
1851 event.type = CE_HUDCHANGE;
1852 event.hudchange.id = id;
1853 event.hudchange.stat = (HudElementStat)stat;
1854 event.hudchange.v2fdata = new v2f(v2fdata);
1855 event.hudchange.v3fdata = new v3f(v3fdata);
1856 event.hudchange.sdata = new std::string(sdata);
1857 event.hudchange.data = intdata;
1858 m_client_event_queue.push_back(event);
1860 else if(command == TOCLIENT_HUD_SET_FLAGS)
1862 std::string datastring((char *)&data[2], datasize - 2);
1863 std::istringstream is(datastring, std::ios_base::binary);
1865 u32 flags = readU32(is);
1866 u32 mask = readU32(is);
1868 player->hud_flags &= ~mask;
1869 player->hud_flags |= flags;
1871 else if(command == TOCLIENT_HUD_SET_PARAM)
1873 std::string datastring((char *)&data[2], datasize - 2);
1874 std::istringstream is(datastring, std::ios_base::binary);
1876 u16 param = readU16(is);
1877 std::string value = deSerializeString(is);
1879 if(param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) {
1880 s32 hotbar_itemcount = readS32((u8*) value.c_str());
1881 if(hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
1882 player->hud_hotbar_itemcount = hotbar_itemcount;
1884 else if (param == HUD_PARAM_HOTBAR_IMAGE) {
1885 ((LocalPlayer *) player)->hotbar_image = value;
1887 else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
1888 ((LocalPlayer *) player)->hotbar_selected_image = value;
1891 else if(command == TOCLIENT_SET_SKY)
1893 std::string datastring((char *)&data[2], datasize - 2);
1894 std::istringstream is(datastring, std::ios_base::binary);
1896 video::SColor *bgcolor = new video::SColor(readARGB8(is));
1897 std::string *type = new std::string(deSerializeString(is));
1898 u16 count = readU16(is);
1899 std::vector<std::string> *params = new std::vector<std::string>;
1901 for(size_t i=0; i<count; i++)
1902 params->push_back(deSerializeString(is));
1905 event.type = CE_SET_SKY;
1906 event.set_sky.bgcolor = bgcolor;
1907 event.set_sky.type = type;
1908 event.set_sky.params = params;
1909 m_client_event_queue.push_back(event);
1911 else if(command == TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO)
1913 std::string datastring((char *)&data[2], datasize - 2);
1914 std::istringstream is(datastring, std::ios_base::binary);
1916 bool do_override = readU8(is);
1917 float day_night_ratio_f = (float)readU16(is) / 65536;
1920 event.type = CE_OVERRIDE_DAY_NIGHT_RATIO;
1921 event.override_day_night_ratio.do_override = do_override;
1922 event.override_day_night_ratio.ratio_f = day_night_ratio_f;
1923 m_client_event_queue.push_back(event);
1927 infostream<<"Client: Ignoring unknown command "
1928 <<command<<std::endl;
1932 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
1934 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1935 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
1938 void Client::interact(u8 action, const PointedThing& pointed)
1940 if(connectedAndInitialized() == false){
1941 infostream<<"Client::interact() "
1942 "cancelled (not connected)"
1947 std::ostringstream os(std::ios_base::binary);
1953 [5] u32 length of the next item
1954 [9] serialized PointedThing
1956 0: start digging (from undersurface) or use
1957 1: stop digging (all parameters ignored)
1958 2: digging completed
1959 3: place block or item (to abovesurface)
1962 writeU16(os, TOSERVER_INTERACT);
1963 writeU8(os, action);
1964 writeU16(os, getPlayerItem());
1965 std::ostringstream tmp_os(std::ios::binary);
1966 pointed.serialize(tmp_os);
1967 os<<serializeLongString(tmp_os.str());
1969 std::string s = os.str();
1970 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1973 Send(0, data, true);
1976 void Client::sendNodemetaFields(v3s16 p, const std::string &formname,
1977 const std::map<std::string, std::string> &fields)
1979 std::ostringstream os(std::ios_base::binary);
1981 writeU16(os, TOSERVER_NODEMETA_FIELDS);
1983 os<<serializeString(formname);
1984 size_t fields_size = fields.size();
1985 assert(fields_size <= 0xFFFF);
1986 writeU16(os, (u16) (fields_size & 0xFFFF));
1987 for(std::map<std::string, std::string>::const_iterator
1988 i = fields.begin(); i != fields.end(); i++){
1989 const std::string &name = i->first;
1990 const std::string &value = i->second;
1991 os<<serializeString(name);
1992 os<<serializeLongString(value);
1996 std::string s = os.str();
1997 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1999 Send(0, data, true);
2002 void Client::sendInventoryFields(const std::string &formname,
2003 const std::map<std::string, std::string> &fields)
2005 std::ostringstream os(std::ios_base::binary);
2007 writeU16(os, TOSERVER_INVENTORY_FIELDS);
2008 os<<serializeString(formname);
2009 size_t fields_size = fields.size();
2010 assert(fields_size <= 0xFFFF);
2011 writeU16(os, (u16) (fields_size & 0xFFFF));
2012 for(std::map<std::string, std::string>::const_iterator
2013 i = fields.begin(); i != fields.end(); i++){
2014 const std::string &name = i->first;
2015 const std::string &value = i->second;
2016 os<<serializeString(name);
2017 os<<serializeLongString(value);
2021 std::string s = os.str();
2022 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2024 Send(0, data, true);
2027 void Client::sendInventoryAction(InventoryAction *a)
2029 std::ostringstream os(std::ios_base::binary);
2033 writeU16(buf, TOSERVER_INVENTORY_ACTION);
2034 os.write((char*)buf, 2);
2039 std::string s = os.str();
2040 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2042 Send(0, data, true);
2045 void Client::sendChatMessage(const std::wstring &message)
2047 std::ostringstream os(std::ios_base::binary);
2051 writeU16(buf, TOSERVER_CHAT_MESSAGE);
2052 os.write((char*)buf, 2);
2055 size_t messagesize = message.size();
2056 assert(messagesize <= 0xFFFF);
2057 writeU16(buf, (u16) (messagesize & 0xFF));
2058 os.write((char*)buf, 2);
2061 for(unsigned int i=0; i<message.size(); i++)
2065 os.write((char*)buf, 2);
2069 std::string s = os.str();
2070 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2072 Send(0, data, true);
2075 void Client::sendChangePassword(const std::wstring &oldpassword,
2076 const std::wstring &newpassword)
2078 Player *player = m_env.getLocalPlayer();
2082 std::string playername = player->getName();
2083 std::string oldpwd = translatePassword(playername, oldpassword);
2084 std::string newpwd = translatePassword(playername, newpassword);
2086 std::ostringstream os(std::ios_base::binary);
2087 u8 buf[2+PASSWORD_SIZE*2];
2089 [0] u16 TOSERVER_PASSWORD
2090 [2] u8[28] old password
2091 [30] u8[28] new password
2094 writeU16(buf, TOSERVER_PASSWORD);
2095 for(unsigned int i=0;i<PASSWORD_SIZE-1;i++)
2097 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
2098 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
2100 buf[2+PASSWORD_SIZE-1] = 0;
2101 buf[30+PASSWORD_SIZE-1] = 0;
2102 os.write((char*)buf, 2+PASSWORD_SIZE*2);
2105 std::string s = os.str();
2106 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2108 Send(0, data, true);
2112 void Client::sendDamage(u8 damage)
2114 DSTACK(__FUNCTION_NAME);
2115 std::ostringstream os(std::ios_base::binary);
2117 writeU16(os, TOSERVER_DAMAGE);
2118 writeU8(os, damage);
2121 std::string s = os.str();
2122 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2124 Send(0, data, true);
2127 void Client::sendBreath(u16 breath)
2129 DSTACK(__FUNCTION_NAME);
2130 std::ostringstream os(std::ios_base::binary);
2132 writeU16(os, TOSERVER_BREATH);
2133 writeU16(os, breath);
2135 std::string s = os.str();
2136 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2138 Send(0, data, true);
2141 void Client::sendRespawn()
2143 DSTACK(__FUNCTION_NAME);
2144 std::ostringstream os(std::ios_base::binary);
2146 writeU16(os, TOSERVER_RESPAWN);
2149 std::string s = os.str();
2150 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2152 Send(0, data, true);
2155 void Client::sendPlayerPos()
2157 LocalPlayer *myplayer = m_env.getLocalPlayer();
2158 if(myplayer == NULL)
2161 // Save bandwidth by only updating position when something changed
2162 if(myplayer->last_position == myplayer->getPosition() &&
2163 myplayer->last_speed == myplayer->getSpeed() &&
2164 myplayer->last_pitch == myplayer->getPitch() &&
2165 myplayer->last_yaw == myplayer->getYaw() &&
2166 myplayer->last_keyPressed == myplayer->keyPressed)
2169 myplayer->last_position = myplayer->getPosition();
2170 myplayer->last_speed = myplayer->getSpeed();
2171 myplayer->last_pitch = myplayer->getPitch();
2172 myplayer->last_yaw = myplayer->getYaw();
2173 myplayer->last_keyPressed = myplayer->keyPressed;
2177 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2178 our_peer_id = m_con.GetPeerID();
2181 // Set peer id if not set already
2182 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2183 myplayer->peer_id = our_peer_id;
2184 // Check that an existing peer_id is the same as the connection's
2185 assert(myplayer->peer_id == our_peer_id);
2187 v3f pf = myplayer->getPosition();
2188 v3f sf = myplayer->getSpeed();
2189 s32 pitch = myplayer->getPitch() * 100;
2190 s32 yaw = myplayer->getYaw() * 100;
2191 u32 keyPressed = myplayer->keyPressed;
2193 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
2194 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
2198 [2] v3s32 position*100
2199 [2+12] v3s32 speed*100
2200 [2+12+12] s32 pitch*100
2201 [2+12+12+4] s32 yaw*100
2202 [2+12+12+4+4] u32 keyPressed
2204 SharedBuffer<u8> data(2+12+12+4+4+4);
2205 writeU16(&data[0], TOSERVER_PLAYERPOS);
2206 writeV3S32(&data[2], position);
2207 writeV3S32(&data[2+12], speed);
2208 writeS32(&data[2+12+12], pitch);
2209 writeS32(&data[2+12+12+4], yaw);
2210 writeU32(&data[2+12+12+4+4], keyPressed);
2211 // Send as unreliable
2212 Send(0, data, false);
2215 void Client::sendPlayerItem(u16 item)
2217 Player *myplayer = m_env.getLocalPlayer();
2218 if(myplayer == NULL)
2221 u16 our_peer_id = m_con.GetPeerID();
2223 // Set peer id if not set already
2224 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2225 myplayer->peer_id = our_peer_id;
2226 // Check that an existing peer_id is the same as the connection's
2227 assert(myplayer->peer_id == our_peer_id);
2229 SharedBuffer<u8> data(2+2);
2230 writeU16(&data[0], TOSERVER_PLAYERITEM);
2231 writeU16(&data[2], item);
2234 Send(0, data, true);
2237 void Client::removeNode(v3s16 p)
2239 std::map<v3s16, MapBlock*> modified_blocks;
2243 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
2245 catch(InvalidPositionException &e)
2249 // add urgent task to update the modified node
2250 addUpdateMeshTaskForNode(p, false, true);
2252 for(std::map<v3s16, MapBlock * >::iterator
2253 i = modified_blocks.begin();
2254 i != modified_blocks.end(); ++i)
2256 addUpdateMeshTaskWithEdge(i->first);
2260 void Client::addNode(v3s16 p, MapNode n, bool remove_metadata)
2262 TimeTaker timer1("Client::addNode()");
2264 std::map<v3s16, MapBlock*> modified_blocks;
2268 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
2269 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
2271 catch(InvalidPositionException &e)
2274 for(std::map<v3s16, MapBlock * >::iterator
2275 i = modified_blocks.begin();
2276 i != modified_blocks.end(); ++i)
2278 addUpdateMeshTaskWithEdge(i->first);
2282 void Client::setPlayerControl(PlayerControl &control)
2284 LocalPlayer *player = m_env.getLocalPlayer();
2285 assert(player != NULL);
2286 player->control = control;
2289 void Client::selectPlayerItem(u16 item)
2291 m_playeritem = item;
2292 m_inventory_updated = true;
2293 sendPlayerItem(item);
2296 // Returns true if the inventory of the local player has been
2297 // updated from the server. If it is true, it is set to false.
2298 bool Client::getLocalInventoryUpdated()
2300 bool updated = m_inventory_updated;
2301 m_inventory_updated = false;
2305 // Copies the inventory of the local player to parameter
2306 void Client::getLocalInventory(Inventory &dst)
2308 Player *player = m_env.getLocalPlayer();
2309 assert(player != NULL);
2310 dst = player->inventory;
2313 Inventory* Client::getInventory(const InventoryLocation &loc)
2316 case InventoryLocation::UNDEFINED:
2319 case InventoryLocation::CURRENT_PLAYER:
2321 Player *player = m_env.getLocalPlayer();
2322 assert(player != NULL);
2323 return &player->inventory;
2326 case InventoryLocation::PLAYER:
2328 Player *player = m_env.getPlayer(loc.name.c_str());
2331 return &player->inventory;
2334 case InventoryLocation::NODEMETA:
2336 NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p);
2339 return meta->getInventory();
2342 case InventoryLocation::DETACHED:
2344 if(m_detached_inventories.count(loc.name) == 0)
2346 return m_detached_inventories[loc.name];
2355 void Client::inventoryAction(InventoryAction *a)
2358 Send it to the server
2360 sendInventoryAction(a);
2363 Predict some local inventory changes
2365 a->clientApply(this, this);
2371 ClientActiveObject * Client::getSelectedActiveObject(
2373 v3f from_pos_f_on_map,
2374 core::line3d<f32> shootline_on_map
2377 std::vector<DistanceSortedActiveObject> objects;
2379 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
2382 // After this, the closest object is the first in the array.
2383 std::sort(objects.begin(), objects.end());
2385 for(unsigned int i=0; i<objects.size(); i++)
2387 ClientActiveObject *obj = objects[i].obj;
2389 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
2390 if(selection_box == NULL)
2393 v3f pos = obj->getPosition();
2395 core::aabbox3d<f32> offsetted_box(
2396 selection_box->MinEdge + pos,
2397 selection_box->MaxEdge + pos
2400 if(offsetted_box.intersectsWithLine(shootline_on_map))
2409 std::list<std::string> Client::getConnectedPlayerNames()
2411 return m_env.getPlayerNames();
2414 float Client::getAnimationTime()
2416 return m_animation_time;
2419 int Client::getCrackLevel()
2421 return m_crack_level;
2424 void Client::setCrack(int level, v3s16 pos)
2426 int old_crack_level = m_crack_level;
2427 v3s16 old_crack_pos = m_crack_pos;
2429 m_crack_level = level;
2432 if(old_crack_level >= 0 && (level < 0 || pos != old_crack_pos))
2435 addUpdateMeshTaskForNode(old_crack_pos, false, true);
2437 if(level >= 0 && (old_crack_level < 0 || pos != old_crack_pos))
2440 addUpdateMeshTaskForNode(pos, false, true);
2446 Player *player = m_env.getLocalPlayer();
2447 assert(player != NULL);
2451 u16 Client::getBreath()
2453 Player *player = m_env.getLocalPlayer();
2454 assert(player != NULL);
2455 return player->getBreath();
2458 bool Client::getChatMessage(std::wstring &message)
2460 if(m_chat_queue.size() == 0)
2462 message = m_chat_queue.pop_front();
2466 void Client::typeChatMessage(const std::wstring &message)
2468 // Discard empty line
2473 sendChatMessage(message);
2476 if (message[0] == L'/')
2478 m_chat_queue.push_back(
2479 (std::wstring)L"issued command: "+message);
2483 LocalPlayer *player = m_env.getLocalPlayer();
2484 assert(player != NULL);
2485 std::wstring name = narrow_to_wide(player->getName());
2486 m_chat_queue.push_back(
2487 (std::wstring)L"<"+name+L"> "+message);
2491 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
2493 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2498 Create a task to update the mesh of the block
2501 MeshMakeData *data = new MeshMakeData(this);
2504 //TimeTaker timer("data fill");
2506 // Debug: 1-6ms, avg=2ms
2508 data->setCrack(m_crack_level, m_crack_pos);
2509 data->setSmoothLighting(g_settings->getBool("smooth_lighting"));
2512 // Add task to queue
2513 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server, urgent);
2516 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent)
2519 v3s16 p = blockpos + v3s16(0,0,0);
2520 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2521 addUpdateMeshTask(p, ack_to_server, urgent);
2523 catch(InvalidPositionException &e){}
2526 for (int i=0;i<6;i++)
2529 v3s16 p = blockpos + g_6dirs[i];
2530 addUpdateMeshTask(p, false, urgent);
2532 catch(InvalidPositionException &e){}
2536 void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool urgent)
2540 infostream<<"Client::addUpdateMeshTaskForNode(): "
2541 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2545 v3s16 blockpos = getNodeBlockPos(nodepos);
2546 v3s16 blockpos_relative = blockpos * MAP_BLOCKSIZE;
2549 v3s16 p = blockpos + v3s16(0,0,0);
2550 addUpdateMeshTask(p, ack_to_server, urgent);
2552 catch(InvalidPositionException &e){}
2555 if(nodepos.X == blockpos_relative.X){
2557 v3s16 p = blockpos + v3s16(-1,0,0);
2558 addUpdateMeshTask(p, false, urgent);
2560 catch(InvalidPositionException &e){}
2563 if(nodepos.Y == blockpos_relative.Y){
2565 v3s16 p = blockpos + v3s16(0,-1,0);
2566 addUpdateMeshTask(p, false, urgent);
2568 catch(InvalidPositionException &e){}
2571 if(nodepos.Z == blockpos_relative.Z){
2573 v3s16 p = blockpos + v3s16(0,0,-1);
2574 addUpdateMeshTask(p, false, urgent);
2576 catch(InvalidPositionException &e){}
2580 ClientEvent Client::getClientEvent()
2582 if(m_client_event_queue.size() == 0)
2585 event.type = CE_NONE;
2588 return m_client_event_queue.pop_front();
2591 float Client::mediaReceiveProgress()
2593 if (m_media_downloader)
2594 return m_media_downloader->getProgress();
2596 return 1.0; // downloader only exists when not yet done
2599 void draw_load_screen(const std::wstring &text,
2600 IrrlichtDevice* device, gui::IGUIFont* font,
2601 float dtime=0 ,int percent=0, bool clouds=true);
2603 void Client::afterContentReceived(IrrlichtDevice *device, gui::IGUIFont* font)
2605 infostream<<"Client::afterContentReceived() started"<<std::endl;
2606 assert(m_itemdef_received);
2607 assert(m_nodedef_received);
2608 assert(mediaReceived());
2610 // Rebuild inherited images and recreate textures
2611 infostream<<"- Rebuilding images and textures"<<std::endl;
2612 m_tsrc->rebuildImagesAndTextures();
2615 infostream<<"- Rebuilding shaders"<<std::endl;
2616 m_shsrc->rebuildShaders();
2618 // Update node aliases
2619 infostream<<"- Updating node aliases"<<std::endl;
2620 m_nodedef->updateAliases(m_itemdef);
2622 // Update node textures
2623 infostream<<"- Updating node textures"<<std::endl;
2624 m_nodedef->updateTextures(m_tsrc);
2626 // Preload item textures and meshes if configured to
2627 if(g_settings->getBool("preload_item_visuals"))
2629 verbosestream<<"Updating item textures and meshes"<<std::endl;
2630 wchar_t* text = wgettext("Item textures...");
2631 draw_load_screen(text,device,font,0,0);
2632 std::set<std::string> names = m_itemdef->getAll();
2633 size_t size = names.size();
2636 for(std::set<std::string>::const_iterator
2637 i = names.begin(); i != names.end(); ++i){
2638 // Asking for these caches the result
2639 m_itemdef->getInventoryTexture(*i, this);
2640 m_itemdef->getWieldMesh(*i, this);
2642 percent = count*100/size;
2643 if (count%50 == 0) // only update every 50 item
2644 draw_load_screen(text,device,font,0,percent);
2649 // Start mesh update thread after setting up content definitions
2650 infostream<<"- Starting mesh update thread"<<std::endl;
2651 m_mesh_update_thread.Start();
2653 infostream<<"Client::afterContentReceived() done"<<std::endl;
2656 float Client::getRTT(void)
2659 return m_con.GetPeerAvgRTT(PEER_ID_SERVER);
2660 } catch(con::PeerNotFoundException &e){
2665 // IGameDef interface
2667 IItemDefManager* Client::getItemDefManager()
2671 INodeDefManager* Client::getNodeDefManager()
2675 ICraftDefManager* Client::getCraftDefManager()
2678 //return m_craftdef;
2680 ITextureSource* Client::getTextureSource()
2684 IShaderSource* Client::getShaderSource()
2688 u16 Client::allocateUnknownNodeId(const std::string &name)
2690 errorstream<<"Client::allocateUnknownNodeId(): "
2691 <<"Client cannot allocate node IDs"<<std::endl;
2693 return CONTENT_IGNORE;
2695 ISoundManager* Client::getSoundManager()
2699 MtEventManager* Client::getEventManager()
2704 scene::IAnimatedMesh* Client::getMesh(const std::string &filename)
2706 std::map<std::string, std::string>::const_iterator i =
2707 m_mesh_data.find(filename);
2708 if(i == m_mesh_data.end()){
2709 errorstream<<"Client::getMesh(): Mesh not found: \""<<filename<<"\""
2713 const std::string &data = i->second;
2714 scene::ISceneManager *smgr = m_device->getSceneManager();
2716 // Create the mesh, remove it from cache and return it
2717 // This allows unique vertex colors and other properties for each instance
2718 Buffer<char> data_rw(data.c_str(), data.size()); // Const-incorrect Irrlicht
2719 io::IFileSystem *irrfs = m_device->getFileSystem();
2720 io::IReadFile *rfile = irrfs->createMemoryReadFile(
2721 *data_rw, data_rw.getSize(), filename.c_str());
2724 scene::IAnimatedMesh *mesh = smgr->getMesh(rfile);
2726 // NOTE: By playing with Irrlicht refcounts, maybe we could cache a bunch
2727 // of uniquely named instances and re-use them
2729 smgr->getMeshCache()->removeMesh(mesh);