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 while(m_mesh_update_thread.m_queue_out.size() > 0)
662 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_front();
663 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
666 //JMutexAutoLock lock(block->mesh_mutex);
668 // Delete the old mesh
669 if(block->mesh != NULL)
671 // TODO: Remove hardware buffers of meshbuffers of block->mesh
676 // Replace with the new mesh
677 block->mesh = r.mesh;
679 if(r.ack_block_to_server)
681 /*infostream<<"Client: ACK block ("<<r.p.X<<","<<r.p.Y
682 <<","<<r.p.Z<<")"<<std::endl;*/
693 u32 replysize = 2+1+6;
694 SharedBuffer<u8> reply(replysize);
695 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
697 writeV3S16(&reply[3], r.p);
699 m_con.Send(PEER_ID_SERVER, 1, reply, true);
705 If the server didn't update the inventory in a while, revert
706 the local inventory (so the player notices the lag problem
707 and knows something is wrong).
709 if(m_inventory_from_server)
711 float interval = 10.0;
712 float count_before = floor(m_inventory_from_server_age / interval);
714 m_inventory_from_server_age += dtime;
716 float count_after = floor(m_inventory_from_server_age / interval);
718 if(count_after != count_before)
720 // Do this every <interval> seconds after TOCLIENT_INVENTORY
721 // Reset the locally changed inventory to the authoritative inventory
722 Player *player = m_env.getLocalPlayer();
723 player->inventory = *m_inventory_from_server;
724 m_inventory_updated = true;
729 // Virtual methods from con::PeerHandler
730 void Client::peerAdded(con::Peer *peer)
732 infostream<<"Client::peerAdded(): peer->id="
733 <<peer->id<<std::endl;
735 void Client::deletingPeer(con::Peer *peer, bool timeout)
737 infostream<<"Client::deletingPeer(): "
738 "Server Peer is getting deleted "
739 <<"(timeout="<<timeout<<")"<<std::endl;
742 void Client::ReceiveAll()
744 DSTACK(__FUNCTION_NAME);
745 u32 start_ms = porting::getTimeMs();
748 // Limit time even if there would be huge amounts of data to
750 if(porting::getTimeMs() > start_ms + 100)
756 catch(con::NoIncomingDataException &e)
760 catch(con::InvalidIncomingDataException &e)
762 infostream<<"Client::ReceiveAll(): "
763 "InvalidIncomingDataException: what()="
764 <<e.what()<<std::endl;
769 void Client::Receive()
771 DSTACK(__FUNCTION_NAME);
772 SharedBuffer<u8> data;
776 //TimeTaker t1("con mutex and receive", m_device);
777 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
778 datasize = m_con.Receive(sender_peer_id, data);
780 //TimeTaker t1("ProcessData", m_device);
781 ProcessData(*data, datasize, sender_peer_id);
785 sender_peer_id given to this shall be quaranteed to be a valid peer
787 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
789 DSTACK(__FUNCTION_NAME);
791 // Ignore packets that don't even fit a command
794 m_packetcounter.add(60000);
798 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
800 //infostream<<"Client: received command="<<command<<std::endl;
801 m_packetcounter.add((u16)command);
804 If this check is removed, be sure to change the queue
805 system to know the ids
807 if(sender_peer_id != PEER_ID_SERVER)
809 infostream<<"Client::ProcessData(): Discarding data not "
810 "coming from server: peer_id="<<sender_peer_id
815 u8 ser_version = m_server_ser_ver;
817 //infostream<<"Client received command="<<(int)command<<std::endl;
819 if(command == TOCLIENT_INIT)
824 u8 deployed = data[2];
826 infostream<<"Client: TOCLIENT_INIT received with "
827 "deployed="<<((int)deployed&0xff)<<std::endl;
829 if(deployed < SER_FMT_VER_LOWEST
830 || deployed > SER_FMT_VER_HIGHEST)
832 infostream<<"Client: TOCLIENT_INIT: Server sent "
833 <<"unsupported ser_fmt_ver"<<std::endl;
837 m_server_ser_ver = deployed;
839 // Get player position
840 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
841 if(datasize >= 2+1+6)
842 playerpos_s16 = readV3S16(&data[2+1]);
843 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
846 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
848 // Set player position
849 Player *player = m_env.getLocalPlayer();
850 assert(player != NULL);
851 player->setPosition(playerpos_f);
854 if(datasize >= 2+1+6+8)
857 m_map_seed = readU64(&data[2+1+6]);
858 infostream<<"Client: received map seed: "<<m_map_seed<<std::endl;
863 SharedBuffer<u8> reply(replysize);
864 writeU16(&reply[0], TOSERVER_INIT2);
866 m_con.Send(PEER_ID_SERVER, 1, reply, true);
871 if(command == TOCLIENT_ACCESS_DENIED)
873 // The server didn't like our password. Note, this needs
874 // to be processed even if the serialisation format has
875 // not been agreed yet, the same as TOCLIENT_INIT.
876 m_access_denied = true;
877 m_access_denied_reason = L"Unknown";
880 std::string datastring((char*)&data[2], datasize-2);
881 std::istringstream is(datastring, std::ios_base::binary);
882 m_access_denied_reason = deSerializeWideString(is);
887 if(ser_version == SER_FMT_VER_INVALID)
889 infostream<<"Client: Server serialization"
890 " format invalid or not initialized."
891 " Skipping incoming command="<<command<<std::endl;
895 // Just here to avoid putting the two if's together when
896 // making some copypasta
899 if(command == TOCLIENT_REMOVENODE)
904 p.X = readS16(&data[2]);
905 p.Y = readS16(&data[4]);
906 p.Z = readS16(&data[6]);
908 //TimeTaker t1("TOCLIENT_REMOVENODE");
912 else if(command == TOCLIENT_ADDNODE)
914 if(datasize < 8 + MapNode::serializedLength(ser_version))
918 p.X = readS16(&data[2]);
919 p.Y = readS16(&data[4]);
920 p.Z = readS16(&data[6]);
922 //TimeTaker t1("TOCLIENT_ADDNODE");
925 n.deSerialize(&data[8], ser_version);
929 else if(command == TOCLIENT_BLOCKDATA)
931 // Ignore too small packet
936 p.X = readS16(&data[2]);
937 p.Y = readS16(&data[4]);
938 p.Z = readS16(&data[6]);
940 /*infostream<<"Client: Thread: BLOCKDATA for ("
941 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
942 /*infostream<<"Client: Thread: BLOCKDATA for ("
943 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
945 std::string datastring((char*)&data[8], datasize-8);
946 std::istringstream istr(datastring, std::ios_base::binary);
952 sector = m_env.getMap().emergeSector(p2d);
954 assert(sector->getPos() == p2d);
956 //TimeTaker timer("MapBlock deSerialize");
959 block = sector->getBlockNoCreateNoEx(p.Y);
963 Update an existing block
965 //infostream<<"Updating"<<std::endl;
966 block->deSerialize(istr, ser_version, false);
973 //infostream<<"Creating new"<<std::endl;
974 block = new MapBlock(&m_env.getMap(), p, this);
975 block->deSerialize(istr, ser_version, false);
976 sector->insertBlock(block);
990 u32 replysize = 2+1+6;
991 SharedBuffer<u8> reply(replysize);
992 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
994 writeV3S16(&reply[3], p);
996 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1000 Add it to mesh update queue and set it to be acknowledged after update.
1002 //infostream<<"Adding mesh update task for received block"<<std::endl;
1003 addUpdateMeshTaskWithEdge(p, true);
1005 else if(command == TOCLIENT_INVENTORY)
1010 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
1013 //TimeTaker t2("mutex locking", m_device);
1014 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1017 //TimeTaker t3("istringstream init", m_device);
1018 std::string datastring((char*)&data[2], datasize-2);
1019 std::istringstream is(datastring, std::ios_base::binary);
1022 //m_env.printPlayers(infostream);
1024 //TimeTaker t4("player get", m_device);
1025 Player *player = m_env.getLocalPlayer();
1026 assert(player != NULL);
1029 //TimeTaker t1("inventory.deSerialize()", m_device);
1030 player->inventory.deSerialize(is);
1033 m_inventory_updated = true;
1035 delete m_inventory_from_server;
1036 m_inventory_from_server = new Inventory(player->inventory);
1037 m_inventory_from_server_age = 0.0;
1039 //infostream<<"Client got player inventory:"<<std::endl;
1040 //player->inventory.print(infostream);
1043 else if(command == TOCLIENT_TIME_OF_DAY)
1048 u16 time_of_day = readU16(&data[2]);
1049 time_of_day = time_of_day % 24000;
1050 //infostream<<"Client: time_of_day="<<time_of_day<<std::endl;
1051 float time_speed = 0;
1052 if(datasize >= 2 + 2 + 4){
1053 time_speed = readF1000(&data[4]);
1055 // Old message; try to approximate speed of time by ourselves
1056 float time_of_day_f = (float)time_of_day / 24000.0;
1057 float tod_diff_f = 0;
1058 if(time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
1059 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0;
1061 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
1062 m_last_time_of_day_f = time_of_day_f;
1063 float time_diff = m_time_of_day_update_timer;
1064 m_time_of_day_update_timer = 0;
1065 if(m_time_of_day_set){
1066 time_speed = 3600.0*24.0 * tod_diff_f / time_diff;
1067 infostream<<"Client: Measured time_of_day speed (old format): "
1068 <<time_speed<<" tod_diff_f="<<tod_diff_f
1069 <<" time_diff="<<time_diff<<std::endl;
1073 // Update environment
1074 m_env.setTimeOfDay(time_of_day);
1075 m_env.setTimeOfDaySpeed(time_speed);
1076 m_time_of_day_set = true;
1078 u32 dr = m_env.getDayNightRatio();
1079 verbosestream<<"Client: time_of_day="<<time_of_day
1080 <<" time_speed="<<time_speed
1081 <<" dr="<<dr<<std::endl;
1083 else if(command == TOCLIENT_CHAT_MESSAGE)
1091 std::string datastring((char*)&data[2], datasize-2);
1092 std::istringstream is(datastring, std::ios_base::binary);
1095 is.read((char*)buf, 2);
1096 u16 len = readU16(buf);
1098 std::wstring message;
1099 for(u16 i=0; i<len; i++)
1101 is.read((char*)buf, 2);
1102 message += (wchar_t)readU16(buf);
1105 /*infostream<<"Client received chat message: "
1106 <<wide_to_narrow(message)<<std::endl;*/
1108 m_chat_queue.push_back(message);
1110 else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1112 //if(g_settings->getBool("enable_experimental"))
1116 u16 count of removed objects
1117 for all removed objects {
1120 u16 count of added objects
1121 for all added objects {
1124 u32 initialization data length
1125 string initialization data
1130 // Get all data except the command number
1131 std::string datastring((char*)&data[2], datasize-2);
1132 // Throw them in an istringstream
1133 std::istringstream is(datastring, std::ios_base::binary);
1137 // Read removed objects
1139 u16 removed_count = readU16((u8*)buf);
1140 for(u16 i=0; i<removed_count; i++)
1143 u16 id = readU16((u8*)buf);
1146 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1147 m_env.removeActiveObject(id);
1151 // Read added objects
1153 u16 added_count = readU16((u8*)buf);
1154 for(u16 i=0; i<added_count; i++)
1157 u16 id = readU16((u8*)buf);
1159 u8 type = readU8((u8*)buf);
1160 std::string data = deSerializeLongString(is);
1163 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1164 m_env.addActiveObject(id, type, data);
1169 else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1171 //if(g_settings->getBool("enable_experimental"))
1183 // Get all data except the command number
1184 std::string datastring((char*)&data[2], datasize-2);
1185 // Throw them in an istringstream
1186 std::istringstream is(datastring, std::ios_base::binary);
1188 while(is.eof() == false)
1192 u16 id = readU16((u8*)buf);
1196 u16 message_size = readU16((u8*)buf);
1197 std::string message;
1198 message.reserve(message_size);
1199 for(u16 i=0; i<message_size; i++)
1202 message.append(buf, 1);
1204 // Pass on to the environment
1206 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1207 m_env.processActiveObjectMessage(id, message);
1212 else if(command == TOCLIENT_HP)
1214 std::string datastring((char*)&data[2], datasize-2);
1215 std::istringstream is(datastring, std::ios_base::binary);
1216 Player *player = m_env.getLocalPlayer();
1217 assert(player != NULL);
1218 u8 oldhp = player->hp;
1224 // Add to ClientEvent queue
1226 event.type = CE_PLAYER_DAMAGE;
1227 event.player_damage.amount = oldhp - hp;
1228 m_client_event_queue.push_back(event);
1231 else if(command == TOCLIENT_MOVE_PLAYER)
1233 std::string datastring((char*)&data[2], datasize-2);
1234 std::istringstream is(datastring, std::ios_base::binary);
1235 Player *player = m_env.getLocalPlayer();
1236 assert(player != NULL);
1237 v3f pos = readV3F1000(is);
1238 f32 pitch = readF1000(is);
1239 f32 yaw = readF1000(is);
1240 player->setPosition(pos);
1241 /*player->setPitch(pitch);
1242 player->setYaw(yaw);*/
1244 infostream<<"Client got TOCLIENT_MOVE_PLAYER"
1245 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1251 Add to ClientEvent queue.
1252 This has to be sent to the main program because otherwise
1253 it would just force the pitch and yaw values to whatever
1254 the camera points to.
1257 event.type = CE_PLAYER_FORCE_MOVE;
1258 event.player_force_move.pitch = pitch;
1259 event.player_force_move.yaw = yaw;
1260 m_client_event_queue.push_back(event);
1262 // Ignore damage for a few seconds, so that the player doesn't
1263 // get damage from falling on ground
1264 m_ignore_damage_timer = 3.0;
1266 else if(command == TOCLIENT_PLAYERITEM)
1268 std::string datastring((char*)&data[2], datasize-2);
1269 std::istringstream is(datastring, std::ios_base::binary);
1271 u16 count = readU16(is);
1273 for (u16 i = 0; i < count; ++i) {
1274 u16 peer_id = readU16(is);
1275 Player *player = m_env.getPlayer(peer_id);
1279 infostream<<"Client: ignoring player item "
1280 << deSerializeString(is)
1281 << " for non-existing peer id " << peer_id
1284 } else if (player->isLocal()) {
1285 infostream<<"Client: ignoring player item "
1286 << deSerializeString(is)
1287 << " for local player" << std::endl;
1290 InventoryList *inv = player->inventory.getList("main");
1291 std::string itemstring(deSerializeString(is));
1293 item.deSerialize(itemstring, m_itemdef);
1294 inv->changeItem(0, item);
1295 if(itemstring.empty())
1297 infostream<<"Client: empty player item for peer "
1298 <<peer_id<<std::endl;
1302 infostream<<"Client: player item for peer "
1303 <<peer_id<<": "<<itemstring<<std::endl;
1308 else if(command == TOCLIENT_DEATHSCREEN)
1310 std::string datastring((char*)&data[2], datasize-2);
1311 std::istringstream is(datastring, std::ios_base::binary);
1313 bool set_camera_point_target = readU8(is);
1314 v3f camera_point_target = readV3F1000(is);
1317 event.type = CE_DEATHSCREEN;
1318 event.deathscreen.set_camera_point_target = set_camera_point_target;
1319 event.deathscreen.camera_point_target_x = camera_point_target.X;
1320 event.deathscreen.camera_point_target_y = camera_point_target.Y;
1321 event.deathscreen.camera_point_target_z = camera_point_target.Z;
1322 m_client_event_queue.push_back(event);
1324 else if(command == TOCLIENT_ANNOUNCE_TEXTURES)
1326 io::IFileSystem *irrfs = m_device->getFileSystem();
1327 video::IVideoDriver *vdrv = m_device->getVideoDriver();
1329 std::string datastring((char*)&data[2], datasize-2);
1330 std::istringstream is(datastring, std::ios_base::binary);
1332 // Mesh update thread must be stopped while
1333 // updating content definitions
1334 assert(!m_mesh_update_thread.IsRunning());
1336 int num_textures = readU16(is);
1338 core::list<TextureRequest> texture_requests;
1340 for(int i=0; i<num_textures; i++){
1342 bool texture_found = false;
1344 //read texture from cache
1345 std::string name = deSerializeString(is);
1346 std::string sha1_texture = deSerializeString(is);
1348 // if name contains illegal characters, ignore the texture
1349 if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
1350 errorstream<<"Client: ignoring illegal texture name "
1351 <<"sent by server: \""<<name<<"\""<<std::endl;
1355 std::string tpath = getTextureCacheDir() + DIR_DELIM + name;
1357 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
1360 if(fis.good() == false){
1361 infostream<<"Client::Texture not found in cache: "
1362 <<name << " expected it at: "<<tpath<<std::endl;
1366 std::ostringstream tmp_os(std::ios_base::binary);
1370 fis.read(buf, 1024);
1371 std::streamsize len = fis.gcount();
1372 tmp_os.write(buf, len);
1381 infostream<<"Client: Failed to read texture from cache\""
1382 <<name<<"\""<<std::endl;
1387 sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
1389 unsigned char *digest = sha1.getDigest();
1391 std::string digest_string = base64_encode(digest, 20);
1393 if (digest_string == sha1_texture) {
1394 // Silly irrlicht's const-incorrectness
1395 Buffer<char> data_rw(tmp_os.str().c_str(), tmp_os.str().size());
1397 // Create an irrlicht memory file
1398 io::IReadFile *rfile = irrfs->createMemoryReadFile(
1399 *data_rw, tmp_os.str().size(), "_tempreadfile");
1402 video::IImage *img = vdrv->createImageFromFile(rfile);
1404 infostream<<"Client: Cannot create image from data of "
1405 <<"received texture \""<<name<<"\""<<std::endl;
1409 m_tsrc->insertSourceImage(name, img);
1413 texture_found = true;
1417 infostream<<"Client::Texture cached sha1 hash not matching server hash: "
1418 <<name << ": server ->"<<sha1_texture <<" client -> "<<digest_string<<std::endl;
1425 //add texture request
1426 if (!texture_found) {
1427 infostream<<"Client: Adding texture to request list: \""
1428 <<name<<"\""<<std::endl;
1429 texture_requests.push_back(TextureRequest(name));
1435 event.type = CE_TEXTURES_UPDATED;
1436 m_client_event_queue.push_back(event);
1439 //send Texture request
1442 u16 number of textures requested
1448 std::ostringstream os(std::ios_base::binary);
1453 writeU16(buf, TOSERVER_REQUEST_TEXTURES);
1454 os.write((char*)buf, 2);
1456 writeU16(buf,texture_requests.size());
1457 os.write((char*)buf, 2);
1460 for(core::list<TextureRequest>::Iterator i = texture_requests.begin();
1461 i != texture_requests.end(); i++) {
1462 os<<serializeString(i->name);
1466 std::string s = os.str();
1467 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1469 Send(0, data, true);
1470 infostream<<"Client: Sending request list to server " <<std::endl;
1472 else if(command == TOCLIENT_TEXTURES)
1474 io::IFileSystem *irrfs = m_device->getFileSystem();
1475 video::IVideoDriver *vdrv = m_device->getVideoDriver();
1477 std::string datastring((char*)&data[2], datasize-2);
1478 std::istringstream is(datastring, std::ios_base::binary);
1480 // Mesh update thread must be stopped while
1481 // updating content definitions
1482 assert(!m_mesh_update_thread.IsRunning());
1486 u16 total number of texture bunches
1487 u16 index of this bunch
1488 u32 number of textures in this bunch
1496 int num_bunches = readU16(is);
1497 int bunch_i = readU16(is);
1498 m_texture_receive_progress = (float)bunch_i / (float)(num_bunches - 1);
1499 if(bunch_i == num_bunches - 1)
1500 m_textures_received = true;
1501 int num_textures = readU32(is);
1502 infostream<<"Client: Received textures: bunch "<<bunch_i<<"/"
1503 <<num_bunches<<" textures="<<num_textures
1504 <<" size="<<datasize<<std::endl;
1505 for(int i=0; i<num_textures; i++){
1506 std::string name = deSerializeString(is);
1507 std::string data = deSerializeLongString(is);
1509 // if name contains illegal characters, ignore the texture
1510 if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
1511 errorstream<<"Client: ignoring illegal texture name "
1512 <<"sent by server: \""<<name<<"\""<<std::endl;
1516 // Silly irrlicht's const-incorrectness
1517 Buffer<char> data_rw(data.c_str(), data.size());
1518 // Create an irrlicht memory file
1519 io::IReadFile *rfile = irrfs->createMemoryReadFile(
1520 *data_rw, data.size(), "_tempreadfile");
1523 video::IImage *img = vdrv->createImageFromFile(rfile);
1525 errorstream<<"Client: Cannot create image from data of "
1526 <<"received texture \""<<name<<"\""<<std::endl;
1531 fs::CreateAllDirs(getTextureCacheDir());
1533 std::string filename = getTextureCacheDir() + DIR_DELIM + name;
1534 std::ofstream outfile(filename.c_str(), std::ios_base::binary | std::ios_base::trunc);
1536 if (outfile.good()) {
1537 outfile.write(data.c_str(),data.length());
1541 errorstream<<"Client: Unable to open cached texture file "<< filename <<std::endl;
1544 m_tsrc->insertSourceImage(name, img);
1550 event.type = CE_TEXTURES_UPDATED;
1551 m_client_event_queue.push_back(event);
1553 else if(command == TOCLIENT_TOOLDEF)
1555 infostream<<"Client: WARNING: Ignoring TOCLIENT_TOOLDEF"<<std::endl;
1557 else if(command == TOCLIENT_NODEDEF)
1559 infostream<<"Client: Received node definitions: packet size: "
1560 <<datasize<<std::endl;
1562 // Mesh update thread must be stopped while
1563 // updating content definitions
1564 assert(!m_mesh_update_thread.IsRunning());
1566 // Decompress node definitions
1567 std::string datastring((char*)&data[2], datasize-2);
1568 std::istringstream is(datastring, std::ios_base::binary);
1569 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1570 std::ostringstream tmp_os;
1571 decompressZlib(tmp_is, tmp_os);
1573 // Deserialize node definitions
1574 std::istringstream tmp_is2(tmp_os.str());
1575 m_nodedef->deSerialize(tmp_is2);
1576 m_nodedef_received = true;
1578 else if(command == TOCLIENT_CRAFTITEMDEF)
1580 infostream<<"Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF"<<std::endl;
1582 else if(command == TOCLIENT_ITEMDEF)
1584 infostream<<"Client: Received item definitions: packet size: "
1585 <<datasize<<std::endl;
1587 // Mesh update thread must be stopped while
1588 // updating content definitions
1589 assert(!m_mesh_update_thread.IsRunning());
1591 // Decompress item definitions
1592 std::string datastring((char*)&data[2], datasize-2);
1593 std::istringstream is(datastring, std::ios_base::binary);
1594 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1595 std::ostringstream tmp_os;
1596 decompressZlib(tmp_is, tmp_os);
1598 // Deserialize node definitions
1599 std::istringstream tmp_is2(tmp_os.str());
1600 m_itemdef->deSerialize(tmp_is2);
1601 m_itemdef_received = true;
1605 infostream<<"Client: Ignoring unknown command "
1606 <<command<<std::endl;
1610 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
1612 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1613 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
1616 void Client::interact(u8 action, const PointedThing& pointed)
1618 if(connectedAndInitialized() == false){
1619 infostream<<"Client::interact() "
1620 "cancelled (not connected)"
1625 std::ostringstream os(std::ios_base::binary);
1631 [5] u32 length of the next item
1632 [9] serialized PointedThing
1634 0: start digging (from undersurface) or use
1635 1: stop digging (all parameters ignored)
1636 2: digging completed
1637 3: place block or item (to abovesurface)
1640 writeU16(os, TOSERVER_INTERACT);
1641 writeU8(os, action);
1642 writeU16(os, getPlayerItem());
1643 std::ostringstream tmp_os(std::ios::binary);
1644 pointed.serialize(tmp_os);
1645 os<<serializeLongString(tmp_os.str());
1647 std::string s = os.str();
1648 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1651 Send(0, data, true);
1654 void Client::sendSignNodeText(v3s16 p, std::string text)
1662 std::ostringstream os(std::ios_base::binary);
1666 writeU16(buf, TOSERVER_SIGNNODETEXT);
1667 os.write((char*)buf, 2);
1671 os.write((char*)buf, 6);
1673 u16 textlen = text.size();
1674 // Write text length
1675 writeS16(buf, textlen);
1676 os.write((char*)buf, 2);
1679 os.write((char*)text.c_str(), textlen);
1682 std::string s = os.str();
1683 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1685 Send(0, data, true);
1688 void Client::sendInventoryAction(InventoryAction *a)
1690 std::ostringstream os(std::ios_base::binary);
1694 writeU16(buf, TOSERVER_INVENTORY_ACTION);
1695 os.write((char*)buf, 2);
1700 std::string s = os.str();
1701 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1703 Send(0, data, true);
1706 void Client::sendChatMessage(const std::wstring &message)
1708 std::ostringstream os(std::ios_base::binary);
1712 writeU16(buf, TOSERVER_CHAT_MESSAGE);
1713 os.write((char*)buf, 2);
1716 writeU16(buf, message.size());
1717 os.write((char*)buf, 2);
1720 for(u32 i=0; i<message.size(); i++)
1724 os.write((char*)buf, 2);
1728 std::string s = os.str();
1729 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1731 Send(0, data, true);
1734 void Client::sendChangePassword(const std::wstring oldpassword,
1735 const std::wstring newpassword)
1737 Player *player = m_env.getLocalPlayer();
1741 std::string playername = player->getName();
1742 std::string oldpwd = translatePassword(playername, oldpassword);
1743 std::string newpwd = translatePassword(playername, newpassword);
1745 std::ostringstream os(std::ios_base::binary);
1746 u8 buf[2+PASSWORD_SIZE*2];
1748 [0] u16 TOSERVER_PASSWORD
1749 [2] u8[28] old password
1750 [30] u8[28] new password
1753 writeU16(buf, TOSERVER_PASSWORD);
1754 for(u32 i=0;i<PASSWORD_SIZE-1;i++)
1756 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
1757 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
1759 buf[2+PASSWORD_SIZE-1] = 0;
1760 buf[30+PASSWORD_SIZE-1] = 0;
1761 os.write((char*)buf, 2+PASSWORD_SIZE*2);
1764 std::string s = os.str();
1765 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1767 Send(0, data, true);
1771 void Client::sendDamage(u8 damage)
1773 DSTACK(__FUNCTION_NAME);
1774 std::ostringstream os(std::ios_base::binary);
1776 writeU16(os, TOSERVER_DAMAGE);
1777 writeU8(os, damage);
1780 std::string s = os.str();
1781 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1783 Send(0, data, true);
1786 void Client::sendRespawn()
1788 DSTACK(__FUNCTION_NAME);
1789 std::ostringstream os(std::ios_base::binary);
1791 writeU16(os, TOSERVER_RESPAWN);
1794 std::string s = os.str();
1795 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1797 Send(0, data, true);
1800 void Client::sendPlayerPos()
1802 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1804 Player *myplayer = m_env.getLocalPlayer();
1805 if(myplayer == NULL)
1810 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1811 our_peer_id = m_con.GetPeerID();
1814 // Set peer id if not set already
1815 if(myplayer->peer_id == PEER_ID_INEXISTENT)
1816 myplayer->peer_id = our_peer_id;
1817 // Check that an existing peer_id is the same as the connection's
1818 assert(myplayer->peer_id == our_peer_id);
1820 v3f pf = myplayer->getPosition();
1821 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
1822 v3f sf = myplayer->getSpeed();
1823 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
1824 s32 pitch = myplayer->getPitch() * 100;
1825 s32 yaw = myplayer->getYaw() * 100;
1830 [2] v3s32 position*100
1831 [2+12] v3s32 speed*100
1832 [2+12+12] s32 pitch*100
1833 [2+12+12+4] s32 yaw*100
1836 SharedBuffer<u8> data(2+12+12+4+4);
1837 writeU16(&data[0], TOSERVER_PLAYERPOS);
1838 writeV3S32(&data[2], position);
1839 writeV3S32(&data[2+12], speed);
1840 writeS32(&data[2+12+12], pitch);
1841 writeS32(&data[2+12+12+4], yaw);
1843 // Send as unreliable
1844 Send(0, data, false);
1847 void Client::sendPlayerItem(u16 item)
1849 Player *myplayer = m_env.getLocalPlayer();
1850 if(myplayer == NULL)
1853 u16 our_peer_id = m_con.GetPeerID();
1855 // Set peer id if not set already
1856 if(myplayer->peer_id == PEER_ID_INEXISTENT)
1857 myplayer->peer_id = our_peer_id;
1858 // Check that an existing peer_id is the same as the connection's
1859 assert(myplayer->peer_id == our_peer_id);
1861 SharedBuffer<u8> data(2+2);
1862 writeU16(&data[0], TOSERVER_PLAYERITEM);
1863 writeU16(&data[2], item);
1866 Send(0, data, true);
1869 void Client::removeNode(v3s16 p)
1871 core::map<v3s16, MapBlock*> modified_blocks;
1875 //TimeTaker t("removeNodeAndUpdate", m_device);
1876 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
1878 catch(InvalidPositionException &e)
1882 // add urgent task to update the modified node
1883 addUpdateMeshTaskForNode(p, false, true);
1885 for(core::map<v3s16, MapBlock * >::Iterator
1886 i = modified_blocks.getIterator();
1887 i.atEnd() == false; i++)
1889 v3s16 p = i.getNode()->getKey();
1890 addUpdateMeshTaskWithEdge(p);
1894 void Client::addNode(v3s16 p, MapNode n)
1896 TimeTaker timer1("Client::addNode()");
1898 core::map<v3s16, MapBlock*> modified_blocks;
1902 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
1903 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks);
1905 catch(InvalidPositionException &e)
1908 for(core::map<v3s16, MapBlock * >::Iterator
1909 i = modified_blocks.getIterator();
1910 i.atEnd() == false; i++)
1912 v3s16 p = i.getNode()->getKey();
1913 addUpdateMeshTaskWithEdge(p);
1917 void Client::setPlayerControl(PlayerControl &control)
1919 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1920 LocalPlayer *player = m_env.getLocalPlayer();
1921 assert(player != NULL);
1922 player->control = control;
1925 void Client::selectPlayerItem(u16 item)
1927 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1928 m_playeritem = item;
1929 m_inventory_updated = true;
1930 sendPlayerItem(item);
1933 // Returns true if the inventory of the local player has been
1934 // updated from the server. If it is true, it is set to false.
1935 bool Client::getLocalInventoryUpdated()
1937 // m_inventory_updated is behind envlock
1938 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1939 bool updated = m_inventory_updated;
1940 m_inventory_updated = false;
1944 // Copies the inventory of the local player to parameter
1945 void Client::getLocalInventory(Inventory &dst)
1947 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1948 Player *player = m_env.getLocalPlayer();
1949 assert(player != NULL);
1950 dst = player->inventory;
1953 Inventory* Client::getInventory(const InventoryLocation &loc)
1956 case InventoryLocation::UNDEFINED:
1959 case InventoryLocation::CURRENT_PLAYER:
1961 Player *player = m_env.getLocalPlayer();
1962 assert(player != NULL);
1963 return &player->inventory;
1966 case InventoryLocation::PLAYER:
1968 Player *player = m_env.getPlayer(loc.name.c_str());
1971 return &player->inventory;
1974 case InventoryLocation::NODEMETA:
1976 NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p);
1979 return meta->getInventory();
1987 void Client::inventoryAction(InventoryAction *a)
1990 Send it to the server
1992 sendInventoryAction(a);
1995 Predict some local inventory changes
1997 a->clientApply(this, this);
2000 ClientActiveObject * Client::getSelectedActiveObject(
2002 v3f from_pos_f_on_map,
2003 core::line3d<f32> shootline_on_map
2006 core::array<DistanceSortedActiveObject> objects;
2008 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
2010 //infostream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
2013 // After this, the closest object is the first in the array.
2016 for(u32 i=0; i<objects.size(); i++)
2018 ClientActiveObject *obj = objects[i].obj;
2020 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
2021 if(selection_box == NULL)
2024 v3f pos = obj->getPosition();
2026 core::aabbox3d<f32> offsetted_box(
2027 selection_box->MinEdge + pos,
2028 selection_box->MaxEdge + pos
2031 if(offsetted_box.intersectsWithLine(shootline_on_map))
2033 //infostream<<"Returning selected object"<<std::endl;
2038 //infostream<<"No object selected; returning NULL."<<std::endl;
2042 void Client::printDebugInfo(std::ostream &os)
2044 //JMutexAutoLock lock1(m_fetchblock_mutex);
2045 /*JMutexAutoLock lock2(m_incoming_queue_mutex);
2047 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
2048 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
2049 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
2053 core::list<std::wstring> Client::getConnectedPlayerNames()
2055 core::list<Player*> players = m_env.getPlayers(true);
2056 core::list<std::wstring> playerNames;
2057 for(core::list<Player*>::Iterator
2058 i = players.begin();
2059 i != players.end(); i++)
2061 Player *player = *i;
2062 playerNames.push_back(narrow_to_wide(player->getName()));
2067 float Client::getAnimationTime()
2069 return m_animation_time;
2072 int Client::getCrackLevel()
2074 return m_crack_level;
2077 void Client::setCrack(int level, v3s16 pos)
2079 int old_crack_level = m_crack_level;
2080 v3s16 old_crack_pos = m_crack_pos;
2082 m_crack_level = level;
2085 if(old_crack_level >= 0 && (level < 0 || pos != old_crack_pos))
2088 addUpdateMeshTaskForNode(old_crack_pos, false, true);
2090 if(level >= 0 && (old_crack_level < 0 || pos != old_crack_pos))
2093 addUpdateMeshTaskForNode(pos, false, true);
2099 Player *player = m_env.getLocalPlayer();
2100 assert(player != NULL);
2104 bool Client::getChatMessage(std::wstring &message)
2106 if(m_chat_queue.size() == 0)
2108 message = m_chat_queue.pop_front();
2112 void Client::typeChatMessage(const std::wstring &message)
2114 // Discard empty line
2119 sendChatMessage(message);
2122 if (message[0] == L'/')
2124 m_chat_queue.push_back(
2125 (std::wstring)L"issued command: "+message);
2129 LocalPlayer *player = m_env.getLocalPlayer();
2130 assert(player != NULL);
2131 std::wstring name = narrow_to_wide(player->getName());
2132 m_chat_queue.push_back(
2133 (std::wstring)L"<"+name+L"> "+message);
2137 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
2139 /*infostream<<"Client::addUpdateMeshTask(): "
2140 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2141 <<" ack_to_server="<<ack_to_server
2142 <<" urgent="<<urgent
2145 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2150 Create a task to update the mesh of the block
2153 MeshMakeData *data = new MeshMakeData(this);
2156 //TimeTaker timer("data fill");
2158 // Debug: 1-6ms, avg=2ms
2160 data->setCrack(m_crack_level, m_crack_pos);
2161 data->setSmoothLighting(g_settings->getBool("smooth_lighting"));
2165 //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
2167 // Add task to queue
2168 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server, urgent);
2170 /*infostream<<"Mesh update input queue size is "
2171 <<m_mesh_update_thread.m_queue_in.size()
2175 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent)
2179 infostream<<"Client::addUpdateMeshTaskWithEdge(): "
2180 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2185 v3s16 p = blockpos + v3s16(0,0,0);
2186 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2187 addUpdateMeshTask(p, ack_to_server, urgent);
2189 catch(InvalidPositionException &e){}
2192 v3s16 p = blockpos + v3s16(-1,0,0);
2193 addUpdateMeshTask(p, false, urgent);
2195 catch(InvalidPositionException &e){}
2197 v3s16 p = blockpos + v3s16(0,-1,0);
2198 addUpdateMeshTask(p, false, urgent);
2200 catch(InvalidPositionException &e){}
2202 v3s16 p = blockpos + v3s16(0,0,-1);
2203 addUpdateMeshTask(p, false, urgent);
2205 catch(InvalidPositionException &e){}
2208 void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool urgent)
2212 infostream<<"Client::addUpdateMeshTaskForNode(): "
2213 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2217 v3s16 blockpos = getNodeBlockPos(nodepos);
2218 v3s16 blockpos_relative = blockpos * MAP_BLOCKSIZE;
2221 v3s16 p = blockpos + v3s16(0,0,0);
2222 addUpdateMeshTask(p, ack_to_server, urgent);
2224 catch(InvalidPositionException &e){}
2226 if(nodepos.X == blockpos_relative.X){
2228 v3s16 p = blockpos + v3s16(-1,0,0);
2229 addUpdateMeshTask(p, false, urgent);
2231 catch(InvalidPositionException &e){}
2233 if(nodepos.Y == blockpos_relative.Y){
2235 v3s16 p = blockpos + v3s16(0,-1,0);
2236 addUpdateMeshTask(p, false, urgent);
2238 catch(InvalidPositionException &e){}
2240 if(nodepos.Z == blockpos_relative.Z){
2242 v3s16 p = blockpos + v3s16(0,0,-1);
2243 addUpdateMeshTask(p, false, urgent);
2245 catch(InvalidPositionException &e){}
2249 ClientEvent Client::getClientEvent()
2251 if(m_client_event_queue.size() == 0)
2254 event.type = CE_NONE;
2257 return m_client_event_queue.pop_front();
2260 void Client::afterContentReceived()
2262 assert(m_itemdef_received);
2263 assert(m_nodedef_received);
2264 assert(m_textures_received);
2266 // Rebuild inherited images and recreate textures
2267 m_tsrc->rebuildImagesAndTextures();
2269 // Update texture atlas
2270 if(g_settings->getBool("enable_texture_atlas"))
2271 m_tsrc->buildMainAtlas(this);
2273 // Update node aliases
2274 m_nodedef->updateAliases(m_itemdef);
2276 // Update node textures
2277 m_nodedef->updateTextures(m_tsrc);
2279 // Update item textures and meshes
2280 m_itemdef->updateTexturesAndMeshes(this);
2282 // Start mesh update thread after setting up content definitions
2283 m_mesh_update_thread.Start();
2286 float Client::getRTT(void)
2289 return m_con.GetPeerAvgRTT(PEER_ID_SERVER);
2290 } catch(con::PeerNotFoundException &e){
2295 // IGameDef interface
2297 IItemDefManager* Client::getItemDefManager()
2301 INodeDefManager* Client::getNodeDefManager()
2305 ICraftDefManager* Client::getCraftDefManager()
2308 //return m_craftdef;
2310 ITextureSource* Client::getTextureSource()
2314 u16 Client::allocateUnknownNodeId(const std::string &name)
2316 errorstream<<"Client::allocateUnknownNodeId(): "
2317 <<"Client cannot allocate node IDs"<<std::endl;
2319 return CONTENT_IGNORE;