3 Copyright (C) 2010 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.
22 #include "clientserver.h"
23 #include "jmutexautolock.h"
27 #include "mapsector.h"
28 #include "mapblock_mesh.h"
33 #include "nodemetadata.h"
36 #include <IFileSystem.h>
39 #include "clientmap.h"
40 #include "filecache.h"
42 #include "util/string.h"
44 #include "IMeshCache.h"
46 static std::string getMediaCacheDir()
48 return porting::path_user + DIR_DELIM + "cache" + DIR_DELIM + "media";
55 MediaRequest(const std::string &name_=""):
64 QueuedMeshUpdate::QueuedMeshUpdate():
67 ack_block_to_server(false)
71 QueuedMeshUpdate::~QueuedMeshUpdate()
81 MeshUpdateQueue::MeshUpdateQueue()
86 MeshUpdateQueue::~MeshUpdateQueue()
88 JMutexAutoLock lock(m_mutex);
90 for(std::vector<QueuedMeshUpdate*>::iterator
92 i != m_queue.end(); i++)
94 QueuedMeshUpdate *q = *i;
100 peer_id=0 adds with nobody to send to
102 void MeshUpdateQueue::addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_server, bool urgent)
104 DSTACK(__FUNCTION_NAME);
108 JMutexAutoLock lock(m_mutex);
114 Find if block is already in queue.
115 If it is, update the data and quit.
117 for(std::vector<QueuedMeshUpdate*>::iterator
119 i != m_queue.end(); i++)
121 QueuedMeshUpdate *q = *i;
127 if(ack_block_to_server)
128 q->ack_block_to_server = true;
136 QueuedMeshUpdate *q = new QueuedMeshUpdate;
139 q->ack_block_to_server = ack_block_to_server;
140 m_queue.push_back(q);
143 // Returned pointer must be deleted
144 // Returns NULL if queue is empty
145 QueuedMeshUpdate * MeshUpdateQueue::pop()
147 JMutexAutoLock lock(m_mutex);
149 bool must_be_urgent = !m_urgents.empty();
150 for(std::vector<QueuedMeshUpdate*>::iterator
152 i != m_queue.end(); i++)
154 QueuedMeshUpdate *q = *i;
155 if(must_be_urgent && m_urgents.count(q->p) == 0)
158 m_urgents.erase(q->p);
168 void * MeshUpdateThread::Thread()
172 log_register_thread("MeshUpdateThread");
174 DSTACK(__FUNCTION_NAME);
176 BEGIN_DEBUG_EXCEPTION_HANDLER
180 /*// Wait for output queue to flush.
181 // Allow 2 in queue, this makes less frametime jitter.
182 // Umm actually, there is no much difference
183 if(m_queue_out.size() >= 2)
189 QueuedMeshUpdate *q = m_queue_in.pop();
196 ScopeProfiler sp(g_profiler, "Client: Mesh making");
198 MapBlockMesh *mesh_new = new MapBlockMesh(q->data);
199 if(mesh_new->getMesh()->getMeshBufferCount() == 0)
208 r.ack_block_to_server = q->ack_block_to_server;
210 /*infostream<<"MeshUpdateThread: Processed "
211 <<"("<<q->p.X<<","<<q->p.Y<<","<<q->p.Z<<")"
214 m_queue_out.push_back(r);
219 END_DEBUG_EXCEPTION_HANDLER(errorstream)
225 IrrlichtDevice *device,
226 const char *playername,
227 std::string password,
228 MapDrawControl &control,
229 IWritableTextureSource *tsrc,
230 IWritableItemDefManager *itemdef,
231 IWritableNodeDefManager *nodedef,
232 ISoundManager *sound,
233 MtEventManager *event
240 m_mesh_update_thread(this),
242 new ClientMap(this, this, control,
243 device->getSceneManager()->getRootSceneNode(),
244 device->getSceneManager(), 666),
245 device->getSceneManager(),
248 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
250 m_server_ser_ver(SER_FMT_VER_INVALID),
252 m_inventory_updated(false),
253 m_inventory_from_server(NULL),
254 m_inventory_from_server_age(0.0),
259 m_password(password),
260 m_access_denied(false),
261 m_media_cache(getMediaCacheDir()),
262 m_media_receive_progress(0),
263 m_media_received(false),
264 m_itemdef_received(false),
265 m_nodedef_received(false),
266 m_time_of_day_set(false),
267 m_last_time_of_day_f(-1),
268 m_time_of_day_update_timer(0),
269 m_removed_sounds_check_timer(0)
271 m_packetcounter_timer = 0.0;
272 //m_delete_unused_sectors_timer = 0.0;
273 m_connection_reinit_timer = 0.0;
274 m_avg_rtt_timer = 0.0;
275 m_playerpos_send_timer = 0.0;
276 m_ignore_damage_timer = 0.0;
278 // Build main texture atlas, now that the GameDef exists (that is, us)
279 if(g_settings->getBool("enable_texture_atlas"))
280 m_tsrc->buildMainAtlas(this);
282 infostream<<"Not building texture atlas."<<std::endl;
288 Player *player = new LocalPlayer(this);
290 player->updateName(playername);
292 m_env.addPlayer(player);
299 //JMutexAutoLock conlock(m_con_mutex); //bulk comment-out
303 m_mesh_update_thread.setRun(false);
304 while(m_mesh_update_thread.IsRunning())
307 delete m_inventory_from_server;
309 // Delete detached inventories
311 for(std::map<std::string, Inventory*>::iterator
312 i = m_detached_inventories.begin();
313 i != m_detached_inventories.end(); i++){
319 void Client::connect(Address address)
321 DSTACK(__FUNCTION_NAME);
322 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
323 m_con.SetTimeoutMs(0);
324 m_con.Connect(address);
327 bool Client::connectedAndInitialized()
329 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
331 if(m_con.Connected() == false)
334 if(m_server_ser_ver == SER_FMT_VER_INVALID)
340 void Client::step(float dtime)
342 DSTACK(__FUNCTION_NAME);
348 if(m_ignore_damage_timer > dtime)
349 m_ignore_damage_timer -= dtime;
351 m_ignore_damage_timer = 0.0;
353 m_animation_time += dtime;
354 if(m_animation_time > 60.0)
355 m_animation_time -= 60.0;
357 m_time_of_day_update_timer += dtime;
359 //infostream<<"Client steps "<<dtime<<std::endl;
362 //TimeTaker timer("ReceiveAll()", m_device);
368 //TimeTaker timer("m_con_mutex + m_con.RunTimeouts()", m_device);
370 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
371 m_con.RunTimeouts(dtime);
378 float &counter = m_packetcounter_timer;
384 infostream<<"Client packetcounter (20s):"<<std::endl;
385 m_packetcounter.print(infostream);
386 m_packetcounter.clear();
390 // Get connection status
391 bool connected = connectedAndInitialized();
396 Delete unused sectors
398 NOTE: This jams the game for a while because deleting sectors
402 float &counter = m_delete_unused_sectors_timer;
410 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
412 core::list<v3s16> deleted_blocks;
414 float delete_unused_sectors_timeout =
415 g_settings->getFloat("client_delete_unused_sectors_timeout");
417 // Delete sector blocks
418 /*u32 num = m_env.getMap().unloadUnusedData
419 (delete_unused_sectors_timeout,
420 true, &deleted_blocks);*/
422 // Delete whole sectors
423 m_env.getMap().unloadUnusedData
424 (delete_unused_sectors_timeout,
427 if(deleted_blocks.size() > 0)
429 /*infostream<<"Client: Deleted blocks of "<<num
430 <<" unused sectors"<<std::endl;*/
431 /*infostream<<"Client: Deleted "<<num
432 <<" unused sectors"<<std::endl;*/
438 // Env is locked so con can be locked.
439 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
441 core::list<v3s16>::Iterator i = deleted_blocks.begin();
442 core::list<v3s16> sendlist;
445 if(sendlist.size() == 255 || i == deleted_blocks.end())
447 if(sendlist.size() == 0)
456 u32 replysize = 2+1+6*sendlist.size();
457 SharedBuffer<u8> reply(replysize);
458 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
459 reply[2] = sendlist.size();
461 for(core::list<v3s16>::Iterator
462 j = sendlist.begin();
463 j != sendlist.end(); j++)
465 writeV3S16(&reply[2+1+6*k], *j);
468 m_con.Send(PEER_ID_SERVER, 1, reply, true);
470 if(i == deleted_blocks.end())
476 sendlist.push_back(*i);
484 if(connected == false)
486 float &counter = m_connection_reinit_timer;
492 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
494 Player *myplayer = m_env.getLocalPlayer();
495 assert(myplayer != NULL);
497 // Send TOSERVER_INIT
498 // [0] u16 TOSERVER_INIT
499 // [2] u8 SER_FMT_VER_HIGHEST
500 // [3] u8[20] player_name
501 // [23] u8[28] password (new in some version)
502 // [51] u16 minimum supported network protocol version (added sometime)
503 // [53] u16 maximum supported network protocol version (added later than the previous one)
504 SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2+2);
505 writeU16(&data[0], TOSERVER_INIT);
506 writeU8(&data[2], SER_FMT_VER_HIGHEST);
508 memset((char*)&data[3], 0, PLAYERNAME_SIZE);
509 snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
511 /*infostream<<"Client: sending initial password hash: \""<<m_password<<"\""
514 memset((char*)&data[23], 0, PASSWORD_SIZE);
515 snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
517 writeU16(&data[51], CLIENT_PROTOCOL_VERSION_MIN);
518 writeU16(&data[53], CLIENT_PROTOCOL_VERSION_MAX);
520 // Send as unreliable
521 Send(0, data, false);
524 // Not connected, return
529 Do stuff if connected
533 Run Map's timers and unload unused data
535 const float map_timer_and_unload_dtime = 5.25;
536 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
538 ScopeProfiler sp(g_profiler, "Client: map timer and unload");
539 core::list<v3s16> deleted_blocks;
540 m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
541 g_settings->getFloat("client_unload_unused_data_timeout"),
544 /*if(deleted_blocks.size() > 0)
545 infostream<<"Client: Unloaded "<<deleted_blocks.size()
546 <<" unused blocks"<<std::endl;*/
550 NOTE: This loop is intentionally iterated the way it is.
553 core::list<v3s16>::Iterator i = deleted_blocks.begin();
554 core::list<v3s16> sendlist;
557 if(sendlist.size() == 255 || i == deleted_blocks.end())
559 if(sendlist.size() == 0)
568 u32 replysize = 2+1+6*sendlist.size();
569 SharedBuffer<u8> reply(replysize);
570 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
571 reply[2] = sendlist.size();
573 for(core::list<v3s16>::Iterator
574 j = sendlist.begin();
575 j != sendlist.end(); j++)
577 writeV3S16(&reply[2+1+6*k], *j);
580 m_con.Send(PEER_ID_SERVER, 1, reply, true);
582 if(i == deleted_blocks.end())
588 sendlist.push_back(*i);
598 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
600 // Control local player (0ms)
601 LocalPlayer *player = m_env.getLocalPlayer();
602 assert(player != NULL);
603 player->applyControl(dtime);
605 //TimeTaker envtimer("env step", m_device);
614 ClientEnvEvent event = m_env.getClientEvent();
615 if(event.type == CEE_NONE)
619 else if(event.type == CEE_PLAYER_DAMAGE)
621 if(m_ignore_damage_timer <= 0)
623 u8 damage = event.player_damage.amount;
625 if(event.player_damage.send_to_server)
628 // Add to ClientEvent queue
630 event.type = CE_PLAYER_DAMAGE;
631 event.player_damage.amount = damage;
632 m_client_event_queue.push_back(event);
642 float &counter = m_avg_rtt_timer;
647 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
648 // connectedAndInitialized() is true, peer exists.
649 float avg_rtt = m_con.GetPeerAvgRTT(PEER_ID_SERVER);
650 infostream<<"Client: avg_rtt="<<avg_rtt<<std::endl;
655 Send player position to server
658 float &counter = m_playerpos_send_timer;
668 Replace updated meshes
671 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
673 //TimeTaker timer("** Processing mesh update result queue");
676 /*infostream<<"Mesh update result queue size is "
677 <<m_mesh_update_thread.m_queue_out.size()
680 int num_processed_meshes = 0;
681 while(m_mesh_update_thread.m_queue_out.size() > 0)
683 num_processed_meshes++;
684 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_front();
685 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
688 //JMutexAutoLock lock(block->mesh_mutex);
690 // Delete the old mesh
691 if(block->mesh != NULL)
693 // TODO: Remove hardware buffers of meshbuffers of block->mesh
698 // Replace with the new mesh
699 block->mesh = r.mesh;
701 if(r.ack_block_to_server)
703 /*infostream<<"Client: ACK block ("<<r.p.X<<","<<r.p.Y
704 <<","<<r.p.Z<<")"<<std::endl;*/
715 u32 replysize = 2+1+6;
716 SharedBuffer<u8> reply(replysize);
717 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
719 writeV3S16(&reply[3], r.p);
721 m_con.Send(PEER_ID_SERVER, 1, reply, true);
724 if(num_processed_meshes > 0)
725 g_profiler->graphAdd("num_processed_meshes", num_processed_meshes);
729 If the server didn't update the inventory in a while, revert
730 the local inventory (so the player notices the lag problem
731 and knows something is wrong).
733 if(m_inventory_from_server)
735 float interval = 10.0;
736 float count_before = floor(m_inventory_from_server_age / interval);
738 m_inventory_from_server_age += dtime;
740 float count_after = floor(m_inventory_from_server_age / interval);
742 if(count_after != count_before)
744 // Do this every <interval> seconds after TOCLIENT_INVENTORY
745 // Reset the locally changed inventory to the authoritative inventory
746 Player *player = m_env.getLocalPlayer();
747 player->inventory = *m_inventory_from_server;
748 m_inventory_updated = true;
753 Update positions of sounds attached to objects
756 for(std::map<int, u16>::iterator
757 i = m_sounds_to_objects.begin();
758 i != m_sounds_to_objects.end(); i++)
760 int client_id = i->first;
761 u16 object_id = i->second;
762 ClientActiveObject *cao = m_env.getActiveObject(object_id);
765 v3f pos = cao->getPosition();
766 m_sound->updateSoundPosition(client_id, pos);
771 Handle removed remotely initiated sounds
773 m_removed_sounds_check_timer += dtime;
774 if(m_removed_sounds_check_timer >= 2.32)
776 m_removed_sounds_check_timer = 0;
777 // Find removed sounds and clear references to them
778 std::set<s32> removed_server_ids;
779 for(std::map<s32, int>::iterator
780 i = m_sounds_server_to_client.begin();
781 i != m_sounds_server_to_client.end();)
783 s32 server_id = i->first;
784 int client_id = i->second;
786 if(!m_sound->soundExists(client_id)){
787 m_sounds_server_to_client.erase(server_id);
788 m_sounds_client_to_server.erase(client_id);
789 m_sounds_to_objects.erase(client_id);
790 removed_server_ids.insert(server_id);
794 if(removed_server_ids.size() != 0)
796 std::ostringstream os(std::ios_base::binary);
797 writeU16(os, TOSERVER_REMOVED_SOUNDS);
798 writeU16(os, removed_server_ids.size());
799 for(std::set<s32>::iterator i = removed_server_ids.begin();
800 i != removed_server_ids.end(); i++)
802 std::string s = os.str();
803 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
810 bool Client::loadMedia(const std::string &data, const std::string &filename)
812 // Silly irrlicht's const-incorrectness
813 Buffer<char> data_rw(data.c_str(), data.size());
817 const char *image_ext[] = {
818 ".png", ".jpg", ".bmp", ".tga",
819 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
822 name = removeStringEnd(filename, image_ext);
825 verbosestream<<"Client: Attempting to load image "
826 <<"file \""<<filename<<"\""<<std::endl;
828 io::IFileSystem *irrfs = m_device->getFileSystem();
829 video::IVideoDriver *vdrv = m_device->getVideoDriver();
831 // Create an irrlicht memory file
832 io::IReadFile *rfile = irrfs->createMemoryReadFile(
833 *data_rw, data_rw.getSize(), "_tempreadfile");
836 video::IImage *img = vdrv->createImageFromFile(rfile);
838 errorstream<<"Client: Cannot create image from data of "
839 <<"file \""<<filename<<"\""<<std::endl;
844 m_tsrc->insertSourceImage(filename, img);
851 const char *sound_ext[] = {
852 ".0.ogg", ".1.ogg", ".2.ogg", ".3.ogg", ".4.ogg",
853 ".5.ogg", ".6.ogg", ".7.ogg", ".8.ogg", ".9.ogg",
856 name = removeStringEnd(filename, sound_ext);
859 verbosestream<<"Client: Attempting to load sound "
860 <<"file \""<<filename<<"\""<<std::endl;
861 m_sound->loadSoundData(name, data);
865 const char *model_ext[] = {
866 ".x", ".b3d", ".md2", ".obj",
869 name = removeStringEnd(filename, model_ext);
872 verbosestream<<"Client: Storing model into Irrlicht: "
873 <<"\""<<filename<<"\""<<std::endl;
875 io::IFileSystem *irrfs = m_device->getFileSystem();
876 io::IReadFile *rfile = irrfs->createMemoryReadFile(
877 *data_rw, data_rw.getSize(), filename.c_str());
880 scene::ISceneManager *smgr = m_device->getSceneManager();
881 scene::IAnimatedMesh *mesh = smgr->getMesh(rfile);
882 smgr->getMeshCache()->addMesh(filename.c_str(), mesh);
887 errorstream<<"Client: Don't know how to load file \""
888 <<filename<<"\""<<std::endl;
892 // Virtual methods from con::PeerHandler
893 void Client::peerAdded(con::Peer *peer)
895 infostream<<"Client::peerAdded(): peer->id="
896 <<peer->id<<std::endl;
898 void Client::deletingPeer(con::Peer *peer, bool timeout)
900 infostream<<"Client::deletingPeer(): "
901 "Server Peer is getting deleted "
902 <<"(timeout="<<timeout<<")"<<std::endl;
905 void Client::ReceiveAll()
907 DSTACK(__FUNCTION_NAME);
908 u32 start_ms = porting::getTimeMs();
911 // Limit time even if there would be huge amounts of data to
913 if(porting::getTimeMs() > start_ms + 100)
918 g_profiler->graphAdd("client_received_packets", 1);
920 catch(con::NoIncomingDataException &e)
924 catch(con::InvalidIncomingDataException &e)
926 infostream<<"Client::ReceiveAll(): "
927 "InvalidIncomingDataException: what()="
928 <<e.what()<<std::endl;
933 void Client::Receive()
935 DSTACK(__FUNCTION_NAME);
936 SharedBuffer<u8> data;
940 //TimeTaker t1("con mutex and receive", m_device);
941 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
942 datasize = m_con.Receive(sender_peer_id, data);
944 //TimeTaker t1("ProcessData", m_device);
945 ProcessData(*data, datasize, sender_peer_id);
949 sender_peer_id given to this shall be quaranteed to be a valid peer
951 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
953 DSTACK(__FUNCTION_NAME);
955 // Ignore packets that don't even fit a command
958 m_packetcounter.add(60000);
962 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
964 //infostream<<"Client: received command="<<command<<std::endl;
965 m_packetcounter.add((u16)command);
968 If this check is removed, be sure to change the queue
969 system to know the ids
971 if(sender_peer_id != PEER_ID_SERVER)
973 infostream<<"Client::ProcessData(): Discarding data not "
974 "coming from server: peer_id="<<sender_peer_id
979 u8 ser_version = m_server_ser_ver;
981 //infostream<<"Client received command="<<(int)command<<std::endl;
983 if(command == TOCLIENT_INIT)
988 u8 deployed = data[2];
990 infostream<<"Client: TOCLIENT_INIT received with "
991 "deployed="<<((int)deployed&0xff)<<std::endl;
993 if(deployed < SER_FMT_VER_LOWEST
994 || deployed > SER_FMT_VER_HIGHEST)
996 infostream<<"Client: TOCLIENT_INIT: Server sent "
997 <<"unsupported ser_fmt_ver"<<std::endl;
1001 m_server_ser_ver = deployed;
1003 // Get player position
1004 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
1005 if(datasize >= 2+1+6)
1006 playerpos_s16 = readV3S16(&data[2+1]);
1007 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
1010 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1012 // Set player position
1013 Player *player = m_env.getLocalPlayer();
1014 assert(player != NULL);
1015 player->setPosition(playerpos_f);
1018 if(datasize >= 2+1+6+8)
1021 m_map_seed = readU64(&data[2+1+6]);
1022 infostream<<"Client: received map seed: "<<m_map_seed<<std::endl;
1027 SharedBuffer<u8> reply(replysize);
1028 writeU16(&reply[0], TOSERVER_INIT2);
1030 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1035 if(command == TOCLIENT_ACCESS_DENIED)
1037 // The server didn't like our password. Note, this needs
1038 // to be processed even if the serialisation format has
1039 // not been agreed yet, the same as TOCLIENT_INIT.
1040 m_access_denied = true;
1041 m_access_denied_reason = L"Unknown";
1044 std::string datastring((char*)&data[2], datasize-2);
1045 std::istringstream is(datastring, std::ios_base::binary);
1046 m_access_denied_reason = deSerializeWideString(is);
1051 if(ser_version == SER_FMT_VER_INVALID)
1053 infostream<<"Client: Server serialization"
1054 " format invalid or not initialized."
1055 " Skipping incoming command="<<command<<std::endl;
1059 // Just here to avoid putting the two if's together when
1060 // making some copypasta
1063 if(command == TOCLIENT_REMOVENODE)
1068 p.X = readS16(&data[2]);
1069 p.Y = readS16(&data[4]);
1070 p.Z = readS16(&data[6]);
1072 //TimeTaker t1("TOCLIENT_REMOVENODE");
1076 else if(command == TOCLIENT_ADDNODE)
1078 if(datasize < 8 + MapNode::serializedLength(ser_version))
1082 p.X = readS16(&data[2]);
1083 p.Y = readS16(&data[4]);
1084 p.Z = readS16(&data[6]);
1086 //TimeTaker t1("TOCLIENT_ADDNODE");
1089 n.deSerialize(&data[8], ser_version);
1093 else if(command == TOCLIENT_BLOCKDATA)
1095 // Ignore too small packet
1100 p.X = readS16(&data[2]);
1101 p.Y = readS16(&data[4]);
1102 p.Z = readS16(&data[6]);
1104 /*infostream<<"Client: Thread: BLOCKDATA for ("
1105 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1106 /*infostream<<"Client: Thread: BLOCKDATA for ("
1107 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1109 std::string datastring((char*)&data[8], datasize-8);
1110 std::istringstream istr(datastring, std::ios_base::binary);
1115 v2s16 p2d(p.X, p.Z);
1116 sector = m_env.getMap().emergeSector(p2d);
1118 assert(sector->getPos() == p2d);
1120 //TimeTaker timer("MapBlock deSerialize");
1123 block = sector->getBlockNoCreateNoEx(p.Y);
1127 Update an existing block
1129 //infostream<<"Updating"<<std::endl;
1130 block->deSerialize(istr, ser_version, false);
1137 //infostream<<"Creating new"<<std::endl;
1138 block = new MapBlock(&m_env.getMap(), p, this);
1139 block->deSerialize(istr, ser_version, false);
1140 sector->insertBlock(block);
1154 u32 replysize = 2+1+6;
1155 SharedBuffer<u8> reply(replysize);
1156 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
1158 writeV3S16(&reply[3], p);
1160 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1164 Add it to mesh update queue and set it to be acknowledged after update.
1166 //infostream<<"Adding mesh update task for received block"<<std::endl;
1167 addUpdateMeshTaskWithEdge(p, true);
1169 else if(command == TOCLIENT_INVENTORY)
1174 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
1177 //TimeTaker t2("mutex locking", m_device);
1178 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1181 //TimeTaker t3("istringstream init", m_device);
1182 std::string datastring((char*)&data[2], datasize-2);
1183 std::istringstream is(datastring, std::ios_base::binary);
1186 //m_env.printPlayers(infostream);
1188 //TimeTaker t4("player get", m_device);
1189 Player *player = m_env.getLocalPlayer();
1190 assert(player != NULL);
1193 //TimeTaker t1("inventory.deSerialize()", m_device);
1194 player->inventory.deSerialize(is);
1197 m_inventory_updated = true;
1199 delete m_inventory_from_server;
1200 m_inventory_from_server = new Inventory(player->inventory);
1201 m_inventory_from_server_age = 0.0;
1203 //infostream<<"Client got player inventory:"<<std::endl;
1204 //player->inventory.print(infostream);
1207 else if(command == TOCLIENT_TIME_OF_DAY)
1212 u16 time_of_day = readU16(&data[2]);
1213 time_of_day = time_of_day % 24000;
1214 //infostream<<"Client: time_of_day="<<time_of_day<<std::endl;
1215 float time_speed = 0;
1216 if(datasize >= 2 + 2 + 4){
1217 time_speed = readF1000(&data[4]);
1219 // Old message; try to approximate speed of time by ourselves
1220 float time_of_day_f = (float)time_of_day / 24000.0;
1221 float tod_diff_f = 0;
1222 if(time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
1223 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0;
1225 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
1226 m_last_time_of_day_f = time_of_day_f;
1227 float time_diff = m_time_of_day_update_timer;
1228 m_time_of_day_update_timer = 0;
1229 if(m_time_of_day_set){
1230 time_speed = 3600.0*24.0 * tod_diff_f / time_diff;
1231 infostream<<"Client: Measured time_of_day speed (old format): "
1232 <<time_speed<<" tod_diff_f="<<tod_diff_f
1233 <<" time_diff="<<time_diff<<std::endl;
1237 // Update environment
1238 m_env.setTimeOfDay(time_of_day);
1239 m_env.setTimeOfDaySpeed(time_speed);
1240 m_time_of_day_set = true;
1242 u32 dr = m_env.getDayNightRatio();
1243 verbosestream<<"Client: time_of_day="<<time_of_day
1244 <<" time_speed="<<time_speed
1245 <<" dr="<<dr<<std::endl;
1247 else if(command == TOCLIENT_CHAT_MESSAGE)
1255 std::string datastring((char*)&data[2], datasize-2);
1256 std::istringstream is(datastring, std::ios_base::binary);
1259 is.read((char*)buf, 2);
1260 u16 len = readU16(buf);
1262 std::wstring message;
1263 for(u16 i=0; i<len; i++)
1265 is.read((char*)buf, 2);
1266 message += (wchar_t)readU16(buf);
1269 /*infostream<<"Client received chat message: "
1270 <<wide_to_narrow(message)<<std::endl;*/
1272 m_chat_queue.push_back(message);
1274 else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1276 //if(g_settings->getBool("enable_experimental"))
1280 u16 count of removed objects
1281 for all removed objects {
1284 u16 count of added objects
1285 for all added objects {
1288 u32 initialization data length
1289 string initialization data
1294 // Get all data except the command number
1295 std::string datastring((char*)&data[2], datasize-2);
1296 // Throw them in an istringstream
1297 std::istringstream is(datastring, std::ios_base::binary);
1301 // Read removed objects
1303 u16 removed_count = readU16((u8*)buf);
1304 for(u16 i=0; i<removed_count; i++)
1307 u16 id = readU16((u8*)buf);
1310 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1311 m_env.removeActiveObject(id);
1315 // Read added objects
1317 u16 added_count = readU16((u8*)buf);
1318 for(u16 i=0; i<added_count; i++)
1321 u16 id = readU16((u8*)buf);
1323 u8 type = readU8((u8*)buf);
1324 std::string data = deSerializeLongString(is);
1327 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1328 m_env.addActiveObject(id, type, data);
1333 else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1335 //if(g_settings->getBool("enable_experimental"))
1347 // Get all data except the command number
1348 std::string datastring((char*)&data[2], datasize-2);
1349 // Throw them in an istringstream
1350 std::istringstream is(datastring, std::ios_base::binary);
1352 while(is.eof() == false)
1356 u16 id = readU16((u8*)buf);
1360 u16 message_size = readU16((u8*)buf);
1361 std::string message;
1362 message.reserve(message_size);
1363 for(u16 i=0; i<message_size; i++)
1366 message.append(buf, 1);
1368 // Pass on to the environment
1370 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1371 m_env.processActiveObjectMessage(id, message);
1376 else if(command == TOCLIENT_HP)
1378 std::string datastring((char*)&data[2], datasize-2);
1379 std::istringstream is(datastring, std::ios_base::binary);
1380 Player *player = m_env.getLocalPlayer();
1381 assert(player != NULL);
1382 u8 oldhp = player->hp;
1388 // Add to ClientEvent queue
1390 event.type = CE_PLAYER_DAMAGE;
1391 event.player_damage.amount = oldhp - hp;
1392 m_client_event_queue.push_back(event);
1395 else if(command == TOCLIENT_MOVE_PLAYER)
1397 std::string datastring((char*)&data[2], datasize-2);
1398 std::istringstream is(datastring, std::ios_base::binary);
1399 Player *player = m_env.getLocalPlayer();
1400 assert(player != NULL);
1401 v3f pos = readV3F1000(is);
1402 f32 pitch = readF1000(is);
1403 f32 yaw = readF1000(is);
1404 player->setPosition(pos);
1405 /*player->setPitch(pitch);
1406 player->setYaw(yaw);*/
1408 infostream<<"Client got TOCLIENT_MOVE_PLAYER"
1409 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1415 Add to ClientEvent queue.
1416 This has to be sent to the main program because otherwise
1417 it would just force the pitch and yaw values to whatever
1418 the camera points to.
1421 event.type = CE_PLAYER_FORCE_MOVE;
1422 event.player_force_move.pitch = pitch;
1423 event.player_force_move.yaw = yaw;
1424 m_client_event_queue.push_back(event);
1426 // Ignore damage for a few seconds, so that the player doesn't
1427 // get damage from falling on ground
1428 m_ignore_damage_timer = 3.0;
1430 else if(command == TOCLIENT_PLAYERITEM)
1432 infostream<<"Client: WARNING: Ignoring TOCLIENT_PLAYERITEM"<<std::endl;
1434 else if(command == TOCLIENT_DEATHSCREEN)
1436 std::string datastring((char*)&data[2], datasize-2);
1437 std::istringstream is(datastring, std::ios_base::binary);
1439 bool set_camera_point_target = readU8(is);
1440 v3f camera_point_target = readV3F1000(is);
1443 event.type = CE_DEATHSCREEN;
1444 event.deathscreen.set_camera_point_target = set_camera_point_target;
1445 event.deathscreen.camera_point_target_x = camera_point_target.X;
1446 event.deathscreen.camera_point_target_y = camera_point_target.Y;
1447 event.deathscreen.camera_point_target_z = camera_point_target.Z;
1448 m_client_event_queue.push_back(event);
1450 else if(command == TOCLIENT_ANNOUNCE_MEDIA)
1452 std::string datastring((char*)&data[2], datasize-2);
1453 std::istringstream is(datastring, std::ios_base::binary);
1455 // Mesh update thread must be stopped while
1456 // updating content definitions
1457 assert(!m_mesh_update_thread.IsRunning());
1459 int num_files = readU16(is);
1461 verbosestream<<"Client received TOCLIENT_ANNOUNCE_MEDIA ("
1462 <<num_files<<" files)"<<std::endl;
1464 core::list<MediaRequest> file_requests;
1466 for(int i=0; i<num_files; i++)
1468 //read file from cache
1469 std::string name = deSerializeString(is);
1470 std::string sha1_base64 = deSerializeString(is);
1472 // if name contains illegal characters, ignore the file
1473 if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
1474 errorstream<<"Client: ignoring illegal file name "
1475 <<"sent by server: \""<<name<<"\""<<std::endl;
1479 std::string sha1_raw = base64_decode(sha1_base64);
1480 std::string sha1_hex = hex_encode(sha1_raw);
1481 std::ostringstream tmp_os(std::ios_base::binary);
1482 bool found_in_cache = m_media_cache.load_sha1(sha1_raw, tmp_os);
1483 m_media_name_sha1_map.set(name, sha1_raw);
1485 // If found in cache, try to load it from there
1488 bool success = loadMedia(tmp_os.str(), name);
1490 verbosestream<<"Client: Loaded cached media: "
1491 <<sha1_hex<<" \""<<name<<"\""<<std::endl;
1494 infostream<<"Client: Failed to load cached media: "
1495 <<sha1_hex<<" \""<<name<<"\""<<std::endl;
1498 // Didn't load from cache; queue it to be requested
1499 verbosestream<<"Client: Adding file to request list: \""
1500 <<sha1_hex<<" \""<<name<<"\""<<std::endl;
1501 file_requests.push_back(MediaRequest(name));
1505 event.type = CE_TEXTURES_UPDATED;
1506 m_client_event_queue.push_back(event);
1510 u16 number of files requested
1516 std::ostringstream os(std::ios_base::binary);
1517 writeU16(os, TOSERVER_REQUEST_MEDIA);
1518 writeU16(os, file_requests.size());
1520 for(core::list<MediaRequest>::Iterator i = file_requests.begin();
1521 i != file_requests.end(); i++) {
1522 os<<serializeString(i->name);
1526 std::string s = os.str();
1527 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1529 Send(0, data, true);
1530 infostream<<"Client: Sending media request list to server ("
1531 <<file_requests.size()<<" files)"<<std::endl;
1533 else if(command == TOCLIENT_MEDIA)
1535 std::string datastring((char*)&data[2], datasize-2);
1536 std::istringstream is(datastring, std::ios_base::binary);
1538 // Mesh update thread must be stopped while
1539 // updating content definitions
1540 assert(!m_mesh_update_thread.IsRunning());
1544 u16 total number of file bunches
1545 u16 index of this bunch
1546 u32 number of files in this bunch
1554 int num_bunches = readU16(is);
1555 int bunch_i = readU16(is);
1556 if(num_bunches >= 2)
1557 m_media_receive_progress = (float)bunch_i / (float)(num_bunches - 1);
1559 m_media_receive_progress = 1.0;
1560 if(bunch_i == num_bunches - 1)
1561 m_media_received = true;
1562 int num_files = readU32(is);
1563 infostream<<"Client: Received files: bunch "<<bunch_i<<"/"
1564 <<num_bunches<<" files="<<num_files
1565 <<" size="<<datasize<<std::endl;
1566 for(int i=0; i<num_files; i++){
1567 std::string name = deSerializeString(is);
1568 std::string data = deSerializeLongString(is);
1570 // if name contains illegal characters, ignore the file
1571 if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
1572 errorstream<<"Client: ignoring illegal file name "
1573 <<"sent by server: \""<<name<<"\""<<std::endl;
1577 bool success = loadMedia(data, name);
1579 verbosestream<<"Client: Loaded received media: "
1580 <<"\""<<name<<"\". Caching."<<std::endl;
1582 infostream<<"Client: Failed to load received media: "
1583 <<"\""<<name<<"\". Not caching."<<std::endl;
1587 bool did = fs::CreateAllDirs(getMediaCacheDir());
1589 errorstream<<"Could not create media cache directory"
1594 core::map<std::string, std::string>::Node *n;
1595 n = m_media_name_sha1_map.find(name);
1597 errorstream<<"The server sent a file that has not "
1598 <<"been announced."<<std::endl;
1600 m_media_cache.update_sha1(data);
1605 event.type = CE_TEXTURES_UPDATED;
1606 m_client_event_queue.push_back(event);
1608 else if(command == TOCLIENT_TOOLDEF)
1610 infostream<<"Client: WARNING: Ignoring TOCLIENT_TOOLDEF"<<std::endl;
1612 else if(command == TOCLIENT_NODEDEF)
1614 infostream<<"Client: Received node definitions: packet size: "
1615 <<datasize<<std::endl;
1617 // Mesh update thread must be stopped while
1618 // updating content definitions
1619 assert(!m_mesh_update_thread.IsRunning());
1621 // Decompress node definitions
1622 std::string datastring((char*)&data[2], datasize-2);
1623 std::istringstream is(datastring, std::ios_base::binary);
1624 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1625 std::ostringstream tmp_os;
1626 decompressZlib(tmp_is, tmp_os);
1628 // Deserialize node definitions
1629 std::istringstream tmp_is2(tmp_os.str());
1630 m_nodedef->deSerialize(tmp_is2);
1631 m_nodedef_received = true;
1633 else if(command == TOCLIENT_CRAFTITEMDEF)
1635 infostream<<"Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF"<<std::endl;
1637 else if(command == TOCLIENT_ITEMDEF)
1639 infostream<<"Client: Received item definitions: packet size: "
1640 <<datasize<<std::endl;
1642 // Mesh update thread must be stopped while
1643 // updating content definitions
1644 assert(!m_mesh_update_thread.IsRunning());
1646 // Decompress item definitions
1647 std::string datastring((char*)&data[2], datasize-2);
1648 std::istringstream is(datastring, std::ios_base::binary);
1649 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1650 std::ostringstream tmp_os;
1651 decompressZlib(tmp_is, tmp_os);
1653 // Deserialize node definitions
1654 std::istringstream tmp_is2(tmp_os.str());
1655 m_itemdef->deSerialize(tmp_is2);
1656 m_itemdef_received = true;
1658 else if(command == TOCLIENT_PLAY_SOUND)
1660 std::string datastring((char*)&data[2], datasize-2);
1661 std::istringstream is(datastring, std::ios_base::binary);
1663 s32 server_id = readS32(is);
1664 std::string name = deSerializeString(is);
1665 float gain = readF1000(is);
1666 int type = readU8(is); // 0=local, 1=positional, 2=object
1667 v3f pos = readV3F1000(is);
1668 u16 object_id = readU16(is);
1669 bool loop = readU8(is);
1674 client_id = m_sound->playSound(name, loop, gain);
1676 case 1: // positional
1677 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1680 ClientActiveObject *cao = m_env.getActiveObject(object_id);
1682 pos = cao->getPosition();
1683 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1684 // TODO: Set up sound to move with object
1689 if(client_id != -1){
1690 m_sounds_server_to_client[server_id] = client_id;
1691 m_sounds_client_to_server[client_id] = server_id;
1693 m_sounds_to_objects[client_id] = object_id;
1696 else if(command == TOCLIENT_STOP_SOUND)
1698 std::string datastring((char*)&data[2], datasize-2);
1699 std::istringstream is(datastring, std::ios_base::binary);
1701 s32 server_id = readS32(is);
1702 std::map<s32, int>::iterator i =
1703 m_sounds_server_to_client.find(server_id);
1704 if(i != m_sounds_server_to_client.end()){
1705 int client_id = i->second;
1706 m_sound->stopSound(client_id);
1709 else if(command == TOCLIENT_PRIVILEGES)
1711 std::string datastring((char*)&data[2], datasize-2);
1712 std::istringstream is(datastring, std::ios_base::binary);
1714 m_privileges.clear();
1715 infostream<<"Client: Privileges updated: ";
1716 u16 num_privileges = readU16(is);
1717 for(u16 i=0; i<num_privileges; i++){
1718 std::string priv = deSerializeString(is);
1719 m_privileges.insert(priv);
1720 infostream<<priv<<" ";
1722 infostream<<std::endl;
1724 else if(command == TOCLIENT_INVENTORY_FORMSPEC)
1726 std::string datastring((char*)&data[2], datasize-2);
1727 std::istringstream is(datastring, std::ios_base::binary);
1729 // Store formspec in LocalPlayer
1730 Player *player = m_env.getLocalPlayer();
1731 assert(player != NULL);
1732 player->inventory_formspec = deSerializeLongString(is);
1734 else if(command == TOCLIENT_DETACHED_INVENTORY)
1736 std::string datastring((char*)&data[2], datasize-2);
1737 std::istringstream is(datastring, std::ios_base::binary);
1739 std::string name = deSerializeString(is);
1741 infostream<<"Client: Detached inventory update: \""<<name<<"\""<<std::endl;
1743 Inventory *inv = NULL;
1744 if(m_detached_inventories.count(name) > 0)
1745 inv = m_detached_inventories[name];
1747 inv = new Inventory(m_itemdef);
1748 m_detached_inventories[name] = inv;
1750 inv->deSerialize(is);
1754 infostream<<"Client: Ignoring unknown command "
1755 <<command<<std::endl;
1759 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
1761 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1762 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
1765 void Client::interact(u8 action, const PointedThing& pointed)
1767 if(connectedAndInitialized() == false){
1768 infostream<<"Client::interact() "
1769 "cancelled (not connected)"
1774 std::ostringstream os(std::ios_base::binary);
1780 [5] u32 length of the next item
1781 [9] serialized PointedThing
1783 0: start digging (from undersurface) or use
1784 1: stop digging (all parameters ignored)
1785 2: digging completed
1786 3: place block or item (to abovesurface)
1789 writeU16(os, TOSERVER_INTERACT);
1790 writeU8(os, action);
1791 writeU16(os, getPlayerItem());
1792 std::ostringstream tmp_os(std::ios::binary);
1793 pointed.serialize(tmp_os);
1794 os<<serializeLongString(tmp_os.str());
1796 std::string s = os.str();
1797 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1800 Send(0, data, true);
1803 void Client::sendNodemetaFields(v3s16 p, const std::string &formname,
1804 const std::map<std::string, std::string> &fields)
1806 std::ostringstream os(std::ios_base::binary);
1808 writeU16(os, TOSERVER_NODEMETA_FIELDS);
1810 os<<serializeString(formname);
1811 writeU16(os, fields.size());
1812 for(std::map<std::string, std::string>::const_iterator
1813 i = fields.begin(); i != fields.end(); i++){
1814 const std::string &name = i->first;
1815 const std::string &value = i->second;
1816 os<<serializeString(name);
1817 os<<serializeLongString(value);
1821 std::string s = os.str();
1822 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1824 Send(0, data, true);
1827 void Client::sendInventoryFields(const std::string &formname,
1828 const std::map<std::string, std::string> &fields)
1830 std::ostringstream os(std::ios_base::binary);
1832 writeU16(os, TOSERVER_INVENTORY_FIELDS);
1833 os<<serializeString(formname);
1834 writeU16(os, fields.size());
1835 for(std::map<std::string, std::string>::const_iterator
1836 i = fields.begin(); i != fields.end(); i++){
1837 const std::string &name = i->first;
1838 const std::string &value = i->second;
1839 os<<serializeString(name);
1840 os<<serializeLongString(value);
1844 std::string s = os.str();
1845 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1847 Send(0, data, true);
1850 void Client::sendInventoryAction(InventoryAction *a)
1852 std::ostringstream os(std::ios_base::binary);
1856 writeU16(buf, TOSERVER_INVENTORY_ACTION);
1857 os.write((char*)buf, 2);
1862 std::string s = os.str();
1863 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1865 Send(0, data, true);
1868 void Client::sendChatMessage(const std::wstring &message)
1870 std::ostringstream os(std::ios_base::binary);
1874 writeU16(buf, TOSERVER_CHAT_MESSAGE);
1875 os.write((char*)buf, 2);
1878 writeU16(buf, message.size());
1879 os.write((char*)buf, 2);
1882 for(u32 i=0; i<message.size(); i++)
1886 os.write((char*)buf, 2);
1890 std::string s = os.str();
1891 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1893 Send(0, data, true);
1896 void Client::sendChangePassword(const std::wstring oldpassword,
1897 const std::wstring newpassword)
1899 Player *player = m_env.getLocalPlayer();
1903 std::string playername = player->getName();
1904 std::string oldpwd = translatePassword(playername, oldpassword);
1905 std::string newpwd = translatePassword(playername, newpassword);
1907 std::ostringstream os(std::ios_base::binary);
1908 u8 buf[2+PASSWORD_SIZE*2];
1910 [0] u16 TOSERVER_PASSWORD
1911 [2] u8[28] old password
1912 [30] u8[28] new password
1915 writeU16(buf, TOSERVER_PASSWORD);
1916 for(u32 i=0;i<PASSWORD_SIZE-1;i++)
1918 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
1919 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
1921 buf[2+PASSWORD_SIZE-1] = 0;
1922 buf[30+PASSWORD_SIZE-1] = 0;
1923 os.write((char*)buf, 2+PASSWORD_SIZE*2);
1926 std::string s = os.str();
1927 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1929 Send(0, data, true);
1933 void Client::sendDamage(u8 damage)
1935 DSTACK(__FUNCTION_NAME);
1936 std::ostringstream os(std::ios_base::binary);
1938 writeU16(os, TOSERVER_DAMAGE);
1939 writeU8(os, damage);
1942 std::string s = os.str();
1943 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1945 Send(0, data, true);
1948 void Client::sendRespawn()
1950 DSTACK(__FUNCTION_NAME);
1951 std::ostringstream os(std::ios_base::binary);
1953 writeU16(os, TOSERVER_RESPAWN);
1956 std::string s = os.str();
1957 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1959 Send(0, data, true);
1962 void Client::sendPlayerPos()
1964 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1966 Player *myplayer = m_env.getLocalPlayer();
1967 if(myplayer == NULL)
1972 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1973 our_peer_id = m_con.GetPeerID();
1976 // Set peer id if not set already
1977 if(myplayer->peer_id == PEER_ID_INEXISTENT)
1978 myplayer->peer_id = our_peer_id;
1979 // Check that an existing peer_id is the same as the connection's
1980 assert(myplayer->peer_id == our_peer_id);
1982 v3f pf = myplayer->getPosition();
1983 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
1984 v3f sf = myplayer->getSpeed();
1985 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
1986 s32 pitch = myplayer->getPitch() * 100;
1987 s32 yaw = myplayer->getYaw() * 100;
1988 u32 keyPressed=myplayer->keyPressed;
1992 [2] v3s32 position*100
1993 [2+12] v3s32 speed*100
1994 [2+12+12] s32 pitch*100
1995 [2+12+12+4] s32 yaw*100
1996 [2+12+12+4+4] u32 keyPressed
1998 SharedBuffer<u8> data(2+12+12+4+4+4);
1999 writeU16(&data[0], TOSERVER_PLAYERPOS);
2000 writeV3S32(&data[2], position);
2001 writeV3S32(&data[2+12], speed);
2002 writeS32(&data[2+12+12], pitch);
2003 writeS32(&data[2+12+12+4], yaw);
2004 writeU32(&data[2+12+12+4+4], keyPressed);
2005 // Send as unreliable
2006 Send(0, data, false);
2009 void Client::sendPlayerItem(u16 item)
2011 Player *myplayer = m_env.getLocalPlayer();
2012 if(myplayer == NULL)
2015 u16 our_peer_id = m_con.GetPeerID();
2017 // Set peer id if not set already
2018 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2019 myplayer->peer_id = our_peer_id;
2020 // Check that an existing peer_id is the same as the connection's
2021 assert(myplayer->peer_id == our_peer_id);
2023 SharedBuffer<u8> data(2+2);
2024 writeU16(&data[0], TOSERVER_PLAYERITEM);
2025 writeU16(&data[2], item);
2028 Send(0, data, true);
2031 void Client::removeNode(v3s16 p)
2033 core::map<v3s16, MapBlock*> modified_blocks;
2037 //TimeTaker t("removeNodeAndUpdate", m_device);
2038 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
2040 catch(InvalidPositionException &e)
2044 // add urgent task to update the modified node
2045 addUpdateMeshTaskForNode(p, false, true);
2047 for(core::map<v3s16, MapBlock * >::Iterator
2048 i = modified_blocks.getIterator();
2049 i.atEnd() == false; i++)
2051 v3s16 p = i.getNode()->getKey();
2052 addUpdateMeshTaskWithEdge(p);
2056 void Client::addNode(v3s16 p, MapNode n)
2058 TimeTaker timer1("Client::addNode()");
2060 core::map<v3s16, MapBlock*> modified_blocks;
2064 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
2065 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks);
2067 catch(InvalidPositionException &e)
2070 for(core::map<v3s16, MapBlock * >::Iterator
2071 i = modified_blocks.getIterator();
2072 i.atEnd() == false; i++)
2074 v3s16 p = i.getNode()->getKey();
2075 addUpdateMeshTaskWithEdge(p);
2079 void Client::setPlayerControl(PlayerControl &control)
2081 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2082 LocalPlayer *player = m_env.getLocalPlayer();
2083 assert(player != NULL);
2084 player->control = control;
2087 void Client::selectPlayerItem(u16 item)
2089 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2090 m_playeritem = item;
2091 m_inventory_updated = true;
2092 sendPlayerItem(item);
2095 // Returns true if the inventory of the local player has been
2096 // updated from the server. If it is true, it is set to false.
2097 bool Client::getLocalInventoryUpdated()
2099 // m_inventory_updated is behind envlock
2100 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2101 bool updated = m_inventory_updated;
2102 m_inventory_updated = false;
2106 // Copies the inventory of the local player to parameter
2107 void Client::getLocalInventory(Inventory &dst)
2109 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2110 Player *player = m_env.getLocalPlayer();
2111 assert(player != NULL);
2112 dst = player->inventory;
2115 Inventory* Client::getInventory(const InventoryLocation &loc)
2118 case InventoryLocation::UNDEFINED:
2121 case InventoryLocation::CURRENT_PLAYER:
2123 Player *player = m_env.getLocalPlayer();
2124 assert(player != NULL);
2125 return &player->inventory;
2128 case InventoryLocation::PLAYER:
2130 Player *player = m_env.getPlayer(loc.name.c_str());
2133 return &player->inventory;
2136 case InventoryLocation::NODEMETA:
2138 NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p);
2141 return meta->getInventory();
2144 case InventoryLocation::DETACHED:
2146 if(m_detached_inventories.count(loc.name) == 0)
2148 return m_detached_inventories[loc.name];
2156 void Client::inventoryAction(InventoryAction *a)
2159 Send it to the server
2161 sendInventoryAction(a);
2164 Predict some local inventory changes
2166 a->clientApply(this, this);
2169 ClientActiveObject * Client::getSelectedActiveObject(
2171 v3f from_pos_f_on_map,
2172 core::line3d<f32> shootline_on_map
2175 core::array<DistanceSortedActiveObject> objects;
2177 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
2179 //infostream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
2182 // After this, the closest object is the first in the array.
2185 for(u32 i=0; i<objects.size(); i++)
2187 ClientActiveObject *obj = objects[i].obj;
2189 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
2190 if(selection_box == NULL)
2193 v3f pos = obj->getPosition();
2195 core::aabbox3d<f32> offsetted_box(
2196 selection_box->MinEdge + pos,
2197 selection_box->MaxEdge + pos
2200 if(offsetted_box.intersectsWithLine(shootline_on_map))
2202 //infostream<<"Returning selected object"<<std::endl;
2207 //infostream<<"No object selected; returning NULL."<<std::endl;
2211 void Client::printDebugInfo(std::ostream &os)
2213 //JMutexAutoLock lock1(m_fetchblock_mutex);
2214 /*JMutexAutoLock lock2(m_incoming_queue_mutex);
2216 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
2217 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
2218 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
2222 core::list<std::wstring> Client::getConnectedPlayerNames()
2224 core::list<Player*> players = m_env.getPlayers(true);
2225 core::list<std::wstring> playerNames;
2226 for(core::list<Player*>::Iterator
2227 i = players.begin();
2228 i != players.end(); i++)
2230 Player *player = *i;
2231 playerNames.push_back(narrow_to_wide(player->getName()));
2236 float Client::getAnimationTime()
2238 return m_animation_time;
2241 int Client::getCrackLevel()
2243 return m_crack_level;
2246 void Client::setCrack(int level, v3s16 pos)
2248 int old_crack_level = m_crack_level;
2249 v3s16 old_crack_pos = m_crack_pos;
2251 m_crack_level = level;
2254 if(old_crack_level >= 0 && (level < 0 || pos != old_crack_pos))
2257 addUpdateMeshTaskForNode(old_crack_pos, false, true);
2259 if(level >= 0 && (old_crack_level < 0 || pos != old_crack_pos))
2262 addUpdateMeshTaskForNode(pos, false, true);
2268 Player *player = m_env.getLocalPlayer();
2269 assert(player != NULL);
2273 bool Client::getChatMessage(std::wstring &message)
2275 if(m_chat_queue.size() == 0)
2277 message = m_chat_queue.pop_front();
2281 void Client::typeChatMessage(const std::wstring &message)
2283 // Discard empty line
2288 sendChatMessage(message);
2291 if (message[0] == L'/')
2293 m_chat_queue.push_back(
2294 (std::wstring)L"issued command: "+message);
2298 LocalPlayer *player = m_env.getLocalPlayer();
2299 assert(player != NULL);
2300 std::wstring name = narrow_to_wide(player->getName());
2301 m_chat_queue.push_back(
2302 (std::wstring)L"<"+name+L"> "+message);
2306 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
2308 /*infostream<<"Client::addUpdateMeshTask(): "
2309 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2310 <<" ack_to_server="<<ack_to_server
2311 <<" urgent="<<urgent
2314 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2319 Create a task to update the mesh of the block
2322 MeshMakeData *data = new MeshMakeData(this);
2325 //TimeTaker timer("data fill");
2327 // Debug: 1-6ms, avg=2ms
2329 data->setCrack(m_crack_level, m_crack_pos);
2330 data->setSmoothLighting(g_settings->getBool("smooth_lighting"));
2334 //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
2336 // Add task to queue
2337 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server, urgent);
2339 /*infostream<<"Mesh update input queue size is "
2340 <<m_mesh_update_thread.m_queue_in.size()
2344 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent)
2348 infostream<<"Client::addUpdateMeshTaskWithEdge(): "
2349 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2354 v3s16 p = blockpos + v3s16(0,0,0);
2355 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2356 addUpdateMeshTask(p, ack_to_server, urgent);
2358 catch(InvalidPositionException &e){}
2361 v3s16 p = blockpos + v3s16(-1,0,0);
2362 addUpdateMeshTask(p, false, urgent);
2364 catch(InvalidPositionException &e){}
2366 v3s16 p = blockpos + v3s16(0,-1,0);
2367 addUpdateMeshTask(p, false, urgent);
2369 catch(InvalidPositionException &e){}
2371 v3s16 p = blockpos + v3s16(0,0,-1);
2372 addUpdateMeshTask(p, false, urgent);
2374 catch(InvalidPositionException &e){}
2377 void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool urgent)
2381 infostream<<"Client::addUpdateMeshTaskForNode(): "
2382 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2386 v3s16 blockpos = getNodeBlockPos(nodepos);
2387 v3s16 blockpos_relative = blockpos * MAP_BLOCKSIZE;
2390 v3s16 p = blockpos + v3s16(0,0,0);
2391 addUpdateMeshTask(p, ack_to_server, urgent);
2393 catch(InvalidPositionException &e){}
2395 if(nodepos.X == blockpos_relative.X){
2397 v3s16 p = blockpos + v3s16(-1,0,0);
2398 addUpdateMeshTask(p, false, urgent);
2400 catch(InvalidPositionException &e){}
2402 if(nodepos.Y == blockpos_relative.Y){
2404 v3s16 p = blockpos + v3s16(0,-1,0);
2405 addUpdateMeshTask(p, false, urgent);
2407 catch(InvalidPositionException &e){}
2409 if(nodepos.Z == blockpos_relative.Z){
2411 v3s16 p = blockpos + v3s16(0,0,-1);
2412 addUpdateMeshTask(p, false, urgent);
2414 catch(InvalidPositionException &e){}
2418 ClientEvent Client::getClientEvent()
2420 if(m_client_event_queue.size() == 0)
2423 event.type = CE_NONE;
2426 return m_client_event_queue.pop_front();
2429 void Client::afterContentReceived()
2431 verbosestream<<"Client::afterContentReceived() started"<<std::endl;
2432 assert(m_itemdef_received);
2433 assert(m_nodedef_received);
2434 assert(m_media_received);
2436 // remove the information about which checksum each texture
2438 m_media_name_sha1_map.clear();
2440 // Rebuild inherited images and recreate textures
2441 verbosestream<<"Rebuilding images and textures"<<std::endl;
2442 m_tsrc->rebuildImagesAndTextures();
2444 // Update texture atlas
2445 verbosestream<<"Updating texture atlas"<<std::endl;
2446 if(g_settings->getBool("enable_texture_atlas"))
2447 m_tsrc->buildMainAtlas(this);
2449 // Update node aliases
2450 verbosestream<<"Updating node aliases"<<std::endl;
2451 m_nodedef->updateAliases(m_itemdef);
2453 // Update node textures
2454 verbosestream<<"Updating node textures"<<std::endl;
2455 m_nodedef->updateTextures(m_tsrc);
2457 // Update item textures and meshes
2458 verbosestream<<"Updating item textures and meshes"<<std::endl;
2459 m_itemdef->updateTexturesAndMeshes(this);
2461 // Start mesh update thread after setting up content definitions
2462 verbosestream<<"Starting mesh update thread"<<std::endl;
2463 m_mesh_update_thread.Start();
2465 verbosestream<<"Client::afterContentReceived() done"<<std::endl;
2468 float Client::getRTT(void)
2471 return m_con.GetPeerAvgRTT(PEER_ID_SERVER);
2472 } catch(con::PeerNotFoundException &e){
2477 // IGameDef interface
2479 IItemDefManager* Client::getItemDefManager()
2483 INodeDefManager* Client::getNodeDefManager()
2487 ICraftDefManager* Client::getCraftDefManager()
2490 //return m_craftdef;
2492 ITextureSource* Client::getTextureSource()
2496 u16 Client::allocateUnknownNodeId(const std::string &name)
2498 errorstream<<"Client::allocateUnknownNodeId(): "
2499 <<"Client cannot allocate node IDs"<<std::endl;
2501 return CONTENT_IGNORE;
2503 ISoundManager* Client::getSoundManager()
2507 MtEventManager* Client::getEventManager()