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.
23 #include "clientserver.h"
24 #include "jmutexautolock.h"
28 #include "mapsector.h"
29 #include "mapblock_mesh.h"
34 #include "nodemetadata.h"
37 #include <IFileSystem.h>
40 #include "clientmap.h"
41 #include "filecache.h"
43 #include "util/string.h"
46 static std::string getMediaCacheDir()
48 return porting::path_user + DIR_DELIM + "cache" + DIR_DELIM + "media";
55 MediaRequest(const std::string &name_=""):
64 QueuedMeshUpdate::QueuedMeshUpdate():
67 ack_block_to_server(false)
71 QueuedMeshUpdate::~QueuedMeshUpdate()
81 MeshUpdateQueue::MeshUpdateQueue()
86 MeshUpdateQueue::~MeshUpdateQueue()
88 JMutexAutoLock lock(m_mutex);
90 for(std::vector<QueuedMeshUpdate*>::iterator
92 i != m_queue.end(); i++)
94 QueuedMeshUpdate *q = *i;
100 peer_id=0 adds with nobody to send to
102 void MeshUpdateQueue::addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_server, bool urgent)
104 DSTACK(__FUNCTION_NAME);
108 JMutexAutoLock lock(m_mutex);
114 Find if block is already in queue.
115 If it is, update the data and quit.
117 for(std::vector<QueuedMeshUpdate*>::iterator
119 i != m_queue.end(); i++)
121 QueuedMeshUpdate *q = *i;
127 if(ack_block_to_server)
128 q->ack_block_to_server = true;
136 QueuedMeshUpdate *q = new QueuedMeshUpdate;
139 q->ack_block_to_server = ack_block_to_server;
140 m_queue.push_back(q);
143 // Returned pointer must be deleted
144 // Returns NULL if queue is empty
145 QueuedMeshUpdate * MeshUpdateQueue::pop()
147 JMutexAutoLock lock(m_mutex);
149 bool must_be_urgent = !m_urgents.empty();
150 for(std::vector<QueuedMeshUpdate*>::iterator
152 i != m_queue.end(); i++)
154 QueuedMeshUpdate *q = *i;
155 if(must_be_urgent && m_urgents.count(q->p) == 0)
158 m_urgents.erase(q->p);
168 void * MeshUpdateThread::Thread()
172 log_register_thread("MeshUpdateThread");
174 DSTACK(__FUNCTION_NAME);
176 BEGIN_DEBUG_EXCEPTION_HANDLER
180 /*// Wait for output queue to flush.
181 // Allow 2 in queue, this makes less frametime jitter.
182 // Umm actually, there is no much difference
183 if(m_queue_out.size() >= 2)
189 QueuedMeshUpdate *q = m_queue_in.pop();
196 ScopeProfiler sp(g_profiler, "Client: Mesh making");
198 MapBlockMesh *mesh_new = new MapBlockMesh(q->data);
199 if(mesh_new->getMesh()->getMeshBufferCount() == 0)
208 r.ack_block_to_server = q->ack_block_to_server;
210 /*infostream<<"MeshUpdateThread: Processed "
211 <<"("<<q->p.X<<","<<q->p.Y<<","<<q->p.Z<<")"
214 m_queue_out.push_back(r);
219 END_DEBUG_EXCEPTION_HANDLER(errorstream)
225 IrrlichtDevice *device,
226 const char *playername,
227 std::string password,
228 MapDrawControl &control,
229 IWritableTextureSource *tsrc,
230 IWritableItemDefManager *itemdef,
231 IWritableNodeDefManager *nodedef,
232 ISoundManager *sound,
233 MtEventManager *event
240 m_mesh_update_thread(this),
242 new ClientMap(this, this, control,
243 device->getSceneManager()->getRootSceneNode(),
244 device->getSceneManager(), 666),
245 device->getSceneManager(),
248 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
250 m_server_ser_ver(SER_FMT_VER_INVALID),
252 m_inventory_updated(false),
253 m_inventory_from_server(NULL),
254 m_inventory_from_server_age(0.0),
259 m_password(password),
260 m_access_denied(false),
261 m_media_cache(getMediaCacheDir()),
262 m_media_receive_progress(0),
263 m_media_received(false),
264 m_itemdef_received(false),
265 m_nodedef_received(false),
266 m_time_of_day_set(false),
267 m_last_time_of_day_f(-1),
268 m_time_of_day_update_timer(0),
269 m_removed_sounds_check_timer(0)
271 m_packetcounter_timer = 0.0;
272 //m_delete_unused_sectors_timer = 0.0;
273 m_connection_reinit_timer = 0.0;
274 m_avg_rtt_timer = 0.0;
275 m_playerpos_send_timer = 0.0;
276 m_ignore_damage_timer = 0.0;
278 // Build main texture atlas, now that the GameDef exists (that is, us)
279 if(g_settings->getBool("enable_texture_atlas"))
280 m_tsrc->buildMainAtlas(this);
282 infostream<<"Not building texture atlas."<<std::endl;
288 Player *player = new LocalPlayer(this);
290 player->updateName(playername);
292 m_env.addPlayer(player);
299 //JMutexAutoLock conlock(m_con_mutex); //bulk comment-out
303 m_mesh_update_thread.setRun(false);
304 while(m_mesh_update_thread.IsRunning())
307 delete m_inventory_from_server;
310 void Client::connect(Address address)
312 DSTACK(__FUNCTION_NAME);
313 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
314 m_con.SetTimeoutMs(0);
315 m_con.Connect(address);
318 bool Client::connectedAndInitialized()
320 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
322 if(m_con.Connected() == false)
325 if(m_server_ser_ver == SER_FMT_VER_INVALID)
331 void Client::step(float dtime)
333 DSTACK(__FUNCTION_NAME);
339 if(m_ignore_damage_timer > dtime)
340 m_ignore_damage_timer -= dtime;
342 m_ignore_damage_timer = 0.0;
344 m_animation_time += dtime;
345 if(m_animation_time > 60.0)
346 m_animation_time -= 60.0;
348 m_time_of_day_update_timer += dtime;
350 //infostream<<"Client steps "<<dtime<<std::endl;
353 //TimeTaker timer("ReceiveAll()", m_device);
359 //TimeTaker timer("m_con_mutex + m_con.RunTimeouts()", m_device);
361 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
362 m_con.RunTimeouts(dtime);
369 float &counter = m_packetcounter_timer;
375 infostream<<"Client packetcounter (20s):"<<std::endl;
376 m_packetcounter.print(infostream);
377 m_packetcounter.clear();
381 // Get connection status
382 bool connected = connectedAndInitialized();
387 Delete unused sectors
389 NOTE: This jams the game for a while because deleting sectors
393 float &counter = m_delete_unused_sectors_timer;
401 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
403 core::list<v3s16> deleted_blocks;
405 float delete_unused_sectors_timeout =
406 g_settings->getFloat("client_delete_unused_sectors_timeout");
408 // Delete sector blocks
409 /*u32 num = m_env.getMap().unloadUnusedData
410 (delete_unused_sectors_timeout,
411 true, &deleted_blocks);*/
413 // Delete whole sectors
414 m_env.getMap().unloadUnusedData
415 (delete_unused_sectors_timeout,
418 if(deleted_blocks.size() > 0)
420 /*infostream<<"Client: Deleted blocks of "<<num
421 <<" unused sectors"<<std::endl;*/
422 /*infostream<<"Client: Deleted "<<num
423 <<" unused sectors"<<std::endl;*/
429 // Env is locked so con can be locked.
430 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
432 core::list<v3s16>::Iterator i = deleted_blocks.begin();
433 core::list<v3s16> sendlist;
436 if(sendlist.size() == 255 || i == deleted_blocks.end())
438 if(sendlist.size() == 0)
447 u32 replysize = 2+1+6*sendlist.size();
448 SharedBuffer<u8> reply(replysize);
449 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
450 reply[2] = sendlist.size();
452 for(core::list<v3s16>::Iterator
453 j = sendlist.begin();
454 j != sendlist.end(); j++)
456 writeV3S16(&reply[2+1+6*k], *j);
459 m_con.Send(PEER_ID_SERVER, 1, reply, true);
461 if(i == deleted_blocks.end())
467 sendlist.push_back(*i);
475 if(connected == false)
477 float &counter = m_connection_reinit_timer;
483 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
485 Player *myplayer = m_env.getLocalPlayer();
486 assert(myplayer != NULL);
488 // Send TOSERVER_INIT
489 // [0] u16 TOSERVER_INIT
490 // [2] u8 SER_FMT_VER_HIGHEST
491 // [3] u8[20] player_name
492 // [23] u8[28] password (new in some version)
493 // [51] u16 client network protocol version (new in some version)
494 SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2);
495 writeU16(&data[0], TOSERVER_INIT);
496 writeU8(&data[2], SER_FMT_VER_HIGHEST);
498 memset((char*)&data[3], 0, PLAYERNAME_SIZE);
499 snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
501 /*infostream<<"Client: sending initial password hash: \""<<m_password<<"\""
504 memset((char*)&data[23], 0, PASSWORD_SIZE);
505 snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
507 // This should be incremented in each version
508 writeU16(&data[51], PROTOCOL_VERSION);
510 // Send as unreliable
511 Send(0, data, false);
514 // Not connected, return
519 Do stuff if connected
523 Run Map's timers and unload unused data
525 const float map_timer_and_unload_dtime = 5.25;
526 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
528 ScopeProfiler sp(g_profiler, "Client: map timer and unload");
529 core::list<v3s16> deleted_blocks;
530 m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
531 g_settings->getFloat("client_unload_unused_data_timeout"),
534 /*if(deleted_blocks.size() > 0)
535 infostream<<"Client: Unloaded "<<deleted_blocks.size()
536 <<" unused blocks"<<std::endl;*/
540 NOTE: This loop is intentionally iterated the way it is.
543 core::list<v3s16>::Iterator i = deleted_blocks.begin();
544 core::list<v3s16> sendlist;
547 if(sendlist.size() == 255 || i == deleted_blocks.end())
549 if(sendlist.size() == 0)
558 u32 replysize = 2+1+6*sendlist.size();
559 SharedBuffer<u8> reply(replysize);
560 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
561 reply[2] = sendlist.size();
563 for(core::list<v3s16>::Iterator
564 j = sendlist.begin();
565 j != sendlist.end(); j++)
567 writeV3S16(&reply[2+1+6*k], *j);
570 m_con.Send(PEER_ID_SERVER, 1, reply, true);
572 if(i == deleted_blocks.end())
578 sendlist.push_back(*i);
588 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
590 // Control local player (0ms)
591 LocalPlayer *player = m_env.getLocalPlayer();
592 assert(player != NULL);
593 player->applyControl(dtime);
595 //TimeTaker envtimer("env step", m_device);
604 ClientEnvEvent event = m_env.getClientEvent();
605 if(event.type == CEE_NONE)
609 else if(event.type == CEE_PLAYER_DAMAGE)
611 if(m_ignore_damage_timer <= 0)
613 u8 damage = event.player_damage.amount;
615 if(event.player_damage.send_to_server)
618 // Add to ClientEvent queue
620 event.type = CE_PLAYER_DAMAGE;
621 event.player_damage.amount = damage;
622 m_client_event_queue.push_back(event);
632 float &counter = m_avg_rtt_timer;
637 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
638 // connectedAndInitialized() is true, peer exists.
639 float avg_rtt = m_con.GetPeerAvgRTT(PEER_ID_SERVER);
640 infostream<<"Client: avg_rtt="<<avg_rtt<<std::endl;
645 Send player position to server
648 float &counter = m_playerpos_send_timer;
658 Replace updated meshes
661 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
663 //TimeTaker timer("** Processing mesh update result queue");
666 /*infostream<<"Mesh update result queue size is "
667 <<m_mesh_update_thread.m_queue_out.size()
670 int num_processed_meshes = 0;
671 while(m_mesh_update_thread.m_queue_out.size() > 0)
673 num_processed_meshes++;
674 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_front();
675 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
678 //JMutexAutoLock lock(block->mesh_mutex);
680 // Delete the old mesh
681 if(block->mesh != NULL)
683 // TODO: Remove hardware buffers of meshbuffers of block->mesh
688 // Replace with the new mesh
689 block->mesh = r.mesh;
691 if(r.ack_block_to_server)
693 /*infostream<<"Client: ACK block ("<<r.p.X<<","<<r.p.Y
694 <<","<<r.p.Z<<")"<<std::endl;*/
705 u32 replysize = 2+1+6;
706 SharedBuffer<u8> reply(replysize);
707 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
709 writeV3S16(&reply[3], r.p);
711 m_con.Send(PEER_ID_SERVER, 1, reply, true);
714 if(num_processed_meshes > 0)
715 g_profiler->graphAdd("num_processed_meshes", num_processed_meshes);
719 If the server didn't update the inventory in a while, revert
720 the local inventory (so the player notices the lag problem
721 and knows something is wrong).
723 if(m_inventory_from_server)
725 float interval = 10.0;
726 float count_before = floor(m_inventory_from_server_age / interval);
728 m_inventory_from_server_age += dtime;
730 float count_after = floor(m_inventory_from_server_age / interval);
732 if(count_after != count_before)
734 // Do this every <interval> seconds after TOCLIENT_INVENTORY
735 // Reset the locally changed inventory to the authoritative inventory
736 Player *player = m_env.getLocalPlayer();
737 player->inventory = *m_inventory_from_server;
738 m_inventory_updated = true;
743 Update positions of sounds attached to objects
746 for(std::map<int, u16>::iterator
747 i = m_sounds_to_objects.begin();
748 i != m_sounds_to_objects.end(); i++)
750 int client_id = i->first;
751 u16 object_id = i->second;
752 ClientActiveObject *cao = m_env.getActiveObject(object_id);
755 v3f pos = cao->getPosition();
756 m_sound->updateSoundPosition(client_id, pos);
761 Handle removed remotely initiated sounds
763 m_removed_sounds_check_timer += dtime;
764 if(m_removed_sounds_check_timer >= 2.32)
766 m_removed_sounds_check_timer = 0;
767 // Find removed sounds and clear references to them
768 std::set<s32> removed_server_ids;
769 for(std::map<s32, int>::iterator
770 i = m_sounds_server_to_client.begin();
771 i != m_sounds_server_to_client.end();)
773 s32 server_id = i->first;
774 int client_id = i->second;
776 if(!m_sound->soundExists(client_id)){
777 m_sounds_server_to_client.erase(server_id);
778 m_sounds_client_to_server.erase(client_id);
779 m_sounds_to_objects.erase(client_id);
780 removed_server_ids.insert(server_id);
784 if(removed_server_ids.size() != 0)
786 std::ostringstream os(std::ios_base::binary);
787 writeU16(os, TOSERVER_REMOVED_SOUNDS);
788 writeU16(os, removed_server_ids.size());
789 for(std::set<s32>::iterator i = removed_server_ids.begin();
790 i != removed_server_ids.end(); i++)
792 std::string s = os.str();
793 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
800 bool Client::loadMedia(const std::string &data, const std::string &filename)
802 // Silly irrlicht's const-incorrectness
803 Buffer<char> data_rw(data.c_str(), data.size());
807 const char *image_ext[] = {
808 ".png", ".jpg", ".bmp", ".tga",
809 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
812 name = removeStringEnd(filename, image_ext);
815 verbosestream<<"Client: Attempting to load image "
816 <<"file \""<<filename<<"\""<<std::endl;
818 io::IFileSystem *irrfs = m_device->getFileSystem();
819 video::IVideoDriver *vdrv = m_device->getVideoDriver();
821 // Create an irrlicht memory file
822 io::IReadFile *rfile = irrfs->createMemoryReadFile(
823 *data_rw, data_rw.getSize(), "_tempreadfile");
826 video::IImage *img = vdrv->createImageFromFile(rfile);
828 errorstream<<"Client: Cannot create image from data of "
829 <<"file \""<<filename<<"\""<<std::endl;
834 m_tsrc->insertSourceImage(filename, img);
841 const char *sound_ext[] = {
842 ".0.ogg", ".1.ogg", ".2.ogg", ".3.ogg", ".4.ogg",
843 ".5.ogg", ".6.ogg", ".7.ogg", ".8.ogg", ".9.ogg",
846 name = removeStringEnd(filename, sound_ext);
849 verbosestream<<"Client: Attempting to load sound "
850 <<"file \""<<filename<<"\""<<std::endl;
851 m_sound->loadSoundData(name, data);
855 errorstream<<"Client: Don't know how to load file \""
856 <<filename<<"\""<<std::endl;
860 // Virtual methods from con::PeerHandler
861 void Client::peerAdded(con::Peer *peer)
863 infostream<<"Client::peerAdded(): peer->id="
864 <<peer->id<<std::endl;
866 void Client::deletingPeer(con::Peer *peer, bool timeout)
868 infostream<<"Client::deletingPeer(): "
869 "Server Peer is getting deleted "
870 <<"(timeout="<<timeout<<")"<<std::endl;
873 void Client::ReceiveAll()
875 DSTACK(__FUNCTION_NAME);
876 u32 start_ms = porting::getTimeMs();
879 // Limit time even if there would be huge amounts of data to
881 if(porting::getTimeMs() > start_ms + 100)
886 g_profiler->graphAdd("client_received_packets", 1);
888 catch(con::NoIncomingDataException &e)
892 catch(con::InvalidIncomingDataException &e)
894 infostream<<"Client::ReceiveAll(): "
895 "InvalidIncomingDataException: what()="
896 <<e.what()<<std::endl;
901 void Client::Receive()
903 DSTACK(__FUNCTION_NAME);
904 SharedBuffer<u8> data;
908 //TimeTaker t1("con mutex and receive", m_device);
909 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
910 datasize = m_con.Receive(sender_peer_id, data);
912 //TimeTaker t1("ProcessData", m_device);
913 ProcessData(*data, datasize, sender_peer_id);
917 sender_peer_id given to this shall be quaranteed to be a valid peer
919 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
921 DSTACK(__FUNCTION_NAME);
923 // Ignore packets that don't even fit a command
926 m_packetcounter.add(60000);
930 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
932 //infostream<<"Client: received command="<<command<<std::endl;
933 m_packetcounter.add((u16)command);
936 If this check is removed, be sure to change the queue
937 system to know the ids
939 if(sender_peer_id != PEER_ID_SERVER)
941 infostream<<"Client::ProcessData(): Discarding data not "
942 "coming from server: peer_id="<<sender_peer_id
947 u8 ser_version = m_server_ser_ver;
949 //infostream<<"Client received command="<<(int)command<<std::endl;
951 if(command == TOCLIENT_INIT)
956 u8 deployed = data[2];
958 infostream<<"Client: TOCLIENT_INIT received with "
959 "deployed="<<((int)deployed&0xff)<<std::endl;
961 if(deployed < SER_FMT_VER_LOWEST
962 || deployed > SER_FMT_VER_HIGHEST)
964 infostream<<"Client: TOCLIENT_INIT: Server sent "
965 <<"unsupported ser_fmt_ver"<<std::endl;
969 m_server_ser_ver = deployed;
971 // Get player position
972 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
973 if(datasize >= 2+1+6)
974 playerpos_s16 = readV3S16(&data[2+1]);
975 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
978 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
980 // Set player position
981 Player *player = m_env.getLocalPlayer();
982 assert(player != NULL);
983 player->setPosition(playerpos_f);
986 if(datasize >= 2+1+6+8)
989 m_map_seed = readU64(&data[2+1+6]);
990 infostream<<"Client: received map seed: "<<m_map_seed<<std::endl;
995 SharedBuffer<u8> reply(replysize);
996 writeU16(&reply[0], TOSERVER_INIT2);
998 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1003 if(command == TOCLIENT_ACCESS_DENIED)
1005 // The server didn't like our password. Note, this needs
1006 // to be processed even if the serialisation format has
1007 // not been agreed yet, the same as TOCLIENT_INIT.
1008 m_access_denied = true;
1009 m_access_denied_reason = L"Unknown";
1012 std::string datastring((char*)&data[2], datasize-2);
1013 std::istringstream is(datastring, std::ios_base::binary);
1014 m_access_denied_reason = deSerializeWideString(is);
1019 if(ser_version == SER_FMT_VER_INVALID)
1021 infostream<<"Client: Server serialization"
1022 " format invalid or not initialized."
1023 " Skipping incoming command="<<command<<std::endl;
1027 // Just here to avoid putting the two if's together when
1028 // making some copypasta
1031 if(command == TOCLIENT_REMOVENODE)
1036 p.X = readS16(&data[2]);
1037 p.Y = readS16(&data[4]);
1038 p.Z = readS16(&data[6]);
1040 //TimeTaker t1("TOCLIENT_REMOVENODE");
1044 else if(command == TOCLIENT_ADDNODE)
1046 if(datasize < 8 + MapNode::serializedLength(ser_version))
1050 p.X = readS16(&data[2]);
1051 p.Y = readS16(&data[4]);
1052 p.Z = readS16(&data[6]);
1054 //TimeTaker t1("TOCLIENT_ADDNODE");
1057 n.deSerialize(&data[8], ser_version);
1061 else if(command == TOCLIENT_BLOCKDATA)
1063 // Ignore too small packet
1068 p.X = readS16(&data[2]);
1069 p.Y = readS16(&data[4]);
1070 p.Z = readS16(&data[6]);
1072 /*infostream<<"Client: Thread: BLOCKDATA for ("
1073 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1074 /*infostream<<"Client: Thread: BLOCKDATA for ("
1075 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1077 std::string datastring((char*)&data[8], datasize-8);
1078 std::istringstream istr(datastring, std::ios_base::binary);
1083 v2s16 p2d(p.X, p.Z);
1084 sector = m_env.getMap().emergeSector(p2d);
1086 assert(sector->getPos() == p2d);
1088 //TimeTaker timer("MapBlock deSerialize");
1091 block = sector->getBlockNoCreateNoEx(p.Y);
1095 Update an existing block
1097 //infostream<<"Updating"<<std::endl;
1098 block->deSerialize(istr, ser_version, false);
1105 //infostream<<"Creating new"<<std::endl;
1106 block = new MapBlock(&m_env.getMap(), p, this);
1107 block->deSerialize(istr, ser_version, false);
1108 sector->insertBlock(block);
1122 u32 replysize = 2+1+6;
1123 SharedBuffer<u8> reply(replysize);
1124 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
1126 writeV3S16(&reply[3], p);
1128 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1132 Add it to mesh update queue and set it to be acknowledged after update.
1134 //infostream<<"Adding mesh update task for received block"<<std::endl;
1135 addUpdateMeshTaskWithEdge(p, true);
1137 else if(command == TOCLIENT_INVENTORY)
1142 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
1145 //TimeTaker t2("mutex locking", m_device);
1146 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1149 //TimeTaker t3("istringstream init", m_device);
1150 std::string datastring((char*)&data[2], datasize-2);
1151 std::istringstream is(datastring, std::ios_base::binary);
1154 //m_env.printPlayers(infostream);
1156 //TimeTaker t4("player get", m_device);
1157 Player *player = m_env.getLocalPlayer();
1158 assert(player != NULL);
1161 //TimeTaker t1("inventory.deSerialize()", m_device);
1162 player->inventory.deSerialize(is);
1165 m_inventory_updated = true;
1167 delete m_inventory_from_server;
1168 m_inventory_from_server = new Inventory(player->inventory);
1169 m_inventory_from_server_age = 0.0;
1171 //infostream<<"Client got player inventory:"<<std::endl;
1172 //player->inventory.print(infostream);
1175 else if(command == TOCLIENT_TIME_OF_DAY)
1180 u16 time_of_day = readU16(&data[2]);
1181 time_of_day = time_of_day % 24000;
1182 //infostream<<"Client: time_of_day="<<time_of_day<<std::endl;
1183 float time_speed = 0;
1184 if(datasize >= 2 + 2 + 4){
1185 time_speed = readF1000(&data[4]);
1187 // Old message; try to approximate speed of time by ourselves
1188 float time_of_day_f = (float)time_of_day / 24000.0;
1189 float tod_diff_f = 0;
1190 if(time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
1191 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0;
1193 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
1194 m_last_time_of_day_f = time_of_day_f;
1195 float time_diff = m_time_of_day_update_timer;
1196 m_time_of_day_update_timer = 0;
1197 if(m_time_of_day_set){
1198 time_speed = 3600.0*24.0 * tod_diff_f / time_diff;
1199 infostream<<"Client: Measured time_of_day speed (old format): "
1200 <<time_speed<<" tod_diff_f="<<tod_diff_f
1201 <<" time_diff="<<time_diff<<std::endl;
1205 // Update environment
1206 m_env.setTimeOfDay(time_of_day);
1207 m_env.setTimeOfDaySpeed(time_speed);
1208 m_time_of_day_set = true;
1210 u32 dr = m_env.getDayNightRatio();
1211 verbosestream<<"Client: time_of_day="<<time_of_day
1212 <<" time_speed="<<time_speed
1213 <<" dr="<<dr<<std::endl;
1215 else if(command == TOCLIENT_CHAT_MESSAGE)
1223 std::string datastring((char*)&data[2], datasize-2);
1224 std::istringstream is(datastring, std::ios_base::binary);
1227 is.read((char*)buf, 2);
1228 u16 len = readU16(buf);
1230 std::wstring message;
1231 for(u16 i=0; i<len; i++)
1233 is.read((char*)buf, 2);
1234 message += (wchar_t)readU16(buf);
1237 /*infostream<<"Client received chat message: "
1238 <<wide_to_narrow(message)<<std::endl;*/
1240 m_chat_queue.push_back(message);
1242 else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1244 //if(g_settings->getBool("enable_experimental"))
1248 u16 count of removed objects
1249 for all removed objects {
1252 u16 count of added objects
1253 for all added objects {
1256 u32 initialization data length
1257 string initialization data
1262 // Get all data except the command number
1263 std::string datastring((char*)&data[2], datasize-2);
1264 // Throw them in an istringstream
1265 std::istringstream is(datastring, std::ios_base::binary);
1269 // Read removed objects
1271 u16 removed_count = readU16((u8*)buf);
1272 for(u16 i=0; i<removed_count; i++)
1275 u16 id = readU16((u8*)buf);
1278 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1279 m_env.removeActiveObject(id);
1283 // Read added objects
1285 u16 added_count = readU16((u8*)buf);
1286 for(u16 i=0; i<added_count; i++)
1289 u16 id = readU16((u8*)buf);
1291 u8 type = readU8((u8*)buf);
1292 std::string data = deSerializeLongString(is);
1295 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1296 m_env.addActiveObject(id, type, data);
1301 else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1303 //if(g_settings->getBool("enable_experimental"))
1315 // Get all data except the command number
1316 std::string datastring((char*)&data[2], datasize-2);
1317 // Throw them in an istringstream
1318 std::istringstream is(datastring, std::ios_base::binary);
1320 while(is.eof() == false)
1324 u16 id = readU16((u8*)buf);
1328 u16 message_size = readU16((u8*)buf);
1329 std::string message;
1330 message.reserve(message_size);
1331 for(u16 i=0; i<message_size; i++)
1334 message.append(buf, 1);
1336 // Pass on to the environment
1338 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1339 m_env.processActiveObjectMessage(id, message);
1344 else if(command == TOCLIENT_HP)
1346 std::string datastring((char*)&data[2], datasize-2);
1347 std::istringstream is(datastring, std::ios_base::binary);
1348 Player *player = m_env.getLocalPlayer();
1349 assert(player != NULL);
1350 u8 oldhp = player->hp;
1356 // Add to ClientEvent queue
1358 event.type = CE_PLAYER_DAMAGE;
1359 event.player_damage.amount = oldhp - hp;
1360 m_client_event_queue.push_back(event);
1363 else if(command == TOCLIENT_MOVE_PLAYER)
1365 std::string datastring((char*)&data[2], datasize-2);
1366 std::istringstream is(datastring, std::ios_base::binary);
1367 Player *player = m_env.getLocalPlayer();
1368 assert(player != NULL);
1369 v3f pos = readV3F1000(is);
1370 f32 pitch = readF1000(is);
1371 f32 yaw = readF1000(is);
1372 player->setPosition(pos);
1373 /*player->setPitch(pitch);
1374 player->setYaw(yaw);*/
1376 infostream<<"Client got TOCLIENT_MOVE_PLAYER"
1377 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1383 Add to ClientEvent queue.
1384 This has to be sent to the main program because otherwise
1385 it would just force the pitch and yaw values to whatever
1386 the camera points to.
1389 event.type = CE_PLAYER_FORCE_MOVE;
1390 event.player_force_move.pitch = pitch;
1391 event.player_force_move.yaw = yaw;
1392 m_client_event_queue.push_back(event);
1394 // Ignore damage for a few seconds, so that the player doesn't
1395 // get damage from falling on ground
1396 m_ignore_damage_timer = 3.0;
1398 else if(command == TOCLIENT_PLAYERITEM)
1400 infostream<<"Client: WARNING: Ignoring TOCLIENT_PLAYERITEM"<<std::endl;
1402 else if(command == TOCLIENT_DEATHSCREEN)
1404 std::string datastring((char*)&data[2], datasize-2);
1405 std::istringstream is(datastring, std::ios_base::binary);
1407 bool set_camera_point_target = readU8(is);
1408 v3f camera_point_target = readV3F1000(is);
1411 event.type = CE_DEATHSCREEN;
1412 event.deathscreen.set_camera_point_target = set_camera_point_target;
1413 event.deathscreen.camera_point_target_x = camera_point_target.X;
1414 event.deathscreen.camera_point_target_y = camera_point_target.Y;
1415 event.deathscreen.camera_point_target_z = camera_point_target.Z;
1416 m_client_event_queue.push_back(event);
1418 else if(command == TOCLIENT_ANNOUNCE_MEDIA)
1420 std::string datastring((char*)&data[2], datasize-2);
1421 std::istringstream is(datastring, std::ios_base::binary);
1423 // Mesh update thread must be stopped while
1424 // updating content definitions
1425 assert(!m_mesh_update_thread.IsRunning());
1427 int num_files = readU16(is);
1429 verbosestream<<"Client received TOCLIENT_ANNOUNCE_MEDIA ("
1430 <<num_files<<" files)"<<std::endl;
1432 core::list<MediaRequest> file_requests;
1434 for(int i=0; i<num_files; i++)
1436 //read file from cache
1437 std::string name = deSerializeString(is);
1438 std::string sha1_base64 = deSerializeString(is);
1440 // if name contains illegal characters, ignore the file
1441 if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
1442 errorstream<<"Client: ignoring illegal file name "
1443 <<"sent by server: \""<<name<<"\""<<std::endl;
1447 std::string sha1_raw = base64_decode(sha1_base64);
1448 std::string sha1_hex = hex_encode(sha1_raw);
1449 std::ostringstream tmp_os(std::ios_base::binary);
1450 bool found_in_cache = m_media_cache.load_sha1(sha1_raw, tmp_os);
1451 m_media_name_sha1_map.set(name, sha1_raw);
1453 // If found in cache, try to load it from there
1456 bool success = loadMedia(tmp_os.str(), name);
1458 verbosestream<<"Client: Loaded cached media: "
1459 <<sha1_hex<<" \""<<name<<"\""<<std::endl;
1462 infostream<<"Client: Failed to load cached media: "
1463 <<sha1_hex<<" \""<<name<<"\""<<std::endl;
1466 // Didn't load from cache; queue it to be requested
1467 verbosestream<<"Client: Adding file to request list: \""
1468 <<sha1_hex<<" \""<<name<<"\""<<std::endl;
1469 file_requests.push_back(MediaRequest(name));
1473 event.type = CE_TEXTURES_UPDATED;
1474 m_client_event_queue.push_back(event);
1478 u16 number of files requested
1484 std::ostringstream os(std::ios_base::binary);
1485 writeU16(os, TOSERVER_REQUEST_MEDIA);
1486 writeU16(os, file_requests.size());
1488 for(core::list<MediaRequest>::Iterator i = file_requests.begin();
1489 i != file_requests.end(); i++) {
1490 os<<serializeString(i->name);
1494 std::string s = os.str();
1495 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1497 Send(0, data, true);
1498 infostream<<"Client: Sending media request list to server ("
1499 <<file_requests.size()<<" files)"<<std::endl;
1501 else if(command == TOCLIENT_MEDIA)
1503 std::string datastring((char*)&data[2], datasize-2);
1504 std::istringstream is(datastring, std::ios_base::binary);
1506 // Mesh update thread must be stopped while
1507 // updating content definitions
1508 assert(!m_mesh_update_thread.IsRunning());
1512 u16 total number of file bunches
1513 u16 index of this bunch
1514 u32 number of files in this bunch
1522 int num_bunches = readU16(is);
1523 int bunch_i = readU16(is);
1524 if(num_bunches >= 2)
1525 m_media_receive_progress = (float)bunch_i / (float)(num_bunches - 1);
1527 m_media_receive_progress = 1.0;
1528 if(bunch_i == num_bunches - 1)
1529 m_media_received = true;
1530 int num_files = readU32(is);
1531 infostream<<"Client: Received files: bunch "<<bunch_i<<"/"
1532 <<num_bunches<<" files="<<num_files
1533 <<" size="<<datasize<<std::endl;
1534 for(int i=0; i<num_files; i++){
1535 std::string name = deSerializeString(is);
1536 std::string data = deSerializeLongString(is);
1538 // if name contains illegal characters, ignore the file
1539 if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
1540 errorstream<<"Client: ignoring illegal file name "
1541 <<"sent by server: \""<<name<<"\""<<std::endl;
1545 bool success = loadMedia(data, name);
1547 verbosestream<<"Client: Loaded received media: "
1548 <<"\""<<name<<"\". Caching."<<std::endl;
1550 infostream<<"Client: Failed to load received media: "
1551 <<"\""<<name<<"\". Not caching."<<std::endl;
1555 bool did = fs::CreateAllDirs(getMediaCacheDir());
1557 errorstream<<"Could not create media cache directory"
1562 core::map<std::string, std::string>::Node *n;
1563 n = m_media_name_sha1_map.find(name);
1565 errorstream<<"The server sent a file that has not "
1566 <<"been announced."<<std::endl;
1568 m_media_cache.update_sha1(data);
1573 event.type = CE_TEXTURES_UPDATED;
1574 m_client_event_queue.push_back(event);
1576 else if(command == TOCLIENT_TOOLDEF)
1578 infostream<<"Client: WARNING: Ignoring TOCLIENT_TOOLDEF"<<std::endl;
1580 else if(command == TOCLIENT_NODEDEF)
1582 infostream<<"Client: Received node definitions: packet size: "
1583 <<datasize<<std::endl;
1585 // Mesh update thread must be stopped while
1586 // updating content definitions
1587 assert(!m_mesh_update_thread.IsRunning());
1589 // Decompress node definitions
1590 std::string datastring((char*)&data[2], datasize-2);
1591 std::istringstream is(datastring, std::ios_base::binary);
1592 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1593 std::ostringstream tmp_os;
1594 decompressZlib(tmp_is, tmp_os);
1596 // Deserialize node definitions
1597 std::istringstream tmp_is2(tmp_os.str());
1598 m_nodedef->deSerialize(tmp_is2);
1599 m_nodedef_received = true;
1601 else if(command == TOCLIENT_CRAFTITEMDEF)
1603 infostream<<"Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF"<<std::endl;
1605 else if(command == TOCLIENT_ITEMDEF)
1607 infostream<<"Client: Received item definitions: packet size: "
1608 <<datasize<<std::endl;
1610 // Mesh update thread must be stopped while
1611 // updating content definitions
1612 assert(!m_mesh_update_thread.IsRunning());
1614 // Decompress item definitions
1615 std::string datastring((char*)&data[2], datasize-2);
1616 std::istringstream is(datastring, std::ios_base::binary);
1617 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1618 std::ostringstream tmp_os;
1619 decompressZlib(tmp_is, tmp_os);
1621 // Deserialize node definitions
1622 std::istringstream tmp_is2(tmp_os.str());
1623 m_itemdef->deSerialize(tmp_is2);
1624 m_itemdef_received = true;
1626 else if(command == TOCLIENT_PLAY_SOUND)
1628 std::string datastring((char*)&data[2], datasize-2);
1629 std::istringstream is(datastring, std::ios_base::binary);
1631 s32 server_id = readS32(is);
1632 std::string name = deSerializeString(is);
1633 float gain = readF1000(is);
1634 int type = readU8(is); // 0=local, 1=positional, 2=object
1635 v3f pos = readV3F1000(is);
1636 u16 object_id = readU16(is);
1637 bool loop = readU8(is);
1642 client_id = m_sound->playSound(name, loop, gain);
1644 case 1: // positional
1645 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1648 ClientActiveObject *cao = m_env.getActiveObject(object_id);
1650 pos = cao->getPosition();
1651 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1652 // TODO: Set up sound to move with object
1657 if(client_id != -1){
1658 m_sounds_server_to_client[server_id] = client_id;
1659 m_sounds_client_to_server[client_id] = server_id;
1661 m_sounds_to_objects[client_id] = object_id;
1664 else if(command == TOCLIENT_STOP_SOUND)
1666 std::string datastring((char*)&data[2], datasize-2);
1667 std::istringstream is(datastring, std::ios_base::binary);
1669 s32 server_id = readS32(is);
1670 std::map<s32, int>::iterator i =
1671 m_sounds_server_to_client.find(server_id);
1672 if(i != m_sounds_server_to_client.end()){
1673 int client_id = i->second;
1674 m_sound->stopSound(client_id);
1677 else if(command == TOCLIENT_PRIVILEGES)
1679 std::string datastring((char*)&data[2], datasize-2);
1680 std::istringstream is(datastring, std::ios_base::binary);
1682 m_privileges.clear();
1683 infostream<<"Client: Privileges updated: ";
1684 u16 num_privileges = readU16(is);
1685 for(u16 i=0; i<num_privileges; i++){
1686 std::string priv = deSerializeString(is);
1687 m_privileges.insert(priv);
1688 infostream<<priv<<" ";
1690 infostream<<std::endl;
1694 infostream<<"Client: Ignoring unknown command "
1695 <<command<<std::endl;
1699 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
1701 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1702 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
1705 void Client::interact(u8 action, const PointedThing& pointed)
1707 if(connectedAndInitialized() == false){
1708 infostream<<"Client::interact() "
1709 "cancelled (not connected)"
1714 std::ostringstream os(std::ios_base::binary);
1720 [5] u32 length of the next item
1721 [9] serialized PointedThing
1723 0: start digging (from undersurface) or use
1724 1: stop digging (all parameters ignored)
1725 2: digging completed
1726 3: place block or item (to abovesurface)
1729 writeU16(os, TOSERVER_INTERACT);
1730 writeU8(os, action);
1731 writeU16(os, getPlayerItem());
1732 std::ostringstream tmp_os(std::ios::binary);
1733 pointed.serialize(tmp_os);
1734 os<<serializeLongString(tmp_os.str());
1736 std::string s = os.str();
1737 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1740 Send(0, data, true);
1743 void Client::sendNodemetaFields(v3s16 p, const std::string &formname,
1744 const std::map<std::string, std::string> &fields)
1746 std::ostringstream os(std::ios_base::binary);
1748 writeU16(os, TOSERVER_NODEMETA_FIELDS);
1750 os<<serializeString(formname);
1751 writeU16(os, fields.size());
1752 for(std::map<std::string, std::string>::const_iterator
1753 i = fields.begin(); i != fields.end(); i++){
1754 const std::string &name = i->first;
1755 const std::string &value = i->second;
1756 os<<serializeString(name);
1757 os<<serializeLongString(value);
1761 std::string s = os.str();
1762 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1764 Send(0, data, true);
1767 void Client::sendInventoryAction(InventoryAction *a)
1769 std::ostringstream os(std::ios_base::binary);
1773 writeU16(buf, TOSERVER_INVENTORY_ACTION);
1774 os.write((char*)buf, 2);
1779 std::string s = os.str();
1780 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1782 Send(0, data, true);
1785 void Client::sendChatMessage(const std::wstring &message)
1787 std::ostringstream os(std::ios_base::binary);
1791 writeU16(buf, TOSERVER_CHAT_MESSAGE);
1792 os.write((char*)buf, 2);
1795 writeU16(buf, message.size());
1796 os.write((char*)buf, 2);
1799 for(u32 i=0; i<message.size(); i++)
1803 os.write((char*)buf, 2);
1807 std::string s = os.str();
1808 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1810 Send(0, data, true);
1813 void Client::sendChangePassword(const std::wstring oldpassword,
1814 const std::wstring newpassword)
1816 Player *player = m_env.getLocalPlayer();
1820 std::string playername = player->getName();
1821 std::string oldpwd = translatePassword(playername, oldpassword);
1822 std::string newpwd = translatePassword(playername, newpassword);
1824 std::ostringstream os(std::ios_base::binary);
1825 u8 buf[2+PASSWORD_SIZE*2];
1827 [0] u16 TOSERVER_PASSWORD
1828 [2] u8[28] old password
1829 [30] u8[28] new password
1832 writeU16(buf, TOSERVER_PASSWORD);
1833 for(u32 i=0;i<PASSWORD_SIZE-1;i++)
1835 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
1836 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
1838 buf[2+PASSWORD_SIZE-1] = 0;
1839 buf[30+PASSWORD_SIZE-1] = 0;
1840 os.write((char*)buf, 2+PASSWORD_SIZE*2);
1843 std::string s = os.str();
1844 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1846 Send(0, data, true);
1850 void Client::sendDamage(u8 damage)
1852 DSTACK(__FUNCTION_NAME);
1853 std::ostringstream os(std::ios_base::binary);
1855 writeU16(os, TOSERVER_DAMAGE);
1856 writeU8(os, damage);
1859 std::string s = os.str();
1860 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1862 Send(0, data, true);
1865 void Client::sendRespawn()
1867 DSTACK(__FUNCTION_NAME);
1868 std::ostringstream os(std::ios_base::binary);
1870 writeU16(os, TOSERVER_RESPAWN);
1873 std::string s = os.str();
1874 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1876 Send(0, data, true);
1879 void Client::sendPlayerPos()
1881 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1883 Player *myplayer = m_env.getLocalPlayer();
1884 if(myplayer == NULL)
1889 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1890 our_peer_id = m_con.GetPeerID();
1893 // Set peer id if not set already
1894 if(myplayer->peer_id == PEER_ID_INEXISTENT)
1895 myplayer->peer_id = our_peer_id;
1896 // Check that an existing peer_id is the same as the connection's
1897 assert(myplayer->peer_id == our_peer_id);
1899 v3f pf = myplayer->getPosition();
1900 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
1901 v3f sf = myplayer->getSpeed();
1902 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
1903 s32 pitch = myplayer->getPitch() * 100;
1904 s32 yaw = myplayer->getYaw() * 100;
1909 [2] v3s32 position*100
1910 [2+12] v3s32 speed*100
1911 [2+12+12] s32 pitch*100
1912 [2+12+12+4] s32 yaw*100
1915 SharedBuffer<u8> data(2+12+12+4+4);
1916 writeU16(&data[0], TOSERVER_PLAYERPOS);
1917 writeV3S32(&data[2], position);
1918 writeV3S32(&data[2+12], speed);
1919 writeS32(&data[2+12+12], pitch);
1920 writeS32(&data[2+12+12+4], yaw);
1922 // Send as unreliable
1923 Send(0, data, false);
1926 void Client::sendPlayerItem(u16 item)
1928 Player *myplayer = m_env.getLocalPlayer();
1929 if(myplayer == NULL)
1932 u16 our_peer_id = m_con.GetPeerID();
1934 // Set peer id if not set already
1935 if(myplayer->peer_id == PEER_ID_INEXISTENT)
1936 myplayer->peer_id = our_peer_id;
1937 // Check that an existing peer_id is the same as the connection's
1938 assert(myplayer->peer_id == our_peer_id);
1940 SharedBuffer<u8> data(2+2);
1941 writeU16(&data[0], TOSERVER_PLAYERITEM);
1942 writeU16(&data[2], item);
1945 Send(0, data, true);
1948 void Client::removeNode(v3s16 p)
1950 core::map<v3s16, MapBlock*> modified_blocks;
1954 //TimeTaker t("removeNodeAndUpdate", m_device);
1955 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
1957 catch(InvalidPositionException &e)
1961 // add urgent task to update the modified node
1962 addUpdateMeshTaskForNode(p, false, true);
1964 for(core::map<v3s16, MapBlock * >::Iterator
1965 i = modified_blocks.getIterator();
1966 i.atEnd() == false; i++)
1968 v3s16 p = i.getNode()->getKey();
1969 addUpdateMeshTaskWithEdge(p);
1973 void Client::addNode(v3s16 p, MapNode n)
1975 TimeTaker timer1("Client::addNode()");
1977 core::map<v3s16, MapBlock*> modified_blocks;
1981 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
1982 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks);
1984 catch(InvalidPositionException &e)
1987 for(core::map<v3s16, MapBlock * >::Iterator
1988 i = modified_blocks.getIterator();
1989 i.atEnd() == false; i++)
1991 v3s16 p = i.getNode()->getKey();
1992 addUpdateMeshTaskWithEdge(p);
1996 void Client::setPlayerControl(PlayerControl &control)
1998 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1999 LocalPlayer *player = m_env.getLocalPlayer();
2000 assert(player != NULL);
2001 player->control = control;
2004 void Client::selectPlayerItem(u16 item)
2006 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2007 m_playeritem = item;
2008 m_inventory_updated = true;
2009 sendPlayerItem(item);
2012 // Returns true if the inventory of the local player has been
2013 // updated from the server. If it is true, it is set to false.
2014 bool Client::getLocalInventoryUpdated()
2016 // m_inventory_updated is behind envlock
2017 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2018 bool updated = m_inventory_updated;
2019 m_inventory_updated = false;
2023 // Copies the inventory of the local player to parameter
2024 void Client::getLocalInventory(Inventory &dst)
2026 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2027 Player *player = m_env.getLocalPlayer();
2028 assert(player != NULL);
2029 dst = player->inventory;
2032 Inventory* Client::getInventory(const InventoryLocation &loc)
2035 case InventoryLocation::UNDEFINED:
2038 case InventoryLocation::CURRENT_PLAYER:
2040 Player *player = m_env.getLocalPlayer();
2041 assert(player != NULL);
2042 return &player->inventory;
2045 case InventoryLocation::PLAYER:
2047 Player *player = m_env.getPlayer(loc.name.c_str());
2050 return &player->inventory;
2053 case InventoryLocation::NODEMETA:
2055 NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p);
2058 return meta->getInventory();
2066 void Client::inventoryAction(InventoryAction *a)
2069 Send it to the server
2071 sendInventoryAction(a);
2074 Predict some local inventory changes
2076 a->clientApply(this, this);
2079 ClientActiveObject * Client::getSelectedActiveObject(
2081 v3f from_pos_f_on_map,
2082 core::line3d<f32> shootline_on_map
2085 core::array<DistanceSortedActiveObject> objects;
2087 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
2089 //infostream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
2092 // After this, the closest object is the first in the array.
2095 for(u32 i=0; i<objects.size(); i++)
2097 ClientActiveObject *obj = objects[i].obj;
2099 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
2100 if(selection_box == NULL)
2103 v3f pos = obj->getPosition();
2105 core::aabbox3d<f32> offsetted_box(
2106 selection_box->MinEdge + pos,
2107 selection_box->MaxEdge + pos
2110 if(offsetted_box.intersectsWithLine(shootline_on_map))
2112 //infostream<<"Returning selected object"<<std::endl;
2117 //infostream<<"No object selected; returning NULL."<<std::endl;
2121 void Client::printDebugInfo(std::ostream &os)
2123 //JMutexAutoLock lock1(m_fetchblock_mutex);
2124 /*JMutexAutoLock lock2(m_incoming_queue_mutex);
2126 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
2127 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
2128 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
2132 core::list<std::wstring> Client::getConnectedPlayerNames()
2134 core::list<Player*> players = m_env.getPlayers(true);
2135 core::list<std::wstring> playerNames;
2136 for(core::list<Player*>::Iterator
2137 i = players.begin();
2138 i != players.end(); i++)
2140 Player *player = *i;
2141 playerNames.push_back(narrow_to_wide(player->getName()));
2146 float Client::getAnimationTime()
2148 return m_animation_time;
2151 int Client::getCrackLevel()
2153 return m_crack_level;
2156 void Client::setCrack(int level, v3s16 pos)
2158 int old_crack_level = m_crack_level;
2159 v3s16 old_crack_pos = m_crack_pos;
2161 m_crack_level = level;
2164 if(old_crack_level >= 0 && (level < 0 || pos != old_crack_pos))
2167 addUpdateMeshTaskForNode(old_crack_pos, false, true);
2169 if(level >= 0 && (old_crack_level < 0 || pos != old_crack_pos))
2172 addUpdateMeshTaskForNode(pos, false, true);
2178 Player *player = m_env.getLocalPlayer();
2179 assert(player != NULL);
2183 bool Client::getChatMessage(std::wstring &message)
2185 if(m_chat_queue.size() == 0)
2187 message = m_chat_queue.pop_front();
2191 void Client::typeChatMessage(const std::wstring &message)
2193 // Discard empty line
2198 sendChatMessage(message);
2201 if (message[0] == L'/')
2203 m_chat_queue.push_back(
2204 (std::wstring)L"issued command: "+message);
2208 LocalPlayer *player = m_env.getLocalPlayer();
2209 assert(player != NULL);
2210 std::wstring name = narrow_to_wide(player->getName());
2211 m_chat_queue.push_back(
2212 (std::wstring)L"<"+name+L"> "+message);
2216 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
2218 /*infostream<<"Client::addUpdateMeshTask(): "
2219 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2220 <<" ack_to_server="<<ack_to_server
2221 <<" urgent="<<urgent
2224 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2229 Create a task to update the mesh of the block
2232 MeshMakeData *data = new MeshMakeData(this);
2235 //TimeTaker timer("data fill");
2237 // Debug: 1-6ms, avg=2ms
2239 data->setCrack(m_crack_level, m_crack_pos);
2240 data->setSmoothLighting(g_settings->getBool("smooth_lighting"));
2244 //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
2246 // Add task to queue
2247 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server, urgent);
2249 /*infostream<<"Mesh update input queue size is "
2250 <<m_mesh_update_thread.m_queue_in.size()
2254 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent)
2258 infostream<<"Client::addUpdateMeshTaskWithEdge(): "
2259 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2264 v3s16 p = blockpos + v3s16(0,0,0);
2265 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2266 addUpdateMeshTask(p, ack_to_server, urgent);
2268 catch(InvalidPositionException &e){}
2271 v3s16 p = blockpos + v3s16(-1,0,0);
2272 addUpdateMeshTask(p, false, urgent);
2274 catch(InvalidPositionException &e){}
2276 v3s16 p = blockpos + v3s16(0,-1,0);
2277 addUpdateMeshTask(p, false, urgent);
2279 catch(InvalidPositionException &e){}
2281 v3s16 p = blockpos + v3s16(0,0,-1);
2282 addUpdateMeshTask(p, false, urgent);
2284 catch(InvalidPositionException &e){}
2287 void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool urgent)
2291 infostream<<"Client::addUpdateMeshTaskForNode(): "
2292 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2296 v3s16 blockpos = getNodeBlockPos(nodepos);
2297 v3s16 blockpos_relative = blockpos * MAP_BLOCKSIZE;
2300 v3s16 p = blockpos + v3s16(0,0,0);
2301 addUpdateMeshTask(p, ack_to_server, urgent);
2303 catch(InvalidPositionException &e){}
2305 if(nodepos.X == blockpos_relative.X){
2307 v3s16 p = blockpos + v3s16(-1,0,0);
2308 addUpdateMeshTask(p, false, urgent);
2310 catch(InvalidPositionException &e){}
2312 if(nodepos.Y == blockpos_relative.Y){
2314 v3s16 p = blockpos + v3s16(0,-1,0);
2315 addUpdateMeshTask(p, false, urgent);
2317 catch(InvalidPositionException &e){}
2319 if(nodepos.Z == blockpos_relative.Z){
2321 v3s16 p = blockpos + v3s16(0,0,-1);
2322 addUpdateMeshTask(p, false, urgent);
2324 catch(InvalidPositionException &e){}
2328 ClientEvent Client::getClientEvent()
2330 if(m_client_event_queue.size() == 0)
2333 event.type = CE_NONE;
2336 return m_client_event_queue.pop_front();
2339 void Client::afterContentReceived()
2341 verbosestream<<"Client::afterContentReceived() started"<<std::endl;
2342 assert(m_itemdef_received);
2343 assert(m_nodedef_received);
2344 assert(m_media_received);
2346 // remove the information about which checksum each texture
2348 m_media_name_sha1_map.clear();
2350 // Rebuild inherited images and recreate textures
2351 verbosestream<<"Rebuilding images and textures"<<std::endl;
2352 m_tsrc->rebuildImagesAndTextures();
2354 // Update texture atlas
2355 verbosestream<<"Updating texture atlas"<<std::endl;
2356 if(g_settings->getBool("enable_texture_atlas"))
2357 m_tsrc->buildMainAtlas(this);
2359 // Update node aliases
2360 verbosestream<<"Updating node aliases"<<std::endl;
2361 m_nodedef->updateAliases(m_itemdef);
2363 // Update node textures
2364 verbosestream<<"Updating node textures"<<std::endl;
2365 m_nodedef->updateTextures(m_tsrc);
2367 // Update item textures and meshes
2368 verbosestream<<"Updating item textures and meshes"<<std::endl;
2369 m_itemdef->updateTexturesAndMeshes(this);
2371 // Start mesh update thread after setting up content definitions
2372 verbosestream<<"Starting mesh update thread"<<std::endl;
2373 m_mesh_update_thread.Start();
2375 verbosestream<<"Client::afterContentReceived() done"<<std::endl;
2378 float Client::getRTT(void)
2381 return m_con.GetPeerAvgRTT(PEER_ID_SERVER);
2382 } catch(con::PeerNotFoundException &e){
2387 // IGameDef interface
2389 IItemDefManager* Client::getItemDefManager()
2393 INodeDefManager* Client::getNodeDefManager()
2397 ICraftDefManager* Client::getCraftDefManager()
2400 //return m_craftdef;
2402 ITextureSource* Client::getTextureSource()
2406 u16 Client::allocateUnknownNodeId(const std::string &name)
2408 errorstream<<"Client::allocateUnknownNodeId(): "
2409 <<"Client cannot allocate node IDs"<<std::endl;
2411 return CONTENT_IGNORE;
2413 ISoundManager* Client::getSoundManager()
2417 MtEventManager* Client::getEventManager()