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 General Public License as published by
7 the Free Software Foundation; either version 2 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 General Public License for more details.
15 You should have received a copy of the GNU 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 "utility_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 std::string datastring((char*)&data[2], datasize-2);
1401 std::istringstream is(datastring, std::ios_base::binary);
1403 u16 count = readU16(is);
1405 for (u16 i = 0; i < count; ++i) {
1406 u16 peer_id = readU16(is);
1407 Player *player = m_env.getPlayer(peer_id);
1411 infostream<<"Client: ignoring player item "
1412 << deSerializeString(is)
1413 << " for non-existing peer id " << peer_id
1416 } else if (player->isLocal()) {
1417 infostream<<"Client: ignoring player item "
1418 << deSerializeString(is)
1419 << " for local player" << std::endl;
1422 InventoryList *inv = player->inventory.getList("main");
1423 std::string itemstring(deSerializeString(is));
1425 item.deSerialize(itemstring, m_itemdef);
1426 inv->changeItem(0, item);
1427 if(itemstring.empty())
1429 infostream<<"Client: empty player item for peer "
1430 <<peer_id<<std::endl;
1434 infostream<<"Client: player item for peer "
1435 <<peer_id<<": "<<itemstring<<std::endl;
1440 else if(command == TOCLIENT_DEATHSCREEN)
1442 std::string datastring((char*)&data[2], datasize-2);
1443 std::istringstream is(datastring, std::ios_base::binary);
1445 bool set_camera_point_target = readU8(is);
1446 v3f camera_point_target = readV3F1000(is);
1449 event.type = CE_DEATHSCREEN;
1450 event.deathscreen.set_camera_point_target = set_camera_point_target;
1451 event.deathscreen.camera_point_target_x = camera_point_target.X;
1452 event.deathscreen.camera_point_target_y = camera_point_target.Y;
1453 event.deathscreen.camera_point_target_z = camera_point_target.Z;
1454 m_client_event_queue.push_back(event);
1456 else if(command == TOCLIENT_ANNOUNCE_MEDIA)
1458 std::string datastring((char*)&data[2], datasize-2);
1459 std::istringstream is(datastring, std::ios_base::binary);
1461 // Mesh update thread must be stopped while
1462 // updating content definitions
1463 assert(!m_mesh_update_thread.IsRunning());
1465 int num_files = readU16(is);
1467 verbosestream<<"Client received TOCLIENT_ANNOUNCE_MEDIA ("
1468 <<num_files<<" files)"<<std::endl;
1470 core::list<MediaRequest> file_requests;
1472 for(int i=0; i<num_files; i++)
1474 //read file from cache
1475 std::string name = deSerializeString(is);
1476 std::string sha1_base64 = deSerializeString(is);
1478 // if name contains illegal characters, ignore the file
1479 if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
1480 errorstream<<"Client: ignoring illegal file name "
1481 <<"sent by server: \""<<name<<"\""<<std::endl;
1485 std::string sha1_raw = base64_decode(sha1_base64);
1486 std::string sha1_hex = hex_encode(sha1_raw);
1487 std::ostringstream tmp_os(std::ios_base::binary);
1488 bool found_in_cache = m_media_cache.load_sha1(sha1_raw, tmp_os);
1489 m_media_name_sha1_map.set(name, sha1_raw);
1491 // If found in cache, try to load it from there
1494 bool success = loadMedia(tmp_os.str(), name);
1496 verbosestream<<"Client: Loaded cached media: "
1497 <<sha1_hex<<" \""<<name<<"\""<<std::endl;
1500 infostream<<"Client: Failed to load cached media: "
1501 <<sha1_hex<<" \""<<name<<"\""<<std::endl;
1504 // Didn't load from cache; queue it to be requested
1505 verbosestream<<"Client: Adding file to request list: \""
1506 <<sha1_hex<<" \""<<name<<"\""<<std::endl;
1507 file_requests.push_back(MediaRequest(name));
1511 event.type = CE_TEXTURES_UPDATED;
1512 m_client_event_queue.push_back(event);
1516 u16 number of files requested
1522 std::ostringstream os(std::ios_base::binary);
1523 writeU16(os, TOSERVER_REQUEST_MEDIA);
1524 writeU16(os, file_requests.size());
1526 for(core::list<MediaRequest>::Iterator i = file_requests.begin();
1527 i != file_requests.end(); i++) {
1528 os<<serializeString(i->name);
1532 std::string s = os.str();
1533 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1535 Send(0, data, true);
1536 infostream<<"Client: Sending media request list to server ("
1537 <<file_requests.size()<<" files)"<<std::endl;
1539 else if(command == TOCLIENT_MEDIA)
1541 std::string datastring((char*)&data[2], datasize-2);
1542 std::istringstream is(datastring, std::ios_base::binary);
1544 // Mesh update thread must be stopped while
1545 // updating content definitions
1546 assert(!m_mesh_update_thread.IsRunning());
1550 u16 total number of file bunches
1551 u16 index of this bunch
1552 u32 number of files in this bunch
1560 int num_bunches = readU16(is);
1561 int bunch_i = readU16(is);
1562 if(num_bunches >= 2)
1563 m_media_receive_progress = (float)bunch_i / (float)(num_bunches - 1);
1565 m_media_receive_progress = 1.0;
1566 if(bunch_i == num_bunches - 1)
1567 m_media_received = true;
1568 int num_files = readU32(is);
1569 infostream<<"Client: Received files: bunch "<<bunch_i<<"/"
1570 <<num_bunches<<" files="<<num_files
1571 <<" size="<<datasize<<std::endl;
1572 for(int i=0; i<num_files; i++){
1573 std::string name = deSerializeString(is);
1574 std::string data = deSerializeLongString(is);
1576 // if name contains illegal characters, ignore the file
1577 if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
1578 errorstream<<"Client: ignoring illegal file name "
1579 <<"sent by server: \""<<name<<"\""<<std::endl;
1583 bool success = loadMedia(data, name);
1585 verbosestream<<"Client: Loaded received media: "
1586 <<"\""<<name<<"\". Caching."<<std::endl;
1588 infostream<<"Client: Failed to load received media: "
1589 <<"\""<<name<<"\". Not caching."<<std::endl;
1593 bool did = fs::CreateAllDirs(getMediaCacheDir());
1595 errorstream<<"Could not create media cache directory"
1600 core::map<std::string, std::string>::Node *n;
1601 n = m_media_name_sha1_map.find(name);
1603 errorstream<<"The server sent a file that has not "
1604 <<"been announced."<<std::endl;
1606 m_media_cache.update_sha1(data);
1611 event.type = CE_TEXTURES_UPDATED;
1612 m_client_event_queue.push_back(event);
1614 else if(command == TOCLIENT_TOOLDEF)
1616 infostream<<"Client: WARNING: Ignoring TOCLIENT_TOOLDEF"<<std::endl;
1618 else if(command == TOCLIENT_NODEDEF)
1620 infostream<<"Client: Received node definitions: packet size: "
1621 <<datasize<<std::endl;
1623 // Mesh update thread must be stopped while
1624 // updating content definitions
1625 assert(!m_mesh_update_thread.IsRunning());
1627 // Decompress node definitions
1628 std::string datastring((char*)&data[2], datasize-2);
1629 std::istringstream is(datastring, std::ios_base::binary);
1630 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1631 std::ostringstream tmp_os;
1632 decompressZlib(tmp_is, tmp_os);
1634 // Deserialize node definitions
1635 std::istringstream tmp_is2(tmp_os.str());
1636 m_nodedef->deSerialize(tmp_is2);
1637 m_nodedef_received = true;
1639 else if(command == TOCLIENT_CRAFTITEMDEF)
1641 infostream<<"Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF"<<std::endl;
1643 else if(command == TOCLIENT_ITEMDEF)
1645 infostream<<"Client: Received item definitions: packet size: "
1646 <<datasize<<std::endl;
1648 // Mesh update thread must be stopped while
1649 // updating content definitions
1650 assert(!m_mesh_update_thread.IsRunning());
1652 // Decompress item definitions
1653 std::string datastring((char*)&data[2], datasize-2);
1654 std::istringstream is(datastring, std::ios_base::binary);
1655 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1656 std::ostringstream tmp_os;
1657 decompressZlib(tmp_is, tmp_os);
1659 // Deserialize node definitions
1660 std::istringstream tmp_is2(tmp_os.str());
1661 m_itemdef->deSerialize(tmp_is2);
1662 m_itemdef_received = true;
1664 else if(command == TOCLIENT_PLAY_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::string name = deSerializeString(is);
1671 float gain = readF1000(is);
1672 int type = readU8(is); // 0=local, 1=positional, 2=object
1673 v3f pos = readV3F1000(is);
1674 u16 object_id = readU16(is);
1675 bool loop = readU8(is);
1680 client_id = m_sound->playSound(name, false, gain);
1682 case 1: // positional
1683 client_id = m_sound->playSoundAt(name, false, gain, pos);
1686 ClientActiveObject *cao = m_env.getActiveObject(object_id);
1688 pos = cao->getPosition();
1689 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1690 // TODO: Set up sound to move with object
1695 if(client_id != -1){
1696 m_sounds_server_to_client[server_id] = client_id;
1697 m_sounds_client_to_server[client_id] = server_id;
1699 m_sounds_to_objects[client_id] = object_id;
1702 else if(command == TOCLIENT_STOP_SOUND)
1704 std::string datastring((char*)&data[2], datasize-2);
1705 std::istringstream is(datastring, std::ios_base::binary);
1707 s32 server_id = readS32(is);
1708 std::map<s32, int>::iterator i =
1709 m_sounds_server_to_client.find(server_id);
1710 if(i != m_sounds_server_to_client.end()){
1711 int client_id = i->second;
1712 m_sound->stopSound(client_id);
1717 infostream<<"Client: Ignoring unknown command "
1718 <<command<<std::endl;
1722 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
1724 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1725 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
1728 void Client::interact(u8 action, const PointedThing& pointed)
1730 if(connectedAndInitialized() == false){
1731 infostream<<"Client::interact() "
1732 "cancelled (not connected)"
1737 std::ostringstream os(std::ios_base::binary);
1743 [5] u32 length of the next item
1744 [9] serialized PointedThing
1746 0: start digging (from undersurface) or use
1747 1: stop digging (all parameters ignored)
1748 2: digging completed
1749 3: place block or item (to abovesurface)
1752 writeU16(os, TOSERVER_INTERACT);
1753 writeU8(os, action);
1754 writeU16(os, getPlayerItem());
1755 std::ostringstream tmp_os(std::ios::binary);
1756 pointed.serialize(tmp_os);
1757 os<<serializeLongString(tmp_os.str());
1759 std::string s = os.str();
1760 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1763 Send(0, data, true);
1766 void Client::sendSignNodeText(v3s16 p, std::string text)
1774 std::ostringstream os(std::ios_base::binary);
1778 writeU16(buf, TOSERVER_SIGNNODETEXT);
1779 os.write((char*)buf, 2);
1783 os.write((char*)buf, 6);
1785 u16 textlen = text.size();
1786 // Write text length
1787 writeS16(buf, textlen);
1788 os.write((char*)buf, 2);
1791 os.write((char*)text.c_str(), textlen);
1794 std::string s = os.str();
1795 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1797 Send(0, data, true);
1800 void Client::sendInventoryAction(InventoryAction *a)
1802 std::ostringstream os(std::ios_base::binary);
1806 writeU16(buf, TOSERVER_INVENTORY_ACTION);
1807 os.write((char*)buf, 2);
1812 std::string s = os.str();
1813 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1815 Send(0, data, true);
1818 void Client::sendChatMessage(const std::wstring &message)
1820 std::ostringstream os(std::ios_base::binary);
1824 writeU16(buf, TOSERVER_CHAT_MESSAGE);
1825 os.write((char*)buf, 2);
1828 writeU16(buf, message.size());
1829 os.write((char*)buf, 2);
1832 for(u32 i=0; i<message.size(); i++)
1836 os.write((char*)buf, 2);
1840 std::string s = os.str();
1841 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1843 Send(0, data, true);
1846 void Client::sendChangePassword(const std::wstring oldpassword,
1847 const std::wstring newpassword)
1849 Player *player = m_env.getLocalPlayer();
1853 std::string playername = player->getName();
1854 std::string oldpwd = translatePassword(playername, oldpassword);
1855 std::string newpwd = translatePassword(playername, newpassword);
1857 std::ostringstream os(std::ios_base::binary);
1858 u8 buf[2+PASSWORD_SIZE*2];
1860 [0] u16 TOSERVER_PASSWORD
1861 [2] u8[28] old password
1862 [30] u8[28] new password
1865 writeU16(buf, TOSERVER_PASSWORD);
1866 for(u32 i=0;i<PASSWORD_SIZE-1;i++)
1868 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
1869 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
1871 buf[2+PASSWORD_SIZE-1] = 0;
1872 buf[30+PASSWORD_SIZE-1] = 0;
1873 os.write((char*)buf, 2+PASSWORD_SIZE*2);
1876 std::string s = os.str();
1877 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1879 Send(0, data, true);
1883 void Client::sendDamage(u8 damage)
1885 DSTACK(__FUNCTION_NAME);
1886 std::ostringstream os(std::ios_base::binary);
1888 writeU16(os, TOSERVER_DAMAGE);
1889 writeU8(os, damage);
1892 std::string s = os.str();
1893 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1895 Send(0, data, true);
1898 void Client::sendRespawn()
1900 DSTACK(__FUNCTION_NAME);
1901 std::ostringstream os(std::ios_base::binary);
1903 writeU16(os, TOSERVER_RESPAWN);
1906 std::string s = os.str();
1907 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1909 Send(0, data, true);
1912 void Client::sendPlayerPos()
1914 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1916 Player *myplayer = m_env.getLocalPlayer();
1917 if(myplayer == NULL)
1922 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1923 our_peer_id = m_con.GetPeerID();
1926 // Set peer id if not set already
1927 if(myplayer->peer_id == PEER_ID_INEXISTENT)
1928 myplayer->peer_id = our_peer_id;
1929 // Check that an existing peer_id is the same as the connection's
1930 assert(myplayer->peer_id == our_peer_id);
1932 v3f pf = myplayer->getPosition();
1933 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
1934 v3f sf = myplayer->getSpeed();
1935 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
1936 s32 pitch = myplayer->getPitch() * 100;
1937 s32 yaw = myplayer->getYaw() * 100;
1942 [2] v3s32 position*100
1943 [2+12] v3s32 speed*100
1944 [2+12+12] s32 pitch*100
1945 [2+12+12+4] s32 yaw*100
1948 SharedBuffer<u8> data(2+12+12+4+4);
1949 writeU16(&data[0], TOSERVER_PLAYERPOS);
1950 writeV3S32(&data[2], position);
1951 writeV3S32(&data[2+12], speed);
1952 writeS32(&data[2+12+12], pitch);
1953 writeS32(&data[2+12+12+4], yaw);
1955 // Send as unreliable
1956 Send(0, data, false);
1959 void Client::sendPlayerItem(u16 item)
1961 Player *myplayer = m_env.getLocalPlayer();
1962 if(myplayer == NULL)
1965 u16 our_peer_id = m_con.GetPeerID();
1967 // Set peer id if not set already
1968 if(myplayer->peer_id == PEER_ID_INEXISTENT)
1969 myplayer->peer_id = our_peer_id;
1970 // Check that an existing peer_id is the same as the connection's
1971 assert(myplayer->peer_id == our_peer_id);
1973 SharedBuffer<u8> data(2+2);
1974 writeU16(&data[0], TOSERVER_PLAYERITEM);
1975 writeU16(&data[2], item);
1978 Send(0, data, true);
1981 void Client::removeNode(v3s16 p)
1983 core::map<v3s16, MapBlock*> modified_blocks;
1987 //TimeTaker t("removeNodeAndUpdate", m_device);
1988 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
1990 catch(InvalidPositionException &e)
1994 // add urgent task to update the modified node
1995 addUpdateMeshTaskForNode(p, false, true);
1997 for(core::map<v3s16, MapBlock * >::Iterator
1998 i = modified_blocks.getIterator();
1999 i.atEnd() == false; i++)
2001 v3s16 p = i.getNode()->getKey();
2002 addUpdateMeshTaskWithEdge(p);
2006 void Client::addNode(v3s16 p, MapNode n)
2008 TimeTaker timer1("Client::addNode()");
2010 core::map<v3s16, MapBlock*> modified_blocks;
2014 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
2015 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks);
2017 catch(InvalidPositionException &e)
2020 for(core::map<v3s16, MapBlock * >::Iterator
2021 i = modified_blocks.getIterator();
2022 i.atEnd() == false; i++)
2024 v3s16 p = i.getNode()->getKey();
2025 addUpdateMeshTaskWithEdge(p);
2029 void Client::setPlayerControl(PlayerControl &control)
2031 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2032 LocalPlayer *player = m_env.getLocalPlayer();
2033 assert(player != NULL);
2034 player->control = control;
2037 void Client::selectPlayerItem(u16 item)
2039 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2040 m_playeritem = item;
2041 m_inventory_updated = true;
2042 sendPlayerItem(item);
2045 // Returns true if the inventory of the local player has been
2046 // updated from the server. If it is true, it is set to false.
2047 bool Client::getLocalInventoryUpdated()
2049 // m_inventory_updated is behind envlock
2050 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2051 bool updated = m_inventory_updated;
2052 m_inventory_updated = false;
2056 // Copies the inventory of the local player to parameter
2057 void Client::getLocalInventory(Inventory &dst)
2059 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2060 Player *player = m_env.getLocalPlayer();
2061 assert(player != NULL);
2062 dst = player->inventory;
2065 Inventory* Client::getInventory(const InventoryLocation &loc)
2068 case InventoryLocation::UNDEFINED:
2071 case InventoryLocation::CURRENT_PLAYER:
2073 Player *player = m_env.getLocalPlayer();
2074 assert(player != NULL);
2075 return &player->inventory;
2078 case InventoryLocation::PLAYER:
2080 Player *player = m_env.getPlayer(loc.name.c_str());
2083 return &player->inventory;
2086 case InventoryLocation::NODEMETA:
2088 NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p);
2091 return meta->getInventory();
2099 void Client::inventoryAction(InventoryAction *a)
2102 Send it to the server
2104 sendInventoryAction(a);
2107 Predict some local inventory changes
2109 a->clientApply(this, this);
2112 ClientActiveObject * Client::getSelectedActiveObject(
2114 v3f from_pos_f_on_map,
2115 core::line3d<f32> shootline_on_map
2118 core::array<DistanceSortedActiveObject> objects;
2120 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
2122 //infostream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
2125 // After this, the closest object is the first in the array.
2128 for(u32 i=0; i<objects.size(); i++)
2130 ClientActiveObject *obj = objects[i].obj;
2132 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
2133 if(selection_box == NULL)
2136 v3f pos = obj->getPosition();
2138 core::aabbox3d<f32> offsetted_box(
2139 selection_box->MinEdge + pos,
2140 selection_box->MaxEdge + pos
2143 if(offsetted_box.intersectsWithLine(shootline_on_map))
2145 //infostream<<"Returning selected object"<<std::endl;
2150 //infostream<<"No object selected; returning NULL."<<std::endl;
2154 void Client::printDebugInfo(std::ostream &os)
2156 //JMutexAutoLock lock1(m_fetchblock_mutex);
2157 /*JMutexAutoLock lock2(m_incoming_queue_mutex);
2159 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
2160 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
2161 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
2165 core::list<std::wstring> Client::getConnectedPlayerNames()
2167 core::list<Player*> players = m_env.getPlayers(true);
2168 core::list<std::wstring> playerNames;
2169 for(core::list<Player*>::Iterator
2170 i = players.begin();
2171 i != players.end(); i++)
2173 Player *player = *i;
2174 playerNames.push_back(narrow_to_wide(player->getName()));
2179 float Client::getAnimationTime()
2181 return m_animation_time;
2184 int Client::getCrackLevel()
2186 return m_crack_level;
2189 void Client::setCrack(int level, v3s16 pos)
2191 int old_crack_level = m_crack_level;
2192 v3s16 old_crack_pos = m_crack_pos;
2194 m_crack_level = level;
2197 if(old_crack_level >= 0 && (level < 0 || pos != old_crack_pos))
2200 addUpdateMeshTaskForNode(old_crack_pos, false, true);
2202 if(level >= 0 && (old_crack_level < 0 || pos != old_crack_pos))
2205 addUpdateMeshTaskForNode(pos, false, true);
2211 Player *player = m_env.getLocalPlayer();
2212 assert(player != NULL);
2216 bool Client::getChatMessage(std::wstring &message)
2218 if(m_chat_queue.size() == 0)
2220 message = m_chat_queue.pop_front();
2224 void Client::typeChatMessage(const std::wstring &message)
2226 // Discard empty line
2231 sendChatMessage(message);
2234 if (message[0] == L'/')
2236 m_chat_queue.push_back(
2237 (std::wstring)L"issued command: "+message);
2241 LocalPlayer *player = m_env.getLocalPlayer();
2242 assert(player != NULL);
2243 std::wstring name = narrow_to_wide(player->getName());
2244 m_chat_queue.push_back(
2245 (std::wstring)L"<"+name+L"> "+message);
2249 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
2251 /*infostream<<"Client::addUpdateMeshTask(): "
2252 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2253 <<" ack_to_server="<<ack_to_server
2254 <<" urgent="<<urgent
2257 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2262 Create a task to update the mesh of the block
2265 MeshMakeData *data = new MeshMakeData(this);
2268 //TimeTaker timer("data fill");
2270 // Debug: 1-6ms, avg=2ms
2272 data->setCrack(m_crack_level, m_crack_pos);
2273 data->setSmoothLighting(g_settings->getBool("smooth_lighting"));
2277 //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
2279 // Add task to queue
2280 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server, urgent);
2282 /*infostream<<"Mesh update input queue size is "
2283 <<m_mesh_update_thread.m_queue_in.size()
2287 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent)
2291 infostream<<"Client::addUpdateMeshTaskWithEdge(): "
2292 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2297 v3s16 p = blockpos + v3s16(0,0,0);
2298 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2299 addUpdateMeshTask(p, ack_to_server, urgent);
2301 catch(InvalidPositionException &e){}
2304 v3s16 p = blockpos + v3s16(-1,0,0);
2305 addUpdateMeshTask(p, false, urgent);
2307 catch(InvalidPositionException &e){}
2309 v3s16 p = blockpos + v3s16(0,-1,0);
2310 addUpdateMeshTask(p, false, urgent);
2312 catch(InvalidPositionException &e){}
2314 v3s16 p = blockpos + v3s16(0,0,-1);
2315 addUpdateMeshTask(p, false, urgent);
2317 catch(InvalidPositionException &e){}
2320 void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool urgent)
2324 infostream<<"Client::addUpdateMeshTaskForNode(): "
2325 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2329 v3s16 blockpos = getNodeBlockPos(nodepos);
2330 v3s16 blockpos_relative = blockpos * MAP_BLOCKSIZE;
2333 v3s16 p = blockpos + v3s16(0,0,0);
2334 addUpdateMeshTask(p, ack_to_server, urgent);
2336 catch(InvalidPositionException &e){}
2338 if(nodepos.X == blockpos_relative.X){
2340 v3s16 p = blockpos + v3s16(-1,0,0);
2341 addUpdateMeshTask(p, false, urgent);
2343 catch(InvalidPositionException &e){}
2345 if(nodepos.Y == blockpos_relative.Y){
2347 v3s16 p = blockpos + v3s16(0,-1,0);
2348 addUpdateMeshTask(p, false, urgent);
2350 catch(InvalidPositionException &e){}
2352 if(nodepos.Z == blockpos_relative.Z){
2354 v3s16 p = blockpos + v3s16(0,0,-1);
2355 addUpdateMeshTask(p, false, urgent);
2357 catch(InvalidPositionException &e){}
2361 ClientEvent Client::getClientEvent()
2363 if(m_client_event_queue.size() == 0)
2366 event.type = CE_NONE;
2369 return m_client_event_queue.pop_front();
2372 void Client::afterContentReceived()
2374 assert(m_itemdef_received);
2375 assert(m_nodedef_received);
2376 assert(m_media_received);
2378 // remove the information about which checksum each texture
2380 m_media_name_sha1_map.clear();
2382 // Rebuild inherited images and recreate textures
2383 m_tsrc->rebuildImagesAndTextures();
2385 // Update texture atlas
2386 if(g_settings->getBool("enable_texture_atlas"))
2387 m_tsrc->buildMainAtlas(this);
2389 // Update node aliases
2390 m_nodedef->updateAliases(m_itemdef);
2392 // Update node textures
2393 m_nodedef->updateTextures(m_tsrc);
2395 // Update item textures and meshes
2396 m_itemdef->updateTexturesAndMeshes(this);
2398 // Start mesh update thread after setting up content definitions
2399 m_mesh_update_thread.Start();
2402 float Client::getRTT(void)
2405 return m_con.GetPeerAvgRTT(PEER_ID_SERVER);
2406 } catch(con::PeerNotFoundException &e){
2411 // IGameDef interface
2413 IItemDefManager* Client::getItemDefManager()
2417 INodeDefManager* Client::getNodeDefManager()
2421 ICraftDefManager* Client::getCraftDefManager()
2424 //return m_craftdef;
2426 ITextureSource* Client::getTextureSource()
2430 u16 Client::allocateUnknownNodeId(const std::string &name)
2432 errorstream<<"Client::allocateUnknownNodeId(): "
2433 <<"Client cannot allocate node IDs"<<std::endl;
2435 return CONTENT_IGNORE;
2437 ISoundManager* Client::getSoundManager()
2441 MtEventManager* Client::getEventManager()