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 const char *model_ext[] = {
864 ".b3d", ".md2", ".obj",
867 name = removeStringEnd(filename, model_ext);
870 verbosestream<<"Client: Storing model into Irrlicht: "
871 <<"file \""<<filename<<"\""<<std::endl;
873 io::IFileSystem *irrfs = m_device->getFileSystem();
875 // Create an irrlicht memory file
876 io::IReadFile *rfile = irrfs->createMemoryReadFile(*data_rw, data_rw.getSize(), filename.c_str(), true);
882 errorstream<<"Client: Don't know how to load file \""
883 <<filename<<"\""<<std::endl;
887 // Virtual methods from con::PeerHandler
888 void Client::peerAdded(con::Peer *peer)
890 infostream<<"Client::peerAdded(): peer->id="
891 <<peer->id<<std::endl;
893 void Client::deletingPeer(con::Peer *peer, bool timeout)
895 infostream<<"Client::deletingPeer(): "
896 "Server Peer is getting deleted "
897 <<"(timeout="<<timeout<<")"<<std::endl;
900 void Client::ReceiveAll()
902 DSTACK(__FUNCTION_NAME);
903 u32 start_ms = porting::getTimeMs();
906 // Limit time even if there would be huge amounts of data to
908 if(porting::getTimeMs() > start_ms + 100)
913 g_profiler->graphAdd("client_received_packets", 1);
915 catch(con::NoIncomingDataException &e)
919 catch(con::InvalidIncomingDataException &e)
921 infostream<<"Client::ReceiveAll(): "
922 "InvalidIncomingDataException: what()="
923 <<e.what()<<std::endl;
928 void Client::Receive()
930 DSTACK(__FUNCTION_NAME);
931 SharedBuffer<u8> data;
935 //TimeTaker t1("con mutex and receive", m_device);
936 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
937 datasize = m_con.Receive(sender_peer_id, data);
939 //TimeTaker t1("ProcessData", m_device);
940 ProcessData(*data, datasize, sender_peer_id);
944 sender_peer_id given to this shall be quaranteed to be a valid peer
946 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
948 DSTACK(__FUNCTION_NAME);
950 // Ignore packets that don't even fit a command
953 m_packetcounter.add(60000);
957 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
959 //infostream<<"Client: received command="<<command<<std::endl;
960 m_packetcounter.add((u16)command);
963 If this check is removed, be sure to change the queue
964 system to know the ids
966 if(sender_peer_id != PEER_ID_SERVER)
968 infostream<<"Client::ProcessData(): Discarding data not "
969 "coming from server: peer_id="<<sender_peer_id
974 u8 ser_version = m_server_ser_ver;
976 //infostream<<"Client received command="<<(int)command<<std::endl;
978 if(command == TOCLIENT_INIT)
983 u8 deployed = data[2];
985 infostream<<"Client: TOCLIENT_INIT received with "
986 "deployed="<<((int)deployed&0xff)<<std::endl;
988 if(deployed < SER_FMT_VER_LOWEST
989 || deployed > SER_FMT_VER_HIGHEST)
991 infostream<<"Client: TOCLIENT_INIT: Server sent "
992 <<"unsupported ser_fmt_ver"<<std::endl;
996 m_server_ser_ver = deployed;
998 // Get player position
999 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
1000 if(datasize >= 2+1+6)
1001 playerpos_s16 = readV3S16(&data[2+1]);
1002 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
1005 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1007 // Set player position
1008 Player *player = m_env.getLocalPlayer();
1009 assert(player != NULL);
1010 player->setPosition(playerpos_f);
1013 if(datasize >= 2+1+6+8)
1016 m_map_seed = readU64(&data[2+1+6]);
1017 infostream<<"Client: received map seed: "<<m_map_seed<<std::endl;
1022 SharedBuffer<u8> reply(replysize);
1023 writeU16(&reply[0], TOSERVER_INIT2);
1025 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1030 if(command == TOCLIENT_ACCESS_DENIED)
1032 // The server didn't like our password. Note, this needs
1033 // to be processed even if the serialisation format has
1034 // not been agreed yet, the same as TOCLIENT_INIT.
1035 m_access_denied = true;
1036 m_access_denied_reason = L"Unknown";
1039 std::string datastring((char*)&data[2], datasize-2);
1040 std::istringstream is(datastring, std::ios_base::binary);
1041 m_access_denied_reason = deSerializeWideString(is);
1046 if(ser_version == SER_FMT_VER_INVALID)
1048 infostream<<"Client: Server serialization"
1049 " format invalid or not initialized."
1050 " Skipping incoming command="<<command<<std::endl;
1054 // Just here to avoid putting the two if's together when
1055 // making some copypasta
1058 if(command == TOCLIENT_REMOVENODE)
1063 p.X = readS16(&data[2]);
1064 p.Y = readS16(&data[4]);
1065 p.Z = readS16(&data[6]);
1067 //TimeTaker t1("TOCLIENT_REMOVENODE");
1071 else if(command == TOCLIENT_ADDNODE)
1073 if(datasize < 8 + MapNode::serializedLength(ser_version))
1077 p.X = readS16(&data[2]);
1078 p.Y = readS16(&data[4]);
1079 p.Z = readS16(&data[6]);
1081 //TimeTaker t1("TOCLIENT_ADDNODE");
1084 n.deSerialize(&data[8], ser_version);
1088 else if(command == TOCLIENT_BLOCKDATA)
1090 // Ignore too small packet
1095 p.X = readS16(&data[2]);
1096 p.Y = readS16(&data[4]);
1097 p.Z = readS16(&data[6]);
1099 /*infostream<<"Client: Thread: BLOCKDATA for ("
1100 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1101 /*infostream<<"Client: Thread: BLOCKDATA for ("
1102 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1104 std::string datastring((char*)&data[8], datasize-8);
1105 std::istringstream istr(datastring, std::ios_base::binary);
1110 v2s16 p2d(p.X, p.Z);
1111 sector = m_env.getMap().emergeSector(p2d);
1113 assert(sector->getPos() == p2d);
1115 //TimeTaker timer("MapBlock deSerialize");
1118 block = sector->getBlockNoCreateNoEx(p.Y);
1122 Update an existing block
1124 //infostream<<"Updating"<<std::endl;
1125 block->deSerialize(istr, ser_version, false);
1132 //infostream<<"Creating new"<<std::endl;
1133 block = new MapBlock(&m_env.getMap(), p, this);
1134 block->deSerialize(istr, ser_version, false);
1135 sector->insertBlock(block);
1149 u32 replysize = 2+1+6;
1150 SharedBuffer<u8> reply(replysize);
1151 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
1153 writeV3S16(&reply[3], p);
1155 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1159 Add it to mesh update queue and set it to be acknowledged after update.
1161 //infostream<<"Adding mesh update task for received block"<<std::endl;
1162 addUpdateMeshTaskWithEdge(p, true);
1164 else if(command == TOCLIENT_INVENTORY)
1169 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
1172 //TimeTaker t2("mutex locking", m_device);
1173 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1176 //TimeTaker t3("istringstream init", m_device);
1177 std::string datastring((char*)&data[2], datasize-2);
1178 std::istringstream is(datastring, std::ios_base::binary);
1181 //m_env.printPlayers(infostream);
1183 //TimeTaker t4("player get", m_device);
1184 Player *player = m_env.getLocalPlayer();
1185 assert(player != NULL);
1188 //TimeTaker t1("inventory.deSerialize()", m_device);
1189 player->inventory.deSerialize(is);
1192 m_inventory_updated = true;
1194 delete m_inventory_from_server;
1195 m_inventory_from_server = new Inventory(player->inventory);
1196 m_inventory_from_server_age = 0.0;
1198 //infostream<<"Client got player inventory:"<<std::endl;
1199 //player->inventory.print(infostream);
1202 else if(command == TOCLIENT_TIME_OF_DAY)
1207 u16 time_of_day = readU16(&data[2]);
1208 time_of_day = time_of_day % 24000;
1209 //infostream<<"Client: time_of_day="<<time_of_day<<std::endl;
1210 float time_speed = 0;
1211 if(datasize >= 2 + 2 + 4){
1212 time_speed = readF1000(&data[4]);
1214 // Old message; try to approximate speed of time by ourselves
1215 float time_of_day_f = (float)time_of_day / 24000.0;
1216 float tod_diff_f = 0;
1217 if(time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
1218 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0;
1220 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
1221 m_last_time_of_day_f = time_of_day_f;
1222 float time_diff = m_time_of_day_update_timer;
1223 m_time_of_day_update_timer = 0;
1224 if(m_time_of_day_set){
1225 time_speed = 3600.0*24.0 * tod_diff_f / time_diff;
1226 infostream<<"Client: Measured time_of_day speed (old format): "
1227 <<time_speed<<" tod_diff_f="<<tod_diff_f
1228 <<" time_diff="<<time_diff<<std::endl;
1232 // Update environment
1233 m_env.setTimeOfDay(time_of_day);
1234 m_env.setTimeOfDaySpeed(time_speed);
1235 m_time_of_day_set = true;
1237 u32 dr = m_env.getDayNightRatio();
1238 verbosestream<<"Client: time_of_day="<<time_of_day
1239 <<" time_speed="<<time_speed
1240 <<" dr="<<dr<<std::endl;
1242 else if(command == TOCLIENT_CHAT_MESSAGE)
1250 std::string datastring((char*)&data[2], datasize-2);
1251 std::istringstream is(datastring, std::ios_base::binary);
1254 is.read((char*)buf, 2);
1255 u16 len = readU16(buf);
1257 std::wstring message;
1258 for(u16 i=0; i<len; i++)
1260 is.read((char*)buf, 2);
1261 message += (wchar_t)readU16(buf);
1264 /*infostream<<"Client received chat message: "
1265 <<wide_to_narrow(message)<<std::endl;*/
1267 m_chat_queue.push_back(message);
1269 else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1271 //if(g_settings->getBool("enable_experimental"))
1275 u16 count of removed objects
1276 for all removed objects {
1279 u16 count of added objects
1280 for all added objects {
1283 u32 initialization data length
1284 string initialization data
1289 // Get all data except the command number
1290 std::string datastring((char*)&data[2], datasize-2);
1291 // Throw them in an istringstream
1292 std::istringstream is(datastring, std::ios_base::binary);
1296 // Read removed objects
1298 u16 removed_count = readU16((u8*)buf);
1299 for(u16 i=0; i<removed_count; i++)
1302 u16 id = readU16((u8*)buf);
1305 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1306 m_env.removeActiveObject(id);
1310 // Read added objects
1312 u16 added_count = readU16((u8*)buf);
1313 for(u16 i=0; i<added_count; i++)
1316 u16 id = readU16((u8*)buf);
1318 u8 type = readU8((u8*)buf);
1319 std::string data = deSerializeLongString(is);
1322 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1323 m_env.addActiveObject(id, type, data);
1328 else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1330 //if(g_settings->getBool("enable_experimental"))
1342 // Get all data except the command number
1343 std::string datastring((char*)&data[2], datasize-2);
1344 // Throw them in an istringstream
1345 std::istringstream is(datastring, std::ios_base::binary);
1347 while(is.eof() == false)
1351 u16 id = readU16((u8*)buf);
1355 u16 message_size = readU16((u8*)buf);
1356 std::string message;
1357 message.reserve(message_size);
1358 for(u16 i=0; i<message_size; i++)
1361 message.append(buf, 1);
1363 // Pass on to the environment
1365 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1366 m_env.processActiveObjectMessage(id, message);
1371 else if(command == TOCLIENT_HP)
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 u8 oldhp = player->hp;
1383 // Add to ClientEvent queue
1385 event.type = CE_PLAYER_DAMAGE;
1386 event.player_damage.amount = oldhp - hp;
1387 m_client_event_queue.push_back(event);
1390 else if(command == TOCLIENT_MOVE_PLAYER)
1392 std::string datastring((char*)&data[2], datasize-2);
1393 std::istringstream is(datastring, std::ios_base::binary);
1394 Player *player = m_env.getLocalPlayer();
1395 assert(player != NULL);
1396 v3f pos = readV3F1000(is);
1397 f32 pitch = readF1000(is);
1398 f32 yaw = readF1000(is);
1399 player->setPosition(pos);
1400 /*player->setPitch(pitch);
1401 player->setYaw(yaw);*/
1403 infostream<<"Client got TOCLIENT_MOVE_PLAYER"
1404 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1410 Add to ClientEvent queue.
1411 This has to be sent to the main program because otherwise
1412 it would just force the pitch and yaw values to whatever
1413 the camera points to.
1416 event.type = CE_PLAYER_FORCE_MOVE;
1417 event.player_force_move.pitch = pitch;
1418 event.player_force_move.yaw = yaw;
1419 m_client_event_queue.push_back(event);
1421 // Ignore damage for a few seconds, so that the player doesn't
1422 // get damage from falling on ground
1423 m_ignore_damage_timer = 3.0;
1425 else if(command == TOCLIENT_PLAYERITEM)
1427 infostream<<"Client: WARNING: Ignoring TOCLIENT_PLAYERITEM"<<std::endl;
1429 else if(command == TOCLIENT_DEATHSCREEN)
1431 std::string datastring((char*)&data[2], datasize-2);
1432 std::istringstream is(datastring, std::ios_base::binary);
1434 bool set_camera_point_target = readU8(is);
1435 v3f camera_point_target = readV3F1000(is);
1438 event.type = CE_DEATHSCREEN;
1439 event.deathscreen.set_camera_point_target = set_camera_point_target;
1440 event.deathscreen.camera_point_target_x = camera_point_target.X;
1441 event.deathscreen.camera_point_target_y = camera_point_target.Y;
1442 event.deathscreen.camera_point_target_z = camera_point_target.Z;
1443 m_client_event_queue.push_back(event);
1445 else if(command == TOCLIENT_ANNOUNCE_MEDIA)
1447 std::string datastring((char*)&data[2], datasize-2);
1448 std::istringstream is(datastring, std::ios_base::binary);
1450 // Mesh update thread must be stopped while
1451 // updating content definitions
1452 assert(!m_mesh_update_thread.IsRunning());
1454 int num_files = readU16(is);
1456 verbosestream<<"Client received TOCLIENT_ANNOUNCE_MEDIA ("
1457 <<num_files<<" files)"<<std::endl;
1459 core::list<MediaRequest> file_requests;
1461 for(int i=0; i<num_files; i++)
1463 //read file from cache
1464 std::string name = deSerializeString(is);
1465 std::string sha1_base64 = deSerializeString(is);
1467 // if name contains illegal characters, ignore the file
1468 if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
1469 errorstream<<"Client: ignoring illegal file name "
1470 <<"sent by server: \""<<name<<"\""<<std::endl;
1474 std::string sha1_raw = base64_decode(sha1_base64);
1475 std::string sha1_hex = hex_encode(sha1_raw);
1476 std::ostringstream tmp_os(std::ios_base::binary);
1477 bool found_in_cache = m_media_cache.load_sha1(sha1_raw, tmp_os);
1478 m_media_name_sha1_map.set(name, sha1_raw);
1480 // If found in cache, try to load it from there
1483 bool success = loadMedia(tmp_os.str(), name);
1485 verbosestream<<"Client: Loaded cached media: "
1486 <<sha1_hex<<" \""<<name<<"\""<<std::endl;
1489 infostream<<"Client: Failed to load cached media: "
1490 <<sha1_hex<<" \""<<name<<"\""<<std::endl;
1493 // Didn't load from cache; queue it to be requested
1494 verbosestream<<"Client: Adding file to request list: \""
1495 <<sha1_hex<<" \""<<name<<"\""<<std::endl;
1496 file_requests.push_back(MediaRequest(name));
1500 event.type = CE_TEXTURES_UPDATED;
1501 m_client_event_queue.push_back(event);
1505 u16 number of files requested
1511 std::ostringstream os(std::ios_base::binary);
1512 writeU16(os, TOSERVER_REQUEST_MEDIA);
1513 writeU16(os, file_requests.size());
1515 for(core::list<MediaRequest>::Iterator i = file_requests.begin();
1516 i != file_requests.end(); i++) {
1517 os<<serializeString(i->name);
1521 std::string s = os.str();
1522 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1524 Send(0, data, true);
1525 infostream<<"Client: Sending media request list to server ("
1526 <<file_requests.size()<<" files)"<<std::endl;
1528 else if(command == TOCLIENT_MEDIA)
1530 std::string datastring((char*)&data[2], datasize-2);
1531 std::istringstream is(datastring, std::ios_base::binary);
1533 // Mesh update thread must be stopped while
1534 // updating content definitions
1535 assert(!m_mesh_update_thread.IsRunning());
1539 u16 total number of file bunches
1540 u16 index of this bunch
1541 u32 number of files in this bunch
1549 int num_bunches = readU16(is);
1550 int bunch_i = readU16(is);
1551 if(num_bunches >= 2)
1552 m_media_receive_progress = (float)bunch_i / (float)(num_bunches - 1);
1554 m_media_receive_progress = 1.0;
1555 if(bunch_i == num_bunches - 1)
1556 m_media_received = true;
1557 int num_files = readU32(is);
1558 infostream<<"Client: Received files: bunch "<<bunch_i<<"/"
1559 <<num_bunches<<" files="<<num_files
1560 <<" size="<<datasize<<std::endl;
1561 for(int i=0; i<num_files; i++){
1562 std::string name = deSerializeString(is);
1563 std::string data = deSerializeLongString(is);
1565 // if name contains illegal characters, ignore the file
1566 if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
1567 errorstream<<"Client: ignoring illegal file name "
1568 <<"sent by server: \""<<name<<"\""<<std::endl;
1572 bool success = loadMedia(data, name);
1574 verbosestream<<"Client: Loaded received media: "
1575 <<"\""<<name<<"\". Caching."<<std::endl;
1577 infostream<<"Client: Failed to load received media: "
1578 <<"\""<<name<<"\". Not caching."<<std::endl;
1582 bool did = fs::CreateAllDirs(getMediaCacheDir());
1584 errorstream<<"Could not create media cache directory"
1589 core::map<std::string, std::string>::Node *n;
1590 n = m_media_name_sha1_map.find(name);
1592 errorstream<<"The server sent a file that has not "
1593 <<"been announced."<<std::endl;
1595 m_media_cache.update_sha1(data);
1600 event.type = CE_TEXTURES_UPDATED;
1601 m_client_event_queue.push_back(event);
1603 else if(command == TOCLIENT_TOOLDEF)
1605 infostream<<"Client: WARNING: Ignoring TOCLIENT_TOOLDEF"<<std::endl;
1607 else if(command == TOCLIENT_NODEDEF)
1609 infostream<<"Client: Received node definitions: packet size: "
1610 <<datasize<<std::endl;
1612 // Mesh update thread must be stopped while
1613 // updating content definitions
1614 assert(!m_mesh_update_thread.IsRunning());
1616 // Decompress node definitions
1617 std::string datastring((char*)&data[2], datasize-2);
1618 std::istringstream is(datastring, std::ios_base::binary);
1619 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1620 std::ostringstream tmp_os;
1621 decompressZlib(tmp_is, tmp_os);
1623 // Deserialize node definitions
1624 std::istringstream tmp_is2(tmp_os.str());
1625 m_nodedef->deSerialize(tmp_is2);
1626 m_nodedef_received = true;
1628 else if(command == TOCLIENT_CRAFTITEMDEF)
1630 infostream<<"Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF"<<std::endl;
1632 else if(command == TOCLIENT_ITEMDEF)
1634 infostream<<"Client: Received item definitions: packet size: "
1635 <<datasize<<std::endl;
1637 // Mesh update thread must be stopped while
1638 // updating content definitions
1639 assert(!m_mesh_update_thread.IsRunning());
1641 // Decompress item definitions
1642 std::string datastring((char*)&data[2], datasize-2);
1643 std::istringstream is(datastring, std::ios_base::binary);
1644 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1645 std::ostringstream tmp_os;
1646 decompressZlib(tmp_is, tmp_os);
1648 // Deserialize node definitions
1649 std::istringstream tmp_is2(tmp_os.str());
1650 m_itemdef->deSerialize(tmp_is2);
1651 m_itemdef_received = true;
1653 else if(command == TOCLIENT_PLAY_SOUND)
1655 std::string datastring((char*)&data[2], datasize-2);
1656 std::istringstream is(datastring, std::ios_base::binary);
1658 s32 server_id = readS32(is);
1659 std::string name = deSerializeString(is);
1660 float gain = readF1000(is);
1661 int type = readU8(is); // 0=local, 1=positional, 2=object
1662 v3f pos = readV3F1000(is);
1663 u16 object_id = readU16(is);
1664 bool loop = readU8(is);
1669 client_id = m_sound->playSound(name, loop, gain);
1671 case 1: // positional
1672 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1675 ClientActiveObject *cao = m_env.getActiveObject(object_id);
1677 pos = cao->getPosition();
1678 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1679 // TODO: Set up sound to move with object
1684 if(client_id != -1){
1685 m_sounds_server_to_client[server_id] = client_id;
1686 m_sounds_client_to_server[client_id] = server_id;
1688 m_sounds_to_objects[client_id] = object_id;
1691 else if(command == TOCLIENT_STOP_SOUND)
1693 std::string datastring((char*)&data[2], datasize-2);
1694 std::istringstream is(datastring, std::ios_base::binary);
1696 s32 server_id = readS32(is);
1697 std::map<s32, int>::iterator i =
1698 m_sounds_server_to_client.find(server_id);
1699 if(i != m_sounds_server_to_client.end()){
1700 int client_id = i->second;
1701 m_sound->stopSound(client_id);
1704 else if(command == TOCLIENT_PRIVILEGES)
1706 std::string datastring((char*)&data[2], datasize-2);
1707 std::istringstream is(datastring, std::ios_base::binary);
1709 m_privileges.clear();
1710 infostream<<"Client: Privileges updated: ";
1711 u16 num_privileges = readU16(is);
1712 for(u16 i=0; i<num_privileges; i++){
1713 std::string priv = deSerializeString(is);
1714 m_privileges.insert(priv);
1715 infostream<<priv<<" ";
1717 infostream<<std::endl;
1719 else if(command == TOCLIENT_INVENTORY_FORMSPEC)
1721 std::string datastring((char*)&data[2], datasize-2);
1722 std::istringstream is(datastring, std::ios_base::binary);
1724 // Store formspec in LocalPlayer
1725 Player *player = m_env.getLocalPlayer();
1726 assert(player != NULL);
1727 player->inventory_formspec = deSerializeLongString(is);
1729 else if(command == TOCLIENT_DETACHED_INVENTORY)
1731 std::string datastring((char*)&data[2], datasize-2);
1732 std::istringstream is(datastring, std::ios_base::binary);
1734 std::string name = deSerializeString(is);
1736 infostream<<"Client: Detached inventory update: \""<<name<<"\""<<std::endl;
1738 Inventory *inv = NULL;
1739 if(m_detached_inventories.count(name) > 0)
1740 inv = m_detached_inventories[name];
1742 inv = new Inventory(m_itemdef);
1743 m_detached_inventories[name] = inv;
1745 inv->deSerialize(is);
1749 infostream<<"Client: Ignoring unknown command "
1750 <<command<<std::endl;
1754 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
1756 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1757 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
1760 void Client::interact(u8 action, const PointedThing& pointed)
1762 if(connectedAndInitialized() == false){
1763 infostream<<"Client::interact() "
1764 "cancelled (not connected)"
1769 std::ostringstream os(std::ios_base::binary);
1775 [5] u32 length of the next item
1776 [9] serialized PointedThing
1778 0: start digging (from undersurface) or use
1779 1: stop digging (all parameters ignored)
1780 2: digging completed
1781 3: place block or item (to abovesurface)
1784 writeU16(os, TOSERVER_INTERACT);
1785 writeU8(os, action);
1786 writeU16(os, getPlayerItem());
1787 std::ostringstream tmp_os(std::ios::binary);
1788 pointed.serialize(tmp_os);
1789 os<<serializeLongString(tmp_os.str());
1791 std::string s = os.str();
1792 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1795 Send(0, data, true);
1798 void Client::sendNodemetaFields(v3s16 p, const std::string &formname,
1799 const std::map<std::string, std::string> &fields)
1801 std::ostringstream os(std::ios_base::binary);
1803 writeU16(os, TOSERVER_NODEMETA_FIELDS);
1805 os<<serializeString(formname);
1806 writeU16(os, fields.size());
1807 for(std::map<std::string, std::string>::const_iterator
1808 i = fields.begin(); i != fields.end(); i++){
1809 const std::string &name = i->first;
1810 const std::string &value = i->second;
1811 os<<serializeString(name);
1812 os<<serializeLongString(value);
1816 std::string s = os.str();
1817 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1819 Send(0, data, true);
1822 void Client::sendInventoryFields(const std::string &formname,
1823 const std::map<std::string, std::string> &fields)
1825 std::ostringstream os(std::ios_base::binary);
1827 writeU16(os, TOSERVER_INVENTORY_FIELDS);
1828 os<<serializeString(formname);
1829 writeU16(os, fields.size());
1830 for(std::map<std::string, std::string>::const_iterator
1831 i = fields.begin(); i != fields.end(); i++){
1832 const std::string &name = i->first;
1833 const std::string &value = i->second;
1834 os<<serializeString(name);
1835 os<<serializeLongString(value);
1839 std::string s = os.str();
1840 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1842 Send(0, data, true);
1845 void Client::sendInventoryAction(InventoryAction *a)
1847 std::ostringstream os(std::ios_base::binary);
1851 writeU16(buf, TOSERVER_INVENTORY_ACTION);
1852 os.write((char*)buf, 2);
1857 std::string s = os.str();
1858 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1860 Send(0, data, true);
1863 void Client::sendChatMessage(const std::wstring &message)
1865 std::ostringstream os(std::ios_base::binary);
1869 writeU16(buf, TOSERVER_CHAT_MESSAGE);
1870 os.write((char*)buf, 2);
1873 writeU16(buf, message.size());
1874 os.write((char*)buf, 2);
1877 for(u32 i=0; i<message.size(); i++)
1881 os.write((char*)buf, 2);
1885 std::string s = os.str();
1886 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1888 Send(0, data, true);
1891 void Client::sendChangePassword(const std::wstring oldpassword,
1892 const std::wstring newpassword)
1894 Player *player = m_env.getLocalPlayer();
1898 std::string playername = player->getName();
1899 std::string oldpwd = translatePassword(playername, oldpassword);
1900 std::string newpwd = translatePassword(playername, newpassword);
1902 std::ostringstream os(std::ios_base::binary);
1903 u8 buf[2+PASSWORD_SIZE*2];
1905 [0] u16 TOSERVER_PASSWORD
1906 [2] u8[28] old password
1907 [30] u8[28] new password
1910 writeU16(buf, TOSERVER_PASSWORD);
1911 for(u32 i=0;i<PASSWORD_SIZE-1;i++)
1913 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
1914 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
1916 buf[2+PASSWORD_SIZE-1] = 0;
1917 buf[30+PASSWORD_SIZE-1] = 0;
1918 os.write((char*)buf, 2+PASSWORD_SIZE*2);
1921 std::string s = os.str();
1922 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1924 Send(0, data, true);
1928 void Client::sendDamage(u8 damage)
1930 DSTACK(__FUNCTION_NAME);
1931 std::ostringstream os(std::ios_base::binary);
1933 writeU16(os, TOSERVER_DAMAGE);
1934 writeU8(os, damage);
1937 std::string s = os.str();
1938 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1940 Send(0, data, true);
1943 void Client::sendRespawn()
1945 DSTACK(__FUNCTION_NAME);
1946 std::ostringstream os(std::ios_base::binary);
1948 writeU16(os, TOSERVER_RESPAWN);
1951 std::string s = os.str();
1952 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1954 Send(0, data, true);
1957 void Client::sendPlayerPos()
1959 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1961 Player *myplayer = m_env.getLocalPlayer();
1962 if(myplayer == NULL)
1967 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1968 our_peer_id = m_con.GetPeerID();
1971 // Set peer id if not set already
1972 if(myplayer->peer_id == PEER_ID_INEXISTENT)
1973 myplayer->peer_id = our_peer_id;
1974 // Check that an existing peer_id is the same as the connection's
1975 assert(myplayer->peer_id == our_peer_id);
1977 v3f pf = myplayer->getPosition();
1978 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
1979 v3f sf = myplayer->getSpeed();
1980 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
1981 s32 pitch = myplayer->getPitch() * 100;
1982 s32 yaw = myplayer->getYaw() * 100;
1987 [2] v3s32 position*100
1988 [2+12] v3s32 speed*100
1989 [2+12+12] s32 pitch*100
1990 [2+12+12+4] s32 yaw*100
1993 SharedBuffer<u8> data(2+12+12+4+4);
1994 writeU16(&data[0], TOSERVER_PLAYERPOS);
1995 writeV3S32(&data[2], position);
1996 writeV3S32(&data[2+12], speed);
1997 writeS32(&data[2+12+12], pitch);
1998 writeS32(&data[2+12+12+4], yaw);
2000 // Send as unreliable
2001 Send(0, data, false);
2004 void Client::sendPlayerItem(u16 item)
2006 Player *myplayer = m_env.getLocalPlayer();
2007 if(myplayer == NULL)
2010 u16 our_peer_id = m_con.GetPeerID();
2012 // Set peer id if not set already
2013 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2014 myplayer->peer_id = our_peer_id;
2015 // Check that an existing peer_id is the same as the connection's
2016 assert(myplayer->peer_id == our_peer_id);
2018 SharedBuffer<u8> data(2+2);
2019 writeU16(&data[0], TOSERVER_PLAYERITEM);
2020 writeU16(&data[2], item);
2023 Send(0, data, true);
2026 void Client::removeNode(v3s16 p)
2028 core::map<v3s16, MapBlock*> modified_blocks;
2032 //TimeTaker t("removeNodeAndUpdate", m_device);
2033 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
2035 catch(InvalidPositionException &e)
2039 // add urgent task to update the modified node
2040 addUpdateMeshTaskForNode(p, false, true);
2042 for(core::map<v3s16, MapBlock * >::Iterator
2043 i = modified_blocks.getIterator();
2044 i.atEnd() == false; i++)
2046 v3s16 p = i.getNode()->getKey();
2047 addUpdateMeshTaskWithEdge(p);
2051 void Client::addNode(v3s16 p, MapNode n)
2053 TimeTaker timer1("Client::addNode()");
2055 core::map<v3s16, MapBlock*> modified_blocks;
2059 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
2060 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks);
2062 catch(InvalidPositionException &e)
2065 for(core::map<v3s16, MapBlock * >::Iterator
2066 i = modified_blocks.getIterator();
2067 i.atEnd() == false; i++)
2069 v3s16 p = i.getNode()->getKey();
2070 addUpdateMeshTaskWithEdge(p);
2074 void Client::setPlayerControl(PlayerControl &control)
2076 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2077 LocalPlayer *player = m_env.getLocalPlayer();
2078 assert(player != NULL);
2079 player->control = control;
2082 void Client::selectPlayerItem(u16 item)
2084 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2085 m_playeritem = item;
2086 m_inventory_updated = true;
2087 sendPlayerItem(item);
2090 // Returns true if the inventory of the local player has been
2091 // updated from the server. If it is true, it is set to false.
2092 bool Client::getLocalInventoryUpdated()
2094 // m_inventory_updated is behind envlock
2095 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2096 bool updated = m_inventory_updated;
2097 m_inventory_updated = false;
2101 // Copies the inventory of the local player to parameter
2102 void Client::getLocalInventory(Inventory &dst)
2104 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2105 Player *player = m_env.getLocalPlayer();
2106 assert(player != NULL);
2107 dst = player->inventory;
2110 Inventory* Client::getInventory(const InventoryLocation &loc)
2113 case InventoryLocation::UNDEFINED:
2116 case InventoryLocation::CURRENT_PLAYER:
2118 Player *player = m_env.getLocalPlayer();
2119 assert(player != NULL);
2120 return &player->inventory;
2123 case InventoryLocation::PLAYER:
2125 Player *player = m_env.getPlayer(loc.name.c_str());
2128 return &player->inventory;
2131 case InventoryLocation::NODEMETA:
2133 NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p);
2136 return meta->getInventory();
2139 case InventoryLocation::DETACHED:
2141 if(m_detached_inventories.count(loc.name) == 0)
2143 return m_detached_inventories[loc.name];
2151 void Client::inventoryAction(InventoryAction *a)
2154 Send it to the server
2156 sendInventoryAction(a);
2159 Predict some local inventory changes
2161 a->clientApply(this, this);
2164 ClientActiveObject * Client::getSelectedActiveObject(
2166 v3f from_pos_f_on_map,
2167 core::line3d<f32> shootline_on_map
2170 core::array<DistanceSortedActiveObject> objects;
2172 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
2174 //infostream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
2177 // After this, the closest object is the first in the array.
2180 for(u32 i=0; i<objects.size(); i++)
2182 ClientActiveObject *obj = objects[i].obj;
2184 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
2185 if(selection_box == NULL)
2188 v3f pos = obj->getPosition();
2190 core::aabbox3d<f32> offsetted_box(
2191 selection_box->MinEdge + pos,
2192 selection_box->MaxEdge + pos
2195 if(offsetted_box.intersectsWithLine(shootline_on_map))
2197 //infostream<<"Returning selected object"<<std::endl;
2202 //infostream<<"No object selected; returning NULL."<<std::endl;
2206 void Client::printDebugInfo(std::ostream &os)
2208 //JMutexAutoLock lock1(m_fetchblock_mutex);
2209 /*JMutexAutoLock lock2(m_incoming_queue_mutex);
2211 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
2212 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
2213 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
2217 core::list<std::wstring> Client::getConnectedPlayerNames()
2219 core::list<Player*> players = m_env.getPlayers(true);
2220 core::list<std::wstring> playerNames;
2221 for(core::list<Player*>::Iterator
2222 i = players.begin();
2223 i != players.end(); i++)
2225 Player *player = *i;
2226 playerNames.push_back(narrow_to_wide(player->getName()));
2231 float Client::getAnimationTime()
2233 return m_animation_time;
2236 int Client::getCrackLevel()
2238 return m_crack_level;
2241 void Client::setCrack(int level, v3s16 pos)
2243 int old_crack_level = m_crack_level;
2244 v3s16 old_crack_pos = m_crack_pos;
2246 m_crack_level = level;
2249 if(old_crack_level >= 0 && (level < 0 || pos != old_crack_pos))
2252 addUpdateMeshTaskForNode(old_crack_pos, false, true);
2254 if(level >= 0 && (old_crack_level < 0 || pos != old_crack_pos))
2257 addUpdateMeshTaskForNode(pos, false, true);
2263 Player *player = m_env.getLocalPlayer();
2264 assert(player != NULL);
2268 bool Client::getChatMessage(std::wstring &message)
2270 if(m_chat_queue.size() == 0)
2272 message = m_chat_queue.pop_front();
2276 void Client::typeChatMessage(const std::wstring &message)
2278 // Discard empty line
2283 sendChatMessage(message);
2286 if (message[0] == L'/')
2288 m_chat_queue.push_back(
2289 (std::wstring)L"issued command: "+message);
2293 LocalPlayer *player = m_env.getLocalPlayer();
2294 assert(player != NULL);
2295 std::wstring name = narrow_to_wide(player->getName());
2296 m_chat_queue.push_back(
2297 (std::wstring)L"<"+name+L"> "+message);
2301 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
2303 /*infostream<<"Client::addUpdateMeshTask(): "
2304 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2305 <<" ack_to_server="<<ack_to_server
2306 <<" urgent="<<urgent
2309 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2314 Create a task to update the mesh of the block
2317 MeshMakeData *data = new MeshMakeData(this);
2320 //TimeTaker timer("data fill");
2322 // Debug: 1-6ms, avg=2ms
2324 data->setCrack(m_crack_level, m_crack_pos);
2325 data->setSmoothLighting(g_settings->getBool("smooth_lighting"));
2329 //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
2331 // Add task to queue
2332 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server, urgent);
2334 /*infostream<<"Mesh update input queue size is "
2335 <<m_mesh_update_thread.m_queue_in.size()
2339 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent)
2343 infostream<<"Client::addUpdateMeshTaskWithEdge(): "
2344 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2349 v3s16 p = blockpos + v3s16(0,0,0);
2350 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2351 addUpdateMeshTask(p, ack_to_server, urgent);
2353 catch(InvalidPositionException &e){}
2356 v3s16 p = blockpos + v3s16(-1,0,0);
2357 addUpdateMeshTask(p, false, urgent);
2359 catch(InvalidPositionException &e){}
2361 v3s16 p = blockpos + v3s16(0,-1,0);
2362 addUpdateMeshTask(p, false, urgent);
2364 catch(InvalidPositionException &e){}
2366 v3s16 p = blockpos + v3s16(0,0,-1);
2367 addUpdateMeshTask(p, false, urgent);
2369 catch(InvalidPositionException &e){}
2372 void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool urgent)
2376 infostream<<"Client::addUpdateMeshTaskForNode(): "
2377 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2381 v3s16 blockpos = getNodeBlockPos(nodepos);
2382 v3s16 blockpos_relative = blockpos * MAP_BLOCKSIZE;
2385 v3s16 p = blockpos + v3s16(0,0,0);
2386 addUpdateMeshTask(p, ack_to_server, urgent);
2388 catch(InvalidPositionException &e){}
2390 if(nodepos.X == blockpos_relative.X){
2392 v3s16 p = blockpos + v3s16(-1,0,0);
2393 addUpdateMeshTask(p, false, urgent);
2395 catch(InvalidPositionException &e){}
2397 if(nodepos.Y == blockpos_relative.Y){
2399 v3s16 p = blockpos + v3s16(0,-1,0);
2400 addUpdateMeshTask(p, false, urgent);
2402 catch(InvalidPositionException &e){}
2404 if(nodepos.Z == blockpos_relative.Z){
2406 v3s16 p = blockpos + v3s16(0,0,-1);
2407 addUpdateMeshTask(p, false, urgent);
2409 catch(InvalidPositionException &e){}
2413 ClientEvent Client::getClientEvent()
2415 if(m_client_event_queue.size() == 0)
2418 event.type = CE_NONE;
2421 return m_client_event_queue.pop_front();
2424 void Client::afterContentReceived()
2426 verbosestream<<"Client::afterContentReceived() started"<<std::endl;
2427 assert(m_itemdef_received);
2428 assert(m_nodedef_received);
2429 assert(m_media_received);
2431 // remove the information about which checksum each texture
2433 m_media_name_sha1_map.clear();
2435 // Rebuild inherited images and recreate textures
2436 verbosestream<<"Rebuilding images and textures"<<std::endl;
2437 m_tsrc->rebuildImagesAndTextures();
2439 // Update texture atlas
2440 verbosestream<<"Updating texture atlas"<<std::endl;
2441 if(g_settings->getBool("enable_texture_atlas"))
2442 m_tsrc->buildMainAtlas(this);
2444 // Update node aliases
2445 verbosestream<<"Updating node aliases"<<std::endl;
2446 m_nodedef->updateAliases(m_itemdef);
2448 // Update node textures
2449 verbosestream<<"Updating node textures"<<std::endl;
2450 m_nodedef->updateTextures(m_tsrc);
2452 // Update item textures and meshes
2453 verbosestream<<"Updating item textures and meshes"<<std::endl;
2454 m_itemdef->updateTexturesAndMeshes(this);
2456 // Start mesh update thread after setting up content definitions
2457 verbosestream<<"Starting mesh update thread"<<std::endl;
2458 m_mesh_update_thread.Start();
2460 verbosestream<<"Client::afterContentReceived() done"<<std::endl;
2463 float Client::getRTT(void)
2466 return m_con.GetPeerAvgRTT(PEER_ID_SERVER);
2467 } catch(con::PeerNotFoundException &e){
2472 // IGameDef interface
2474 IItemDefManager* Client::getItemDefManager()
2478 INodeDefManager* Client::getNodeDefManager()
2482 ICraftDefManager* Client::getCraftDefManager()
2485 //return m_craftdef;
2487 ITextureSource* Client::getTextureSource()
2491 u16 Client::allocateUnknownNodeId(const std::string &name)
2493 errorstream<<"Client::allocateUnknownNodeId(): "
2494 <<"Client cannot allocate node IDs"<<std::endl;
2496 return CONTENT_IGNORE;
2498 ISoundManager* Client::getSoundManager()
2502 MtEventManager* Client::getEventManager()