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"
45 static std::string getMediaCacheDir()
47 return porting::path_user + DIR_DELIM + "cache" + DIR_DELIM + "media";
54 MediaRequest(const std::string &name_=""):
63 QueuedMeshUpdate::QueuedMeshUpdate():
66 ack_block_to_server(false)
70 QueuedMeshUpdate::~QueuedMeshUpdate()
80 MeshUpdateQueue::MeshUpdateQueue()
85 MeshUpdateQueue::~MeshUpdateQueue()
87 JMutexAutoLock lock(m_mutex);
89 for(std::vector<QueuedMeshUpdate*>::iterator
91 i != m_queue.end(); i++)
93 QueuedMeshUpdate *q = *i;
99 peer_id=0 adds with nobody to send to
101 void MeshUpdateQueue::addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_server, bool urgent)
103 DSTACK(__FUNCTION_NAME);
107 JMutexAutoLock lock(m_mutex);
113 Find if block is already in queue.
114 If it is, update the data and quit.
116 for(std::vector<QueuedMeshUpdate*>::iterator
118 i != m_queue.end(); i++)
120 QueuedMeshUpdate *q = *i;
126 if(ack_block_to_server)
127 q->ack_block_to_server = true;
135 QueuedMeshUpdate *q = new QueuedMeshUpdate;
138 q->ack_block_to_server = ack_block_to_server;
139 m_queue.push_back(q);
142 // Returned pointer must be deleted
143 // Returns NULL if queue is empty
144 QueuedMeshUpdate * MeshUpdateQueue::pop()
146 JMutexAutoLock lock(m_mutex);
148 bool must_be_urgent = !m_urgents.empty();
149 for(std::vector<QueuedMeshUpdate*>::iterator
151 i != m_queue.end(); i++)
153 QueuedMeshUpdate *q = *i;
154 if(must_be_urgent && m_urgents.count(q->p) == 0)
157 m_urgents.erase(q->p);
167 void * MeshUpdateThread::Thread()
171 log_register_thread("MeshUpdateThread");
173 DSTACK(__FUNCTION_NAME);
175 BEGIN_DEBUG_EXCEPTION_HANDLER
179 /*// Wait for output queue to flush.
180 // Allow 2 in queue, this makes less frametime jitter.
181 // Umm actually, there is no much difference
182 if(m_queue_out.size() >= 2)
188 QueuedMeshUpdate *q = m_queue_in.pop();
195 ScopeProfiler sp(g_profiler, "Client: Mesh making");
197 MapBlockMesh *mesh_new = new MapBlockMesh(q->data);
198 if(mesh_new->getMesh()->getMeshBufferCount() == 0)
207 r.ack_block_to_server = q->ack_block_to_server;
209 /*infostream<<"MeshUpdateThread: Processed "
210 <<"("<<q->p.X<<","<<q->p.Y<<","<<q->p.Z<<")"
213 m_queue_out.push_back(r);
218 END_DEBUG_EXCEPTION_HANDLER(errorstream)
224 IrrlichtDevice *device,
225 const char *playername,
226 std::string password,
227 MapDrawControl &control,
228 IWritableTextureSource *tsrc,
229 IWritableItemDefManager *itemdef,
230 IWritableNodeDefManager *nodedef,
231 ISoundManager *sound,
232 MtEventManager *event
239 m_mesh_update_thread(this),
241 new ClientMap(this, this, control,
242 device->getSceneManager()->getRootSceneNode(),
243 device->getSceneManager(), 666),
244 device->getSceneManager(),
247 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
249 m_server_ser_ver(SER_FMT_VER_INVALID),
251 m_inventory_updated(false),
252 m_inventory_from_server(NULL),
253 m_inventory_from_server_age(0.0),
258 m_password(password),
259 m_access_denied(false),
260 m_media_cache(getMediaCacheDir()),
261 m_media_receive_progress(0),
262 m_media_received(false),
263 m_itemdef_received(false),
264 m_nodedef_received(false),
265 m_time_of_day_set(false),
266 m_last_time_of_day_f(-1),
267 m_time_of_day_update_timer(0),
268 m_removed_sounds_check_timer(0)
270 m_packetcounter_timer = 0.0;
271 //m_delete_unused_sectors_timer = 0.0;
272 m_connection_reinit_timer = 0.0;
273 m_avg_rtt_timer = 0.0;
274 m_playerpos_send_timer = 0.0;
275 m_ignore_damage_timer = 0.0;
277 // Build main texture atlas, now that the GameDef exists (that is, us)
278 if(g_settings->getBool("enable_texture_atlas"))
279 m_tsrc->buildMainAtlas(this);
281 infostream<<"Not building texture atlas."<<std::endl;
287 Player *player = new LocalPlayer(this);
289 player->updateName(playername);
291 m_env.addPlayer(player);
298 //JMutexAutoLock conlock(m_con_mutex); //bulk comment-out
302 m_mesh_update_thread.setRun(false);
303 while(m_mesh_update_thread.IsRunning())
306 delete m_inventory_from_server;
308 // Delete detached inventories
310 for(std::map<std::string, Inventory*>::iterator
311 i = m_detached_inventories.begin();
312 i != m_detached_inventories.end(); i++){
318 void Client::connect(Address address)
320 DSTACK(__FUNCTION_NAME);
321 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
322 m_con.SetTimeoutMs(0);
323 m_con.Connect(address);
326 bool Client::connectedAndInitialized()
328 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
330 if(m_con.Connected() == false)
333 if(m_server_ser_ver == SER_FMT_VER_INVALID)
339 void Client::step(float dtime)
341 DSTACK(__FUNCTION_NAME);
347 if(m_ignore_damage_timer > dtime)
348 m_ignore_damage_timer -= dtime;
350 m_ignore_damage_timer = 0.0;
352 m_animation_time += dtime;
353 if(m_animation_time > 60.0)
354 m_animation_time -= 60.0;
356 m_time_of_day_update_timer += dtime;
358 //infostream<<"Client steps "<<dtime<<std::endl;
361 //TimeTaker timer("ReceiveAll()", m_device);
367 //TimeTaker timer("m_con_mutex + m_con.RunTimeouts()", m_device);
369 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
370 m_con.RunTimeouts(dtime);
377 float &counter = m_packetcounter_timer;
383 infostream<<"Client packetcounter (20s):"<<std::endl;
384 m_packetcounter.print(infostream);
385 m_packetcounter.clear();
389 // Get connection status
390 bool connected = connectedAndInitialized();
395 Delete unused sectors
397 NOTE: This jams the game for a while because deleting sectors
401 float &counter = m_delete_unused_sectors_timer;
409 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
411 core::list<v3s16> deleted_blocks;
413 float delete_unused_sectors_timeout =
414 g_settings->getFloat("client_delete_unused_sectors_timeout");
416 // Delete sector blocks
417 /*u32 num = m_env.getMap().unloadUnusedData
418 (delete_unused_sectors_timeout,
419 true, &deleted_blocks);*/
421 // Delete whole sectors
422 m_env.getMap().unloadUnusedData
423 (delete_unused_sectors_timeout,
426 if(deleted_blocks.size() > 0)
428 /*infostream<<"Client: Deleted blocks of "<<num
429 <<" unused sectors"<<std::endl;*/
430 /*infostream<<"Client: Deleted "<<num
431 <<" unused sectors"<<std::endl;*/
437 // Env is locked so con can be locked.
438 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
440 core::list<v3s16>::Iterator i = deleted_blocks.begin();
441 core::list<v3s16> sendlist;
444 if(sendlist.size() == 255 || i == deleted_blocks.end())
446 if(sendlist.size() == 0)
455 u32 replysize = 2+1+6*sendlist.size();
456 SharedBuffer<u8> reply(replysize);
457 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
458 reply[2] = sendlist.size();
460 for(core::list<v3s16>::Iterator
461 j = sendlist.begin();
462 j != sendlist.end(); j++)
464 writeV3S16(&reply[2+1+6*k], *j);
467 m_con.Send(PEER_ID_SERVER, 1, reply, true);
469 if(i == deleted_blocks.end())
475 sendlist.push_back(*i);
483 if(connected == false)
485 float &counter = m_connection_reinit_timer;
491 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
493 Player *myplayer = m_env.getLocalPlayer();
494 assert(myplayer != NULL);
496 // Send TOSERVER_INIT
497 // [0] u16 TOSERVER_INIT
498 // [2] u8 SER_FMT_VER_HIGHEST
499 // [3] u8[20] player_name
500 // [23] u8[28] password (new in some version)
501 // [51] u16 client network protocol version (new in some version)
502 SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2);
503 writeU16(&data[0], TOSERVER_INIT);
504 writeU8(&data[2], SER_FMT_VER_HIGHEST);
506 memset((char*)&data[3], 0, PLAYERNAME_SIZE);
507 snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
509 /*infostream<<"Client: sending initial password hash: \""<<m_password<<"\""
512 memset((char*)&data[23], 0, PASSWORD_SIZE);
513 snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
515 // This should be incremented in each version
516 writeU16(&data[51], PROTOCOL_VERSION);
518 // Send as unreliable
519 Send(0, data, false);
522 // Not connected, return
527 Do stuff if connected
531 Run Map's timers and unload unused data
533 const float map_timer_and_unload_dtime = 5.25;
534 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
536 ScopeProfiler sp(g_profiler, "Client: map timer and unload");
537 core::list<v3s16> deleted_blocks;
538 m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
539 g_settings->getFloat("client_unload_unused_data_timeout"),
542 /*if(deleted_blocks.size() > 0)
543 infostream<<"Client: Unloaded "<<deleted_blocks.size()
544 <<" unused blocks"<<std::endl;*/
548 NOTE: This loop is intentionally iterated the way it is.
551 core::list<v3s16>::Iterator i = deleted_blocks.begin();
552 core::list<v3s16> sendlist;
555 if(sendlist.size() == 255 || i == deleted_blocks.end())
557 if(sendlist.size() == 0)
566 u32 replysize = 2+1+6*sendlist.size();
567 SharedBuffer<u8> reply(replysize);
568 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
569 reply[2] = sendlist.size();
571 for(core::list<v3s16>::Iterator
572 j = sendlist.begin();
573 j != sendlist.end(); j++)
575 writeV3S16(&reply[2+1+6*k], *j);
578 m_con.Send(PEER_ID_SERVER, 1, reply, true);
580 if(i == deleted_blocks.end())
586 sendlist.push_back(*i);
596 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
598 // Control local player (0ms)
599 LocalPlayer *player = m_env.getLocalPlayer();
600 assert(player != NULL);
601 player->applyControl(dtime);
603 //TimeTaker envtimer("env step", m_device);
612 ClientEnvEvent event = m_env.getClientEvent();
613 if(event.type == CEE_NONE)
617 else if(event.type == CEE_PLAYER_DAMAGE)
619 if(m_ignore_damage_timer <= 0)
621 u8 damage = event.player_damage.amount;
623 if(event.player_damage.send_to_server)
626 // Add to ClientEvent queue
628 event.type = CE_PLAYER_DAMAGE;
629 event.player_damage.amount = damage;
630 m_client_event_queue.push_back(event);
640 float &counter = m_avg_rtt_timer;
645 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
646 // connectedAndInitialized() is true, peer exists.
647 float avg_rtt = m_con.GetPeerAvgRTT(PEER_ID_SERVER);
648 infostream<<"Client: avg_rtt="<<avg_rtt<<std::endl;
653 Send player position to server
656 float &counter = m_playerpos_send_timer;
666 Replace updated meshes
669 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
671 //TimeTaker timer("** Processing mesh update result queue");
674 /*infostream<<"Mesh update result queue size is "
675 <<m_mesh_update_thread.m_queue_out.size()
678 int num_processed_meshes = 0;
679 while(m_mesh_update_thread.m_queue_out.size() > 0)
681 num_processed_meshes++;
682 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_front();
683 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
686 //JMutexAutoLock lock(block->mesh_mutex);
688 // Delete the old mesh
689 if(block->mesh != NULL)
691 // TODO: Remove hardware buffers of meshbuffers of block->mesh
696 // Replace with the new mesh
697 block->mesh = r.mesh;
699 if(r.ack_block_to_server)
701 /*infostream<<"Client: ACK block ("<<r.p.X<<","<<r.p.Y
702 <<","<<r.p.Z<<")"<<std::endl;*/
713 u32 replysize = 2+1+6;
714 SharedBuffer<u8> reply(replysize);
715 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
717 writeV3S16(&reply[3], r.p);
719 m_con.Send(PEER_ID_SERVER, 1, reply, true);
722 if(num_processed_meshes > 0)
723 g_profiler->graphAdd("num_processed_meshes", num_processed_meshes);
727 If the server didn't update the inventory in a while, revert
728 the local inventory (so the player notices the lag problem
729 and knows something is wrong).
731 if(m_inventory_from_server)
733 float interval = 10.0;
734 float count_before = floor(m_inventory_from_server_age / interval);
736 m_inventory_from_server_age += dtime;
738 float count_after = floor(m_inventory_from_server_age / interval);
740 if(count_after != count_before)
742 // Do this every <interval> seconds after TOCLIENT_INVENTORY
743 // Reset the locally changed inventory to the authoritative inventory
744 Player *player = m_env.getLocalPlayer();
745 player->inventory = *m_inventory_from_server;
746 m_inventory_updated = true;
751 Update positions of sounds attached to objects
754 for(std::map<int, u16>::iterator
755 i = m_sounds_to_objects.begin();
756 i != m_sounds_to_objects.end(); i++)
758 int client_id = i->first;
759 u16 object_id = i->second;
760 ClientActiveObject *cao = m_env.getActiveObject(object_id);
763 v3f pos = cao->getPosition();
764 m_sound->updateSoundPosition(client_id, pos);
769 Handle removed remotely initiated sounds
771 m_removed_sounds_check_timer += dtime;
772 if(m_removed_sounds_check_timer >= 2.32)
774 m_removed_sounds_check_timer = 0;
775 // Find removed sounds and clear references to them
776 std::set<s32> removed_server_ids;
777 for(std::map<s32, int>::iterator
778 i = m_sounds_server_to_client.begin();
779 i != m_sounds_server_to_client.end();)
781 s32 server_id = i->first;
782 int client_id = i->second;
784 if(!m_sound->soundExists(client_id)){
785 m_sounds_server_to_client.erase(server_id);
786 m_sounds_client_to_server.erase(client_id);
787 m_sounds_to_objects.erase(client_id);
788 removed_server_ids.insert(server_id);
792 if(removed_server_ids.size() != 0)
794 std::ostringstream os(std::ios_base::binary);
795 writeU16(os, TOSERVER_REMOVED_SOUNDS);
796 writeU16(os, removed_server_ids.size());
797 for(std::set<s32>::iterator i = removed_server_ids.begin();
798 i != removed_server_ids.end(); i++)
800 std::string s = os.str();
801 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
808 bool Client::loadMedia(const std::string &data, const std::string &filename)
810 // Silly irrlicht's const-incorrectness
811 Buffer<char> data_rw(data.c_str(), data.size());
815 const char *image_ext[] = {
816 ".png", ".jpg", ".bmp", ".tga",
817 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
820 name = removeStringEnd(filename, image_ext);
823 verbosestream<<"Client: Attempting to load image "
824 <<"file \""<<filename<<"\""<<std::endl;
826 io::IFileSystem *irrfs = m_device->getFileSystem();
827 video::IVideoDriver *vdrv = m_device->getVideoDriver();
829 // Create an irrlicht memory file
830 io::IReadFile *rfile = irrfs->createMemoryReadFile(
831 *data_rw, data_rw.getSize(), "_tempreadfile");
834 video::IImage *img = vdrv->createImageFromFile(rfile);
836 errorstream<<"Client: Cannot create image from data of "
837 <<"file \""<<filename<<"\""<<std::endl;
842 m_tsrc->insertSourceImage(filename, img);
849 const char *sound_ext[] = {
850 ".0.ogg", ".1.ogg", ".2.ogg", ".3.ogg", ".4.ogg",
851 ".5.ogg", ".6.ogg", ".7.ogg", ".8.ogg", ".9.ogg",
854 name = removeStringEnd(filename, sound_ext);
857 verbosestream<<"Client: Attempting to load sound "
858 <<"file \""<<filename<<"\""<<std::endl;
859 m_sound->loadSoundData(name, data);
863 errorstream<<"Client: Don't know how to load file \""
864 <<filename<<"\""<<std::endl;
868 // Virtual methods from con::PeerHandler
869 void Client::peerAdded(con::Peer *peer)
871 infostream<<"Client::peerAdded(): peer->id="
872 <<peer->id<<std::endl;
874 void Client::deletingPeer(con::Peer *peer, bool timeout)
876 infostream<<"Client::deletingPeer(): "
877 "Server Peer is getting deleted "
878 <<"(timeout="<<timeout<<")"<<std::endl;
881 void Client::ReceiveAll()
883 DSTACK(__FUNCTION_NAME);
884 u32 start_ms = porting::getTimeMs();
887 // Limit time even if there would be huge amounts of data to
889 if(porting::getTimeMs() > start_ms + 100)
894 g_profiler->graphAdd("client_received_packets", 1);
896 catch(con::NoIncomingDataException &e)
900 catch(con::InvalidIncomingDataException &e)
902 infostream<<"Client::ReceiveAll(): "
903 "InvalidIncomingDataException: what()="
904 <<e.what()<<std::endl;
909 void Client::Receive()
911 DSTACK(__FUNCTION_NAME);
912 SharedBuffer<u8> data;
916 //TimeTaker t1("con mutex and receive", m_device);
917 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
918 datasize = m_con.Receive(sender_peer_id, data);
920 //TimeTaker t1("ProcessData", m_device);
921 ProcessData(*data, datasize, sender_peer_id);
925 sender_peer_id given to this shall be quaranteed to be a valid peer
927 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
929 DSTACK(__FUNCTION_NAME);
931 // Ignore packets that don't even fit a command
934 m_packetcounter.add(60000);
938 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
940 //infostream<<"Client: received command="<<command<<std::endl;
941 m_packetcounter.add((u16)command);
944 If this check is removed, be sure to change the queue
945 system to know the ids
947 if(sender_peer_id != PEER_ID_SERVER)
949 infostream<<"Client::ProcessData(): Discarding data not "
950 "coming from server: peer_id="<<sender_peer_id
955 u8 ser_version = m_server_ser_ver;
957 //infostream<<"Client received command="<<(int)command<<std::endl;
959 if(command == TOCLIENT_INIT)
964 u8 deployed = data[2];
966 infostream<<"Client: TOCLIENT_INIT received with "
967 "deployed="<<((int)deployed&0xff)<<std::endl;
969 if(deployed < SER_FMT_VER_LOWEST
970 || deployed > SER_FMT_VER_HIGHEST)
972 infostream<<"Client: TOCLIENT_INIT: Server sent "
973 <<"unsupported ser_fmt_ver"<<std::endl;
977 m_server_ser_ver = deployed;
979 // Get player position
980 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
981 if(datasize >= 2+1+6)
982 playerpos_s16 = readV3S16(&data[2+1]);
983 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
986 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
988 // Set player position
989 Player *player = m_env.getLocalPlayer();
990 assert(player != NULL);
991 player->setPosition(playerpos_f);
994 if(datasize >= 2+1+6+8)
997 m_map_seed = readU64(&data[2+1+6]);
998 infostream<<"Client: received map seed: "<<m_map_seed<<std::endl;
1003 SharedBuffer<u8> reply(replysize);
1004 writeU16(&reply[0], TOSERVER_INIT2);
1006 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1011 if(command == TOCLIENT_ACCESS_DENIED)
1013 // The server didn't like our password. Note, this needs
1014 // to be processed even if the serialisation format has
1015 // not been agreed yet, the same as TOCLIENT_INIT.
1016 m_access_denied = true;
1017 m_access_denied_reason = L"Unknown";
1020 std::string datastring((char*)&data[2], datasize-2);
1021 std::istringstream is(datastring, std::ios_base::binary);
1022 m_access_denied_reason = deSerializeWideString(is);
1027 if(ser_version == SER_FMT_VER_INVALID)
1029 infostream<<"Client: Server serialization"
1030 " format invalid or not initialized."
1031 " Skipping incoming command="<<command<<std::endl;
1035 // Just here to avoid putting the two if's together when
1036 // making some copypasta
1039 if(command == TOCLIENT_REMOVENODE)
1044 p.X = readS16(&data[2]);
1045 p.Y = readS16(&data[4]);
1046 p.Z = readS16(&data[6]);
1048 //TimeTaker t1("TOCLIENT_REMOVENODE");
1052 else if(command == TOCLIENT_ADDNODE)
1054 if(datasize < 8 + MapNode::serializedLength(ser_version))
1058 p.X = readS16(&data[2]);
1059 p.Y = readS16(&data[4]);
1060 p.Z = readS16(&data[6]);
1062 //TimeTaker t1("TOCLIENT_ADDNODE");
1065 n.deSerialize(&data[8], ser_version);
1069 else if(command == TOCLIENT_BLOCKDATA)
1071 // Ignore too small packet
1076 p.X = readS16(&data[2]);
1077 p.Y = readS16(&data[4]);
1078 p.Z = readS16(&data[6]);
1080 /*infostream<<"Client: Thread: BLOCKDATA for ("
1081 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1082 /*infostream<<"Client: Thread: BLOCKDATA for ("
1083 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1085 std::string datastring((char*)&data[8], datasize-8);
1086 std::istringstream istr(datastring, std::ios_base::binary);
1091 v2s16 p2d(p.X, p.Z);
1092 sector = m_env.getMap().emergeSector(p2d);
1094 assert(sector->getPos() == p2d);
1096 //TimeTaker timer("MapBlock deSerialize");
1099 block = sector->getBlockNoCreateNoEx(p.Y);
1103 Update an existing block
1105 //infostream<<"Updating"<<std::endl;
1106 block->deSerialize(istr, ser_version, false);
1113 //infostream<<"Creating new"<<std::endl;
1114 block = new MapBlock(&m_env.getMap(), p, this);
1115 block->deSerialize(istr, ser_version, false);
1116 sector->insertBlock(block);
1130 u32 replysize = 2+1+6;
1131 SharedBuffer<u8> reply(replysize);
1132 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
1134 writeV3S16(&reply[3], p);
1136 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1140 Add it to mesh update queue and set it to be acknowledged after update.
1142 //infostream<<"Adding mesh update task for received block"<<std::endl;
1143 addUpdateMeshTaskWithEdge(p, true);
1145 else if(command == TOCLIENT_INVENTORY)
1150 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
1153 //TimeTaker t2("mutex locking", m_device);
1154 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1157 //TimeTaker t3("istringstream init", m_device);
1158 std::string datastring((char*)&data[2], datasize-2);
1159 std::istringstream is(datastring, std::ios_base::binary);
1162 //m_env.printPlayers(infostream);
1164 //TimeTaker t4("player get", m_device);
1165 Player *player = m_env.getLocalPlayer();
1166 assert(player != NULL);
1169 //TimeTaker t1("inventory.deSerialize()", m_device);
1170 player->inventory.deSerialize(is);
1173 m_inventory_updated = true;
1175 delete m_inventory_from_server;
1176 m_inventory_from_server = new Inventory(player->inventory);
1177 m_inventory_from_server_age = 0.0;
1179 //infostream<<"Client got player inventory:"<<std::endl;
1180 //player->inventory.print(infostream);
1183 else if(command == TOCLIENT_TIME_OF_DAY)
1188 u16 time_of_day = readU16(&data[2]);
1189 time_of_day = time_of_day % 24000;
1190 //infostream<<"Client: time_of_day="<<time_of_day<<std::endl;
1191 float time_speed = 0;
1192 if(datasize >= 2 + 2 + 4){
1193 time_speed = readF1000(&data[4]);
1195 // Old message; try to approximate speed of time by ourselves
1196 float time_of_day_f = (float)time_of_day / 24000.0;
1197 float tod_diff_f = 0;
1198 if(time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
1199 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0;
1201 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
1202 m_last_time_of_day_f = time_of_day_f;
1203 float time_diff = m_time_of_day_update_timer;
1204 m_time_of_day_update_timer = 0;
1205 if(m_time_of_day_set){
1206 time_speed = 3600.0*24.0 * tod_diff_f / time_diff;
1207 infostream<<"Client: Measured time_of_day speed (old format): "
1208 <<time_speed<<" tod_diff_f="<<tod_diff_f
1209 <<" time_diff="<<time_diff<<std::endl;
1213 // Update environment
1214 m_env.setTimeOfDay(time_of_day);
1215 m_env.setTimeOfDaySpeed(time_speed);
1216 m_time_of_day_set = true;
1218 u32 dr = m_env.getDayNightRatio();
1219 verbosestream<<"Client: time_of_day="<<time_of_day
1220 <<" time_speed="<<time_speed
1221 <<" dr="<<dr<<std::endl;
1223 else if(command == TOCLIENT_CHAT_MESSAGE)
1231 std::string datastring((char*)&data[2], datasize-2);
1232 std::istringstream is(datastring, std::ios_base::binary);
1235 is.read((char*)buf, 2);
1236 u16 len = readU16(buf);
1238 std::wstring message;
1239 for(u16 i=0; i<len; i++)
1241 is.read((char*)buf, 2);
1242 message += (wchar_t)readU16(buf);
1245 /*infostream<<"Client received chat message: "
1246 <<wide_to_narrow(message)<<std::endl;*/
1248 m_chat_queue.push_back(message);
1250 else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1252 //if(g_settings->getBool("enable_experimental"))
1256 u16 count of removed objects
1257 for all removed objects {
1260 u16 count of added objects
1261 for all added objects {
1264 u32 initialization data length
1265 string initialization data
1270 // Get all data except the command number
1271 std::string datastring((char*)&data[2], datasize-2);
1272 // Throw them in an istringstream
1273 std::istringstream is(datastring, std::ios_base::binary);
1277 // Read removed objects
1279 u16 removed_count = readU16((u8*)buf);
1280 for(u16 i=0; i<removed_count; i++)
1283 u16 id = readU16((u8*)buf);
1286 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1287 m_env.removeActiveObject(id);
1291 // Read added objects
1293 u16 added_count = readU16((u8*)buf);
1294 for(u16 i=0; i<added_count; i++)
1297 u16 id = readU16((u8*)buf);
1299 u8 type = readU8((u8*)buf);
1300 std::string data = deSerializeLongString(is);
1303 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1304 m_env.addActiveObject(id, type, data);
1309 else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1311 //if(g_settings->getBool("enable_experimental"))
1323 // Get all data except the command number
1324 std::string datastring((char*)&data[2], datasize-2);
1325 // Throw them in an istringstream
1326 std::istringstream is(datastring, std::ios_base::binary);
1328 while(is.eof() == false)
1332 u16 id = readU16((u8*)buf);
1336 u16 message_size = readU16((u8*)buf);
1337 std::string message;
1338 message.reserve(message_size);
1339 for(u16 i=0; i<message_size; i++)
1342 message.append(buf, 1);
1344 // Pass on to the environment
1346 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1347 m_env.processActiveObjectMessage(id, message);
1352 else if(command == TOCLIENT_HP)
1354 std::string datastring((char*)&data[2], datasize-2);
1355 std::istringstream is(datastring, std::ios_base::binary);
1356 Player *player = m_env.getLocalPlayer();
1357 assert(player != NULL);
1358 u8 oldhp = player->hp;
1364 // Add to ClientEvent queue
1366 event.type = CE_PLAYER_DAMAGE;
1367 event.player_damage.amount = oldhp - hp;
1368 m_client_event_queue.push_back(event);
1371 else if(command == TOCLIENT_MOVE_PLAYER)
1373 std::string datastring((char*)&data[2], datasize-2);
1374 std::istringstream is(datastring, std::ios_base::binary);
1375 Player *player = m_env.getLocalPlayer();
1376 assert(player != NULL);
1377 v3f pos = readV3F1000(is);
1378 f32 pitch = readF1000(is);
1379 f32 yaw = readF1000(is);
1380 player->setPosition(pos);
1381 /*player->setPitch(pitch);
1382 player->setYaw(yaw);*/
1384 infostream<<"Client got TOCLIENT_MOVE_PLAYER"
1385 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1391 Add to ClientEvent queue.
1392 This has to be sent to the main program because otherwise
1393 it would just force the pitch and yaw values to whatever
1394 the camera points to.
1397 event.type = CE_PLAYER_FORCE_MOVE;
1398 event.player_force_move.pitch = pitch;
1399 event.player_force_move.yaw = yaw;
1400 m_client_event_queue.push_back(event);
1402 // Ignore damage for a few seconds, so that the player doesn't
1403 // get damage from falling on ground
1404 m_ignore_damage_timer = 3.0;
1406 else if(command == TOCLIENT_PLAYERITEM)
1408 infostream<<"Client: WARNING: Ignoring TOCLIENT_PLAYERITEM"<<std::endl;
1410 else if(command == TOCLIENT_DEATHSCREEN)
1412 std::string datastring((char*)&data[2], datasize-2);
1413 std::istringstream is(datastring, std::ios_base::binary);
1415 bool set_camera_point_target = readU8(is);
1416 v3f camera_point_target = readV3F1000(is);
1419 event.type = CE_DEATHSCREEN;
1420 event.deathscreen.set_camera_point_target = set_camera_point_target;
1421 event.deathscreen.camera_point_target_x = camera_point_target.X;
1422 event.deathscreen.camera_point_target_y = camera_point_target.Y;
1423 event.deathscreen.camera_point_target_z = camera_point_target.Z;
1424 m_client_event_queue.push_back(event);
1426 else if(command == TOCLIENT_ANNOUNCE_MEDIA)
1428 std::string datastring((char*)&data[2], datasize-2);
1429 std::istringstream is(datastring, std::ios_base::binary);
1431 // Mesh update thread must be stopped while
1432 // updating content definitions
1433 assert(!m_mesh_update_thread.IsRunning());
1435 int num_files = readU16(is);
1437 verbosestream<<"Client received TOCLIENT_ANNOUNCE_MEDIA ("
1438 <<num_files<<" files)"<<std::endl;
1440 core::list<MediaRequest> file_requests;
1442 for(int i=0; i<num_files; i++)
1444 //read file from cache
1445 std::string name = deSerializeString(is);
1446 std::string sha1_base64 = deSerializeString(is);
1448 // if name contains illegal characters, ignore the file
1449 if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
1450 errorstream<<"Client: ignoring illegal file name "
1451 <<"sent by server: \""<<name<<"\""<<std::endl;
1455 std::string sha1_raw = base64_decode(sha1_base64);
1456 std::string sha1_hex = hex_encode(sha1_raw);
1457 std::ostringstream tmp_os(std::ios_base::binary);
1458 bool found_in_cache = m_media_cache.load_sha1(sha1_raw, tmp_os);
1459 m_media_name_sha1_map.set(name, sha1_raw);
1461 // If found in cache, try to load it from there
1464 bool success = loadMedia(tmp_os.str(), name);
1466 verbosestream<<"Client: Loaded cached media: "
1467 <<sha1_hex<<" \""<<name<<"\""<<std::endl;
1470 infostream<<"Client: Failed to load cached media: "
1471 <<sha1_hex<<" \""<<name<<"\""<<std::endl;
1474 // Didn't load from cache; queue it to be requested
1475 verbosestream<<"Client: Adding file to request list: \""
1476 <<sha1_hex<<" \""<<name<<"\""<<std::endl;
1477 file_requests.push_back(MediaRequest(name));
1481 event.type = CE_TEXTURES_UPDATED;
1482 m_client_event_queue.push_back(event);
1486 u16 number of files requested
1492 std::ostringstream os(std::ios_base::binary);
1493 writeU16(os, TOSERVER_REQUEST_MEDIA);
1494 writeU16(os, file_requests.size());
1496 for(core::list<MediaRequest>::Iterator i = file_requests.begin();
1497 i != file_requests.end(); i++) {
1498 os<<serializeString(i->name);
1502 std::string s = os.str();
1503 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1505 Send(0, data, true);
1506 infostream<<"Client: Sending media request list to server ("
1507 <<file_requests.size()<<" files)"<<std::endl;
1509 else if(command == TOCLIENT_MEDIA)
1511 std::string datastring((char*)&data[2], datasize-2);
1512 std::istringstream is(datastring, std::ios_base::binary);
1514 // Mesh update thread must be stopped while
1515 // updating content definitions
1516 assert(!m_mesh_update_thread.IsRunning());
1520 u16 total number of file bunches
1521 u16 index of this bunch
1522 u32 number of files in this bunch
1530 int num_bunches = readU16(is);
1531 int bunch_i = readU16(is);
1532 if(num_bunches >= 2)
1533 m_media_receive_progress = (float)bunch_i / (float)(num_bunches - 1);
1535 m_media_receive_progress = 1.0;
1536 if(bunch_i == num_bunches - 1)
1537 m_media_received = true;
1538 int num_files = readU32(is);
1539 infostream<<"Client: Received files: bunch "<<bunch_i<<"/"
1540 <<num_bunches<<" files="<<num_files
1541 <<" size="<<datasize<<std::endl;
1542 for(int i=0; i<num_files; i++){
1543 std::string name = deSerializeString(is);
1544 std::string data = deSerializeLongString(is);
1546 // if name contains illegal characters, ignore the file
1547 if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
1548 errorstream<<"Client: ignoring illegal file name "
1549 <<"sent by server: \""<<name<<"\""<<std::endl;
1553 bool success = loadMedia(data, name);
1555 verbosestream<<"Client: Loaded received media: "
1556 <<"\""<<name<<"\". Caching."<<std::endl;
1558 infostream<<"Client: Failed to load received media: "
1559 <<"\""<<name<<"\". Not caching."<<std::endl;
1563 bool did = fs::CreateAllDirs(getMediaCacheDir());
1565 errorstream<<"Could not create media cache directory"
1570 core::map<std::string, std::string>::Node *n;
1571 n = m_media_name_sha1_map.find(name);
1573 errorstream<<"The server sent a file that has not "
1574 <<"been announced."<<std::endl;
1576 m_media_cache.update_sha1(data);
1581 event.type = CE_TEXTURES_UPDATED;
1582 m_client_event_queue.push_back(event);
1584 else if(command == TOCLIENT_TOOLDEF)
1586 infostream<<"Client: WARNING: Ignoring TOCLIENT_TOOLDEF"<<std::endl;
1588 else if(command == TOCLIENT_NODEDEF)
1590 infostream<<"Client: Received node definitions: packet size: "
1591 <<datasize<<std::endl;
1593 // Mesh update thread must be stopped while
1594 // updating content definitions
1595 assert(!m_mesh_update_thread.IsRunning());
1597 // Decompress node definitions
1598 std::string datastring((char*)&data[2], datasize-2);
1599 std::istringstream is(datastring, std::ios_base::binary);
1600 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1601 std::ostringstream tmp_os;
1602 decompressZlib(tmp_is, tmp_os);
1604 // Deserialize node definitions
1605 std::istringstream tmp_is2(tmp_os.str());
1606 m_nodedef->deSerialize(tmp_is2);
1607 m_nodedef_received = true;
1609 else if(command == TOCLIENT_CRAFTITEMDEF)
1611 infostream<<"Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF"<<std::endl;
1613 else if(command == TOCLIENT_ITEMDEF)
1615 infostream<<"Client: Received item 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 item 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_itemdef->deSerialize(tmp_is2);
1632 m_itemdef_received = true;
1634 else if(command == TOCLIENT_PLAY_SOUND)
1636 std::string datastring((char*)&data[2], datasize-2);
1637 std::istringstream is(datastring, std::ios_base::binary);
1639 s32 server_id = readS32(is);
1640 std::string name = deSerializeString(is);
1641 float gain = readF1000(is);
1642 int type = readU8(is); // 0=local, 1=positional, 2=object
1643 v3f pos = readV3F1000(is);
1644 u16 object_id = readU16(is);
1645 bool loop = readU8(is);
1650 client_id = m_sound->playSound(name, loop, gain);
1652 case 1: // positional
1653 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1656 ClientActiveObject *cao = m_env.getActiveObject(object_id);
1658 pos = cao->getPosition();
1659 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1660 // TODO: Set up sound to move with object
1665 if(client_id != -1){
1666 m_sounds_server_to_client[server_id] = client_id;
1667 m_sounds_client_to_server[client_id] = server_id;
1669 m_sounds_to_objects[client_id] = object_id;
1672 else if(command == TOCLIENT_STOP_SOUND)
1674 std::string datastring((char*)&data[2], datasize-2);
1675 std::istringstream is(datastring, std::ios_base::binary);
1677 s32 server_id = readS32(is);
1678 std::map<s32, int>::iterator i =
1679 m_sounds_server_to_client.find(server_id);
1680 if(i != m_sounds_server_to_client.end()){
1681 int client_id = i->second;
1682 m_sound->stopSound(client_id);
1685 else if(command == TOCLIENT_PRIVILEGES)
1687 std::string datastring((char*)&data[2], datasize-2);
1688 std::istringstream is(datastring, std::ios_base::binary);
1690 m_privileges.clear();
1691 infostream<<"Client: Privileges updated: ";
1692 u16 num_privileges = readU16(is);
1693 for(u16 i=0; i<num_privileges; i++){
1694 std::string priv = deSerializeString(is);
1695 m_privileges.insert(priv);
1696 infostream<<priv<<" ";
1698 infostream<<std::endl;
1700 else if(command == TOCLIENT_INVENTORY_FORMSPEC)
1702 std::string datastring((char*)&data[2], datasize-2);
1703 std::istringstream is(datastring, std::ios_base::binary);
1705 // Store formspec in LocalPlayer
1706 Player *player = m_env.getLocalPlayer();
1707 assert(player != NULL);
1708 player->inventory_formspec = deSerializeLongString(is);
1710 else if(command == TOCLIENT_DETACHED_INVENTORY)
1712 std::string datastring((char*)&data[2], datasize-2);
1713 std::istringstream is(datastring, std::ios_base::binary);
1715 std::string name = deSerializeString(is);
1717 infostream<<"Client: Detached inventory update: \""<<name<<"\""<<std::endl;
1719 Inventory *inv = NULL;
1720 if(m_detached_inventories.count(name) > 0)
1721 inv = m_detached_inventories[name];
1723 inv = new Inventory(m_itemdef);
1724 m_detached_inventories[name] = inv;
1726 inv->deSerialize(is);
1730 infostream<<"Client: Ignoring unknown command "
1731 <<command<<std::endl;
1735 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
1737 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1738 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
1741 void Client::interact(u8 action, const PointedThing& pointed)
1743 if(connectedAndInitialized() == false){
1744 infostream<<"Client::interact() "
1745 "cancelled (not connected)"
1750 std::ostringstream os(std::ios_base::binary);
1756 [5] u32 length of the next item
1757 [9] serialized PointedThing
1759 0: start digging (from undersurface) or use
1760 1: stop digging (all parameters ignored)
1761 2: digging completed
1762 3: place block or item (to abovesurface)
1765 writeU16(os, TOSERVER_INTERACT);
1766 writeU8(os, action);
1767 writeU16(os, getPlayerItem());
1768 std::ostringstream tmp_os(std::ios::binary);
1769 pointed.serialize(tmp_os);
1770 os<<serializeLongString(tmp_os.str());
1772 std::string s = os.str();
1773 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1776 Send(0, data, true);
1779 void Client::sendNodemetaFields(v3s16 p, const std::string &formname,
1780 const std::map<std::string, std::string> &fields)
1782 std::ostringstream os(std::ios_base::binary);
1784 writeU16(os, TOSERVER_NODEMETA_FIELDS);
1786 os<<serializeString(formname);
1787 writeU16(os, fields.size());
1788 for(std::map<std::string, std::string>::const_iterator
1789 i = fields.begin(); i != fields.end(); i++){
1790 const std::string &name = i->first;
1791 const std::string &value = i->second;
1792 os<<serializeString(name);
1793 os<<serializeLongString(value);
1797 std::string s = os.str();
1798 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1800 Send(0, data, true);
1803 void Client::sendInventoryFields(const std::string &formname,
1804 const std::map<std::string, std::string> &fields)
1806 std::ostringstream os(std::ios_base::binary);
1808 writeU16(os, TOSERVER_INVENTORY_FIELDS);
1809 os<<serializeString(formname);
1810 writeU16(os, fields.size());
1811 for(std::map<std::string, std::string>::const_iterator
1812 i = fields.begin(); i != fields.end(); i++){
1813 const std::string &name = i->first;
1814 const std::string &value = i->second;
1815 os<<serializeString(name);
1816 os<<serializeLongString(value);
1820 std::string s = os.str();
1821 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1823 Send(0, data, true);
1826 void Client::sendInventoryAction(InventoryAction *a)
1828 std::ostringstream os(std::ios_base::binary);
1832 writeU16(buf, TOSERVER_INVENTORY_ACTION);
1833 os.write((char*)buf, 2);
1838 std::string s = os.str();
1839 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1841 Send(0, data, true);
1844 void Client::sendChatMessage(const std::wstring &message)
1846 std::ostringstream os(std::ios_base::binary);
1850 writeU16(buf, TOSERVER_CHAT_MESSAGE);
1851 os.write((char*)buf, 2);
1854 writeU16(buf, message.size());
1855 os.write((char*)buf, 2);
1858 for(u32 i=0; i<message.size(); i++)
1862 os.write((char*)buf, 2);
1866 std::string s = os.str();
1867 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1869 Send(0, data, true);
1872 void Client::sendChangePassword(const std::wstring oldpassword,
1873 const std::wstring newpassword)
1875 Player *player = m_env.getLocalPlayer();
1879 std::string playername = player->getName();
1880 std::string oldpwd = translatePassword(playername, oldpassword);
1881 std::string newpwd = translatePassword(playername, newpassword);
1883 std::ostringstream os(std::ios_base::binary);
1884 u8 buf[2+PASSWORD_SIZE*2];
1886 [0] u16 TOSERVER_PASSWORD
1887 [2] u8[28] old password
1888 [30] u8[28] new password
1891 writeU16(buf, TOSERVER_PASSWORD);
1892 for(u32 i=0;i<PASSWORD_SIZE-1;i++)
1894 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
1895 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
1897 buf[2+PASSWORD_SIZE-1] = 0;
1898 buf[30+PASSWORD_SIZE-1] = 0;
1899 os.write((char*)buf, 2+PASSWORD_SIZE*2);
1902 std::string s = os.str();
1903 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1905 Send(0, data, true);
1909 void Client::sendDamage(u8 damage)
1911 DSTACK(__FUNCTION_NAME);
1912 std::ostringstream os(std::ios_base::binary);
1914 writeU16(os, TOSERVER_DAMAGE);
1915 writeU8(os, damage);
1918 std::string s = os.str();
1919 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1921 Send(0, data, true);
1924 void Client::sendRespawn()
1926 DSTACK(__FUNCTION_NAME);
1927 std::ostringstream os(std::ios_base::binary);
1929 writeU16(os, TOSERVER_RESPAWN);
1932 std::string s = os.str();
1933 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1935 Send(0, data, true);
1938 void Client::sendPlayerPos()
1940 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1942 Player *myplayer = m_env.getLocalPlayer();
1943 if(myplayer == NULL)
1948 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1949 our_peer_id = m_con.GetPeerID();
1952 // Set peer id if not set already
1953 if(myplayer->peer_id == PEER_ID_INEXISTENT)
1954 myplayer->peer_id = our_peer_id;
1955 // Check that an existing peer_id is the same as the connection's
1956 assert(myplayer->peer_id == our_peer_id);
1958 v3f pf = myplayer->getPosition();
1959 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
1960 v3f sf = myplayer->getSpeed();
1961 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
1962 s32 pitch = myplayer->getPitch() * 100;
1963 s32 yaw = myplayer->getYaw() * 100;
1968 [2] v3s32 position*100
1969 [2+12] v3s32 speed*100
1970 [2+12+12] s32 pitch*100
1971 [2+12+12+4] s32 yaw*100
1974 SharedBuffer<u8> data(2+12+12+4+4);
1975 writeU16(&data[0], TOSERVER_PLAYERPOS);
1976 writeV3S32(&data[2], position);
1977 writeV3S32(&data[2+12], speed);
1978 writeS32(&data[2+12+12], pitch);
1979 writeS32(&data[2+12+12+4], yaw);
1981 // Send as unreliable
1982 Send(0, data, false);
1985 void Client::sendPlayerItem(u16 item)
1987 Player *myplayer = m_env.getLocalPlayer();
1988 if(myplayer == NULL)
1991 u16 our_peer_id = m_con.GetPeerID();
1993 // Set peer id if not set already
1994 if(myplayer->peer_id == PEER_ID_INEXISTENT)
1995 myplayer->peer_id = our_peer_id;
1996 // Check that an existing peer_id is the same as the connection's
1997 assert(myplayer->peer_id == our_peer_id);
1999 SharedBuffer<u8> data(2+2);
2000 writeU16(&data[0], TOSERVER_PLAYERITEM);
2001 writeU16(&data[2], item);
2004 Send(0, data, true);
2007 void Client::removeNode(v3s16 p)
2009 core::map<v3s16, MapBlock*> modified_blocks;
2013 //TimeTaker t("removeNodeAndUpdate", m_device);
2014 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
2016 catch(InvalidPositionException &e)
2020 // add urgent task to update the modified node
2021 addUpdateMeshTaskForNode(p, false, true);
2023 for(core::map<v3s16, MapBlock * >::Iterator
2024 i = modified_blocks.getIterator();
2025 i.atEnd() == false; i++)
2027 v3s16 p = i.getNode()->getKey();
2028 addUpdateMeshTaskWithEdge(p);
2032 void Client::addNode(v3s16 p, MapNode n)
2034 TimeTaker timer1("Client::addNode()");
2036 core::map<v3s16, MapBlock*> modified_blocks;
2040 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
2041 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks);
2043 catch(InvalidPositionException &e)
2046 for(core::map<v3s16, MapBlock * >::Iterator
2047 i = modified_blocks.getIterator();
2048 i.atEnd() == false; i++)
2050 v3s16 p = i.getNode()->getKey();
2051 addUpdateMeshTaskWithEdge(p);
2055 void Client::setPlayerControl(PlayerControl &control)
2057 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2058 LocalPlayer *player = m_env.getLocalPlayer();
2059 assert(player != NULL);
2060 player->control = control;
2063 void Client::selectPlayerItem(u16 item)
2065 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2066 m_playeritem = item;
2067 m_inventory_updated = true;
2068 sendPlayerItem(item);
2071 // Returns true if the inventory of the local player has been
2072 // updated from the server. If it is true, it is set to false.
2073 bool Client::getLocalInventoryUpdated()
2075 // m_inventory_updated is behind envlock
2076 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2077 bool updated = m_inventory_updated;
2078 m_inventory_updated = false;
2082 // Copies the inventory of the local player to parameter
2083 void Client::getLocalInventory(Inventory &dst)
2085 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2086 Player *player = m_env.getLocalPlayer();
2087 assert(player != NULL);
2088 dst = player->inventory;
2091 Inventory* Client::getInventory(const InventoryLocation &loc)
2094 case InventoryLocation::UNDEFINED:
2097 case InventoryLocation::CURRENT_PLAYER:
2099 Player *player = m_env.getLocalPlayer();
2100 assert(player != NULL);
2101 return &player->inventory;
2104 case InventoryLocation::PLAYER:
2106 Player *player = m_env.getPlayer(loc.name.c_str());
2109 return &player->inventory;
2112 case InventoryLocation::NODEMETA:
2114 NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p);
2117 return meta->getInventory();
2120 case InventoryLocation::DETACHED:
2122 if(m_detached_inventories.count(loc.name) == 0)
2124 return m_detached_inventories[loc.name];
2132 void Client::inventoryAction(InventoryAction *a)
2135 Send it to the server
2137 sendInventoryAction(a);
2140 Predict some local inventory changes
2142 a->clientApply(this, this);
2145 ClientActiveObject * Client::getSelectedActiveObject(
2147 v3f from_pos_f_on_map,
2148 core::line3d<f32> shootline_on_map
2151 core::array<DistanceSortedActiveObject> objects;
2153 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
2155 //infostream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
2158 // After this, the closest object is the first in the array.
2161 for(u32 i=0; i<objects.size(); i++)
2163 ClientActiveObject *obj = objects[i].obj;
2165 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
2166 if(selection_box == NULL)
2169 v3f pos = obj->getPosition();
2171 core::aabbox3d<f32> offsetted_box(
2172 selection_box->MinEdge + pos,
2173 selection_box->MaxEdge + pos
2176 if(offsetted_box.intersectsWithLine(shootline_on_map))
2178 //infostream<<"Returning selected object"<<std::endl;
2183 //infostream<<"No object selected; returning NULL."<<std::endl;
2187 void Client::printDebugInfo(std::ostream &os)
2189 //JMutexAutoLock lock1(m_fetchblock_mutex);
2190 /*JMutexAutoLock lock2(m_incoming_queue_mutex);
2192 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
2193 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
2194 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
2198 core::list<std::wstring> Client::getConnectedPlayerNames()
2200 core::list<Player*> players = m_env.getPlayers(true);
2201 core::list<std::wstring> playerNames;
2202 for(core::list<Player*>::Iterator
2203 i = players.begin();
2204 i != players.end(); i++)
2206 Player *player = *i;
2207 playerNames.push_back(narrow_to_wide(player->getName()));
2212 float Client::getAnimationTime()
2214 return m_animation_time;
2217 int Client::getCrackLevel()
2219 return m_crack_level;
2222 void Client::setCrack(int level, v3s16 pos)
2224 int old_crack_level = m_crack_level;
2225 v3s16 old_crack_pos = m_crack_pos;
2227 m_crack_level = level;
2230 if(old_crack_level >= 0 && (level < 0 || pos != old_crack_pos))
2233 addUpdateMeshTaskForNode(old_crack_pos, false, true);
2235 if(level >= 0 && (old_crack_level < 0 || pos != old_crack_pos))
2238 addUpdateMeshTaskForNode(pos, false, true);
2244 Player *player = m_env.getLocalPlayer();
2245 assert(player != NULL);
2249 bool Client::getChatMessage(std::wstring &message)
2251 if(m_chat_queue.size() == 0)
2253 message = m_chat_queue.pop_front();
2257 void Client::typeChatMessage(const std::wstring &message)
2259 // Discard empty line
2264 sendChatMessage(message);
2267 if (message[0] == L'/')
2269 m_chat_queue.push_back(
2270 (std::wstring)L"issued command: "+message);
2274 LocalPlayer *player = m_env.getLocalPlayer();
2275 assert(player != NULL);
2276 std::wstring name = narrow_to_wide(player->getName());
2277 m_chat_queue.push_back(
2278 (std::wstring)L"<"+name+L"> "+message);
2282 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
2284 /*infostream<<"Client::addUpdateMeshTask(): "
2285 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2286 <<" ack_to_server="<<ack_to_server
2287 <<" urgent="<<urgent
2290 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2295 Create a task to update the mesh of the block
2298 MeshMakeData *data = new MeshMakeData(this);
2301 //TimeTaker timer("data fill");
2303 // Debug: 1-6ms, avg=2ms
2305 data->setCrack(m_crack_level, m_crack_pos);
2306 data->setSmoothLighting(g_settings->getBool("smooth_lighting"));
2310 //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
2312 // Add task to queue
2313 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server, urgent);
2315 /*infostream<<"Mesh update input queue size is "
2316 <<m_mesh_update_thread.m_queue_in.size()
2320 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent)
2324 infostream<<"Client::addUpdateMeshTaskWithEdge(): "
2325 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2330 v3s16 p = blockpos + v3s16(0,0,0);
2331 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2332 addUpdateMeshTask(p, ack_to_server, urgent);
2334 catch(InvalidPositionException &e){}
2337 v3s16 p = blockpos + v3s16(-1,0,0);
2338 addUpdateMeshTask(p, false, urgent);
2340 catch(InvalidPositionException &e){}
2342 v3s16 p = blockpos + v3s16(0,-1,0);
2343 addUpdateMeshTask(p, false, urgent);
2345 catch(InvalidPositionException &e){}
2347 v3s16 p = blockpos + v3s16(0,0,-1);
2348 addUpdateMeshTask(p, false, urgent);
2350 catch(InvalidPositionException &e){}
2353 void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool urgent)
2357 infostream<<"Client::addUpdateMeshTaskForNode(): "
2358 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2362 v3s16 blockpos = getNodeBlockPos(nodepos);
2363 v3s16 blockpos_relative = blockpos * MAP_BLOCKSIZE;
2366 v3s16 p = blockpos + v3s16(0,0,0);
2367 addUpdateMeshTask(p, ack_to_server, urgent);
2369 catch(InvalidPositionException &e){}
2371 if(nodepos.X == blockpos_relative.X){
2373 v3s16 p = blockpos + v3s16(-1,0,0);
2374 addUpdateMeshTask(p, false, urgent);
2376 catch(InvalidPositionException &e){}
2378 if(nodepos.Y == blockpos_relative.Y){
2380 v3s16 p = blockpos + v3s16(0,-1,0);
2381 addUpdateMeshTask(p, false, urgent);
2383 catch(InvalidPositionException &e){}
2385 if(nodepos.Z == blockpos_relative.Z){
2387 v3s16 p = blockpos + v3s16(0,0,-1);
2388 addUpdateMeshTask(p, false, urgent);
2390 catch(InvalidPositionException &e){}
2394 ClientEvent Client::getClientEvent()
2396 if(m_client_event_queue.size() == 0)
2399 event.type = CE_NONE;
2402 return m_client_event_queue.pop_front();
2405 void Client::afterContentReceived()
2407 verbosestream<<"Client::afterContentReceived() started"<<std::endl;
2408 assert(m_itemdef_received);
2409 assert(m_nodedef_received);
2410 assert(m_media_received);
2412 // remove the information about which checksum each texture
2414 m_media_name_sha1_map.clear();
2416 // Rebuild inherited images and recreate textures
2417 verbosestream<<"Rebuilding images and textures"<<std::endl;
2418 m_tsrc->rebuildImagesAndTextures();
2420 // Update texture atlas
2421 verbosestream<<"Updating texture atlas"<<std::endl;
2422 if(g_settings->getBool("enable_texture_atlas"))
2423 m_tsrc->buildMainAtlas(this);
2425 // Update node aliases
2426 verbosestream<<"Updating node aliases"<<std::endl;
2427 m_nodedef->updateAliases(m_itemdef);
2429 // Update node textures
2430 verbosestream<<"Updating node textures"<<std::endl;
2431 m_nodedef->updateTextures(m_tsrc);
2433 // Update item textures and meshes
2434 verbosestream<<"Updating item textures and meshes"<<std::endl;
2435 m_itemdef->updateTexturesAndMeshes(this);
2437 // Start mesh update thread after setting up content definitions
2438 verbosestream<<"Starting mesh update thread"<<std::endl;
2439 m_mesh_update_thread.Start();
2441 verbosestream<<"Client::afterContentReceived() done"<<std::endl;
2444 float Client::getRTT(void)
2447 return m_con.GetPeerAvgRTT(PEER_ID_SERVER);
2448 } catch(con::PeerNotFoundException &e){
2453 // IGameDef interface
2455 IItemDefManager* Client::getItemDefManager()
2459 INodeDefManager* Client::getNodeDefManager()
2463 ICraftDefManager* Client::getCraftDefManager()
2466 //return m_craftdef;
2468 ITextureSource* Client::getTextureSource()
2472 u16 Client::allocateUnknownNodeId(const std::string &name)
2474 errorstream<<"Client::allocateUnknownNodeId(): "
2475 <<"Client cannot allocate node IDs"<<std::endl;
2477 return CONTENT_IGNORE;
2479 ISoundManager* Client::getSoundManager()
2483 MtEventManager* Client::getEventManager()