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"
50 #include <curl/curl.h>
53 static std::string getMediaCacheDir()
55 return porting::path_user + DIR_DELIM + "cache" + DIR_DELIM + "media";
62 QueuedMeshUpdate::QueuedMeshUpdate():
65 ack_block_to_server(false)
69 QueuedMeshUpdate::~QueuedMeshUpdate()
79 MeshUpdateQueue::MeshUpdateQueue()
84 MeshUpdateQueue::~MeshUpdateQueue()
86 JMutexAutoLock lock(m_mutex);
88 for(std::vector<QueuedMeshUpdate*>::iterator
90 i != m_queue.end(); i++)
92 QueuedMeshUpdate *q = *i;
98 peer_id=0 adds with nobody to send to
100 void MeshUpdateQueue::addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_server, bool urgent)
102 DSTACK(__FUNCTION_NAME);
106 JMutexAutoLock lock(m_mutex);
112 Find if block is already in queue.
113 If it is, update the data and quit.
115 for(std::vector<QueuedMeshUpdate*>::iterator
117 i != m_queue.end(); i++)
119 QueuedMeshUpdate *q = *i;
125 if(ack_block_to_server)
126 q->ack_block_to_server = true;
134 QueuedMeshUpdate *q = new QueuedMeshUpdate;
137 q->ack_block_to_server = ack_block_to_server;
138 m_queue.push_back(q);
141 // Returned pointer must be deleted
142 // Returns NULL if queue is empty
143 QueuedMeshUpdate * MeshUpdateQueue::pop()
145 JMutexAutoLock lock(m_mutex);
147 bool must_be_urgent = !m_urgents.empty();
148 for(std::vector<QueuedMeshUpdate*>::iterator
150 i != m_queue.end(); i++)
152 QueuedMeshUpdate *q = *i;
153 if(must_be_urgent && m_urgents.count(q->p) == 0)
156 m_urgents.erase(q->p);
166 void * MeshUpdateThread::Thread()
170 log_register_thread("MeshUpdateThread");
172 DSTACK(__FUNCTION_NAME);
174 BEGIN_DEBUG_EXCEPTION_HANDLER
178 /*// Wait for output queue to flush.
179 // Allow 2 in queue, this makes less frametime jitter.
180 // Umm actually, there is no much difference
181 if(m_queue_out.size() >= 2)
187 QueuedMeshUpdate *q = m_queue_in.pop();
194 ScopeProfiler sp(g_profiler, "Client: Mesh making");
196 MapBlockMesh *mesh_new = new MapBlockMesh(q->data);
197 if(mesh_new->getMesh()->getMeshBufferCount() == 0)
206 r.ack_block_to_server = q->ack_block_to_server;
208 /*infostream<<"MeshUpdateThread: Processed "
209 <<"("<<q->p.X<<","<<q->p.Y<<","<<q->p.Z<<")"
212 m_queue_out.push_back(r);
217 END_DEBUG_EXCEPTION_HANDLER(errorstream)
222 void * MediaFetchThread::Thread()
226 log_register_thread("MediaFetchThread");
228 DSTACK(__FUNCTION_NAME);
230 BEGIN_DEBUG_EXCEPTION_HANDLER
235 for (core::list<MediaRequest>::Iterator i = m_file_requests.begin();
236 i != m_file_requests.end(); i++) {
237 curl = curl_easy_init();
239 curl_easy_setopt(curl, CURLOPT_URL, (m_remote_url + i->name).c_str());
240 curl_easy_setopt(curl, CURLOPT_FAILONERROR, true);
241 std::ostringstream stream;
242 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_write_data);
243 curl_easy_setopt(curl, CURLOPT_WRITEDATA, &stream);
244 res = curl_easy_perform(curl);
245 if (res == CURLE_OK) {
246 std::string data = stream.str();
247 m_file_data.push_back(make_pair(i->name, data));
249 m_failed.push_back(*i);
250 infostream << "cURL request failed for " << i->name << std::endl;
252 curl_easy_cleanup(curl);
256 END_DEBUG_EXCEPTION_HANDLER(errorstream)
262 IrrlichtDevice *device,
263 const char *playername,
264 std::string password,
265 MapDrawControl &control,
266 IWritableTextureSource *tsrc,
267 IWritableShaderSource *shsrc,
268 IWritableItemDefManager *itemdef,
269 IWritableNodeDefManager *nodedef,
270 ISoundManager *sound,
271 MtEventManager *event
279 m_mesh_update_thread(this),
281 new ClientMap(this, this, control,
282 device->getSceneManager()->getRootSceneNode(),
283 device->getSceneManager(), 666),
284 device->getSceneManager(),
287 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
289 m_server_ser_ver(SER_FMT_VER_INVALID),
291 m_inventory_updated(false),
292 m_inventory_from_server(NULL),
293 m_inventory_from_server_age(0.0),
298 m_password(password),
299 m_access_denied(false),
300 m_media_cache(getMediaCacheDir()),
301 m_media_receive_started(false),
303 m_media_received_count(0),
304 m_itemdef_received(false),
305 m_nodedef_received(false),
306 m_time_of_day_set(false),
307 m_last_time_of_day_f(-1),
308 m_time_of_day_update_timer(0),
309 m_recommended_send_interval(0.1),
310 m_removed_sounds_check_timer(0)
312 m_packetcounter_timer = 0.0;
313 //m_delete_unused_sectors_timer = 0.0;
314 m_connection_reinit_timer = 0.0;
315 m_avg_rtt_timer = 0.0;
316 m_playerpos_send_timer = 0.0;
317 m_ignore_damage_timer = 0.0;
319 // Build main texture atlas, now that the GameDef exists (that is, us)
320 if(g_settings->getBool("enable_texture_atlas"))
321 m_tsrc->buildMainAtlas(this);
323 infostream<<"Not building texture atlas."<<std::endl;
329 Player *player = new LocalPlayer(this);
331 player->updateName(playername);
333 m_env.addPlayer(player);
340 //JMutexAutoLock conlock(m_con_mutex); //bulk comment-out
344 m_mesh_update_thread.setRun(false);
345 while(m_mesh_update_thread.IsRunning())
348 delete m_inventory_from_server;
350 // Delete detached inventories
352 for(std::map<std::string, Inventory*>::iterator
353 i = m_detached_inventories.begin();
354 i != m_detached_inventories.end(); i++){
360 void Client::connect(Address address)
362 DSTACK(__FUNCTION_NAME);
363 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
364 m_con.SetTimeoutMs(0);
365 m_con.Connect(address);
368 bool Client::connectedAndInitialized()
370 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
372 if(m_con.Connected() == false)
375 if(m_server_ser_ver == SER_FMT_VER_INVALID)
381 void Client::step(float dtime)
383 DSTACK(__FUNCTION_NAME);
389 if(m_ignore_damage_timer > dtime)
390 m_ignore_damage_timer -= dtime;
392 m_ignore_damage_timer = 0.0;
394 m_animation_time += dtime;
395 if(m_animation_time > 60.0)
396 m_animation_time -= 60.0;
398 m_time_of_day_update_timer += dtime;
400 //infostream<<"Client steps "<<dtime<<std::endl;
403 //TimeTaker timer("ReceiveAll()", m_device);
409 //TimeTaker timer("m_con_mutex + m_con.RunTimeouts()", m_device);
411 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
412 m_con.RunTimeouts(dtime);
419 float &counter = m_packetcounter_timer;
425 infostream<<"Client packetcounter (20s):"<<std::endl;
426 m_packetcounter.print(infostream);
427 m_packetcounter.clear();
431 // Get connection status
432 bool connected = connectedAndInitialized();
437 Delete unused sectors
439 NOTE: This jams the game for a while because deleting sectors
443 float &counter = m_delete_unused_sectors_timer;
451 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
453 core::list<v3s16> deleted_blocks;
455 float delete_unused_sectors_timeout =
456 g_settings->getFloat("client_delete_unused_sectors_timeout");
458 // Delete sector blocks
459 /*u32 num = m_env.getMap().unloadUnusedData
460 (delete_unused_sectors_timeout,
461 true, &deleted_blocks);*/
463 // Delete whole sectors
464 m_env.getMap().unloadUnusedData
465 (delete_unused_sectors_timeout,
468 if(deleted_blocks.size() > 0)
470 /*infostream<<"Client: Deleted blocks of "<<num
471 <<" unused sectors"<<std::endl;*/
472 /*infostream<<"Client: Deleted "<<num
473 <<" unused sectors"<<std::endl;*/
479 // Env is locked so con can be locked.
480 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
482 core::list<v3s16>::Iterator i = deleted_blocks.begin();
483 core::list<v3s16> sendlist;
486 if(sendlist.size() == 255 || i == deleted_blocks.end())
488 if(sendlist.size() == 0)
497 u32 replysize = 2+1+6*sendlist.size();
498 SharedBuffer<u8> reply(replysize);
499 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
500 reply[2] = sendlist.size();
502 for(core::list<v3s16>::Iterator
503 j = sendlist.begin();
504 j != sendlist.end(); j++)
506 writeV3S16(&reply[2+1+6*k], *j);
509 m_con.Send(PEER_ID_SERVER, 1, reply, true);
511 if(i == deleted_blocks.end())
517 sendlist.push_back(*i);
525 if(connected == false)
527 float &counter = m_connection_reinit_timer;
533 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
535 Player *myplayer = m_env.getLocalPlayer();
536 assert(myplayer != NULL);
538 // Send TOSERVER_INIT
539 // [0] u16 TOSERVER_INIT
540 // [2] u8 SER_FMT_VER_HIGHEST
541 // [3] u8[20] player_name
542 // [23] u8[28] password (new in some version)
543 // [51] u16 minimum supported network protocol version (added sometime)
544 // [53] u16 maximum supported network protocol version (added later than the previous one)
545 SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2+2);
546 writeU16(&data[0], TOSERVER_INIT);
547 writeU8(&data[2], SER_FMT_VER_HIGHEST);
549 memset((char*)&data[3], 0, PLAYERNAME_SIZE);
550 snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
552 /*infostream<<"Client: sending initial password hash: \""<<m_password<<"\""
555 memset((char*)&data[23], 0, PASSWORD_SIZE);
556 snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
558 writeU16(&data[51], CLIENT_PROTOCOL_VERSION_MIN);
559 writeU16(&data[53], CLIENT_PROTOCOL_VERSION_MAX);
561 // Send as unreliable
562 Send(0, data, false);
565 // Not connected, return
570 Do stuff if connected
574 Run Map's timers and unload unused data
576 const float map_timer_and_unload_dtime = 5.25;
577 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
579 ScopeProfiler sp(g_profiler, "Client: map timer and unload");
580 core::list<v3s16> deleted_blocks;
581 m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
582 g_settings->getFloat("client_unload_unused_data_timeout"),
585 /*if(deleted_blocks.size() > 0)
586 infostream<<"Client: Unloaded "<<deleted_blocks.size()
587 <<" unused blocks"<<std::endl;*/
591 NOTE: This loop is intentionally iterated the way it is.
594 core::list<v3s16>::Iterator i = deleted_blocks.begin();
595 core::list<v3s16> sendlist;
598 if(sendlist.size() == 255 || i == deleted_blocks.end())
600 if(sendlist.size() == 0)
609 u32 replysize = 2+1+6*sendlist.size();
610 SharedBuffer<u8> reply(replysize);
611 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
612 reply[2] = sendlist.size();
614 for(core::list<v3s16>::Iterator
615 j = sendlist.begin();
616 j != sendlist.end(); j++)
618 writeV3S16(&reply[2+1+6*k], *j);
621 m_con.Send(PEER_ID_SERVER, 1, reply, true);
623 if(i == deleted_blocks.end())
629 sendlist.push_back(*i);
639 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
641 // Control local player (0ms)
642 LocalPlayer *player = m_env.getLocalPlayer();
643 assert(player != NULL);
644 player->applyControl(dtime);
646 //TimeTaker envtimer("env step", m_device);
655 ClientEnvEvent event = m_env.getClientEvent();
656 if(event.type == CEE_NONE)
660 else if(event.type == CEE_PLAYER_DAMAGE)
662 if(m_ignore_damage_timer <= 0)
664 u8 damage = event.player_damage.amount;
666 if(event.player_damage.send_to_server)
669 // Add to ClientEvent queue
671 event.type = CE_PLAYER_DAMAGE;
672 event.player_damage.amount = damage;
673 m_client_event_queue.push_back(event);
683 float &counter = m_avg_rtt_timer;
688 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
689 // connectedAndInitialized() is true, peer exists.
690 float avg_rtt = m_con.GetPeerAvgRTT(PEER_ID_SERVER);
691 infostream<<"Client: avg_rtt="<<avg_rtt<<std::endl;
696 Send player position to server
699 float &counter = m_playerpos_send_timer;
701 if(counter >= m_recommended_send_interval)
709 Replace updated meshes
712 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
714 //TimeTaker timer("** Processing mesh update result queue");
717 /*infostream<<"Mesh update result queue size is "
718 <<m_mesh_update_thread.m_queue_out.size()
721 int num_processed_meshes = 0;
722 while(m_mesh_update_thread.m_queue_out.size() > 0)
724 num_processed_meshes++;
725 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_front();
726 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
729 //JMutexAutoLock lock(block->mesh_mutex);
731 // Delete the old mesh
732 if(block->mesh != NULL)
734 // TODO: Remove hardware buffers of meshbuffers of block->mesh
739 // Replace with the new mesh
740 block->mesh = r.mesh;
742 if(r.ack_block_to_server)
744 /*infostream<<"Client: ACK block ("<<r.p.X<<","<<r.p.Y
745 <<","<<r.p.Z<<")"<<std::endl;*/
756 u32 replysize = 2+1+6;
757 SharedBuffer<u8> reply(replysize);
758 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
760 writeV3S16(&reply[3], r.p);
762 m_con.Send(PEER_ID_SERVER, 1, reply, true);
765 if(num_processed_meshes > 0)
766 g_profiler->graphAdd("num_processed_meshes", num_processed_meshes);
772 if (m_media_receive_started) {
773 bool all_stopped = true;
774 for (core::list<MediaFetchThread>::Iterator thread = m_media_fetch_threads.begin();
775 thread != m_media_fetch_threads.end(); thread++) {
776 all_stopped &= !thread->IsRunning();
777 while (thread->m_file_data.size() > 0) {
778 std::pair <std::string, std::string> out = thread->m_file_data.pop_front();
779 ++m_media_received_count;
781 bool success = loadMedia(out.second, out.first);
783 verbosestream<<"Client: Loaded received media: "
784 <<"\""<<out.first<<"\". Caching."<<std::endl;
786 infostream<<"Client: Failed to load received media: "
787 <<"\""<<out.first<<"\". Not caching."<<std::endl;
791 bool did = fs::CreateAllDirs(getMediaCacheDir());
793 errorstream<<"Could not create media cache directory"
798 core::map<std::string, std::string>::Node *n;
799 n = m_media_name_sha1_map.find(out.first);
801 errorstream<<"The server sent a file that has not "
802 <<"been announced."<<std::endl;
804 m_media_cache.update_sha1(out.second);
809 core::list<MediaRequest> fetch_failed;
810 for (core::list<MediaFetchThread>::Iterator thread = m_media_fetch_threads.begin();
811 thread != m_media_fetch_threads.end(); thread++) {
812 for (core::list<MediaRequest>::Iterator request = thread->m_failed.begin();
813 request != thread->m_failed.end(); request++)
814 fetch_failed.push_back(*request);
815 thread->m_failed.clear();
817 if (fetch_failed.size() > 0) {
818 infostream << "Failed to remote-fetch " << fetch_failed.size() << " files. "
819 << "Requesting them the usual way." << std::endl;
820 request_media(fetch_failed);
822 m_media_fetch_threads.clear();
827 If the server didn't update the inventory in a while, revert
828 the local inventory (so the player notices the lag problem
829 and knows something is wrong).
831 if(m_inventory_from_server)
833 float interval = 10.0;
834 float count_before = floor(m_inventory_from_server_age / interval);
836 m_inventory_from_server_age += dtime;
838 float count_after = floor(m_inventory_from_server_age / interval);
840 if(count_after != count_before)
842 // Do this every <interval> seconds after TOCLIENT_INVENTORY
843 // Reset the locally changed inventory to the authoritative inventory
844 Player *player = m_env.getLocalPlayer();
845 player->inventory = *m_inventory_from_server;
846 m_inventory_updated = true;
851 Update positions of sounds attached to objects
854 for(std::map<int, u16>::iterator
855 i = m_sounds_to_objects.begin();
856 i != m_sounds_to_objects.end(); i++)
858 int client_id = i->first;
859 u16 object_id = i->second;
860 ClientActiveObject *cao = m_env.getActiveObject(object_id);
863 v3f pos = cao->getPosition();
864 m_sound->updateSoundPosition(client_id, pos);
869 Handle removed remotely initiated sounds
871 m_removed_sounds_check_timer += dtime;
872 if(m_removed_sounds_check_timer >= 2.32)
874 m_removed_sounds_check_timer = 0;
875 // Find removed sounds and clear references to them
876 std::set<s32> removed_server_ids;
877 for(std::map<s32, int>::iterator
878 i = m_sounds_server_to_client.begin();
879 i != m_sounds_server_to_client.end();)
881 s32 server_id = i->first;
882 int client_id = i->second;
884 if(!m_sound->soundExists(client_id)){
885 m_sounds_server_to_client.erase(server_id);
886 m_sounds_client_to_server.erase(client_id);
887 m_sounds_to_objects.erase(client_id);
888 removed_server_ids.insert(server_id);
892 if(removed_server_ids.size() != 0)
894 std::ostringstream os(std::ios_base::binary);
895 writeU16(os, TOSERVER_REMOVED_SOUNDS);
896 writeU16(os, removed_server_ids.size());
897 for(std::set<s32>::iterator i = removed_server_ids.begin();
898 i != removed_server_ids.end(); i++)
900 std::string s = os.str();
901 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
908 bool Client::loadMedia(const std::string &data, const std::string &filename)
910 // Silly irrlicht's const-incorrectness
911 Buffer<char> data_rw(data.c_str(), data.size());
915 const char *image_ext[] = {
916 ".png", ".jpg", ".bmp", ".tga",
917 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
920 name = removeStringEnd(filename, image_ext);
923 verbosestream<<"Client: Attempting to load image "
924 <<"file \""<<filename<<"\""<<std::endl;
926 io::IFileSystem *irrfs = m_device->getFileSystem();
927 video::IVideoDriver *vdrv = m_device->getVideoDriver();
929 // Create an irrlicht memory file
930 io::IReadFile *rfile = irrfs->createMemoryReadFile(
931 *data_rw, data_rw.getSize(), "_tempreadfile");
934 video::IImage *img = vdrv->createImageFromFile(rfile);
936 errorstream<<"Client: Cannot create image from data of "
937 <<"file \""<<filename<<"\""<<std::endl;
942 m_tsrc->insertSourceImage(filename, img);
949 const char *sound_ext[] = {
950 ".0.ogg", ".1.ogg", ".2.ogg", ".3.ogg", ".4.ogg",
951 ".5.ogg", ".6.ogg", ".7.ogg", ".8.ogg", ".9.ogg",
954 name = removeStringEnd(filename, sound_ext);
957 verbosestream<<"Client: Attempting to load sound "
958 <<"file \""<<filename<<"\""<<std::endl;
959 m_sound->loadSoundData(name, data);
963 const char *model_ext[] = {
964 ".x", ".b3d", ".md2", ".obj",
967 name = removeStringEnd(filename, model_ext);
970 verbosestream<<"Client: Storing model into Irrlicht: "
971 <<"\""<<filename<<"\""<<std::endl;
973 io::IFileSystem *irrfs = m_device->getFileSystem();
974 io::IReadFile *rfile = irrfs->createMemoryReadFile(
975 *data_rw, data_rw.getSize(), filename.c_str());
978 scene::ISceneManager *smgr = m_device->getSceneManager();
979 scene::IAnimatedMesh *mesh = smgr->getMesh(rfile);
980 smgr->getMeshCache()->addMesh(filename.c_str(), mesh);
985 errorstream<<"Client: Don't know how to load file \""
986 <<filename<<"\""<<std::endl;
990 // Virtual methods from con::PeerHandler
991 void Client::peerAdded(con::Peer *peer)
993 infostream<<"Client::peerAdded(): peer->id="
994 <<peer->id<<std::endl;
996 void Client::deletingPeer(con::Peer *peer, bool timeout)
998 infostream<<"Client::deletingPeer(): "
999 "Server Peer is getting deleted "
1000 <<"(timeout="<<timeout<<")"<<std::endl;
1005 u16 number of files requested
1011 void Client::request_media(const core::list<MediaRequest> &file_requests)
1013 std::ostringstream os(std::ios_base::binary);
1014 writeU16(os, TOSERVER_REQUEST_MEDIA);
1015 writeU16(os, file_requests.size());
1017 for(core::list<MediaRequest>::ConstIterator i = file_requests.begin();
1018 i != file_requests.end(); i++) {
1019 os<<serializeString(i->name);
1023 std::string s = os.str();
1024 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1026 Send(0, data, true);
1027 infostream<<"Client: Sending media request list to server ("
1028 <<file_requests.size()<<" files)"<<std::endl;
1031 void Client::ReceiveAll()
1033 DSTACK(__FUNCTION_NAME);
1034 u32 start_ms = porting::getTimeMs();
1037 // Limit time even if there would be huge amounts of data to
1039 if(porting::getTimeMs() > start_ms + 100)
1044 g_profiler->graphAdd("client_received_packets", 1);
1046 catch(con::NoIncomingDataException &e)
1050 catch(con::InvalidIncomingDataException &e)
1052 infostream<<"Client::ReceiveAll(): "
1053 "InvalidIncomingDataException: what()="
1054 <<e.what()<<std::endl;
1059 void Client::Receive()
1061 DSTACK(__FUNCTION_NAME);
1062 SharedBuffer<u8> data;
1066 //TimeTaker t1("con mutex and receive", m_device);
1067 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1068 datasize = m_con.Receive(sender_peer_id, data);
1070 //TimeTaker t1("ProcessData", m_device);
1071 ProcessData(*data, datasize, sender_peer_id);
1075 sender_peer_id given to this shall be quaranteed to be a valid peer
1077 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
1079 DSTACK(__FUNCTION_NAME);
1081 // Ignore packets that don't even fit a command
1084 m_packetcounter.add(60000);
1088 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
1090 //infostream<<"Client: received command="<<command<<std::endl;
1091 m_packetcounter.add((u16)command);
1094 If this check is removed, be sure to change the queue
1095 system to know the ids
1097 if(sender_peer_id != PEER_ID_SERVER)
1099 infostream<<"Client::ProcessData(): Discarding data not "
1100 "coming from server: peer_id="<<sender_peer_id
1105 u8 ser_version = m_server_ser_ver;
1107 //infostream<<"Client received command="<<(int)command<<std::endl;
1109 if(command == TOCLIENT_INIT)
1114 u8 deployed = data[2];
1116 infostream<<"Client: TOCLIENT_INIT received with "
1117 "deployed="<<((int)deployed&0xff)<<std::endl;
1119 if(deployed < SER_FMT_VER_LOWEST
1120 || deployed > SER_FMT_VER_HIGHEST)
1122 infostream<<"Client: TOCLIENT_INIT: Server sent "
1123 <<"unsupported ser_fmt_ver"<<std::endl;
1127 m_server_ser_ver = deployed;
1129 // Get player position
1130 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
1131 if(datasize >= 2+1+6)
1132 playerpos_s16 = readV3S16(&data[2+1]);
1133 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
1136 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1138 // Set player position
1139 Player *player = m_env.getLocalPlayer();
1140 assert(player != NULL);
1141 player->setPosition(playerpos_f);
1144 if(datasize >= 2+1+6+8)
1147 m_map_seed = readU64(&data[2+1+6]);
1148 infostream<<"Client: received map seed: "<<m_map_seed<<std::endl;
1151 if(datasize >= 2+1+6+8+4)
1154 m_recommended_send_interval = readF1000(&data[2+1+6+8]);
1155 infostream<<"Client: received recommended send interval "
1156 <<m_recommended_send_interval<<std::endl;
1161 SharedBuffer<u8> reply(replysize);
1162 writeU16(&reply[0], TOSERVER_INIT2);
1164 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1169 if(command == TOCLIENT_ACCESS_DENIED)
1171 // The server didn't like our password. Note, this needs
1172 // to be processed even if the serialisation format has
1173 // not been agreed yet, the same as TOCLIENT_INIT.
1174 m_access_denied = true;
1175 m_access_denied_reason = L"Unknown";
1178 std::string datastring((char*)&data[2], datasize-2);
1179 std::istringstream is(datastring, std::ios_base::binary);
1180 m_access_denied_reason = deSerializeWideString(is);
1185 if(ser_version == SER_FMT_VER_INVALID)
1187 infostream<<"Client: Server serialization"
1188 " format invalid or not initialized."
1189 " Skipping incoming command="<<command<<std::endl;
1193 // Just here to avoid putting the two if's together when
1194 // making some copypasta
1197 if(command == TOCLIENT_REMOVENODE)
1202 p.X = readS16(&data[2]);
1203 p.Y = readS16(&data[4]);
1204 p.Z = readS16(&data[6]);
1206 //TimeTaker t1("TOCLIENT_REMOVENODE");
1210 else if(command == TOCLIENT_ADDNODE)
1212 if(datasize < 8 + MapNode::serializedLength(ser_version))
1216 p.X = readS16(&data[2]);
1217 p.Y = readS16(&data[4]);
1218 p.Z = readS16(&data[6]);
1220 //TimeTaker t1("TOCLIENT_ADDNODE");
1223 n.deSerialize(&data[8], ser_version);
1227 else if(command == TOCLIENT_BLOCKDATA)
1229 // Ignore too small packet
1234 p.X = readS16(&data[2]);
1235 p.Y = readS16(&data[4]);
1236 p.Z = readS16(&data[6]);
1238 /*infostream<<"Client: Thread: BLOCKDATA for ("
1239 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1240 /*infostream<<"Client: Thread: BLOCKDATA for ("
1241 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1243 std::string datastring((char*)&data[8], datasize-8);
1244 std::istringstream istr(datastring, std::ios_base::binary);
1249 v2s16 p2d(p.X, p.Z);
1250 sector = m_env.getMap().emergeSector(p2d);
1252 assert(sector->getPos() == p2d);
1254 //TimeTaker timer("MapBlock deSerialize");
1257 block = sector->getBlockNoCreateNoEx(p.Y);
1261 Update an existing block
1263 //infostream<<"Updating"<<std::endl;
1264 block->deSerialize(istr, ser_version, false);
1271 //infostream<<"Creating new"<<std::endl;
1272 block = new MapBlock(&m_env.getMap(), p, this);
1273 block->deSerialize(istr, ser_version, false);
1274 sector->insertBlock(block);
1288 u32 replysize = 2+1+6;
1289 SharedBuffer<u8> reply(replysize);
1290 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
1292 writeV3S16(&reply[3], p);
1294 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1298 Add it to mesh update queue and set it to be acknowledged after update.
1300 //infostream<<"Adding mesh update task for received block"<<std::endl;
1301 addUpdateMeshTaskWithEdge(p, true);
1303 else if(command == TOCLIENT_INVENTORY)
1308 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
1311 //TimeTaker t2("mutex locking", m_device);
1312 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1315 //TimeTaker t3("istringstream init", m_device);
1316 std::string datastring((char*)&data[2], datasize-2);
1317 std::istringstream is(datastring, std::ios_base::binary);
1320 //m_env.printPlayers(infostream);
1322 //TimeTaker t4("player get", m_device);
1323 Player *player = m_env.getLocalPlayer();
1324 assert(player != NULL);
1327 //TimeTaker t1("inventory.deSerialize()", m_device);
1328 player->inventory.deSerialize(is);
1331 m_inventory_updated = true;
1333 delete m_inventory_from_server;
1334 m_inventory_from_server = new Inventory(player->inventory);
1335 m_inventory_from_server_age = 0.0;
1337 //infostream<<"Client got player inventory:"<<std::endl;
1338 //player->inventory.print(infostream);
1341 else if(command == TOCLIENT_TIME_OF_DAY)
1346 u16 time_of_day = readU16(&data[2]);
1347 time_of_day = time_of_day % 24000;
1348 //infostream<<"Client: time_of_day="<<time_of_day<<std::endl;
1349 float time_speed = 0;
1350 if(datasize >= 2 + 2 + 4){
1351 time_speed = readF1000(&data[4]);
1353 // Old message; try to approximate speed of time by ourselves
1354 float time_of_day_f = (float)time_of_day / 24000.0;
1355 float tod_diff_f = 0;
1356 if(time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
1357 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0;
1359 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
1360 m_last_time_of_day_f = time_of_day_f;
1361 float time_diff = m_time_of_day_update_timer;
1362 m_time_of_day_update_timer = 0;
1363 if(m_time_of_day_set){
1364 time_speed = 3600.0*24.0 * tod_diff_f / time_diff;
1365 infostream<<"Client: Measured time_of_day speed (old format): "
1366 <<time_speed<<" tod_diff_f="<<tod_diff_f
1367 <<" time_diff="<<time_diff<<std::endl;
1371 // Update environment
1372 m_env.setTimeOfDay(time_of_day);
1373 m_env.setTimeOfDaySpeed(time_speed);
1374 m_time_of_day_set = true;
1376 u32 dr = m_env.getDayNightRatio();
1377 verbosestream<<"Client: time_of_day="<<time_of_day
1378 <<" time_speed="<<time_speed
1379 <<" dr="<<dr<<std::endl;
1381 else if(command == TOCLIENT_CHAT_MESSAGE)
1389 std::string datastring((char*)&data[2], datasize-2);
1390 std::istringstream is(datastring, std::ios_base::binary);
1393 is.read((char*)buf, 2);
1394 u16 len = readU16(buf);
1396 std::wstring message;
1397 for(u16 i=0; i<len; i++)
1399 is.read((char*)buf, 2);
1400 message += (wchar_t)readU16(buf);
1403 /*infostream<<"Client received chat message: "
1404 <<wide_to_narrow(message)<<std::endl;*/
1406 m_chat_queue.push_back(message);
1408 else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1410 //if(g_settings->getBool("enable_experimental"))
1414 u16 count of removed objects
1415 for all removed objects {
1418 u16 count of added objects
1419 for all added objects {
1422 u32 initialization data length
1423 string initialization data
1428 // Get all data except the command number
1429 std::string datastring((char*)&data[2], datasize-2);
1430 // Throw them in an istringstream
1431 std::istringstream is(datastring, std::ios_base::binary);
1435 // Read removed objects
1437 u16 removed_count = readU16((u8*)buf);
1438 for(u16 i=0; i<removed_count; i++)
1441 u16 id = readU16((u8*)buf);
1444 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1445 m_env.removeActiveObject(id);
1449 // Read added objects
1451 u16 added_count = readU16((u8*)buf);
1452 for(u16 i=0; i<added_count; i++)
1455 u16 id = readU16((u8*)buf);
1457 u8 type = readU8((u8*)buf);
1458 std::string data = deSerializeLongString(is);
1461 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1462 m_env.addActiveObject(id, type, data);
1467 else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1469 //if(g_settings->getBool("enable_experimental"))
1481 // Get all data except the command number
1482 std::string datastring((char*)&data[2], datasize-2);
1483 // Throw them in an istringstream
1484 std::istringstream is(datastring, std::ios_base::binary);
1486 while(is.eof() == false)
1490 u16 id = readU16((u8*)buf);
1494 u16 message_size = readU16((u8*)buf);
1495 std::string message;
1496 message.reserve(message_size);
1497 for(u16 i=0; i<message_size; i++)
1500 message.append(buf, 1);
1502 // Pass on to the environment
1504 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1505 m_env.processActiveObjectMessage(id, message);
1510 else if(command == TOCLIENT_HP)
1512 std::string datastring((char*)&data[2], datasize-2);
1513 std::istringstream is(datastring, std::ios_base::binary);
1514 Player *player = m_env.getLocalPlayer();
1515 assert(player != NULL);
1516 u8 oldhp = player->hp;
1522 // Add to ClientEvent queue
1524 event.type = CE_PLAYER_DAMAGE;
1525 event.player_damage.amount = oldhp - hp;
1526 m_client_event_queue.push_back(event);
1529 else if(command == TOCLIENT_MOVE_PLAYER)
1531 std::string datastring((char*)&data[2], datasize-2);
1532 std::istringstream is(datastring, std::ios_base::binary);
1533 Player *player = m_env.getLocalPlayer();
1534 assert(player != NULL);
1535 v3f pos = readV3F1000(is);
1536 f32 pitch = readF1000(is);
1537 f32 yaw = readF1000(is);
1538 player->setPosition(pos);
1539 /*player->setPitch(pitch);
1540 player->setYaw(yaw);*/
1542 infostream<<"Client got TOCLIENT_MOVE_PLAYER"
1543 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1549 Add to ClientEvent queue.
1550 This has to be sent to the main program because otherwise
1551 it would just force the pitch and yaw values to whatever
1552 the camera points to.
1555 event.type = CE_PLAYER_FORCE_MOVE;
1556 event.player_force_move.pitch = pitch;
1557 event.player_force_move.yaw = yaw;
1558 m_client_event_queue.push_back(event);
1560 // Ignore damage for a few seconds, so that the player doesn't
1561 // get damage from falling on ground
1562 m_ignore_damage_timer = 3.0;
1564 else if(command == TOCLIENT_PLAYERITEM)
1566 infostream<<"Client: WARNING: Ignoring TOCLIENT_PLAYERITEM"<<std::endl;
1568 else if(command == TOCLIENT_DEATHSCREEN)
1570 std::string datastring((char*)&data[2], datasize-2);
1571 std::istringstream is(datastring, std::ios_base::binary);
1573 bool set_camera_point_target = readU8(is);
1574 v3f camera_point_target = readV3F1000(is);
1577 event.type = CE_DEATHSCREEN;
1578 event.deathscreen.set_camera_point_target = set_camera_point_target;
1579 event.deathscreen.camera_point_target_x = camera_point_target.X;
1580 event.deathscreen.camera_point_target_y = camera_point_target.Y;
1581 event.deathscreen.camera_point_target_z = camera_point_target.Z;
1582 m_client_event_queue.push_back(event);
1584 else if(command == TOCLIENT_ANNOUNCE_MEDIA)
1586 std::string datastring((char*)&data[2], datasize-2);
1587 std::istringstream is(datastring, std::ios_base::binary);
1589 // Mesh update thread must be stopped while
1590 // updating content definitions
1591 assert(!m_mesh_update_thread.IsRunning());
1593 int num_files = readU16(is);
1595 infostream<<"Client: Received media announcement: packet size: "
1596 <<datasize<<std::endl;
1598 core::list<MediaRequest> file_requests;
1600 for(int i=0; i<num_files; i++)
1602 //read file from cache
1603 std::string name = deSerializeString(is);
1604 std::string sha1_base64 = deSerializeString(is);
1606 // if name contains illegal characters, ignore the file
1607 if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
1608 errorstream<<"Client: ignoring illegal file name "
1609 <<"sent by server: \""<<name<<"\""<<std::endl;
1613 std::string sha1_raw = base64_decode(sha1_base64);
1614 std::string sha1_hex = hex_encode(sha1_raw);
1615 std::ostringstream tmp_os(std::ios_base::binary);
1616 bool found_in_cache = m_media_cache.load_sha1(sha1_raw, tmp_os);
1617 m_media_name_sha1_map.set(name, sha1_raw);
1619 // If found in cache, try to load it from there
1622 bool success = loadMedia(tmp_os.str(), name);
1624 verbosestream<<"Client: Loaded cached media: "
1625 <<sha1_hex<<" \""<<name<<"\""<<std::endl;
1628 infostream<<"Client: Failed to load cached media: "
1629 <<sha1_hex<<" \""<<name<<"\""<<std::endl;
1632 // Didn't load from cache; queue it to be requested
1633 verbosestream<<"Client: Adding file to request list: \""
1634 <<sha1_hex<<" \""<<name<<"\""<<std::endl;
1635 file_requests.push_back(MediaRequest(name));
1638 std::string remote_media = "";
1640 remote_media = deSerializeString(is);
1642 catch(SerializationError) {
1643 // not supported by server or turned off
1646 m_media_count = file_requests.size();
1647 m_media_receive_started = true;
1649 if (remote_media == "" || !USE_CURL) {
1650 request_media(file_requests);
1653 for (size_t i = 0; i < g_settings->getU16("media_fetch_threads"); ++i) {
1654 m_media_fetch_threads.push_back(MediaFetchThread(this));
1657 core::list<MediaFetchThread>::Iterator cur = m_media_fetch_threads.begin();
1658 for(core::list<MediaRequest>::Iterator i = file_requests.begin();
1659 i != file_requests.end(); i++) {
1660 cur->m_file_requests.push_back(*i);
1662 if (cur == m_media_fetch_threads.end())
1663 cur = m_media_fetch_threads.begin();
1665 for (core::list<MediaFetchThread>::Iterator i = m_media_fetch_threads.begin();
1666 i != m_media_fetch_threads.end(); i++) {
1667 i->m_remote_url = remote_media;
1672 // notify server we received everything
1673 std::ostringstream os(std::ios_base::binary);
1674 writeU16(os, TOSERVER_RECEIVED_MEDIA);
1675 std::string s = os.str();
1676 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1678 Send(0, data, true);
1681 event.type = CE_TEXTURES_UPDATED;
1682 m_client_event_queue.push_back(event);
1684 else if(command == TOCLIENT_MEDIA)
1686 if (m_media_count == 0)
1688 std::string datastring((char*)&data[2], datasize-2);
1689 std::istringstream is(datastring, std::ios_base::binary);
1691 // Mesh update thread must be stopped while
1692 // updating content definitions
1693 assert(!m_mesh_update_thread.IsRunning());
1697 u16 total number of file bunches
1698 u16 index of this bunch
1699 u32 number of files in this bunch
1707 int num_bunches = readU16(is);
1708 int bunch_i = readU16(is);
1709 int num_files = readU32(is);
1710 infostream<<"Client: Received files: bunch "<<bunch_i<<"/"
1711 <<num_bunches<<" files="<<num_files
1712 <<" size="<<datasize<<std::endl;
1713 for(int i=0; i<num_files; i++){
1714 m_media_received_count++;
1715 std::string name = deSerializeString(is);
1716 std::string data = deSerializeLongString(is);
1718 // if name contains illegal characters, ignore the file
1719 if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
1720 errorstream<<"Client: ignoring illegal file name "
1721 <<"sent by server: \""<<name<<"\""<<std::endl;
1725 bool success = loadMedia(data, name);
1727 verbosestream<<"Client: Loaded received media: "
1728 <<"\""<<name<<"\". Caching."<<std::endl;
1730 infostream<<"Client: Failed to load received media: "
1731 <<"\""<<name<<"\". Not caching."<<std::endl;
1735 bool did = fs::CreateAllDirs(getMediaCacheDir());
1737 errorstream<<"Could not create media cache directory"
1742 core::map<std::string, std::string>::Node *n;
1743 n = m_media_name_sha1_map.find(name);
1745 errorstream<<"The server sent a file that has not "
1746 <<"been announced."<<std::endl;
1748 m_media_cache.update_sha1(data);
1753 event.type = CE_TEXTURES_UPDATED;
1754 m_client_event_queue.push_back(event);
1756 else if(command == TOCLIENT_TOOLDEF)
1758 infostream<<"Client: WARNING: Ignoring TOCLIENT_TOOLDEF"<<std::endl;
1760 else if(command == TOCLIENT_NODEDEF)
1762 infostream<<"Client: Received node definitions: packet size: "
1763 <<datasize<<std::endl;
1765 // Mesh update thread must be stopped while
1766 // updating content definitions
1767 assert(!m_mesh_update_thread.IsRunning());
1769 // Decompress node definitions
1770 std::string datastring((char*)&data[2], datasize-2);
1771 std::istringstream is(datastring, std::ios_base::binary);
1772 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1773 std::ostringstream tmp_os;
1774 decompressZlib(tmp_is, tmp_os);
1776 // Deserialize node definitions
1777 std::istringstream tmp_is2(tmp_os.str());
1778 m_nodedef->deSerialize(tmp_is2);
1779 m_nodedef_received = true;
1781 else if(command == TOCLIENT_CRAFTITEMDEF)
1783 infostream<<"Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF"<<std::endl;
1785 else if(command == TOCLIENT_ITEMDEF)
1787 infostream<<"Client: Received item definitions: packet size: "
1788 <<datasize<<std::endl;
1790 // Mesh update thread must be stopped while
1791 // updating content definitions
1792 assert(!m_mesh_update_thread.IsRunning());
1794 // Decompress item definitions
1795 std::string datastring((char*)&data[2], datasize-2);
1796 std::istringstream is(datastring, std::ios_base::binary);
1797 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1798 std::ostringstream tmp_os;
1799 decompressZlib(tmp_is, tmp_os);
1801 // Deserialize node definitions
1802 std::istringstream tmp_is2(tmp_os.str());
1803 m_itemdef->deSerialize(tmp_is2);
1804 m_itemdef_received = true;
1806 else if(command == TOCLIENT_PLAY_SOUND)
1808 std::string datastring((char*)&data[2], datasize-2);
1809 std::istringstream is(datastring, std::ios_base::binary);
1811 s32 server_id = readS32(is);
1812 std::string name = deSerializeString(is);
1813 float gain = readF1000(is);
1814 int type = readU8(is); // 0=local, 1=positional, 2=object
1815 v3f pos = readV3F1000(is);
1816 u16 object_id = readU16(is);
1817 bool loop = readU8(is);
1822 client_id = m_sound->playSound(name, loop, gain);
1824 case 1: // positional
1825 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1828 ClientActiveObject *cao = m_env.getActiveObject(object_id);
1830 pos = cao->getPosition();
1831 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1832 // TODO: Set up sound to move with object
1837 if(client_id != -1){
1838 m_sounds_server_to_client[server_id] = client_id;
1839 m_sounds_client_to_server[client_id] = server_id;
1841 m_sounds_to_objects[client_id] = object_id;
1844 else if(command == TOCLIENT_STOP_SOUND)
1846 std::string datastring((char*)&data[2], datasize-2);
1847 std::istringstream is(datastring, std::ios_base::binary);
1849 s32 server_id = readS32(is);
1850 std::map<s32, int>::iterator i =
1851 m_sounds_server_to_client.find(server_id);
1852 if(i != m_sounds_server_to_client.end()){
1853 int client_id = i->second;
1854 m_sound->stopSound(client_id);
1857 else if(command == TOCLIENT_PRIVILEGES)
1859 std::string datastring((char*)&data[2], datasize-2);
1860 std::istringstream is(datastring, std::ios_base::binary);
1862 m_privileges.clear();
1863 infostream<<"Client: Privileges updated: ";
1864 u16 num_privileges = readU16(is);
1865 for(u16 i=0; i<num_privileges; i++){
1866 std::string priv = deSerializeString(is);
1867 m_privileges.insert(priv);
1868 infostream<<priv<<" ";
1870 infostream<<std::endl;
1872 else if(command == TOCLIENT_INVENTORY_FORMSPEC)
1874 std::string datastring((char*)&data[2], datasize-2);
1875 std::istringstream is(datastring, std::ios_base::binary);
1877 // Store formspec in LocalPlayer
1878 Player *player = m_env.getLocalPlayer();
1879 assert(player != NULL);
1880 player->inventory_formspec = deSerializeLongString(is);
1882 else if(command == TOCLIENT_DETACHED_INVENTORY)
1884 std::string datastring((char*)&data[2], datasize-2);
1885 std::istringstream is(datastring, std::ios_base::binary);
1887 std::string name = deSerializeString(is);
1889 infostream<<"Client: Detached inventory update: \""<<name<<"\""<<std::endl;
1891 Inventory *inv = NULL;
1892 if(m_detached_inventories.count(name) > 0)
1893 inv = m_detached_inventories[name];
1895 inv = new Inventory(m_itemdef);
1896 m_detached_inventories[name] = inv;
1898 inv->deSerialize(is);
1902 infostream<<"Client: Ignoring unknown command "
1903 <<command<<std::endl;
1907 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
1909 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1910 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
1913 void Client::interact(u8 action, const PointedThing& pointed)
1915 if(connectedAndInitialized() == false){
1916 infostream<<"Client::interact() "
1917 "cancelled (not connected)"
1922 std::ostringstream os(std::ios_base::binary);
1928 [5] u32 length of the next item
1929 [9] serialized PointedThing
1931 0: start digging (from undersurface) or use
1932 1: stop digging (all parameters ignored)
1933 2: digging completed
1934 3: place block or item (to abovesurface)
1937 writeU16(os, TOSERVER_INTERACT);
1938 writeU8(os, action);
1939 writeU16(os, getPlayerItem());
1940 std::ostringstream tmp_os(std::ios::binary);
1941 pointed.serialize(tmp_os);
1942 os<<serializeLongString(tmp_os.str());
1944 std::string s = os.str();
1945 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1948 Send(0, data, true);
1951 void Client::sendNodemetaFields(v3s16 p, const std::string &formname,
1952 const std::map<std::string, std::string> &fields)
1954 std::ostringstream os(std::ios_base::binary);
1956 writeU16(os, TOSERVER_NODEMETA_FIELDS);
1958 os<<serializeString(formname);
1959 writeU16(os, fields.size());
1960 for(std::map<std::string, std::string>::const_iterator
1961 i = fields.begin(); i != fields.end(); i++){
1962 const std::string &name = i->first;
1963 const std::string &value = i->second;
1964 os<<serializeString(name);
1965 os<<serializeLongString(value);
1969 std::string s = os.str();
1970 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1972 Send(0, data, true);
1975 void Client::sendInventoryFields(const std::string &formname,
1976 const std::map<std::string, std::string> &fields)
1978 std::ostringstream os(std::ios_base::binary);
1980 writeU16(os, TOSERVER_INVENTORY_FIELDS);
1981 os<<serializeString(formname);
1982 writeU16(os, fields.size());
1983 for(std::map<std::string, std::string>::const_iterator
1984 i = fields.begin(); i != fields.end(); i++){
1985 const std::string &name = i->first;
1986 const std::string &value = i->second;
1987 os<<serializeString(name);
1988 os<<serializeLongString(value);
1992 std::string s = os.str();
1993 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1995 Send(0, data, true);
1998 void Client::sendInventoryAction(InventoryAction *a)
2000 std::ostringstream os(std::ios_base::binary);
2004 writeU16(buf, TOSERVER_INVENTORY_ACTION);
2005 os.write((char*)buf, 2);
2010 std::string s = os.str();
2011 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2013 Send(0, data, true);
2016 void Client::sendChatMessage(const std::wstring &message)
2018 std::ostringstream os(std::ios_base::binary);
2022 writeU16(buf, TOSERVER_CHAT_MESSAGE);
2023 os.write((char*)buf, 2);
2026 writeU16(buf, message.size());
2027 os.write((char*)buf, 2);
2030 for(u32 i=0; i<message.size(); i++)
2034 os.write((char*)buf, 2);
2038 std::string s = os.str();
2039 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2041 Send(0, data, true);
2044 void Client::sendChangePassword(const std::wstring oldpassword,
2045 const std::wstring newpassword)
2047 Player *player = m_env.getLocalPlayer();
2051 std::string playername = player->getName();
2052 std::string oldpwd = translatePassword(playername, oldpassword);
2053 std::string newpwd = translatePassword(playername, newpassword);
2055 std::ostringstream os(std::ios_base::binary);
2056 u8 buf[2+PASSWORD_SIZE*2];
2058 [0] u16 TOSERVER_PASSWORD
2059 [2] u8[28] old password
2060 [30] u8[28] new password
2063 writeU16(buf, TOSERVER_PASSWORD);
2064 for(u32 i=0;i<PASSWORD_SIZE-1;i++)
2066 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
2067 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
2069 buf[2+PASSWORD_SIZE-1] = 0;
2070 buf[30+PASSWORD_SIZE-1] = 0;
2071 os.write((char*)buf, 2+PASSWORD_SIZE*2);
2074 std::string s = os.str();
2075 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2077 Send(0, data, true);
2081 void Client::sendDamage(u8 damage)
2083 DSTACK(__FUNCTION_NAME);
2084 std::ostringstream os(std::ios_base::binary);
2086 writeU16(os, TOSERVER_DAMAGE);
2087 writeU8(os, damage);
2090 std::string s = os.str();
2091 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2093 Send(0, data, true);
2096 void Client::sendRespawn()
2098 DSTACK(__FUNCTION_NAME);
2099 std::ostringstream os(std::ios_base::binary);
2101 writeU16(os, TOSERVER_RESPAWN);
2104 std::string s = os.str();
2105 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2107 Send(0, data, true);
2110 void Client::sendPlayerPos()
2112 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2114 LocalPlayer *myplayer = m_env.getLocalPlayer();
2115 if(myplayer == NULL)
2118 // Save bandwidth by only updating position when something changed
2119 if(myplayer->last_position == myplayer->getPosition() &&
2120 myplayer->last_speed == myplayer->getSpeed() &&
2121 myplayer->last_pitch == myplayer->getPitch() &&
2122 myplayer->last_yaw == myplayer->getYaw() &&
2123 myplayer->last_keyPressed == myplayer->keyPressed)
2126 myplayer->last_position = myplayer->getPosition();
2127 myplayer->last_speed = myplayer->getSpeed();
2128 myplayer->last_pitch = myplayer->getPitch();
2129 myplayer->last_yaw = myplayer->getYaw();
2130 myplayer->last_keyPressed = myplayer->keyPressed;
2134 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2135 our_peer_id = m_con.GetPeerID();
2138 // Set peer id if not set already
2139 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2140 myplayer->peer_id = our_peer_id;
2141 // Check that an existing peer_id is the same as the connection's
2142 assert(myplayer->peer_id == our_peer_id);
2144 v3f pf = myplayer->getPosition();
2145 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
2146 v3f sf = myplayer->getSpeed();
2147 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
2148 s32 pitch = myplayer->getPitch() * 100;
2149 s32 yaw = myplayer->getYaw() * 100;
2150 u32 keyPressed=myplayer->keyPressed;
2154 [2] v3s32 position*100
2155 [2+12] v3s32 speed*100
2156 [2+12+12] s32 pitch*100
2157 [2+12+12+4] s32 yaw*100
2158 [2+12+12+4+4] u32 keyPressed
2160 SharedBuffer<u8> data(2+12+12+4+4+4);
2161 writeU16(&data[0], TOSERVER_PLAYERPOS);
2162 writeV3S32(&data[2], position);
2163 writeV3S32(&data[2+12], speed);
2164 writeS32(&data[2+12+12], pitch);
2165 writeS32(&data[2+12+12+4], yaw);
2166 writeU32(&data[2+12+12+4+4], keyPressed);
2167 // Send as unreliable
2168 Send(0, data, false);
2171 void Client::sendPlayerItem(u16 item)
2173 Player *myplayer = m_env.getLocalPlayer();
2174 if(myplayer == NULL)
2177 u16 our_peer_id = m_con.GetPeerID();
2179 // Set peer id if not set already
2180 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2181 myplayer->peer_id = our_peer_id;
2182 // Check that an existing peer_id is the same as the connection's
2183 assert(myplayer->peer_id == our_peer_id);
2185 SharedBuffer<u8> data(2+2);
2186 writeU16(&data[0], TOSERVER_PLAYERITEM);
2187 writeU16(&data[2], item);
2190 Send(0, data, true);
2193 void Client::removeNode(v3s16 p)
2195 core::map<v3s16, MapBlock*> modified_blocks;
2199 //TimeTaker t("removeNodeAndUpdate", m_device);
2200 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
2202 catch(InvalidPositionException &e)
2206 // add urgent task to update the modified node
2207 addUpdateMeshTaskForNode(p, false, true);
2209 for(core::map<v3s16, MapBlock * >::Iterator
2210 i = modified_blocks.getIterator();
2211 i.atEnd() == false; i++)
2213 v3s16 p = i.getNode()->getKey();
2214 addUpdateMeshTaskWithEdge(p);
2218 void Client::addNode(v3s16 p, MapNode n)
2220 TimeTaker timer1("Client::addNode()");
2222 core::map<v3s16, MapBlock*> modified_blocks;
2226 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
2227 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks);
2229 catch(InvalidPositionException &e)
2232 for(core::map<v3s16, MapBlock * >::Iterator
2233 i = modified_blocks.getIterator();
2234 i.atEnd() == false; i++)
2236 v3s16 p = i.getNode()->getKey();
2237 addUpdateMeshTaskWithEdge(p);
2241 void Client::setPlayerControl(PlayerControl &control)
2243 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2244 LocalPlayer *player = m_env.getLocalPlayer();
2245 assert(player != NULL);
2246 player->control = control;
2249 void Client::selectPlayerItem(u16 item)
2251 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2252 m_playeritem = item;
2253 m_inventory_updated = true;
2254 sendPlayerItem(item);
2257 // Returns true if the inventory of the local player has been
2258 // updated from the server. If it is true, it is set to false.
2259 bool Client::getLocalInventoryUpdated()
2261 // m_inventory_updated is behind envlock
2262 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2263 bool updated = m_inventory_updated;
2264 m_inventory_updated = false;
2268 // Copies the inventory of the local player to parameter
2269 void Client::getLocalInventory(Inventory &dst)
2271 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2272 Player *player = m_env.getLocalPlayer();
2273 assert(player != NULL);
2274 dst = player->inventory;
2277 Inventory* Client::getInventory(const InventoryLocation &loc)
2280 case InventoryLocation::UNDEFINED:
2283 case InventoryLocation::CURRENT_PLAYER:
2285 Player *player = m_env.getLocalPlayer();
2286 assert(player != NULL);
2287 return &player->inventory;
2290 case InventoryLocation::PLAYER:
2292 Player *player = m_env.getPlayer(loc.name.c_str());
2295 return &player->inventory;
2298 case InventoryLocation::NODEMETA:
2300 NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p);
2303 return meta->getInventory();
2306 case InventoryLocation::DETACHED:
2308 if(m_detached_inventories.count(loc.name) == 0)
2310 return m_detached_inventories[loc.name];
2318 void Client::inventoryAction(InventoryAction *a)
2321 Send it to the server
2323 sendInventoryAction(a);
2326 Predict some local inventory changes
2328 a->clientApply(this, this);
2331 ClientActiveObject * Client::getSelectedActiveObject(
2333 v3f from_pos_f_on_map,
2334 core::line3d<f32> shootline_on_map
2337 core::array<DistanceSortedActiveObject> objects;
2339 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
2341 //infostream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
2344 // After this, the closest object is the first in the array.
2347 for(u32 i=0; i<objects.size(); i++)
2349 ClientActiveObject *obj = objects[i].obj;
2351 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
2352 if(selection_box == NULL)
2355 v3f pos = obj->getPosition();
2357 core::aabbox3d<f32> offsetted_box(
2358 selection_box->MinEdge + pos,
2359 selection_box->MaxEdge + pos
2362 if(offsetted_box.intersectsWithLine(shootline_on_map))
2364 //infostream<<"Returning selected object"<<std::endl;
2369 //infostream<<"No object selected; returning NULL."<<std::endl;
2373 void Client::printDebugInfo(std::ostream &os)
2375 //JMutexAutoLock lock1(m_fetchblock_mutex);
2376 /*JMutexAutoLock lock2(m_incoming_queue_mutex);
2378 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
2379 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
2380 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
2384 core::list<std::wstring> Client::getConnectedPlayerNames()
2386 core::list<Player*> players = m_env.getPlayers(true);
2387 core::list<std::wstring> playerNames;
2388 for(core::list<Player*>::Iterator
2389 i = players.begin();
2390 i != players.end(); i++)
2392 Player *player = *i;
2393 playerNames.push_back(narrow_to_wide(player->getName()));
2398 float Client::getAnimationTime()
2400 return m_animation_time;
2403 int Client::getCrackLevel()
2405 return m_crack_level;
2408 void Client::setCrack(int level, v3s16 pos)
2410 int old_crack_level = m_crack_level;
2411 v3s16 old_crack_pos = m_crack_pos;
2413 m_crack_level = level;
2416 if(old_crack_level >= 0 && (level < 0 || pos != old_crack_pos))
2419 addUpdateMeshTaskForNode(old_crack_pos, false, true);
2421 if(level >= 0 && (old_crack_level < 0 || pos != old_crack_pos))
2424 addUpdateMeshTaskForNode(pos, false, true);
2430 Player *player = m_env.getLocalPlayer();
2431 assert(player != NULL);
2435 bool Client::getChatMessage(std::wstring &message)
2437 if(m_chat_queue.size() == 0)
2439 message = m_chat_queue.pop_front();
2443 void Client::typeChatMessage(const std::wstring &message)
2445 // Discard empty line
2450 sendChatMessage(message);
2453 if (message[0] == L'/')
2455 m_chat_queue.push_back(
2456 (std::wstring)L"issued command: "+message);
2460 LocalPlayer *player = m_env.getLocalPlayer();
2461 assert(player != NULL);
2462 std::wstring name = narrow_to_wide(player->getName());
2463 m_chat_queue.push_back(
2464 (std::wstring)L"<"+name+L"> "+message);
2468 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
2470 /*infostream<<"Client::addUpdateMeshTask(): "
2471 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2472 <<" ack_to_server="<<ack_to_server
2473 <<" urgent="<<urgent
2476 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2481 Create a task to update the mesh of the block
2484 MeshMakeData *data = new MeshMakeData(this);
2487 //TimeTaker timer("data fill");
2489 // Debug: 1-6ms, avg=2ms
2491 data->setCrack(m_crack_level, m_crack_pos);
2492 data->setSmoothLighting(g_settings->getBool("smooth_lighting"));
2496 //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
2498 // Add task to queue
2499 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server, urgent);
2501 /*infostream<<"Mesh update input queue size is "
2502 <<m_mesh_update_thread.m_queue_in.size()
2506 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent)
2510 infostream<<"Client::addUpdateMeshTaskWithEdge(): "
2511 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2516 v3s16 p = blockpos + v3s16(0,0,0);
2517 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2518 addUpdateMeshTask(p, ack_to_server, urgent);
2520 catch(InvalidPositionException &e){}
2523 v3s16 p = blockpos + v3s16(-1,0,0);
2524 addUpdateMeshTask(p, false, urgent);
2526 catch(InvalidPositionException &e){}
2528 v3s16 p = blockpos + v3s16(0,-1,0);
2529 addUpdateMeshTask(p, false, urgent);
2531 catch(InvalidPositionException &e){}
2533 v3s16 p = blockpos + v3s16(0,0,-1);
2534 addUpdateMeshTask(p, false, urgent);
2536 catch(InvalidPositionException &e){}
2539 void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool urgent)
2543 infostream<<"Client::addUpdateMeshTaskForNode(): "
2544 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2548 v3s16 blockpos = getNodeBlockPos(nodepos);
2549 v3s16 blockpos_relative = blockpos * MAP_BLOCKSIZE;
2552 v3s16 p = blockpos + v3s16(0,0,0);
2553 addUpdateMeshTask(p, ack_to_server, urgent);
2555 catch(InvalidPositionException &e){}
2557 if(nodepos.X == blockpos_relative.X){
2559 v3s16 p = blockpos + v3s16(-1,0,0);
2560 addUpdateMeshTask(p, false, urgent);
2562 catch(InvalidPositionException &e){}
2564 if(nodepos.Y == blockpos_relative.Y){
2566 v3s16 p = blockpos + v3s16(0,-1,0);
2567 addUpdateMeshTask(p, false, urgent);
2569 catch(InvalidPositionException &e){}
2571 if(nodepos.Z == blockpos_relative.Z){
2573 v3s16 p = blockpos + v3s16(0,0,-1);
2574 addUpdateMeshTask(p, false, urgent);
2576 catch(InvalidPositionException &e){}
2580 ClientEvent Client::getClientEvent()
2582 if(m_client_event_queue.size() == 0)
2585 event.type = CE_NONE;
2588 return m_client_event_queue.pop_front();
2591 void Client::afterContentReceived()
2593 infostream<<"Client::afterContentReceived() started"<<std::endl;
2594 assert(m_itemdef_received);
2595 assert(m_nodedef_received);
2596 assert(texturesReceived());
2598 // remove the information about which checksum each texture
2600 m_media_name_sha1_map.clear();
2602 // Rebuild inherited images and recreate textures
2603 infostream<<"- Rebuilding images and textures"<<std::endl;
2604 m_tsrc->rebuildImagesAndTextures();
2606 // Update texture atlas
2607 infostream<<"- Updating texture atlas"<<std::endl;
2608 if(g_settings->getBool("enable_texture_atlas"))
2609 m_tsrc->buildMainAtlas(this);
2612 m_shsrc->rebuildShaders();
2614 // Update node aliases
2615 infostream<<"- Updating node aliases"<<std::endl;
2616 m_nodedef->updateAliases(m_itemdef);
2618 // Update node textures
2619 infostream<<"- Updating node textures"<<std::endl;
2620 m_nodedef->updateTextures(m_tsrc);
2622 // Preload item textures and meshes if configured to
2623 if(g_settings->getBool("preload_item_visuals"))
2625 verbosestream<<"Updating item textures and meshes"<<std::endl;
2626 std::set<std::string> names = m_itemdef->getAll();
2627 for(std::set<std::string>::const_iterator
2628 i = names.begin(); i != names.end(); ++i){
2629 // Asking for these caches the result
2630 m_itemdef->getInventoryTexture(*i, this);
2631 m_itemdef->getWieldMesh(*i, this);
2635 // Start mesh update thread after setting up content definitions
2636 infostream<<"- Starting mesh update thread"<<std::endl;
2637 m_mesh_update_thread.Start();
2639 infostream<<"Client::afterContentReceived() done"<<std::endl;
2642 float Client::getRTT(void)
2645 return m_con.GetPeerAvgRTT(PEER_ID_SERVER);
2646 } catch(con::PeerNotFoundException &e){
2651 // IGameDef interface
2653 IItemDefManager* Client::getItemDefManager()
2657 INodeDefManager* Client::getNodeDefManager()
2661 ICraftDefManager* Client::getCraftDefManager()
2664 //return m_craftdef;
2666 ITextureSource* Client::getTextureSource()
2670 IShaderSource* Client::getShaderSource()
2674 u16 Client::allocateUnknownNodeId(const std::string &name)
2676 errorstream<<"Client::allocateUnknownNodeId(): "
2677 <<"Client cannot allocate node IDs"<<std::endl;
2679 return CONTENT_IGNORE;
2681 ISoundManager* Client::getSoundManager()
2685 MtEventManager* Client::getEventManager()