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"
43 static std::string getTextureCacheDir()
45 return porting::path_user + DIR_DELIM + "cache" + DIR_DELIM + "textures";
52 TextureRequest(const std::string &name_=""):
61 QueuedMeshUpdate::QueuedMeshUpdate():
64 ack_block_to_server(false)
68 QueuedMeshUpdate::~QueuedMeshUpdate()
78 MeshUpdateQueue::MeshUpdateQueue()
83 MeshUpdateQueue::~MeshUpdateQueue()
85 JMutexAutoLock lock(m_mutex);
87 for(std::vector<QueuedMeshUpdate*>::iterator
89 i != m_queue.end(); i++)
91 QueuedMeshUpdate *q = *i;
97 peer_id=0 adds with nobody to send to
99 void MeshUpdateQueue::addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_server, bool urgent)
101 DSTACK(__FUNCTION_NAME);
105 JMutexAutoLock lock(m_mutex);
111 Find if block is already in queue.
112 If it is, update the data and quit.
114 for(std::vector<QueuedMeshUpdate*>::iterator
116 i != m_queue.end(); i++)
118 QueuedMeshUpdate *q = *i;
124 if(ack_block_to_server)
125 q->ack_block_to_server = true;
133 QueuedMeshUpdate *q = new QueuedMeshUpdate;
136 q->ack_block_to_server = ack_block_to_server;
137 m_queue.push_back(q);
140 // Returned pointer must be deleted
141 // Returns NULL if queue is empty
142 QueuedMeshUpdate * MeshUpdateQueue::pop()
144 JMutexAutoLock lock(m_mutex);
146 bool must_be_urgent = !m_urgents.empty();
147 for(std::vector<QueuedMeshUpdate*>::iterator
149 i != m_queue.end(); i++)
151 QueuedMeshUpdate *q = *i;
152 if(must_be_urgent && m_urgents.count(q->p) == 0)
155 m_urgents.erase(q->p);
165 void * MeshUpdateThread::Thread()
169 log_register_thread("MeshUpdateThread");
171 DSTACK(__FUNCTION_NAME);
173 BEGIN_DEBUG_EXCEPTION_HANDLER
177 /*// Wait for output queue to flush.
178 // Allow 2 in queue, this makes less frametime jitter.
179 // Umm actually, there is no much difference
180 if(m_queue_out.size() >= 2)
186 QueuedMeshUpdate *q = m_queue_in.pop();
193 ScopeProfiler sp(g_profiler, "Client: Mesh making");
195 MapBlockMesh *mesh_new = new MapBlockMesh(q->data);
196 if(mesh_new->getMesh()->getMeshBufferCount() == 0)
205 r.ack_block_to_server = q->ack_block_to_server;
207 /*infostream<<"MeshUpdateThread: Processed "
208 <<"("<<q->p.X<<","<<q->p.Y<<","<<q->p.Z<<")"
211 m_queue_out.push_back(r);
216 END_DEBUG_EXCEPTION_HANDLER(errorstream)
222 IrrlichtDevice *device,
223 const char *playername,
224 std::string password,
225 MapDrawControl &control,
226 IWritableTextureSource *tsrc,
227 IWritableItemDefManager *itemdef,
228 IWritableNodeDefManager *nodedef,
235 m_mesh_update_thread(this),
237 new ClientMap(this, this, control,
238 device->getSceneManager()->getRootSceneNode(),
239 device->getSceneManager(), 666),
240 device->getSceneManager(),
243 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
245 m_server_ser_ver(SER_FMT_VER_INVALID),
247 m_inventory_updated(false),
248 m_inventory_from_server(NULL),
249 m_inventory_from_server_age(0.0),
254 m_password(password),
255 m_access_denied(false),
256 m_texture_receive_progress(0),
257 m_textures_received(false),
258 m_itemdef_received(false),
259 m_nodedef_received(false),
260 m_time_of_day_set(false),
261 m_last_time_of_day_f(-1),
262 m_time_of_day_update_timer(0)
264 m_packetcounter_timer = 0.0;
265 //m_delete_unused_sectors_timer = 0.0;
266 m_connection_reinit_timer = 0.0;
267 m_avg_rtt_timer = 0.0;
268 m_playerpos_send_timer = 0.0;
269 m_ignore_damage_timer = 0.0;
271 // Build main texture atlas, now that the GameDef exists (that is, us)
272 if(g_settings->getBool("enable_texture_atlas"))
273 m_tsrc->buildMainAtlas(this);
275 infostream<<"Not building texture atlas."<<std::endl;
281 Player *player = new LocalPlayer(this);
283 player->updateName(playername);
285 m_env.addPlayer(player);
292 //JMutexAutoLock conlock(m_con_mutex); //bulk comment-out
296 m_mesh_update_thread.setRun(false);
297 while(m_mesh_update_thread.IsRunning())
300 delete m_inventory_from_server;
303 void Client::connect(Address address)
305 DSTACK(__FUNCTION_NAME);
306 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
307 m_con.SetTimeoutMs(0);
308 m_con.Connect(address);
311 bool Client::connectedAndInitialized()
313 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
315 if(m_con.Connected() == false)
318 if(m_server_ser_ver == SER_FMT_VER_INVALID)
324 void Client::step(float dtime)
326 DSTACK(__FUNCTION_NAME);
332 if(m_ignore_damage_timer > dtime)
333 m_ignore_damage_timer -= dtime;
335 m_ignore_damage_timer = 0.0;
337 m_animation_time += dtime;
338 if(m_animation_time > 60.0)
339 m_animation_time -= 60.0;
341 m_time_of_day_update_timer += dtime;
343 //infostream<<"Client steps "<<dtime<<std::endl;
346 //TimeTaker timer("ReceiveAll()", m_device);
352 //TimeTaker timer("m_con_mutex + m_con.RunTimeouts()", m_device);
354 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
355 m_con.RunTimeouts(dtime);
362 float &counter = m_packetcounter_timer;
368 infostream<<"Client packetcounter (20s):"<<std::endl;
369 m_packetcounter.print(infostream);
370 m_packetcounter.clear();
374 // Get connection status
375 bool connected = connectedAndInitialized();
380 Delete unused sectors
382 NOTE: This jams the game for a while because deleting sectors
386 float &counter = m_delete_unused_sectors_timer;
394 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
396 core::list<v3s16> deleted_blocks;
398 float delete_unused_sectors_timeout =
399 g_settings->getFloat("client_delete_unused_sectors_timeout");
401 // Delete sector blocks
402 /*u32 num = m_env.getMap().unloadUnusedData
403 (delete_unused_sectors_timeout,
404 true, &deleted_blocks);*/
406 // Delete whole sectors
407 m_env.getMap().unloadUnusedData
408 (delete_unused_sectors_timeout,
411 if(deleted_blocks.size() > 0)
413 /*infostream<<"Client: Deleted blocks of "<<num
414 <<" unused sectors"<<std::endl;*/
415 /*infostream<<"Client: Deleted "<<num
416 <<" unused sectors"<<std::endl;*/
422 // Env is locked so con can be locked.
423 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
425 core::list<v3s16>::Iterator i = deleted_blocks.begin();
426 core::list<v3s16> sendlist;
429 if(sendlist.size() == 255 || i == deleted_blocks.end())
431 if(sendlist.size() == 0)
440 u32 replysize = 2+1+6*sendlist.size();
441 SharedBuffer<u8> reply(replysize);
442 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
443 reply[2] = sendlist.size();
445 for(core::list<v3s16>::Iterator
446 j = sendlist.begin();
447 j != sendlist.end(); j++)
449 writeV3S16(&reply[2+1+6*k], *j);
452 m_con.Send(PEER_ID_SERVER, 1, reply, true);
454 if(i == deleted_blocks.end())
460 sendlist.push_back(*i);
468 if(connected == false)
470 float &counter = m_connection_reinit_timer;
476 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
478 Player *myplayer = m_env.getLocalPlayer();
479 assert(myplayer != NULL);
481 // Send TOSERVER_INIT
482 // [0] u16 TOSERVER_INIT
483 // [2] u8 SER_FMT_VER_HIGHEST
484 // [3] u8[20] player_name
485 // [23] u8[28] password (new in some version)
486 // [51] u16 client network protocol version (new in some version)
487 SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2);
488 writeU16(&data[0], TOSERVER_INIT);
489 writeU8(&data[2], SER_FMT_VER_HIGHEST);
491 memset((char*)&data[3], 0, PLAYERNAME_SIZE);
492 snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
494 /*infostream<<"Client: sending initial password hash: \""<<m_password<<"\""
497 memset((char*)&data[23], 0, PASSWORD_SIZE);
498 snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
500 // This should be incremented in each version
501 writeU16(&data[51], PROTOCOL_VERSION);
503 // Send as unreliable
504 Send(0, data, false);
507 // Not connected, return
512 Do stuff if connected
516 Run Map's timers and unload unused data
518 const float map_timer_and_unload_dtime = 5.25;
519 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
521 ScopeProfiler sp(g_profiler, "Client: map timer and unload");
522 core::list<v3s16> deleted_blocks;
523 m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
524 g_settings->getFloat("client_unload_unused_data_timeout"),
527 /*if(deleted_blocks.size() > 0)
528 infostream<<"Client: Unloaded "<<deleted_blocks.size()
529 <<" unused blocks"<<std::endl;*/
533 NOTE: This loop is intentionally iterated the way it is.
536 core::list<v3s16>::Iterator i = deleted_blocks.begin();
537 core::list<v3s16> sendlist;
540 if(sendlist.size() == 255 || i == deleted_blocks.end())
542 if(sendlist.size() == 0)
551 u32 replysize = 2+1+6*sendlist.size();
552 SharedBuffer<u8> reply(replysize);
553 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
554 reply[2] = sendlist.size();
556 for(core::list<v3s16>::Iterator
557 j = sendlist.begin();
558 j != sendlist.end(); j++)
560 writeV3S16(&reply[2+1+6*k], *j);
563 m_con.Send(PEER_ID_SERVER, 1, reply, true);
565 if(i == deleted_blocks.end())
571 sendlist.push_back(*i);
581 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
583 // Control local player (0ms)
584 LocalPlayer *player = m_env.getLocalPlayer();
585 assert(player != NULL);
586 player->applyControl(dtime);
588 //TimeTaker envtimer("env step", m_device);
597 ClientEnvEvent event = m_env.getClientEvent();
598 if(event.type == CEE_NONE)
602 else if(event.type == CEE_PLAYER_DAMAGE)
604 if(m_ignore_damage_timer <= 0)
606 u8 damage = event.player_damage.amount;
608 if(event.player_damage.send_to_server)
611 // Add to ClientEvent queue
613 event.type = CE_PLAYER_DAMAGE;
614 event.player_damage.amount = damage;
615 m_client_event_queue.push_back(event);
625 float &counter = m_avg_rtt_timer;
630 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
631 // connectedAndInitialized() is true, peer exists.
632 float avg_rtt = m_con.GetPeerAvgRTT(PEER_ID_SERVER);
633 infostream<<"Client: avg_rtt="<<avg_rtt<<std::endl;
638 Send player position to server
641 float &counter = m_playerpos_send_timer;
651 Replace updated meshes
654 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
656 //TimeTaker timer("** Processing mesh update result queue");
659 /*infostream<<"Mesh update result queue size is "
660 <<m_mesh_update_thread.m_queue_out.size()
663 int num_processed_meshes = 0;
664 while(m_mesh_update_thread.m_queue_out.size() > 0)
666 num_processed_meshes++;
667 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_front();
668 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
671 //JMutexAutoLock lock(block->mesh_mutex);
673 // Delete the old mesh
674 if(block->mesh != NULL)
676 // TODO: Remove hardware buffers of meshbuffers of block->mesh
681 // Replace with the new mesh
682 block->mesh = r.mesh;
684 if(r.ack_block_to_server)
686 /*infostream<<"Client: ACK block ("<<r.p.X<<","<<r.p.Y
687 <<","<<r.p.Z<<")"<<std::endl;*/
698 u32 replysize = 2+1+6;
699 SharedBuffer<u8> reply(replysize);
700 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
702 writeV3S16(&reply[3], r.p);
704 m_con.Send(PEER_ID_SERVER, 1, reply, true);
707 if(num_processed_meshes > 0)
708 g_profiler->graphAdd("num_processed_meshes", num_processed_meshes);
712 If the server didn't update the inventory in a while, revert
713 the local inventory (so the player notices the lag problem
714 and knows something is wrong).
716 if(m_inventory_from_server)
718 float interval = 10.0;
719 float count_before = floor(m_inventory_from_server_age / interval);
721 m_inventory_from_server_age += dtime;
723 float count_after = floor(m_inventory_from_server_age / interval);
725 if(count_after != count_before)
727 // Do this every <interval> seconds after TOCLIENT_INVENTORY
728 // Reset the locally changed inventory to the authoritative inventory
729 Player *player = m_env.getLocalPlayer();
730 player->inventory = *m_inventory_from_server;
731 m_inventory_updated = true;
736 // Virtual methods from con::PeerHandler
737 void Client::peerAdded(con::Peer *peer)
739 infostream<<"Client::peerAdded(): peer->id="
740 <<peer->id<<std::endl;
742 void Client::deletingPeer(con::Peer *peer, bool timeout)
744 infostream<<"Client::deletingPeer(): "
745 "Server Peer is getting deleted "
746 <<"(timeout="<<timeout<<")"<<std::endl;
749 void Client::ReceiveAll()
751 DSTACK(__FUNCTION_NAME);
752 u32 start_ms = porting::getTimeMs();
755 // Limit time even if there would be huge amounts of data to
757 if(porting::getTimeMs() > start_ms + 100)
762 g_profiler->graphAdd("client_received_packets", 1);
764 catch(con::NoIncomingDataException &e)
768 catch(con::InvalidIncomingDataException &e)
770 infostream<<"Client::ReceiveAll(): "
771 "InvalidIncomingDataException: what()="
772 <<e.what()<<std::endl;
777 void Client::Receive()
779 DSTACK(__FUNCTION_NAME);
780 SharedBuffer<u8> data;
784 //TimeTaker t1("con mutex and receive", m_device);
785 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
786 datasize = m_con.Receive(sender_peer_id, data);
788 //TimeTaker t1("ProcessData", m_device);
789 ProcessData(*data, datasize, sender_peer_id);
793 sender_peer_id given to this shall be quaranteed to be a valid peer
795 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
797 DSTACK(__FUNCTION_NAME);
799 // Ignore packets that don't even fit a command
802 m_packetcounter.add(60000);
806 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
808 //infostream<<"Client: received command="<<command<<std::endl;
809 m_packetcounter.add((u16)command);
812 If this check is removed, be sure to change the queue
813 system to know the ids
815 if(sender_peer_id != PEER_ID_SERVER)
817 infostream<<"Client::ProcessData(): Discarding data not "
818 "coming from server: peer_id="<<sender_peer_id
823 u8 ser_version = m_server_ser_ver;
825 //infostream<<"Client received command="<<(int)command<<std::endl;
827 if(command == TOCLIENT_INIT)
832 u8 deployed = data[2];
834 infostream<<"Client: TOCLIENT_INIT received with "
835 "deployed="<<((int)deployed&0xff)<<std::endl;
837 if(deployed < SER_FMT_VER_LOWEST
838 || deployed > SER_FMT_VER_HIGHEST)
840 infostream<<"Client: TOCLIENT_INIT: Server sent "
841 <<"unsupported ser_fmt_ver"<<std::endl;
845 m_server_ser_ver = deployed;
847 // Get player position
848 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
849 if(datasize >= 2+1+6)
850 playerpos_s16 = readV3S16(&data[2+1]);
851 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
854 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
856 // Set player position
857 Player *player = m_env.getLocalPlayer();
858 assert(player != NULL);
859 player->setPosition(playerpos_f);
862 if(datasize >= 2+1+6+8)
865 m_map_seed = readU64(&data[2+1+6]);
866 infostream<<"Client: received map seed: "<<m_map_seed<<std::endl;
871 SharedBuffer<u8> reply(replysize);
872 writeU16(&reply[0], TOSERVER_INIT2);
874 m_con.Send(PEER_ID_SERVER, 1, reply, true);
879 if(command == TOCLIENT_ACCESS_DENIED)
881 // The server didn't like our password. Note, this needs
882 // to be processed even if the serialisation format has
883 // not been agreed yet, the same as TOCLIENT_INIT.
884 m_access_denied = true;
885 m_access_denied_reason = L"Unknown";
888 std::string datastring((char*)&data[2], datasize-2);
889 std::istringstream is(datastring, std::ios_base::binary);
890 m_access_denied_reason = deSerializeWideString(is);
895 if(ser_version == SER_FMT_VER_INVALID)
897 infostream<<"Client: Server serialization"
898 " format invalid or not initialized."
899 " Skipping incoming command="<<command<<std::endl;
903 // Just here to avoid putting the two if's together when
904 // making some copypasta
907 if(command == TOCLIENT_REMOVENODE)
912 p.X = readS16(&data[2]);
913 p.Y = readS16(&data[4]);
914 p.Z = readS16(&data[6]);
916 //TimeTaker t1("TOCLIENT_REMOVENODE");
920 else if(command == TOCLIENT_ADDNODE)
922 if(datasize < 8 + MapNode::serializedLength(ser_version))
926 p.X = readS16(&data[2]);
927 p.Y = readS16(&data[4]);
928 p.Z = readS16(&data[6]);
930 //TimeTaker t1("TOCLIENT_ADDNODE");
933 n.deSerialize(&data[8], ser_version);
937 else if(command == TOCLIENT_BLOCKDATA)
939 // Ignore too small packet
944 p.X = readS16(&data[2]);
945 p.Y = readS16(&data[4]);
946 p.Z = readS16(&data[6]);
948 /*infostream<<"Client: Thread: BLOCKDATA for ("
949 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
950 /*infostream<<"Client: Thread: BLOCKDATA for ("
951 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
953 std::string datastring((char*)&data[8], datasize-8);
954 std::istringstream istr(datastring, std::ios_base::binary);
960 sector = m_env.getMap().emergeSector(p2d);
962 assert(sector->getPos() == p2d);
964 //TimeTaker timer("MapBlock deSerialize");
967 block = sector->getBlockNoCreateNoEx(p.Y);
971 Update an existing block
973 //infostream<<"Updating"<<std::endl;
974 block->deSerialize(istr, ser_version, false);
981 //infostream<<"Creating new"<<std::endl;
982 block = new MapBlock(&m_env.getMap(), p, this);
983 block->deSerialize(istr, ser_version, false);
984 sector->insertBlock(block);
998 u32 replysize = 2+1+6;
999 SharedBuffer<u8> reply(replysize);
1000 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
1002 writeV3S16(&reply[3], p);
1004 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1008 Add it to mesh update queue and set it to be acknowledged after update.
1010 //infostream<<"Adding mesh update task for received block"<<std::endl;
1011 addUpdateMeshTaskWithEdge(p, true);
1013 else if(command == TOCLIENT_INVENTORY)
1018 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
1021 //TimeTaker t2("mutex locking", m_device);
1022 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1025 //TimeTaker t3("istringstream init", m_device);
1026 std::string datastring((char*)&data[2], datasize-2);
1027 std::istringstream is(datastring, std::ios_base::binary);
1030 //m_env.printPlayers(infostream);
1032 //TimeTaker t4("player get", m_device);
1033 Player *player = m_env.getLocalPlayer();
1034 assert(player != NULL);
1037 //TimeTaker t1("inventory.deSerialize()", m_device);
1038 player->inventory.deSerialize(is);
1041 m_inventory_updated = true;
1043 delete m_inventory_from_server;
1044 m_inventory_from_server = new Inventory(player->inventory);
1045 m_inventory_from_server_age = 0.0;
1047 //infostream<<"Client got player inventory:"<<std::endl;
1048 //player->inventory.print(infostream);
1051 else if(command == TOCLIENT_TIME_OF_DAY)
1056 u16 time_of_day = readU16(&data[2]);
1057 time_of_day = time_of_day % 24000;
1058 //infostream<<"Client: time_of_day="<<time_of_day<<std::endl;
1059 float time_speed = 0;
1060 if(datasize >= 2 + 2 + 4){
1061 time_speed = readF1000(&data[4]);
1063 // Old message; try to approximate speed of time by ourselves
1064 float time_of_day_f = (float)time_of_day / 24000.0;
1065 float tod_diff_f = 0;
1066 if(time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
1067 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0;
1069 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
1070 m_last_time_of_day_f = time_of_day_f;
1071 float time_diff = m_time_of_day_update_timer;
1072 m_time_of_day_update_timer = 0;
1073 if(m_time_of_day_set){
1074 time_speed = 3600.0*24.0 * tod_diff_f / time_diff;
1075 infostream<<"Client: Measured time_of_day speed (old format): "
1076 <<time_speed<<" tod_diff_f="<<tod_diff_f
1077 <<" time_diff="<<time_diff<<std::endl;
1081 // Update environment
1082 m_env.setTimeOfDay(time_of_day);
1083 m_env.setTimeOfDaySpeed(time_speed);
1084 m_time_of_day_set = true;
1086 u32 dr = m_env.getDayNightRatio();
1087 verbosestream<<"Client: time_of_day="<<time_of_day
1088 <<" time_speed="<<time_speed
1089 <<" dr="<<dr<<std::endl;
1091 else if(command == TOCLIENT_CHAT_MESSAGE)
1099 std::string datastring((char*)&data[2], datasize-2);
1100 std::istringstream is(datastring, std::ios_base::binary);
1103 is.read((char*)buf, 2);
1104 u16 len = readU16(buf);
1106 std::wstring message;
1107 for(u16 i=0; i<len; i++)
1109 is.read((char*)buf, 2);
1110 message += (wchar_t)readU16(buf);
1113 /*infostream<<"Client received chat message: "
1114 <<wide_to_narrow(message)<<std::endl;*/
1116 m_chat_queue.push_back(message);
1118 else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1120 //if(g_settings->getBool("enable_experimental"))
1124 u16 count of removed objects
1125 for all removed objects {
1128 u16 count of added objects
1129 for all added objects {
1132 u32 initialization data length
1133 string initialization data
1138 // Get all data except the command number
1139 std::string datastring((char*)&data[2], datasize-2);
1140 // Throw them in an istringstream
1141 std::istringstream is(datastring, std::ios_base::binary);
1145 // Read removed objects
1147 u16 removed_count = readU16((u8*)buf);
1148 for(u16 i=0; i<removed_count; i++)
1151 u16 id = readU16((u8*)buf);
1154 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1155 m_env.removeActiveObject(id);
1159 // Read added objects
1161 u16 added_count = readU16((u8*)buf);
1162 for(u16 i=0; i<added_count; i++)
1165 u16 id = readU16((u8*)buf);
1167 u8 type = readU8((u8*)buf);
1168 std::string data = deSerializeLongString(is);
1171 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1172 m_env.addActiveObject(id, type, data);
1177 else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1179 //if(g_settings->getBool("enable_experimental"))
1191 // Get all data except the command number
1192 std::string datastring((char*)&data[2], datasize-2);
1193 // Throw them in an istringstream
1194 std::istringstream is(datastring, std::ios_base::binary);
1196 while(is.eof() == false)
1200 u16 id = readU16((u8*)buf);
1204 u16 message_size = readU16((u8*)buf);
1205 std::string message;
1206 message.reserve(message_size);
1207 for(u16 i=0; i<message_size; i++)
1210 message.append(buf, 1);
1212 // Pass on to the environment
1214 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1215 m_env.processActiveObjectMessage(id, message);
1220 else if(command == TOCLIENT_HP)
1222 std::string datastring((char*)&data[2], datasize-2);
1223 std::istringstream is(datastring, std::ios_base::binary);
1224 Player *player = m_env.getLocalPlayer();
1225 assert(player != NULL);
1226 u8 oldhp = player->hp;
1232 // Add to ClientEvent queue
1234 event.type = CE_PLAYER_DAMAGE;
1235 event.player_damage.amount = oldhp - hp;
1236 m_client_event_queue.push_back(event);
1239 else if(command == TOCLIENT_MOVE_PLAYER)
1241 std::string datastring((char*)&data[2], datasize-2);
1242 std::istringstream is(datastring, std::ios_base::binary);
1243 Player *player = m_env.getLocalPlayer();
1244 assert(player != NULL);
1245 v3f pos = readV3F1000(is);
1246 f32 pitch = readF1000(is);
1247 f32 yaw = readF1000(is);
1248 player->setPosition(pos);
1249 /*player->setPitch(pitch);
1250 player->setYaw(yaw);*/
1252 infostream<<"Client got TOCLIENT_MOVE_PLAYER"
1253 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1259 Add to ClientEvent queue.
1260 This has to be sent to the main program because otherwise
1261 it would just force the pitch and yaw values to whatever
1262 the camera points to.
1265 event.type = CE_PLAYER_FORCE_MOVE;
1266 event.player_force_move.pitch = pitch;
1267 event.player_force_move.yaw = yaw;
1268 m_client_event_queue.push_back(event);
1270 // Ignore damage for a few seconds, so that the player doesn't
1271 // get damage from falling on ground
1272 m_ignore_damage_timer = 3.0;
1274 else if(command == TOCLIENT_PLAYERITEM)
1276 std::string datastring((char*)&data[2], datasize-2);
1277 std::istringstream is(datastring, std::ios_base::binary);
1279 u16 count = readU16(is);
1281 for (u16 i = 0; i < count; ++i) {
1282 u16 peer_id = readU16(is);
1283 Player *player = m_env.getPlayer(peer_id);
1287 infostream<<"Client: ignoring player item "
1288 << deSerializeString(is)
1289 << " for non-existing peer id " << peer_id
1292 } else if (player->isLocal()) {
1293 infostream<<"Client: ignoring player item "
1294 << deSerializeString(is)
1295 << " for local player" << std::endl;
1298 InventoryList *inv = player->inventory.getList("main");
1299 std::string itemstring(deSerializeString(is));
1301 item.deSerialize(itemstring, m_itemdef);
1302 inv->changeItem(0, item);
1303 if(itemstring.empty())
1305 infostream<<"Client: empty player item for peer "
1306 <<peer_id<<std::endl;
1310 infostream<<"Client: player item for peer "
1311 <<peer_id<<": "<<itemstring<<std::endl;
1316 else if(command == TOCLIENT_DEATHSCREEN)
1318 std::string datastring((char*)&data[2], datasize-2);
1319 std::istringstream is(datastring, std::ios_base::binary);
1321 bool set_camera_point_target = readU8(is);
1322 v3f camera_point_target = readV3F1000(is);
1325 event.type = CE_DEATHSCREEN;
1326 event.deathscreen.set_camera_point_target = set_camera_point_target;
1327 event.deathscreen.camera_point_target_x = camera_point_target.X;
1328 event.deathscreen.camera_point_target_y = camera_point_target.Y;
1329 event.deathscreen.camera_point_target_z = camera_point_target.Z;
1330 m_client_event_queue.push_back(event);
1332 else if(command == TOCLIENT_ANNOUNCE_TEXTURES)
1334 io::IFileSystem *irrfs = m_device->getFileSystem();
1335 video::IVideoDriver *vdrv = m_device->getVideoDriver();
1337 std::string datastring((char*)&data[2], datasize-2);
1338 std::istringstream is(datastring, std::ios_base::binary);
1340 // Mesh update thread must be stopped while
1341 // updating content definitions
1342 assert(!m_mesh_update_thread.IsRunning());
1344 int num_textures = readU16(is);
1346 core::list<TextureRequest> texture_requests;
1348 for(int i=0; i<num_textures; i++){
1350 bool texture_found = false;
1352 //read texture from cache
1353 std::string name = deSerializeString(is);
1354 std::string sha1_texture = deSerializeString(is);
1356 // if name contains illegal characters, ignore the texture
1357 if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
1358 errorstream<<"Client: ignoring illegal texture name "
1359 <<"sent by server: \""<<name<<"\""<<std::endl;
1363 std::string tpath = getTextureCacheDir() + DIR_DELIM + name;
1365 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
1368 if(fis.good() == false){
1369 infostream<<"Client::Texture not found in cache: "
1370 <<name << " expected it at: "<<tpath<<std::endl;
1374 std::ostringstream tmp_os(std::ios_base::binary);
1378 fis.read(buf, 1024);
1379 std::streamsize len = fis.gcount();
1380 tmp_os.write(buf, len);
1389 infostream<<"Client: Failed to read texture from cache\""
1390 <<name<<"\""<<std::endl;
1395 sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
1397 unsigned char *digest = sha1.getDigest();
1399 std::string digest_string = base64_encode(digest, 20);
1401 if (digest_string == sha1_texture) {
1402 // Silly irrlicht's const-incorrectness
1403 Buffer<char> data_rw(tmp_os.str().c_str(), tmp_os.str().size());
1405 // Create an irrlicht memory file
1406 io::IReadFile *rfile = irrfs->createMemoryReadFile(
1407 *data_rw, tmp_os.str().size(), "_tempreadfile");
1410 video::IImage *img = vdrv->createImageFromFile(rfile);
1412 infostream<<"Client: Cannot create image from data of "
1413 <<"received texture \""<<name<<"\""<<std::endl;
1417 m_tsrc->insertSourceImage(name, img);
1421 texture_found = true;
1425 infostream<<"Client::Texture cached sha1 hash not matching server hash: "
1426 <<name << ": server ->"<<sha1_texture <<" client -> "<<digest_string<<std::endl;
1433 //add texture request
1434 if (!texture_found) {
1435 infostream<<"Client: Adding texture to request list: \""
1436 <<name<<"\""<<std::endl;
1437 texture_requests.push_back(TextureRequest(name));
1443 event.type = CE_TEXTURES_UPDATED;
1444 m_client_event_queue.push_back(event);
1447 //send Texture request
1450 u16 number of textures requested
1456 std::ostringstream os(std::ios_base::binary);
1461 writeU16(buf, TOSERVER_REQUEST_TEXTURES);
1462 os.write((char*)buf, 2);
1464 writeU16(buf,texture_requests.size());
1465 os.write((char*)buf, 2);
1468 for(core::list<TextureRequest>::Iterator i = texture_requests.begin();
1469 i != texture_requests.end(); i++) {
1470 os<<serializeString(i->name);
1474 std::string s = os.str();
1475 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1477 Send(0, data, true);
1478 infostream<<"Client: Sending request list to server " <<std::endl;
1480 else if(command == TOCLIENT_TEXTURES)
1482 io::IFileSystem *irrfs = m_device->getFileSystem();
1483 video::IVideoDriver *vdrv = m_device->getVideoDriver();
1485 std::string datastring((char*)&data[2], datasize-2);
1486 std::istringstream is(datastring, std::ios_base::binary);
1488 // Mesh update thread must be stopped while
1489 // updating content definitions
1490 assert(!m_mesh_update_thread.IsRunning());
1494 u16 total number of texture bunches
1495 u16 index of this bunch
1496 u32 number of textures in this bunch
1504 int num_bunches = readU16(is);
1505 int bunch_i = readU16(is);
1506 m_texture_receive_progress = (float)bunch_i / (float)(num_bunches - 1);
1507 if(bunch_i == num_bunches - 1)
1508 m_textures_received = true;
1509 int num_textures = readU32(is);
1510 infostream<<"Client: Received textures: bunch "<<bunch_i<<"/"
1511 <<num_bunches<<" textures="<<num_textures
1512 <<" size="<<datasize<<std::endl;
1513 for(int i=0; i<num_textures; i++){
1514 std::string name = deSerializeString(is);
1515 std::string data = deSerializeLongString(is);
1517 // if name contains illegal characters, ignore the texture
1518 if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
1519 errorstream<<"Client: ignoring illegal texture name "
1520 <<"sent by server: \""<<name<<"\""<<std::endl;
1524 // Silly irrlicht's const-incorrectness
1525 Buffer<char> data_rw(data.c_str(), data.size());
1526 // Create an irrlicht memory file
1527 io::IReadFile *rfile = irrfs->createMemoryReadFile(
1528 *data_rw, data.size(), "_tempreadfile");
1531 video::IImage *img = vdrv->createImageFromFile(rfile);
1533 errorstream<<"Client: Cannot create image from data of "
1534 <<"received texture \""<<name<<"\""<<std::endl;
1539 fs::CreateAllDirs(getTextureCacheDir());
1541 std::string filename = getTextureCacheDir() + DIR_DELIM + name;
1542 std::ofstream outfile(filename.c_str(), std::ios_base::binary | std::ios_base::trunc);
1544 if (outfile.good()) {
1545 outfile.write(data.c_str(),data.length());
1549 errorstream<<"Client: Unable to open cached texture file "<< filename <<std::endl;
1552 m_tsrc->insertSourceImage(name, img);
1558 event.type = CE_TEXTURES_UPDATED;
1559 m_client_event_queue.push_back(event);
1561 else if(command == TOCLIENT_TOOLDEF)
1563 infostream<<"Client: WARNING: Ignoring TOCLIENT_TOOLDEF"<<std::endl;
1565 else if(command == TOCLIENT_NODEDEF)
1567 infostream<<"Client: Received node definitions: packet size: "
1568 <<datasize<<std::endl;
1570 // Mesh update thread must be stopped while
1571 // updating content definitions
1572 assert(!m_mesh_update_thread.IsRunning());
1574 // Decompress node definitions
1575 std::string datastring((char*)&data[2], datasize-2);
1576 std::istringstream is(datastring, std::ios_base::binary);
1577 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1578 std::ostringstream tmp_os;
1579 decompressZlib(tmp_is, tmp_os);
1581 // Deserialize node definitions
1582 std::istringstream tmp_is2(tmp_os.str());
1583 m_nodedef->deSerialize(tmp_is2);
1584 m_nodedef_received = true;
1586 else if(command == TOCLIENT_CRAFTITEMDEF)
1588 infostream<<"Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF"<<std::endl;
1590 else if(command == TOCLIENT_ITEMDEF)
1592 infostream<<"Client: Received item definitions: packet size: "
1593 <<datasize<<std::endl;
1595 // Mesh update thread must be stopped while
1596 // updating content definitions
1597 assert(!m_mesh_update_thread.IsRunning());
1599 // Decompress item definitions
1600 std::string datastring((char*)&data[2], datasize-2);
1601 std::istringstream is(datastring, std::ios_base::binary);
1602 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1603 std::ostringstream tmp_os;
1604 decompressZlib(tmp_is, tmp_os);
1606 // Deserialize node definitions
1607 std::istringstream tmp_is2(tmp_os.str());
1608 m_itemdef->deSerialize(tmp_is2);
1609 m_itemdef_received = true;
1613 infostream<<"Client: Ignoring unknown command "
1614 <<command<<std::endl;
1618 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
1620 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1621 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
1624 void Client::interact(u8 action, const PointedThing& pointed)
1626 if(connectedAndInitialized() == false){
1627 infostream<<"Client::interact() "
1628 "cancelled (not connected)"
1633 std::ostringstream os(std::ios_base::binary);
1639 [5] u32 length of the next item
1640 [9] serialized PointedThing
1642 0: start digging (from undersurface) or use
1643 1: stop digging (all parameters ignored)
1644 2: digging completed
1645 3: place block or item (to abovesurface)
1648 writeU16(os, TOSERVER_INTERACT);
1649 writeU8(os, action);
1650 writeU16(os, getPlayerItem());
1651 std::ostringstream tmp_os(std::ios::binary);
1652 pointed.serialize(tmp_os);
1653 os<<serializeLongString(tmp_os.str());
1655 std::string s = os.str();
1656 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1659 Send(0, data, true);
1662 void Client::sendSignNodeText(v3s16 p, std::string text)
1670 std::ostringstream os(std::ios_base::binary);
1674 writeU16(buf, TOSERVER_SIGNNODETEXT);
1675 os.write((char*)buf, 2);
1679 os.write((char*)buf, 6);
1681 u16 textlen = text.size();
1682 // Write text length
1683 writeS16(buf, textlen);
1684 os.write((char*)buf, 2);
1687 os.write((char*)text.c_str(), textlen);
1690 std::string s = os.str();
1691 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1693 Send(0, data, true);
1696 void Client::sendInventoryAction(InventoryAction *a)
1698 std::ostringstream os(std::ios_base::binary);
1702 writeU16(buf, TOSERVER_INVENTORY_ACTION);
1703 os.write((char*)buf, 2);
1708 std::string s = os.str();
1709 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1711 Send(0, data, true);
1714 void Client::sendChatMessage(const std::wstring &message)
1716 std::ostringstream os(std::ios_base::binary);
1720 writeU16(buf, TOSERVER_CHAT_MESSAGE);
1721 os.write((char*)buf, 2);
1724 writeU16(buf, message.size());
1725 os.write((char*)buf, 2);
1728 for(u32 i=0; i<message.size(); i++)
1732 os.write((char*)buf, 2);
1736 std::string s = os.str();
1737 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1739 Send(0, data, true);
1742 void Client::sendChangePassword(const std::wstring oldpassword,
1743 const std::wstring newpassword)
1745 Player *player = m_env.getLocalPlayer();
1749 std::string playername = player->getName();
1750 std::string oldpwd = translatePassword(playername, oldpassword);
1751 std::string newpwd = translatePassword(playername, newpassword);
1753 std::ostringstream os(std::ios_base::binary);
1754 u8 buf[2+PASSWORD_SIZE*2];
1756 [0] u16 TOSERVER_PASSWORD
1757 [2] u8[28] old password
1758 [30] u8[28] new password
1761 writeU16(buf, TOSERVER_PASSWORD);
1762 for(u32 i=0;i<PASSWORD_SIZE-1;i++)
1764 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
1765 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
1767 buf[2+PASSWORD_SIZE-1] = 0;
1768 buf[30+PASSWORD_SIZE-1] = 0;
1769 os.write((char*)buf, 2+PASSWORD_SIZE*2);
1772 std::string s = os.str();
1773 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1775 Send(0, data, true);
1779 void Client::sendDamage(u8 damage)
1781 DSTACK(__FUNCTION_NAME);
1782 std::ostringstream os(std::ios_base::binary);
1784 writeU16(os, TOSERVER_DAMAGE);
1785 writeU8(os, damage);
1788 std::string s = os.str();
1789 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1791 Send(0, data, true);
1794 void Client::sendRespawn()
1796 DSTACK(__FUNCTION_NAME);
1797 std::ostringstream os(std::ios_base::binary);
1799 writeU16(os, TOSERVER_RESPAWN);
1802 std::string s = os.str();
1803 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1805 Send(0, data, true);
1808 void Client::sendPlayerPos()
1810 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1812 Player *myplayer = m_env.getLocalPlayer();
1813 if(myplayer == NULL)
1818 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1819 our_peer_id = m_con.GetPeerID();
1822 // Set peer id if not set already
1823 if(myplayer->peer_id == PEER_ID_INEXISTENT)
1824 myplayer->peer_id = our_peer_id;
1825 // Check that an existing peer_id is the same as the connection's
1826 assert(myplayer->peer_id == our_peer_id);
1828 v3f pf = myplayer->getPosition();
1829 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
1830 v3f sf = myplayer->getSpeed();
1831 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
1832 s32 pitch = myplayer->getPitch() * 100;
1833 s32 yaw = myplayer->getYaw() * 100;
1838 [2] v3s32 position*100
1839 [2+12] v3s32 speed*100
1840 [2+12+12] s32 pitch*100
1841 [2+12+12+4] s32 yaw*100
1844 SharedBuffer<u8> data(2+12+12+4+4);
1845 writeU16(&data[0], TOSERVER_PLAYERPOS);
1846 writeV3S32(&data[2], position);
1847 writeV3S32(&data[2+12], speed);
1848 writeS32(&data[2+12+12], pitch);
1849 writeS32(&data[2+12+12+4], yaw);
1851 // Send as unreliable
1852 Send(0, data, false);
1855 void Client::sendPlayerItem(u16 item)
1857 Player *myplayer = m_env.getLocalPlayer();
1858 if(myplayer == NULL)
1861 u16 our_peer_id = m_con.GetPeerID();
1863 // Set peer id if not set already
1864 if(myplayer->peer_id == PEER_ID_INEXISTENT)
1865 myplayer->peer_id = our_peer_id;
1866 // Check that an existing peer_id is the same as the connection's
1867 assert(myplayer->peer_id == our_peer_id);
1869 SharedBuffer<u8> data(2+2);
1870 writeU16(&data[0], TOSERVER_PLAYERITEM);
1871 writeU16(&data[2], item);
1874 Send(0, data, true);
1877 void Client::removeNode(v3s16 p)
1879 core::map<v3s16, MapBlock*> modified_blocks;
1883 //TimeTaker t("removeNodeAndUpdate", m_device);
1884 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
1886 catch(InvalidPositionException &e)
1890 // add urgent task to update the modified node
1891 addUpdateMeshTaskForNode(p, false, true);
1893 for(core::map<v3s16, MapBlock * >::Iterator
1894 i = modified_blocks.getIterator();
1895 i.atEnd() == false; i++)
1897 v3s16 p = i.getNode()->getKey();
1898 addUpdateMeshTaskWithEdge(p);
1902 void Client::addNode(v3s16 p, MapNode n)
1904 TimeTaker timer1("Client::addNode()");
1906 core::map<v3s16, MapBlock*> modified_blocks;
1910 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
1911 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks);
1913 catch(InvalidPositionException &e)
1916 for(core::map<v3s16, MapBlock * >::Iterator
1917 i = modified_blocks.getIterator();
1918 i.atEnd() == false; i++)
1920 v3s16 p = i.getNode()->getKey();
1921 addUpdateMeshTaskWithEdge(p);
1925 void Client::setPlayerControl(PlayerControl &control)
1927 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1928 LocalPlayer *player = m_env.getLocalPlayer();
1929 assert(player != NULL);
1930 player->control = control;
1933 void Client::selectPlayerItem(u16 item)
1935 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1936 m_playeritem = item;
1937 m_inventory_updated = true;
1938 sendPlayerItem(item);
1941 // Returns true if the inventory of the local player has been
1942 // updated from the server. If it is true, it is set to false.
1943 bool Client::getLocalInventoryUpdated()
1945 // m_inventory_updated is behind envlock
1946 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1947 bool updated = m_inventory_updated;
1948 m_inventory_updated = false;
1952 // Copies the inventory of the local player to parameter
1953 void Client::getLocalInventory(Inventory &dst)
1955 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1956 Player *player = m_env.getLocalPlayer();
1957 assert(player != NULL);
1958 dst = player->inventory;
1961 Inventory* Client::getInventory(const InventoryLocation &loc)
1964 case InventoryLocation::UNDEFINED:
1967 case InventoryLocation::CURRENT_PLAYER:
1969 Player *player = m_env.getLocalPlayer();
1970 assert(player != NULL);
1971 return &player->inventory;
1974 case InventoryLocation::PLAYER:
1976 Player *player = m_env.getPlayer(loc.name.c_str());
1979 return &player->inventory;
1982 case InventoryLocation::NODEMETA:
1984 NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p);
1987 return meta->getInventory();
1995 void Client::inventoryAction(InventoryAction *a)
1998 Send it to the server
2000 sendInventoryAction(a);
2003 Predict some local inventory changes
2005 a->clientApply(this, this);
2008 ClientActiveObject * Client::getSelectedActiveObject(
2010 v3f from_pos_f_on_map,
2011 core::line3d<f32> shootline_on_map
2014 core::array<DistanceSortedActiveObject> objects;
2016 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
2018 //infostream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
2021 // After this, the closest object is the first in the array.
2024 for(u32 i=0; i<objects.size(); i++)
2026 ClientActiveObject *obj = objects[i].obj;
2028 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
2029 if(selection_box == NULL)
2032 v3f pos = obj->getPosition();
2034 core::aabbox3d<f32> offsetted_box(
2035 selection_box->MinEdge + pos,
2036 selection_box->MaxEdge + pos
2039 if(offsetted_box.intersectsWithLine(shootline_on_map))
2041 //infostream<<"Returning selected object"<<std::endl;
2046 //infostream<<"No object selected; returning NULL."<<std::endl;
2050 void Client::printDebugInfo(std::ostream &os)
2052 //JMutexAutoLock lock1(m_fetchblock_mutex);
2053 /*JMutexAutoLock lock2(m_incoming_queue_mutex);
2055 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
2056 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
2057 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
2061 core::list<std::wstring> Client::getConnectedPlayerNames()
2063 core::list<Player*> players = m_env.getPlayers(true);
2064 core::list<std::wstring> playerNames;
2065 for(core::list<Player*>::Iterator
2066 i = players.begin();
2067 i != players.end(); i++)
2069 Player *player = *i;
2070 playerNames.push_back(narrow_to_wide(player->getName()));
2075 float Client::getAnimationTime()
2077 return m_animation_time;
2080 int Client::getCrackLevel()
2082 return m_crack_level;
2085 void Client::setCrack(int level, v3s16 pos)
2087 int old_crack_level = m_crack_level;
2088 v3s16 old_crack_pos = m_crack_pos;
2090 m_crack_level = level;
2093 if(old_crack_level >= 0 && (level < 0 || pos != old_crack_pos))
2096 addUpdateMeshTaskForNode(old_crack_pos, false, true);
2098 if(level >= 0 && (old_crack_level < 0 || pos != old_crack_pos))
2101 addUpdateMeshTaskForNode(pos, false, true);
2107 Player *player = m_env.getLocalPlayer();
2108 assert(player != NULL);
2112 bool Client::getChatMessage(std::wstring &message)
2114 if(m_chat_queue.size() == 0)
2116 message = m_chat_queue.pop_front();
2120 void Client::typeChatMessage(const std::wstring &message)
2122 // Discard empty line
2127 sendChatMessage(message);
2130 if (message[0] == L'/')
2132 m_chat_queue.push_back(
2133 (std::wstring)L"issued command: "+message);
2137 LocalPlayer *player = m_env.getLocalPlayer();
2138 assert(player != NULL);
2139 std::wstring name = narrow_to_wide(player->getName());
2140 m_chat_queue.push_back(
2141 (std::wstring)L"<"+name+L"> "+message);
2145 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
2147 /*infostream<<"Client::addUpdateMeshTask(): "
2148 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2149 <<" ack_to_server="<<ack_to_server
2150 <<" urgent="<<urgent
2153 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2158 Create a task to update the mesh of the block
2161 MeshMakeData *data = new MeshMakeData(this);
2164 //TimeTaker timer("data fill");
2166 // Debug: 1-6ms, avg=2ms
2168 data->setCrack(m_crack_level, m_crack_pos);
2169 data->setSmoothLighting(g_settings->getBool("smooth_lighting"));
2173 //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
2175 // Add task to queue
2176 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server, urgent);
2178 /*infostream<<"Mesh update input queue size is "
2179 <<m_mesh_update_thread.m_queue_in.size()
2183 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent)
2187 infostream<<"Client::addUpdateMeshTaskWithEdge(): "
2188 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2193 v3s16 p = blockpos + v3s16(0,0,0);
2194 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2195 addUpdateMeshTask(p, ack_to_server, urgent);
2197 catch(InvalidPositionException &e){}
2200 v3s16 p = blockpos + v3s16(-1,0,0);
2201 addUpdateMeshTask(p, false, urgent);
2203 catch(InvalidPositionException &e){}
2205 v3s16 p = blockpos + v3s16(0,-1,0);
2206 addUpdateMeshTask(p, false, urgent);
2208 catch(InvalidPositionException &e){}
2210 v3s16 p = blockpos + v3s16(0,0,-1);
2211 addUpdateMeshTask(p, false, urgent);
2213 catch(InvalidPositionException &e){}
2216 void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool urgent)
2220 infostream<<"Client::addUpdateMeshTaskForNode(): "
2221 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2225 v3s16 blockpos = getNodeBlockPos(nodepos);
2226 v3s16 blockpos_relative = blockpos * MAP_BLOCKSIZE;
2229 v3s16 p = blockpos + v3s16(0,0,0);
2230 addUpdateMeshTask(p, ack_to_server, urgent);
2232 catch(InvalidPositionException &e){}
2234 if(nodepos.X == blockpos_relative.X){
2236 v3s16 p = blockpos + v3s16(-1,0,0);
2237 addUpdateMeshTask(p, false, urgent);
2239 catch(InvalidPositionException &e){}
2241 if(nodepos.Y == blockpos_relative.Y){
2243 v3s16 p = blockpos + v3s16(0,-1,0);
2244 addUpdateMeshTask(p, false, urgent);
2246 catch(InvalidPositionException &e){}
2248 if(nodepos.Z == blockpos_relative.Z){
2250 v3s16 p = blockpos + v3s16(0,0,-1);
2251 addUpdateMeshTask(p, false, urgent);
2253 catch(InvalidPositionException &e){}
2257 ClientEvent Client::getClientEvent()
2259 if(m_client_event_queue.size() == 0)
2262 event.type = CE_NONE;
2265 return m_client_event_queue.pop_front();
2268 void Client::afterContentReceived()
2270 assert(m_itemdef_received);
2271 assert(m_nodedef_received);
2272 assert(m_textures_received);
2274 // Rebuild inherited images and recreate textures
2275 m_tsrc->rebuildImagesAndTextures();
2277 // Update texture atlas
2278 if(g_settings->getBool("enable_texture_atlas"))
2279 m_tsrc->buildMainAtlas(this);
2281 // Update node aliases
2282 m_nodedef->updateAliases(m_itemdef);
2284 // Update node textures
2285 m_nodedef->updateTextures(m_tsrc);
2287 // Update item textures and meshes
2288 m_itemdef->updateTexturesAndMeshes(this);
2290 // Start mesh update thread after setting up content definitions
2291 m_mesh_update_thread.Start();
2294 float Client::getRTT(void)
2297 return m_con.GetPeerAvgRTT(PEER_ID_SERVER);
2298 } catch(con::PeerNotFoundException &e){
2303 // IGameDef interface
2305 IItemDefManager* Client::getItemDefManager()
2309 INodeDefManager* Client::getNodeDefManager()
2313 ICraftDefManager* Client::getCraftDefManager()
2316 //return m_craftdef;
2318 ITextureSource* Client::getTextureSource()
2322 u16 Client::allocateUnknownNodeId(const std::string &name)
2324 errorstream<<"Client::allocateUnknownNodeId(): "
2325 <<"Client cannot allocate node IDs"<<std::endl;
2327 return CONTENT_IGNORE;
2329 ISoundManager* Client::getSoundManager()