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"
45 #include "util/serialize.h"
47 static std::string getMediaCacheDir()
49 return porting::path_user + DIR_DELIM + "cache" + DIR_DELIM + "media";
56 MediaRequest(const std::string &name_=""):
65 QueuedMeshUpdate::QueuedMeshUpdate():
68 ack_block_to_server(false)
72 QueuedMeshUpdate::~QueuedMeshUpdate()
82 MeshUpdateQueue::MeshUpdateQueue()
87 MeshUpdateQueue::~MeshUpdateQueue()
89 JMutexAutoLock lock(m_mutex);
91 for(std::vector<QueuedMeshUpdate*>::iterator
93 i != m_queue.end(); i++)
95 QueuedMeshUpdate *q = *i;
101 peer_id=0 adds with nobody to send to
103 void MeshUpdateQueue::addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_server, bool urgent)
105 DSTACK(__FUNCTION_NAME);
109 JMutexAutoLock lock(m_mutex);
115 Find if block is already in queue.
116 If it is, update the data and quit.
118 for(std::vector<QueuedMeshUpdate*>::iterator
120 i != m_queue.end(); i++)
122 QueuedMeshUpdate *q = *i;
128 if(ack_block_to_server)
129 q->ack_block_to_server = true;
137 QueuedMeshUpdate *q = new QueuedMeshUpdate;
140 q->ack_block_to_server = ack_block_to_server;
141 m_queue.push_back(q);
144 // Returned pointer must be deleted
145 // Returns NULL if queue is empty
146 QueuedMeshUpdate * MeshUpdateQueue::pop()
148 JMutexAutoLock lock(m_mutex);
150 bool must_be_urgent = !m_urgents.empty();
151 for(std::vector<QueuedMeshUpdate*>::iterator
153 i != m_queue.end(); i++)
155 QueuedMeshUpdate *q = *i;
156 if(must_be_urgent && m_urgents.count(q->p) == 0)
159 m_urgents.erase(q->p);
169 void * MeshUpdateThread::Thread()
173 log_register_thread("MeshUpdateThread");
175 DSTACK(__FUNCTION_NAME);
177 BEGIN_DEBUG_EXCEPTION_HANDLER
181 /*// Wait for output queue to flush.
182 // Allow 2 in queue, this makes less frametime jitter.
183 // Umm actually, there is no much difference
184 if(m_queue_out.size() >= 2)
190 QueuedMeshUpdate *q = m_queue_in.pop();
197 ScopeProfiler sp(g_profiler, "Client: Mesh making");
199 MapBlockMesh *mesh_new = new MapBlockMesh(q->data);
200 if(mesh_new->getMesh()->getMeshBufferCount() == 0)
209 r.ack_block_to_server = q->ack_block_to_server;
211 /*infostream<<"MeshUpdateThread: Processed "
212 <<"("<<q->p.X<<","<<q->p.Y<<","<<q->p.Z<<")"
215 m_queue_out.push_back(r);
220 END_DEBUG_EXCEPTION_HANDLER(errorstream)
226 IrrlichtDevice *device,
227 const char *playername,
228 std::string password,
229 MapDrawControl &control,
230 IWritableTextureSource *tsrc,
231 IWritableItemDefManager *itemdef,
232 IWritableNodeDefManager *nodedef,
233 ISoundManager *sound,
234 MtEventManager *event
241 m_mesh_update_thread(this),
243 new ClientMap(this, this, control,
244 device->getSceneManager()->getRootSceneNode(),
245 device->getSceneManager(), 666),
246 device->getSceneManager(),
249 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
251 m_server_ser_ver(SER_FMT_VER_INVALID),
253 m_inventory_updated(false),
254 m_inventory_from_server(NULL),
255 m_inventory_from_server_age(0.0),
260 m_password(password),
261 m_access_denied(false),
262 m_media_cache(getMediaCacheDir()),
263 m_media_receive_progress(0),
264 m_media_received(false),
265 m_itemdef_received(false),
266 m_nodedef_received(false),
267 m_time_of_day_set(false),
268 m_last_time_of_day_f(-1),
269 m_time_of_day_update_timer(0),
270 m_removed_sounds_check_timer(0)
272 m_packetcounter_timer = 0.0;
273 //m_delete_unused_sectors_timer = 0.0;
274 m_connection_reinit_timer = 0.0;
275 m_avg_rtt_timer = 0.0;
276 m_playerpos_send_timer = 0.0;
277 m_ignore_damage_timer = 0.0;
279 // Build main texture atlas, now that the GameDef exists (that is, us)
280 if(g_settings->getBool("enable_texture_atlas"))
281 m_tsrc->buildMainAtlas(this);
283 infostream<<"Not building texture atlas."<<std::endl;
289 Player *player = new LocalPlayer(this);
291 player->updateName(playername);
293 m_env.addPlayer(player);
300 //JMutexAutoLock conlock(m_con_mutex); //bulk comment-out
304 m_mesh_update_thread.setRun(false);
305 while(m_mesh_update_thread.IsRunning())
308 delete m_inventory_from_server;
310 // Delete detached inventories
312 for(std::map<std::string, Inventory*>::iterator
313 i = m_detached_inventories.begin();
314 i != m_detached_inventories.end(); i++){
320 void Client::connect(Address address)
322 DSTACK(__FUNCTION_NAME);
323 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
324 m_con.SetTimeoutMs(0);
325 m_con.Connect(address);
328 bool Client::connectedAndInitialized()
330 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
332 if(m_con.Connected() == false)
335 if(m_server_ser_ver == SER_FMT_VER_INVALID)
341 void Client::step(float dtime)
343 DSTACK(__FUNCTION_NAME);
349 if(m_ignore_damage_timer > dtime)
350 m_ignore_damage_timer -= dtime;
352 m_ignore_damage_timer = 0.0;
354 m_animation_time += dtime;
355 if(m_animation_time > 60.0)
356 m_animation_time -= 60.0;
358 m_time_of_day_update_timer += dtime;
360 //infostream<<"Client steps "<<dtime<<std::endl;
363 //TimeTaker timer("ReceiveAll()", m_device);
369 //TimeTaker timer("m_con_mutex + m_con.RunTimeouts()", m_device);
371 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
372 m_con.RunTimeouts(dtime);
379 float &counter = m_packetcounter_timer;
385 infostream<<"Client packetcounter (20s):"<<std::endl;
386 m_packetcounter.print(infostream);
387 m_packetcounter.clear();
391 // Get connection status
392 bool connected = connectedAndInitialized();
397 Delete unused sectors
399 NOTE: This jams the game for a while because deleting sectors
403 float &counter = m_delete_unused_sectors_timer;
411 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
413 core::list<v3s16> deleted_blocks;
415 float delete_unused_sectors_timeout =
416 g_settings->getFloat("client_delete_unused_sectors_timeout");
418 // Delete sector blocks
419 /*u32 num = m_env.getMap().unloadUnusedData
420 (delete_unused_sectors_timeout,
421 true, &deleted_blocks);*/
423 // Delete whole sectors
424 m_env.getMap().unloadUnusedData
425 (delete_unused_sectors_timeout,
428 if(deleted_blocks.size() > 0)
430 /*infostream<<"Client: Deleted blocks of "<<num
431 <<" unused sectors"<<std::endl;*/
432 /*infostream<<"Client: Deleted "<<num
433 <<" unused sectors"<<std::endl;*/
439 // Env is locked so con can be locked.
440 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
442 core::list<v3s16>::Iterator i = deleted_blocks.begin();
443 core::list<v3s16> sendlist;
446 if(sendlist.size() == 255 || i == deleted_blocks.end())
448 if(sendlist.size() == 0)
457 u32 replysize = 2+1+6*sendlist.size();
458 SharedBuffer<u8> reply(replysize);
459 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
460 reply[2] = sendlist.size();
462 for(core::list<v3s16>::Iterator
463 j = sendlist.begin();
464 j != sendlist.end(); j++)
466 writeV3S16(&reply[2+1+6*k], *j);
469 m_con.Send(PEER_ID_SERVER, 1, reply, true);
471 if(i == deleted_blocks.end())
477 sendlist.push_back(*i);
485 if(connected == false)
487 float &counter = m_connection_reinit_timer;
493 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
495 Player *myplayer = m_env.getLocalPlayer();
496 assert(myplayer != NULL);
498 // Send TOSERVER_INIT
499 // [0] u16 TOSERVER_INIT
500 // [2] u8 SER_FMT_VER_HIGHEST
501 // [3] u8[20] player_name
502 // [23] u8[28] password (new in some version)
503 // [51] u16 minimum supported network protocol version (added sometime)
504 // [53] u16 maximum supported network protocol version (added later than the previous one)
505 SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2+2);
506 writeU16(&data[0], TOSERVER_INIT);
507 writeU8(&data[2], SER_FMT_VER_HIGHEST);
509 memset((char*)&data[3], 0, PLAYERNAME_SIZE);
510 snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
512 /*infostream<<"Client: sending initial password hash: \""<<m_password<<"\""
515 memset((char*)&data[23], 0, PASSWORD_SIZE);
516 snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
518 writeU16(&data[51], CLIENT_PROTOCOL_VERSION_MIN);
519 writeU16(&data[53], CLIENT_PROTOCOL_VERSION_MAX);
521 // Send as unreliable
522 Send(0, data, false);
525 // Not connected, return
530 Do stuff if connected
534 Run Map's timers and unload unused data
536 const float map_timer_and_unload_dtime = 5.25;
537 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
539 ScopeProfiler sp(g_profiler, "Client: map timer and unload");
540 core::list<v3s16> deleted_blocks;
541 m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
542 g_settings->getFloat("client_unload_unused_data_timeout"),
545 /*if(deleted_blocks.size() > 0)
546 infostream<<"Client: Unloaded "<<deleted_blocks.size()
547 <<" unused blocks"<<std::endl;*/
551 NOTE: This loop is intentionally iterated the way it is.
554 core::list<v3s16>::Iterator i = deleted_blocks.begin();
555 core::list<v3s16> sendlist;
558 if(sendlist.size() == 255 || i == deleted_blocks.end())
560 if(sendlist.size() == 0)
569 u32 replysize = 2+1+6*sendlist.size();
570 SharedBuffer<u8> reply(replysize);
571 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
572 reply[2] = sendlist.size();
574 for(core::list<v3s16>::Iterator
575 j = sendlist.begin();
576 j != sendlist.end(); j++)
578 writeV3S16(&reply[2+1+6*k], *j);
581 m_con.Send(PEER_ID_SERVER, 1, reply, true);
583 if(i == deleted_blocks.end())
589 sendlist.push_back(*i);
599 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
601 // Control local player (0ms)
602 LocalPlayer *player = m_env.getLocalPlayer();
603 assert(player != NULL);
604 player->applyControl(dtime);
606 //TimeTaker envtimer("env step", m_device);
615 ClientEnvEvent event = m_env.getClientEvent();
616 if(event.type == CEE_NONE)
620 else if(event.type == CEE_PLAYER_DAMAGE)
622 if(m_ignore_damage_timer <= 0)
624 u8 damage = event.player_damage.amount;
626 if(event.player_damage.send_to_server)
629 // Add to ClientEvent queue
631 event.type = CE_PLAYER_DAMAGE;
632 event.player_damage.amount = damage;
633 m_client_event_queue.push_back(event);
643 float &counter = m_avg_rtt_timer;
648 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
649 // connectedAndInitialized() is true, peer exists.
650 float avg_rtt = m_con.GetPeerAvgRTT(PEER_ID_SERVER);
651 infostream<<"Client: avg_rtt="<<avg_rtt<<std::endl;
656 Send player position to server
659 float &counter = m_playerpos_send_timer;
669 Replace updated meshes
672 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
674 //TimeTaker timer("** Processing mesh update result queue");
677 /*infostream<<"Mesh update result queue size is "
678 <<m_mesh_update_thread.m_queue_out.size()
681 int num_processed_meshes = 0;
682 while(m_mesh_update_thread.m_queue_out.size() > 0)
684 num_processed_meshes++;
685 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_front();
686 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
689 //JMutexAutoLock lock(block->mesh_mutex);
691 // Delete the old mesh
692 if(block->mesh != NULL)
694 // TODO: Remove hardware buffers of meshbuffers of block->mesh
699 // Replace with the new mesh
700 block->mesh = r.mesh;
702 if(r.ack_block_to_server)
704 /*infostream<<"Client: ACK block ("<<r.p.X<<","<<r.p.Y
705 <<","<<r.p.Z<<")"<<std::endl;*/
716 u32 replysize = 2+1+6;
717 SharedBuffer<u8> reply(replysize);
718 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
720 writeV3S16(&reply[3], r.p);
722 m_con.Send(PEER_ID_SERVER, 1, reply, true);
725 if(num_processed_meshes > 0)
726 g_profiler->graphAdd("num_processed_meshes", num_processed_meshes);
730 If the server didn't update the inventory in a while, revert
731 the local inventory (so the player notices the lag problem
732 and knows something is wrong).
734 if(m_inventory_from_server)
736 float interval = 10.0;
737 float count_before = floor(m_inventory_from_server_age / interval);
739 m_inventory_from_server_age += dtime;
741 float count_after = floor(m_inventory_from_server_age / interval);
743 if(count_after != count_before)
745 // Do this every <interval> seconds after TOCLIENT_INVENTORY
746 // Reset the locally changed inventory to the authoritative inventory
747 Player *player = m_env.getLocalPlayer();
748 player->inventory = *m_inventory_from_server;
749 m_inventory_updated = true;
754 Update positions of sounds attached to objects
757 for(std::map<int, u16>::iterator
758 i = m_sounds_to_objects.begin();
759 i != m_sounds_to_objects.end(); i++)
761 int client_id = i->first;
762 u16 object_id = i->second;
763 ClientActiveObject *cao = m_env.getActiveObject(object_id);
766 v3f pos = cao->getPosition();
767 m_sound->updateSoundPosition(client_id, pos);
772 Handle removed remotely initiated sounds
774 m_removed_sounds_check_timer += dtime;
775 if(m_removed_sounds_check_timer >= 2.32)
777 m_removed_sounds_check_timer = 0;
778 // Find removed sounds and clear references to them
779 std::set<s32> removed_server_ids;
780 for(std::map<s32, int>::iterator
781 i = m_sounds_server_to_client.begin();
782 i != m_sounds_server_to_client.end();)
784 s32 server_id = i->first;
785 int client_id = i->second;
787 if(!m_sound->soundExists(client_id)){
788 m_sounds_server_to_client.erase(server_id);
789 m_sounds_client_to_server.erase(client_id);
790 m_sounds_to_objects.erase(client_id);
791 removed_server_ids.insert(server_id);
795 if(removed_server_ids.size() != 0)
797 std::ostringstream os(std::ios_base::binary);
798 writeU16(os, TOSERVER_REMOVED_SOUNDS);
799 writeU16(os, removed_server_ids.size());
800 for(std::set<s32>::iterator i = removed_server_ids.begin();
801 i != removed_server_ids.end(); i++)
803 std::string s = os.str();
804 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
811 bool Client::loadMedia(const std::string &data, const std::string &filename)
813 // Silly irrlicht's const-incorrectness
814 Buffer<char> data_rw(data.c_str(), data.size());
818 const char *image_ext[] = {
819 ".png", ".jpg", ".bmp", ".tga",
820 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
823 name = removeStringEnd(filename, image_ext);
826 verbosestream<<"Client: Attempting to load image "
827 <<"file \""<<filename<<"\""<<std::endl;
829 io::IFileSystem *irrfs = m_device->getFileSystem();
830 video::IVideoDriver *vdrv = m_device->getVideoDriver();
832 // Create an irrlicht memory file
833 io::IReadFile *rfile = irrfs->createMemoryReadFile(
834 *data_rw, data_rw.getSize(), "_tempreadfile");
837 video::IImage *img = vdrv->createImageFromFile(rfile);
839 errorstream<<"Client: Cannot create image from data of "
840 <<"file \""<<filename<<"\""<<std::endl;
845 m_tsrc->insertSourceImage(filename, img);
852 const char *sound_ext[] = {
853 ".0.ogg", ".1.ogg", ".2.ogg", ".3.ogg", ".4.ogg",
854 ".5.ogg", ".6.ogg", ".7.ogg", ".8.ogg", ".9.ogg",
857 name = removeStringEnd(filename, sound_ext);
860 verbosestream<<"Client: Attempting to load sound "
861 <<"file \""<<filename<<"\""<<std::endl;
862 m_sound->loadSoundData(name, data);
866 const char *model_ext[] = {
867 ".x", ".b3d", ".md2", ".obj",
870 name = removeStringEnd(filename, model_ext);
873 verbosestream<<"Client: Storing model into Irrlicht: "
874 <<"\""<<filename<<"\""<<std::endl;
876 io::IFileSystem *irrfs = m_device->getFileSystem();
877 io::IReadFile *rfile = irrfs->createMemoryReadFile(
878 *data_rw, data_rw.getSize(), filename.c_str());
881 scene::ISceneManager *smgr = m_device->getSceneManager();
882 scene::IAnimatedMesh *mesh = smgr->getMesh(rfile);
883 smgr->getMeshCache()->addMesh(filename.c_str(), mesh);
888 errorstream<<"Client: Don't know how to load file \""
889 <<filename<<"\""<<std::endl;
893 // Virtual methods from con::PeerHandler
894 void Client::peerAdded(con::Peer *peer)
896 infostream<<"Client::peerAdded(): peer->id="
897 <<peer->id<<std::endl;
899 void Client::deletingPeer(con::Peer *peer, bool timeout)
901 infostream<<"Client::deletingPeer(): "
902 "Server Peer is getting deleted "
903 <<"(timeout="<<timeout<<")"<<std::endl;
906 void Client::ReceiveAll()
908 DSTACK(__FUNCTION_NAME);
909 u32 start_ms = porting::getTimeMs();
912 // Limit time even if there would be huge amounts of data to
914 if(porting::getTimeMs() > start_ms + 100)
919 g_profiler->graphAdd("client_received_packets", 1);
921 catch(con::NoIncomingDataException &e)
925 catch(con::InvalidIncomingDataException &e)
927 infostream<<"Client::ReceiveAll(): "
928 "InvalidIncomingDataException: what()="
929 <<e.what()<<std::endl;
934 void Client::Receive()
936 DSTACK(__FUNCTION_NAME);
937 SharedBuffer<u8> data;
941 //TimeTaker t1("con mutex and receive", m_device);
942 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
943 datasize = m_con.Receive(sender_peer_id, data);
945 //TimeTaker t1("ProcessData", m_device);
946 ProcessData(*data, datasize, sender_peer_id);
950 sender_peer_id given to this shall be quaranteed to be a valid peer
952 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
954 DSTACK(__FUNCTION_NAME);
956 // Ignore packets that don't even fit a command
959 m_packetcounter.add(60000);
963 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
965 //infostream<<"Client: received command="<<command<<std::endl;
966 m_packetcounter.add((u16)command);
969 If this check is removed, be sure to change the queue
970 system to know the ids
972 if(sender_peer_id != PEER_ID_SERVER)
974 infostream<<"Client::ProcessData(): Discarding data not "
975 "coming from server: peer_id="<<sender_peer_id
980 u8 ser_version = m_server_ser_ver;
982 //infostream<<"Client received command="<<(int)command<<std::endl;
984 if(command == TOCLIENT_INIT)
989 u8 deployed = data[2];
991 infostream<<"Client: TOCLIENT_INIT received with "
992 "deployed="<<((int)deployed&0xff)<<std::endl;
994 if(deployed < SER_FMT_VER_LOWEST
995 || deployed > SER_FMT_VER_HIGHEST)
997 infostream<<"Client: TOCLIENT_INIT: Server sent "
998 <<"unsupported ser_fmt_ver"<<std::endl;
1002 m_server_ser_ver = deployed;
1004 // Get player position
1005 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
1006 if(datasize >= 2+1+6)
1007 playerpos_s16 = readV3S16(&data[2+1]);
1008 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
1011 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1013 // Set player position
1014 Player *player = m_env.getLocalPlayer();
1015 assert(player != NULL);
1016 player->setPosition(playerpos_f);
1019 if(datasize >= 2+1+6+8)
1022 m_map_seed = readU64(&data[2+1+6]);
1023 infostream<<"Client: received map seed: "<<m_map_seed<<std::endl;
1028 SharedBuffer<u8> reply(replysize);
1029 writeU16(&reply[0], TOSERVER_INIT2);
1031 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1036 if(command == TOCLIENT_ACCESS_DENIED)
1038 // The server didn't like our password. Note, this needs
1039 // to be processed even if the serialisation format has
1040 // not been agreed yet, the same as TOCLIENT_INIT.
1041 m_access_denied = true;
1042 m_access_denied_reason = L"Unknown";
1045 std::string datastring((char*)&data[2], datasize-2);
1046 std::istringstream is(datastring, std::ios_base::binary);
1047 m_access_denied_reason = deSerializeWideString(is);
1052 if(ser_version == SER_FMT_VER_INVALID)
1054 infostream<<"Client: Server serialization"
1055 " format invalid or not initialized."
1056 " Skipping incoming command="<<command<<std::endl;
1060 // Just here to avoid putting the two if's together when
1061 // making some copypasta
1064 if(command == TOCLIENT_REMOVENODE)
1069 p.X = readS16(&data[2]);
1070 p.Y = readS16(&data[4]);
1071 p.Z = readS16(&data[6]);
1073 //TimeTaker t1("TOCLIENT_REMOVENODE");
1077 else if(command == TOCLIENT_ADDNODE)
1079 if(datasize < 8 + MapNode::serializedLength(ser_version))
1083 p.X = readS16(&data[2]);
1084 p.Y = readS16(&data[4]);
1085 p.Z = readS16(&data[6]);
1087 //TimeTaker t1("TOCLIENT_ADDNODE");
1090 n.deSerialize(&data[8], ser_version);
1094 else if(command == TOCLIENT_BLOCKDATA)
1096 // Ignore too small packet
1101 p.X = readS16(&data[2]);
1102 p.Y = readS16(&data[4]);
1103 p.Z = readS16(&data[6]);
1105 /*infostream<<"Client: Thread: BLOCKDATA for ("
1106 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1107 /*infostream<<"Client: Thread: BLOCKDATA for ("
1108 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1110 std::string datastring((char*)&data[8], datasize-8);
1111 std::istringstream istr(datastring, std::ios_base::binary);
1116 v2s16 p2d(p.X, p.Z);
1117 sector = m_env.getMap().emergeSector(p2d);
1119 assert(sector->getPos() == p2d);
1121 //TimeTaker timer("MapBlock deSerialize");
1124 block = sector->getBlockNoCreateNoEx(p.Y);
1128 Update an existing block
1130 //infostream<<"Updating"<<std::endl;
1131 block->deSerialize(istr, ser_version, false);
1138 //infostream<<"Creating new"<<std::endl;
1139 block = new MapBlock(&m_env.getMap(), p, this);
1140 block->deSerialize(istr, ser_version, false);
1141 sector->insertBlock(block);
1155 u32 replysize = 2+1+6;
1156 SharedBuffer<u8> reply(replysize);
1157 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
1159 writeV3S16(&reply[3], p);
1161 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1165 Add it to mesh update queue and set it to be acknowledged after update.
1167 //infostream<<"Adding mesh update task for received block"<<std::endl;
1168 addUpdateMeshTaskWithEdge(p, true);
1170 else if(command == TOCLIENT_INVENTORY)
1175 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
1178 //TimeTaker t2("mutex locking", m_device);
1179 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1182 //TimeTaker t3("istringstream init", m_device);
1183 std::string datastring((char*)&data[2], datasize-2);
1184 std::istringstream is(datastring, std::ios_base::binary);
1187 //m_env.printPlayers(infostream);
1189 //TimeTaker t4("player get", m_device);
1190 Player *player = m_env.getLocalPlayer();
1191 assert(player != NULL);
1194 //TimeTaker t1("inventory.deSerialize()", m_device);
1195 player->inventory.deSerialize(is);
1198 m_inventory_updated = true;
1200 delete m_inventory_from_server;
1201 m_inventory_from_server = new Inventory(player->inventory);
1202 m_inventory_from_server_age = 0.0;
1204 //infostream<<"Client got player inventory:"<<std::endl;
1205 //player->inventory.print(infostream);
1208 else if(command == TOCLIENT_TIME_OF_DAY)
1213 u16 time_of_day = readU16(&data[2]);
1214 time_of_day = time_of_day % 24000;
1215 //infostream<<"Client: time_of_day="<<time_of_day<<std::endl;
1216 float time_speed = 0;
1217 if(datasize >= 2 + 2 + 4){
1218 time_speed = readF1000(&data[4]);
1220 // Old message; try to approximate speed of time by ourselves
1221 float time_of_day_f = (float)time_of_day / 24000.0;
1222 float tod_diff_f = 0;
1223 if(time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
1224 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0;
1226 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
1227 m_last_time_of_day_f = time_of_day_f;
1228 float time_diff = m_time_of_day_update_timer;
1229 m_time_of_day_update_timer = 0;
1230 if(m_time_of_day_set){
1231 time_speed = 3600.0*24.0 * tod_diff_f / time_diff;
1232 infostream<<"Client: Measured time_of_day speed (old format): "
1233 <<time_speed<<" tod_diff_f="<<tod_diff_f
1234 <<" time_diff="<<time_diff<<std::endl;
1238 // Update environment
1239 m_env.setTimeOfDay(time_of_day);
1240 m_env.setTimeOfDaySpeed(time_speed);
1241 m_time_of_day_set = true;
1243 u32 dr = m_env.getDayNightRatio();
1244 verbosestream<<"Client: time_of_day="<<time_of_day
1245 <<" time_speed="<<time_speed
1246 <<" dr="<<dr<<std::endl;
1248 else if(command == TOCLIENT_CHAT_MESSAGE)
1256 std::string datastring((char*)&data[2], datasize-2);
1257 std::istringstream is(datastring, std::ios_base::binary);
1260 is.read((char*)buf, 2);
1261 u16 len = readU16(buf);
1263 std::wstring message;
1264 for(u16 i=0; i<len; i++)
1266 is.read((char*)buf, 2);
1267 message += (wchar_t)readU16(buf);
1270 /*infostream<<"Client received chat message: "
1271 <<wide_to_narrow(message)<<std::endl;*/
1273 m_chat_queue.push_back(message);
1275 else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1277 //if(g_settings->getBool("enable_experimental"))
1281 u16 count of removed objects
1282 for all removed objects {
1285 u16 count of added objects
1286 for all added objects {
1289 u32 initialization data length
1290 string initialization data
1295 // Get all data except the command number
1296 std::string datastring((char*)&data[2], datasize-2);
1297 // Throw them in an istringstream
1298 std::istringstream is(datastring, std::ios_base::binary);
1302 // Read removed objects
1304 u16 removed_count = readU16((u8*)buf);
1305 for(u16 i=0; i<removed_count; i++)
1308 u16 id = readU16((u8*)buf);
1311 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1312 m_env.removeActiveObject(id);
1316 // Read added objects
1318 u16 added_count = readU16((u8*)buf);
1319 for(u16 i=0; i<added_count; i++)
1322 u16 id = readU16((u8*)buf);
1324 u8 type = readU8((u8*)buf);
1325 std::string data = deSerializeLongString(is);
1328 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1329 m_env.addActiveObject(id, type, data);
1334 else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1336 //if(g_settings->getBool("enable_experimental"))
1348 // Get all data except the command number
1349 std::string datastring((char*)&data[2], datasize-2);
1350 // Throw them in an istringstream
1351 std::istringstream is(datastring, std::ios_base::binary);
1353 while(is.eof() == false)
1357 u16 id = readU16((u8*)buf);
1361 u16 message_size = readU16((u8*)buf);
1362 std::string message;
1363 message.reserve(message_size);
1364 for(u16 i=0; i<message_size; i++)
1367 message.append(buf, 1);
1369 // Pass on to the environment
1371 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1372 m_env.processActiveObjectMessage(id, message);
1377 else if(command == TOCLIENT_HP)
1379 std::string datastring((char*)&data[2], datasize-2);
1380 std::istringstream is(datastring, std::ios_base::binary);
1381 Player *player = m_env.getLocalPlayer();
1382 assert(player != NULL);
1383 u8 oldhp = player->hp;
1389 // Add to ClientEvent queue
1391 event.type = CE_PLAYER_DAMAGE;
1392 event.player_damage.amount = oldhp - hp;
1393 m_client_event_queue.push_back(event);
1396 else if(command == TOCLIENT_MOVE_PLAYER)
1398 std::string datastring((char*)&data[2], datasize-2);
1399 std::istringstream is(datastring, std::ios_base::binary);
1400 Player *player = m_env.getLocalPlayer();
1401 assert(player != NULL);
1402 v3f pos = readV3F1000(is);
1403 f32 pitch = readF1000(is);
1404 f32 yaw = readF1000(is);
1405 player->setPosition(pos);
1406 /*player->setPitch(pitch);
1407 player->setYaw(yaw);*/
1409 infostream<<"Client got TOCLIENT_MOVE_PLAYER"
1410 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1416 Add to ClientEvent queue.
1417 This has to be sent to the main program because otherwise
1418 it would just force the pitch and yaw values to whatever
1419 the camera points to.
1422 event.type = CE_PLAYER_FORCE_MOVE;
1423 event.player_force_move.pitch = pitch;
1424 event.player_force_move.yaw = yaw;
1425 m_client_event_queue.push_back(event);
1427 // Ignore damage for a few seconds, so that the player doesn't
1428 // get damage from falling on ground
1429 m_ignore_damage_timer = 3.0;
1431 else if(command == TOCLIENT_PLAYERITEM)
1433 infostream<<"Client: WARNING: Ignoring TOCLIENT_PLAYERITEM"<<std::endl;
1435 else if(command == TOCLIENT_DEATHSCREEN)
1437 std::string datastring((char*)&data[2], datasize-2);
1438 std::istringstream is(datastring, std::ios_base::binary);
1440 bool set_camera_point_target = readU8(is);
1441 v3f camera_point_target = readV3F1000(is);
1444 event.type = CE_DEATHSCREEN;
1445 event.deathscreen.set_camera_point_target = set_camera_point_target;
1446 event.deathscreen.camera_point_target_x = camera_point_target.X;
1447 event.deathscreen.camera_point_target_y = camera_point_target.Y;
1448 event.deathscreen.camera_point_target_z = camera_point_target.Z;
1449 m_client_event_queue.push_back(event);
1451 else if(command == TOCLIENT_ANNOUNCE_MEDIA)
1453 std::string datastring((char*)&data[2], datasize-2);
1454 std::istringstream is(datastring, std::ios_base::binary);
1456 // Mesh update thread must be stopped while
1457 // updating content definitions
1458 assert(!m_mesh_update_thread.IsRunning());
1460 int num_files = readU16(is);
1462 verbosestream<<"Client received TOCLIENT_ANNOUNCE_MEDIA ("
1463 <<num_files<<" files)"<<std::endl;
1465 core::list<MediaRequest> file_requests;
1467 for(int i=0; i<num_files; i++)
1469 //read file from cache
1470 std::string name = deSerializeString(is);
1471 std::string sha1_base64 = deSerializeString(is);
1473 // if name contains illegal characters, ignore the file
1474 if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
1475 errorstream<<"Client: ignoring illegal file name "
1476 <<"sent by server: \""<<name<<"\""<<std::endl;
1480 std::string sha1_raw = base64_decode(sha1_base64);
1481 std::string sha1_hex = hex_encode(sha1_raw);
1482 std::ostringstream tmp_os(std::ios_base::binary);
1483 bool found_in_cache = m_media_cache.load_sha1(sha1_raw, tmp_os);
1484 m_media_name_sha1_map.set(name, sha1_raw);
1486 // If found in cache, try to load it from there
1489 bool success = loadMedia(tmp_os.str(), name);
1491 verbosestream<<"Client: Loaded cached media: "
1492 <<sha1_hex<<" \""<<name<<"\""<<std::endl;
1495 infostream<<"Client: Failed to load cached media: "
1496 <<sha1_hex<<" \""<<name<<"\""<<std::endl;
1499 // Didn't load from cache; queue it to be requested
1500 verbosestream<<"Client: Adding file to request list: \""
1501 <<sha1_hex<<" \""<<name<<"\""<<std::endl;
1502 file_requests.push_back(MediaRequest(name));
1506 event.type = CE_TEXTURES_UPDATED;
1507 m_client_event_queue.push_back(event);
1511 u16 number of files requested
1517 std::ostringstream os(std::ios_base::binary);
1518 writeU16(os, TOSERVER_REQUEST_MEDIA);
1519 writeU16(os, file_requests.size());
1521 for(core::list<MediaRequest>::Iterator i = file_requests.begin();
1522 i != file_requests.end(); i++) {
1523 os<<serializeString(i->name);
1527 std::string s = os.str();
1528 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1530 Send(0, data, true);
1531 infostream<<"Client: Sending media request list to server ("
1532 <<file_requests.size()<<" files)"<<std::endl;
1534 else if(command == TOCLIENT_MEDIA)
1536 std::string datastring((char*)&data[2], datasize-2);
1537 std::istringstream is(datastring, std::ios_base::binary);
1539 // Mesh update thread must be stopped while
1540 // updating content definitions
1541 assert(!m_mesh_update_thread.IsRunning());
1545 u16 total number of file bunches
1546 u16 index of this bunch
1547 u32 number of files in this bunch
1555 int num_bunches = readU16(is);
1556 int bunch_i = readU16(is);
1557 if(num_bunches >= 2)
1558 m_media_receive_progress = (float)bunch_i / (float)(num_bunches - 1);
1560 m_media_receive_progress = 1.0;
1561 if(bunch_i == num_bunches - 1)
1562 m_media_received = true;
1563 int num_files = readU32(is);
1564 infostream<<"Client: Received files: bunch "<<bunch_i<<"/"
1565 <<num_bunches<<" files="<<num_files
1566 <<" size="<<datasize<<std::endl;
1567 for(int i=0; i<num_files; i++){
1568 std::string name = deSerializeString(is);
1569 std::string data = deSerializeLongString(is);
1571 // if name contains illegal characters, ignore the file
1572 if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
1573 errorstream<<"Client: ignoring illegal file name "
1574 <<"sent by server: \""<<name<<"\""<<std::endl;
1578 bool success = loadMedia(data, name);
1580 verbosestream<<"Client: Loaded received media: "
1581 <<"\""<<name<<"\". Caching."<<std::endl;
1583 infostream<<"Client: Failed to load received media: "
1584 <<"\""<<name<<"\". Not caching."<<std::endl;
1588 bool did = fs::CreateAllDirs(getMediaCacheDir());
1590 errorstream<<"Could not create media cache directory"
1595 core::map<std::string, std::string>::Node *n;
1596 n = m_media_name_sha1_map.find(name);
1598 errorstream<<"The server sent a file that has not "
1599 <<"been announced."<<std::endl;
1601 m_media_cache.update_sha1(data);
1606 event.type = CE_TEXTURES_UPDATED;
1607 m_client_event_queue.push_back(event);
1609 else if(command == TOCLIENT_TOOLDEF)
1611 infostream<<"Client: WARNING: Ignoring TOCLIENT_TOOLDEF"<<std::endl;
1613 else if(command == TOCLIENT_NODEDEF)
1615 infostream<<"Client: Received node definitions: packet size: "
1616 <<datasize<<std::endl;
1618 // Mesh update thread must be stopped while
1619 // updating content definitions
1620 assert(!m_mesh_update_thread.IsRunning());
1622 // Decompress node definitions
1623 std::string datastring((char*)&data[2], datasize-2);
1624 std::istringstream is(datastring, std::ios_base::binary);
1625 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1626 std::ostringstream tmp_os;
1627 decompressZlib(tmp_is, tmp_os);
1629 // Deserialize node definitions
1630 std::istringstream tmp_is2(tmp_os.str());
1631 m_nodedef->deSerialize(tmp_is2);
1632 m_nodedef_received = true;
1634 else if(command == TOCLIENT_CRAFTITEMDEF)
1636 infostream<<"Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF"<<std::endl;
1638 else if(command == TOCLIENT_ITEMDEF)
1640 infostream<<"Client: Received item definitions: packet size: "
1641 <<datasize<<std::endl;
1643 // Mesh update thread must be stopped while
1644 // updating content definitions
1645 assert(!m_mesh_update_thread.IsRunning());
1647 // Decompress item definitions
1648 std::string datastring((char*)&data[2], datasize-2);
1649 std::istringstream is(datastring, std::ios_base::binary);
1650 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1651 std::ostringstream tmp_os;
1652 decompressZlib(tmp_is, tmp_os);
1654 // Deserialize node definitions
1655 std::istringstream tmp_is2(tmp_os.str());
1656 m_itemdef->deSerialize(tmp_is2);
1657 m_itemdef_received = true;
1659 else if(command == TOCLIENT_PLAY_SOUND)
1661 std::string datastring((char*)&data[2], datasize-2);
1662 std::istringstream is(datastring, std::ios_base::binary);
1664 s32 server_id = readS32(is);
1665 std::string name = deSerializeString(is);
1666 float gain = readF1000(is);
1667 int type = readU8(is); // 0=local, 1=positional, 2=object
1668 v3f pos = readV3F1000(is);
1669 u16 object_id = readU16(is);
1670 bool loop = readU8(is);
1675 client_id = m_sound->playSound(name, loop, gain);
1677 case 1: // positional
1678 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1681 ClientActiveObject *cao = m_env.getActiveObject(object_id);
1683 pos = cao->getPosition();
1684 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1685 // TODO: Set up sound to move with object
1690 if(client_id != -1){
1691 m_sounds_server_to_client[server_id] = client_id;
1692 m_sounds_client_to_server[client_id] = server_id;
1694 m_sounds_to_objects[client_id] = object_id;
1697 else if(command == TOCLIENT_STOP_SOUND)
1699 std::string datastring((char*)&data[2], datasize-2);
1700 std::istringstream is(datastring, std::ios_base::binary);
1702 s32 server_id = readS32(is);
1703 std::map<s32, int>::iterator i =
1704 m_sounds_server_to_client.find(server_id);
1705 if(i != m_sounds_server_to_client.end()){
1706 int client_id = i->second;
1707 m_sound->stopSound(client_id);
1710 else if(command == TOCLIENT_PRIVILEGES)
1712 std::string datastring((char*)&data[2], datasize-2);
1713 std::istringstream is(datastring, std::ios_base::binary);
1715 m_privileges.clear();
1716 infostream<<"Client: Privileges updated: ";
1717 u16 num_privileges = readU16(is);
1718 for(u16 i=0; i<num_privileges; i++){
1719 std::string priv = deSerializeString(is);
1720 m_privileges.insert(priv);
1721 infostream<<priv<<" ";
1723 infostream<<std::endl;
1725 else if(command == TOCLIENT_INVENTORY_FORMSPEC)
1727 std::string datastring((char*)&data[2], datasize-2);
1728 std::istringstream is(datastring, std::ios_base::binary);
1730 // Store formspec in LocalPlayer
1731 Player *player = m_env.getLocalPlayer();
1732 assert(player != NULL);
1733 player->inventory_formspec = deSerializeLongString(is);
1735 else if(command == TOCLIENT_DETACHED_INVENTORY)
1737 std::string datastring((char*)&data[2], datasize-2);
1738 std::istringstream is(datastring, std::ios_base::binary);
1740 std::string name = deSerializeString(is);
1742 infostream<<"Client: Detached inventory update: \""<<name<<"\""<<std::endl;
1744 Inventory *inv = NULL;
1745 if(m_detached_inventories.count(name) > 0)
1746 inv = m_detached_inventories[name];
1748 inv = new Inventory(m_itemdef);
1749 m_detached_inventories[name] = inv;
1751 inv->deSerialize(is);
1755 infostream<<"Client: Ignoring unknown command "
1756 <<command<<std::endl;
1760 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
1762 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1763 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
1766 void Client::interact(u8 action, const PointedThing& pointed)
1768 if(connectedAndInitialized() == false){
1769 infostream<<"Client::interact() "
1770 "cancelled (not connected)"
1775 std::ostringstream os(std::ios_base::binary);
1781 [5] u32 length of the next item
1782 [9] serialized PointedThing
1784 0: start digging (from undersurface) or use
1785 1: stop digging (all parameters ignored)
1786 2: digging completed
1787 3: place block or item (to abovesurface)
1790 writeU16(os, TOSERVER_INTERACT);
1791 writeU8(os, action);
1792 writeU16(os, getPlayerItem());
1793 std::ostringstream tmp_os(std::ios::binary);
1794 pointed.serialize(tmp_os);
1795 os<<serializeLongString(tmp_os.str());
1797 std::string s = os.str();
1798 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1801 Send(0, data, true);
1804 void Client::sendNodemetaFields(v3s16 p, const std::string &formname,
1805 const std::map<std::string, std::string> &fields)
1807 std::ostringstream os(std::ios_base::binary);
1809 writeU16(os, TOSERVER_NODEMETA_FIELDS);
1811 os<<serializeString(formname);
1812 writeU16(os, fields.size());
1813 for(std::map<std::string, std::string>::const_iterator
1814 i = fields.begin(); i != fields.end(); i++){
1815 const std::string &name = i->first;
1816 const std::string &value = i->second;
1817 os<<serializeString(name);
1818 os<<serializeLongString(value);
1822 std::string s = os.str();
1823 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1825 Send(0, data, true);
1828 void Client::sendInventoryFields(const std::string &formname,
1829 const std::map<std::string, std::string> &fields)
1831 std::ostringstream os(std::ios_base::binary);
1833 writeU16(os, TOSERVER_INVENTORY_FIELDS);
1834 os<<serializeString(formname);
1835 writeU16(os, fields.size());
1836 for(std::map<std::string, std::string>::const_iterator
1837 i = fields.begin(); i != fields.end(); i++){
1838 const std::string &name = i->first;
1839 const std::string &value = i->second;
1840 os<<serializeString(name);
1841 os<<serializeLongString(value);
1845 std::string s = os.str();
1846 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1848 Send(0, data, true);
1851 void Client::sendInventoryAction(InventoryAction *a)
1853 std::ostringstream os(std::ios_base::binary);
1857 writeU16(buf, TOSERVER_INVENTORY_ACTION);
1858 os.write((char*)buf, 2);
1863 std::string s = os.str();
1864 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1866 Send(0, data, true);
1869 void Client::sendChatMessage(const std::wstring &message)
1871 std::ostringstream os(std::ios_base::binary);
1875 writeU16(buf, TOSERVER_CHAT_MESSAGE);
1876 os.write((char*)buf, 2);
1879 writeU16(buf, message.size());
1880 os.write((char*)buf, 2);
1883 for(u32 i=0; i<message.size(); i++)
1887 os.write((char*)buf, 2);
1891 std::string s = os.str();
1892 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1894 Send(0, data, true);
1897 void Client::sendChangePassword(const std::wstring oldpassword,
1898 const std::wstring newpassword)
1900 Player *player = m_env.getLocalPlayer();
1904 std::string playername = player->getName();
1905 std::string oldpwd = translatePassword(playername, oldpassword);
1906 std::string newpwd = translatePassword(playername, newpassword);
1908 std::ostringstream os(std::ios_base::binary);
1909 u8 buf[2+PASSWORD_SIZE*2];
1911 [0] u16 TOSERVER_PASSWORD
1912 [2] u8[28] old password
1913 [30] u8[28] new password
1916 writeU16(buf, TOSERVER_PASSWORD);
1917 for(u32 i=0;i<PASSWORD_SIZE-1;i++)
1919 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
1920 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
1922 buf[2+PASSWORD_SIZE-1] = 0;
1923 buf[30+PASSWORD_SIZE-1] = 0;
1924 os.write((char*)buf, 2+PASSWORD_SIZE*2);
1927 std::string s = os.str();
1928 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1930 Send(0, data, true);
1934 void Client::sendDamage(u8 damage)
1936 DSTACK(__FUNCTION_NAME);
1937 std::ostringstream os(std::ios_base::binary);
1939 writeU16(os, TOSERVER_DAMAGE);
1940 writeU8(os, damage);
1943 std::string s = os.str();
1944 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1946 Send(0, data, true);
1949 void Client::sendRespawn()
1951 DSTACK(__FUNCTION_NAME);
1952 std::ostringstream os(std::ios_base::binary);
1954 writeU16(os, TOSERVER_RESPAWN);
1957 std::string s = os.str();
1958 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1960 Send(0, data, true);
1963 void Client::sendPlayerPos()
1965 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1967 Player *myplayer = m_env.getLocalPlayer();
1968 if(myplayer == NULL)
1973 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1974 our_peer_id = m_con.GetPeerID();
1977 // Set peer id if not set already
1978 if(myplayer->peer_id == PEER_ID_INEXISTENT)
1979 myplayer->peer_id = our_peer_id;
1980 // Check that an existing peer_id is the same as the connection's
1981 assert(myplayer->peer_id == our_peer_id);
1983 v3f pf = myplayer->getPosition();
1984 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
1985 v3f sf = myplayer->getSpeed();
1986 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
1987 s32 pitch = myplayer->getPitch() * 100;
1988 s32 yaw = myplayer->getYaw() * 100;
1989 u32 keyPressed=myplayer->keyPressed;
1993 [2] v3s32 position*100
1994 [2+12] v3s32 speed*100
1995 [2+12+12] s32 pitch*100
1996 [2+12+12+4] s32 yaw*100
1997 [2+12+12+4+4] u32 keyPressed
1999 SharedBuffer<u8> data(2+12+12+4+4+4);
2000 writeU16(&data[0], TOSERVER_PLAYERPOS);
2001 writeV3S32(&data[2], position);
2002 writeV3S32(&data[2+12], speed);
2003 writeS32(&data[2+12+12], pitch);
2004 writeS32(&data[2+12+12+4], yaw);
2005 writeU32(&data[2+12+12+4+4], keyPressed);
2006 // Send as unreliable
2007 Send(0, data, false);
2010 void Client::sendPlayerItem(u16 item)
2012 Player *myplayer = m_env.getLocalPlayer();
2013 if(myplayer == NULL)
2016 u16 our_peer_id = m_con.GetPeerID();
2018 // Set peer id if not set already
2019 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2020 myplayer->peer_id = our_peer_id;
2021 // Check that an existing peer_id is the same as the connection's
2022 assert(myplayer->peer_id == our_peer_id);
2024 SharedBuffer<u8> data(2+2);
2025 writeU16(&data[0], TOSERVER_PLAYERITEM);
2026 writeU16(&data[2], item);
2029 Send(0, data, true);
2032 void Client::removeNode(v3s16 p)
2034 core::map<v3s16, MapBlock*> modified_blocks;
2038 //TimeTaker t("removeNodeAndUpdate", m_device);
2039 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
2041 catch(InvalidPositionException &e)
2045 // add urgent task to update the modified node
2046 addUpdateMeshTaskForNode(p, false, true);
2048 for(core::map<v3s16, MapBlock * >::Iterator
2049 i = modified_blocks.getIterator();
2050 i.atEnd() == false; i++)
2052 v3s16 p = i.getNode()->getKey();
2053 addUpdateMeshTaskWithEdge(p);
2057 void Client::addNode(v3s16 p, MapNode n)
2059 TimeTaker timer1("Client::addNode()");
2061 core::map<v3s16, MapBlock*> modified_blocks;
2065 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
2066 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks);
2068 catch(InvalidPositionException &e)
2071 for(core::map<v3s16, MapBlock * >::Iterator
2072 i = modified_blocks.getIterator();
2073 i.atEnd() == false; i++)
2075 v3s16 p = i.getNode()->getKey();
2076 addUpdateMeshTaskWithEdge(p);
2080 void Client::setPlayerControl(PlayerControl &control)
2082 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2083 LocalPlayer *player = m_env.getLocalPlayer();
2084 assert(player != NULL);
2085 player->control = control;
2088 void Client::selectPlayerItem(u16 item)
2090 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2091 m_playeritem = item;
2092 m_inventory_updated = true;
2093 sendPlayerItem(item);
2096 // Returns true if the inventory of the local player has been
2097 // updated from the server. If it is true, it is set to false.
2098 bool Client::getLocalInventoryUpdated()
2100 // m_inventory_updated is behind envlock
2101 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2102 bool updated = m_inventory_updated;
2103 m_inventory_updated = false;
2107 // Copies the inventory of the local player to parameter
2108 void Client::getLocalInventory(Inventory &dst)
2110 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2111 Player *player = m_env.getLocalPlayer();
2112 assert(player != NULL);
2113 dst = player->inventory;
2116 Inventory* Client::getInventory(const InventoryLocation &loc)
2119 case InventoryLocation::UNDEFINED:
2122 case InventoryLocation::CURRENT_PLAYER:
2124 Player *player = m_env.getLocalPlayer();
2125 assert(player != NULL);
2126 return &player->inventory;
2129 case InventoryLocation::PLAYER:
2131 Player *player = m_env.getPlayer(loc.name.c_str());
2134 return &player->inventory;
2137 case InventoryLocation::NODEMETA:
2139 NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p);
2142 return meta->getInventory();
2145 case InventoryLocation::DETACHED:
2147 if(m_detached_inventories.count(loc.name) == 0)
2149 return m_detached_inventories[loc.name];
2157 void Client::inventoryAction(InventoryAction *a)
2160 Send it to the server
2162 sendInventoryAction(a);
2165 Predict some local inventory changes
2167 a->clientApply(this, this);
2170 ClientActiveObject * Client::getSelectedActiveObject(
2172 v3f from_pos_f_on_map,
2173 core::line3d<f32> shootline_on_map
2176 core::array<DistanceSortedActiveObject> objects;
2178 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
2180 //infostream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
2183 // After this, the closest object is the first in the array.
2186 for(u32 i=0; i<objects.size(); i++)
2188 ClientActiveObject *obj = objects[i].obj;
2190 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
2191 if(selection_box == NULL)
2194 v3f pos = obj->getPosition();
2196 core::aabbox3d<f32> offsetted_box(
2197 selection_box->MinEdge + pos,
2198 selection_box->MaxEdge + pos
2201 if(offsetted_box.intersectsWithLine(shootline_on_map))
2203 //infostream<<"Returning selected object"<<std::endl;
2208 //infostream<<"No object selected; returning NULL."<<std::endl;
2212 void Client::printDebugInfo(std::ostream &os)
2214 //JMutexAutoLock lock1(m_fetchblock_mutex);
2215 /*JMutexAutoLock lock2(m_incoming_queue_mutex);
2217 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
2218 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
2219 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
2223 core::list<std::wstring> Client::getConnectedPlayerNames()
2225 core::list<Player*> players = m_env.getPlayers(true);
2226 core::list<std::wstring> playerNames;
2227 for(core::list<Player*>::Iterator
2228 i = players.begin();
2229 i != players.end(); i++)
2231 Player *player = *i;
2232 playerNames.push_back(narrow_to_wide(player->getName()));
2237 float Client::getAnimationTime()
2239 return m_animation_time;
2242 int Client::getCrackLevel()
2244 return m_crack_level;
2247 void Client::setCrack(int level, v3s16 pos)
2249 int old_crack_level = m_crack_level;
2250 v3s16 old_crack_pos = m_crack_pos;
2252 m_crack_level = level;
2255 if(old_crack_level >= 0 && (level < 0 || pos != old_crack_pos))
2258 addUpdateMeshTaskForNode(old_crack_pos, false, true);
2260 if(level >= 0 && (old_crack_level < 0 || pos != old_crack_pos))
2263 addUpdateMeshTaskForNode(pos, false, true);
2269 Player *player = m_env.getLocalPlayer();
2270 assert(player != NULL);
2274 bool Client::getChatMessage(std::wstring &message)
2276 if(m_chat_queue.size() == 0)
2278 message = m_chat_queue.pop_front();
2282 void Client::typeChatMessage(const std::wstring &message)
2284 // Discard empty line
2289 sendChatMessage(message);
2292 if (message[0] == L'/')
2294 m_chat_queue.push_back(
2295 (std::wstring)L"issued command: "+message);
2299 LocalPlayer *player = m_env.getLocalPlayer();
2300 assert(player != NULL);
2301 std::wstring name = narrow_to_wide(player->getName());
2302 m_chat_queue.push_back(
2303 (std::wstring)L"<"+name+L"> "+message);
2307 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
2309 /*infostream<<"Client::addUpdateMeshTask(): "
2310 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2311 <<" ack_to_server="<<ack_to_server
2312 <<" urgent="<<urgent
2315 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2320 Create a task to update the mesh of the block
2323 MeshMakeData *data = new MeshMakeData(this);
2326 //TimeTaker timer("data fill");
2328 // Debug: 1-6ms, avg=2ms
2330 data->setCrack(m_crack_level, m_crack_pos);
2331 data->setSmoothLighting(g_settings->getBool("smooth_lighting"));
2335 //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
2337 // Add task to queue
2338 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server, urgent);
2340 /*infostream<<"Mesh update input queue size is "
2341 <<m_mesh_update_thread.m_queue_in.size()
2345 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent)
2349 infostream<<"Client::addUpdateMeshTaskWithEdge(): "
2350 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2355 v3s16 p = blockpos + v3s16(0,0,0);
2356 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2357 addUpdateMeshTask(p, ack_to_server, urgent);
2359 catch(InvalidPositionException &e){}
2362 v3s16 p = blockpos + v3s16(-1,0,0);
2363 addUpdateMeshTask(p, false, urgent);
2365 catch(InvalidPositionException &e){}
2367 v3s16 p = blockpos + v3s16(0,-1,0);
2368 addUpdateMeshTask(p, false, urgent);
2370 catch(InvalidPositionException &e){}
2372 v3s16 p = blockpos + v3s16(0,0,-1);
2373 addUpdateMeshTask(p, false, urgent);
2375 catch(InvalidPositionException &e){}
2378 void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool urgent)
2382 infostream<<"Client::addUpdateMeshTaskForNode(): "
2383 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2387 v3s16 blockpos = getNodeBlockPos(nodepos);
2388 v3s16 blockpos_relative = blockpos * MAP_BLOCKSIZE;
2391 v3s16 p = blockpos + v3s16(0,0,0);
2392 addUpdateMeshTask(p, ack_to_server, urgent);
2394 catch(InvalidPositionException &e){}
2396 if(nodepos.X == blockpos_relative.X){
2398 v3s16 p = blockpos + v3s16(-1,0,0);
2399 addUpdateMeshTask(p, false, urgent);
2401 catch(InvalidPositionException &e){}
2403 if(nodepos.Y == blockpos_relative.Y){
2405 v3s16 p = blockpos + v3s16(0,-1,0);
2406 addUpdateMeshTask(p, false, urgent);
2408 catch(InvalidPositionException &e){}
2410 if(nodepos.Z == blockpos_relative.Z){
2412 v3s16 p = blockpos + v3s16(0,0,-1);
2413 addUpdateMeshTask(p, false, urgent);
2415 catch(InvalidPositionException &e){}
2419 ClientEvent Client::getClientEvent()
2421 if(m_client_event_queue.size() == 0)
2424 event.type = CE_NONE;
2427 return m_client_event_queue.pop_front();
2430 void Client::afterContentReceived()
2432 verbosestream<<"Client::afterContentReceived() started"<<std::endl;
2433 assert(m_itemdef_received);
2434 assert(m_nodedef_received);
2435 assert(m_media_received);
2437 // remove the information about which checksum each texture
2439 m_media_name_sha1_map.clear();
2441 // Rebuild inherited images and recreate textures
2442 verbosestream<<"Rebuilding images and textures"<<std::endl;
2443 m_tsrc->rebuildImagesAndTextures();
2445 // Update texture atlas
2446 verbosestream<<"Updating texture atlas"<<std::endl;
2447 if(g_settings->getBool("enable_texture_atlas"))
2448 m_tsrc->buildMainAtlas(this);
2450 // Update node aliases
2451 verbosestream<<"Updating node aliases"<<std::endl;
2452 m_nodedef->updateAliases(m_itemdef);
2454 // Update node textures
2455 verbosestream<<"Updating node textures"<<std::endl;
2456 m_nodedef->updateTextures(m_tsrc);
2458 // Update item textures and meshes
2459 verbosestream<<"Updating item textures and meshes"<<std::endl;
2460 m_itemdef->updateTexturesAndMeshes(this);
2462 // Start mesh update thread after setting up content definitions
2463 verbosestream<<"Starting mesh update thread"<<std::endl;
2464 m_mesh_update_thread.Start();
2466 verbosestream<<"Client::afterContentReceived() done"<<std::endl;
2469 float Client::getRTT(void)
2472 return m_con.GetPeerAvgRTT(PEER_ID_SERVER);
2473 } catch(con::PeerNotFoundException &e){
2478 // IGameDef interface
2480 IItemDefManager* Client::getItemDefManager()
2484 INodeDefManager* Client::getNodeDefManager()
2488 ICraftDefManager* Client::getCraftDefManager()
2491 //return m_craftdef;
2493 ITextureSource* Client::getTextureSource()
2497 u16 Client::allocateUnknownNodeId(const std::string &name)
2499 errorstream<<"Client::allocateUnknownNodeId(): "
2500 <<"Client cannot allocate node IDs"<<std::endl;
2502 return CONTENT_IGNORE;
2504 ISoundManager* Client::getSoundManager()
2508 MtEventManager* Client::getEventManager()