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"
44 static std::string getMediaCacheDir()
46 return porting::path_user + DIR_DELIM + "cache" + DIR_DELIM + "media";
53 MediaRequest(const std::string &name_=""):
62 QueuedMeshUpdate::QueuedMeshUpdate():
65 ack_block_to_server(false)
69 QueuedMeshUpdate::~QueuedMeshUpdate()
79 MeshUpdateQueue::MeshUpdateQueue()
84 MeshUpdateQueue::~MeshUpdateQueue()
86 JMutexAutoLock lock(m_mutex);
88 for(std::vector<QueuedMeshUpdate*>::iterator
90 i != m_queue.end(); i++)
92 QueuedMeshUpdate *q = *i;
98 peer_id=0 adds with nobody to send to
100 void MeshUpdateQueue::addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_server, bool urgent)
102 DSTACK(__FUNCTION_NAME);
106 JMutexAutoLock lock(m_mutex);
112 Find if block is already in queue.
113 If it is, update the data and quit.
115 for(std::vector<QueuedMeshUpdate*>::iterator
117 i != m_queue.end(); i++)
119 QueuedMeshUpdate *q = *i;
125 if(ack_block_to_server)
126 q->ack_block_to_server = true;
134 QueuedMeshUpdate *q = new QueuedMeshUpdate;
137 q->ack_block_to_server = ack_block_to_server;
138 m_queue.push_back(q);
141 // Returned pointer must be deleted
142 // Returns NULL if queue is empty
143 QueuedMeshUpdate * MeshUpdateQueue::pop()
145 JMutexAutoLock lock(m_mutex);
147 bool must_be_urgent = !m_urgents.empty();
148 for(std::vector<QueuedMeshUpdate*>::iterator
150 i != m_queue.end(); i++)
152 QueuedMeshUpdate *q = *i;
153 if(must_be_urgent && m_urgents.count(q->p) == 0)
156 m_urgents.erase(q->p);
166 void * MeshUpdateThread::Thread()
170 log_register_thread("MeshUpdateThread");
172 DSTACK(__FUNCTION_NAME);
174 BEGIN_DEBUG_EXCEPTION_HANDLER
178 /*// Wait for output queue to flush.
179 // Allow 2 in queue, this makes less frametime jitter.
180 // Umm actually, there is no much difference
181 if(m_queue_out.size() >= 2)
187 QueuedMeshUpdate *q = m_queue_in.pop();
194 ScopeProfiler sp(g_profiler, "Client: Mesh making");
196 MapBlockMesh *mesh_new = new MapBlockMesh(q->data);
197 if(mesh_new->getMesh()->getMeshBufferCount() == 0)
206 r.ack_block_to_server = q->ack_block_to_server;
208 /*infostream<<"MeshUpdateThread: Processed "
209 <<"("<<q->p.X<<","<<q->p.Y<<","<<q->p.Z<<")"
212 m_queue_out.push_back(r);
217 END_DEBUG_EXCEPTION_HANDLER(errorstream)
223 IrrlichtDevice *device,
224 const char *playername,
225 std::string password,
226 MapDrawControl &control,
227 IWritableTextureSource *tsrc,
228 IWritableItemDefManager *itemdef,
229 IWritableNodeDefManager *nodedef,
230 ISoundManager *sound,
231 MtEventManager *event
238 m_mesh_update_thread(this),
240 new ClientMap(this, this, control,
241 device->getSceneManager()->getRootSceneNode(),
242 device->getSceneManager(), 666),
243 device->getSceneManager(),
246 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
248 m_server_ser_ver(SER_FMT_VER_INVALID),
250 m_inventory_updated(false),
251 m_inventory_from_server(NULL),
252 m_inventory_from_server_age(0.0),
257 m_password(password),
258 m_access_denied(false),
259 m_media_cache(getMediaCacheDir()),
260 m_media_receive_progress(0),
261 m_media_received(false),
262 m_itemdef_received(false),
263 m_nodedef_received(false),
264 m_time_of_day_set(false),
265 m_last_time_of_day_f(-1),
266 m_time_of_day_update_timer(0),
267 m_removed_sounds_check_timer(0)
269 m_packetcounter_timer = 0.0;
270 //m_delete_unused_sectors_timer = 0.0;
271 m_connection_reinit_timer = 0.0;
272 m_avg_rtt_timer = 0.0;
273 m_playerpos_send_timer = 0.0;
274 m_ignore_damage_timer = 0.0;
276 // Build main texture atlas, now that the GameDef exists (that is, us)
277 if(g_settings->getBool("enable_texture_atlas"))
278 m_tsrc->buildMainAtlas(this);
280 infostream<<"Not building texture atlas."<<std::endl;
286 Player *player = new LocalPlayer(this);
288 player->updateName(playername);
290 m_env.addPlayer(player);
297 //JMutexAutoLock conlock(m_con_mutex); //bulk comment-out
301 m_mesh_update_thread.setRun(false);
302 while(m_mesh_update_thread.IsRunning())
305 delete m_inventory_from_server;
308 void Client::connect(Address address)
310 DSTACK(__FUNCTION_NAME);
311 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
312 m_con.SetTimeoutMs(0);
313 m_con.Connect(address);
316 bool Client::connectedAndInitialized()
318 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
320 if(m_con.Connected() == false)
323 if(m_server_ser_ver == SER_FMT_VER_INVALID)
329 void Client::step(float dtime)
331 DSTACK(__FUNCTION_NAME);
337 if(m_ignore_damage_timer > dtime)
338 m_ignore_damage_timer -= dtime;
340 m_ignore_damage_timer = 0.0;
342 m_animation_time += dtime;
343 if(m_animation_time > 60.0)
344 m_animation_time -= 60.0;
346 m_time_of_day_update_timer += dtime;
348 //infostream<<"Client steps "<<dtime<<std::endl;
351 //TimeTaker timer("ReceiveAll()", m_device);
357 //TimeTaker timer("m_con_mutex + m_con.RunTimeouts()", m_device);
359 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
360 m_con.RunTimeouts(dtime);
367 float &counter = m_packetcounter_timer;
373 infostream<<"Client packetcounter (20s):"<<std::endl;
374 m_packetcounter.print(infostream);
375 m_packetcounter.clear();
379 // Get connection status
380 bool connected = connectedAndInitialized();
385 Delete unused sectors
387 NOTE: This jams the game for a while because deleting sectors
391 float &counter = m_delete_unused_sectors_timer;
399 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
401 core::list<v3s16> deleted_blocks;
403 float delete_unused_sectors_timeout =
404 g_settings->getFloat("client_delete_unused_sectors_timeout");
406 // Delete sector blocks
407 /*u32 num = m_env.getMap().unloadUnusedData
408 (delete_unused_sectors_timeout,
409 true, &deleted_blocks);*/
411 // Delete whole sectors
412 m_env.getMap().unloadUnusedData
413 (delete_unused_sectors_timeout,
416 if(deleted_blocks.size() > 0)
418 /*infostream<<"Client: Deleted blocks of "<<num
419 <<" unused sectors"<<std::endl;*/
420 /*infostream<<"Client: Deleted "<<num
421 <<" unused sectors"<<std::endl;*/
427 // Env is locked so con can be locked.
428 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
430 core::list<v3s16>::Iterator i = deleted_blocks.begin();
431 core::list<v3s16> sendlist;
434 if(sendlist.size() == 255 || i == deleted_blocks.end())
436 if(sendlist.size() == 0)
445 u32 replysize = 2+1+6*sendlist.size();
446 SharedBuffer<u8> reply(replysize);
447 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
448 reply[2] = sendlist.size();
450 for(core::list<v3s16>::Iterator
451 j = sendlist.begin();
452 j != sendlist.end(); j++)
454 writeV3S16(&reply[2+1+6*k], *j);
457 m_con.Send(PEER_ID_SERVER, 1, reply, true);
459 if(i == deleted_blocks.end())
465 sendlist.push_back(*i);
473 if(connected == false)
475 float &counter = m_connection_reinit_timer;
481 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
483 Player *myplayer = m_env.getLocalPlayer();
484 assert(myplayer != NULL);
486 // Send TOSERVER_INIT
487 // [0] u16 TOSERVER_INIT
488 // [2] u8 SER_FMT_VER_HIGHEST
489 // [3] u8[20] player_name
490 // [23] u8[28] password (new in some version)
491 // [51] u16 client network protocol version (new in some version)
492 SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2);
493 writeU16(&data[0], TOSERVER_INIT);
494 writeU8(&data[2], SER_FMT_VER_HIGHEST);
496 memset((char*)&data[3], 0, PLAYERNAME_SIZE);
497 snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
499 /*infostream<<"Client: sending initial password hash: \""<<m_password<<"\""
502 memset((char*)&data[23], 0, PASSWORD_SIZE);
503 snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
505 // This should be incremented in each version
506 writeU16(&data[51], PROTOCOL_VERSION);
508 // Send as unreliable
509 Send(0, data, false);
512 // Not connected, return
517 Do stuff if connected
521 Run Map's timers and unload unused data
523 const float map_timer_and_unload_dtime = 5.25;
524 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
526 ScopeProfiler sp(g_profiler, "Client: map timer and unload");
527 core::list<v3s16> deleted_blocks;
528 m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
529 g_settings->getFloat("client_unload_unused_data_timeout"),
532 /*if(deleted_blocks.size() > 0)
533 infostream<<"Client: Unloaded "<<deleted_blocks.size()
534 <<" unused blocks"<<std::endl;*/
538 NOTE: This loop is intentionally iterated the way it is.
541 core::list<v3s16>::Iterator i = deleted_blocks.begin();
542 core::list<v3s16> sendlist;
545 if(sendlist.size() == 255 || i == deleted_blocks.end())
547 if(sendlist.size() == 0)
556 u32 replysize = 2+1+6*sendlist.size();
557 SharedBuffer<u8> reply(replysize);
558 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
559 reply[2] = sendlist.size();
561 for(core::list<v3s16>::Iterator
562 j = sendlist.begin();
563 j != sendlist.end(); j++)
565 writeV3S16(&reply[2+1+6*k], *j);
568 m_con.Send(PEER_ID_SERVER, 1, reply, true);
570 if(i == deleted_blocks.end())
576 sendlist.push_back(*i);
586 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
588 // Control local player (0ms)
589 LocalPlayer *player = m_env.getLocalPlayer();
590 assert(player != NULL);
591 player->applyControl(dtime);
593 //TimeTaker envtimer("env step", m_device);
602 ClientEnvEvent event = m_env.getClientEvent();
603 if(event.type == CEE_NONE)
607 else if(event.type == CEE_PLAYER_DAMAGE)
609 if(m_ignore_damage_timer <= 0)
611 u8 damage = event.player_damage.amount;
613 if(event.player_damage.send_to_server)
616 // Add to ClientEvent queue
618 event.type = CE_PLAYER_DAMAGE;
619 event.player_damage.amount = damage;
620 m_client_event_queue.push_back(event);
630 float &counter = m_avg_rtt_timer;
635 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
636 // connectedAndInitialized() is true, peer exists.
637 float avg_rtt = m_con.GetPeerAvgRTT(PEER_ID_SERVER);
638 infostream<<"Client: avg_rtt="<<avg_rtt<<std::endl;
643 Send player position to server
646 float &counter = m_playerpos_send_timer;
656 Replace updated meshes
659 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
661 //TimeTaker timer("** Processing mesh update result queue");
664 /*infostream<<"Mesh update result queue size is "
665 <<m_mesh_update_thread.m_queue_out.size()
668 int num_processed_meshes = 0;
669 while(m_mesh_update_thread.m_queue_out.size() > 0)
671 num_processed_meshes++;
672 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_front();
673 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
676 //JMutexAutoLock lock(block->mesh_mutex);
678 // Delete the old mesh
679 if(block->mesh != NULL)
681 // TODO: Remove hardware buffers of meshbuffers of block->mesh
686 // Replace with the new mesh
687 block->mesh = r.mesh;
689 if(r.ack_block_to_server)
691 /*infostream<<"Client: ACK block ("<<r.p.X<<","<<r.p.Y
692 <<","<<r.p.Z<<")"<<std::endl;*/
703 u32 replysize = 2+1+6;
704 SharedBuffer<u8> reply(replysize);
705 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
707 writeV3S16(&reply[3], r.p);
709 m_con.Send(PEER_ID_SERVER, 1, reply, true);
712 if(num_processed_meshes > 0)
713 g_profiler->graphAdd("num_processed_meshes", num_processed_meshes);
717 If the server didn't update the inventory in a while, revert
718 the local inventory (so the player notices the lag problem
719 and knows something is wrong).
721 if(m_inventory_from_server)
723 float interval = 10.0;
724 float count_before = floor(m_inventory_from_server_age / interval);
726 m_inventory_from_server_age += dtime;
728 float count_after = floor(m_inventory_from_server_age / interval);
730 if(count_after != count_before)
732 // Do this every <interval> seconds after TOCLIENT_INVENTORY
733 // Reset the locally changed inventory to the authoritative inventory
734 Player *player = m_env.getLocalPlayer();
735 player->inventory = *m_inventory_from_server;
736 m_inventory_updated = true;
741 Update positions of sounds attached to objects
744 for(std::map<int, u16>::iterator
745 i = m_sounds_to_objects.begin();
746 i != m_sounds_to_objects.end(); i++)
748 int client_id = i->first;
749 u16 object_id = i->second;
750 ClientActiveObject *cao = m_env.getActiveObject(object_id);
753 v3f pos = cao->getPosition();
754 m_sound->updateSoundPosition(client_id, pos);
759 Handle removed remotely initiated sounds
761 m_removed_sounds_check_timer += dtime;
762 if(m_removed_sounds_check_timer >= 2.32)
764 m_removed_sounds_check_timer = 0;
765 // Find removed sounds and clear references to them
766 std::set<s32> removed_server_ids;
767 for(std::map<s32, int>::iterator
768 i = m_sounds_server_to_client.begin();
769 i != m_sounds_server_to_client.end();)
771 s32 server_id = i->first;
772 int client_id = i->second;
774 if(!m_sound->soundExists(client_id)){
775 m_sounds_server_to_client.erase(server_id);
776 m_sounds_client_to_server.erase(client_id);
777 m_sounds_to_objects.erase(client_id);
778 removed_server_ids.insert(server_id);
782 if(removed_server_ids.size() != 0)
784 std::ostringstream os(std::ios_base::binary);
785 writeU16(os, TOSERVER_REMOVED_SOUNDS);
786 writeU16(os, removed_server_ids.size());
787 for(std::set<s32>::iterator i = removed_server_ids.begin();
788 i != removed_server_ids.end(); i++)
790 std::string s = os.str();
791 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
798 // Virtual methods from con::PeerHandler
799 void Client::peerAdded(con::Peer *peer)
801 infostream<<"Client::peerAdded(): peer->id="
802 <<peer->id<<std::endl;
804 void Client::deletingPeer(con::Peer *peer, bool timeout)
806 infostream<<"Client::deletingPeer(): "
807 "Server Peer is getting deleted "
808 <<"(timeout="<<timeout<<")"<<std::endl;
811 void Client::ReceiveAll()
813 DSTACK(__FUNCTION_NAME);
814 u32 start_ms = porting::getTimeMs();
817 // Limit time even if there would be huge amounts of data to
819 if(porting::getTimeMs() > start_ms + 100)
824 g_profiler->graphAdd("client_received_packets", 1);
826 catch(con::NoIncomingDataException &e)
830 catch(con::InvalidIncomingDataException &e)
832 infostream<<"Client::ReceiveAll(): "
833 "InvalidIncomingDataException: what()="
834 <<e.what()<<std::endl;
839 void Client::Receive()
841 DSTACK(__FUNCTION_NAME);
842 SharedBuffer<u8> data;
846 //TimeTaker t1("con mutex and receive", m_device);
847 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
848 datasize = m_con.Receive(sender_peer_id, data);
850 //TimeTaker t1("ProcessData", m_device);
851 ProcessData(*data, datasize, sender_peer_id);
855 sender_peer_id given to this shall be quaranteed to be a valid peer
857 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
859 DSTACK(__FUNCTION_NAME);
861 // Ignore packets that don't even fit a command
864 m_packetcounter.add(60000);
868 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
870 //infostream<<"Client: received command="<<command<<std::endl;
871 m_packetcounter.add((u16)command);
874 If this check is removed, be sure to change the queue
875 system to know the ids
877 if(sender_peer_id != PEER_ID_SERVER)
879 infostream<<"Client::ProcessData(): Discarding data not "
880 "coming from server: peer_id="<<sender_peer_id
885 u8 ser_version = m_server_ser_ver;
887 //infostream<<"Client received command="<<(int)command<<std::endl;
889 if(command == TOCLIENT_INIT)
894 u8 deployed = data[2];
896 infostream<<"Client: TOCLIENT_INIT received with "
897 "deployed="<<((int)deployed&0xff)<<std::endl;
899 if(deployed < SER_FMT_VER_LOWEST
900 || deployed > SER_FMT_VER_HIGHEST)
902 infostream<<"Client: TOCLIENT_INIT: Server sent "
903 <<"unsupported ser_fmt_ver"<<std::endl;
907 m_server_ser_ver = deployed;
909 // Get player position
910 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
911 if(datasize >= 2+1+6)
912 playerpos_s16 = readV3S16(&data[2+1]);
913 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
916 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
918 // Set player position
919 Player *player = m_env.getLocalPlayer();
920 assert(player != NULL);
921 player->setPosition(playerpos_f);
924 if(datasize >= 2+1+6+8)
927 m_map_seed = readU64(&data[2+1+6]);
928 infostream<<"Client: received map seed: "<<m_map_seed<<std::endl;
933 SharedBuffer<u8> reply(replysize);
934 writeU16(&reply[0], TOSERVER_INIT2);
936 m_con.Send(PEER_ID_SERVER, 1, reply, true);
941 if(command == TOCLIENT_ACCESS_DENIED)
943 // The server didn't like our password. Note, this needs
944 // to be processed even if the serialisation format has
945 // not been agreed yet, the same as TOCLIENT_INIT.
946 m_access_denied = true;
947 m_access_denied_reason = L"Unknown";
950 std::string datastring((char*)&data[2], datasize-2);
951 std::istringstream is(datastring, std::ios_base::binary);
952 m_access_denied_reason = deSerializeWideString(is);
957 if(ser_version == SER_FMT_VER_INVALID)
959 infostream<<"Client: Server serialization"
960 " format invalid or not initialized."
961 " Skipping incoming command="<<command<<std::endl;
965 // Just here to avoid putting the two if's together when
966 // making some copypasta
969 if(command == TOCLIENT_REMOVENODE)
974 p.X = readS16(&data[2]);
975 p.Y = readS16(&data[4]);
976 p.Z = readS16(&data[6]);
978 //TimeTaker t1("TOCLIENT_REMOVENODE");
982 else if(command == TOCLIENT_ADDNODE)
984 if(datasize < 8 + MapNode::serializedLength(ser_version))
988 p.X = readS16(&data[2]);
989 p.Y = readS16(&data[4]);
990 p.Z = readS16(&data[6]);
992 //TimeTaker t1("TOCLIENT_ADDNODE");
995 n.deSerialize(&data[8], ser_version);
999 else if(command == TOCLIENT_BLOCKDATA)
1001 // Ignore too small packet
1006 p.X = readS16(&data[2]);
1007 p.Y = readS16(&data[4]);
1008 p.Z = readS16(&data[6]);
1010 /*infostream<<"Client: Thread: BLOCKDATA for ("
1011 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1012 /*infostream<<"Client: Thread: BLOCKDATA for ("
1013 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1015 std::string datastring((char*)&data[8], datasize-8);
1016 std::istringstream istr(datastring, std::ios_base::binary);
1021 v2s16 p2d(p.X, p.Z);
1022 sector = m_env.getMap().emergeSector(p2d);
1024 assert(sector->getPos() == p2d);
1026 //TimeTaker timer("MapBlock deSerialize");
1029 block = sector->getBlockNoCreateNoEx(p.Y);
1033 Update an existing block
1035 //infostream<<"Updating"<<std::endl;
1036 block->deSerialize(istr, ser_version, false);
1043 //infostream<<"Creating new"<<std::endl;
1044 block = new MapBlock(&m_env.getMap(), p, this);
1045 block->deSerialize(istr, ser_version, false);
1046 sector->insertBlock(block);
1060 u32 replysize = 2+1+6;
1061 SharedBuffer<u8> reply(replysize);
1062 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
1064 writeV3S16(&reply[3], p);
1066 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1070 Add it to mesh update queue and set it to be acknowledged after update.
1072 //infostream<<"Adding mesh update task for received block"<<std::endl;
1073 addUpdateMeshTaskWithEdge(p, true);
1075 else if(command == TOCLIENT_INVENTORY)
1080 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
1083 //TimeTaker t2("mutex locking", m_device);
1084 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1087 //TimeTaker t3("istringstream init", m_device);
1088 std::string datastring((char*)&data[2], datasize-2);
1089 std::istringstream is(datastring, std::ios_base::binary);
1092 //m_env.printPlayers(infostream);
1094 //TimeTaker t4("player get", m_device);
1095 Player *player = m_env.getLocalPlayer();
1096 assert(player != NULL);
1099 //TimeTaker t1("inventory.deSerialize()", m_device);
1100 player->inventory.deSerialize(is);
1103 m_inventory_updated = true;
1105 delete m_inventory_from_server;
1106 m_inventory_from_server = new Inventory(player->inventory);
1107 m_inventory_from_server_age = 0.0;
1109 //infostream<<"Client got player inventory:"<<std::endl;
1110 //player->inventory.print(infostream);
1113 else if(command == TOCLIENT_TIME_OF_DAY)
1118 u16 time_of_day = readU16(&data[2]);
1119 time_of_day = time_of_day % 24000;
1120 //infostream<<"Client: time_of_day="<<time_of_day<<std::endl;
1121 float time_speed = 0;
1122 if(datasize >= 2 + 2 + 4){
1123 time_speed = readF1000(&data[4]);
1125 // Old message; try to approximate speed of time by ourselves
1126 float time_of_day_f = (float)time_of_day / 24000.0;
1127 float tod_diff_f = 0;
1128 if(time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
1129 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0;
1131 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
1132 m_last_time_of_day_f = time_of_day_f;
1133 float time_diff = m_time_of_day_update_timer;
1134 m_time_of_day_update_timer = 0;
1135 if(m_time_of_day_set){
1136 time_speed = 3600.0*24.0 * tod_diff_f / time_diff;
1137 infostream<<"Client: Measured time_of_day speed (old format): "
1138 <<time_speed<<" tod_diff_f="<<tod_diff_f
1139 <<" time_diff="<<time_diff<<std::endl;
1143 // Update environment
1144 m_env.setTimeOfDay(time_of_day);
1145 m_env.setTimeOfDaySpeed(time_speed);
1146 m_time_of_day_set = true;
1148 u32 dr = m_env.getDayNightRatio();
1149 verbosestream<<"Client: time_of_day="<<time_of_day
1150 <<" time_speed="<<time_speed
1151 <<" dr="<<dr<<std::endl;
1153 else if(command == TOCLIENT_CHAT_MESSAGE)
1161 std::string datastring((char*)&data[2], datasize-2);
1162 std::istringstream is(datastring, std::ios_base::binary);
1165 is.read((char*)buf, 2);
1166 u16 len = readU16(buf);
1168 std::wstring message;
1169 for(u16 i=0; i<len; i++)
1171 is.read((char*)buf, 2);
1172 message += (wchar_t)readU16(buf);
1175 /*infostream<<"Client received chat message: "
1176 <<wide_to_narrow(message)<<std::endl;*/
1178 m_chat_queue.push_back(message);
1180 else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1182 //if(g_settings->getBool("enable_experimental"))
1186 u16 count of removed objects
1187 for all removed objects {
1190 u16 count of added objects
1191 for all added objects {
1194 u32 initialization data length
1195 string initialization data
1200 // Get all data except the command number
1201 std::string datastring((char*)&data[2], datasize-2);
1202 // Throw them in an istringstream
1203 std::istringstream is(datastring, std::ios_base::binary);
1207 // Read removed objects
1209 u16 removed_count = readU16((u8*)buf);
1210 for(u16 i=0; i<removed_count; i++)
1213 u16 id = readU16((u8*)buf);
1216 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1217 m_env.removeActiveObject(id);
1221 // Read added objects
1223 u16 added_count = readU16((u8*)buf);
1224 for(u16 i=0; i<added_count; i++)
1227 u16 id = readU16((u8*)buf);
1229 u8 type = readU8((u8*)buf);
1230 std::string data = deSerializeLongString(is);
1233 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1234 m_env.addActiveObject(id, type, data);
1239 else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1241 //if(g_settings->getBool("enable_experimental"))
1253 // Get all data except the command number
1254 std::string datastring((char*)&data[2], datasize-2);
1255 // Throw them in an istringstream
1256 std::istringstream is(datastring, std::ios_base::binary);
1258 while(is.eof() == false)
1262 u16 id = readU16((u8*)buf);
1266 u16 message_size = readU16((u8*)buf);
1267 std::string message;
1268 message.reserve(message_size);
1269 for(u16 i=0; i<message_size; i++)
1272 message.append(buf, 1);
1274 // Pass on to the environment
1276 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1277 m_env.processActiveObjectMessage(id, message);
1282 else if(command == TOCLIENT_HP)
1284 std::string datastring((char*)&data[2], datasize-2);
1285 std::istringstream is(datastring, std::ios_base::binary);
1286 Player *player = m_env.getLocalPlayer();
1287 assert(player != NULL);
1288 u8 oldhp = player->hp;
1294 // Add to ClientEvent queue
1296 event.type = CE_PLAYER_DAMAGE;
1297 event.player_damage.amount = oldhp - hp;
1298 m_client_event_queue.push_back(event);
1301 else if(command == TOCLIENT_MOVE_PLAYER)
1303 std::string datastring((char*)&data[2], datasize-2);
1304 std::istringstream is(datastring, std::ios_base::binary);
1305 Player *player = m_env.getLocalPlayer();
1306 assert(player != NULL);
1307 v3f pos = readV3F1000(is);
1308 f32 pitch = readF1000(is);
1309 f32 yaw = readF1000(is);
1310 player->setPosition(pos);
1311 /*player->setPitch(pitch);
1312 player->setYaw(yaw);*/
1314 infostream<<"Client got TOCLIENT_MOVE_PLAYER"
1315 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1321 Add to ClientEvent queue.
1322 This has to be sent to the main program because otherwise
1323 it would just force the pitch and yaw values to whatever
1324 the camera points to.
1327 event.type = CE_PLAYER_FORCE_MOVE;
1328 event.player_force_move.pitch = pitch;
1329 event.player_force_move.yaw = yaw;
1330 m_client_event_queue.push_back(event);
1332 // Ignore damage for a few seconds, so that the player doesn't
1333 // get damage from falling on ground
1334 m_ignore_damage_timer = 3.0;
1336 else if(command == TOCLIENT_PLAYERITEM)
1338 std::string datastring((char*)&data[2], datasize-2);
1339 std::istringstream is(datastring, std::ios_base::binary);
1341 u16 count = readU16(is);
1343 for (u16 i = 0; i < count; ++i) {
1344 u16 peer_id = readU16(is);
1345 Player *player = m_env.getPlayer(peer_id);
1349 infostream<<"Client: ignoring player item "
1350 << deSerializeString(is)
1351 << " for non-existing peer id " << peer_id
1354 } else if (player->isLocal()) {
1355 infostream<<"Client: ignoring player item "
1356 << deSerializeString(is)
1357 << " for local player" << std::endl;
1360 InventoryList *inv = player->inventory.getList("main");
1361 std::string itemstring(deSerializeString(is));
1363 item.deSerialize(itemstring, m_itemdef);
1364 inv->changeItem(0, item);
1365 if(itemstring.empty())
1367 infostream<<"Client: empty player item for peer "
1368 <<peer_id<<std::endl;
1372 infostream<<"Client: player item for peer "
1373 <<peer_id<<": "<<itemstring<<std::endl;
1378 else if(command == TOCLIENT_DEATHSCREEN)
1380 std::string datastring((char*)&data[2], datasize-2);
1381 std::istringstream is(datastring, std::ios_base::binary);
1383 bool set_camera_point_target = readU8(is);
1384 v3f camera_point_target = readV3F1000(is);
1387 event.type = CE_DEATHSCREEN;
1388 event.deathscreen.set_camera_point_target = set_camera_point_target;
1389 event.deathscreen.camera_point_target_x = camera_point_target.X;
1390 event.deathscreen.camera_point_target_y = camera_point_target.Y;
1391 event.deathscreen.camera_point_target_z = camera_point_target.Z;
1392 m_client_event_queue.push_back(event);
1394 else if(command == TOCLIENT_ANNOUNCE_MEDIA)
1396 io::IFileSystem *irrfs = m_device->getFileSystem();
1397 video::IVideoDriver *vdrv = m_device->getVideoDriver();
1399 std::string datastring((char*)&data[2], datasize-2);
1400 std::istringstream is(datastring, std::ios_base::binary);
1402 // Mesh update thread must be stopped while
1403 // updating content definitions
1404 assert(!m_mesh_update_thread.IsRunning());
1406 int num_files = readU16(is);
1408 verbosestream<<"Client received TOCLIENT_ANNOUNCE_MEDIA ("
1409 <<num_files<<" files)"<<std::endl;
1411 core::list<MediaRequest> file_requests;
1413 for(int i=0; i<num_files; i++){
1415 bool file_found = false;
1417 //read file from cache
1418 std::string name = deSerializeString(is);
1419 std::string sha1_file = deSerializeString(is);
1421 // if name contains illegal characters, ignore the file
1422 if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
1423 errorstream<<"Client: ignoring illegal file name "
1424 <<"sent by server: \""<<name<<"\""<<std::endl;
1428 std::string sha1_decoded = base64_decode(sha1_file);
1429 std::ostringstream tmp_os(std::ios_base::binary);
1430 bool file_in_cache = m_media_cache.loadByChecksum(sha1_decoded,
1432 m_media_name_sha1_map.set(name, sha1_decoded);
1437 sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
1439 unsigned char *digest = sha1.getDigest();
1441 std::string digest_string = base64_encode(digest, 20);
1443 if (digest_string == sha1_file) {
1444 // Silly irrlicht's const-incorrectness
1445 Buffer<char> data_rw(tmp_os.str().c_str(), tmp_os.str().size());
1447 // Create an irrlicht memory file
1448 io::IReadFile *rfile = irrfs->createMemoryReadFile(
1449 *data_rw, tmp_os.str().size(), "_tempreadfile");
1452 video::IImage *img = vdrv->createImageFromFile(rfile);
1454 infostream<<"Client: Cannot create image from data of "
1455 <<"received file \""<<name<<"\""<<std::endl;
1459 m_tsrc->insertSourceImage(name, img);
1467 infostream<<"Client::Media cached sha1 hash not matching server hash: "
1468 <<name << ": server ->"<<sha1_file <<" client -> "<<digest_string<<std::endl;
1476 infostream<<"Client: Adding file to request list: \""
1477 <<name<<"\""<<std::endl;
1478 file_requests.push_back(MediaRequest(name));
1484 event.type = CE_TEXTURES_UPDATED;
1485 m_client_event_queue.push_back(event);
1488 //send Media request
1491 u16 number of files requested
1497 std::ostringstream os(std::ios_base::binary);
1502 writeU16(buf, TOSERVER_REQUEST_MEDIA);
1503 os.write((char*)buf, 2);
1505 writeU16(buf,file_requests.size());
1506 os.write((char*)buf, 2);
1509 for(core::list<MediaRequest>::Iterator i = file_requests.begin();
1510 i != file_requests.end(); i++) {
1511 os<<serializeString(i->name);
1515 std::string s = os.str();
1516 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1518 Send(0, data, true);
1519 infostream<<"Client: Sending media request list to server ("
1520 <<file_requests.size()<<" files)"<<std::endl;
1522 else if(command == TOCLIENT_MEDIA)
1524 verbosestream<<"Client received TOCLIENT_MEDIA"<<std::endl;
1526 io::IFileSystem *irrfs = m_device->getFileSystem();
1527 video::IVideoDriver *vdrv = m_device->getVideoDriver();
1529 std::string datastring((char*)&data[2], datasize-2);
1530 std::istringstream is(datastring, std::ios_base::binary);
1532 // Mesh update thread must be stopped while
1533 // updating content definitions
1534 assert(!m_mesh_update_thread.IsRunning());
1538 u16 total number of file bunches
1539 u16 index of this bunch
1540 u32 number of files in this bunch
1548 int num_bunches = readU16(is);
1549 int bunch_i = readU16(is);
1550 m_media_receive_progress = (float)bunch_i / (float)(num_bunches - 1);
1551 if(bunch_i == num_bunches - 1)
1552 m_media_received = true;
1553 int num_files = readU32(is);
1554 infostream<<"Client: Received files: bunch "<<bunch_i<<"/"
1555 <<num_bunches<<" files="<<num_files
1556 <<" size="<<datasize<<std::endl;
1557 for(int i=0; i<num_files; i++){
1558 std::string name = deSerializeString(is);
1559 std::string data = deSerializeLongString(is);
1561 // if name contains illegal characters, ignore the file
1562 if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
1563 errorstream<<"Client: ignoring illegal file name "
1564 <<"sent by server: \""<<name<<"\""<<std::endl;
1568 // Silly irrlicht's const-incorrectness
1569 Buffer<char> data_rw(data.c_str(), data.size());
1570 // Create an irrlicht memory file
1571 io::IReadFile *rfile = irrfs->createMemoryReadFile(
1572 *data_rw, data.size(), "_tempreadfile");
1575 video::IImage *img = vdrv->createImageFromFile(rfile);
1577 errorstream<<"Client: Cannot create image from data of "
1578 <<"received file \""<<name<<"\""<<std::endl;
1583 bool did = fs::CreateAllDirs(getMediaCacheDir());
1585 errorstream<<"Could not create media cache directory"
1590 core::map<std::string, std::string>::Node *n;
1591 n = m_media_name_sha1_map.find(name);
1593 errorstream<<"The server sent a file that has not "
1594 <<"been announced."<<std::endl;
1596 m_media_cache.updateByChecksum(n->getValue(), data);
1599 m_tsrc->insertSourceImage(name, img);
1605 event.type = CE_TEXTURES_UPDATED;
1606 m_client_event_queue.push_back(event);
1608 else if(command == TOCLIENT_TOOLDEF)
1610 infostream<<"Client: WARNING: Ignoring TOCLIENT_TOOLDEF"<<std::endl;
1612 else if(command == TOCLIENT_NODEDEF)
1614 infostream<<"Client: Received node definitions: packet size: "
1615 <<datasize<<std::endl;
1617 // Mesh update thread must be stopped while
1618 // updating content definitions
1619 assert(!m_mesh_update_thread.IsRunning());
1621 // Decompress node definitions
1622 std::string datastring((char*)&data[2], datasize-2);
1623 std::istringstream is(datastring, std::ios_base::binary);
1624 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1625 std::ostringstream tmp_os;
1626 decompressZlib(tmp_is, tmp_os);
1628 // Deserialize node definitions
1629 std::istringstream tmp_is2(tmp_os.str());
1630 m_nodedef->deSerialize(tmp_is2);
1631 m_nodedef_received = true;
1633 else if(command == TOCLIENT_CRAFTITEMDEF)
1635 infostream<<"Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF"<<std::endl;
1637 else if(command == TOCLIENT_ITEMDEF)
1639 infostream<<"Client: Received item definitions: packet size: "
1640 <<datasize<<std::endl;
1642 // Mesh update thread must be stopped while
1643 // updating content definitions
1644 assert(!m_mesh_update_thread.IsRunning());
1646 // Decompress item definitions
1647 std::string datastring((char*)&data[2], datasize-2);
1648 std::istringstream is(datastring, std::ios_base::binary);
1649 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1650 std::ostringstream tmp_os;
1651 decompressZlib(tmp_is, tmp_os);
1653 // Deserialize node definitions
1654 std::istringstream tmp_is2(tmp_os.str());
1655 m_itemdef->deSerialize(tmp_is2);
1656 m_itemdef_received = true;
1658 else if(command == TOCLIENT_PLAY_SOUND)
1660 std::string datastring((char*)&data[2], datasize-2);
1661 std::istringstream is(datastring, std::ios_base::binary);
1663 s32 server_id = readS32(is);
1664 std::string name = deSerializeString(is);
1665 float gain = readF1000(is);
1666 int type = readU8(is); // 0=local, 1=positional, 2=object
1667 v3f pos = readV3F1000(is);
1668 u16 object_id = readU16(is);
1669 bool loop = readU8(is);
1674 client_id = m_sound->playSound(name, false, gain);
1676 case 1: // positional
1677 client_id = m_sound->playSoundAt(name, false, gain, pos);
1680 ClientActiveObject *cao = m_env.getActiveObject(object_id);
1682 pos = cao->getPosition();
1683 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1684 // TODO: Set up sound to move with object
1689 if(client_id != -1){
1690 m_sounds_server_to_client[server_id] = client_id;
1691 m_sounds_client_to_server[client_id] = server_id;
1693 m_sounds_to_objects[client_id] = object_id;
1696 else if(command == TOCLIENT_STOP_SOUND)
1698 std::string datastring((char*)&data[2], datasize-2);
1699 std::istringstream is(datastring, std::ios_base::binary);
1701 s32 server_id = readS32(is);
1702 std::map<s32, int>::iterator i =
1703 m_sounds_server_to_client.find(server_id);
1704 if(i != m_sounds_server_to_client.end()){
1705 int client_id = i->second;
1706 m_sound->stopSound(client_id);
1711 infostream<<"Client: Ignoring unknown command "
1712 <<command<<std::endl;
1716 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
1718 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1719 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
1722 void Client::interact(u8 action, const PointedThing& pointed)
1724 if(connectedAndInitialized() == false){
1725 infostream<<"Client::interact() "
1726 "cancelled (not connected)"
1731 std::ostringstream os(std::ios_base::binary);
1737 [5] u32 length of the next item
1738 [9] serialized PointedThing
1740 0: start digging (from undersurface) or use
1741 1: stop digging (all parameters ignored)
1742 2: digging completed
1743 3: place block or item (to abovesurface)
1746 writeU16(os, TOSERVER_INTERACT);
1747 writeU8(os, action);
1748 writeU16(os, getPlayerItem());
1749 std::ostringstream tmp_os(std::ios::binary);
1750 pointed.serialize(tmp_os);
1751 os<<serializeLongString(tmp_os.str());
1753 std::string s = os.str();
1754 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1757 Send(0, data, true);
1760 void Client::sendSignNodeText(v3s16 p, std::string text)
1768 std::ostringstream os(std::ios_base::binary);
1772 writeU16(buf, TOSERVER_SIGNNODETEXT);
1773 os.write((char*)buf, 2);
1777 os.write((char*)buf, 6);
1779 u16 textlen = text.size();
1780 // Write text length
1781 writeS16(buf, textlen);
1782 os.write((char*)buf, 2);
1785 os.write((char*)text.c_str(), textlen);
1788 std::string s = os.str();
1789 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1791 Send(0, data, true);
1794 void Client::sendInventoryAction(InventoryAction *a)
1796 std::ostringstream os(std::ios_base::binary);
1800 writeU16(buf, TOSERVER_INVENTORY_ACTION);
1801 os.write((char*)buf, 2);
1806 std::string s = os.str();
1807 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1809 Send(0, data, true);
1812 void Client::sendChatMessage(const std::wstring &message)
1814 std::ostringstream os(std::ios_base::binary);
1818 writeU16(buf, TOSERVER_CHAT_MESSAGE);
1819 os.write((char*)buf, 2);
1822 writeU16(buf, message.size());
1823 os.write((char*)buf, 2);
1826 for(u32 i=0; i<message.size(); i++)
1830 os.write((char*)buf, 2);
1834 std::string s = os.str();
1835 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1837 Send(0, data, true);
1840 void Client::sendChangePassword(const std::wstring oldpassword,
1841 const std::wstring newpassword)
1843 Player *player = m_env.getLocalPlayer();
1847 std::string playername = player->getName();
1848 std::string oldpwd = translatePassword(playername, oldpassword);
1849 std::string newpwd = translatePassword(playername, newpassword);
1851 std::ostringstream os(std::ios_base::binary);
1852 u8 buf[2+PASSWORD_SIZE*2];
1854 [0] u16 TOSERVER_PASSWORD
1855 [2] u8[28] old password
1856 [30] u8[28] new password
1859 writeU16(buf, TOSERVER_PASSWORD);
1860 for(u32 i=0;i<PASSWORD_SIZE-1;i++)
1862 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
1863 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
1865 buf[2+PASSWORD_SIZE-1] = 0;
1866 buf[30+PASSWORD_SIZE-1] = 0;
1867 os.write((char*)buf, 2+PASSWORD_SIZE*2);
1870 std::string s = os.str();
1871 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1873 Send(0, data, true);
1877 void Client::sendDamage(u8 damage)
1879 DSTACK(__FUNCTION_NAME);
1880 std::ostringstream os(std::ios_base::binary);
1882 writeU16(os, TOSERVER_DAMAGE);
1883 writeU8(os, damage);
1886 std::string s = os.str();
1887 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1889 Send(0, data, true);
1892 void Client::sendRespawn()
1894 DSTACK(__FUNCTION_NAME);
1895 std::ostringstream os(std::ios_base::binary);
1897 writeU16(os, TOSERVER_RESPAWN);
1900 std::string s = os.str();
1901 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1903 Send(0, data, true);
1906 void Client::sendPlayerPos()
1908 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1910 Player *myplayer = m_env.getLocalPlayer();
1911 if(myplayer == NULL)
1916 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1917 our_peer_id = m_con.GetPeerID();
1920 // Set peer id if not set already
1921 if(myplayer->peer_id == PEER_ID_INEXISTENT)
1922 myplayer->peer_id = our_peer_id;
1923 // Check that an existing peer_id is the same as the connection's
1924 assert(myplayer->peer_id == our_peer_id);
1926 v3f pf = myplayer->getPosition();
1927 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
1928 v3f sf = myplayer->getSpeed();
1929 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
1930 s32 pitch = myplayer->getPitch() * 100;
1931 s32 yaw = myplayer->getYaw() * 100;
1936 [2] v3s32 position*100
1937 [2+12] v3s32 speed*100
1938 [2+12+12] s32 pitch*100
1939 [2+12+12+4] s32 yaw*100
1942 SharedBuffer<u8> data(2+12+12+4+4);
1943 writeU16(&data[0], TOSERVER_PLAYERPOS);
1944 writeV3S32(&data[2], position);
1945 writeV3S32(&data[2+12], speed);
1946 writeS32(&data[2+12+12], pitch);
1947 writeS32(&data[2+12+12+4], yaw);
1949 // Send as unreliable
1950 Send(0, data, false);
1953 void Client::sendPlayerItem(u16 item)
1955 Player *myplayer = m_env.getLocalPlayer();
1956 if(myplayer == NULL)
1959 u16 our_peer_id = m_con.GetPeerID();
1961 // Set peer id if not set already
1962 if(myplayer->peer_id == PEER_ID_INEXISTENT)
1963 myplayer->peer_id = our_peer_id;
1964 // Check that an existing peer_id is the same as the connection's
1965 assert(myplayer->peer_id == our_peer_id);
1967 SharedBuffer<u8> data(2+2);
1968 writeU16(&data[0], TOSERVER_PLAYERITEM);
1969 writeU16(&data[2], item);
1972 Send(0, data, true);
1975 void Client::removeNode(v3s16 p)
1977 core::map<v3s16, MapBlock*> modified_blocks;
1981 //TimeTaker t("removeNodeAndUpdate", m_device);
1982 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
1984 catch(InvalidPositionException &e)
1988 // add urgent task to update the modified node
1989 addUpdateMeshTaskForNode(p, false, true);
1991 for(core::map<v3s16, MapBlock * >::Iterator
1992 i = modified_blocks.getIterator();
1993 i.atEnd() == false; i++)
1995 v3s16 p = i.getNode()->getKey();
1996 addUpdateMeshTaskWithEdge(p);
2000 void Client::addNode(v3s16 p, MapNode n)
2002 TimeTaker timer1("Client::addNode()");
2004 core::map<v3s16, MapBlock*> modified_blocks;
2008 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
2009 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks);
2011 catch(InvalidPositionException &e)
2014 for(core::map<v3s16, MapBlock * >::Iterator
2015 i = modified_blocks.getIterator();
2016 i.atEnd() == false; i++)
2018 v3s16 p = i.getNode()->getKey();
2019 addUpdateMeshTaskWithEdge(p);
2023 void Client::setPlayerControl(PlayerControl &control)
2025 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2026 LocalPlayer *player = m_env.getLocalPlayer();
2027 assert(player != NULL);
2028 player->control = control;
2031 void Client::selectPlayerItem(u16 item)
2033 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2034 m_playeritem = item;
2035 m_inventory_updated = true;
2036 sendPlayerItem(item);
2039 // Returns true if the inventory of the local player has been
2040 // updated from the server. If it is true, it is set to false.
2041 bool Client::getLocalInventoryUpdated()
2043 // m_inventory_updated is behind envlock
2044 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2045 bool updated = m_inventory_updated;
2046 m_inventory_updated = false;
2050 // Copies the inventory of the local player to parameter
2051 void Client::getLocalInventory(Inventory &dst)
2053 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2054 Player *player = m_env.getLocalPlayer();
2055 assert(player != NULL);
2056 dst = player->inventory;
2059 Inventory* Client::getInventory(const InventoryLocation &loc)
2062 case InventoryLocation::UNDEFINED:
2065 case InventoryLocation::CURRENT_PLAYER:
2067 Player *player = m_env.getLocalPlayer();
2068 assert(player != NULL);
2069 return &player->inventory;
2072 case InventoryLocation::PLAYER:
2074 Player *player = m_env.getPlayer(loc.name.c_str());
2077 return &player->inventory;
2080 case InventoryLocation::NODEMETA:
2082 NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p);
2085 return meta->getInventory();
2093 void Client::inventoryAction(InventoryAction *a)
2096 Send it to the server
2098 sendInventoryAction(a);
2101 Predict some local inventory changes
2103 a->clientApply(this, this);
2106 ClientActiveObject * Client::getSelectedActiveObject(
2108 v3f from_pos_f_on_map,
2109 core::line3d<f32> shootline_on_map
2112 core::array<DistanceSortedActiveObject> objects;
2114 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
2116 //infostream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
2119 // After this, the closest object is the first in the array.
2122 for(u32 i=0; i<objects.size(); i++)
2124 ClientActiveObject *obj = objects[i].obj;
2126 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
2127 if(selection_box == NULL)
2130 v3f pos = obj->getPosition();
2132 core::aabbox3d<f32> offsetted_box(
2133 selection_box->MinEdge + pos,
2134 selection_box->MaxEdge + pos
2137 if(offsetted_box.intersectsWithLine(shootline_on_map))
2139 //infostream<<"Returning selected object"<<std::endl;
2144 //infostream<<"No object selected; returning NULL."<<std::endl;
2148 void Client::printDebugInfo(std::ostream &os)
2150 //JMutexAutoLock lock1(m_fetchblock_mutex);
2151 /*JMutexAutoLock lock2(m_incoming_queue_mutex);
2153 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
2154 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
2155 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
2159 core::list<std::wstring> Client::getConnectedPlayerNames()
2161 core::list<Player*> players = m_env.getPlayers(true);
2162 core::list<std::wstring> playerNames;
2163 for(core::list<Player*>::Iterator
2164 i = players.begin();
2165 i != players.end(); i++)
2167 Player *player = *i;
2168 playerNames.push_back(narrow_to_wide(player->getName()));
2173 float Client::getAnimationTime()
2175 return m_animation_time;
2178 int Client::getCrackLevel()
2180 return m_crack_level;
2183 void Client::setCrack(int level, v3s16 pos)
2185 int old_crack_level = m_crack_level;
2186 v3s16 old_crack_pos = m_crack_pos;
2188 m_crack_level = level;
2191 if(old_crack_level >= 0 && (level < 0 || pos != old_crack_pos))
2194 addUpdateMeshTaskForNode(old_crack_pos, false, true);
2196 if(level >= 0 && (old_crack_level < 0 || pos != old_crack_pos))
2199 addUpdateMeshTaskForNode(pos, false, true);
2205 Player *player = m_env.getLocalPlayer();
2206 assert(player != NULL);
2210 bool Client::getChatMessage(std::wstring &message)
2212 if(m_chat_queue.size() == 0)
2214 message = m_chat_queue.pop_front();
2218 void Client::typeChatMessage(const std::wstring &message)
2220 // Discard empty line
2225 sendChatMessage(message);
2228 if (message[0] == L'/')
2230 m_chat_queue.push_back(
2231 (std::wstring)L"issued command: "+message);
2235 LocalPlayer *player = m_env.getLocalPlayer();
2236 assert(player != NULL);
2237 std::wstring name = narrow_to_wide(player->getName());
2238 m_chat_queue.push_back(
2239 (std::wstring)L"<"+name+L"> "+message);
2243 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
2245 /*infostream<<"Client::addUpdateMeshTask(): "
2246 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2247 <<" ack_to_server="<<ack_to_server
2248 <<" urgent="<<urgent
2251 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2256 Create a task to update the mesh of the block
2259 MeshMakeData *data = new MeshMakeData(this);
2262 //TimeTaker timer("data fill");
2264 // Debug: 1-6ms, avg=2ms
2266 data->setCrack(m_crack_level, m_crack_pos);
2267 data->setSmoothLighting(g_settings->getBool("smooth_lighting"));
2271 //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
2273 // Add task to queue
2274 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server, urgent);
2276 /*infostream<<"Mesh update input queue size is "
2277 <<m_mesh_update_thread.m_queue_in.size()
2281 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent)
2285 infostream<<"Client::addUpdateMeshTaskWithEdge(): "
2286 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2291 v3s16 p = blockpos + v3s16(0,0,0);
2292 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2293 addUpdateMeshTask(p, ack_to_server, urgent);
2295 catch(InvalidPositionException &e){}
2298 v3s16 p = blockpos + v3s16(-1,0,0);
2299 addUpdateMeshTask(p, false, urgent);
2301 catch(InvalidPositionException &e){}
2303 v3s16 p = blockpos + v3s16(0,-1,0);
2304 addUpdateMeshTask(p, false, urgent);
2306 catch(InvalidPositionException &e){}
2308 v3s16 p = blockpos + v3s16(0,0,-1);
2309 addUpdateMeshTask(p, false, urgent);
2311 catch(InvalidPositionException &e){}
2314 void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool urgent)
2318 infostream<<"Client::addUpdateMeshTaskForNode(): "
2319 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2323 v3s16 blockpos = getNodeBlockPos(nodepos);
2324 v3s16 blockpos_relative = blockpos * MAP_BLOCKSIZE;
2327 v3s16 p = blockpos + v3s16(0,0,0);
2328 addUpdateMeshTask(p, ack_to_server, urgent);
2330 catch(InvalidPositionException &e){}
2332 if(nodepos.X == blockpos_relative.X){
2334 v3s16 p = blockpos + v3s16(-1,0,0);
2335 addUpdateMeshTask(p, false, urgent);
2337 catch(InvalidPositionException &e){}
2339 if(nodepos.Y == blockpos_relative.Y){
2341 v3s16 p = blockpos + v3s16(0,-1,0);
2342 addUpdateMeshTask(p, false, urgent);
2344 catch(InvalidPositionException &e){}
2346 if(nodepos.Z == blockpos_relative.Z){
2348 v3s16 p = blockpos + v3s16(0,0,-1);
2349 addUpdateMeshTask(p, false, urgent);
2351 catch(InvalidPositionException &e){}
2355 ClientEvent Client::getClientEvent()
2357 if(m_client_event_queue.size() == 0)
2360 event.type = CE_NONE;
2363 return m_client_event_queue.pop_front();
2366 void Client::afterContentReceived()
2368 assert(m_itemdef_received);
2369 assert(m_nodedef_received);
2370 assert(m_media_received);
2372 // remove the information about which checksum each texture
2374 m_media_name_sha1_map.clear();
2376 // Rebuild inherited images and recreate textures
2377 m_tsrc->rebuildImagesAndTextures();
2379 // Update texture atlas
2380 if(g_settings->getBool("enable_texture_atlas"))
2381 m_tsrc->buildMainAtlas(this);
2383 // Update node aliases
2384 m_nodedef->updateAliases(m_itemdef);
2386 // Update node textures
2387 m_nodedef->updateTextures(m_tsrc);
2389 // Update item textures and meshes
2390 m_itemdef->updateTexturesAndMeshes(this);
2392 // Start mesh update thread after setting up content definitions
2393 m_mesh_update_thread.Start();
2396 float Client::getRTT(void)
2399 return m_con.GetPeerAvgRTT(PEER_ID_SERVER);
2400 } catch(con::PeerNotFoundException &e){
2405 // IGameDef interface
2407 IItemDefManager* Client::getItemDefManager()
2411 INodeDefManager* Client::getNodeDefManager()
2415 ICraftDefManager* Client::getCraftDefManager()
2418 //return m_craftdef;
2420 ITextureSource* Client::getTextureSource()
2424 u16 Client::allocateUnknownNodeId(const std::string &name)
2426 errorstream<<"Client::allocateUnknownNodeId(): "
2427 <<"Client cannot allocate node IDs"<<std::endl;
2429 return CONTENT_IGNORE;
2431 ISoundManager* Client::getSoundManager()
2435 MtEventManager* Client::getEventManager()