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"
37 #include <IFileSystem.h>
40 #include "clientmap.h"
41 #include "filecache.h"
43 #include "util/string.h"
45 #include "IMeshCache.h"
46 #include "util/serialize.h"
48 static std::string getMediaCacheDir()
50 return porting::path_user + DIR_DELIM + "cache" + DIR_DELIM + "media";
57 MediaRequest(const std::string &name_=""):
66 QueuedMeshUpdate::QueuedMeshUpdate():
69 ack_block_to_server(false)
73 QueuedMeshUpdate::~QueuedMeshUpdate()
83 MeshUpdateQueue::MeshUpdateQueue()
88 MeshUpdateQueue::~MeshUpdateQueue()
90 JMutexAutoLock lock(m_mutex);
92 for(std::vector<QueuedMeshUpdate*>::iterator
94 i != m_queue.end(); i++)
96 QueuedMeshUpdate *q = *i;
102 peer_id=0 adds with nobody to send to
104 void MeshUpdateQueue::addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_server, bool urgent)
106 DSTACK(__FUNCTION_NAME);
110 JMutexAutoLock lock(m_mutex);
116 Find if block is already in queue.
117 If it is, update the data and quit.
119 for(std::vector<QueuedMeshUpdate*>::iterator
121 i != m_queue.end(); i++)
123 QueuedMeshUpdate *q = *i;
129 if(ack_block_to_server)
130 q->ack_block_to_server = true;
138 QueuedMeshUpdate *q = new QueuedMeshUpdate;
141 q->ack_block_to_server = ack_block_to_server;
142 m_queue.push_back(q);
145 // Returned pointer must be deleted
146 // Returns NULL if queue is empty
147 QueuedMeshUpdate * MeshUpdateQueue::pop()
149 JMutexAutoLock lock(m_mutex);
151 bool must_be_urgent = !m_urgents.empty();
152 for(std::vector<QueuedMeshUpdate*>::iterator
154 i != m_queue.end(); i++)
156 QueuedMeshUpdate *q = *i;
157 if(must_be_urgent && m_urgents.count(q->p) == 0)
160 m_urgents.erase(q->p);
170 void * MeshUpdateThread::Thread()
174 log_register_thread("MeshUpdateThread");
176 DSTACK(__FUNCTION_NAME);
178 BEGIN_DEBUG_EXCEPTION_HANDLER
182 /*// Wait for output queue to flush.
183 // Allow 2 in queue, this makes less frametime jitter.
184 // Umm actually, there is no much difference
185 if(m_queue_out.size() >= 2)
191 QueuedMeshUpdate *q = m_queue_in.pop();
198 ScopeProfiler sp(g_profiler, "Client: Mesh making");
200 MapBlockMesh *mesh_new = new MapBlockMesh(q->data);
201 if(mesh_new->getMesh()->getMeshBufferCount() == 0)
210 r.ack_block_to_server = q->ack_block_to_server;
212 /*infostream<<"MeshUpdateThread: Processed "
213 <<"("<<q->p.X<<","<<q->p.Y<<","<<q->p.Z<<")"
216 m_queue_out.push_back(r);
221 END_DEBUG_EXCEPTION_HANDLER(errorstream)
227 IrrlichtDevice *device,
228 const char *playername,
229 std::string password,
230 MapDrawControl &control,
231 IWritableTextureSource *tsrc,
232 IWritableShaderSource *shsrc,
233 IWritableItemDefManager *itemdef,
234 IWritableNodeDefManager *nodedef,
235 ISoundManager *sound,
236 MtEventManager *event
244 m_mesh_update_thread(this),
246 new ClientMap(this, this, control,
247 device->getSceneManager()->getRootSceneNode(),
248 device->getSceneManager(), 666),
249 device->getSceneManager(),
252 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
254 m_server_ser_ver(SER_FMT_VER_INVALID),
256 m_inventory_updated(false),
257 m_inventory_from_server(NULL),
258 m_inventory_from_server_age(0.0),
263 m_password(password),
264 m_access_denied(false),
265 m_media_cache(getMediaCacheDir()),
266 m_media_receive_progress(0),
267 m_media_received(false),
268 m_itemdef_received(false),
269 m_nodedef_received(false),
270 m_time_of_day_set(false),
271 m_last_time_of_day_f(-1),
272 m_time_of_day_update_timer(0),
273 m_recommended_send_interval(0.1),
274 m_removed_sounds_check_timer(0)
276 m_packetcounter_timer = 0.0;
277 //m_delete_unused_sectors_timer = 0.0;
278 m_connection_reinit_timer = 0.0;
279 m_avg_rtt_timer = 0.0;
280 m_playerpos_send_timer = 0.0;
281 m_ignore_damage_timer = 0.0;
283 // Build main texture atlas, now that the GameDef exists (that is, us)
284 if(g_settings->getBool("enable_texture_atlas"))
285 m_tsrc->buildMainAtlas(this);
287 infostream<<"Not building texture atlas."<<std::endl;
293 Player *player = new LocalPlayer(this);
295 player->updateName(playername);
297 m_env.addPlayer(player);
304 //JMutexAutoLock conlock(m_con_mutex); //bulk comment-out
308 m_mesh_update_thread.setRun(false);
309 while(m_mesh_update_thread.IsRunning())
312 delete m_inventory_from_server;
314 // Delete detached inventories
316 for(std::map<std::string, Inventory*>::iterator
317 i = m_detached_inventories.begin();
318 i != m_detached_inventories.end(); i++){
324 void Client::connect(Address address)
326 DSTACK(__FUNCTION_NAME);
327 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
328 m_con.SetTimeoutMs(0);
329 m_con.Connect(address);
332 bool Client::connectedAndInitialized()
334 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
336 if(m_con.Connected() == false)
339 if(m_server_ser_ver == SER_FMT_VER_INVALID)
345 void Client::step(float dtime)
347 DSTACK(__FUNCTION_NAME);
353 if(m_ignore_damage_timer > dtime)
354 m_ignore_damage_timer -= dtime;
356 m_ignore_damage_timer = 0.0;
358 m_animation_time += dtime;
359 if(m_animation_time > 60.0)
360 m_animation_time -= 60.0;
362 m_time_of_day_update_timer += dtime;
364 //infostream<<"Client steps "<<dtime<<std::endl;
367 //TimeTaker timer("ReceiveAll()", m_device);
373 //TimeTaker timer("m_con_mutex + m_con.RunTimeouts()", m_device);
375 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
376 m_con.RunTimeouts(dtime);
383 float &counter = m_packetcounter_timer;
389 infostream<<"Client packetcounter (20s):"<<std::endl;
390 m_packetcounter.print(infostream);
391 m_packetcounter.clear();
395 // Get connection status
396 bool connected = connectedAndInitialized();
401 Delete unused sectors
403 NOTE: This jams the game for a while because deleting sectors
407 float &counter = m_delete_unused_sectors_timer;
415 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
417 core::list<v3s16> deleted_blocks;
419 float delete_unused_sectors_timeout =
420 g_settings->getFloat("client_delete_unused_sectors_timeout");
422 // Delete sector blocks
423 /*u32 num = m_env.getMap().unloadUnusedData
424 (delete_unused_sectors_timeout,
425 true, &deleted_blocks);*/
427 // Delete whole sectors
428 m_env.getMap().unloadUnusedData
429 (delete_unused_sectors_timeout,
432 if(deleted_blocks.size() > 0)
434 /*infostream<<"Client: Deleted blocks of "<<num
435 <<" unused sectors"<<std::endl;*/
436 /*infostream<<"Client: Deleted "<<num
437 <<" unused sectors"<<std::endl;*/
443 // Env is locked so con can be locked.
444 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
446 core::list<v3s16>::Iterator i = deleted_blocks.begin();
447 core::list<v3s16> sendlist;
450 if(sendlist.size() == 255 || i == deleted_blocks.end())
452 if(sendlist.size() == 0)
461 u32 replysize = 2+1+6*sendlist.size();
462 SharedBuffer<u8> reply(replysize);
463 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
464 reply[2] = sendlist.size();
466 for(core::list<v3s16>::Iterator
467 j = sendlist.begin();
468 j != sendlist.end(); j++)
470 writeV3S16(&reply[2+1+6*k], *j);
473 m_con.Send(PEER_ID_SERVER, 1, reply, true);
475 if(i == deleted_blocks.end())
481 sendlist.push_back(*i);
489 if(connected == false)
491 float &counter = m_connection_reinit_timer;
497 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
499 Player *myplayer = m_env.getLocalPlayer();
500 assert(myplayer != NULL);
502 // Send TOSERVER_INIT
503 // [0] u16 TOSERVER_INIT
504 // [2] u8 SER_FMT_VER_HIGHEST
505 // [3] u8[20] player_name
506 // [23] u8[28] password (new in some version)
507 // [51] u16 minimum supported network protocol version (added sometime)
508 // [53] u16 maximum supported network protocol version (added later than the previous one)
509 SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2+2);
510 writeU16(&data[0], TOSERVER_INIT);
511 writeU8(&data[2], SER_FMT_VER_HIGHEST);
513 memset((char*)&data[3], 0, PLAYERNAME_SIZE);
514 snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
516 /*infostream<<"Client: sending initial password hash: \""<<m_password<<"\""
519 memset((char*)&data[23], 0, PASSWORD_SIZE);
520 snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
522 writeU16(&data[51], CLIENT_PROTOCOL_VERSION_MIN);
523 writeU16(&data[53], CLIENT_PROTOCOL_VERSION_MAX);
525 // Send as unreliable
526 Send(0, data, false);
529 // Not connected, return
534 Do stuff if connected
538 Run Map's timers and unload unused data
540 const float map_timer_and_unload_dtime = 5.25;
541 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
543 ScopeProfiler sp(g_profiler, "Client: map timer and unload");
544 core::list<v3s16> deleted_blocks;
545 m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
546 g_settings->getFloat("client_unload_unused_data_timeout"),
549 /*if(deleted_blocks.size() > 0)
550 infostream<<"Client: Unloaded "<<deleted_blocks.size()
551 <<" unused blocks"<<std::endl;*/
555 NOTE: This loop is intentionally iterated the way it is.
558 core::list<v3s16>::Iterator i = deleted_blocks.begin();
559 core::list<v3s16> sendlist;
562 if(sendlist.size() == 255 || i == deleted_blocks.end())
564 if(sendlist.size() == 0)
573 u32 replysize = 2+1+6*sendlist.size();
574 SharedBuffer<u8> reply(replysize);
575 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
576 reply[2] = sendlist.size();
578 for(core::list<v3s16>::Iterator
579 j = sendlist.begin();
580 j != sendlist.end(); j++)
582 writeV3S16(&reply[2+1+6*k], *j);
585 m_con.Send(PEER_ID_SERVER, 1, reply, true);
587 if(i == deleted_blocks.end())
593 sendlist.push_back(*i);
603 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
605 // Control local player (0ms)
606 LocalPlayer *player = m_env.getLocalPlayer();
607 assert(player != NULL);
608 player->applyControl(dtime);
610 //TimeTaker envtimer("env step", m_device);
619 ClientEnvEvent event = m_env.getClientEvent();
620 if(event.type == CEE_NONE)
624 else if(event.type == CEE_PLAYER_DAMAGE)
626 if(m_ignore_damage_timer <= 0)
628 u8 damage = event.player_damage.amount;
630 if(event.player_damage.send_to_server)
633 // Add to ClientEvent queue
635 event.type = CE_PLAYER_DAMAGE;
636 event.player_damage.amount = damage;
637 m_client_event_queue.push_back(event);
647 float &counter = m_avg_rtt_timer;
652 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
653 // connectedAndInitialized() is true, peer exists.
654 float avg_rtt = m_con.GetPeerAvgRTT(PEER_ID_SERVER);
655 infostream<<"Client: avg_rtt="<<avg_rtt<<std::endl;
660 Send player position to server
663 float &counter = m_playerpos_send_timer;
665 if(counter >= m_recommended_send_interval)
673 Replace updated meshes
676 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
678 //TimeTaker timer("** Processing mesh update result queue");
681 /*infostream<<"Mesh update result queue size is "
682 <<m_mesh_update_thread.m_queue_out.size()
685 int num_processed_meshes = 0;
686 while(m_mesh_update_thread.m_queue_out.size() > 0)
688 num_processed_meshes++;
689 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_front();
690 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
693 //JMutexAutoLock lock(block->mesh_mutex);
695 // Delete the old mesh
696 if(block->mesh != NULL)
698 // TODO: Remove hardware buffers of meshbuffers of block->mesh
703 // Replace with the new mesh
704 block->mesh = r.mesh;
706 if(r.ack_block_to_server)
708 /*infostream<<"Client: ACK block ("<<r.p.X<<","<<r.p.Y
709 <<","<<r.p.Z<<")"<<std::endl;*/
720 u32 replysize = 2+1+6;
721 SharedBuffer<u8> reply(replysize);
722 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
724 writeV3S16(&reply[3], r.p);
726 m_con.Send(PEER_ID_SERVER, 1, reply, true);
729 if(num_processed_meshes > 0)
730 g_profiler->graphAdd("num_processed_meshes", num_processed_meshes);
734 If the server didn't update the inventory in a while, revert
735 the local inventory (so the player notices the lag problem
736 and knows something is wrong).
738 if(m_inventory_from_server)
740 float interval = 10.0;
741 float count_before = floor(m_inventory_from_server_age / interval);
743 m_inventory_from_server_age += dtime;
745 float count_after = floor(m_inventory_from_server_age / interval);
747 if(count_after != count_before)
749 // Do this every <interval> seconds after TOCLIENT_INVENTORY
750 // Reset the locally changed inventory to the authoritative inventory
751 Player *player = m_env.getLocalPlayer();
752 player->inventory = *m_inventory_from_server;
753 m_inventory_updated = true;
758 Update positions of sounds attached to objects
761 for(std::map<int, u16>::iterator
762 i = m_sounds_to_objects.begin();
763 i != m_sounds_to_objects.end(); i++)
765 int client_id = i->first;
766 u16 object_id = i->second;
767 ClientActiveObject *cao = m_env.getActiveObject(object_id);
770 v3f pos = cao->getPosition();
771 m_sound->updateSoundPosition(client_id, pos);
776 Handle removed remotely initiated sounds
778 m_removed_sounds_check_timer += dtime;
779 if(m_removed_sounds_check_timer >= 2.32)
781 m_removed_sounds_check_timer = 0;
782 // Find removed sounds and clear references to them
783 std::set<s32> removed_server_ids;
784 for(std::map<s32, int>::iterator
785 i = m_sounds_server_to_client.begin();
786 i != m_sounds_server_to_client.end();)
788 s32 server_id = i->first;
789 int client_id = i->second;
791 if(!m_sound->soundExists(client_id)){
792 m_sounds_server_to_client.erase(server_id);
793 m_sounds_client_to_server.erase(client_id);
794 m_sounds_to_objects.erase(client_id);
795 removed_server_ids.insert(server_id);
799 if(removed_server_ids.size() != 0)
801 std::ostringstream os(std::ios_base::binary);
802 writeU16(os, TOSERVER_REMOVED_SOUNDS);
803 writeU16(os, removed_server_ids.size());
804 for(std::set<s32>::iterator i = removed_server_ids.begin();
805 i != removed_server_ids.end(); i++)
807 std::string s = os.str();
808 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
815 bool Client::loadMedia(const std::string &data, const std::string &filename)
817 // Silly irrlicht's const-incorrectness
818 Buffer<char> data_rw(data.c_str(), data.size());
822 const char *image_ext[] = {
823 ".png", ".jpg", ".bmp", ".tga",
824 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
827 name = removeStringEnd(filename, image_ext);
830 verbosestream<<"Client: Attempting to load image "
831 <<"file \""<<filename<<"\""<<std::endl;
833 io::IFileSystem *irrfs = m_device->getFileSystem();
834 video::IVideoDriver *vdrv = m_device->getVideoDriver();
836 // Create an irrlicht memory file
837 io::IReadFile *rfile = irrfs->createMemoryReadFile(
838 *data_rw, data_rw.getSize(), "_tempreadfile");
841 video::IImage *img = vdrv->createImageFromFile(rfile);
843 errorstream<<"Client: Cannot create image from data of "
844 <<"file \""<<filename<<"\""<<std::endl;
849 m_tsrc->insertSourceImage(filename, img);
856 const char *sound_ext[] = {
857 ".0.ogg", ".1.ogg", ".2.ogg", ".3.ogg", ".4.ogg",
858 ".5.ogg", ".6.ogg", ".7.ogg", ".8.ogg", ".9.ogg",
861 name = removeStringEnd(filename, sound_ext);
864 verbosestream<<"Client: Attempting to load sound "
865 <<"file \""<<filename<<"\""<<std::endl;
866 m_sound->loadSoundData(name, data);
870 const char *model_ext[] = {
871 ".x", ".b3d", ".md2", ".obj",
874 name = removeStringEnd(filename, model_ext);
877 verbosestream<<"Client: Storing model into Irrlicht: "
878 <<"\""<<filename<<"\""<<std::endl;
880 io::IFileSystem *irrfs = m_device->getFileSystem();
881 io::IReadFile *rfile = irrfs->createMemoryReadFile(
882 *data_rw, data_rw.getSize(), filename.c_str());
885 scene::ISceneManager *smgr = m_device->getSceneManager();
886 scene::IAnimatedMesh *mesh = smgr->getMesh(rfile);
887 smgr->getMeshCache()->addMesh(filename.c_str(), mesh);
892 errorstream<<"Client: Don't know how to load file \""
893 <<filename<<"\""<<std::endl;
897 // Virtual methods from con::PeerHandler
898 void Client::peerAdded(con::Peer *peer)
900 infostream<<"Client::peerAdded(): peer->id="
901 <<peer->id<<std::endl;
903 void Client::deletingPeer(con::Peer *peer, bool timeout)
905 infostream<<"Client::deletingPeer(): "
906 "Server Peer is getting deleted "
907 <<"(timeout="<<timeout<<")"<<std::endl;
910 void Client::ReceiveAll()
912 DSTACK(__FUNCTION_NAME);
913 u32 start_ms = porting::getTimeMs();
916 // Limit time even if there would be huge amounts of data to
918 if(porting::getTimeMs() > start_ms + 100)
923 g_profiler->graphAdd("client_received_packets", 1);
925 catch(con::NoIncomingDataException &e)
929 catch(con::InvalidIncomingDataException &e)
931 infostream<<"Client::ReceiveAll(): "
932 "InvalidIncomingDataException: what()="
933 <<e.what()<<std::endl;
938 void Client::Receive()
940 DSTACK(__FUNCTION_NAME);
941 SharedBuffer<u8> data;
945 //TimeTaker t1("con mutex and receive", m_device);
946 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
947 datasize = m_con.Receive(sender_peer_id, data);
949 //TimeTaker t1("ProcessData", m_device);
950 ProcessData(*data, datasize, sender_peer_id);
954 sender_peer_id given to this shall be quaranteed to be a valid peer
956 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
958 DSTACK(__FUNCTION_NAME);
960 // Ignore packets that don't even fit a command
963 m_packetcounter.add(60000);
967 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
969 //infostream<<"Client: received command="<<command<<std::endl;
970 m_packetcounter.add((u16)command);
973 If this check is removed, be sure to change the queue
974 system to know the ids
976 if(sender_peer_id != PEER_ID_SERVER)
978 infostream<<"Client::ProcessData(): Discarding data not "
979 "coming from server: peer_id="<<sender_peer_id
984 u8 ser_version = m_server_ser_ver;
986 //infostream<<"Client received command="<<(int)command<<std::endl;
988 if(command == TOCLIENT_INIT)
993 u8 deployed = data[2];
995 infostream<<"Client: TOCLIENT_INIT received with "
996 "deployed="<<((int)deployed&0xff)<<std::endl;
998 if(deployed < SER_FMT_VER_LOWEST
999 || deployed > SER_FMT_VER_HIGHEST)
1001 infostream<<"Client: TOCLIENT_INIT: Server sent "
1002 <<"unsupported ser_fmt_ver"<<std::endl;
1006 m_server_ser_ver = deployed;
1008 // Get player position
1009 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
1010 if(datasize >= 2+1+6)
1011 playerpos_s16 = readV3S16(&data[2+1]);
1012 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
1015 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1017 // Set player position
1018 Player *player = m_env.getLocalPlayer();
1019 assert(player != NULL);
1020 player->setPosition(playerpos_f);
1023 if(datasize >= 2+1+6+8)
1026 m_map_seed = readU64(&data[2+1+6]);
1027 infostream<<"Client: received map seed: "<<m_map_seed<<std::endl;
1030 if(datasize >= 2+1+6+8+4)
1033 m_recommended_send_interval = readF1000(&data[2+1+6+8]);
1034 infostream<<"Client: received recommended send interval "
1035 <<m_recommended_send_interval<<std::endl;
1040 SharedBuffer<u8> reply(replysize);
1041 writeU16(&reply[0], TOSERVER_INIT2);
1043 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1048 if(command == TOCLIENT_ACCESS_DENIED)
1050 // The server didn't like our password. Note, this needs
1051 // to be processed even if the serialisation format has
1052 // not been agreed yet, the same as TOCLIENT_INIT.
1053 m_access_denied = true;
1054 m_access_denied_reason = L"Unknown";
1057 std::string datastring((char*)&data[2], datasize-2);
1058 std::istringstream is(datastring, std::ios_base::binary);
1059 m_access_denied_reason = deSerializeWideString(is);
1064 if(ser_version == SER_FMT_VER_INVALID)
1066 infostream<<"Client: Server serialization"
1067 " format invalid or not initialized."
1068 " Skipping incoming command="<<command<<std::endl;
1072 // Just here to avoid putting the two if's together when
1073 // making some copypasta
1076 if(command == TOCLIENT_REMOVENODE)
1081 p.X = readS16(&data[2]);
1082 p.Y = readS16(&data[4]);
1083 p.Z = readS16(&data[6]);
1085 //TimeTaker t1("TOCLIENT_REMOVENODE");
1089 else if(command == TOCLIENT_ADDNODE)
1091 if(datasize < 8 + MapNode::serializedLength(ser_version))
1095 p.X = readS16(&data[2]);
1096 p.Y = readS16(&data[4]);
1097 p.Z = readS16(&data[6]);
1099 //TimeTaker t1("TOCLIENT_ADDNODE");
1102 n.deSerialize(&data[8], ser_version);
1106 else if(command == TOCLIENT_BLOCKDATA)
1108 // Ignore too small packet
1113 p.X = readS16(&data[2]);
1114 p.Y = readS16(&data[4]);
1115 p.Z = readS16(&data[6]);
1117 /*infostream<<"Client: Thread: BLOCKDATA for ("
1118 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1119 /*infostream<<"Client: Thread: BLOCKDATA for ("
1120 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1122 std::string datastring((char*)&data[8], datasize-8);
1123 std::istringstream istr(datastring, std::ios_base::binary);
1128 v2s16 p2d(p.X, p.Z);
1129 sector = m_env.getMap().emergeSector(p2d);
1131 assert(sector->getPos() == p2d);
1133 //TimeTaker timer("MapBlock deSerialize");
1136 block = sector->getBlockNoCreateNoEx(p.Y);
1140 Update an existing block
1142 //infostream<<"Updating"<<std::endl;
1143 block->deSerialize(istr, ser_version, false);
1150 //infostream<<"Creating new"<<std::endl;
1151 block = new MapBlock(&m_env.getMap(), p, this);
1152 block->deSerialize(istr, ser_version, false);
1153 sector->insertBlock(block);
1167 u32 replysize = 2+1+6;
1168 SharedBuffer<u8> reply(replysize);
1169 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
1171 writeV3S16(&reply[3], p);
1173 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1177 Add it to mesh update queue and set it to be acknowledged after update.
1179 //infostream<<"Adding mesh update task for received block"<<std::endl;
1180 addUpdateMeshTaskWithEdge(p, true);
1182 else if(command == TOCLIENT_INVENTORY)
1187 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
1190 //TimeTaker t2("mutex locking", m_device);
1191 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1194 //TimeTaker t3("istringstream init", m_device);
1195 std::string datastring((char*)&data[2], datasize-2);
1196 std::istringstream is(datastring, std::ios_base::binary);
1199 //m_env.printPlayers(infostream);
1201 //TimeTaker t4("player get", m_device);
1202 Player *player = m_env.getLocalPlayer();
1203 assert(player != NULL);
1206 //TimeTaker t1("inventory.deSerialize()", m_device);
1207 player->inventory.deSerialize(is);
1210 m_inventory_updated = true;
1212 delete m_inventory_from_server;
1213 m_inventory_from_server = new Inventory(player->inventory);
1214 m_inventory_from_server_age = 0.0;
1216 //infostream<<"Client got player inventory:"<<std::endl;
1217 //player->inventory.print(infostream);
1220 else if(command == TOCLIENT_TIME_OF_DAY)
1225 u16 time_of_day = readU16(&data[2]);
1226 time_of_day = time_of_day % 24000;
1227 //infostream<<"Client: time_of_day="<<time_of_day<<std::endl;
1228 float time_speed = 0;
1229 if(datasize >= 2 + 2 + 4){
1230 time_speed = readF1000(&data[4]);
1232 // Old message; try to approximate speed of time by ourselves
1233 float time_of_day_f = (float)time_of_day / 24000.0;
1234 float tod_diff_f = 0;
1235 if(time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
1236 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0;
1238 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
1239 m_last_time_of_day_f = time_of_day_f;
1240 float time_diff = m_time_of_day_update_timer;
1241 m_time_of_day_update_timer = 0;
1242 if(m_time_of_day_set){
1243 time_speed = 3600.0*24.0 * tod_diff_f / time_diff;
1244 infostream<<"Client: Measured time_of_day speed (old format): "
1245 <<time_speed<<" tod_diff_f="<<tod_diff_f
1246 <<" time_diff="<<time_diff<<std::endl;
1250 // Update environment
1251 m_env.setTimeOfDay(time_of_day);
1252 m_env.setTimeOfDaySpeed(time_speed);
1253 m_time_of_day_set = true;
1255 u32 dr = m_env.getDayNightRatio();
1256 verbosestream<<"Client: time_of_day="<<time_of_day
1257 <<" time_speed="<<time_speed
1258 <<" dr="<<dr<<std::endl;
1260 else if(command == TOCLIENT_CHAT_MESSAGE)
1268 std::string datastring((char*)&data[2], datasize-2);
1269 std::istringstream is(datastring, std::ios_base::binary);
1272 is.read((char*)buf, 2);
1273 u16 len = readU16(buf);
1275 std::wstring message;
1276 for(u16 i=0; i<len; i++)
1278 is.read((char*)buf, 2);
1279 message += (wchar_t)readU16(buf);
1282 /*infostream<<"Client received chat message: "
1283 <<wide_to_narrow(message)<<std::endl;*/
1285 m_chat_queue.push_back(message);
1287 else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1289 //if(g_settings->getBool("enable_experimental"))
1293 u16 count of removed objects
1294 for all removed objects {
1297 u16 count of added objects
1298 for all added objects {
1301 u32 initialization data length
1302 string initialization data
1307 // Get all data except the command number
1308 std::string datastring((char*)&data[2], datasize-2);
1309 // Throw them in an istringstream
1310 std::istringstream is(datastring, std::ios_base::binary);
1314 // Read removed objects
1316 u16 removed_count = readU16((u8*)buf);
1317 for(u16 i=0; i<removed_count; i++)
1320 u16 id = readU16((u8*)buf);
1323 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1324 m_env.removeActiveObject(id);
1328 // Read added objects
1330 u16 added_count = readU16((u8*)buf);
1331 for(u16 i=0; i<added_count; i++)
1334 u16 id = readU16((u8*)buf);
1336 u8 type = readU8((u8*)buf);
1337 std::string data = deSerializeLongString(is);
1340 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1341 m_env.addActiveObject(id, type, data);
1346 else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1348 //if(g_settings->getBool("enable_experimental"))
1360 // Get all data except the command number
1361 std::string datastring((char*)&data[2], datasize-2);
1362 // Throw them in an istringstream
1363 std::istringstream is(datastring, std::ios_base::binary);
1365 while(is.eof() == false)
1369 u16 id = readU16((u8*)buf);
1373 u16 message_size = readU16((u8*)buf);
1374 std::string message;
1375 message.reserve(message_size);
1376 for(u16 i=0; i<message_size; i++)
1379 message.append(buf, 1);
1381 // Pass on to the environment
1383 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1384 m_env.processActiveObjectMessage(id, message);
1389 else if(command == TOCLIENT_HP)
1391 std::string datastring((char*)&data[2], datasize-2);
1392 std::istringstream is(datastring, std::ios_base::binary);
1393 Player *player = m_env.getLocalPlayer();
1394 assert(player != NULL);
1395 u8 oldhp = player->hp;
1401 // Add to ClientEvent queue
1403 event.type = CE_PLAYER_DAMAGE;
1404 event.player_damage.amount = oldhp - hp;
1405 m_client_event_queue.push_back(event);
1408 else if(command == TOCLIENT_MOVE_PLAYER)
1410 std::string datastring((char*)&data[2], datasize-2);
1411 std::istringstream is(datastring, std::ios_base::binary);
1412 Player *player = m_env.getLocalPlayer();
1413 assert(player != NULL);
1414 v3f pos = readV3F1000(is);
1415 f32 pitch = readF1000(is);
1416 f32 yaw = readF1000(is);
1417 player->setPosition(pos);
1418 /*player->setPitch(pitch);
1419 player->setYaw(yaw);*/
1421 infostream<<"Client got TOCLIENT_MOVE_PLAYER"
1422 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1428 Add to ClientEvent queue.
1429 This has to be sent to the main program because otherwise
1430 it would just force the pitch and yaw values to whatever
1431 the camera points to.
1434 event.type = CE_PLAYER_FORCE_MOVE;
1435 event.player_force_move.pitch = pitch;
1436 event.player_force_move.yaw = yaw;
1437 m_client_event_queue.push_back(event);
1439 // Ignore damage for a few seconds, so that the player doesn't
1440 // get damage from falling on ground
1441 m_ignore_damage_timer = 3.0;
1443 else if(command == TOCLIENT_PLAYERITEM)
1445 infostream<<"Client: WARNING: Ignoring TOCLIENT_PLAYERITEM"<<std::endl;
1447 else if(command == TOCLIENT_DEATHSCREEN)
1449 std::string datastring((char*)&data[2], datasize-2);
1450 std::istringstream is(datastring, std::ios_base::binary);
1452 bool set_camera_point_target = readU8(is);
1453 v3f camera_point_target = readV3F1000(is);
1456 event.type = CE_DEATHSCREEN;
1457 event.deathscreen.set_camera_point_target = set_camera_point_target;
1458 event.deathscreen.camera_point_target_x = camera_point_target.X;
1459 event.deathscreen.camera_point_target_y = camera_point_target.Y;
1460 event.deathscreen.camera_point_target_z = camera_point_target.Z;
1461 m_client_event_queue.push_back(event);
1463 else if(command == TOCLIENT_ANNOUNCE_MEDIA)
1465 std::string datastring((char*)&data[2], datasize-2);
1466 std::istringstream is(datastring, std::ios_base::binary);
1468 // Mesh update thread must be stopped while
1469 // updating content definitions
1470 assert(!m_mesh_update_thread.IsRunning());
1472 int num_files = readU16(is);
1474 infostream<<"Client: Received media announcement: packet size: "
1475 <<datasize<<std::endl;
1477 core::list<MediaRequest> file_requests;
1479 for(int i=0; i<num_files; i++)
1481 //read file from cache
1482 std::string name = deSerializeString(is);
1483 std::string sha1_base64 = deSerializeString(is);
1485 // if name contains illegal characters, ignore the file
1486 if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
1487 errorstream<<"Client: ignoring illegal file name "
1488 <<"sent by server: \""<<name<<"\""<<std::endl;
1492 std::string sha1_raw = base64_decode(sha1_base64);
1493 std::string sha1_hex = hex_encode(sha1_raw);
1494 std::ostringstream tmp_os(std::ios_base::binary);
1495 bool found_in_cache = m_media_cache.load_sha1(sha1_raw, tmp_os);
1496 m_media_name_sha1_map.set(name, sha1_raw);
1498 // If found in cache, try to load it from there
1501 bool success = loadMedia(tmp_os.str(), name);
1503 verbosestream<<"Client: Loaded cached media: "
1504 <<sha1_hex<<" \""<<name<<"\""<<std::endl;
1507 infostream<<"Client: Failed to load cached media: "
1508 <<sha1_hex<<" \""<<name<<"\""<<std::endl;
1511 // Didn't load from cache; queue it to be requested
1512 verbosestream<<"Client: Adding file to request list: \""
1513 <<sha1_hex<<" \""<<name<<"\""<<std::endl;
1514 file_requests.push_back(MediaRequest(name));
1518 event.type = CE_TEXTURES_UPDATED;
1519 m_client_event_queue.push_back(event);
1523 u16 number of files requested
1529 std::ostringstream os(std::ios_base::binary);
1530 writeU16(os, TOSERVER_REQUEST_MEDIA);
1531 writeU16(os, file_requests.size());
1533 for(core::list<MediaRequest>::Iterator i = file_requests.begin();
1534 i != file_requests.end(); i++) {
1535 os<<serializeString(i->name);
1539 std::string s = os.str();
1540 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1542 Send(0, data, true);
1543 infostream<<"Client: Sending media request list to server ("
1544 <<file_requests.size()<<" files)"<<std::endl;
1546 else if(command == TOCLIENT_MEDIA)
1548 std::string datastring((char*)&data[2], datasize-2);
1549 std::istringstream is(datastring, std::ios_base::binary);
1551 // Mesh update thread must be stopped while
1552 // updating content definitions
1553 assert(!m_mesh_update_thread.IsRunning());
1557 u16 total number of file bunches
1558 u16 index of this bunch
1559 u32 number of files in this bunch
1567 int num_bunches = readU16(is);
1568 int bunch_i = readU16(is);
1569 if(num_bunches >= 2)
1570 m_media_receive_progress = (float)bunch_i / (float)(num_bunches - 1);
1572 m_media_receive_progress = 1.0;
1573 if(bunch_i == num_bunches - 1)
1574 m_media_received = true;
1575 int num_files = readU32(is);
1576 infostream<<"Client: Received files: bunch "<<bunch_i<<"/"
1577 <<num_bunches<<" files="<<num_files
1578 <<" size="<<datasize<<std::endl;
1579 for(int i=0; i<num_files; i++){
1580 std::string name = deSerializeString(is);
1581 std::string data = deSerializeLongString(is);
1583 // if name contains illegal characters, ignore the file
1584 if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
1585 errorstream<<"Client: ignoring illegal file name "
1586 <<"sent by server: \""<<name<<"\""<<std::endl;
1590 bool success = loadMedia(data, name);
1592 verbosestream<<"Client: Loaded received media: "
1593 <<"\""<<name<<"\". Caching."<<std::endl;
1595 infostream<<"Client: Failed to load received media: "
1596 <<"\""<<name<<"\". Not caching."<<std::endl;
1600 bool did = fs::CreateAllDirs(getMediaCacheDir());
1602 errorstream<<"Could not create media cache directory"
1607 core::map<std::string, std::string>::Node *n;
1608 n = m_media_name_sha1_map.find(name);
1610 errorstream<<"The server sent a file that has not "
1611 <<"been announced."<<std::endl;
1613 m_media_cache.update_sha1(data);
1618 event.type = CE_TEXTURES_UPDATED;
1619 m_client_event_queue.push_back(event);
1621 else if(command == TOCLIENT_TOOLDEF)
1623 infostream<<"Client: WARNING: Ignoring TOCLIENT_TOOLDEF"<<std::endl;
1625 else if(command == TOCLIENT_NODEDEF)
1627 infostream<<"Client: Received node definitions: packet size: "
1628 <<datasize<<std::endl;
1630 // Mesh update thread must be stopped while
1631 // updating content definitions
1632 assert(!m_mesh_update_thread.IsRunning());
1634 // Decompress node definitions
1635 std::string datastring((char*)&data[2], datasize-2);
1636 std::istringstream is(datastring, std::ios_base::binary);
1637 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1638 std::ostringstream tmp_os;
1639 decompressZlib(tmp_is, tmp_os);
1641 // Deserialize node definitions
1642 std::istringstream tmp_is2(tmp_os.str());
1643 m_nodedef->deSerialize(tmp_is2);
1644 m_nodedef_received = true;
1646 else if(command == TOCLIENT_CRAFTITEMDEF)
1648 infostream<<"Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF"<<std::endl;
1650 else if(command == TOCLIENT_ITEMDEF)
1652 infostream<<"Client: Received item definitions: packet size: "
1653 <<datasize<<std::endl;
1655 // Mesh update thread must be stopped while
1656 // updating content definitions
1657 assert(!m_mesh_update_thread.IsRunning());
1659 // Decompress item definitions
1660 std::string datastring((char*)&data[2], datasize-2);
1661 std::istringstream is(datastring, std::ios_base::binary);
1662 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1663 std::ostringstream tmp_os;
1664 decompressZlib(tmp_is, tmp_os);
1666 // Deserialize node definitions
1667 std::istringstream tmp_is2(tmp_os.str());
1668 m_itemdef->deSerialize(tmp_is2);
1669 m_itemdef_received = true;
1671 else if(command == TOCLIENT_PLAY_SOUND)
1673 std::string datastring((char*)&data[2], datasize-2);
1674 std::istringstream is(datastring, std::ios_base::binary);
1676 s32 server_id = readS32(is);
1677 std::string name = deSerializeString(is);
1678 float gain = readF1000(is);
1679 int type = readU8(is); // 0=local, 1=positional, 2=object
1680 v3f pos = readV3F1000(is);
1681 u16 object_id = readU16(is);
1682 bool loop = readU8(is);
1687 client_id = m_sound->playSound(name, loop, gain);
1689 case 1: // positional
1690 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1693 ClientActiveObject *cao = m_env.getActiveObject(object_id);
1695 pos = cao->getPosition();
1696 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1697 // TODO: Set up sound to move with object
1702 if(client_id != -1){
1703 m_sounds_server_to_client[server_id] = client_id;
1704 m_sounds_client_to_server[client_id] = server_id;
1706 m_sounds_to_objects[client_id] = object_id;
1709 else if(command == TOCLIENT_STOP_SOUND)
1711 std::string datastring((char*)&data[2], datasize-2);
1712 std::istringstream is(datastring, std::ios_base::binary);
1714 s32 server_id = readS32(is);
1715 std::map<s32, int>::iterator i =
1716 m_sounds_server_to_client.find(server_id);
1717 if(i != m_sounds_server_to_client.end()){
1718 int client_id = i->second;
1719 m_sound->stopSound(client_id);
1722 else if(command == TOCLIENT_PRIVILEGES)
1724 std::string datastring((char*)&data[2], datasize-2);
1725 std::istringstream is(datastring, std::ios_base::binary);
1727 m_privileges.clear();
1728 infostream<<"Client: Privileges updated: ";
1729 u16 num_privileges = readU16(is);
1730 for(u16 i=0; i<num_privileges; i++){
1731 std::string priv = deSerializeString(is);
1732 m_privileges.insert(priv);
1733 infostream<<priv<<" ";
1735 infostream<<std::endl;
1737 else if(command == TOCLIENT_INVENTORY_FORMSPEC)
1739 std::string datastring((char*)&data[2], datasize-2);
1740 std::istringstream is(datastring, std::ios_base::binary);
1742 // Store formspec in LocalPlayer
1743 Player *player = m_env.getLocalPlayer();
1744 assert(player != NULL);
1745 player->inventory_formspec = deSerializeLongString(is);
1747 else if(command == TOCLIENT_DETACHED_INVENTORY)
1749 std::string datastring((char*)&data[2], datasize-2);
1750 std::istringstream is(datastring, std::ios_base::binary);
1752 std::string name = deSerializeString(is);
1754 infostream<<"Client: Detached inventory update: \""<<name<<"\""<<std::endl;
1756 Inventory *inv = NULL;
1757 if(m_detached_inventories.count(name) > 0)
1758 inv = m_detached_inventories[name];
1760 inv = new Inventory(m_itemdef);
1761 m_detached_inventories[name] = inv;
1763 inv->deSerialize(is);
1767 infostream<<"Client: Ignoring unknown command "
1768 <<command<<std::endl;
1772 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
1774 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1775 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
1778 void Client::interact(u8 action, const PointedThing& pointed)
1780 if(connectedAndInitialized() == false){
1781 infostream<<"Client::interact() "
1782 "cancelled (not connected)"
1787 std::ostringstream os(std::ios_base::binary);
1793 [5] u32 length of the next item
1794 [9] serialized PointedThing
1796 0: start digging (from undersurface) or use
1797 1: stop digging (all parameters ignored)
1798 2: digging completed
1799 3: place block or item (to abovesurface)
1802 writeU16(os, TOSERVER_INTERACT);
1803 writeU8(os, action);
1804 writeU16(os, getPlayerItem());
1805 std::ostringstream tmp_os(std::ios::binary);
1806 pointed.serialize(tmp_os);
1807 os<<serializeLongString(tmp_os.str());
1809 std::string s = os.str();
1810 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1813 Send(0, data, true);
1816 void Client::sendNodemetaFields(v3s16 p, const std::string &formname,
1817 const std::map<std::string, std::string> &fields)
1819 std::ostringstream os(std::ios_base::binary);
1821 writeU16(os, TOSERVER_NODEMETA_FIELDS);
1823 os<<serializeString(formname);
1824 writeU16(os, fields.size());
1825 for(std::map<std::string, std::string>::const_iterator
1826 i = fields.begin(); i != fields.end(); i++){
1827 const std::string &name = i->first;
1828 const std::string &value = i->second;
1829 os<<serializeString(name);
1830 os<<serializeLongString(value);
1834 std::string s = os.str();
1835 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1837 Send(0, data, true);
1840 void Client::sendInventoryFields(const std::string &formname,
1841 const std::map<std::string, std::string> &fields)
1843 std::ostringstream os(std::ios_base::binary);
1845 writeU16(os, TOSERVER_INVENTORY_FIELDS);
1846 os<<serializeString(formname);
1847 writeU16(os, fields.size());
1848 for(std::map<std::string, std::string>::const_iterator
1849 i = fields.begin(); i != fields.end(); i++){
1850 const std::string &name = i->first;
1851 const std::string &value = i->second;
1852 os<<serializeString(name);
1853 os<<serializeLongString(value);
1857 std::string s = os.str();
1858 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1860 Send(0, data, true);
1863 void Client::sendInventoryAction(InventoryAction *a)
1865 std::ostringstream os(std::ios_base::binary);
1869 writeU16(buf, TOSERVER_INVENTORY_ACTION);
1870 os.write((char*)buf, 2);
1875 std::string s = os.str();
1876 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1878 Send(0, data, true);
1881 void Client::sendChatMessage(const std::wstring &message)
1883 std::ostringstream os(std::ios_base::binary);
1887 writeU16(buf, TOSERVER_CHAT_MESSAGE);
1888 os.write((char*)buf, 2);
1891 writeU16(buf, message.size());
1892 os.write((char*)buf, 2);
1895 for(u32 i=0; i<message.size(); i++)
1899 os.write((char*)buf, 2);
1903 std::string s = os.str();
1904 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1906 Send(0, data, true);
1909 void Client::sendChangePassword(const std::wstring oldpassword,
1910 const std::wstring newpassword)
1912 Player *player = m_env.getLocalPlayer();
1916 std::string playername = player->getName();
1917 std::string oldpwd = translatePassword(playername, oldpassword);
1918 std::string newpwd = translatePassword(playername, newpassword);
1920 std::ostringstream os(std::ios_base::binary);
1921 u8 buf[2+PASSWORD_SIZE*2];
1923 [0] u16 TOSERVER_PASSWORD
1924 [2] u8[28] old password
1925 [30] u8[28] new password
1928 writeU16(buf, TOSERVER_PASSWORD);
1929 for(u32 i=0;i<PASSWORD_SIZE-1;i++)
1931 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
1932 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
1934 buf[2+PASSWORD_SIZE-1] = 0;
1935 buf[30+PASSWORD_SIZE-1] = 0;
1936 os.write((char*)buf, 2+PASSWORD_SIZE*2);
1939 std::string s = os.str();
1940 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1942 Send(0, data, true);
1946 void Client::sendDamage(u8 damage)
1948 DSTACK(__FUNCTION_NAME);
1949 std::ostringstream os(std::ios_base::binary);
1951 writeU16(os, TOSERVER_DAMAGE);
1952 writeU8(os, damage);
1955 std::string s = os.str();
1956 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1958 Send(0, data, true);
1961 void Client::sendRespawn()
1963 DSTACK(__FUNCTION_NAME);
1964 std::ostringstream os(std::ios_base::binary);
1966 writeU16(os, TOSERVER_RESPAWN);
1969 std::string s = os.str();
1970 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1972 Send(0, data, true);
1975 void Client::sendPlayerPos()
1977 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1979 Player *myplayer = m_env.getLocalPlayer();
1980 if(myplayer == NULL)
1985 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1986 our_peer_id = m_con.GetPeerID();
1989 // Set peer id if not set already
1990 if(myplayer->peer_id == PEER_ID_INEXISTENT)
1991 myplayer->peer_id = our_peer_id;
1992 // Check that an existing peer_id is the same as the connection's
1993 assert(myplayer->peer_id == our_peer_id);
1995 v3f pf = myplayer->getPosition();
1996 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
1997 v3f sf = myplayer->getSpeed();
1998 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
1999 s32 pitch = myplayer->getPitch() * 100;
2000 s32 yaw = myplayer->getYaw() * 100;
2001 u32 keyPressed=myplayer->keyPressed;
2005 [2] v3s32 position*100
2006 [2+12] v3s32 speed*100
2007 [2+12+12] s32 pitch*100
2008 [2+12+12+4] s32 yaw*100
2009 [2+12+12+4+4] u32 keyPressed
2011 SharedBuffer<u8> data(2+12+12+4+4+4);
2012 writeU16(&data[0], TOSERVER_PLAYERPOS);
2013 writeV3S32(&data[2], position);
2014 writeV3S32(&data[2+12], speed);
2015 writeS32(&data[2+12+12], pitch);
2016 writeS32(&data[2+12+12+4], yaw);
2017 writeU32(&data[2+12+12+4+4], keyPressed);
2018 // Send as unreliable
2019 Send(0, data, false);
2022 void Client::sendPlayerItem(u16 item)
2024 Player *myplayer = m_env.getLocalPlayer();
2025 if(myplayer == NULL)
2028 u16 our_peer_id = m_con.GetPeerID();
2030 // Set peer id if not set already
2031 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2032 myplayer->peer_id = our_peer_id;
2033 // Check that an existing peer_id is the same as the connection's
2034 assert(myplayer->peer_id == our_peer_id);
2036 SharedBuffer<u8> data(2+2);
2037 writeU16(&data[0], TOSERVER_PLAYERITEM);
2038 writeU16(&data[2], item);
2041 Send(0, data, true);
2044 void Client::removeNode(v3s16 p)
2046 core::map<v3s16, MapBlock*> modified_blocks;
2050 //TimeTaker t("removeNodeAndUpdate", m_device);
2051 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
2053 catch(InvalidPositionException &e)
2057 // add urgent task to update the modified node
2058 addUpdateMeshTaskForNode(p, false, true);
2060 for(core::map<v3s16, MapBlock * >::Iterator
2061 i = modified_blocks.getIterator();
2062 i.atEnd() == false; i++)
2064 v3s16 p = i.getNode()->getKey();
2065 addUpdateMeshTaskWithEdge(p);
2069 void Client::addNode(v3s16 p, MapNode n)
2071 TimeTaker timer1("Client::addNode()");
2073 core::map<v3s16, MapBlock*> modified_blocks;
2077 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
2078 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks);
2080 catch(InvalidPositionException &e)
2083 for(core::map<v3s16, MapBlock * >::Iterator
2084 i = modified_blocks.getIterator();
2085 i.atEnd() == false; i++)
2087 v3s16 p = i.getNode()->getKey();
2088 addUpdateMeshTaskWithEdge(p);
2092 void Client::setPlayerControl(PlayerControl &control)
2094 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2095 LocalPlayer *player = m_env.getLocalPlayer();
2096 assert(player != NULL);
2097 player->control = control;
2100 void Client::selectPlayerItem(u16 item)
2102 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2103 m_playeritem = item;
2104 m_inventory_updated = true;
2105 sendPlayerItem(item);
2108 // Returns true if the inventory of the local player has been
2109 // updated from the server. If it is true, it is set to false.
2110 bool Client::getLocalInventoryUpdated()
2112 // m_inventory_updated is behind envlock
2113 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2114 bool updated = m_inventory_updated;
2115 m_inventory_updated = false;
2119 // Copies the inventory of the local player to parameter
2120 void Client::getLocalInventory(Inventory &dst)
2122 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2123 Player *player = m_env.getLocalPlayer();
2124 assert(player != NULL);
2125 dst = player->inventory;
2128 Inventory* Client::getInventory(const InventoryLocation &loc)
2131 case InventoryLocation::UNDEFINED:
2134 case InventoryLocation::CURRENT_PLAYER:
2136 Player *player = m_env.getLocalPlayer();
2137 assert(player != NULL);
2138 return &player->inventory;
2141 case InventoryLocation::PLAYER:
2143 Player *player = m_env.getPlayer(loc.name.c_str());
2146 return &player->inventory;
2149 case InventoryLocation::NODEMETA:
2151 NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p);
2154 return meta->getInventory();
2157 case InventoryLocation::DETACHED:
2159 if(m_detached_inventories.count(loc.name) == 0)
2161 return m_detached_inventories[loc.name];
2169 void Client::inventoryAction(InventoryAction *a)
2172 Send it to the server
2174 sendInventoryAction(a);
2177 Predict some local inventory changes
2179 a->clientApply(this, this);
2182 ClientActiveObject * Client::getSelectedActiveObject(
2184 v3f from_pos_f_on_map,
2185 core::line3d<f32> shootline_on_map
2188 core::array<DistanceSortedActiveObject> objects;
2190 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
2192 //infostream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
2195 // After this, the closest object is the first in the array.
2198 for(u32 i=0; i<objects.size(); i++)
2200 ClientActiveObject *obj = objects[i].obj;
2202 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
2203 if(selection_box == NULL)
2206 v3f pos = obj->getPosition();
2208 core::aabbox3d<f32> offsetted_box(
2209 selection_box->MinEdge + pos,
2210 selection_box->MaxEdge + pos
2213 if(offsetted_box.intersectsWithLine(shootline_on_map))
2215 //infostream<<"Returning selected object"<<std::endl;
2220 //infostream<<"No object selected; returning NULL."<<std::endl;
2224 void Client::printDebugInfo(std::ostream &os)
2226 //JMutexAutoLock lock1(m_fetchblock_mutex);
2227 /*JMutexAutoLock lock2(m_incoming_queue_mutex);
2229 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
2230 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
2231 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
2235 core::list<std::wstring> Client::getConnectedPlayerNames()
2237 core::list<Player*> players = m_env.getPlayers(true);
2238 core::list<std::wstring> playerNames;
2239 for(core::list<Player*>::Iterator
2240 i = players.begin();
2241 i != players.end(); i++)
2243 Player *player = *i;
2244 playerNames.push_back(narrow_to_wide(player->getName()));
2249 float Client::getAnimationTime()
2251 return m_animation_time;
2254 int Client::getCrackLevel()
2256 return m_crack_level;
2259 void Client::setCrack(int level, v3s16 pos)
2261 int old_crack_level = m_crack_level;
2262 v3s16 old_crack_pos = m_crack_pos;
2264 m_crack_level = level;
2267 if(old_crack_level >= 0 && (level < 0 || pos != old_crack_pos))
2270 addUpdateMeshTaskForNode(old_crack_pos, false, true);
2272 if(level >= 0 && (old_crack_level < 0 || pos != old_crack_pos))
2275 addUpdateMeshTaskForNode(pos, false, true);
2281 Player *player = m_env.getLocalPlayer();
2282 assert(player != NULL);
2286 bool Client::getChatMessage(std::wstring &message)
2288 if(m_chat_queue.size() == 0)
2290 message = m_chat_queue.pop_front();
2294 void Client::typeChatMessage(const std::wstring &message)
2296 // Discard empty line
2301 sendChatMessage(message);
2304 if (message[0] == L'/')
2306 m_chat_queue.push_back(
2307 (std::wstring)L"issued command: "+message);
2311 LocalPlayer *player = m_env.getLocalPlayer();
2312 assert(player != NULL);
2313 std::wstring name = narrow_to_wide(player->getName());
2314 m_chat_queue.push_back(
2315 (std::wstring)L"<"+name+L"> "+message);
2319 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
2321 /*infostream<<"Client::addUpdateMeshTask(): "
2322 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2323 <<" ack_to_server="<<ack_to_server
2324 <<" urgent="<<urgent
2327 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2332 Create a task to update the mesh of the block
2335 MeshMakeData *data = new MeshMakeData(this);
2338 //TimeTaker timer("data fill");
2340 // Debug: 1-6ms, avg=2ms
2342 data->setCrack(m_crack_level, m_crack_pos);
2343 data->setSmoothLighting(g_settings->getBool("smooth_lighting"));
2347 //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
2349 // Add task to queue
2350 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server, urgent);
2352 /*infostream<<"Mesh update input queue size is "
2353 <<m_mesh_update_thread.m_queue_in.size()
2357 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent)
2361 infostream<<"Client::addUpdateMeshTaskWithEdge(): "
2362 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2367 v3s16 p = blockpos + v3s16(0,0,0);
2368 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2369 addUpdateMeshTask(p, ack_to_server, urgent);
2371 catch(InvalidPositionException &e){}
2374 v3s16 p = blockpos + v3s16(-1,0,0);
2375 addUpdateMeshTask(p, false, urgent);
2377 catch(InvalidPositionException &e){}
2379 v3s16 p = blockpos + v3s16(0,-1,0);
2380 addUpdateMeshTask(p, false, urgent);
2382 catch(InvalidPositionException &e){}
2384 v3s16 p = blockpos + v3s16(0,0,-1);
2385 addUpdateMeshTask(p, false, urgent);
2387 catch(InvalidPositionException &e){}
2390 void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool urgent)
2394 infostream<<"Client::addUpdateMeshTaskForNode(): "
2395 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2399 v3s16 blockpos = getNodeBlockPos(nodepos);
2400 v3s16 blockpos_relative = blockpos * MAP_BLOCKSIZE;
2403 v3s16 p = blockpos + v3s16(0,0,0);
2404 addUpdateMeshTask(p, ack_to_server, urgent);
2406 catch(InvalidPositionException &e){}
2408 if(nodepos.X == blockpos_relative.X){
2410 v3s16 p = blockpos + v3s16(-1,0,0);
2411 addUpdateMeshTask(p, false, urgent);
2413 catch(InvalidPositionException &e){}
2415 if(nodepos.Y == blockpos_relative.Y){
2417 v3s16 p = blockpos + v3s16(0,-1,0);
2418 addUpdateMeshTask(p, false, urgent);
2420 catch(InvalidPositionException &e){}
2422 if(nodepos.Z == blockpos_relative.Z){
2424 v3s16 p = blockpos + v3s16(0,0,-1);
2425 addUpdateMeshTask(p, false, urgent);
2427 catch(InvalidPositionException &e){}
2431 ClientEvent Client::getClientEvent()
2433 if(m_client_event_queue.size() == 0)
2436 event.type = CE_NONE;
2439 return m_client_event_queue.pop_front();
2442 void Client::afterContentReceived()
2444 infostream<<"Client::afterContentReceived() started"<<std::endl;
2445 assert(m_itemdef_received);
2446 assert(m_nodedef_received);
2447 assert(m_media_received);
2449 // remove the information about which checksum each texture
2451 m_media_name_sha1_map.clear();
2453 // Rebuild inherited images and recreate textures
2454 infostream<<"- Rebuilding images and textures"<<std::endl;
2455 m_tsrc->rebuildImagesAndTextures();
2457 // Update texture atlas
2458 infostream<<"- Updating texture atlas"<<std::endl;
2459 if(g_settings->getBool("enable_texture_atlas"))
2460 m_tsrc->buildMainAtlas(this);
2463 m_shsrc->rebuildShaders();
2465 // Update node aliases
2466 infostream<<"- Updating node aliases"<<std::endl;
2467 m_nodedef->updateAliases(m_itemdef);
2469 // Update node textures
2470 infostream<<"- Updating node textures"<<std::endl;
2471 m_nodedef->updateTextures(m_tsrc);
2473 // Preload item textures and meshes if configured to
2474 if(g_settings->getBool("preload_item_visuals"))
2476 verbosestream<<"Updating item textures and meshes"<<std::endl;
2477 std::set<std::string> names = m_itemdef->getAll();
2478 for(std::set<std::string>::const_iterator
2479 i = names.begin(); i != names.end(); ++i){
2480 // Asking for these caches the result
2481 m_itemdef->getInventoryTexture(*i, this);
2482 m_itemdef->getWieldMesh(*i, this);
2486 // Start mesh update thread after setting up content definitions
2487 infostream<<"- Starting mesh update thread"<<std::endl;
2488 m_mesh_update_thread.Start();
2490 infostream<<"Client::afterContentReceived() done"<<std::endl;
2493 float Client::getRTT(void)
2496 return m_con.GetPeerAvgRTT(PEER_ID_SERVER);
2497 } catch(con::PeerNotFoundException &e){
2502 // IGameDef interface
2504 IItemDefManager* Client::getItemDefManager()
2508 INodeDefManager* Client::getNodeDefManager()
2512 ICraftDefManager* Client::getCraftDefManager()
2515 //return m_craftdef;
2517 ITextureSource* Client::getTextureSource()
2521 IShaderSource* Client::getShaderSource()
2525 u16 Client::allocateUnknownNodeId(const std::string &name)
2527 errorstream<<"Client::allocateUnknownNodeId(): "
2528 <<"Client cannot allocate node IDs"<<std::endl;
2530 return CONTENT_IGNORE;
2532 ISoundManager* Client::getSoundManager()
2536 MtEventManager* Client::getEventManager()