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"
42 static std::string getTextureCacheDir()
44 return porting::path_user + DIR_DELIM + "cache" + DIR_DELIM + "textures";
51 TextureRequest(const std::string &name_=""):
60 QueuedMeshUpdate::QueuedMeshUpdate():
63 ack_block_to_server(false)
67 QueuedMeshUpdate::~QueuedMeshUpdate()
77 MeshUpdateQueue::MeshUpdateQueue()
82 MeshUpdateQueue::~MeshUpdateQueue()
84 JMutexAutoLock lock(m_mutex);
86 for(std::vector<QueuedMeshUpdate*>::iterator
88 i != m_queue.end(); i++)
90 QueuedMeshUpdate *q = *i;
96 peer_id=0 adds with nobody to send to
98 void MeshUpdateQueue::addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_server, bool urgent)
100 DSTACK(__FUNCTION_NAME);
104 JMutexAutoLock lock(m_mutex);
110 Find if block is already in queue.
111 If it is, update the data and quit.
113 for(std::vector<QueuedMeshUpdate*>::iterator
115 i != m_queue.end(); i++)
117 QueuedMeshUpdate *q = *i;
123 if(ack_block_to_server)
124 q->ack_block_to_server = true;
132 QueuedMeshUpdate *q = new QueuedMeshUpdate;
135 q->ack_block_to_server = ack_block_to_server;
136 m_queue.push_back(q);
139 // Returned pointer must be deleted
140 // Returns NULL if queue is empty
141 QueuedMeshUpdate * MeshUpdateQueue::pop()
143 JMutexAutoLock lock(m_mutex);
145 bool must_be_urgent = !m_urgents.empty();
146 for(std::vector<QueuedMeshUpdate*>::iterator
148 i != m_queue.end(); i++)
150 QueuedMeshUpdate *q = *i;
151 if(must_be_urgent && m_urgents.count(q->p) == 0)
154 m_urgents.erase(q->p);
164 void * MeshUpdateThread::Thread()
168 log_register_thread("MeshUpdateThread");
170 DSTACK(__FUNCTION_NAME);
172 BEGIN_DEBUG_EXCEPTION_HANDLER
176 /*// Wait for output queue to flush.
177 // Allow 2 in queue, this makes less frametime jitter.
178 // Umm actually, there is no much difference
179 if(m_queue_out.size() >= 2)
185 QueuedMeshUpdate *q = m_queue_in.pop();
192 ScopeProfiler sp(g_profiler, "Client: Mesh making");
194 MapBlockMesh *mesh_new = new MapBlockMesh(q->data);
195 if(mesh_new->getMesh()->getMeshBufferCount() == 0)
204 r.ack_block_to_server = q->ack_block_to_server;
206 /*infostream<<"MeshUpdateThread: Processed "
207 <<"("<<q->p.X<<","<<q->p.Y<<","<<q->p.Z<<")"
210 m_queue_out.push_back(r);
215 END_DEBUG_EXCEPTION_HANDLER(errorstream)
221 IrrlichtDevice *device,
222 const char *playername,
223 std::string password,
224 MapDrawControl &control,
225 IWritableTextureSource *tsrc,
226 IWritableItemDefManager *itemdef,
227 IWritableNodeDefManager *nodedef
232 m_mesh_update_thread(this),
234 new ClientMap(this, this, control,
235 device->getSceneManager()->getRootSceneNode(),
236 device->getSceneManager(), 666),
237 device->getSceneManager(),
240 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
242 m_server_ser_ver(SER_FMT_VER_INVALID),
244 m_inventory_updated(false),
245 m_inventory_from_server(NULL),
246 m_inventory_from_server_age(0.0),
251 m_password(password),
252 m_access_denied(false),
253 m_texture_receive_progress(0),
254 m_textures_received(false),
255 m_itemdef_received(false),
256 m_nodedef_received(false),
257 m_time_of_day_set(false),
258 m_last_time_of_day_f(-1),
259 m_time_of_day_update_timer(0)
261 m_packetcounter_timer = 0.0;
262 //m_delete_unused_sectors_timer = 0.0;
263 m_connection_reinit_timer = 0.0;
264 m_avg_rtt_timer = 0.0;
265 m_playerpos_send_timer = 0.0;
266 m_ignore_damage_timer = 0.0;
268 // Build main texture atlas, now that the GameDef exists (that is, us)
269 if(g_settings->getBool("enable_texture_atlas"))
270 m_tsrc->buildMainAtlas(this);
272 infostream<<"Not building texture atlas."<<std::endl;
278 Player *player = new LocalPlayer(this);
280 player->updateName(playername);
282 m_env.addPlayer(player);
289 //JMutexAutoLock conlock(m_con_mutex); //bulk comment-out
293 m_mesh_update_thread.setRun(false);
294 while(m_mesh_update_thread.IsRunning())
297 delete m_inventory_from_server;
300 void Client::connect(Address address)
302 DSTACK(__FUNCTION_NAME);
303 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
304 m_con.SetTimeoutMs(0);
305 m_con.Connect(address);
308 bool Client::connectedAndInitialized()
310 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
312 if(m_con.Connected() == false)
315 if(m_server_ser_ver == SER_FMT_VER_INVALID)
321 void Client::step(float dtime)
323 DSTACK(__FUNCTION_NAME);
329 if(m_ignore_damage_timer > dtime)
330 m_ignore_damage_timer -= dtime;
332 m_ignore_damage_timer = 0.0;
334 m_animation_time += dtime;
335 if(m_animation_time > 60.0)
336 m_animation_time -= 60.0;
338 m_time_of_day_update_timer += dtime;
340 //infostream<<"Client steps "<<dtime<<std::endl;
343 //TimeTaker timer("ReceiveAll()", m_device);
349 //TimeTaker timer("m_con_mutex + m_con.RunTimeouts()", m_device);
351 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
352 m_con.RunTimeouts(dtime);
359 float &counter = m_packetcounter_timer;
365 infostream<<"Client packetcounter (20s):"<<std::endl;
366 m_packetcounter.print(infostream);
367 m_packetcounter.clear();
371 // Get connection status
372 bool connected = connectedAndInitialized();
377 Delete unused sectors
379 NOTE: This jams the game for a while because deleting sectors
383 float &counter = m_delete_unused_sectors_timer;
391 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
393 core::list<v3s16> deleted_blocks;
395 float delete_unused_sectors_timeout =
396 g_settings->getFloat("client_delete_unused_sectors_timeout");
398 // Delete sector blocks
399 /*u32 num = m_env.getMap().unloadUnusedData
400 (delete_unused_sectors_timeout,
401 true, &deleted_blocks);*/
403 // Delete whole sectors
404 m_env.getMap().unloadUnusedData
405 (delete_unused_sectors_timeout,
408 if(deleted_blocks.size() > 0)
410 /*infostream<<"Client: Deleted blocks of "<<num
411 <<" unused sectors"<<std::endl;*/
412 /*infostream<<"Client: Deleted "<<num
413 <<" unused sectors"<<std::endl;*/
419 // Env is locked so con can be locked.
420 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
422 core::list<v3s16>::Iterator i = deleted_blocks.begin();
423 core::list<v3s16> sendlist;
426 if(sendlist.size() == 255 || i == deleted_blocks.end())
428 if(sendlist.size() == 0)
437 u32 replysize = 2+1+6*sendlist.size();
438 SharedBuffer<u8> reply(replysize);
439 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
440 reply[2] = sendlist.size();
442 for(core::list<v3s16>::Iterator
443 j = sendlist.begin();
444 j != sendlist.end(); j++)
446 writeV3S16(&reply[2+1+6*k], *j);
449 m_con.Send(PEER_ID_SERVER, 1, reply, true);
451 if(i == deleted_blocks.end())
457 sendlist.push_back(*i);
465 if(connected == false)
467 float &counter = m_connection_reinit_timer;
473 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
475 Player *myplayer = m_env.getLocalPlayer();
476 assert(myplayer != NULL);
478 // Send TOSERVER_INIT
479 // [0] u16 TOSERVER_INIT
480 // [2] u8 SER_FMT_VER_HIGHEST
481 // [3] u8[20] player_name
482 // [23] u8[28] password (new in some version)
483 // [51] u16 client network protocol version (new in some version)
484 SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2);
485 writeU16(&data[0], TOSERVER_INIT);
486 writeU8(&data[2], SER_FMT_VER_HIGHEST);
488 memset((char*)&data[3], 0, PLAYERNAME_SIZE);
489 snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
491 /*infostream<<"Client: sending initial password hash: \""<<m_password<<"\""
494 memset((char*)&data[23], 0, PASSWORD_SIZE);
495 snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
497 // This should be incremented in each version
498 writeU16(&data[51], PROTOCOL_VERSION);
500 // Send as unreliable
501 Send(0, data, false);
504 // Not connected, return
509 Do stuff if connected
513 Run Map's timers and unload unused data
515 const float map_timer_and_unload_dtime = 5.25;
516 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
518 ScopeProfiler sp(g_profiler, "Client: map timer and unload");
519 core::list<v3s16> deleted_blocks;
520 m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
521 g_settings->getFloat("client_unload_unused_data_timeout"),
524 /*if(deleted_blocks.size() > 0)
525 infostream<<"Client: Unloaded "<<deleted_blocks.size()
526 <<" unused blocks"<<std::endl;*/
530 NOTE: This loop is intentionally iterated the way it is.
533 core::list<v3s16>::Iterator i = deleted_blocks.begin();
534 core::list<v3s16> sendlist;
537 if(sendlist.size() == 255 || i == deleted_blocks.end())
539 if(sendlist.size() == 0)
548 u32 replysize = 2+1+6*sendlist.size();
549 SharedBuffer<u8> reply(replysize);
550 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
551 reply[2] = sendlist.size();
553 for(core::list<v3s16>::Iterator
554 j = sendlist.begin();
555 j != sendlist.end(); j++)
557 writeV3S16(&reply[2+1+6*k], *j);
560 m_con.Send(PEER_ID_SERVER, 1, reply, true);
562 if(i == deleted_blocks.end())
568 sendlist.push_back(*i);
578 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
580 // Control local player (0ms)
581 LocalPlayer *player = m_env.getLocalPlayer();
582 assert(player != NULL);
583 player->applyControl(dtime);
585 //TimeTaker envtimer("env step", m_device);
594 ClientEnvEvent event = m_env.getClientEvent();
595 if(event.type == CEE_NONE)
599 else if(event.type == CEE_PLAYER_DAMAGE)
601 if(m_ignore_damage_timer <= 0)
603 u8 damage = event.player_damage.amount;
605 if(event.player_damage.send_to_server)
608 // Add to ClientEvent queue
610 event.type = CE_PLAYER_DAMAGE;
611 event.player_damage.amount = damage;
612 m_client_event_queue.push_back(event);
622 float &counter = m_avg_rtt_timer;
627 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
628 // connectedAndInitialized() is true, peer exists.
629 float avg_rtt = m_con.GetPeerAvgRTT(PEER_ID_SERVER);
630 infostream<<"Client: avg_rtt="<<avg_rtt<<std::endl;
635 Send player position to server
638 float &counter = m_playerpos_send_timer;
648 Replace updated meshes
651 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
653 //TimeTaker timer("** Processing mesh update result queue");
656 /*infostream<<"Mesh update result queue size is "
657 <<m_mesh_update_thread.m_queue_out.size()
660 int num_processed_meshes = 0;
661 while(m_mesh_update_thread.m_queue_out.size() > 0)
663 num_processed_meshes++;
664 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_front();
665 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
668 //JMutexAutoLock lock(block->mesh_mutex);
670 // Delete the old mesh
671 if(block->mesh != NULL)
673 // TODO: Remove hardware buffers of meshbuffers of block->mesh
678 // Replace with the new mesh
679 block->mesh = r.mesh;
681 if(r.ack_block_to_server)
683 /*infostream<<"Client: ACK block ("<<r.p.X<<","<<r.p.Y
684 <<","<<r.p.Z<<")"<<std::endl;*/
695 u32 replysize = 2+1+6;
696 SharedBuffer<u8> reply(replysize);
697 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
699 writeV3S16(&reply[3], r.p);
701 m_con.Send(PEER_ID_SERVER, 1, reply, true);
704 if(num_processed_meshes > 0)
705 g_profiler->graphAdd("num_processed_meshes", num_processed_meshes);
709 If the server didn't update the inventory in a while, revert
710 the local inventory (so the player notices the lag problem
711 and knows something is wrong).
713 if(m_inventory_from_server)
715 float interval = 10.0;
716 float count_before = floor(m_inventory_from_server_age / interval);
718 m_inventory_from_server_age += dtime;
720 float count_after = floor(m_inventory_from_server_age / interval);
722 if(count_after != count_before)
724 // Do this every <interval> seconds after TOCLIENT_INVENTORY
725 // Reset the locally changed inventory to the authoritative inventory
726 Player *player = m_env.getLocalPlayer();
727 player->inventory = *m_inventory_from_server;
728 m_inventory_updated = true;
733 // Virtual methods from con::PeerHandler
734 void Client::peerAdded(con::Peer *peer)
736 infostream<<"Client::peerAdded(): peer->id="
737 <<peer->id<<std::endl;
739 void Client::deletingPeer(con::Peer *peer, bool timeout)
741 infostream<<"Client::deletingPeer(): "
742 "Server Peer is getting deleted "
743 <<"(timeout="<<timeout<<")"<<std::endl;
746 void Client::ReceiveAll()
748 DSTACK(__FUNCTION_NAME);
749 u32 start_ms = porting::getTimeMs();
752 // Limit time even if there would be huge amounts of data to
754 if(porting::getTimeMs() > start_ms + 100)
759 g_profiler->graphAdd("client_received_packets", 1);
761 catch(con::NoIncomingDataException &e)
765 catch(con::InvalidIncomingDataException &e)
767 infostream<<"Client::ReceiveAll(): "
768 "InvalidIncomingDataException: what()="
769 <<e.what()<<std::endl;
774 void Client::Receive()
776 DSTACK(__FUNCTION_NAME);
777 SharedBuffer<u8> data;
781 //TimeTaker t1("con mutex and receive", m_device);
782 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
783 datasize = m_con.Receive(sender_peer_id, data);
785 //TimeTaker t1("ProcessData", m_device);
786 ProcessData(*data, datasize, sender_peer_id);
790 sender_peer_id given to this shall be quaranteed to be a valid peer
792 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
794 DSTACK(__FUNCTION_NAME);
796 // Ignore packets that don't even fit a command
799 m_packetcounter.add(60000);
803 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
805 //infostream<<"Client: received command="<<command<<std::endl;
806 m_packetcounter.add((u16)command);
809 If this check is removed, be sure to change the queue
810 system to know the ids
812 if(sender_peer_id != PEER_ID_SERVER)
814 infostream<<"Client::ProcessData(): Discarding data not "
815 "coming from server: peer_id="<<sender_peer_id
820 u8 ser_version = m_server_ser_ver;
822 //infostream<<"Client received command="<<(int)command<<std::endl;
824 if(command == TOCLIENT_INIT)
829 u8 deployed = data[2];
831 infostream<<"Client: TOCLIENT_INIT received with "
832 "deployed="<<((int)deployed&0xff)<<std::endl;
834 if(deployed < SER_FMT_VER_LOWEST
835 || deployed > SER_FMT_VER_HIGHEST)
837 infostream<<"Client: TOCLIENT_INIT: Server sent "
838 <<"unsupported ser_fmt_ver"<<std::endl;
842 m_server_ser_ver = deployed;
844 // Get player position
845 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
846 if(datasize >= 2+1+6)
847 playerpos_s16 = readV3S16(&data[2+1]);
848 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
851 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
853 // Set player position
854 Player *player = m_env.getLocalPlayer();
855 assert(player != NULL);
856 player->setPosition(playerpos_f);
859 if(datasize >= 2+1+6+8)
862 m_map_seed = readU64(&data[2+1+6]);
863 infostream<<"Client: received map seed: "<<m_map_seed<<std::endl;
868 SharedBuffer<u8> reply(replysize);
869 writeU16(&reply[0], TOSERVER_INIT2);
871 m_con.Send(PEER_ID_SERVER, 1, reply, true);
876 if(command == TOCLIENT_ACCESS_DENIED)
878 // The server didn't like our password. Note, this needs
879 // to be processed even if the serialisation format has
880 // not been agreed yet, the same as TOCLIENT_INIT.
881 m_access_denied = true;
882 m_access_denied_reason = L"Unknown";
885 std::string datastring((char*)&data[2], datasize-2);
886 std::istringstream is(datastring, std::ios_base::binary);
887 m_access_denied_reason = deSerializeWideString(is);
892 if(ser_version == SER_FMT_VER_INVALID)
894 infostream<<"Client: Server serialization"
895 " format invalid or not initialized."
896 " Skipping incoming command="<<command<<std::endl;
900 // Just here to avoid putting the two if's together when
901 // making some copypasta
904 if(command == TOCLIENT_REMOVENODE)
909 p.X = readS16(&data[2]);
910 p.Y = readS16(&data[4]);
911 p.Z = readS16(&data[6]);
913 //TimeTaker t1("TOCLIENT_REMOVENODE");
917 else if(command == TOCLIENT_ADDNODE)
919 if(datasize < 8 + MapNode::serializedLength(ser_version))
923 p.X = readS16(&data[2]);
924 p.Y = readS16(&data[4]);
925 p.Z = readS16(&data[6]);
927 //TimeTaker t1("TOCLIENT_ADDNODE");
930 n.deSerialize(&data[8], ser_version);
934 else if(command == TOCLIENT_BLOCKDATA)
936 // Ignore too small packet
941 p.X = readS16(&data[2]);
942 p.Y = readS16(&data[4]);
943 p.Z = readS16(&data[6]);
945 /*infostream<<"Client: Thread: BLOCKDATA for ("
946 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
947 /*infostream<<"Client: Thread: BLOCKDATA for ("
948 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
950 std::string datastring((char*)&data[8], datasize-8);
951 std::istringstream istr(datastring, std::ios_base::binary);
957 sector = m_env.getMap().emergeSector(p2d);
959 assert(sector->getPos() == p2d);
961 //TimeTaker timer("MapBlock deSerialize");
964 block = sector->getBlockNoCreateNoEx(p.Y);
968 Update an existing block
970 //infostream<<"Updating"<<std::endl;
971 block->deSerialize(istr, ser_version, false);
978 //infostream<<"Creating new"<<std::endl;
979 block = new MapBlock(&m_env.getMap(), p, this);
980 block->deSerialize(istr, ser_version, false);
981 sector->insertBlock(block);
995 u32 replysize = 2+1+6;
996 SharedBuffer<u8> reply(replysize);
997 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
999 writeV3S16(&reply[3], p);
1001 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1005 Add it to mesh update queue and set it to be acknowledged after update.
1007 //infostream<<"Adding mesh update task for received block"<<std::endl;
1008 addUpdateMeshTaskWithEdge(p, true);
1010 else if(command == TOCLIENT_INVENTORY)
1015 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
1018 //TimeTaker t2("mutex locking", m_device);
1019 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1022 //TimeTaker t3("istringstream init", m_device);
1023 std::string datastring((char*)&data[2], datasize-2);
1024 std::istringstream is(datastring, std::ios_base::binary);
1027 //m_env.printPlayers(infostream);
1029 //TimeTaker t4("player get", m_device);
1030 Player *player = m_env.getLocalPlayer();
1031 assert(player != NULL);
1034 //TimeTaker t1("inventory.deSerialize()", m_device);
1035 player->inventory.deSerialize(is);
1038 m_inventory_updated = true;
1040 delete m_inventory_from_server;
1041 m_inventory_from_server = new Inventory(player->inventory);
1042 m_inventory_from_server_age = 0.0;
1044 //infostream<<"Client got player inventory:"<<std::endl;
1045 //player->inventory.print(infostream);
1048 else if(command == TOCLIENT_TIME_OF_DAY)
1053 u16 time_of_day = readU16(&data[2]);
1054 time_of_day = time_of_day % 24000;
1055 //infostream<<"Client: time_of_day="<<time_of_day<<std::endl;
1056 float time_speed = 0;
1057 if(datasize >= 2 + 2 + 4){
1058 time_speed = readF1000(&data[4]);
1060 // Old message; try to approximate speed of time by ourselves
1061 float time_of_day_f = (float)time_of_day / 24000.0;
1062 float tod_diff_f = 0;
1063 if(time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
1064 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0;
1066 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
1067 m_last_time_of_day_f = time_of_day_f;
1068 float time_diff = m_time_of_day_update_timer;
1069 m_time_of_day_update_timer = 0;
1070 if(m_time_of_day_set){
1071 time_speed = 3600.0*24.0 * tod_diff_f / time_diff;
1072 infostream<<"Client: Measured time_of_day speed (old format): "
1073 <<time_speed<<" tod_diff_f="<<tod_diff_f
1074 <<" time_diff="<<time_diff<<std::endl;
1078 // Update environment
1079 m_env.setTimeOfDay(time_of_day);
1080 m_env.setTimeOfDaySpeed(time_speed);
1081 m_time_of_day_set = true;
1083 u32 dr = m_env.getDayNightRatio();
1084 verbosestream<<"Client: time_of_day="<<time_of_day
1085 <<" time_speed="<<time_speed
1086 <<" dr="<<dr<<std::endl;
1088 else if(command == TOCLIENT_CHAT_MESSAGE)
1096 std::string datastring((char*)&data[2], datasize-2);
1097 std::istringstream is(datastring, std::ios_base::binary);
1100 is.read((char*)buf, 2);
1101 u16 len = readU16(buf);
1103 std::wstring message;
1104 for(u16 i=0; i<len; i++)
1106 is.read((char*)buf, 2);
1107 message += (wchar_t)readU16(buf);
1110 /*infostream<<"Client received chat message: "
1111 <<wide_to_narrow(message)<<std::endl;*/
1113 m_chat_queue.push_back(message);
1115 else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1117 //if(g_settings->getBool("enable_experimental"))
1121 u16 count of removed objects
1122 for all removed objects {
1125 u16 count of added objects
1126 for all added objects {
1129 u32 initialization data length
1130 string initialization data
1135 // Get all data except the command number
1136 std::string datastring((char*)&data[2], datasize-2);
1137 // Throw them in an istringstream
1138 std::istringstream is(datastring, std::ios_base::binary);
1142 // Read removed objects
1144 u16 removed_count = readU16((u8*)buf);
1145 for(u16 i=0; i<removed_count; i++)
1148 u16 id = readU16((u8*)buf);
1151 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1152 m_env.removeActiveObject(id);
1156 // Read added objects
1158 u16 added_count = readU16((u8*)buf);
1159 for(u16 i=0; i<added_count; i++)
1162 u16 id = readU16((u8*)buf);
1164 u8 type = readU8((u8*)buf);
1165 std::string data = deSerializeLongString(is);
1168 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1169 m_env.addActiveObject(id, type, data);
1174 else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1176 //if(g_settings->getBool("enable_experimental"))
1188 // Get all data except the command number
1189 std::string datastring((char*)&data[2], datasize-2);
1190 // Throw them in an istringstream
1191 std::istringstream is(datastring, std::ios_base::binary);
1193 while(is.eof() == false)
1197 u16 id = readU16((u8*)buf);
1201 u16 message_size = readU16((u8*)buf);
1202 std::string message;
1203 message.reserve(message_size);
1204 for(u16 i=0; i<message_size; i++)
1207 message.append(buf, 1);
1209 // Pass on to the environment
1211 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1212 m_env.processActiveObjectMessage(id, message);
1217 else if(command == TOCLIENT_HP)
1219 std::string datastring((char*)&data[2], datasize-2);
1220 std::istringstream is(datastring, std::ios_base::binary);
1221 Player *player = m_env.getLocalPlayer();
1222 assert(player != NULL);
1223 u8 oldhp = player->hp;
1229 // Add to ClientEvent queue
1231 event.type = CE_PLAYER_DAMAGE;
1232 event.player_damage.amount = oldhp - hp;
1233 m_client_event_queue.push_back(event);
1236 else if(command == TOCLIENT_MOVE_PLAYER)
1238 std::string datastring((char*)&data[2], datasize-2);
1239 std::istringstream is(datastring, std::ios_base::binary);
1240 Player *player = m_env.getLocalPlayer();
1241 assert(player != NULL);
1242 v3f pos = readV3F1000(is);
1243 f32 pitch = readF1000(is);
1244 f32 yaw = readF1000(is);
1245 player->setPosition(pos);
1246 /*player->setPitch(pitch);
1247 player->setYaw(yaw);*/
1249 infostream<<"Client got TOCLIENT_MOVE_PLAYER"
1250 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1256 Add to ClientEvent queue.
1257 This has to be sent to the main program because otherwise
1258 it would just force the pitch and yaw values to whatever
1259 the camera points to.
1262 event.type = CE_PLAYER_FORCE_MOVE;
1263 event.player_force_move.pitch = pitch;
1264 event.player_force_move.yaw = yaw;
1265 m_client_event_queue.push_back(event);
1267 // Ignore damage for a few seconds, so that the player doesn't
1268 // get damage from falling on ground
1269 m_ignore_damage_timer = 3.0;
1271 else if(command == TOCLIENT_PLAYERITEM)
1273 std::string datastring((char*)&data[2], datasize-2);
1274 std::istringstream is(datastring, std::ios_base::binary);
1276 u16 count = readU16(is);
1278 for (u16 i = 0; i < count; ++i) {
1279 u16 peer_id = readU16(is);
1280 Player *player = m_env.getPlayer(peer_id);
1284 infostream<<"Client: ignoring player item "
1285 << deSerializeString(is)
1286 << " for non-existing peer id " << peer_id
1289 } else if (player->isLocal()) {
1290 infostream<<"Client: ignoring player item "
1291 << deSerializeString(is)
1292 << " for local player" << std::endl;
1295 InventoryList *inv = player->inventory.getList("main");
1296 std::string itemstring(deSerializeString(is));
1298 item.deSerialize(itemstring, m_itemdef);
1299 inv->changeItem(0, item);
1300 if(itemstring.empty())
1302 infostream<<"Client: empty player item for peer "
1303 <<peer_id<<std::endl;
1307 infostream<<"Client: player item for peer "
1308 <<peer_id<<": "<<itemstring<<std::endl;
1313 else if(command == TOCLIENT_DEATHSCREEN)
1315 std::string datastring((char*)&data[2], datasize-2);
1316 std::istringstream is(datastring, std::ios_base::binary);
1318 bool set_camera_point_target = readU8(is);
1319 v3f camera_point_target = readV3F1000(is);
1322 event.type = CE_DEATHSCREEN;
1323 event.deathscreen.set_camera_point_target = set_camera_point_target;
1324 event.deathscreen.camera_point_target_x = camera_point_target.X;
1325 event.deathscreen.camera_point_target_y = camera_point_target.Y;
1326 event.deathscreen.camera_point_target_z = camera_point_target.Z;
1327 m_client_event_queue.push_back(event);
1329 else if(command == TOCLIENT_ANNOUNCE_TEXTURES)
1331 io::IFileSystem *irrfs = m_device->getFileSystem();
1332 video::IVideoDriver *vdrv = m_device->getVideoDriver();
1334 std::string datastring((char*)&data[2], datasize-2);
1335 std::istringstream is(datastring, std::ios_base::binary);
1337 // Mesh update thread must be stopped while
1338 // updating content definitions
1339 assert(!m_mesh_update_thread.IsRunning());
1341 int num_textures = readU16(is);
1343 core::list<TextureRequest> texture_requests;
1345 for(int i=0; i<num_textures; i++){
1347 bool texture_found = false;
1349 //read texture from cache
1350 std::string name = deSerializeString(is);
1351 std::string sha1_texture = deSerializeString(is);
1353 // if name contains illegal characters, ignore the texture
1354 if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
1355 errorstream<<"Client: ignoring illegal texture name "
1356 <<"sent by server: \""<<name<<"\""<<std::endl;
1360 std::string tpath = getTextureCacheDir() + DIR_DELIM + name;
1362 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
1365 if(fis.good() == false){
1366 infostream<<"Client::Texture not found in cache: "
1367 <<name << " expected it at: "<<tpath<<std::endl;
1371 std::ostringstream tmp_os(std::ios_base::binary);
1375 fis.read(buf, 1024);
1376 std::streamsize len = fis.gcount();
1377 tmp_os.write(buf, len);
1386 infostream<<"Client: Failed to read texture from cache\""
1387 <<name<<"\""<<std::endl;
1392 sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
1394 unsigned char *digest = sha1.getDigest();
1396 std::string digest_string = base64_encode(digest, 20);
1398 if (digest_string == sha1_texture) {
1399 // Silly irrlicht's const-incorrectness
1400 Buffer<char> data_rw(tmp_os.str().c_str(), tmp_os.str().size());
1402 // Create an irrlicht memory file
1403 io::IReadFile *rfile = irrfs->createMemoryReadFile(
1404 *data_rw, tmp_os.str().size(), "_tempreadfile");
1407 video::IImage *img = vdrv->createImageFromFile(rfile);
1409 infostream<<"Client: Cannot create image from data of "
1410 <<"received texture \""<<name<<"\""<<std::endl;
1414 m_tsrc->insertSourceImage(name, img);
1418 texture_found = true;
1422 infostream<<"Client::Texture cached sha1 hash not matching server hash: "
1423 <<name << ": server ->"<<sha1_texture <<" client -> "<<digest_string<<std::endl;
1430 //add texture request
1431 if (!texture_found) {
1432 infostream<<"Client: Adding texture to request list: \""
1433 <<name<<"\""<<std::endl;
1434 texture_requests.push_back(TextureRequest(name));
1440 event.type = CE_TEXTURES_UPDATED;
1441 m_client_event_queue.push_back(event);
1444 //send Texture request
1447 u16 number of textures requested
1453 std::ostringstream os(std::ios_base::binary);
1458 writeU16(buf, TOSERVER_REQUEST_TEXTURES);
1459 os.write((char*)buf, 2);
1461 writeU16(buf,texture_requests.size());
1462 os.write((char*)buf, 2);
1465 for(core::list<TextureRequest>::Iterator i = texture_requests.begin();
1466 i != texture_requests.end(); i++) {
1467 os<<serializeString(i->name);
1471 std::string s = os.str();
1472 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1474 Send(0, data, true);
1475 infostream<<"Client: Sending request list to server " <<std::endl;
1477 else if(command == TOCLIENT_TEXTURES)
1479 io::IFileSystem *irrfs = m_device->getFileSystem();
1480 video::IVideoDriver *vdrv = m_device->getVideoDriver();
1482 std::string datastring((char*)&data[2], datasize-2);
1483 std::istringstream is(datastring, std::ios_base::binary);
1485 // Mesh update thread must be stopped while
1486 // updating content definitions
1487 assert(!m_mesh_update_thread.IsRunning());
1491 u16 total number of texture bunches
1492 u16 index of this bunch
1493 u32 number of textures in this bunch
1501 int num_bunches = readU16(is);
1502 int bunch_i = readU16(is);
1503 m_texture_receive_progress = (float)bunch_i / (float)(num_bunches - 1);
1504 if(bunch_i == num_bunches - 1)
1505 m_textures_received = true;
1506 int num_textures = readU32(is);
1507 infostream<<"Client: Received textures: bunch "<<bunch_i<<"/"
1508 <<num_bunches<<" textures="<<num_textures
1509 <<" size="<<datasize<<std::endl;
1510 for(int i=0; i<num_textures; i++){
1511 std::string name = deSerializeString(is);
1512 std::string data = deSerializeLongString(is);
1514 // if name contains illegal characters, ignore the texture
1515 if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
1516 errorstream<<"Client: ignoring illegal texture name "
1517 <<"sent by server: \""<<name<<"\""<<std::endl;
1521 // Silly irrlicht's const-incorrectness
1522 Buffer<char> data_rw(data.c_str(), data.size());
1523 // Create an irrlicht memory file
1524 io::IReadFile *rfile = irrfs->createMemoryReadFile(
1525 *data_rw, data.size(), "_tempreadfile");
1528 video::IImage *img = vdrv->createImageFromFile(rfile);
1530 errorstream<<"Client: Cannot create image from data of "
1531 <<"received texture \""<<name<<"\""<<std::endl;
1536 fs::CreateAllDirs(getTextureCacheDir());
1538 std::string filename = getTextureCacheDir() + DIR_DELIM + name;
1539 std::ofstream outfile(filename.c_str(), std::ios_base::binary | std::ios_base::trunc);
1541 if (outfile.good()) {
1542 outfile.write(data.c_str(),data.length());
1546 errorstream<<"Client: Unable to open cached texture file "<< filename <<std::endl;
1549 m_tsrc->insertSourceImage(name, img);
1555 event.type = CE_TEXTURES_UPDATED;
1556 m_client_event_queue.push_back(event);
1558 else if(command == TOCLIENT_TOOLDEF)
1560 infostream<<"Client: WARNING: Ignoring TOCLIENT_TOOLDEF"<<std::endl;
1562 else if(command == TOCLIENT_NODEDEF)
1564 infostream<<"Client: Received node definitions: packet size: "
1565 <<datasize<<std::endl;
1567 // Mesh update thread must be stopped while
1568 // updating content definitions
1569 assert(!m_mesh_update_thread.IsRunning());
1571 // Decompress node definitions
1572 std::string datastring((char*)&data[2], datasize-2);
1573 std::istringstream is(datastring, std::ios_base::binary);
1574 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1575 std::ostringstream tmp_os;
1576 decompressZlib(tmp_is, tmp_os);
1578 // Deserialize node definitions
1579 std::istringstream tmp_is2(tmp_os.str());
1580 m_nodedef->deSerialize(tmp_is2);
1581 m_nodedef_received = true;
1583 else if(command == TOCLIENT_CRAFTITEMDEF)
1585 infostream<<"Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF"<<std::endl;
1587 else if(command == TOCLIENT_ITEMDEF)
1589 infostream<<"Client: Received item definitions: packet size: "
1590 <<datasize<<std::endl;
1592 // Mesh update thread must be stopped while
1593 // updating content definitions
1594 assert(!m_mesh_update_thread.IsRunning());
1596 // Decompress item definitions
1597 std::string datastring((char*)&data[2], datasize-2);
1598 std::istringstream is(datastring, std::ios_base::binary);
1599 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1600 std::ostringstream tmp_os;
1601 decompressZlib(tmp_is, tmp_os);
1603 // Deserialize node definitions
1604 std::istringstream tmp_is2(tmp_os.str());
1605 m_itemdef->deSerialize(tmp_is2);
1606 m_itemdef_received = true;
1610 infostream<<"Client: Ignoring unknown command "
1611 <<command<<std::endl;
1615 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
1617 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1618 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
1621 void Client::interact(u8 action, const PointedThing& pointed)
1623 if(connectedAndInitialized() == false){
1624 infostream<<"Client::interact() "
1625 "cancelled (not connected)"
1630 std::ostringstream os(std::ios_base::binary);
1636 [5] u32 length of the next item
1637 [9] serialized PointedThing
1639 0: start digging (from undersurface) or use
1640 1: stop digging (all parameters ignored)
1641 2: digging completed
1642 3: place block or item (to abovesurface)
1645 writeU16(os, TOSERVER_INTERACT);
1646 writeU8(os, action);
1647 writeU16(os, getPlayerItem());
1648 std::ostringstream tmp_os(std::ios::binary);
1649 pointed.serialize(tmp_os);
1650 os<<serializeLongString(tmp_os.str());
1652 std::string s = os.str();
1653 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1656 Send(0, data, true);
1659 void Client::sendSignNodeText(v3s16 p, std::string text)
1667 std::ostringstream os(std::ios_base::binary);
1671 writeU16(buf, TOSERVER_SIGNNODETEXT);
1672 os.write((char*)buf, 2);
1676 os.write((char*)buf, 6);
1678 u16 textlen = text.size();
1679 // Write text length
1680 writeS16(buf, textlen);
1681 os.write((char*)buf, 2);
1684 os.write((char*)text.c_str(), textlen);
1687 std::string s = os.str();
1688 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1690 Send(0, data, true);
1693 void Client::sendInventoryAction(InventoryAction *a)
1695 std::ostringstream os(std::ios_base::binary);
1699 writeU16(buf, TOSERVER_INVENTORY_ACTION);
1700 os.write((char*)buf, 2);
1705 std::string s = os.str();
1706 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1708 Send(0, data, true);
1711 void Client::sendChatMessage(const std::wstring &message)
1713 std::ostringstream os(std::ios_base::binary);
1717 writeU16(buf, TOSERVER_CHAT_MESSAGE);
1718 os.write((char*)buf, 2);
1721 writeU16(buf, message.size());
1722 os.write((char*)buf, 2);
1725 for(u32 i=0; i<message.size(); i++)
1729 os.write((char*)buf, 2);
1733 std::string s = os.str();
1734 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1736 Send(0, data, true);
1739 void Client::sendChangePassword(const std::wstring oldpassword,
1740 const std::wstring newpassword)
1742 Player *player = m_env.getLocalPlayer();
1746 std::string playername = player->getName();
1747 std::string oldpwd = translatePassword(playername, oldpassword);
1748 std::string newpwd = translatePassword(playername, newpassword);
1750 std::ostringstream os(std::ios_base::binary);
1751 u8 buf[2+PASSWORD_SIZE*2];
1753 [0] u16 TOSERVER_PASSWORD
1754 [2] u8[28] old password
1755 [30] u8[28] new password
1758 writeU16(buf, TOSERVER_PASSWORD);
1759 for(u32 i=0;i<PASSWORD_SIZE-1;i++)
1761 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
1762 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
1764 buf[2+PASSWORD_SIZE-1] = 0;
1765 buf[30+PASSWORD_SIZE-1] = 0;
1766 os.write((char*)buf, 2+PASSWORD_SIZE*2);
1769 std::string s = os.str();
1770 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1772 Send(0, data, true);
1776 void Client::sendDamage(u8 damage)
1778 DSTACK(__FUNCTION_NAME);
1779 std::ostringstream os(std::ios_base::binary);
1781 writeU16(os, TOSERVER_DAMAGE);
1782 writeU8(os, damage);
1785 std::string s = os.str();
1786 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1788 Send(0, data, true);
1791 void Client::sendRespawn()
1793 DSTACK(__FUNCTION_NAME);
1794 std::ostringstream os(std::ios_base::binary);
1796 writeU16(os, TOSERVER_RESPAWN);
1799 std::string s = os.str();
1800 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1802 Send(0, data, true);
1805 void Client::sendPlayerPos()
1807 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1809 Player *myplayer = m_env.getLocalPlayer();
1810 if(myplayer == NULL)
1815 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1816 our_peer_id = m_con.GetPeerID();
1819 // Set peer id if not set already
1820 if(myplayer->peer_id == PEER_ID_INEXISTENT)
1821 myplayer->peer_id = our_peer_id;
1822 // Check that an existing peer_id is the same as the connection's
1823 assert(myplayer->peer_id == our_peer_id);
1825 v3f pf = myplayer->getPosition();
1826 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
1827 v3f sf = myplayer->getSpeed();
1828 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
1829 s32 pitch = myplayer->getPitch() * 100;
1830 s32 yaw = myplayer->getYaw() * 100;
1835 [2] v3s32 position*100
1836 [2+12] v3s32 speed*100
1837 [2+12+12] s32 pitch*100
1838 [2+12+12+4] s32 yaw*100
1841 SharedBuffer<u8> data(2+12+12+4+4);
1842 writeU16(&data[0], TOSERVER_PLAYERPOS);
1843 writeV3S32(&data[2], position);
1844 writeV3S32(&data[2+12], speed);
1845 writeS32(&data[2+12+12], pitch);
1846 writeS32(&data[2+12+12+4], yaw);
1848 // Send as unreliable
1849 Send(0, data, false);
1852 void Client::sendPlayerItem(u16 item)
1854 Player *myplayer = m_env.getLocalPlayer();
1855 if(myplayer == NULL)
1858 u16 our_peer_id = m_con.GetPeerID();
1860 // Set peer id if not set already
1861 if(myplayer->peer_id == PEER_ID_INEXISTENT)
1862 myplayer->peer_id = our_peer_id;
1863 // Check that an existing peer_id is the same as the connection's
1864 assert(myplayer->peer_id == our_peer_id);
1866 SharedBuffer<u8> data(2+2);
1867 writeU16(&data[0], TOSERVER_PLAYERITEM);
1868 writeU16(&data[2], item);
1871 Send(0, data, true);
1874 void Client::removeNode(v3s16 p)
1876 core::map<v3s16, MapBlock*> modified_blocks;
1880 //TimeTaker t("removeNodeAndUpdate", m_device);
1881 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
1883 catch(InvalidPositionException &e)
1887 // add urgent task to update the modified node
1888 addUpdateMeshTaskForNode(p, false, true);
1890 for(core::map<v3s16, MapBlock * >::Iterator
1891 i = modified_blocks.getIterator();
1892 i.atEnd() == false; i++)
1894 v3s16 p = i.getNode()->getKey();
1895 addUpdateMeshTaskWithEdge(p);
1899 void Client::addNode(v3s16 p, MapNode n)
1901 TimeTaker timer1("Client::addNode()");
1903 core::map<v3s16, MapBlock*> modified_blocks;
1907 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
1908 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks);
1910 catch(InvalidPositionException &e)
1913 for(core::map<v3s16, MapBlock * >::Iterator
1914 i = modified_blocks.getIterator();
1915 i.atEnd() == false; i++)
1917 v3s16 p = i.getNode()->getKey();
1918 addUpdateMeshTaskWithEdge(p);
1922 void Client::setPlayerControl(PlayerControl &control)
1924 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1925 LocalPlayer *player = m_env.getLocalPlayer();
1926 assert(player != NULL);
1927 player->control = control;
1930 void Client::selectPlayerItem(u16 item)
1932 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1933 m_playeritem = item;
1934 m_inventory_updated = true;
1935 sendPlayerItem(item);
1938 // Returns true if the inventory of the local player has been
1939 // updated from the server. If it is true, it is set to false.
1940 bool Client::getLocalInventoryUpdated()
1942 // m_inventory_updated is behind envlock
1943 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1944 bool updated = m_inventory_updated;
1945 m_inventory_updated = false;
1949 // Copies the inventory of the local player to parameter
1950 void Client::getLocalInventory(Inventory &dst)
1952 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1953 Player *player = m_env.getLocalPlayer();
1954 assert(player != NULL);
1955 dst = player->inventory;
1958 Inventory* Client::getInventory(const InventoryLocation &loc)
1961 case InventoryLocation::UNDEFINED:
1964 case InventoryLocation::CURRENT_PLAYER:
1966 Player *player = m_env.getLocalPlayer();
1967 assert(player != NULL);
1968 return &player->inventory;
1971 case InventoryLocation::PLAYER:
1973 Player *player = m_env.getPlayer(loc.name.c_str());
1976 return &player->inventory;
1979 case InventoryLocation::NODEMETA:
1981 NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p);
1984 return meta->getInventory();
1992 void Client::inventoryAction(InventoryAction *a)
1995 Send it to the server
1997 sendInventoryAction(a);
2000 Predict some local inventory changes
2002 a->clientApply(this, this);
2005 ClientActiveObject * Client::getSelectedActiveObject(
2007 v3f from_pos_f_on_map,
2008 core::line3d<f32> shootline_on_map
2011 core::array<DistanceSortedActiveObject> objects;
2013 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
2015 //infostream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
2018 // After this, the closest object is the first in the array.
2021 for(u32 i=0; i<objects.size(); i++)
2023 ClientActiveObject *obj = objects[i].obj;
2025 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
2026 if(selection_box == NULL)
2029 v3f pos = obj->getPosition();
2031 core::aabbox3d<f32> offsetted_box(
2032 selection_box->MinEdge + pos,
2033 selection_box->MaxEdge + pos
2036 if(offsetted_box.intersectsWithLine(shootline_on_map))
2038 //infostream<<"Returning selected object"<<std::endl;
2043 //infostream<<"No object selected; returning NULL."<<std::endl;
2047 void Client::printDebugInfo(std::ostream &os)
2049 //JMutexAutoLock lock1(m_fetchblock_mutex);
2050 /*JMutexAutoLock lock2(m_incoming_queue_mutex);
2052 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
2053 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
2054 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
2058 core::list<std::wstring> Client::getConnectedPlayerNames()
2060 core::list<Player*> players = m_env.getPlayers(true);
2061 core::list<std::wstring> playerNames;
2062 for(core::list<Player*>::Iterator
2063 i = players.begin();
2064 i != players.end(); i++)
2066 Player *player = *i;
2067 playerNames.push_back(narrow_to_wide(player->getName()));
2072 float Client::getAnimationTime()
2074 return m_animation_time;
2077 int Client::getCrackLevel()
2079 return m_crack_level;
2082 void Client::setCrack(int level, v3s16 pos)
2084 int old_crack_level = m_crack_level;
2085 v3s16 old_crack_pos = m_crack_pos;
2087 m_crack_level = level;
2090 if(old_crack_level >= 0 && (level < 0 || pos != old_crack_pos))
2093 addUpdateMeshTaskForNode(old_crack_pos, false, true);
2095 if(level >= 0 && (old_crack_level < 0 || pos != old_crack_pos))
2098 addUpdateMeshTaskForNode(pos, false, true);
2104 Player *player = m_env.getLocalPlayer();
2105 assert(player != NULL);
2109 bool Client::getChatMessage(std::wstring &message)
2111 if(m_chat_queue.size() == 0)
2113 message = m_chat_queue.pop_front();
2117 void Client::typeChatMessage(const std::wstring &message)
2119 // Discard empty line
2124 sendChatMessage(message);
2127 if (message[0] == L'/')
2129 m_chat_queue.push_back(
2130 (std::wstring)L"issued command: "+message);
2134 LocalPlayer *player = m_env.getLocalPlayer();
2135 assert(player != NULL);
2136 std::wstring name = narrow_to_wide(player->getName());
2137 m_chat_queue.push_back(
2138 (std::wstring)L"<"+name+L"> "+message);
2142 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
2144 /*infostream<<"Client::addUpdateMeshTask(): "
2145 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2146 <<" ack_to_server="<<ack_to_server
2147 <<" urgent="<<urgent
2150 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2155 Create a task to update the mesh of the block
2158 MeshMakeData *data = new MeshMakeData(this);
2161 //TimeTaker timer("data fill");
2163 // Debug: 1-6ms, avg=2ms
2165 data->setCrack(m_crack_level, m_crack_pos);
2166 data->setSmoothLighting(g_settings->getBool("smooth_lighting"));
2170 //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
2172 // Add task to queue
2173 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server, urgent);
2175 /*infostream<<"Mesh update input queue size is "
2176 <<m_mesh_update_thread.m_queue_in.size()
2180 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent)
2184 infostream<<"Client::addUpdateMeshTaskWithEdge(): "
2185 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2190 v3s16 p = blockpos + v3s16(0,0,0);
2191 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2192 addUpdateMeshTask(p, ack_to_server, urgent);
2194 catch(InvalidPositionException &e){}
2197 v3s16 p = blockpos + v3s16(-1,0,0);
2198 addUpdateMeshTask(p, false, urgent);
2200 catch(InvalidPositionException &e){}
2202 v3s16 p = blockpos + v3s16(0,-1,0);
2203 addUpdateMeshTask(p, false, urgent);
2205 catch(InvalidPositionException &e){}
2207 v3s16 p = blockpos + v3s16(0,0,-1);
2208 addUpdateMeshTask(p, false, urgent);
2210 catch(InvalidPositionException &e){}
2213 void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool urgent)
2217 infostream<<"Client::addUpdateMeshTaskForNode(): "
2218 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2222 v3s16 blockpos = getNodeBlockPos(nodepos);
2223 v3s16 blockpos_relative = blockpos * MAP_BLOCKSIZE;
2226 v3s16 p = blockpos + v3s16(0,0,0);
2227 addUpdateMeshTask(p, ack_to_server, urgent);
2229 catch(InvalidPositionException &e){}
2231 if(nodepos.X == blockpos_relative.X){
2233 v3s16 p = blockpos + v3s16(-1,0,0);
2234 addUpdateMeshTask(p, false, urgent);
2236 catch(InvalidPositionException &e){}
2238 if(nodepos.Y == blockpos_relative.Y){
2240 v3s16 p = blockpos + v3s16(0,-1,0);
2241 addUpdateMeshTask(p, false, urgent);
2243 catch(InvalidPositionException &e){}
2245 if(nodepos.Z == blockpos_relative.Z){
2247 v3s16 p = blockpos + v3s16(0,0,-1);
2248 addUpdateMeshTask(p, false, urgent);
2250 catch(InvalidPositionException &e){}
2254 ClientEvent Client::getClientEvent()
2256 if(m_client_event_queue.size() == 0)
2259 event.type = CE_NONE;
2262 return m_client_event_queue.pop_front();
2265 void Client::afterContentReceived()
2267 assert(m_itemdef_received);
2268 assert(m_nodedef_received);
2269 assert(m_textures_received);
2271 // Rebuild inherited images and recreate textures
2272 m_tsrc->rebuildImagesAndTextures();
2274 // Update texture atlas
2275 if(g_settings->getBool("enable_texture_atlas"))
2276 m_tsrc->buildMainAtlas(this);
2278 // Update node aliases
2279 m_nodedef->updateAliases(m_itemdef);
2281 // Update node textures
2282 m_nodedef->updateTextures(m_tsrc);
2284 // Update item textures and meshes
2285 m_itemdef->updateTexturesAndMeshes(this);
2287 // Start mesh update thread after setting up content definitions
2288 m_mesh_update_thread.Start();
2291 float Client::getRTT(void)
2294 return m_con.GetPeerAvgRTT(PEER_ID_SERVER);
2295 } catch(con::PeerNotFoundException &e){
2300 // IGameDef interface
2302 IItemDefManager* Client::getItemDefManager()
2306 INodeDefManager* Client::getNodeDefManager()
2310 ICraftDefManager* Client::getCraftDefManager()
2313 //return m_craftdef;
2315 ITextureSource* Client::getTextureSource()
2319 u16 Client::allocateUnknownNodeId(const std::string &name)
2321 errorstream<<"Client::allocateUnknownNodeId(): "
2322 <<"Client cannot allocate node IDs"<<std::endl;
2324 return CONTENT_IGNORE;