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,
229 ISoundManager *sound,
230 MtEventManager *event
237 m_mesh_update_thread(this),
239 new ClientMap(this, this, control,
240 device->getSceneManager()->getRootSceneNode(),
241 device->getSceneManager(), 666),
242 device->getSceneManager(),
245 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
247 m_server_ser_ver(SER_FMT_VER_INVALID),
249 m_inventory_updated(false),
250 m_inventory_from_server(NULL),
251 m_inventory_from_server_age(0.0),
256 m_password(password),
257 m_access_denied(false),
258 m_texture_receive_progress(0),
259 m_textures_received(false),
260 m_itemdef_received(false),
261 m_nodedef_received(false),
262 m_time_of_day_set(false),
263 m_last_time_of_day_f(-1),
264 m_time_of_day_update_timer(0)
266 m_packetcounter_timer = 0.0;
267 //m_delete_unused_sectors_timer = 0.0;
268 m_connection_reinit_timer = 0.0;
269 m_avg_rtt_timer = 0.0;
270 m_playerpos_send_timer = 0.0;
271 m_ignore_damage_timer = 0.0;
273 // Build main texture atlas, now that the GameDef exists (that is, us)
274 if(g_settings->getBool("enable_texture_atlas"))
275 m_tsrc->buildMainAtlas(this);
277 infostream<<"Not building texture atlas."<<std::endl;
283 Player *player = new LocalPlayer(this);
285 player->updateName(playername);
287 m_env.addPlayer(player);
294 //JMutexAutoLock conlock(m_con_mutex); //bulk comment-out
298 m_mesh_update_thread.setRun(false);
299 while(m_mesh_update_thread.IsRunning())
302 delete m_inventory_from_server;
305 void Client::connect(Address address)
307 DSTACK(__FUNCTION_NAME);
308 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
309 m_con.SetTimeoutMs(0);
310 m_con.Connect(address);
313 bool Client::connectedAndInitialized()
315 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
317 if(m_con.Connected() == false)
320 if(m_server_ser_ver == SER_FMT_VER_INVALID)
326 void Client::step(float dtime)
328 DSTACK(__FUNCTION_NAME);
334 if(m_ignore_damage_timer > dtime)
335 m_ignore_damage_timer -= dtime;
337 m_ignore_damage_timer = 0.0;
339 m_animation_time += dtime;
340 if(m_animation_time > 60.0)
341 m_animation_time -= 60.0;
343 m_time_of_day_update_timer += dtime;
345 //infostream<<"Client steps "<<dtime<<std::endl;
348 //TimeTaker timer("ReceiveAll()", m_device);
354 //TimeTaker timer("m_con_mutex + m_con.RunTimeouts()", m_device);
356 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
357 m_con.RunTimeouts(dtime);
364 float &counter = m_packetcounter_timer;
370 infostream<<"Client packetcounter (20s):"<<std::endl;
371 m_packetcounter.print(infostream);
372 m_packetcounter.clear();
376 // Get connection status
377 bool connected = connectedAndInitialized();
382 Delete unused sectors
384 NOTE: This jams the game for a while because deleting sectors
388 float &counter = m_delete_unused_sectors_timer;
396 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
398 core::list<v3s16> deleted_blocks;
400 float delete_unused_sectors_timeout =
401 g_settings->getFloat("client_delete_unused_sectors_timeout");
403 // Delete sector blocks
404 /*u32 num = m_env.getMap().unloadUnusedData
405 (delete_unused_sectors_timeout,
406 true, &deleted_blocks);*/
408 // Delete whole sectors
409 m_env.getMap().unloadUnusedData
410 (delete_unused_sectors_timeout,
413 if(deleted_blocks.size() > 0)
415 /*infostream<<"Client: Deleted blocks of "<<num
416 <<" unused sectors"<<std::endl;*/
417 /*infostream<<"Client: Deleted "<<num
418 <<" unused sectors"<<std::endl;*/
424 // Env is locked so con can be locked.
425 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
427 core::list<v3s16>::Iterator i = deleted_blocks.begin();
428 core::list<v3s16> sendlist;
431 if(sendlist.size() == 255 || i == deleted_blocks.end())
433 if(sendlist.size() == 0)
442 u32 replysize = 2+1+6*sendlist.size();
443 SharedBuffer<u8> reply(replysize);
444 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
445 reply[2] = sendlist.size();
447 for(core::list<v3s16>::Iterator
448 j = sendlist.begin();
449 j != sendlist.end(); j++)
451 writeV3S16(&reply[2+1+6*k], *j);
454 m_con.Send(PEER_ID_SERVER, 1, reply, true);
456 if(i == deleted_blocks.end())
462 sendlist.push_back(*i);
470 if(connected == false)
472 float &counter = m_connection_reinit_timer;
478 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
480 Player *myplayer = m_env.getLocalPlayer();
481 assert(myplayer != NULL);
483 // Send TOSERVER_INIT
484 // [0] u16 TOSERVER_INIT
485 // [2] u8 SER_FMT_VER_HIGHEST
486 // [3] u8[20] player_name
487 // [23] u8[28] password (new in some version)
488 // [51] u16 client network protocol version (new in some version)
489 SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2);
490 writeU16(&data[0], TOSERVER_INIT);
491 writeU8(&data[2], SER_FMT_VER_HIGHEST);
493 memset((char*)&data[3], 0, PLAYERNAME_SIZE);
494 snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
496 /*infostream<<"Client: sending initial password hash: \""<<m_password<<"\""
499 memset((char*)&data[23], 0, PASSWORD_SIZE);
500 snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
502 // This should be incremented in each version
503 writeU16(&data[51], PROTOCOL_VERSION);
505 // Send as unreliable
506 Send(0, data, false);
509 // Not connected, return
514 Do stuff if connected
518 Run Map's timers and unload unused data
520 const float map_timer_and_unload_dtime = 5.25;
521 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
523 ScopeProfiler sp(g_profiler, "Client: map timer and unload");
524 core::list<v3s16> deleted_blocks;
525 m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
526 g_settings->getFloat("client_unload_unused_data_timeout"),
529 /*if(deleted_blocks.size() > 0)
530 infostream<<"Client: Unloaded "<<deleted_blocks.size()
531 <<" unused blocks"<<std::endl;*/
535 NOTE: This loop is intentionally iterated the way it is.
538 core::list<v3s16>::Iterator i = deleted_blocks.begin();
539 core::list<v3s16> sendlist;
542 if(sendlist.size() == 255 || i == deleted_blocks.end())
544 if(sendlist.size() == 0)
553 u32 replysize = 2+1+6*sendlist.size();
554 SharedBuffer<u8> reply(replysize);
555 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
556 reply[2] = sendlist.size();
558 for(core::list<v3s16>::Iterator
559 j = sendlist.begin();
560 j != sendlist.end(); j++)
562 writeV3S16(&reply[2+1+6*k], *j);
565 m_con.Send(PEER_ID_SERVER, 1, reply, true);
567 if(i == deleted_blocks.end())
573 sendlist.push_back(*i);
583 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
585 // Control local player (0ms)
586 LocalPlayer *player = m_env.getLocalPlayer();
587 assert(player != NULL);
588 player->applyControl(dtime);
590 //TimeTaker envtimer("env step", m_device);
599 ClientEnvEvent event = m_env.getClientEvent();
600 if(event.type == CEE_NONE)
604 else if(event.type == CEE_PLAYER_DAMAGE)
606 if(m_ignore_damage_timer <= 0)
608 u8 damage = event.player_damage.amount;
610 if(event.player_damage.send_to_server)
613 // Add to ClientEvent queue
615 event.type = CE_PLAYER_DAMAGE;
616 event.player_damage.amount = damage;
617 m_client_event_queue.push_back(event);
627 float &counter = m_avg_rtt_timer;
632 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
633 // connectedAndInitialized() is true, peer exists.
634 float avg_rtt = m_con.GetPeerAvgRTT(PEER_ID_SERVER);
635 infostream<<"Client: avg_rtt="<<avg_rtt<<std::endl;
640 Send player position to server
643 float &counter = m_playerpos_send_timer;
653 Replace updated meshes
656 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
658 //TimeTaker timer("** Processing mesh update result queue");
661 /*infostream<<"Mesh update result queue size is "
662 <<m_mesh_update_thread.m_queue_out.size()
665 int num_processed_meshes = 0;
666 while(m_mesh_update_thread.m_queue_out.size() > 0)
668 num_processed_meshes++;
669 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_front();
670 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
673 //JMutexAutoLock lock(block->mesh_mutex);
675 // Delete the old mesh
676 if(block->mesh != NULL)
678 // TODO: Remove hardware buffers of meshbuffers of block->mesh
683 // Replace with the new mesh
684 block->mesh = r.mesh;
686 if(r.ack_block_to_server)
688 /*infostream<<"Client: ACK block ("<<r.p.X<<","<<r.p.Y
689 <<","<<r.p.Z<<")"<<std::endl;*/
700 u32 replysize = 2+1+6;
701 SharedBuffer<u8> reply(replysize);
702 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
704 writeV3S16(&reply[3], r.p);
706 m_con.Send(PEER_ID_SERVER, 1, reply, true);
709 if(num_processed_meshes > 0)
710 g_profiler->graphAdd("num_processed_meshes", num_processed_meshes);
714 If the server didn't update the inventory in a while, revert
715 the local inventory (so the player notices the lag problem
716 and knows something is wrong).
718 if(m_inventory_from_server)
720 float interval = 10.0;
721 float count_before = floor(m_inventory_from_server_age / interval);
723 m_inventory_from_server_age += dtime;
725 float count_after = floor(m_inventory_from_server_age / interval);
727 if(count_after != count_before)
729 // Do this every <interval> seconds after TOCLIENT_INVENTORY
730 // Reset the locally changed inventory to the authoritative inventory
731 Player *player = m_env.getLocalPlayer();
732 player->inventory = *m_inventory_from_server;
733 m_inventory_updated = true;
738 // Virtual methods from con::PeerHandler
739 void Client::peerAdded(con::Peer *peer)
741 infostream<<"Client::peerAdded(): peer->id="
742 <<peer->id<<std::endl;
744 void Client::deletingPeer(con::Peer *peer, bool timeout)
746 infostream<<"Client::deletingPeer(): "
747 "Server Peer is getting deleted "
748 <<"(timeout="<<timeout<<")"<<std::endl;
751 void Client::ReceiveAll()
753 DSTACK(__FUNCTION_NAME);
754 u32 start_ms = porting::getTimeMs();
757 // Limit time even if there would be huge amounts of data to
759 if(porting::getTimeMs() > start_ms + 100)
764 g_profiler->graphAdd("client_received_packets", 1);
766 catch(con::NoIncomingDataException &e)
770 catch(con::InvalidIncomingDataException &e)
772 infostream<<"Client::ReceiveAll(): "
773 "InvalidIncomingDataException: what()="
774 <<e.what()<<std::endl;
779 void Client::Receive()
781 DSTACK(__FUNCTION_NAME);
782 SharedBuffer<u8> data;
786 //TimeTaker t1("con mutex and receive", m_device);
787 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
788 datasize = m_con.Receive(sender_peer_id, data);
790 //TimeTaker t1("ProcessData", m_device);
791 ProcessData(*data, datasize, sender_peer_id);
795 sender_peer_id given to this shall be quaranteed to be a valid peer
797 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
799 DSTACK(__FUNCTION_NAME);
801 // Ignore packets that don't even fit a command
804 m_packetcounter.add(60000);
808 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
810 //infostream<<"Client: received command="<<command<<std::endl;
811 m_packetcounter.add((u16)command);
814 If this check is removed, be sure to change the queue
815 system to know the ids
817 if(sender_peer_id != PEER_ID_SERVER)
819 infostream<<"Client::ProcessData(): Discarding data not "
820 "coming from server: peer_id="<<sender_peer_id
825 u8 ser_version = m_server_ser_ver;
827 //infostream<<"Client received command="<<(int)command<<std::endl;
829 if(command == TOCLIENT_INIT)
834 u8 deployed = data[2];
836 infostream<<"Client: TOCLIENT_INIT received with "
837 "deployed="<<((int)deployed&0xff)<<std::endl;
839 if(deployed < SER_FMT_VER_LOWEST
840 || deployed > SER_FMT_VER_HIGHEST)
842 infostream<<"Client: TOCLIENT_INIT: Server sent "
843 <<"unsupported ser_fmt_ver"<<std::endl;
847 m_server_ser_ver = deployed;
849 // Get player position
850 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
851 if(datasize >= 2+1+6)
852 playerpos_s16 = readV3S16(&data[2+1]);
853 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
856 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
858 // Set player position
859 Player *player = m_env.getLocalPlayer();
860 assert(player != NULL);
861 player->setPosition(playerpos_f);
864 if(datasize >= 2+1+6+8)
867 m_map_seed = readU64(&data[2+1+6]);
868 infostream<<"Client: received map seed: "<<m_map_seed<<std::endl;
873 SharedBuffer<u8> reply(replysize);
874 writeU16(&reply[0], TOSERVER_INIT2);
876 m_con.Send(PEER_ID_SERVER, 1, reply, true);
881 if(command == TOCLIENT_ACCESS_DENIED)
883 // The server didn't like our password. Note, this needs
884 // to be processed even if the serialisation format has
885 // not been agreed yet, the same as TOCLIENT_INIT.
886 m_access_denied = true;
887 m_access_denied_reason = L"Unknown";
890 std::string datastring((char*)&data[2], datasize-2);
891 std::istringstream is(datastring, std::ios_base::binary);
892 m_access_denied_reason = deSerializeWideString(is);
897 if(ser_version == SER_FMT_VER_INVALID)
899 infostream<<"Client: Server serialization"
900 " format invalid or not initialized."
901 " Skipping incoming command="<<command<<std::endl;
905 // Just here to avoid putting the two if's together when
906 // making some copypasta
909 if(command == TOCLIENT_REMOVENODE)
914 p.X = readS16(&data[2]);
915 p.Y = readS16(&data[4]);
916 p.Z = readS16(&data[6]);
918 //TimeTaker t1("TOCLIENT_REMOVENODE");
922 else if(command == TOCLIENT_ADDNODE)
924 if(datasize < 8 + MapNode::serializedLength(ser_version))
928 p.X = readS16(&data[2]);
929 p.Y = readS16(&data[4]);
930 p.Z = readS16(&data[6]);
932 //TimeTaker t1("TOCLIENT_ADDNODE");
935 n.deSerialize(&data[8], ser_version);
939 else if(command == TOCLIENT_BLOCKDATA)
941 // Ignore too small packet
946 p.X = readS16(&data[2]);
947 p.Y = readS16(&data[4]);
948 p.Z = readS16(&data[6]);
950 /*infostream<<"Client: Thread: BLOCKDATA for ("
951 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
952 /*infostream<<"Client: Thread: BLOCKDATA for ("
953 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
955 std::string datastring((char*)&data[8], datasize-8);
956 std::istringstream istr(datastring, std::ios_base::binary);
962 sector = m_env.getMap().emergeSector(p2d);
964 assert(sector->getPos() == p2d);
966 //TimeTaker timer("MapBlock deSerialize");
969 block = sector->getBlockNoCreateNoEx(p.Y);
973 Update an existing block
975 //infostream<<"Updating"<<std::endl;
976 block->deSerialize(istr, ser_version, false);
983 //infostream<<"Creating new"<<std::endl;
984 block = new MapBlock(&m_env.getMap(), p, this);
985 block->deSerialize(istr, ser_version, false);
986 sector->insertBlock(block);
1000 u32 replysize = 2+1+6;
1001 SharedBuffer<u8> reply(replysize);
1002 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
1004 writeV3S16(&reply[3], p);
1006 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1010 Add it to mesh update queue and set it to be acknowledged after update.
1012 //infostream<<"Adding mesh update task for received block"<<std::endl;
1013 addUpdateMeshTaskWithEdge(p, true);
1015 else if(command == TOCLIENT_INVENTORY)
1020 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
1023 //TimeTaker t2("mutex locking", m_device);
1024 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1027 //TimeTaker t3("istringstream init", m_device);
1028 std::string datastring((char*)&data[2], datasize-2);
1029 std::istringstream is(datastring, std::ios_base::binary);
1032 //m_env.printPlayers(infostream);
1034 //TimeTaker t4("player get", m_device);
1035 Player *player = m_env.getLocalPlayer();
1036 assert(player != NULL);
1039 //TimeTaker t1("inventory.deSerialize()", m_device);
1040 player->inventory.deSerialize(is);
1043 m_inventory_updated = true;
1045 delete m_inventory_from_server;
1046 m_inventory_from_server = new Inventory(player->inventory);
1047 m_inventory_from_server_age = 0.0;
1049 //infostream<<"Client got player inventory:"<<std::endl;
1050 //player->inventory.print(infostream);
1053 else if(command == TOCLIENT_TIME_OF_DAY)
1058 u16 time_of_day = readU16(&data[2]);
1059 time_of_day = time_of_day % 24000;
1060 //infostream<<"Client: time_of_day="<<time_of_day<<std::endl;
1061 float time_speed = 0;
1062 if(datasize >= 2 + 2 + 4){
1063 time_speed = readF1000(&data[4]);
1065 // Old message; try to approximate speed of time by ourselves
1066 float time_of_day_f = (float)time_of_day / 24000.0;
1067 float tod_diff_f = 0;
1068 if(time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
1069 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0;
1071 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
1072 m_last_time_of_day_f = time_of_day_f;
1073 float time_diff = m_time_of_day_update_timer;
1074 m_time_of_day_update_timer = 0;
1075 if(m_time_of_day_set){
1076 time_speed = 3600.0*24.0 * tod_diff_f / time_diff;
1077 infostream<<"Client: Measured time_of_day speed (old format): "
1078 <<time_speed<<" tod_diff_f="<<tod_diff_f
1079 <<" time_diff="<<time_diff<<std::endl;
1083 // Update environment
1084 m_env.setTimeOfDay(time_of_day);
1085 m_env.setTimeOfDaySpeed(time_speed);
1086 m_time_of_day_set = true;
1088 u32 dr = m_env.getDayNightRatio();
1089 verbosestream<<"Client: time_of_day="<<time_of_day
1090 <<" time_speed="<<time_speed
1091 <<" dr="<<dr<<std::endl;
1093 else if(command == TOCLIENT_CHAT_MESSAGE)
1101 std::string datastring((char*)&data[2], datasize-2);
1102 std::istringstream is(datastring, std::ios_base::binary);
1105 is.read((char*)buf, 2);
1106 u16 len = readU16(buf);
1108 std::wstring message;
1109 for(u16 i=0; i<len; i++)
1111 is.read((char*)buf, 2);
1112 message += (wchar_t)readU16(buf);
1115 /*infostream<<"Client received chat message: "
1116 <<wide_to_narrow(message)<<std::endl;*/
1118 m_chat_queue.push_back(message);
1120 else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1122 //if(g_settings->getBool("enable_experimental"))
1126 u16 count of removed objects
1127 for all removed objects {
1130 u16 count of added objects
1131 for all added objects {
1134 u32 initialization data length
1135 string initialization data
1140 // Get all data except the command number
1141 std::string datastring((char*)&data[2], datasize-2);
1142 // Throw them in an istringstream
1143 std::istringstream is(datastring, std::ios_base::binary);
1147 // Read removed objects
1149 u16 removed_count = readU16((u8*)buf);
1150 for(u16 i=0; i<removed_count; i++)
1153 u16 id = readU16((u8*)buf);
1156 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1157 m_env.removeActiveObject(id);
1161 // Read added objects
1163 u16 added_count = readU16((u8*)buf);
1164 for(u16 i=0; i<added_count; i++)
1167 u16 id = readU16((u8*)buf);
1169 u8 type = readU8((u8*)buf);
1170 std::string data = deSerializeLongString(is);
1173 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1174 m_env.addActiveObject(id, type, data);
1179 else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1181 //if(g_settings->getBool("enable_experimental"))
1193 // Get all data except the command number
1194 std::string datastring((char*)&data[2], datasize-2);
1195 // Throw them in an istringstream
1196 std::istringstream is(datastring, std::ios_base::binary);
1198 while(is.eof() == false)
1202 u16 id = readU16((u8*)buf);
1206 u16 message_size = readU16((u8*)buf);
1207 std::string message;
1208 message.reserve(message_size);
1209 for(u16 i=0; i<message_size; i++)
1212 message.append(buf, 1);
1214 // Pass on to the environment
1216 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1217 m_env.processActiveObjectMessage(id, message);
1222 else if(command == TOCLIENT_HP)
1224 std::string datastring((char*)&data[2], datasize-2);
1225 std::istringstream is(datastring, std::ios_base::binary);
1226 Player *player = m_env.getLocalPlayer();
1227 assert(player != NULL);
1228 u8 oldhp = player->hp;
1234 // Add to ClientEvent queue
1236 event.type = CE_PLAYER_DAMAGE;
1237 event.player_damage.amount = oldhp - hp;
1238 m_client_event_queue.push_back(event);
1241 else if(command == TOCLIENT_MOVE_PLAYER)
1243 std::string datastring((char*)&data[2], datasize-2);
1244 std::istringstream is(datastring, std::ios_base::binary);
1245 Player *player = m_env.getLocalPlayer();
1246 assert(player != NULL);
1247 v3f pos = readV3F1000(is);
1248 f32 pitch = readF1000(is);
1249 f32 yaw = readF1000(is);
1250 player->setPosition(pos);
1251 /*player->setPitch(pitch);
1252 player->setYaw(yaw);*/
1254 infostream<<"Client got TOCLIENT_MOVE_PLAYER"
1255 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1261 Add to ClientEvent queue.
1262 This has to be sent to the main program because otherwise
1263 it would just force the pitch and yaw values to whatever
1264 the camera points to.
1267 event.type = CE_PLAYER_FORCE_MOVE;
1268 event.player_force_move.pitch = pitch;
1269 event.player_force_move.yaw = yaw;
1270 m_client_event_queue.push_back(event);
1272 // Ignore damage for a few seconds, so that the player doesn't
1273 // get damage from falling on ground
1274 m_ignore_damage_timer = 3.0;
1276 else if(command == TOCLIENT_PLAYERITEM)
1278 std::string datastring((char*)&data[2], datasize-2);
1279 std::istringstream is(datastring, std::ios_base::binary);
1281 u16 count = readU16(is);
1283 for (u16 i = 0; i < count; ++i) {
1284 u16 peer_id = readU16(is);
1285 Player *player = m_env.getPlayer(peer_id);
1289 infostream<<"Client: ignoring player item "
1290 << deSerializeString(is)
1291 << " for non-existing peer id " << peer_id
1294 } else if (player->isLocal()) {
1295 infostream<<"Client: ignoring player item "
1296 << deSerializeString(is)
1297 << " for local player" << std::endl;
1300 InventoryList *inv = player->inventory.getList("main");
1301 std::string itemstring(deSerializeString(is));
1303 item.deSerialize(itemstring, m_itemdef);
1304 inv->changeItem(0, item);
1305 if(itemstring.empty())
1307 infostream<<"Client: empty player item for peer "
1308 <<peer_id<<std::endl;
1312 infostream<<"Client: player item for peer "
1313 <<peer_id<<": "<<itemstring<<std::endl;
1318 else if(command == TOCLIENT_DEATHSCREEN)
1320 std::string datastring((char*)&data[2], datasize-2);
1321 std::istringstream is(datastring, std::ios_base::binary);
1323 bool set_camera_point_target = readU8(is);
1324 v3f camera_point_target = readV3F1000(is);
1327 event.type = CE_DEATHSCREEN;
1328 event.deathscreen.set_camera_point_target = set_camera_point_target;
1329 event.deathscreen.camera_point_target_x = camera_point_target.X;
1330 event.deathscreen.camera_point_target_y = camera_point_target.Y;
1331 event.deathscreen.camera_point_target_z = camera_point_target.Z;
1332 m_client_event_queue.push_back(event);
1334 else if(command == TOCLIENT_ANNOUNCE_TEXTURES)
1336 io::IFileSystem *irrfs = m_device->getFileSystem();
1337 video::IVideoDriver *vdrv = m_device->getVideoDriver();
1339 std::string datastring((char*)&data[2], datasize-2);
1340 std::istringstream is(datastring, std::ios_base::binary);
1342 // Mesh update thread must be stopped while
1343 // updating content definitions
1344 assert(!m_mesh_update_thread.IsRunning());
1346 int num_textures = readU16(is);
1348 core::list<TextureRequest> texture_requests;
1350 for(int i=0; i<num_textures; i++){
1352 bool texture_found = false;
1354 //read texture from cache
1355 std::string name = deSerializeString(is);
1356 std::string sha1_texture = deSerializeString(is);
1358 // if name contains illegal characters, ignore the texture
1359 if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
1360 errorstream<<"Client: ignoring illegal texture name "
1361 <<"sent by server: \""<<name<<"\""<<std::endl;
1365 std::string tpath = getTextureCacheDir() + DIR_DELIM + name;
1367 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
1370 if(fis.good() == false){
1371 infostream<<"Client::Texture not found in cache: "
1372 <<name << " expected it at: "<<tpath<<std::endl;
1376 std::ostringstream tmp_os(std::ios_base::binary);
1380 fis.read(buf, 1024);
1381 std::streamsize len = fis.gcount();
1382 tmp_os.write(buf, len);
1391 infostream<<"Client: Failed to read texture from cache\""
1392 <<name<<"\""<<std::endl;
1397 sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
1399 unsigned char *digest = sha1.getDigest();
1401 std::string digest_string = base64_encode(digest, 20);
1403 if (digest_string == sha1_texture) {
1404 // Silly irrlicht's const-incorrectness
1405 Buffer<char> data_rw(tmp_os.str().c_str(), tmp_os.str().size());
1407 // Create an irrlicht memory file
1408 io::IReadFile *rfile = irrfs->createMemoryReadFile(
1409 *data_rw, tmp_os.str().size(), "_tempreadfile");
1412 video::IImage *img = vdrv->createImageFromFile(rfile);
1414 infostream<<"Client: Cannot create image from data of "
1415 <<"received texture \""<<name<<"\""<<std::endl;
1419 m_tsrc->insertSourceImage(name, img);
1423 texture_found = true;
1427 infostream<<"Client::Texture cached sha1 hash not matching server hash: "
1428 <<name << ": server ->"<<sha1_texture <<" client -> "<<digest_string<<std::endl;
1435 //add texture request
1436 if (!texture_found) {
1437 infostream<<"Client: Adding texture to request list: \""
1438 <<name<<"\""<<std::endl;
1439 texture_requests.push_back(TextureRequest(name));
1445 event.type = CE_TEXTURES_UPDATED;
1446 m_client_event_queue.push_back(event);
1449 //send Texture request
1452 u16 number of textures requested
1458 std::ostringstream os(std::ios_base::binary);
1463 writeU16(buf, TOSERVER_REQUEST_TEXTURES);
1464 os.write((char*)buf, 2);
1466 writeU16(buf,texture_requests.size());
1467 os.write((char*)buf, 2);
1470 for(core::list<TextureRequest>::Iterator i = texture_requests.begin();
1471 i != texture_requests.end(); i++) {
1472 os<<serializeString(i->name);
1476 std::string s = os.str();
1477 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1479 Send(0, data, true);
1480 infostream<<"Client: Sending request list to server " <<std::endl;
1482 else if(command == TOCLIENT_TEXTURES)
1484 io::IFileSystem *irrfs = m_device->getFileSystem();
1485 video::IVideoDriver *vdrv = m_device->getVideoDriver();
1487 std::string datastring((char*)&data[2], datasize-2);
1488 std::istringstream is(datastring, std::ios_base::binary);
1490 // Mesh update thread must be stopped while
1491 // updating content definitions
1492 assert(!m_mesh_update_thread.IsRunning());
1496 u16 total number of texture bunches
1497 u16 index of this bunch
1498 u32 number of textures in this bunch
1506 int num_bunches = readU16(is);
1507 int bunch_i = readU16(is);
1508 m_texture_receive_progress = (float)bunch_i / (float)(num_bunches - 1);
1509 if(bunch_i == num_bunches - 1)
1510 m_textures_received = true;
1511 int num_textures = readU32(is);
1512 infostream<<"Client: Received textures: bunch "<<bunch_i<<"/"
1513 <<num_bunches<<" textures="<<num_textures
1514 <<" size="<<datasize<<std::endl;
1515 for(int i=0; i<num_textures; i++){
1516 std::string name = deSerializeString(is);
1517 std::string data = deSerializeLongString(is);
1519 // if name contains illegal characters, ignore the texture
1520 if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
1521 errorstream<<"Client: ignoring illegal texture name "
1522 <<"sent by server: \""<<name<<"\""<<std::endl;
1526 // Silly irrlicht's const-incorrectness
1527 Buffer<char> data_rw(data.c_str(), data.size());
1528 // Create an irrlicht memory file
1529 io::IReadFile *rfile = irrfs->createMemoryReadFile(
1530 *data_rw, data.size(), "_tempreadfile");
1533 video::IImage *img = vdrv->createImageFromFile(rfile);
1535 errorstream<<"Client: Cannot create image from data of "
1536 <<"received texture \""<<name<<"\""<<std::endl;
1541 fs::CreateAllDirs(getTextureCacheDir());
1543 std::string filename = getTextureCacheDir() + DIR_DELIM + name;
1544 std::ofstream outfile(filename.c_str(), std::ios_base::binary | std::ios_base::trunc);
1546 if (outfile.good()) {
1547 outfile.write(data.c_str(),data.length());
1551 errorstream<<"Client: Unable to open cached texture file "<< filename <<std::endl;
1554 m_tsrc->insertSourceImage(name, img);
1560 event.type = CE_TEXTURES_UPDATED;
1561 m_client_event_queue.push_back(event);
1563 else if(command == TOCLIENT_TOOLDEF)
1565 infostream<<"Client: WARNING: Ignoring TOCLIENT_TOOLDEF"<<std::endl;
1567 else if(command == TOCLIENT_NODEDEF)
1569 infostream<<"Client: Received node definitions: packet size: "
1570 <<datasize<<std::endl;
1572 // Mesh update thread must be stopped while
1573 // updating content definitions
1574 assert(!m_mesh_update_thread.IsRunning());
1576 // Decompress node definitions
1577 std::string datastring((char*)&data[2], datasize-2);
1578 std::istringstream is(datastring, std::ios_base::binary);
1579 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1580 std::ostringstream tmp_os;
1581 decompressZlib(tmp_is, tmp_os);
1583 // Deserialize node definitions
1584 std::istringstream tmp_is2(tmp_os.str());
1585 m_nodedef->deSerialize(tmp_is2);
1586 m_nodedef_received = true;
1588 else if(command == TOCLIENT_CRAFTITEMDEF)
1590 infostream<<"Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF"<<std::endl;
1592 else if(command == TOCLIENT_ITEMDEF)
1594 infostream<<"Client: Received item definitions: packet size: "
1595 <<datasize<<std::endl;
1597 // Mesh update thread must be stopped while
1598 // updating content definitions
1599 assert(!m_mesh_update_thread.IsRunning());
1601 // Decompress item definitions
1602 std::string datastring((char*)&data[2], datasize-2);
1603 std::istringstream is(datastring, std::ios_base::binary);
1604 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1605 std::ostringstream tmp_os;
1606 decompressZlib(tmp_is, tmp_os);
1608 // Deserialize node definitions
1609 std::istringstream tmp_is2(tmp_os.str());
1610 m_itemdef->deSerialize(tmp_is2);
1611 m_itemdef_received = true;
1615 infostream<<"Client: Ignoring unknown command "
1616 <<command<<std::endl;
1620 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
1622 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1623 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
1626 void Client::interact(u8 action, const PointedThing& pointed)
1628 if(connectedAndInitialized() == false){
1629 infostream<<"Client::interact() "
1630 "cancelled (not connected)"
1635 std::ostringstream os(std::ios_base::binary);
1641 [5] u32 length of the next item
1642 [9] serialized PointedThing
1644 0: start digging (from undersurface) or use
1645 1: stop digging (all parameters ignored)
1646 2: digging completed
1647 3: place block or item (to abovesurface)
1650 writeU16(os, TOSERVER_INTERACT);
1651 writeU8(os, action);
1652 writeU16(os, getPlayerItem());
1653 std::ostringstream tmp_os(std::ios::binary);
1654 pointed.serialize(tmp_os);
1655 os<<serializeLongString(tmp_os.str());
1657 std::string s = os.str();
1658 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1661 Send(0, data, true);
1664 void Client::sendSignNodeText(v3s16 p, std::string text)
1672 std::ostringstream os(std::ios_base::binary);
1676 writeU16(buf, TOSERVER_SIGNNODETEXT);
1677 os.write((char*)buf, 2);
1681 os.write((char*)buf, 6);
1683 u16 textlen = text.size();
1684 // Write text length
1685 writeS16(buf, textlen);
1686 os.write((char*)buf, 2);
1689 os.write((char*)text.c_str(), textlen);
1692 std::string s = os.str();
1693 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1695 Send(0, data, true);
1698 void Client::sendInventoryAction(InventoryAction *a)
1700 std::ostringstream os(std::ios_base::binary);
1704 writeU16(buf, TOSERVER_INVENTORY_ACTION);
1705 os.write((char*)buf, 2);
1710 std::string s = os.str();
1711 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1713 Send(0, data, true);
1716 void Client::sendChatMessage(const std::wstring &message)
1718 std::ostringstream os(std::ios_base::binary);
1722 writeU16(buf, TOSERVER_CHAT_MESSAGE);
1723 os.write((char*)buf, 2);
1726 writeU16(buf, message.size());
1727 os.write((char*)buf, 2);
1730 for(u32 i=0; i<message.size(); i++)
1734 os.write((char*)buf, 2);
1738 std::string s = os.str();
1739 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1741 Send(0, data, true);
1744 void Client::sendChangePassword(const std::wstring oldpassword,
1745 const std::wstring newpassword)
1747 Player *player = m_env.getLocalPlayer();
1751 std::string playername = player->getName();
1752 std::string oldpwd = translatePassword(playername, oldpassword);
1753 std::string newpwd = translatePassword(playername, newpassword);
1755 std::ostringstream os(std::ios_base::binary);
1756 u8 buf[2+PASSWORD_SIZE*2];
1758 [0] u16 TOSERVER_PASSWORD
1759 [2] u8[28] old password
1760 [30] u8[28] new password
1763 writeU16(buf, TOSERVER_PASSWORD);
1764 for(u32 i=0;i<PASSWORD_SIZE-1;i++)
1766 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
1767 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
1769 buf[2+PASSWORD_SIZE-1] = 0;
1770 buf[30+PASSWORD_SIZE-1] = 0;
1771 os.write((char*)buf, 2+PASSWORD_SIZE*2);
1774 std::string s = os.str();
1775 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1777 Send(0, data, true);
1781 void Client::sendDamage(u8 damage)
1783 DSTACK(__FUNCTION_NAME);
1784 std::ostringstream os(std::ios_base::binary);
1786 writeU16(os, TOSERVER_DAMAGE);
1787 writeU8(os, damage);
1790 std::string s = os.str();
1791 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1793 Send(0, data, true);
1796 void Client::sendRespawn()
1798 DSTACK(__FUNCTION_NAME);
1799 std::ostringstream os(std::ios_base::binary);
1801 writeU16(os, TOSERVER_RESPAWN);
1804 std::string s = os.str();
1805 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1807 Send(0, data, true);
1810 void Client::sendPlayerPos()
1812 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1814 Player *myplayer = m_env.getLocalPlayer();
1815 if(myplayer == NULL)
1820 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1821 our_peer_id = m_con.GetPeerID();
1824 // Set peer id if not set already
1825 if(myplayer->peer_id == PEER_ID_INEXISTENT)
1826 myplayer->peer_id = our_peer_id;
1827 // Check that an existing peer_id is the same as the connection's
1828 assert(myplayer->peer_id == our_peer_id);
1830 v3f pf = myplayer->getPosition();
1831 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
1832 v3f sf = myplayer->getSpeed();
1833 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
1834 s32 pitch = myplayer->getPitch() * 100;
1835 s32 yaw = myplayer->getYaw() * 100;
1840 [2] v3s32 position*100
1841 [2+12] v3s32 speed*100
1842 [2+12+12] s32 pitch*100
1843 [2+12+12+4] s32 yaw*100
1846 SharedBuffer<u8> data(2+12+12+4+4);
1847 writeU16(&data[0], TOSERVER_PLAYERPOS);
1848 writeV3S32(&data[2], position);
1849 writeV3S32(&data[2+12], speed);
1850 writeS32(&data[2+12+12], pitch);
1851 writeS32(&data[2+12+12+4], yaw);
1853 // Send as unreliable
1854 Send(0, data, false);
1857 void Client::sendPlayerItem(u16 item)
1859 Player *myplayer = m_env.getLocalPlayer();
1860 if(myplayer == NULL)
1863 u16 our_peer_id = m_con.GetPeerID();
1865 // Set peer id if not set already
1866 if(myplayer->peer_id == PEER_ID_INEXISTENT)
1867 myplayer->peer_id = our_peer_id;
1868 // Check that an existing peer_id is the same as the connection's
1869 assert(myplayer->peer_id == our_peer_id);
1871 SharedBuffer<u8> data(2+2);
1872 writeU16(&data[0], TOSERVER_PLAYERITEM);
1873 writeU16(&data[2], item);
1876 Send(0, data, true);
1879 void Client::removeNode(v3s16 p)
1881 core::map<v3s16, MapBlock*> modified_blocks;
1885 //TimeTaker t("removeNodeAndUpdate", m_device);
1886 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
1888 catch(InvalidPositionException &e)
1892 // add urgent task to update the modified node
1893 addUpdateMeshTaskForNode(p, false, true);
1895 for(core::map<v3s16, MapBlock * >::Iterator
1896 i = modified_blocks.getIterator();
1897 i.atEnd() == false; i++)
1899 v3s16 p = i.getNode()->getKey();
1900 addUpdateMeshTaskWithEdge(p);
1904 void Client::addNode(v3s16 p, MapNode n)
1906 TimeTaker timer1("Client::addNode()");
1908 core::map<v3s16, MapBlock*> modified_blocks;
1912 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
1913 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks);
1915 catch(InvalidPositionException &e)
1918 for(core::map<v3s16, MapBlock * >::Iterator
1919 i = modified_blocks.getIterator();
1920 i.atEnd() == false; i++)
1922 v3s16 p = i.getNode()->getKey();
1923 addUpdateMeshTaskWithEdge(p);
1927 void Client::setPlayerControl(PlayerControl &control)
1929 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1930 LocalPlayer *player = m_env.getLocalPlayer();
1931 assert(player != NULL);
1932 player->control = control;
1935 void Client::selectPlayerItem(u16 item)
1937 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1938 m_playeritem = item;
1939 m_inventory_updated = true;
1940 sendPlayerItem(item);
1943 // Returns true if the inventory of the local player has been
1944 // updated from the server. If it is true, it is set to false.
1945 bool Client::getLocalInventoryUpdated()
1947 // m_inventory_updated is behind envlock
1948 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1949 bool updated = m_inventory_updated;
1950 m_inventory_updated = false;
1954 // Copies the inventory of the local player to parameter
1955 void Client::getLocalInventory(Inventory &dst)
1957 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1958 Player *player = m_env.getLocalPlayer();
1959 assert(player != NULL);
1960 dst = player->inventory;
1963 Inventory* Client::getInventory(const InventoryLocation &loc)
1966 case InventoryLocation::UNDEFINED:
1969 case InventoryLocation::CURRENT_PLAYER:
1971 Player *player = m_env.getLocalPlayer();
1972 assert(player != NULL);
1973 return &player->inventory;
1976 case InventoryLocation::PLAYER:
1978 Player *player = m_env.getPlayer(loc.name.c_str());
1981 return &player->inventory;
1984 case InventoryLocation::NODEMETA:
1986 NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p);
1989 return meta->getInventory();
1997 void Client::inventoryAction(InventoryAction *a)
2000 Send it to the server
2002 sendInventoryAction(a);
2005 Predict some local inventory changes
2007 a->clientApply(this, this);
2010 ClientActiveObject * Client::getSelectedActiveObject(
2012 v3f from_pos_f_on_map,
2013 core::line3d<f32> shootline_on_map
2016 core::array<DistanceSortedActiveObject> objects;
2018 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
2020 //infostream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
2023 // After this, the closest object is the first in the array.
2026 for(u32 i=0; i<objects.size(); i++)
2028 ClientActiveObject *obj = objects[i].obj;
2030 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
2031 if(selection_box == NULL)
2034 v3f pos = obj->getPosition();
2036 core::aabbox3d<f32> offsetted_box(
2037 selection_box->MinEdge + pos,
2038 selection_box->MaxEdge + pos
2041 if(offsetted_box.intersectsWithLine(shootline_on_map))
2043 //infostream<<"Returning selected object"<<std::endl;
2048 //infostream<<"No object selected; returning NULL."<<std::endl;
2052 void Client::printDebugInfo(std::ostream &os)
2054 //JMutexAutoLock lock1(m_fetchblock_mutex);
2055 /*JMutexAutoLock lock2(m_incoming_queue_mutex);
2057 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
2058 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
2059 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
2063 core::list<std::wstring> Client::getConnectedPlayerNames()
2065 core::list<Player*> players = m_env.getPlayers(true);
2066 core::list<std::wstring> playerNames;
2067 for(core::list<Player*>::Iterator
2068 i = players.begin();
2069 i != players.end(); i++)
2071 Player *player = *i;
2072 playerNames.push_back(narrow_to_wide(player->getName()));
2077 float Client::getAnimationTime()
2079 return m_animation_time;
2082 int Client::getCrackLevel()
2084 return m_crack_level;
2087 void Client::setCrack(int level, v3s16 pos)
2089 int old_crack_level = m_crack_level;
2090 v3s16 old_crack_pos = m_crack_pos;
2092 m_crack_level = level;
2095 if(old_crack_level >= 0 && (level < 0 || pos != old_crack_pos))
2098 addUpdateMeshTaskForNode(old_crack_pos, false, true);
2100 if(level >= 0 && (old_crack_level < 0 || pos != old_crack_pos))
2103 addUpdateMeshTaskForNode(pos, false, true);
2109 Player *player = m_env.getLocalPlayer();
2110 assert(player != NULL);
2114 bool Client::getChatMessage(std::wstring &message)
2116 if(m_chat_queue.size() == 0)
2118 message = m_chat_queue.pop_front();
2122 void Client::typeChatMessage(const std::wstring &message)
2124 // Discard empty line
2129 sendChatMessage(message);
2132 if (message[0] == L'/')
2134 m_chat_queue.push_back(
2135 (std::wstring)L"issued command: "+message);
2139 LocalPlayer *player = m_env.getLocalPlayer();
2140 assert(player != NULL);
2141 std::wstring name = narrow_to_wide(player->getName());
2142 m_chat_queue.push_back(
2143 (std::wstring)L"<"+name+L"> "+message);
2147 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
2149 /*infostream<<"Client::addUpdateMeshTask(): "
2150 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2151 <<" ack_to_server="<<ack_to_server
2152 <<" urgent="<<urgent
2155 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2160 Create a task to update the mesh of the block
2163 MeshMakeData *data = new MeshMakeData(this);
2166 //TimeTaker timer("data fill");
2168 // Debug: 1-6ms, avg=2ms
2170 data->setCrack(m_crack_level, m_crack_pos);
2171 data->setSmoothLighting(g_settings->getBool("smooth_lighting"));
2175 //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
2177 // Add task to queue
2178 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server, urgent);
2180 /*infostream<<"Mesh update input queue size is "
2181 <<m_mesh_update_thread.m_queue_in.size()
2185 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent)
2189 infostream<<"Client::addUpdateMeshTaskWithEdge(): "
2190 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2195 v3s16 p = blockpos + v3s16(0,0,0);
2196 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2197 addUpdateMeshTask(p, ack_to_server, urgent);
2199 catch(InvalidPositionException &e){}
2202 v3s16 p = blockpos + v3s16(-1,0,0);
2203 addUpdateMeshTask(p, false, urgent);
2205 catch(InvalidPositionException &e){}
2207 v3s16 p = blockpos + v3s16(0,-1,0);
2208 addUpdateMeshTask(p, false, urgent);
2210 catch(InvalidPositionException &e){}
2212 v3s16 p = blockpos + v3s16(0,0,-1);
2213 addUpdateMeshTask(p, false, urgent);
2215 catch(InvalidPositionException &e){}
2218 void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool urgent)
2222 infostream<<"Client::addUpdateMeshTaskForNode(): "
2223 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2227 v3s16 blockpos = getNodeBlockPos(nodepos);
2228 v3s16 blockpos_relative = blockpos * MAP_BLOCKSIZE;
2231 v3s16 p = blockpos + v3s16(0,0,0);
2232 addUpdateMeshTask(p, ack_to_server, urgent);
2234 catch(InvalidPositionException &e){}
2236 if(nodepos.X == blockpos_relative.X){
2238 v3s16 p = blockpos + v3s16(-1,0,0);
2239 addUpdateMeshTask(p, false, urgent);
2241 catch(InvalidPositionException &e){}
2243 if(nodepos.Y == blockpos_relative.Y){
2245 v3s16 p = blockpos + v3s16(0,-1,0);
2246 addUpdateMeshTask(p, false, urgent);
2248 catch(InvalidPositionException &e){}
2250 if(nodepos.Z == blockpos_relative.Z){
2252 v3s16 p = blockpos + v3s16(0,0,-1);
2253 addUpdateMeshTask(p, false, urgent);
2255 catch(InvalidPositionException &e){}
2259 ClientEvent Client::getClientEvent()
2261 if(m_client_event_queue.size() == 0)
2264 event.type = CE_NONE;
2267 return m_client_event_queue.pop_front();
2270 void Client::afterContentReceived()
2272 assert(m_itemdef_received);
2273 assert(m_nodedef_received);
2274 assert(m_textures_received);
2276 // Rebuild inherited images and recreate textures
2277 m_tsrc->rebuildImagesAndTextures();
2279 // Update texture atlas
2280 if(g_settings->getBool("enable_texture_atlas"))
2281 m_tsrc->buildMainAtlas(this);
2283 // Update node aliases
2284 m_nodedef->updateAliases(m_itemdef);
2286 // Update node textures
2287 m_nodedef->updateTextures(m_tsrc);
2289 // Update item textures and meshes
2290 m_itemdef->updateTexturesAndMeshes(this);
2292 // Start mesh update thread after setting up content definitions
2293 m_mesh_update_thread.Start();
2296 float Client::getRTT(void)
2299 return m_con.GetPeerAvgRTT(PEER_ID_SERVER);
2300 } catch(con::PeerNotFoundException &e){
2305 // IGameDef interface
2307 IItemDefManager* Client::getItemDefManager()
2311 INodeDefManager* Client::getNodeDefManager()
2315 ICraftDefManager* Client::getCraftDefManager()
2318 //return m_craftdef;
2320 ITextureSource* Client::getTextureSource()
2324 u16 Client::allocateUnknownNodeId(const std::string &name)
2326 errorstream<<"Client::allocateUnknownNodeId(): "
2327 <<"Client cannot allocate node IDs"<<std::endl;
2329 return CONTENT_IGNORE;
2331 ISoundManager* Client::getSoundManager()
2335 MtEventManager* Client::getEventManager()