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
233 m_mesh_update_thread(this),
235 new ClientMap(this, this, control,
236 device->getSceneManager()->getRootSceneNode(),
237 device->getSceneManager(), 666),
238 device->getSceneManager(),
241 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
243 m_server_ser_ver(SER_FMT_VER_INVALID),
245 m_inventory_updated(false),
246 m_inventory_from_server(NULL),
247 m_inventory_from_server_age(0.0),
252 m_password(password),
253 m_access_denied(false),
254 m_texture_receive_progress(0),
255 m_textures_received(false),
256 m_itemdef_received(false),
257 m_nodedef_received(false),
258 m_time_of_day_set(false),
259 m_last_time_of_day_f(-1),
260 m_time_of_day_update_timer(0)
262 m_packetcounter_timer = 0.0;
263 //m_delete_unused_sectors_timer = 0.0;
264 m_connection_reinit_timer = 0.0;
265 m_avg_rtt_timer = 0.0;
266 m_playerpos_send_timer = 0.0;
267 m_ignore_damage_timer = 0.0;
269 // Build main texture atlas, now that the GameDef exists (that is, us)
270 if(g_settings->getBool("enable_texture_atlas"))
271 m_tsrc->buildMainAtlas(this);
273 infostream<<"Not building texture atlas."<<std::endl;
279 Player *player = new LocalPlayer(this);
281 player->updateName(playername);
283 m_env.addPlayer(player);
290 //JMutexAutoLock conlock(m_con_mutex); //bulk comment-out
294 m_mesh_update_thread.setRun(false);
295 while(m_mesh_update_thread.IsRunning())
298 delete m_inventory_from_server;
301 void Client::connect(Address address)
303 DSTACK(__FUNCTION_NAME);
304 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
305 m_con.SetTimeoutMs(0);
306 m_con.Connect(address);
309 bool Client::connectedAndInitialized()
311 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
313 if(m_con.Connected() == false)
316 if(m_server_ser_ver == SER_FMT_VER_INVALID)
322 void Client::step(float dtime)
324 DSTACK(__FUNCTION_NAME);
330 if(m_ignore_damage_timer > dtime)
331 m_ignore_damage_timer -= dtime;
333 m_ignore_damage_timer = 0.0;
335 m_animation_time += dtime;
336 if(m_animation_time > 60.0)
337 m_animation_time -= 60.0;
339 m_time_of_day_update_timer += dtime;
341 //infostream<<"Client steps "<<dtime<<std::endl;
344 //TimeTaker timer("ReceiveAll()", m_device);
350 //TimeTaker timer("m_con_mutex + m_con.RunTimeouts()", m_device);
352 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
353 m_con.RunTimeouts(dtime);
360 float &counter = m_packetcounter_timer;
366 infostream<<"Client packetcounter (20s):"<<std::endl;
367 m_packetcounter.print(infostream);
368 m_packetcounter.clear();
372 // Get connection status
373 bool connected = connectedAndInitialized();
378 Delete unused sectors
380 NOTE: This jams the game for a while because deleting sectors
384 float &counter = m_delete_unused_sectors_timer;
392 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
394 core::list<v3s16> deleted_blocks;
396 float delete_unused_sectors_timeout =
397 g_settings->getFloat("client_delete_unused_sectors_timeout");
399 // Delete sector blocks
400 /*u32 num = m_env.getMap().unloadUnusedData
401 (delete_unused_sectors_timeout,
402 true, &deleted_blocks);*/
404 // Delete whole sectors
405 m_env.getMap().unloadUnusedData
406 (delete_unused_sectors_timeout,
409 if(deleted_blocks.size() > 0)
411 /*infostream<<"Client: Deleted blocks of "<<num
412 <<" unused sectors"<<std::endl;*/
413 /*infostream<<"Client: Deleted "<<num
414 <<" unused sectors"<<std::endl;*/
420 // Env is locked so con can be locked.
421 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
423 core::list<v3s16>::Iterator i = deleted_blocks.begin();
424 core::list<v3s16> sendlist;
427 if(sendlist.size() == 255 || i == deleted_blocks.end())
429 if(sendlist.size() == 0)
438 u32 replysize = 2+1+6*sendlist.size();
439 SharedBuffer<u8> reply(replysize);
440 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
441 reply[2] = sendlist.size();
443 for(core::list<v3s16>::Iterator
444 j = sendlist.begin();
445 j != sendlist.end(); j++)
447 writeV3S16(&reply[2+1+6*k], *j);
450 m_con.Send(PEER_ID_SERVER, 1, reply, true);
452 if(i == deleted_blocks.end())
458 sendlist.push_back(*i);
466 if(connected == false)
468 float &counter = m_connection_reinit_timer;
474 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
476 Player *myplayer = m_env.getLocalPlayer();
477 assert(myplayer != NULL);
479 // Send TOSERVER_INIT
480 // [0] u16 TOSERVER_INIT
481 // [2] u8 SER_FMT_VER_HIGHEST
482 // [3] u8[20] player_name
483 // [23] u8[28] password (new in some version)
484 // [51] u16 client network protocol version (new in some version)
485 SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2);
486 writeU16(&data[0], TOSERVER_INIT);
487 writeU8(&data[2], SER_FMT_VER_HIGHEST);
489 memset((char*)&data[3], 0, PLAYERNAME_SIZE);
490 snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
492 /*infostream<<"Client: sending initial password hash: \""<<m_password<<"\""
495 memset((char*)&data[23], 0, PASSWORD_SIZE);
496 snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
498 // This should be incremented in each version
499 writeU16(&data[51], PROTOCOL_VERSION);
501 // Send as unreliable
502 Send(0, data, false);
505 // Not connected, return
510 Do stuff if connected
514 Run Map's timers and unload unused data
516 const float map_timer_and_unload_dtime = 5.25;
517 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
519 ScopeProfiler sp(g_profiler, "Client: map timer and unload");
520 core::list<v3s16> deleted_blocks;
521 m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
522 g_settings->getFloat("client_unload_unused_data_timeout"),
525 /*if(deleted_blocks.size() > 0)
526 infostream<<"Client: Unloaded "<<deleted_blocks.size()
527 <<" unused blocks"<<std::endl;*/
531 NOTE: This loop is intentionally iterated the way it is.
534 core::list<v3s16>::Iterator i = deleted_blocks.begin();
535 core::list<v3s16> sendlist;
538 if(sendlist.size() == 255 || i == deleted_blocks.end())
540 if(sendlist.size() == 0)
549 u32 replysize = 2+1+6*sendlist.size();
550 SharedBuffer<u8> reply(replysize);
551 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
552 reply[2] = sendlist.size();
554 for(core::list<v3s16>::Iterator
555 j = sendlist.begin();
556 j != sendlist.end(); j++)
558 writeV3S16(&reply[2+1+6*k], *j);
561 m_con.Send(PEER_ID_SERVER, 1, reply, true);
563 if(i == deleted_blocks.end())
569 sendlist.push_back(*i);
579 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
581 // Control local player (0ms)
582 LocalPlayer *player = m_env.getLocalPlayer();
583 assert(player != NULL);
584 player->applyControl(dtime);
586 //TimeTaker envtimer("env step", m_device);
595 ClientEnvEvent event = m_env.getClientEvent();
596 if(event.type == CEE_NONE)
600 else if(event.type == CEE_PLAYER_DAMAGE)
602 if(m_ignore_damage_timer <= 0)
604 u8 damage = event.player_damage.amount;
606 if(event.player_damage.send_to_server)
609 // Add to ClientEvent queue
611 event.type = CE_PLAYER_DAMAGE;
612 event.player_damage.amount = damage;
613 m_client_event_queue.push_back(event);
623 float &counter = m_avg_rtt_timer;
628 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
629 // connectedAndInitialized() is true, peer exists.
630 float avg_rtt = m_con.GetPeerAvgRTT(PEER_ID_SERVER);
631 infostream<<"Client: avg_rtt="<<avg_rtt<<std::endl;
636 Send player position to server
639 float &counter = m_playerpos_send_timer;
649 Replace updated meshes
652 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
654 //TimeTaker timer("** Processing mesh update result queue");
657 /*infostream<<"Mesh update result queue size is "
658 <<m_mesh_update_thread.m_queue_out.size()
661 int num_processed_meshes = 0;
662 while(m_mesh_update_thread.m_queue_out.size() > 0)
664 num_processed_meshes++;
665 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_front();
666 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
669 //JMutexAutoLock lock(block->mesh_mutex);
671 // Delete the old mesh
672 if(block->mesh != NULL)
674 // TODO: Remove hardware buffers of meshbuffers of block->mesh
679 // Replace with the new mesh
680 block->mesh = r.mesh;
682 if(r.ack_block_to_server)
684 /*infostream<<"Client: ACK block ("<<r.p.X<<","<<r.p.Y
685 <<","<<r.p.Z<<")"<<std::endl;*/
696 u32 replysize = 2+1+6;
697 SharedBuffer<u8> reply(replysize);
698 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
700 writeV3S16(&reply[3], r.p);
702 m_con.Send(PEER_ID_SERVER, 1, reply, true);
705 if(num_processed_meshes > 0)
706 g_profiler->graphAdd("num_processed_meshes", num_processed_meshes);
710 If the server didn't update the inventory in a while, revert
711 the local inventory (so the player notices the lag problem
712 and knows something is wrong).
714 if(m_inventory_from_server)
716 float interval = 10.0;
717 float count_before = floor(m_inventory_from_server_age / interval);
719 m_inventory_from_server_age += dtime;
721 float count_after = floor(m_inventory_from_server_age / interval);
723 if(count_after != count_before)
725 // Do this every <interval> seconds after TOCLIENT_INVENTORY
726 // Reset the locally changed inventory to the authoritative inventory
727 Player *player = m_env.getLocalPlayer();
728 player->inventory = *m_inventory_from_server;
729 m_inventory_updated = true;
734 // Virtual methods from con::PeerHandler
735 void Client::peerAdded(con::Peer *peer)
737 infostream<<"Client::peerAdded(): peer->id="
738 <<peer->id<<std::endl;
740 void Client::deletingPeer(con::Peer *peer, bool timeout)
742 infostream<<"Client::deletingPeer(): "
743 "Server Peer is getting deleted "
744 <<"(timeout="<<timeout<<")"<<std::endl;
747 void Client::ReceiveAll()
749 DSTACK(__FUNCTION_NAME);
750 u32 start_ms = porting::getTimeMs();
753 // Limit time even if there would be huge amounts of data to
755 if(porting::getTimeMs() > start_ms + 100)
760 g_profiler->graphAdd("client_received_packets", 1);
762 catch(con::NoIncomingDataException &e)
766 catch(con::InvalidIncomingDataException &e)
768 infostream<<"Client::ReceiveAll(): "
769 "InvalidIncomingDataException: what()="
770 <<e.what()<<std::endl;
775 void Client::Receive()
777 DSTACK(__FUNCTION_NAME);
778 SharedBuffer<u8> data;
782 //TimeTaker t1("con mutex and receive", m_device);
783 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
784 datasize = m_con.Receive(sender_peer_id, data);
786 //TimeTaker t1("ProcessData", m_device);
787 ProcessData(*data, datasize, sender_peer_id);
791 sender_peer_id given to this shall be quaranteed to be a valid peer
793 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
795 DSTACK(__FUNCTION_NAME);
797 // Ignore packets that don't even fit a command
800 m_packetcounter.add(60000);
804 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
806 //infostream<<"Client: received command="<<command<<std::endl;
807 m_packetcounter.add((u16)command);
810 If this check is removed, be sure to change the queue
811 system to know the ids
813 if(sender_peer_id != PEER_ID_SERVER)
815 infostream<<"Client::ProcessData(): Discarding data not "
816 "coming from server: peer_id="<<sender_peer_id
821 u8 ser_version = m_server_ser_ver;
823 //infostream<<"Client received command="<<(int)command<<std::endl;
825 if(command == TOCLIENT_INIT)
830 u8 deployed = data[2];
832 infostream<<"Client: TOCLIENT_INIT received with "
833 "deployed="<<((int)deployed&0xff)<<std::endl;
835 if(deployed < SER_FMT_VER_LOWEST
836 || deployed > SER_FMT_VER_HIGHEST)
838 infostream<<"Client: TOCLIENT_INIT: Server sent "
839 <<"unsupported ser_fmt_ver"<<std::endl;
843 m_server_ser_ver = deployed;
845 // Get player position
846 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
847 if(datasize >= 2+1+6)
848 playerpos_s16 = readV3S16(&data[2+1]);
849 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
852 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
854 // Set player position
855 Player *player = m_env.getLocalPlayer();
856 assert(player != NULL);
857 player->setPosition(playerpos_f);
860 if(datasize >= 2+1+6+8)
863 m_map_seed = readU64(&data[2+1+6]);
864 infostream<<"Client: received map seed: "<<m_map_seed<<std::endl;
869 SharedBuffer<u8> reply(replysize);
870 writeU16(&reply[0], TOSERVER_INIT2);
872 m_con.Send(PEER_ID_SERVER, 1, reply, true);
877 if(command == TOCLIENT_ACCESS_DENIED)
879 // The server didn't like our password. Note, this needs
880 // to be processed even if the serialisation format has
881 // not been agreed yet, the same as TOCLIENT_INIT.
882 m_access_denied = true;
883 m_access_denied_reason = L"Unknown";
886 std::string datastring((char*)&data[2], datasize-2);
887 std::istringstream is(datastring, std::ios_base::binary);
888 m_access_denied_reason = deSerializeWideString(is);
893 if(ser_version == SER_FMT_VER_INVALID)
895 infostream<<"Client: Server serialization"
896 " format invalid or not initialized."
897 " Skipping incoming command="<<command<<std::endl;
901 // Just here to avoid putting the two if's together when
902 // making some copypasta
905 if(command == TOCLIENT_REMOVENODE)
910 p.X = readS16(&data[2]);
911 p.Y = readS16(&data[4]);
912 p.Z = readS16(&data[6]);
914 //TimeTaker t1("TOCLIENT_REMOVENODE");
918 else if(command == TOCLIENT_ADDNODE)
920 if(datasize < 8 + MapNode::serializedLength(ser_version))
924 p.X = readS16(&data[2]);
925 p.Y = readS16(&data[4]);
926 p.Z = readS16(&data[6]);
928 //TimeTaker t1("TOCLIENT_ADDNODE");
931 n.deSerialize(&data[8], ser_version);
935 else if(command == TOCLIENT_BLOCKDATA)
937 // Ignore too small packet
942 p.X = readS16(&data[2]);
943 p.Y = readS16(&data[4]);
944 p.Z = readS16(&data[6]);
946 /*infostream<<"Client: Thread: BLOCKDATA for ("
947 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
948 /*infostream<<"Client: Thread: BLOCKDATA for ("
949 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
951 std::string datastring((char*)&data[8], datasize-8);
952 std::istringstream istr(datastring, std::ios_base::binary);
958 sector = m_env.getMap().emergeSector(p2d);
960 assert(sector->getPos() == p2d);
962 //TimeTaker timer("MapBlock deSerialize");
965 block = sector->getBlockNoCreateNoEx(p.Y);
969 Update an existing block
971 //infostream<<"Updating"<<std::endl;
972 block->deSerialize(istr, ser_version, false);
979 //infostream<<"Creating new"<<std::endl;
980 block = new MapBlock(&m_env.getMap(), p, this);
981 block->deSerialize(istr, ser_version, false);
982 sector->insertBlock(block);
996 u32 replysize = 2+1+6;
997 SharedBuffer<u8> reply(replysize);
998 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
1000 writeV3S16(&reply[3], p);
1002 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1006 Add it to mesh update queue and set it to be acknowledged after update.
1008 //infostream<<"Adding mesh update task for received block"<<std::endl;
1009 addUpdateMeshTaskWithEdge(p, true);
1011 else if(command == TOCLIENT_INVENTORY)
1016 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
1019 //TimeTaker t2("mutex locking", m_device);
1020 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1023 //TimeTaker t3("istringstream init", m_device);
1024 std::string datastring((char*)&data[2], datasize-2);
1025 std::istringstream is(datastring, std::ios_base::binary);
1028 //m_env.printPlayers(infostream);
1030 //TimeTaker t4("player get", m_device);
1031 Player *player = m_env.getLocalPlayer();
1032 assert(player != NULL);
1035 //TimeTaker t1("inventory.deSerialize()", m_device);
1036 player->inventory.deSerialize(is);
1039 m_inventory_updated = true;
1041 delete m_inventory_from_server;
1042 m_inventory_from_server = new Inventory(player->inventory);
1043 m_inventory_from_server_age = 0.0;
1045 //infostream<<"Client got player inventory:"<<std::endl;
1046 //player->inventory.print(infostream);
1049 else if(command == TOCLIENT_TIME_OF_DAY)
1054 u16 time_of_day = readU16(&data[2]);
1055 time_of_day = time_of_day % 24000;
1056 //infostream<<"Client: time_of_day="<<time_of_day<<std::endl;
1057 float time_speed = 0;
1058 if(datasize >= 2 + 2 + 4){
1059 time_speed = readF1000(&data[4]);
1061 // Old message; try to approximate speed of time by ourselves
1062 float time_of_day_f = (float)time_of_day / 24000.0;
1063 float tod_diff_f = 0;
1064 if(time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
1065 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0;
1067 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
1068 m_last_time_of_day_f = time_of_day_f;
1069 float time_diff = m_time_of_day_update_timer;
1070 m_time_of_day_update_timer = 0;
1071 if(m_time_of_day_set){
1072 time_speed = 3600.0*24.0 * tod_diff_f / time_diff;
1073 infostream<<"Client: Measured time_of_day speed (old format): "
1074 <<time_speed<<" tod_diff_f="<<tod_diff_f
1075 <<" time_diff="<<time_diff<<std::endl;
1079 // Update environment
1080 m_env.setTimeOfDay(time_of_day);
1081 m_env.setTimeOfDaySpeed(time_speed);
1082 m_time_of_day_set = true;
1084 u32 dr = m_env.getDayNightRatio();
1085 verbosestream<<"Client: time_of_day="<<time_of_day
1086 <<" time_speed="<<time_speed
1087 <<" dr="<<dr<<std::endl;
1089 else if(command == TOCLIENT_CHAT_MESSAGE)
1097 std::string datastring((char*)&data[2], datasize-2);
1098 std::istringstream is(datastring, std::ios_base::binary);
1101 is.read((char*)buf, 2);
1102 u16 len = readU16(buf);
1104 std::wstring message;
1105 for(u16 i=0; i<len; i++)
1107 is.read((char*)buf, 2);
1108 message += (wchar_t)readU16(buf);
1111 /*infostream<<"Client received chat message: "
1112 <<wide_to_narrow(message)<<std::endl;*/
1114 m_chat_queue.push_back(message);
1116 else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1118 //if(g_settings->getBool("enable_experimental"))
1122 u16 count of removed objects
1123 for all removed objects {
1126 u16 count of added objects
1127 for all added objects {
1130 u32 initialization data length
1131 string initialization data
1136 // Get all data except the command number
1137 std::string datastring((char*)&data[2], datasize-2);
1138 // Throw them in an istringstream
1139 std::istringstream is(datastring, std::ios_base::binary);
1143 // Read removed objects
1145 u16 removed_count = readU16((u8*)buf);
1146 for(u16 i=0; i<removed_count; i++)
1149 u16 id = readU16((u8*)buf);
1152 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1153 m_env.removeActiveObject(id);
1157 // Read added objects
1159 u16 added_count = readU16((u8*)buf);
1160 for(u16 i=0; i<added_count; i++)
1163 u16 id = readU16((u8*)buf);
1165 u8 type = readU8((u8*)buf);
1166 std::string data = deSerializeLongString(is);
1169 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1170 m_env.addActiveObject(id, type, data);
1175 else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1177 //if(g_settings->getBool("enable_experimental"))
1189 // Get all data except the command number
1190 std::string datastring((char*)&data[2], datasize-2);
1191 // Throw them in an istringstream
1192 std::istringstream is(datastring, std::ios_base::binary);
1194 while(is.eof() == false)
1198 u16 id = readU16((u8*)buf);
1202 u16 message_size = readU16((u8*)buf);
1203 std::string message;
1204 message.reserve(message_size);
1205 for(u16 i=0; i<message_size; i++)
1208 message.append(buf, 1);
1210 // Pass on to the environment
1212 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1213 m_env.processActiveObjectMessage(id, message);
1218 else if(command == TOCLIENT_HP)
1220 std::string datastring((char*)&data[2], datasize-2);
1221 std::istringstream is(datastring, std::ios_base::binary);
1222 Player *player = m_env.getLocalPlayer();
1223 assert(player != NULL);
1224 u8 oldhp = player->hp;
1230 // Add to ClientEvent queue
1232 event.type = CE_PLAYER_DAMAGE;
1233 event.player_damage.amount = oldhp - hp;
1234 m_client_event_queue.push_back(event);
1237 else if(command == TOCLIENT_MOVE_PLAYER)
1239 std::string datastring((char*)&data[2], datasize-2);
1240 std::istringstream is(datastring, std::ios_base::binary);
1241 Player *player = m_env.getLocalPlayer();
1242 assert(player != NULL);
1243 v3f pos = readV3F1000(is);
1244 f32 pitch = readF1000(is);
1245 f32 yaw = readF1000(is);
1246 player->setPosition(pos);
1247 /*player->setPitch(pitch);
1248 player->setYaw(yaw);*/
1250 infostream<<"Client got TOCLIENT_MOVE_PLAYER"
1251 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1257 Add to ClientEvent queue.
1258 This has to be sent to the main program because otherwise
1259 it would just force the pitch and yaw values to whatever
1260 the camera points to.
1263 event.type = CE_PLAYER_FORCE_MOVE;
1264 event.player_force_move.pitch = pitch;
1265 event.player_force_move.yaw = yaw;
1266 m_client_event_queue.push_back(event);
1268 // Ignore damage for a few seconds, so that the player doesn't
1269 // get damage from falling on ground
1270 m_ignore_damage_timer = 3.0;
1272 else if(command == TOCLIENT_PLAYERITEM)
1274 std::string datastring((char*)&data[2], datasize-2);
1275 std::istringstream is(datastring, std::ios_base::binary);
1277 u16 count = readU16(is);
1279 for (u16 i = 0; i < count; ++i) {
1280 u16 peer_id = readU16(is);
1281 Player *player = m_env.getPlayer(peer_id);
1285 infostream<<"Client: ignoring player item "
1286 << deSerializeString(is)
1287 << " for non-existing peer id " << peer_id
1290 } else if (player->isLocal()) {
1291 infostream<<"Client: ignoring player item "
1292 << deSerializeString(is)
1293 << " for local player" << std::endl;
1296 InventoryList *inv = player->inventory.getList("main");
1297 std::string itemstring(deSerializeString(is));
1299 item.deSerialize(itemstring, m_itemdef);
1300 inv->changeItem(0, item);
1301 if(itemstring.empty())
1303 infostream<<"Client: empty player item for peer "
1304 <<peer_id<<std::endl;
1308 infostream<<"Client: player item for peer "
1309 <<peer_id<<": "<<itemstring<<std::endl;
1314 else if(command == TOCLIENT_DEATHSCREEN)
1316 std::string datastring((char*)&data[2], datasize-2);
1317 std::istringstream is(datastring, std::ios_base::binary);
1319 bool set_camera_point_target = readU8(is);
1320 v3f camera_point_target = readV3F1000(is);
1323 event.type = CE_DEATHSCREEN;
1324 event.deathscreen.set_camera_point_target = set_camera_point_target;
1325 event.deathscreen.camera_point_target_x = camera_point_target.X;
1326 event.deathscreen.camera_point_target_y = camera_point_target.Y;
1327 event.deathscreen.camera_point_target_z = camera_point_target.Z;
1328 m_client_event_queue.push_back(event);
1330 else if(command == TOCLIENT_ANNOUNCE_TEXTURES)
1332 io::IFileSystem *irrfs = m_device->getFileSystem();
1333 video::IVideoDriver *vdrv = m_device->getVideoDriver();
1335 std::string datastring((char*)&data[2], datasize-2);
1336 std::istringstream is(datastring, std::ios_base::binary);
1338 // Mesh update thread must be stopped while
1339 // updating content definitions
1340 assert(!m_mesh_update_thread.IsRunning());
1342 int num_textures = readU16(is);
1344 core::list<TextureRequest> texture_requests;
1346 for(int i=0; i<num_textures; i++){
1348 bool texture_found = false;
1350 //read texture from cache
1351 std::string name = deSerializeString(is);
1352 std::string sha1_texture = deSerializeString(is);
1354 // if name contains illegal characters, ignore the texture
1355 if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
1356 errorstream<<"Client: ignoring illegal texture name "
1357 <<"sent by server: \""<<name<<"\""<<std::endl;
1361 std::string tpath = getTextureCacheDir() + DIR_DELIM + name;
1363 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
1366 if(fis.good() == false){
1367 infostream<<"Client::Texture not found in cache: "
1368 <<name << " expected it at: "<<tpath<<std::endl;
1372 std::ostringstream tmp_os(std::ios_base::binary);
1376 fis.read(buf, 1024);
1377 std::streamsize len = fis.gcount();
1378 tmp_os.write(buf, len);
1387 infostream<<"Client: Failed to read texture from cache\""
1388 <<name<<"\""<<std::endl;
1393 sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
1395 unsigned char *digest = sha1.getDigest();
1397 std::string digest_string = base64_encode(digest, 20);
1399 if (digest_string == sha1_texture) {
1400 // Silly irrlicht's const-incorrectness
1401 Buffer<char> data_rw(tmp_os.str().c_str(), tmp_os.str().size());
1403 // Create an irrlicht memory file
1404 io::IReadFile *rfile = irrfs->createMemoryReadFile(
1405 *data_rw, tmp_os.str().size(), "_tempreadfile");
1408 video::IImage *img = vdrv->createImageFromFile(rfile);
1410 infostream<<"Client: Cannot create image from data of "
1411 <<"received texture \""<<name<<"\""<<std::endl;
1415 m_tsrc->insertSourceImage(name, img);
1419 texture_found = true;
1423 infostream<<"Client::Texture cached sha1 hash not matching server hash: "
1424 <<name << ": server ->"<<sha1_texture <<" client -> "<<digest_string<<std::endl;
1431 //add texture request
1432 if (!texture_found) {
1433 infostream<<"Client: Adding texture to request list: \""
1434 <<name<<"\""<<std::endl;
1435 texture_requests.push_back(TextureRequest(name));
1441 event.type = CE_TEXTURES_UPDATED;
1442 m_client_event_queue.push_back(event);
1445 //send Texture request
1448 u16 number of textures requested
1454 std::ostringstream os(std::ios_base::binary);
1459 writeU16(buf, TOSERVER_REQUEST_TEXTURES);
1460 os.write((char*)buf, 2);
1462 writeU16(buf,texture_requests.size());
1463 os.write((char*)buf, 2);
1466 for(core::list<TextureRequest>::Iterator i = texture_requests.begin();
1467 i != texture_requests.end(); i++) {
1468 os<<serializeString(i->name);
1472 std::string s = os.str();
1473 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1475 Send(0, data, true);
1476 infostream<<"Client: Sending request list to server " <<std::endl;
1478 else if(command == TOCLIENT_TEXTURES)
1480 io::IFileSystem *irrfs = m_device->getFileSystem();
1481 video::IVideoDriver *vdrv = m_device->getVideoDriver();
1483 std::string datastring((char*)&data[2], datasize-2);
1484 std::istringstream is(datastring, std::ios_base::binary);
1486 // Mesh update thread must be stopped while
1487 // updating content definitions
1488 assert(!m_mesh_update_thread.IsRunning());
1492 u16 total number of texture bunches
1493 u16 index of this bunch
1494 u32 number of textures in this bunch
1502 int num_bunches = readU16(is);
1503 int bunch_i = readU16(is);
1504 m_texture_receive_progress = (float)bunch_i / (float)(num_bunches - 1);
1505 if(bunch_i == num_bunches - 1)
1506 m_textures_received = true;
1507 int num_textures = readU32(is);
1508 infostream<<"Client: Received textures: bunch "<<bunch_i<<"/"
1509 <<num_bunches<<" textures="<<num_textures
1510 <<" size="<<datasize<<std::endl;
1511 for(int i=0; i<num_textures; i++){
1512 std::string name = deSerializeString(is);
1513 std::string data = deSerializeLongString(is);
1515 // if name contains illegal characters, ignore the texture
1516 if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
1517 errorstream<<"Client: ignoring illegal texture name "
1518 <<"sent by server: \""<<name<<"\""<<std::endl;
1522 // Silly irrlicht's const-incorrectness
1523 Buffer<char> data_rw(data.c_str(), data.size());
1524 // Create an irrlicht memory file
1525 io::IReadFile *rfile = irrfs->createMemoryReadFile(
1526 *data_rw, data.size(), "_tempreadfile");
1529 video::IImage *img = vdrv->createImageFromFile(rfile);
1531 errorstream<<"Client: Cannot create image from data of "
1532 <<"received texture \""<<name<<"\""<<std::endl;
1537 fs::CreateAllDirs(getTextureCacheDir());
1539 std::string filename = getTextureCacheDir() + DIR_DELIM + name;
1540 std::ofstream outfile(filename.c_str(), std::ios_base::binary | std::ios_base::trunc);
1542 if (outfile.good()) {
1543 outfile.write(data.c_str(),data.length());
1547 errorstream<<"Client: Unable to open cached texture file "<< filename <<std::endl;
1550 m_tsrc->insertSourceImage(name, img);
1556 event.type = CE_TEXTURES_UPDATED;
1557 m_client_event_queue.push_back(event);
1559 else if(command == TOCLIENT_TOOLDEF)
1561 infostream<<"Client: WARNING: Ignoring TOCLIENT_TOOLDEF"<<std::endl;
1563 else if(command == TOCLIENT_NODEDEF)
1565 infostream<<"Client: Received node definitions: packet size: "
1566 <<datasize<<std::endl;
1568 // Mesh update thread must be stopped while
1569 // updating content definitions
1570 assert(!m_mesh_update_thread.IsRunning());
1572 // Decompress node definitions
1573 std::string datastring((char*)&data[2], datasize-2);
1574 std::istringstream is(datastring, std::ios_base::binary);
1575 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1576 std::ostringstream tmp_os;
1577 decompressZlib(tmp_is, tmp_os);
1579 // Deserialize node definitions
1580 std::istringstream tmp_is2(tmp_os.str());
1581 m_nodedef->deSerialize(tmp_is2);
1582 m_nodedef_received = true;
1584 else if(command == TOCLIENT_CRAFTITEMDEF)
1586 infostream<<"Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF"<<std::endl;
1588 else if(command == TOCLIENT_ITEMDEF)
1590 infostream<<"Client: Received item definitions: packet size: "
1591 <<datasize<<std::endl;
1593 // Mesh update thread must be stopped while
1594 // updating content definitions
1595 assert(!m_mesh_update_thread.IsRunning());
1597 // Decompress item definitions
1598 std::string datastring((char*)&data[2], datasize-2);
1599 std::istringstream is(datastring, std::ios_base::binary);
1600 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1601 std::ostringstream tmp_os;
1602 decompressZlib(tmp_is, tmp_os);
1604 // Deserialize node definitions
1605 std::istringstream tmp_is2(tmp_os.str());
1606 m_itemdef->deSerialize(tmp_is2);
1607 m_itemdef_received = true;
1611 infostream<<"Client: Ignoring unknown command "
1612 <<command<<std::endl;
1616 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
1618 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1619 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
1622 void Client::interact(u8 action, const PointedThing& pointed)
1624 if(connectedAndInitialized() == false){
1625 infostream<<"Client::interact() "
1626 "cancelled (not connected)"
1631 std::ostringstream os(std::ios_base::binary);
1637 [5] u32 length of the next item
1638 [9] serialized PointedThing
1640 0: start digging (from undersurface) or use
1641 1: stop digging (all parameters ignored)
1642 2: digging completed
1643 3: place block or item (to abovesurface)
1646 writeU16(os, TOSERVER_INTERACT);
1647 writeU8(os, action);
1648 writeU16(os, getPlayerItem());
1649 std::ostringstream tmp_os(std::ios::binary);
1650 pointed.serialize(tmp_os);
1651 os<<serializeLongString(tmp_os.str());
1653 std::string s = os.str();
1654 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1657 Send(0, data, true);
1660 void Client::sendSignNodeText(v3s16 p, std::string text)
1668 std::ostringstream os(std::ios_base::binary);
1672 writeU16(buf, TOSERVER_SIGNNODETEXT);
1673 os.write((char*)buf, 2);
1677 os.write((char*)buf, 6);
1679 u16 textlen = text.size();
1680 // Write text length
1681 writeS16(buf, textlen);
1682 os.write((char*)buf, 2);
1685 os.write((char*)text.c_str(), textlen);
1688 std::string s = os.str();
1689 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1691 Send(0, data, true);
1694 void Client::sendInventoryAction(InventoryAction *a)
1696 std::ostringstream os(std::ios_base::binary);
1700 writeU16(buf, TOSERVER_INVENTORY_ACTION);
1701 os.write((char*)buf, 2);
1706 std::string s = os.str();
1707 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1709 Send(0, data, true);
1712 void Client::sendChatMessage(const std::wstring &message)
1714 std::ostringstream os(std::ios_base::binary);
1718 writeU16(buf, TOSERVER_CHAT_MESSAGE);
1719 os.write((char*)buf, 2);
1722 writeU16(buf, message.size());
1723 os.write((char*)buf, 2);
1726 for(u32 i=0; i<message.size(); i++)
1730 os.write((char*)buf, 2);
1734 std::string s = os.str();
1735 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1737 Send(0, data, true);
1740 void Client::sendChangePassword(const std::wstring oldpassword,
1741 const std::wstring newpassword)
1743 Player *player = m_env.getLocalPlayer();
1747 std::string playername = player->getName();
1748 std::string oldpwd = translatePassword(playername, oldpassword);
1749 std::string newpwd = translatePassword(playername, newpassword);
1751 std::ostringstream os(std::ios_base::binary);
1752 u8 buf[2+PASSWORD_SIZE*2];
1754 [0] u16 TOSERVER_PASSWORD
1755 [2] u8[28] old password
1756 [30] u8[28] new password
1759 writeU16(buf, TOSERVER_PASSWORD);
1760 for(u32 i=0;i<PASSWORD_SIZE-1;i++)
1762 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
1763 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
1765 buf[2+PASSWORD_SIZE-1] = 0;
1766 buf[30+PASSWORD_SIZE-1] = 0;
1767 os.write((char*)buf, 2+PASSWORD_SIZE*2);
1770 std::string s = os.str();
1771 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1773 Send(0, data, true);
1777 void Client::sendDamage(u8 damage)
1779 DSTACK(__FUNCTION_NAME);
1780 std::ostringstream os(std::ios_base::binary);
1782 writeU16(os, TOSERVER_DAMAGE);
1783 writeU8(os, damage);
1786 std::string s = os.str();
1787 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1789 Send(0, data, true);
1792 void Client::sendRespawn()
1794 DSTACK(__FUNCTION_NAME);
1795 std::ostringstream os(std::ios_base::binary);
1797 writeU16(os, TOSERVER_RESPAWN);
1800 std::string s = os.str();
1801 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1803 Send(0, data, true);
1806 void Client::sendPlayerPos()
1808 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1810 Player *myplayer = m_env.getLocalPlayer();
1811 if(myplayer == NULL)
1816 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1817 our_peer_id = m_con.GetPeerID();
1820 // Set peer id if not set already
1821 if(myplayer->peer_id == PEER_ID_INEXISTENT)
1822 myplayer->peer_id = our_peer_id;
1823 // Check that an existing peer_id is the same as the connection's
1824 assert(myplayer->peer_id == our_peer_id);
1826 v3f pf = myplayer->getPosition();
1827 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
1828 v3f sf = myplayer->getSpeed();
1829 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
1830 s32 pitch = myplayer->getPitch() * 100;
1831 s32 yaw = myplayer->getYaw() * 100;
1836 [2] v3s32 position*100
1837 [2+12] v3s32 speed*100
1838 [2+12+12] s32 pitch*100
1839 [2+12+12+4] s32 yaw*100
1842 SharedBuffer<u8> data(2+12+12+4+4);
1843 writeU16(&data[0], TOSERVER_PLAYERPOS);
1844 writeV3S32(&data[2], position);
1845 writeV3S32(&data[2+12], speed);
1846 writeS32(&data[2+12+12], pitch);
1847 writeS32(&data[2+12+12+4], yaw);
1849 // Send as unreliable
1850 Send(0, data, false);
1853 void Client::sendPlayerItem(u16 item)
1855 Player *myplayer = m_env.getLocalPlayer();
1856 if(myplayer == NULL)
1859 u16 our_peer_id = m_con.GetPeerID();
1861 // Set peer id if not set already
1862 if(myplayer->peer_id == PEER_ID_INEXISTENT)
1863 myplayer->peer_id = our_peer_id;
1864 // Check that an existing peer_id is the same as the connection's
1865 assert(myplayer->peer_id == our_peer_id);
1867 SharedBuffer<u8> data(2+2);
1868 writeU16(&data[0], TOSERVER_PLAYERITEM);
1869 writeU16(&data[2], item);
1872 Send(0, data, true);
1875 void Client::removeNode(v3s16 p)
1877 core::map<v3s16, MapBlock*> modified_blocks;
1881 //TimeTaker t("removeNodeAndUpdate", m_device);
1882 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
1884 catch(InvalidPositionException &e)
1888 // add urgent task to update the modified node
1889 addUpdateMeshTaskForNode(p, false, true);
1891 for(core::map<v3s16, MapBlock * >::Iterator
1892 i = modified_blocks.getIterator();
1893 i.atEnd() == false; i++)
1895 v3s16 p = i.getNode()->getKey();
1896 addUpdateMeshTaskWithEdge(p);
1900 void Client::addNode(v3s16 p, MapNode n)
1902 TimeTaker timer1("Client::addNode()");
1904 core::map<v3s16, MapBlock*> modified_blocks;
1908 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
1909 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks);
1911 catch(InvalidPositionException &e)
1914 for(core::map<v3s16, MapBlock * >::Iterator
1915 i = modified_blocks.getIterator();
1916 i.atEnd() == false; i++)
1918 v3s16 p = i.getNode()->getKey();
1919 addUpdateMeshTaskWithEdge(p);
1923 void Client::setPlayerControl(PlayerControl &control)
1925 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1926 LocalPlayer *player = m_env.getLocalPlayer();
1927 assert(player != NULL);
1928 player->control = control;
1931 void Client::selectPlayerItem(u16 item)
1933 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1934 m_playeritem = item;
1935 m_inventory_updated = true;
1936 sendPlayerItem(item);
1939 // Returns true if the inventory of the local player has been
1940 // updated from the server. If it is true, it is set to false.
1941 bool Client::getLocalInventoryUpdated()
1943 // m_inventory_updated is behind envlock
1944 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1945 bool updated = m_inventory_updated;
1946 m_inventory_updated = false;
1950 // Copies the inventory of the local player to parameter
1951 void Client::getLocalInventory(Inventory &dst)
1953 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1954 Player *player = m_env.getLocalPlayer();
1955 assert(player != NULL);
1956 dst = player->inventory;
1959 Inventory* Client::getInventory(const InventoryLocation &loc)
1962 case InventoryLocation::UNDEFINED:
1965 case InventoryLocation::CURRENT_PLAYER:
1967 Player *player = m_env.getLocalPlayer();
1968 assert(player != NULL);
1969 return &player->inventory;
1972 case InventoryLocation::PLAYER:
1974 Player *player = m_env.getPlayer(loc.name.c_str());
1977 return &player->inventory;
1980 case InventoryLocation::NODEMETA:
1982 NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p);
1985 return meta->getInventory();
1993 void Client::inventoryAction(InventoryAction *a)
1996 Send it to the server
1998 sendInventoryAction(a);
2001 Predict some local inventory changes
2003 a->clientApply(this, this);
2006 ClientActiveObject * Client::getSelectedActiveObject(
2008 v3f from_pos_f_on_map,
2009 core::line3d<f32> shootline_on_map
2012 core::array<DistanceSortedActiveObject> objects;
2014 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
2016 //infostream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
2019 // After this, the closest object is the first in the array.
2022 for(u32 i=0; i<objects.size(); i++)
2024 ClientActiveObject *obj = objects[i].obj;
2026 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
2027 if(selection_box == NULL)
2030 v3f pos = obj->getPosition();
2032 core::aabbox3d<f32> offsetted_box(
2033 selection_box->MinEdge + pos,
2034 selection_box->MaxEdge + pos
2037 if(offsetted_box.intersectsWithLine(shootline_on_map))
2039 //infostream<<"Returning selected object"<<std::endl;
2044 //infostream<<"No object selected; returning NULL."<<std::endl;
2048 void Client::printDebugInfo(std::ostream &os)
2050 //JMutexAutoLock lock1(m_fetchblock_mutex);
2051 /*JMutexAutoLock lock2(m_incoming_queue_mutex);
2053 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
2054 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
2055 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
2059 core::list<std::wstring> Client::getConnectedPlayerNames()
2061 core::list<Player*> players = m_env.getPlayers(true);
2062 core::list<std::wstring> playerNames;
2063 for(core::list<Player*>::Iterator
2064 i = players.begin();
2065 i != players.end(); i++)
2067 Player *player = *i;
2068 playerNames.push_back(narrow_to_wide(player->getName()));
2073 float Client::getAnimationTime()
2075 return m_animation_time;
2078 int Client::getCrackLevel()
2080 return m_crack_level;
2083 void Client::setCrack(int level, v3s16 pos)
2085 int old_crack_level = m_crack_level;
2086 v3s16 old_crack_pos = m_crack_pos;
2088 m_crack_level = level;
2091 if(old_crack_level >= 0 && (level < 0 || pos != old_crack_pos))
2094 addUpdateMeshTaskForNode(old_crack_pos, false, true);
2096 if(level >= 0 && (old_crack_level < 0 || pos != old_crack_pos))
2099 addUpdateMeshTaskForNode(pos, false, true);
2105 Player *player = m_env.getLocalPlayer();
2106 assert(player != NULL);
2110 bool Client::getChatMessage(std::wstring &message)
2112 if(m_chat_queue.size() == 0)
2114 message = m_chat_queue.pop_front();
2118 void Client::typeChatMessage(const std::wstring &message)
2120 // Discard empty line
2125 sendChatMessage(message);
2128 if (message[0] == L'/')
2130 m_chat_queue.push_back(
2131 (std::wstring)L"issued command: "+message);
2135 LocalPlayer *player = m_env.getLocalPlayer();
2136 assert(player != NULL);
2137 std::wstring name = narrow_to_wide(player->getName());
2138 m_chat_queue.push_back(
2139 (std::wstring)L"<"+name+L"> "+message);
2143 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
2145 /*infostream<<"Client::addUpdateMeshTask(): "
2146 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2147 <<" ack_to_server="<<ack_to_server
2148 <<" urgent="<<urgent
2151 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2156 Create a task to update the mesh of the block
2159 MeshMakeData *data = new MeshMakeData(this);
2162 //TimeTaker timer("data fill");
2164 // Debug: 1-6ms, avg=2ms
2166 data->setCrack(m_crack_level, m_crack_pos);
2167 data->setSmoothLighting(g_settings->getBool("smooth_lighting"));
2171 //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
2173 // Add task to queue
2174 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server, urgent);
2176 /*infostream<<"Mesh update input queue size is "
2177 <<m_mesh_update_thread.m_queue_in.size()
2181 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent)
2185 infostream<<"Client::addUpdateMeshTaskWithEdge(): "
2186 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2191 v3s16 p = blockpos + v3s16(0,0,0);
2192 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2193 addUpdateMeshTask(p, ack_to_server, urgent);
2195 catch(InvalidPositionException &e){}
2198 v3s16 p = blockpos + v3s16(-1,0,0);
2199 addUpdateMeshTask(p, false, urgent);
2201 catch(InvalidPositionException &e){}
2203 v3s16 p = blockpos + v3s16(0,-1,0);
2204 addUpdateMeshTask(p, false, urgent);
2206 catch(InvalidPositionException &e){}
2208 v3s16 p = blockpos + v3s16(0,0,-1);
2209 addUpdateMeshTask(p, false, urgent);
2211 catch(InvalidPositionException &e){}
2214 void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool urgent)
2218 infostream<<"Client::addUpdateMeshTaskForNode(): "
2219 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2223 v3s16 blockpos = getNodeBlockPos(nodepos);
2224 v3s16 blockpos_relative = blockpos * MAP_BLOCKSIZE;
2227 v3s16 p = blockpos + v3s16(0,0,0);
2228 addUpdateMeshTask(p, ack_to_server, urgent);
2230 catch(InvalidPositionException &e){}
2232 if(nodepos.X == blockpos_relative.X){
2234 v3s16 p = blockpos + v3s16(-1,0,0);
2235 addUpdateMeshTask(p, false, urgent);
2237 catch(InvalidPositionException &e){}
2239 if(nodepos.Y == blockpos_relative.Y){
2241 v3s16 p = blockpos + v3s16(0,-1,0);
2242 addUpdateMeshTask(p, false, urgent);
2244 catch(InvalidPositionException &e){}
2246 if(nodepos.Z == blockpos_relative.Z){
2248 v3s16 p = blockpos + v3s16(0,0,-1);
2249 addUpdateMeshTask(p, false, urgent);
2251 catch(InvalidPositionException &e){}
2255 ClientEvent Client::getClientEvent()
2257 if(m_client_event_queue.size() == 0)
2260 event.type = CE_NONE;
2263 return m_client_event_queue.pop_front();
2266 void Client::afterContentReceived()
2268 assert(m_itemdef_received);
2269 assert(m_nodedef_received);
2270 assert(m_textures_received);
2272 // Rebuild inherited images and recreate textures
2273 m_tsrc->rebuildImagesAndTextures();
2275 // Update texture atlas
2276 if(g_settings->getBool("enable_texture_atlas"))
2277 m_tsrc->buildMainAtlas(this);
2279 // Update node aliases
2280 m_nodedef->updateAliases(m_itemdef);
2282 // Update node textures
2283 m_nodedef->updateTextures(m_tsrc);
2285 // Update item textures and meshes
2286 m_itemdef->updateTexturesAndMeshes(this);
2288 // Start mesh update thread after setting up content definitions
2289 m_mesh_update_thread.Start();
2292 float Client::getRTT(void)
2295 return m_con.GetPeerAvgRTT(PEER_ID_SERVER);
2296 } catch(con::PeerNotFoundException &e){
2301 // IGameDef interface
2303 IItemDefManager* Client::getItemDefManager()
2307 INodeDefManager* Client::getNodeDefManager()
2311 ICraftDefManager* Client::getCraftDefManager()
2314 //return m_craftdef;
2316 ITextureSource* Client::getTextureSource()
2320 u16 Client::allocateUnknownNodeId(const std::string &name)
2322 errorstream<<"Client::allocateUnknownNodeId(): "
2323 <<"Client cannot allocate node IDs"<<std::endl;
2325 return CONTENT_IGNORE;
2327 ISoundManager* Client::getSoundManager()
2329 return &dummySoundManager;