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_recommended_send_interval(0.1),
271 m_removed_sounds_check_timer(0)
273 m_packetcounter_timer = 0.0;
274 //m_delete_unused_sectors_timer = 0.0;
275 m_connection_reinit_timer = 0.0;
276 m_avg_rtt_timer = 0.0;
277 m_playerpos_send_timer = 0.0;
278 m_ignore_damage_timer = 0.0;
280 // Build main texture atlas, now that the GameDef exists (that is, us)
281 if(g_settings->getBool("enable_texture_atlas"))
282 m_tsrc->buildMainAtlas(this);
284 infostream<<"Not building texture atlas."<<std::endl;
290 Player *player = new LocalPlayer(this);
292 player->updateName(playername);
294 m_env.addPlayer(player);
301 //JMutexAutoLock conlock(m_con_mutex); //bulk comment-out
305 m_mesh_update_thread.setRun(false);
306 while(m_mesh_update_thread.IsRunning())
309 delete m_inventory_from_server;
311 // Delete detached inventories
313 for(std::map<std::string, Inventory*>::iterator
314 i = m_detached_inventories.begin();
315 i != m_detached_inventories.end(); i++){
321 void Client::connect(Address address)
323 DSTACK(__FUNCTION_NAME);
324 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
325 m_con.SetTimeoutMs(0);
326 m_con.Connect(address);
329 bool Client::connectedAndInitialized()
331 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
333 if(m_con.Connected() == false)
336 if(m_server_ser_ver == SER_FMT_VER_INVALID)
342 void Client::step(float dtime)
344 DSTACK(__FUNCTION_NAME);
350 if(m_ignore_damage_timer > dtime)
351 m_ignore_damage_timer -= dtime;
353 m_ignore_damage_timer = 0.0;
355 m_animation_time += dtime;
356 if(m_animation_time > 60.0)
357 m_animation_time -= 60.0;
359 m_time_of_day_update_timer += dtime;
361 //infostream<<"Client steps "<<dtime<<std::endl;
364 //TimeTaker timer("ReceiveAll()", m_device);
370 //TimeTaker timer("m_con_mutex + m_con.RunTimeouts()", m_device);
372 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
373 m_con.RunTimeouts(dtime);
380 float &counter = m_packetcounter_timer;
386 infostream<<"Client packetcounter (20s):"<<std::endl;
387 m_packetcounter.print(infostream);
388 m_packetcounter.clear();
392 // Get connection status
393 bool connected = connectedAndInitialized();
398 Delete unused sectors
400 NOTE: This jams the game for a while because deleting sectors
404 float &counter = m_delete_unused_sectors_timer;
412 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
414 core::list<v3s16> deleted_blocks;
416 float delete_unused_sectors_timeout =
417 g_settings->getFloat("client_delete_unused_sectors_timeout");
419 // Delete sector blocks
420 /*u32 num = m_env.getMap().unloadUnusedData
421 (delete_unused_sectors_timeout,
422 true, &deleted_blocks);*/
424 // Delete whole sectors
425 m_env.getMap().unloadUnusedData
426 (delete_unused_sectors_timeout,
429 if(deleted_blocks.size() > 0)
431 /*infostream<<"Client: Deleted blocks of "<<num
432 <<" unused sectors"<<std::endl;*/
433 /*infostream<<"Client: Deleted "<<num
434 <<" unused sectors"<<std::endl;*/
440 // Env is locked so con can be locked.
441 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
443 core::list<v3s16>::Iterator i = deleted_blocks.begin();
444 core::list<v3s16> sendlist;
447 if(sendlist.size() == 255 || i == deleted_blocks.end())
449 if(sendlist.size() == 0)
458 u32 replysize = 2+1+6*sendlist.size();
459 SharedBuffer<u8> reply(replysize);
460 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
461 reply[2] = sendlist.size();
463 for(core::list<v3s16>::Iterator
464 j = sendlist.begin();
465 j != sendlist.end(); j++)
467 writeV3S16(&reply[2+1+6*k], *j);
470 m_con.Send(PEER_ID_SERVER, 1, reply, true);
472 if(i == deleted_blocks.end())
478 sendlist.push_back(*i);
486 if(connected == false)
488 float &counter = m_connection_reinit_timer;
494 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
496 Player *myplayer = m_env.getLocalPlayer();
497 assert(myplayer != NULL);
499 // Send TOSERVER_INIT
500 // [0] u16 TOSERVER_INIT
501 // [2] u8 SER_FMT_VER_HIGHEST
502 // [3] u8[20] player_name
503 // [23] u8[28] password (new in some version)
504 // [51] u16 minimum supported network protocol version (added sometime)
505 // [53] u16 maximum supported network protocol version (added later than the previous one)
506 SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2+2);
507 writeU16(&data[0], TOSERVER_INIT);
508 writeU8(&data[2], SER_FMT_VER_HIGHEST);
510 memset((char*)&data[3], 0, PLAYERNAME_SIZE);
511 snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
513 /*infostream<<"Client: sending initial password hash: \""<<m_password<<"\""
516 memset((char*)&data[23], 0, PASSWORD_SIZE);
517 snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
519 writeU16(&data[51], CLIENT_PROTOCOL_VERSION_MIN);
520 writeU16(&data[53], CLIENT_PROTOCOL_VERSION_MAX);
522 // Send as unreliable
523 Send(0, data, false);
526 // Not connected, return
531 Do stuff if connected
535 Run Map's timers and unload unused data
537 const float map_timer_and_unload_dtime = 5.25;
538 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
540 ScopeProfiler sp(g_profiler, "Client: map timer and unload");
541 core::list<v3s16> deleted_blocks;
542 m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
543 g_settings->getFloat("client_unload_unused_data_timeout"),
546 /*if(deleted_blocks.size() > 0)
547 infostream<<"Client: Unloaded "<<deleted_blocks.size()
548 <<" unused blocks"<<std::endl;*/
552 NOTE: This loop is intentionally iterated the way it is.
555 core::list<v3s16>::Iterator i = deleted_blocks.begin();
556 core::list<v3s16> sendlist;
559 if(sendlist.size() == 255 || i == deleted_blocks.end())
561 if(sendlist.size() == 0)
570 u32 replysize = 2+1+6*sendlist.size();
571 SharedBuffer<u8> reply(replysize);
572 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
573 reply[2] = sendlist.size();
575 for(core::list<v3s16>::Iterator
576 j = sendlist.begin();
577 j != sendlist.end(); j++)
579 writeV3S16(&reply[2+1+6*k], *j);
582 m_con.Send(PEER_ID_SERVER, 1, reply, true);
584 if(i == deleted_blocks.end())
590 sendlist.push_back(*i);
600 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
602 // Control local player (0ms)
603 LocalPlayer *player = m_env.getLocalPlayer();
604 assert(player != NULL);
605 player->applyControl(dtime);
607 //TimeTaker envtimer("env step", m_device);
616 ClientEnvEvent event = m_env.getClientEvent();
617 if(event.type == CEE_NONE)
621 else if(event.type == CEE_PLAYER_DAMAGE)
623 if(m_ignore_damage_timer <= 0)
625 u8 damage = event.player_damage.amount;
627 if(event.player_damage.send_to_server)
630 // Add to ClientEvent queue
632 event.type = CE_PLAYER_DAMAGE;
633 event.player_damage.amount = damage;
634 m_client_event_queue.push_back(event);
644 float &counter = m_avg_rtt_timer;
649 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
650 // connectedAndInitialized() is true, peer exists.
651 float avg_rtt = m_con.GetPeerAvgRTT(PEER_ID_SERVER);
652 infostream<<"Client: avg_rtt="<<avg_rtt<<std::endl;
657 Send player position to server
660 float &counter = m_playerpos_send_timer;
662 if(counter >= m_recommended_send_interval)
670 Replace updated meshes
673 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
675 //TimeTaker timer("** Processing mesh update result queue");
678 /*infostream<<"Mesh update result queue size is "
679 <<m_mesh_update_thread.m_queue_out.size()
682 int num_processed_meshes = 0;
683 while(m_mesh_update_thread.m_queue_out.size() > 0)
685 num_processed_meshes++;
686 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_front();
687 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
690 //JMutexAutoLock lock(block->mesh_mutex);
692 // Delete the old mesh
693 if(block->mesh != NULL)
695 // TODO: Remove hardware buffers of meshbuffers of block->mesh
700 // Replace with the new mesh
701 block->mesh = r.mesh;
703 if(r.ack_block_to_server)
705 /*infostream<<"Client: ACK block ("<<r.p.X<<","<<r.p.Y
706 <<","<<r.p.Z<<")"<<std::endl;*/
717 u32 replysize = 2+1+6;
718 SharedBuffer<u8> reply(replysize);
719 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
721 writeV3S16(&reply[3], r.p);
723 m_con.Send(PEER_ID_SERVER, 1, reply, true);
726 if(num_processed_meshes > 0)
727 g_profiler->graphAdd("num_processed_meshes", num_processed_meshes);
731 If the server didn't update the inventory in a while, revert
732 the local inventory (so the player notices the lag problem
733 and knows something is wrong).
735 if(m_inventory_from_server)
737 float interval = 10.0;
738 float count_before = floor(m_inventory_from_server_age / interval);
740 m_inventory_from_server_age += dtime;
742 float count_after = floor(m_inventory_from_server_age / interval);
744 if(count_after != count_before)
746 // Do this every <interval> seconds after TOCLIENT_INVENTORY
747 // Reset the locally changed inventory to the authoritative inventory
748 Player *player = m_env.getLocalPlayer();
749 player->inventory = *m_inventory_from_server;
750 m_inventory_updated = true;
755 Update positions of sounds attached to objects
758 for(std::map<int, u16>::iterator
759 i = m_sounds_to_objects.begin();
760 i != m_sounds_to_objects.end(); i++)
762 int client_id = i->first;
763 u16 object_id = i->second;
764 ClientActiveObject *cao = m_env.getActiveObject(object_id);
767 v3f pos = cao->getPosition();
768 m_sound->updateSoundPosition(client_id, pos);
773 Handle removed remotely initiated sounds
775 m_removed_sounds_check_timer += dtime;
776 if(m_removed_sounds_check_timer >= 2.32)
778 m_removed_sounds_check_timer = 0;
779 // Find removed sounds and clear references to them
780 std::set<s32> removed_server_ids;
781 for(std::map<s32, int>::iterator
782 i = m_sounds_server_to_client.begin();
783 i != m_sounds_server_to_client.end();)
785 s32 server_id = i->first;
786 int client_id = i->second;
788 if(!m_sound->soundExists(client_id)){
789 m_sounds_server_to_client.erase(server_id);
790 m_sounds_client_to_server.erase(client_id);
791 m_sounds_to_objects.erase(client_id);
792 removed_server_ids.insert(server_id);
796 if(removed_server_ids.size() != 0)
798 std::ostringstream os(std::ios_base::binary);
799 writeU16(os, TOSERVER_REMOVED_SOUNDS);
800 writeU16(os, removed_server_ids.size());
801 for(std::set<s32>::iterator i = removed_server_ids.begin();
802 i != removed_server_ids.end(); i++)
804 std::string s = os.str();
805 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
812 bool Client::loadMedia(const std::string &data, const std::string &filename)
814 // Silly irrlicht's const-incorrectness
815 Buffer<char> data_rw(data.c_str(), data.size());
819 const char *image_ext[] = {
820 ".png", ".jpg", ".bmp", ".tga",
821 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
824 name = removeStringEnd(filename, image_ext);
827 verbosestream<<"Client: Attempting to load image "
828 <<"file \""<<filename<<"\""<<std::endl;
830 io::IFileSystem *irrfs = m_device->getFileSystem();
831 video::IVideoDriver *vdrv = m_device->getVideoDriver();
833 // Create an irrlicht memory file
834 io::IReadFile *rfile = irrfs->createMemoryReadFile(
835 *data_rw, data_rw.getSize(), "_tempreadfile");
838 video::IImage *img = vdrv->createImageFromFile(rfile);
840 errorstream<<"Client: Cannot create image from data of "
841 <<"file \""<<filename<<"\""<<std::endl;
846 m_tsrc->insertSourceImage(filename, img);
853 const char *sound_ext[] = {
854 ".0.ogg", ".1.ogg", ".2.ogg", ".3.ogg", ".4.ogg",
855 ".5.ogg", ".6.ogg", ".7.ogg", ".8.ogg", ".9.ogg",
858 name = removeStringEnd(filename, sound_ext);
861 verbosestream<<"Client: Attempting to load sound "
862 <<"file \""<<filename<<"\""<<std::endl;
863 m_sound->loadSoundData(name, data);
867 const char *model_ext[] = {
868 ".x", ".b3d", ".md2", ".obj",
871 name = removeStringEnd(filename, model_ext);
874 verbosestream<<"Client: Storing model into Irrlicht: "
875 <<"\""<<filename<<"\""<<std::endl;
877 io::IFileSystem *irrfs = m_device->getFileSystem();
878 io::IReadFile *rfile = irrfs->createMemoryReadFile(
879 *data_rw, data_rw.getSize(), filename.c_str());
882 scene::ISceneManager *smgr = m_device->getSceneManager();
883 scene::IAnimatedMesh *mesh = smgr->getMesh(rfile);
884 smgr->getMeshCache()->addMesh(filename.c_str(), mesh);
889 errorstream<<"Client: Don't know how to load file \""
890 <<filename<<"\""<<std::endl;
894 // Virtual methods from con::PeerHandler
895 void Client::peerAdded(con::Peer *peer)
897 infostream<<"Client::peerAdded(): peer->id="
898 <<peer->id<<std::endl;
900 void Client::deletingPeer(con::Peer *peer, bool timeout)
902 infostream<<"Client::deletingPeer(): "
903 "Server Peer is getting deleted "
904 <<"(timeout="<<timeout<<")"<<std::endl;
907 void Client::ReceiveAll()
909 DSTACK(__FUNCTION_NAME);
910 u32 start_ms = porting::getTimeMs();
913 // Limit time even if there would be huge amounts of data to
915 if(porting::getTimeMs() > start_ms + 100)
920 g_profiler->graphAdd("client_received_packets", 1);
922 catch(con::NoIncomingDataException &e)
926 catch(con::InvalidIncomingDataException &e)
928 infostream<<"Client::ReceiveAll(): "
929 "InvalidIncomingDataException: what()="
930 <<e.what()<<std::endl;
935 void Client::Receive()
937 DSTACK(__FUNCTION_NAME);
938 SharedBuffer<u8> data;
942 //TimeTaker t1("con mutex and receive", m_device);
943 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
944 datasize = m_con.Receive(sender_peer_id, data);
946 //TimeTaker t1("ProcessData", m_device);
947 ProcessData(*data, datasize, sender_peer_id);
951 sender_peer_id given to this shall be quaranteed to be a valid peer
953 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
955 DSTACK(__FUNCTION_NAME);
957 // Ignore packets that don't even fit a command
960 m_packetcounter.add(60000);
964 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
966 //infostream<<"Client: received command="<<command<<std::endl;
967 m_packetcounter.add((u16)command);
970 If this check is removed, be sure to change the queue
971 system to know the ids
973 if(sender_peer_id != PEER_ID_SERVER)
975 infostream<<"Client::ProcessData(): Discarding data not "
976 "coming from server: peer_id="<<sender_peer_id
981 u8 ser_version = m_server_ser_ver;
983 //infostream<<"Client received command="<<(int)command<<std::endl;
985 if(command == TOCLIENT_INIT)
990 u8 deployed = data[2];
992 infostream<<"Client: TOCLIENT_INIT received with "
993 "deployed="<<((int)deployed&0xff)<<std::endl;
995 if(deployed < SER_FMT_VER_LOWEST
996 || deployed > SER_FMT_VER_HIGHEST)
998 infostream<<"Client: TOCLIENT_INIT: Server sent "
999 <<"unsupported ser_fmt_ver"<<std::endl;
1003 m_server_ser_ver = deployed;
1005 // Get player position
1006 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
1007 if(datasize >= 2+1+6)
1008 playerpos_s16 = readV3S16(&data[2+1]);
1009 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
1012 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1014 // Set player position
1015 Player *player = m_env.getLocalPlayer();
1016 assert(player != NULL);
1017 player->setPosition(playerpos_f);
1020 if(datasize >= 2+1+6+8)
1023 m_map_seed = readU64(&data[2+1+6]);
1024 infostream<<"Client: received map seed: "<<m_map_seed<<std::endl;
1027 if(datasize >= 2+1+6+8+4)
1030 m_recommended_send_interval = readF1000(&data[2+1+6+8]);
1031 infostream<<"Client: received recommended send interval "
1032 <<m_recommended_send_interval<<std::endl;
1037 SharedBuffer<u8> reply(replysize);
1038 writeU16(&reply[0], TOSERVER_INIT2);
1040 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1045 if(command == TOCLIENT_ACCESS_DENIED)
1047 // The server didn't like our password. Note, this needs
1048 // to be processed even if the serialisation format has
1049 // not been agreed yet, the same as TOCLIENT_INIT.
1050 m_access_denied = true;
1051 m_access_denied_reason = L"Unknown";
1054 std::string datastring((char*)&data[2], datasize-2);
1055 std::istringstream is(datastring, std::ios_base::binary);
1056 m_access_denied_reason = deSerializeWideString(is);
1061 if(ser_version == SER_FMT_VER_INVALID)
1063 infostream<<"Client: Server serialization"
1064 " format invalid or not initialized."
1065 " Skipping incoming command="<<command<<std::endl;
1069 // Just here to avoid putting the two if's together when
1070 // making some copypasta
1073 if(command == TOCLIENT_REMOVENODE)
1078 p.X = readS16(&data[2]);
1079 p.Y = readS16(&data[4]);
1080 p.Z = readS16(&data[6]);
1082 //TimeTaker t1("TOCLIENT_REMOVENODE");
1086 else if(command == TOCLIENT_ADDNODE)
1088 if(datasize < 8 + MapNode::serializedLength(ser_version))
1092 p.X = readS16(&data[2]);
1093 p.Y = readS16(&data[4]);
1094 p.Z = readS16(&data[6]);
1096 //TimeTaker t1("TOCLIENT_ADDNODE");
1099 n.deSerialize(&data[8], ser_version);
1103 else if(command == TOCLIENT_BLOCKDATA)
1105 // Ignore too small packet
1110 p.X = readS16(&data[2]);
1111 p.Y = readS16(&data[4]);
1112 p.Z = readS16(&data[6]);
1114 /*infostream<<"Client: Thread: BLOCKDATA for ("
1115 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1116 /*infostream<<"Client: Thread: BLOCKDATA for ("
1117 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1119 std::string datastring((char*)&data[8], datasize-8);
1120 std::istringstream istr(datastring, std::ios_base::binary);
1125 v2s16 p2d(p.X, p.Z);
1126 sector = m_env.getMap().emergeSector(p2d);
1128 assert(sector->getPos() == p2d);
1130 //TimeTaker timer("MapBlock deSerialize");
1133 block = sector->getBlockNoCreateNoEx(p.Y);
1137 Update an existing block
1139 //infostream<<"Updating"<<std::endl;
1140 block->deSerialize(istr, ser_version, false);
1147 //infostream<<"Creating new"<<std::endl;
1148 block = new MapBlock(&m_env.getMap(), p, this);
1149 block->deSerialize(istr, ser_version, false);
1150 sector->insertBlock(block);
1164 u32 replysize = 2+1+6;
1165 SharedBuffer<u8> reply(replysize);
1166 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
1168 writeV3S16(&reply[3], p);
1170 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1174 Add it to mesh update queue and set it to be acknowledged after update.
1176 //infostream<<"Adding mesh update task for received block"<<std::endl;
1177 addUpdateMeshTaskWithEdge(p, true);
1179 else if(command == TOCLIENT_INVENTORY)
1184 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
1187 //TimeTaker t2("mutex locking", m_device);
1188 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1191 //TimeTaker t3("istringstream init", m_device);
1192 std::string datastring((char*)&data[2], datasize-2);
1193 std::istringstream is(datastring, std::ios_base::binary);
1196 //m_env.printPlayers(infostream);
1198 //TimeTaker t4("player get", m_device);
1199 Player *player = m_env.getLocalPlayer();
1200 assert(player != NULL);
1203 //TimeTaker t1("inventory.deSerialize()", m_device);
1204 player->inventory.deSerialize(is);
1207 m_inventory_updated = true;
1209 delete m_inventory_from_server;
1210 m_inventory_from_server = new Inventory(player->inventory);
1211 m_inventory_from_server_age = 0.0;
1213 //infostream<<"Client got player inventory:"<<std::endl;
1214 //player->inventory.print(infostream);
1217 else if(command == TOCLIENT_TIME_OF_DAY)
1222 u16 time_of_day = readU16(&data[2]);
1223 time_of_day = time_of_day % 24000;
1224 //infostream<<"Client: time_of_day="<<time_of_day<<std::endl;
1225 float time_speed = 0;
1226 if(datasize >= 2 + 2 + 4){
1227 time_speed = readF1000(&data[4]);
1229 // Old message; try to approximate speed of time by ourselves
1230 float time_of_day_f = (float)time_of_day / 24000.0;
1231 float tod_diff_f = 0;
1232 if(time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
1233 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0;
1235 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
1236 m_last_time_of_day_f = time_of_day_f;
1237 float time_diff = m_time_of_day_update_timer;
1238 m_time_of_day_update_timer = 0;
1239 if(m_time_of_day_set){
1240 time_speed = 3600.0*24.0 * tod_diff_f / time_diff;
1241 infostream<<"Client: Measured time_of_day speed (old format): "
1242 <<time_speed<<" tod_diff_f="<<tod_diff_f
1243 <<" time_diff="<<time_diff<<std::endl;
1247 // Update environment
1248 m_env.setTimeOfDay(time_of_day);
1249 m_env.setTimeOfDaySpeed(time_speed);
1250 m_time_of_day_set = true;
1252 u32 dr = m_env.getDayNightRatio();
1253 verbosestream<<"Client: time_of_day="<<time_of_day
1254 <<" time_speed="<<time_speed
1255 <<" dr="<<dr<<std::endl;
1257 else if(command == TOCLIENT_CHAT_MESSAGE)
1265 std::string datastring((char*)&data[2], datasize-2);
1266 std::istringstream is(datastring, std::ios_base::binary);
1269 is.read((char*)buf, 2);
1270 u16 len = readU16(buf);
1272 std::wstring message;
1273 for(u16 i=0; i<len; i++)
1275 is.read((char*)buf, 2);
1276 message += (wchar_t)readU16(buf);
1279 /*infostream<<"Client received chat message: "
1280 <<wide_to_narrow(message)<<std::endl;*/
1282 m_chat_queue.push_back(message);
1284 else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1286 //if(g_settings->getBool("enable_experimental"))
1290 u16 count of removed objects
1291 for all removed objects {
1294 u16 count of added objects
1295 for all added objects {
1298 u32 initialization data length
1299 string initialization data
1304 // Get all data except the command number
1305 std::string datastring((char*)&data[2], datasize-2);
1306 // Throw them in an istringstream
1307 std::istringstream is(datastring, std::ios_base::binary);
1311 // Read removed objects
1313 u16 removed_count = readU16((u8*)buf);
1314 for(u16 i=0; i<removed_count; i++)
1317 u16 id = readU16((u8*)buf);
1320 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1321 m_env.removeActiveObject(id);
1325 // Read added objects
1327 u16 added_count = readU16((u8*)buf);
1328 for(u16 i=0; i<added_count; i++)
1331 u16 id = readU16((u8*)buf);
1333 u8 type = readU8((u8*)buf);
1334 std::string data = deSerializeLongString(is);
1337 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1338 m_env.addActiveObject(id, type, data);
1343 else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1345 //if(g_settings->getBool("enable_experimental"))
1357 // Get all data except the command number
1358 std::string datastring((char*)&data[2], datasize-2);
1359 // Throw them in an istringstream
1360 std::istringstream is(datastring, std::ios_base::binary);
1362 while(is.eof() == false)
1366 u16 id = readU16((u8*)buf);
1370 u16 message_size = readU16((u8*)buf);
1371 std::string message;
1372 message.reserve(message_size);
1373 for(u16 i=0; i<message_size; i++)
1376 message.append(buf, 1);
1378 // Pass on to the environment
1380 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1381 m_env.processActiveObjectMessage(id, message);
1386 else if(command == TOCLIENT_HP)
1388 std::string datastring((char*)&data[2], datasize-2);
1389 std::istringstream is(datastring, std::ios_base::binary);
1390 Player *player = m_env.getLocalPlayer();
1391 assert(player != NULL);
1392 u8 oldhp = player->hp;
1398 // Add to ClientEvent queue
1400 event.type = CE_PLAYER_DAMAGE;
1401 event.player_damage.amount = oldhp - hp;
1402 m_client_event_queue.push_back(event);
1405 else if(command == TOCLIENT_MOVE_PLAYER)
1407 std::string datastring((char*)&data[2], datasize-2);
1408 std::istringstream is(datastring, std::ios_base::binary);
1409 Player *player = m_env.getLocalPlayer();
1410 assert(player != NULL);
1411 v3f pos = readV3F1000(is);
1412 f32 pitch = readF1000(is);
1413 f32 yaw = readF1000(is);
1414 player->setPosition(pos);
1415 /*player->setPitch(pitch);
1416 player->setYaw(yaw);*/
1418 infostream<<"Client got TOCLIENT_MOVE_PLAYER"
1419 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1425 Add to ClientEvent queue.
1426 This has to be sent to the main program because otherwise
1427 it would just force the pitch and yaw values to whatever
1428 the camera points to.
1431 event.type = CE_PLAYER_FORCE_MOVE;
1432 event.player_force_move.pitch = pitch;
1433 event.player_force_move.yaw = yaw;
1434 m_client_event_queue.push_back(event);
1436 // Ignore damage for a few seconds, so that the player doesn't
1437 // get damage from falling on ground
1438 m_ignore_damage_timer = 3.0;
1440 else if(command == TOCLIENT_PLAYERITEM)
1442 infostream<<"Client: WARNING: Ignoring TOCLIENT_PLAYERITEM"<<std::endl;
1444 else if(command == TOCLIENT_DEATHSCREEN)
1446 std::string datastring((char*)&data[2], datasize-2);
1447 std::istringstream is(datastring, std::ios_base::binary);
1449 bool set_camera_point_target = readU8(is);
1450 v3f camera_point_target = readV3F1000(is);
1453 event.type = CE_DEATHSCREEN;
1454 event.deathscreen.set_camera_point_target = set_camera_point_target;
1455 event.deathscreen.camera_point_target_x = camera_point_target.X;
1456 event.deathscreen.camera_point_target_y = camera_point_target.Y;
1457 event.deathscreen.camera_point_target_z = camera_point_target.Z;
1458 m_client_event_queue.push_back(event);
1460 else if(command == TOCLIENT_ANNOUNCE_MEDIA)
1462 std::string datastring((char*)&data[2], datasize-2);
1463 std::istringstream is(datastring, std::ios_base::binary);
1465 // Mesh update thread must be stopped while
1466 // updating content definitions
1467 assert(!m_mesh_update_thread.IsRunning());
1469 int num_files = readU16(is);
1471 infostream<<"Client: Received media announcement: packet size: "
1472 <<datasize<<std::endl;
1474 core::list<MediaRequest> file_requests;
1476 for(int i=0; i<num_files; i++)
1478 //read file from cache
1479 std::string name = deSerializeString(is);
1480 std::string sha1_base64 = deSerializeString(is);
1482 // if name contains illegal characters, ignore the file
1483 if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
1484 errorstream<<"Client: ignoring illegal file name "
1485 <<"sent by server: \""<<name<<"\""<<std::endl;
1489 std::string sha1_raw = base64_decode(sha1_base64);
1490 std::string sha1_hex = hex_encode(sha1_raw);
1491 std::ostringstream tmp_os(std::ios_base::binary);
1492 bool found_in_cache = m_media_cache.load_sha1(sha1_raw, tmp_os);
1493 m_media_name_sha1_map.set(name, sha1_raw);
1495 // If found in cache, try to load it from there
1498 bool success = loadMedia(tmp_os.str(), name);
1500 verbosestream<<"Client: Loaded cached media: "
1501 <<sha1_hex<<" \""<<name<<"\""<<std::endl;
1504 infostream<<"Client: Failed to load cached media: "
1505 <<sha1_hex<<" \""<<name<<"\""<<std::endl;
1508 // Didn't load from cache; queue it to be requested
1509 verbosestream<<"Client: Adding file to request list: \""
1510 <<sha1_hex<<" \""<<name<<"\""<<std::endl;
1511 file_requests.push_back(MediaRequest(name));
1515 event.type = CE_TEXTURES_UPDATED;
1516 m_client_event_queue.push_back(event);
1520 u16 number of files requested
1526 std::ostringstream os(std::ios_base::binary);
1527 writeU16(os, TOSERVER_REQUEST_MEDIA);
1528 writeU16(os, file_requests.size());
1530 for(core::list<MediaRequest>::Iterator i = file_requests.begin();
1531 i != file_requests.end(); i++) {
1532 os<<serializeString(i->name);
1536 std::string s = os.str();
1537 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1539 Send(0, data, true);
1540 infostream<<"Client: Sending media request list to server ("
1541 <<file_requests.size()<<" files)"<<std::endl;
1543 else if(command == TOCLIENT_MEDIA)
1545 std::string datastring((char*)&data[2], datasize-2);
1546 std::istringstream is(datastring, std::ios_base::binary);
1548 // Mesh update thread must be stopped while
1549 // updating content definitions
1550 assert(!m_mesh_update_thread.IsRunning());
1554 u16 total number of file bunches
1555 u16 index of this bunch
1556 u32 number of files in this bunch
1564 int num_bunches = readU16(is);
1565 int bunch_i = readU16(is);
1566 if(num_bunches >= 2)
1567 m_media_receive_progress = (float)bunch_i / (float)(num_bunches - 1);
1569 m_media_receive_progress = 1.0;
1570 if(bunch_i == num_bunches - 1)
1571 m_media_received = true;
1572 int num_files = readU32(is);
1573 infostream<<"Client: Received files: bunch "<<bunch_i<<"/"
1574 <<num_bunches<<" files="<<num_files
1575 <<" size="<<datasize<<std::endl;
1576 for(int i=0; i<num_files; i++){
1577 std::string name = deSerializeString(is);
1578 std::string data = deSerializeLongString(is);
1580 // if name contains illegal characters, ignore the file
1581 if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
1582 errorstream<<"Client: ignoring illegal file name "
1583 <<"sent by server: \""<<name<<"\""<<std::endl;
1587 bool success = loadMedia(data, name);
1589 verbosestream<<"Client: Loaded received media: "
1590 <<"\""<<name<<"\". Caching."<<std::endl;
1592 infostream<<"Client: Failed to load received media: "
1593 <<"\""<<name<<"\". Not caching."<<std::endl;
1597 bool did = fs::CreateAllDirs(getMediaCacheDir());
1599 errorstream<<"Could not create media cache directory"
1604 core::map<std::string, std::string>::Node *n;
1605 n = m_media_name_sha1_map.find(name);
1607 errorstream<<"The server sent a file that has not "
1608 <<"been announced."<<std::endl;
1610 m_media_cache.update_sha1(data);
1615 event.type = CE_TEXTURES_UPDATED;
1616 m_client_event_queue.push_back(event);
1618 else if(command == TOCLIENT_TOOLDEF)
1620 infostream<<"Client: WARNING: Ignoring TOCLIENT_TOOLDEF"<<std::endl;
1622 else if(command == TOCLIENT_NODEDEF)
1624 infostream<<"Client: Received node definitions: packet size: "
1625 <<datasize<<std::endl;
1627 // Mesh update thread must be stopped while
1628 // updating content definitions
1629 assert(!m_mesh_update_thread.IsRunning());
1631 // Decompress node definitions
1632 std::string datastring((char*)&data[2], datasize-2);
1633 std::istringstream is(datastring, std::ios_base::binary);
1634 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1635 std::ostringstream tmp_os;
1636 decompressZlib(tmp_is, tmp_os);
1638 // Deserialize node definitions
1639 std::istringstream tmp_is2(tmp_os.str());
1640 m_nodedef->deSerialize(tmp_is2);
1641 m_nodedef_received = true;
1643 else if(command == TOCLIENT_CRAFTITEMDEF)
1645 infostream<<"Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF"<<std::endl;
1647 else if(command == TOCLIENT_ITEMDEF)
1649 infostream<<"Client: Received item definitions: packet size: "
1650 <<datasize<<std::endl;
1652 // Mesh update thread must be stopped while
1653 // updating content definitions
1654 assert(!m_mesh_update_thread.IsRunning());
1656 // Decompress item definitions
1657 std::string datastring((char*)&data[2], datasize-2);
1658 std::istringstream is(datastring, std::ios_base::binary);
1659 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1660 std::ostringstream tmp_os;
1661 decompressZlib(tmp_is, tmp_os);
1663 // Deserialize node definitions
1664 std::istringstream tmp_is2(tmp_os.str());
1665 m_itemdef->deSerialize(tmp_is2);
1666 m_itemdef_received = true;
1668 else if(command == TOCLIENT_PLAY_SOUND)
1670 std::string datastring((char*)&data[2], datasize-2);
1671 std::istringstream is(datastring, std::ios_base::binary);
1673 s32 server_id = readS32(is);
1674 std::string name = deSerializeString(is);
1675 float gain = readF1000(is);
1676 int type = readU8(is); // 0=local, 1=positional, 2=object
1677 v3f pos = readV3F1000(is);
1678 u16 object_id = readU16(is);
1679 bool loop = readU8(is);
1684 client_id = m_sound->playSound(name, loop, gain);
1686 case 1: // positional
1687 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1690 ClientActiveObject *cao = m_env.getActiveObject(object_id);
1692 pos = cao->getPosition();
1693 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1694 // TODO: Set up sound to move with object
1699 if(client_id != -1){
1700 m_sounds_server_to_client[server_id] = client_id;
1701 m_sounds_client_to_server[client_id] = server_id;
1703 m_sounds_to_objects[client_id] = object_id;
1706 else if(command == TOCLIENT_STOP_SOUND)
1708 std::string datastring((char*)&data[2], datasize-2);
1709 std::istringstream is(datastring, std::ios_base::binary);
1711 s32 server_id = readS32(is);
1712 std::map<s32, int>::iterator i =
1713 m_sounds_server_to_client.find(server_id);
1714 if(i != m_sounds_server_to_client.end()){
1715 int client_id = i->second;
1716 m_sound->stopSound(client_id);
1719 else if(command == TOCLIENT_PRIVILEGES)
1721 std::string datastring((char*)&data[2], datasize-2);
1722 std::istringstream is(datastring, std::ios_base::binary);
1724 m_privileges.clear();
1725 infostream<<"Client: Privileges updated: ";
1726 u16 num_privileges = readU16(is);
1727 for(u16 i=0; i<num_privileges; i++){
1728 std::string priv = deSerializeString(is);
1729 m_privileges.insert(priv);
1730 infostream<<priv<<" ";
1732 infostream<<std::endl;
1734 else if(command == TOCLIENT_INVENTORY_FORMSPEC)
1736 std::string datastring((char*)&data[2], datasize-2);
1737 std::istringstream is(datastring, std::ios_base::binary);
1739 // Store formspec in LocalPlayer
1740 Player *player = m_env.getLocalPlayer();
1741 assert(player != NULL);
1742 player->inventory_formspec = deSerializeLongString(is);
1744 else if(command == TOCLIENT_DETACHED_INVENTORY)
1746 std::string datastring((char*)&data[2], datasize-2);
1747 std::istringstream is(datastring, std::ios_base::binary);
1749 std::string name = deSerializeString(is);
1751 infostream<<"Client: Detached inventory update: \""<<name<<"\""<<std::endl;
1753 Inventory *inv = NULL;
1754 if(m_detached_inventories.count(name) > 0)
1755 inv = m_detached_inventories[name];
1757 inv = new Inventory(m_itemdef);
1758 m_detached_inventories[name] = inv;
1760 inv->deSerialize(is);
1764 infostream<<"Client: Ignoring unknown command "
1765 <<command<<std::endl;
1769 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
1771 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1772 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
1775 void Client::interact(u8 action, const PointedThing& pointed)
1777 if(connectedAndInitialized() == false){
1778 infostream<<"Client::interact() "
1779 "cancelled (not connected)"
1784 std::ostringstream os(std::ios_base::binary);
1790 [5] u32 length of the next item
1791 [9] serialized PointedThing
1793 0: start digging (from undersurface) or use
1794 1: stop digging (all parameters ignored)
1795 2: digging completed
1796 3: place block or item (to abovesurface)
1799 writeU16(os, TOSERVER_INTERACT);
1800 writeU8(os, action);
1801 writeU16(os, getPlayerItem());
1802 std::ostringstream tmp_os(std::ios::binary);
1803 pointed.serialize(tmp_os);
1804 os<<serializeLongString(tmp_os.str());
1806 std::string s = os.str();
1807 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1810 Send(0, data, true);
1813 void Client::sendNodemetaFields(v3s16 p, const std::string &formname,
1814 const std::map<std::string, std::string> &fields)
1816 std::ostringstream os(std::ios_base::binary);
1818 writeU16(os, TOSERVER_NODEMETA_FIELDS);
1820 os<<serializeString(formname);
1821 writeU16(os, fields.size());
1822 for(std::map<std::string, std::string>::const_iterator
1823 i = fields.begin(); i != fields.end(); i++){
1824 const std::string &name = i->first;
1825 const std::string &value = i->second;
1826 os<<serializeString(name);
1827 os<<serializeLongString(value);
1831 std::string s = os.str();
1832 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1834 Send(0, data, true);
1837 void Client::sendInventoryFields(const std::string &formname,
1838 const std::map<std::string, std::string> &fields)
1840 std::ostringstream os(std::ios_base::binary);
1842 writeU16(os, TOSERVER_INVENTORY_FIELDS);
1843 os<<serializeString(formname);
1844 writeU16(os, fields.size());
1845 for(std::map<std::string, std::string>::const_iterator
1846 i = fields.begin(); i != fields.end(); i++){
1847 const std::string &name = i->first;
1848 const std::string &value = i->second;
1849 os<<serializeString(name);
1850 os<<serializeLongString(value);
1854 std::string s = os.str();
1855 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1857 Send(0, data, true);
1860 void Client::sendInventoryAction(InventoryAction *a)
1862 std::ostringstream os(std::ios_base::binary);
1866 writeU16(buf, TOSERVER_INVENTORY_ACTION);
1867 os.write((char*)buf, 2);
1872 std::string s = os.str();
1873 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1875 Send(0, data, true);
1878 void Client::sendChatMessage(const std::wstring &message)
1880 std::ostringstream os(std::ios_base::binary);
1884 writeU16(buf, TOSERVER_CHAT_MESSAGE);
1885 os.write((char*)buf, 2);
1888 writeU16(buf, message.size());
1889 os.write((char*)buf, 2);
1892 for(u32 i=0; i<message.size(); i++)
1896 os.write((char*)buf, 2);
1900 std::string s = os.str();
1901 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1903 Send(0, data, true);
1906 void Client::sendChangePassword(const std::wstring oldpassword,
1907 const std::wstring newpassword)
1909 Player *player = m_env.getLocalPlayer();
1913 std::string playername = player->getName();
1914 std::string oldpwd = translatePassword(playername, oldpassword);
1915 std::string newpwd = translatePassword(playername, newpassword);
1917 std::ostringstream os(std::ios_base::binary);
1918 u8 buf[2+PASSWORD_SIZE*2];
1920 [0] u16 TOSERVER_PASSWORD
1921 [2] u8[28] old password
1922 [30] u8[28] new password
1925 writeU16(buf, TOSERVER_PASSWORD);
1926 for(u32 i=0;i<PASSWORD_SIZE-1;i++)
1928 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
1929 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
1931 buf[2+PASSWORD_SIZE-1] = 0;
1932 buf[30+PASSWORD_SIZE-1] = 0;
1933 os.write((char*)buf, 2+PASSWORD_SIZE*2);
1936 std::string s = os.str();
1937 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1939 Send(0, data, true);
1943 void Client::sendDamage(u8 damage)
1945 DSTACK(__FUNCTION_NAME);
1946 std::ostringstream os(std::ios_base::binary);
1948 writeU16(os, TOSERVER_DAMAGE);
1949 writeU8(os, damage);
1952 std::string s = os.str();
1953 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1955 Send(0, data, true);
1958 void Client::sendRespawn()
1960 DSTACK(__FUNCTION_NAME);
1961 std::ostringstream os(std::ios_base::binary);
1963 writeU16(os, TOSERVER_RESPAWN);
1966 std::string s = os.str();
1967 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1969 Send(0, data, true);
1972 void Client::sendPlayerPos()
1974 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1976 Player *myplayer = m_env.getLocalPlayer();
1977 if(myplayer == NULL)
1982 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1983 our_peer_id = m_con.GetPeerID();
1986 // Set peer id if not set already
1987 if(myplayer->peer_id == PEER_ID_INEXISTENT)
1988 myplayer->peer_id = our_peer_id;
1989 // Check that an existing peer_id is the same as the connection's
1990 assert(myplayer->peer_id == our_peer_id);
1992 v3f pf = myplayer->getPosition();
1993 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
1994 v3f sf = myplayer->getSpeed();
1995 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
1996 s32 pitch = myplayer->getPitch() * 100;
1997 s32 yaw = myplayer->getYaw() * 100;
1998 u32 keyPressed=myplayer->keyPressed;
2002 [2] v3s32 position*100
2003 [2+12] v3s32 speed*100
2004 [2+12+12] s32 pitch*100
2005 [2+12+12+4] s32 yaw*100
2006 [2+12+12+4+4] u32 keyPressed
2008 SharedBuffer<u8> data(2+12+12+4+4+4);
2009 writeU16(&data[0], TOSERVER_PLAYERPOS);
2010 writeV3S32(&data[2], position);
2011 writeV3S32(&data[2+12], speed);
2012 writeS32(&data[2+12+12], pitch);
2013 writeS32(&data[2+12+12+4], yaw);
2014 writeU32(&data[2+12+12+4+4], keyPressed);
2015 // Send as unreliable
2016 Send(0, data, false);
2019 void Client::sendPlayerItem(u16 item)
2021 Player *myplayer = m_env.getLocalPlayer();
2022 if(myplayer == NULL)
2025 u16 our_peer_id = m_con.GetPeerID();
2027 // Set peer id if not set already
2028 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2029 myplayer->peer_id = our_peer_id;
2030 // Check that an existing peer_id is the same as the connection's
2031 assert(myplayer->peer_id == our_peer_id);
2033 SharedBuffer<u8> data(2+2);
2034 writeU16(&data[0], TOSERVER_PLAYERITEM);
2035 writeU16(&data[2], item);
2038 Send(0, data, true);
2041 void Client::removeNode(v3s16 p)
2043 core::map<v3s16, MapBlock*> modified_blocks;
2047 //TimeTaker t("removeNodeAndUpdate", m_device);
2048 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
2050 catch(InvalidPositionException &e)
2054 // add urgent task to update the modified node
2055 addUpdateMeshTaskForNode(p, false, true);
2057 for(core::map<v3s16, MapBlock * >::Iterator
2058 i = modified_blocks.getIterator();
2059 i.atEnd() == false; i++)
2061 v3s16 p = i.getNode()->getKey();
2062 addUpdateMeshTaskWithEdge(p);
2066 void Client::addNode(v3s16 p, MapNode n)
2068 TimeTaker timer1("Client::addNode()");
2070 core::map<v3s16, MapBlock*> modified_blocks;
2074 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
2075 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks);
2077 catch(InvalidPositionException &e)
2080 for(core::map<v3s16, MapBlock * >::Iterator
2081 i = modified_blocks.getIterator();
2082 i.atEnd() == false; i++)
2084 v3s16 p = i.getNode()->getKey();
2085 addUpdateMeshTaskWithEdge(p);
2089 void Client::setPlayerControl(PlayerControl &control)
2091 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2092 LocalPlayer *player = m_env.getLocalPlayer();
2093 assert(player != NULL);
2094 player->control = control;
2097 void Client::selectPlayerItem(u16 item)
2099 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2100 m_playeritem = item;
2101 m_inventory_updated = true;
2102 sendPlayerItem(item);
2105 // Returns true if the inventory of the local player has been
2106 // updated from the server. If it is true, it is set to false.
2107 bool Client::getLocalInventoryUpdated()
2109 // m_inventory_updated is behind envlock
2110 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2111 bool updated = m_inventory_updated;
2112 m_inventory_updated = false;
2116 // Copies the inventory of the local player to parameter
2117 void Client::getLocalInventory(Inventory &dst)
2119 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2120 Player *player = m_env.getLocalPlayer();
2121 assert(player != NULL);
2122 dst = player->inventory;
2125 Inventory* Client::getInventory(const InventoryLocation &loc)
2128 case InventoryLocation::UNDEFINED:
2131 case InventoryLocation::CURRENT_PLAYER:
2133 Player *player = m_env.getLocalPlayer();
2134 assert(player != NULL);
2135 return &player->inventory;
2138 case InventoryLocation::PLAYER:
2140 Player *player = m_env.getPlayer(loc.name.c_str());
2143 return &player->inventory;
2146 case InventoryLocation::NODEMETA:
2148 NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p);
2151 return meta->getInventory();
2154 case InventoryLocation::DETACHED:
2156 if(m_detached_inventories.count(loc.name) == 0)
2158 return m_detached_inventories[loc.name];
2166 void Client::inventoryAction(InventoryAction *a)
2169 Send it to the server
2171 sendInventoryAction(a);
2174 Predict some local inventory changes
2176 a->clientApply(this, this);
2179 ClientActiveObject * Client::getSelectedActiveObject(
2181 v3f from_pos_f_on_map,
2182 core::line3d<f32> shootline_on_map
2185 core::array<DistanceSortedActiveObject> objects;
2187 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
2189 //infostream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
2192 // After this, the closest object is the first in the array.
2195 for(u32 i=0; i<objects.size(); i++)
2197 ClientActiveObject *obj = objects[i].obj;
2199 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
2200 if(selection_box == NULL)
2203 v3f pos = obj->getPosition();
2205 core::aabbox3d<f32> offsetted_box(
2206 selection_box->MinEdge + pos,
2207 selection_box->MaxEdge + pos
2210 if(offsetted_box.intersectsWithLine(shootline_on_map))
2212 //infostream<<"Returning selected object"<<std::endl;
2217 //infostream<<"No object selected; returning NULL."<<std::endl;
2221 void Client::printDebugInfo(std::ostream &os)
2223 //JMutexAutoLock lock1(m_fetchblock_mutex);
2224 /*JMutexAutoLock lock2(m_incoming_queue_mutex);
2226 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
2227 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
2228 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
2232 core::list<std::wstring> Client::getConnectedPlayerNames()
2234 core::list<Player*> players = m_env.getPlayers(true);
2235 core::list<std::wstring> playerNames;
2236 for(core::list<Player*>::Iterator
2237 i = players.begin();
2238 i != players.end(); i++)
2240 Player *player = *i;
2241 playerNames.push_back(narrow_to_wide(player->getName()));
2246 float Client::getAnimationTime()
2248 return m_animation_time;
2251 int Client::getCrackLevel()
2253 return m_crack_level;
2256 void Client::setCrack(int level, v3s16 pos)
2258 int old_crack_level = m_crack_level;
2259 v3s16 old_crack_pos = m_crack_pos;
2261 m_crack_level = level;
2264 if(old_crack_level >= 0 && (level < 0 || pos != old_crack_pos))
2267 addUpdateMeshTaskForNode(old_crack_pos, false, true);
2269 if(level >= 0 && (old_crack_level < 0 || pos != old_crack_pos))
2272 addUpdateMeshTaskForNode(pos, false, true);
2278 Player *player = m_env.getLocalPlayer();
2279 assert(player != NULL);
2283 bool Client::getChatMessage(std::wstring &message)
2285 if(m_chat_queue.size() == 0)
2287 message = m_chat_queue.pop_front();
2291 void Client::typeChatMessage(const std::wstring &message)
2293 // Discard empty line
2298 sendChatMessage(message);
2301 if (message[0] == L'/')
2303 m_chat_queue.push_back(
2304 (std::wstring)L"issued command: "+message);
2308 LocalPlayer *player = m_env.getLocalPlayer();
2309 assert(player != NULL);
2310 std::wstring name = narrow_to_wide(player->getName());
2311 m_chat_queue.push_back(
2312 (std::wstring)L"<"+name+L"> "+message);
2316 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
2318 /*infostream<<"Client::addUpdateMeshTask(): "
2319 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2320 <<" ack_to_server="<<ack_to_server
2321 <<" urgent="<<urgent
2324 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2329 Create a task to update the mesh of the block
2332 MeshMakeData *data = new MeshMakeData(this);
2335 //TimeTaker timer("data fill");
2337 // Debug: 1-6ms, avg=2ms
2339 data->setCrack(m_crack_level, m_crack_pos);
2340 data->setSmoothLighting(g_settings->getBool("smooth_lighting"));
2344 //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
2346 // Add task to queue
2347 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server, urgent);
2349 /*infostream<<"Mesh update input queue size is "
2350 <<m_mesh_update_thread.m_queue_in.size()
2354 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent)
2358 infostream<<"Client::addUpdateMeshTaskWithEdge(): "
2359 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2364 v3s16 p = blockpos + v3s16(0,0,0);
2365 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2366 addUpdateMeshTask(p, ack_to_server, urgent);
2368 catch(InvalidPositionException &e){}
2371 v3s16 p = blockpos + v3s16(-1,0,0);
2372 addUpdateMeshTask(p, false, urgent);
2374 catch(InvalidPositionException &e){}
2376 v3s16 p = blockpos + v3s16(0,-1,0);
2377 addUpdateMeshTask(p, false, urgent);
2379 catch(InvalidPositionException &e){}
2381 v3s16 p = blockpos + v3s16(0,0,-1);
2382 addUpdateMeshTask(p, false, urgent);
2384 catch(InvalidPositionException &e){}
2387 void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool urgent)
2391 infostream<<"Client::addUpdateMeshTaskForNode(): "
2392 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2396 v3s16 blockpos = getNodeBlockPos(nodepos);
2397 v3s16 blockpos_relative = blockpos * MAP_BLOCKSIZE;
2400 v3s16 p = blockpos + v3s16(0,0,0);
2401 addUpdateMeshTask(p, ack_to_server, urgent);
2403 catch(InvalidPositionException &e){}
2405 if(nodepos.X == blockpos_relative.X){
2407 v3s16 p = blockpos + v3s16(-1,0,0);
2408 addUpdateMeshTask(p, false, urgent);
2410 catch(InvalidPositionException &e){}
2412 if(nodepos.Y == blockpos_relative.Y){
2414 v3s16 p = blockpos + v3s16(0,-1,0);
2415 addUpdateMeshTask(p, false, urgent);
2417 catch(InvalidPositionException &e){}
2419 if(nodepos.Z == blockpos_relative.Z){
2421 v3s16 p = blockpos + v3s16(0,0,-1);
2422 addUpdateMeshTask(p, false, urgent);
2424 catch(InvalidPositionException &e){}
2428 ClientEvent Client::getClientEvent()
2430 if(m_client_event_queue.size() == 0)
2433 event.type = CE_NONE;
2436 return m_client_event_queue.pop_front();
2439 void Client::afterContentReceived()
2441 infostream<<"Client::afterContentReceived() started"<<std::endl;
2442 assert(m_itemdef_received);
2443 assert(m_nodedef_received);
2444 assert(m_media_received);
2446 // remove the information about which checksum each texture
2448 m_media_name_sha1_map.clear();
2450 // Rebuild inherited images and recreate textures
2451 infostream<<"- Rebuilding images and textures"<<std::endl;
2452 m_tsrc->rebuildImagesAndTextures();
2454 // Update texture atlas
2455 infostream<<"- Updating texture atlas"<<std::endl;
2456 if(g_settings->getBool("enable_texture_atlas"))
2457 m_tsrc->buildMainAtlas(this);
2459 // Update node aliases
2460 infostream<<"- Updating node aliases"<<std::endl;
2461 m_nodedef->updateAliases(m_itemdef);
2463 // Update node textures
2464 infostream<<"- Updating node textures"<<std::endl;
2465 m_nodedef->updateTextures(m_tsrc);
2467 // Preload item textures and meshes if configured to
2468 if(g_settings->getBool("preload_item_visuals"))
2470 verbosestream<<"Updating item textures and meshes"<<std::endl;
2471 std::set<std::string> names = m_itemdef->getAll();
2472 for(std::set<std::string>::const_iterator
2473 i = names.begin(); i != names.end(); ++i){
2474 // Asking for these caches the result
2475 m_itemdef->getInventoryTexture(*i, this);
2476 m_itemdef->getWieldMesh(*i, this);
2480 // Start mesh update thread after setting up content definitions
2481 infostream<<"- Starting mesh update thread"<<std::endl;
2482 m_mesh_update_thread.Start();
2484 infostream<<"Client::afterContentReceived() done"<<std::endl;
2487 float Client::getRTT(void)
2490 return m_con.GetPeerAvgRTT(PEER_ID_SERVER);
2491 } catch(con::PeerNotFoundException &e){
2496 // IGameDef interface
2498 IItemDefManager* Client::getItemDefManager()
2502 INodeDefManager* Client::getNodeDefManager()
2506 ICraftDefManager* Client::getCraftDefManager()
2509 //return m_craftdef;
2511 ITextureSource* Client::getTextureSource()
2515 u16 Client::allocateUnknownNodeId(const std::string &name)
2517 errorstream<<"Client::allocateUnknownNodeId(): "
2518 <<"Client cannot allocate node IDs"<<std::endl;
2520 return CONTENT_IGNORE;
2522 ISoundManager* Client::getSoundManager()
2526 MtEventManager* Client::getEventManager()