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 "craftitemdef.h"
38 #include <IFileSystem.h>
42 static std::string getTextureCacheDir()
44 return porting::path_userdata + DIR_DELIM + "cache" + DIR_DELIM + "texture";
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 core::list<QueuedMeshUpdate*>::Iterator i;
87 for(i=m_queue.begin(); i!=m_queue.end(); i++)
89 QueuedMeshUpdate *q = *i;
95 peer_id=0 adds with nobody to send to
97 void MeshUpdateQueue::addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_server)
99 DSTACK(__FUNCTION_NAME);
103 JMutexAutoLock lock(m_mutex);
106 Find if block is already in queue.
107 If it is, update the data and quit.
109 core::list<QueuedMeshUpdate*>::Iterator i;
110 for(i=m_queue.begin(); i!=m_queue.end(); i++)
112 QueuedMeshUpdate *q = *i;
118 if(ack_block_to_server)
119 q->ack_block_to_server = true;
127 QueuedMeshUpdate *q = new QueuedMeshUpdate;
130 q->ack_block_to_server = ack_block_to_server;
131 m_queue.push_back(q);
134 // Returned pointer must be deleted
135 // Returns NULL if queue is empty
136 QueuedMeshUpdate * MeshUpdateQueue::pop()
138 JMutexAutoLock lock(m_mutex);
140 core::list<QueuedMeshUpdate*>::Iterator i = m_queue.begin();
141 if(i == m_queue.end())
143 QueuedMeshUpdate *q = *i;
152 void * MeshUpdateThread::Thread()
156 log_register_thread("MeshUpdateThread");
158 DSTACK(__FUNCTION_NAME);
160 BEGIN_DEBUG_EXCEPTION_HANDLER
164 /*// Wait for output queue to flush.
165 // Allow 2 in queue, this makes less frametime jitter.
166 // Umm actually, there is no much difference
167 if(m_queue_out.size() >= 2)
173 QueuedMeshUpdate *q = m_queue_in.pop();
180 ScopeProfiler sp(g_profiler, "Client: Mesh making");
182 scene::SMesh *mesh_new = NULL;
183 mesh_new = makeMapBlockMesh(q->data, m_gamedef);
188 r.ack_block_to_server = q->ack_block_to_server;
190 /*infostream<<"MeshUpdateThread: Processed "
191 <<"("<<q->p.X<<","<<q->p.Y<<","<<q->p.Z<<")"
194 m_queue_out.push_back(r);
199 END_DEBUG_EXCEPTION_HANDLER(errorstream)
205 IrrlichtDevice *device,
206 const char *playername,
207 std::string password,
208 MapDrawControl &control,
209 IWritableTextureSource *tsrc,
210 IWritableToolDefManager *tooldef,
211 IWritableNodeDefManager *nodedef,
212 IWritableCraftItemDefManager *craftitemdef
217 m_craftitemdef(craftitemdef),
218 m_mesh_update_thread(this),
220 new ClientMap(this, this, control,
221 device->getSceneManager()->getRootSceneNode(),
222 device->getSceneManager(), 666),
223 device->getSceneManager(),
226 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
228 m_server_ser_ver(SER_FMT_VER_INVALID),
230 m_inventory_updated(false),
233 m_password(password),
234 m_access_denied(false),
235 m_texture_receive_progress(0),
236 m_textures_received(false),
237 m_tooldef_received(false),
238 m_nodedef_received(false),
239 m_craftitemdef_received(false)
241 m_packetcounter_timer = 0.0;
242 //m_delete_unused_sectors_timer = 0.0;
243 m_connection_reinit_timer = 0.0;
244 m_avg_rtt_timer = 0.0;
245 m_playerpos_send_timer = 0.0;
246 m_ignore_damage_timer = 0.0;
248 // Build main texture atlas, now that the GameDef exists (that is, us)
249 if(g_settings->getBool("enable_texture_atlas"))
250 m_tsrc->buildMainAtlas(this);
252 infostream<<"Not building texture atlas."<<std::endl;
254 // Update node textures
255 m_nodedef->updateTextures(m_tsrc);
257 // Start threads after setting up content definitions
258 m_mesh_update_thread.Start();
264 Player *player = new LocalPlayer(this);
266 player->updateName(playername);
268 m_env.addPlayer(player);
270 // Initialize player in the inventory context
271 m_inventory_context.current_player = player;
278 //JMutexAutoLock conlock(m_con_mutex); //bulk comment-out
282 m_mesh_update_thread.setRun(false);
283 while(m_mesh_update_thread.IsRunning())
287 void Client::connect(Address address)
289 DSTACK(__FUNCTION_NAME);
290 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
291 m_con.SetTimeoutMs(0);
292 m_con.Connect(address);
295 bool Client::connectedAndInitialized()
297 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
299 if(m_con.Connected() == false)
302 if(m_server_ser_ver == SER_FMT_VER_INVALID)
308 void Client::step(float dtime)
310 DSTACK(__FUNCTION_NAME);
316 if(m_ignore_damage_timer > dtime)
317 m_ignore_damage_timer -= dtime;
319 m_ignore_damage_timer = 0.0;
321 //infostream<<"Client steps "<<dtime<<std::endl;
324 //TimeTaker timer("ReceiveAll()", m_device);
330 //TimeTaker timer("m_con_mutex + m_con.RunTimeouts()", m_device);
332 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
333 m_con.RunTimeouts(dtime);
340 float &counter = m_packetcounter_timer;
346 infostream<<"Client packetcounter (20s):"<<std::endl;
347 m_packetcounter.print(infostream);
348 m_packetcounter.clear();
352 // Get connection status
353 bool connected = connectedAndInitialized();
358 Delete unused sectors
360 NOTE: This jams the game for a while because deleting sectors
364 float &counter = m_delete_unused_sectors_timer;
372 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
374 core::list<v3s16> deleted_blocks;
376 float delete_unused_sectors_timeout =
377 g_settings->getFloat("client_delete_unused_sectors_timeout");
379 // Delete sector blocks
380 /*u32 num = m_env.getMap().unloadUnusedData
381 (delete_unused_sectors_timeout,
382 true, &deleted_blocks);*/
384 // Delete whole sectors
385 m_env.getMap().unloadUnusedData
386 (delete_unused_sectors_timeout,
389 if(deleted_blocks.size() > 0)
391 /*infostream<<"Client: Deleted blocks of "<<num
392 <<" unused sectors"<<std::endl;*/
393 /*infostream<<"Client: Deleted "<<num
394 <<" unused sectors"<<std::endl;*/
400 // Env is locked so con can be locked.
401 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
403 core::list<v3s16>::Iterator i = deleted_blocks.begin();
404 core::list<v3s16> sendlist;
407 if(sendlist.size() == 255 || i == deleted_blocks.end())
409 if(sendlist.size() == 0)
418 u32 replysize = 2+1+6*sendlist.size();
419 SharedBuffer<u8> reply(replysize);
420 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
421 reply[2] = sendlist.size();
423 for(core::list<v3s16>::Iterator
424 j = sendlist.begin();
425 j != sendlist.end(); j++)
427 writeV3S16(&reply[2+1+6*k], *j);
430 m_con.Send(PEER_ID_SERVER, 1, reply, true);
432 if(i == deleted_blocks.end())
438 sendlist.push_back(*i);
446 if(connected == false)
448 float &counter = m_connection_reinit_timer;
454 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
456 Player *myplayer = m_env.getLocalPlayer();
457 assert(myplayer != NULL);
459 // Send TOSERVER_INIT
460 // [0] u16 TOSERVER_INIT
461 // [2] u8 SER_FMT_VER_HIGHEST
462 // [3] u8[20] player_name
463 // [23] u8[28] password (new in some version)
464 // [51] u16 client network protocol version (new in some version)
465 SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2);
466 writeU16(&data[0], TOSERVER_INIT);
467 writeU8(&data[2], SER_FMT_VER_HIGHEST);
469 memset((char*)&data[3], 0, PLAYERNAME_SIZE);
470 snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
472 /*infostream<<"Client: sending initial password hash: \""<<m_password<<"\""
475 memset((char*)&data[23], 0, PASSWORD_SIZE);
476 snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
478 // This should be incremented in each version
479 writeU16(&data[51], PROTOCOL_VERSION);
481 // Send as unreliable
482 Send(0, data, false);
485 // Not connected, return
490 Do stuff if connected
494 Run Map's timers and unload unused data
496 const float map_timer_and_unload_dtime = 5.25;
497 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
499 ScopeProfiler sp(g_profiler, "Client: map timer and unload");
500 core::list<v3s16> deleted_blocks;
501 m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
502 g_settings->getFloat("client_unload_unused_data_timeout"),
505 /*if(deleted_blocks.size() > 0)
506 infostream<<"Client: Unloaded "<<deleted_blocks.size()
507 <<" unused blocks"<<std::endl;*/
511 NOTE: This loop is intentionally iterated the way it is.
514 core::list<v3s16>::Iterator i = deleted_blocks.begin();
515 core::list<v3s16> sendlist;
518 if(sendlist.size() == 255 || i == deleted_blocks.end())
520 if(sendlist.size() == 0)
529 u32 replysize = 2+1+6*sendlist.size();
530 SharedBuffer<u8> reply(replysize);
531 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
532 reply[2] = sendlist.size();
534 for(core::list<v3s16>::Iterator
535 j = sendlist.begin();
536 j != sendlist.end(); j++)
538 writeV3S16(&reply[2+1+6*k], *j);
541 m_con.Send(PEER_ID_SERVER, 1, reply, true);
543 if(i == deleted_blocks.end())
549 sendlist.push_back(*i);
559 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
561 // Control local player (0ms)
562 LocalPlayer *player = m_env.getLocalPlayer();
563 assert(player != NULL);
564 player->applyControl(dtime);
566 //TimeTaker envtimer("env step", m_device);
575 ClientEnvEvent event = m_env.getClientEvent();
576 if(event.type == CEE_NONE)
580 else if(event.type == CEE_PLAYER_DAMAGE)
582 if(m_ignore_damage_timer <= 0)
584 u8 damage = event.player_damage.amount;
586 if(event.player_damage.send_to_server)
589 // Add to ClientEvent queue
591 event.type = CE_PLAYER_DAMAGE;
592 event.player_damage.amount = damage;
593 m_client_event_queue.push_back(event);
603 float &counter = m_avg_rtt_timer;
608 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
609 // connectedAndInitialized() is true, peer exists.
610 float avg_rtt = m_con.GetPeerAvgRTT(PEER_ID_SERVER);
611 infostream<<"Client: avg_rtt="<<avg_rtt<<std::endl;
616 Send player position to server
619 float &counter = m_playerpos_send_timer;
629 Replace updated meshes
632 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
634 //TimeTaker timer("** Processing mesh update result queue");
637 /*infostream<<"Mesh update result queue size is "
638 <<m_mesh_update_thread.m_queue_out.size()
641 while(m_mesh_update_thread.m_queue_out.size() > 0)
643 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_front();
644 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
647 block->replaceMesh(r.mesh);
649 if(r.ack_block_to_server)
651 /*infostream<<"Client: ACK block ("<<r.p.X<<","<<r.p.Y
652 <<","<<r.p.Z<<")"<<std::endl;*/
663 u32 replysize = 2+1+6;
664 SharedBuffer<u8> reply(replysize);
665 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
667 writeV3S16(&reply[3], r.p);
669 m_con.Send(PEER_ID_SERVER, 1, reply, true);
675 // Virtual methods from con::PeerHandler
676 void Client::peerAdded(con::Peer *peer)
678 infostream<<"Client::peerAdded(): peer->id="
679 <<peer->id<<std::endl;
681 void Client::deletingPeer(con::Peer *peer, bool timeout)
683 infostream<<"Client::deletingPeer(): "
684 "Server Peer is getting deleted "
685 <<"(timeout="<<timeout<<")"<<std::endl;
688 void Client::ReceiveAll()
690 DSTACK(__FUNCTION_NAME);
691 u32 start_ms = porting::getTimeMs();
694 // Limit time even if there would be huge amounts of data to
696 if(porting::getTimeMs() > start_ms + 100)
702 catch(con::NoIncomingDataException &e)
706 catch(con::InvalidIncomingDataException &e)
708 infostream<<"Client::ReceiveAll(): "
709 "InvalidIncomingDataException: what()="
710 <<e.what()<<std::endl;
715 void Client::Receive()
717 DSTACK(__FUNCTION_NAME);
718 SharedBuffer<u8> data;
722 //TimeTaker t1("con mutex and receive", m_device);
723 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
724 datasize = m_con.Receive(sender_peer_id, data);
726 //TimeTaker t1("ProcessData", m_device);
727 ProcessData(*data, datasize, sender_peer_id);
731 sender_peer_id given to this shall be quaranteed to be a valid peer
733 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
735 DSTACK(__FUNCTION_NAME);
737 // Ignore packets that don't even fit a command
740 m_packetcounter.add(60000);
744 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
746 //infostream<<"Client: received command="<<command<<std::endl;
747 m_packetcounter.add((u16)command);
750 If this check is removed, be sure to change the queue
751 system to know the ids
753 if(sender_peer_id != PEER_ID_SERVER)
755 infostream<<"Client::ProcessData(): Discarding data not "
756 "coming from server: peer_id="<<sender_peer_id
761 u8 ser_version = m_server_ser_ver;
763 //infostream<<"Client received command="<<(int)command<<std::endl;
765 if(command == TOCLIENT_INIT)
770 u8 deployed = data[2];
772 infostream<<"Client: TOCLIENT_INIT received with "
773 "deployed="<<((int)deployed&0xff)<<std::endl;
775 if(deployed < SER_FMT_VER_LOWEST
776 || deployed > SER_FMT_VER_HIGHEST)
778 infostream<<"Client: TOCLIENT_INIT: Server sent "
779 <<"unsupported ser_fmt_ver"<<std::endl;
783 m_server_ser_ver = deployed;
785 // Get player position
786 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
787 if(datasize >= 2+1+6)
788 playerpos_s16 = readV3S16(&data[2+1]);
789 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
792 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
794 // Set player position
795 Player *player = m_env.getLocalPlayer();
796 assert(player != NULL);
797 player->setPosition(playerpos_f);
800 if(datasize >= 2+1+6+8)
803 m_map_seed = readU64(&data[2+1+6]);
804 infostream<<"Client: received map seed: "<<m_map_seed<<std::endl;
809 SharedBuffer<u8> reply(replysize);
810 writeU16(&reply[0], TOSERVER_INIT2);
812 m_con.Send(PEER_ID_SERVER, 1, reply, true);
817 if(command == TOCLIENT_ACCESS_DENIED)
819 // The server didn't like our password. Note, this needs
820 // to be processed even if the serialisation format has
821 // not been agreed yet, the same as TOCLIENT_INIT.
822 m_access_denied = true;
823 m_access_denied_reason = L"Unknown";
826 std::string datastring((char*)&data[2], datasize-2);
827 std::istringstream is(datastring, std::ios_base::binary);
828 m_access_denied_reason = deSerializeWideString(is);
833 if(ser_version == SER_FMT_VER_INVALID)
835 infostream<<"Client: Server serialization"
836 " format invalid or not initialized."
837 " Skipping incoming command="<<command<<std::endl;
841 // Just here to avoid putting the two if's together when
842 // making some copypasta
845 if(command == TOCLIENT_REMOVENODE)
850 p.X = readS16(&data[2]);
851 p.Y = readS16(&data[4]);
852 p.Z = readS16(&data[6]);
854 //TimeTaker t1("TOCLIENT_REMOVENODE");
856 // This will clear the cracking animation after digging
857 ((ClientMap&)m_env.getMap()).clearTempMod(p);
861 else if(command == TOCLIENT_ADDNODE)
863 if(datasize < 8 + MapNode::serializedLength(ser_version))
867 p.X = readS16(&data[2]);
868 p.Y = readS16(&data[4]);
869 p.Z = readS16(&data[6]);
871 //TimeTaker t1("TOCLIENT_ADDNODE");
874 n.deSerialize(&data[8], ser_version);
878 else if(command == TOCLIENT_BLOCKDATA)
880 // Ignore too small packet
885 p.X = readS16(&data[2]);
886 p.Y = readS16(&data[4]);
887 p.Z = readS16(&data[6]);
889 /*infostream<<"Client: Thread: BLOCKDATA for ("
890 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
891 /*infostream<<"Client: Thread: BLOCKDATA for ("
892 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
894 std::string datastring((char*)&data[8], datasize-8);
895 std::istringstream istr(datastring, std::ios_base::binary);
901 sector = m_env.getMap().emergeSector(p2d);
903 assert(sector->getPos() == p2d);
905 //TimeTaker timer("MapBlock deSerialize");
908 block = sector->getBlockNoCreateNoEx(p.Y);
912 Update an existing block
914 //infostream<<"Updating"<<std::endl;
915 block->deSerialize(istr, ser_version);
922 //infostream<<"Creating new"<<std::endl;
923 block = new MapBlock(&m_env.getMap(), p, this);
924 block->deSerialize(istr, ser_version);
925 sector->insertBlock(block);
939 u32 replysize = 2+1+6;
940 SharedBuffer<u8> reply(replysize);
941 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
943 writeV3S16(&reply[3], p);
945 m_con.Send(PEER_ID_SERVER, 1, reply, true);
949 Update Mesh of this block and blocks at x-, y- and z-.
950 Environment should not be locked as it interlocks with the
951 main thread, from which is will want to retrieve textures.
954 //m_env.getClientMap().updateMeshes(block->getPos(), getDayNightRatio());
956 Add it to mesh update queue and set it to be acknowledged after update.
958 //infostream<<"Adding mesh update task for received block"<<std::endl;
959 addUpdateMeshTaskWithEdge(p, true);
961 else if(command == TOCLIENT_INVENTORY)
966 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
969 //TimeTaker t2("mutex locking", m_device);
970 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
973 //TimeTaker t3("istringstream init", m_device);
974 std::string datastring((char*)&data[2], datasize-2);
975 std::istringstream is(datastring, std::ios_base::binary);
978 //m_env.printPlayers(infostream);
980 //TimeTaker t4("player get", m_device);
981 Player *player = m_env.getLocalPlayer();
982 assert(player != NULL);
985 //TimeTaker t1("inventory.deSerialize()", m_device);
986 player->inventory.deSerialize(is, this);
989 m_inventory_updated = true;
991 //infostream<<"Client got player inventory:"<<std::endl;
992 //player->inventory.print(infostream);
995 else if(command == TOCLIENT_TIME_OF_DAY)
1000 u16 time_of_day = readU16(&data[2]);
1001 time_of_day = time_of_day % 24000;
1002 //infostream<<"Client: time_of_day="<<time_of_day<<std::endl;
1010 m_env.setTimeOfDay(time_of_day);
1012 u32 dr = m_env.getDayNightRatio();
1014 infostream<<"Client: time_of_day="<<time_of_day
1020 else if(command == TOCLIENT_CHAT_MESSAGE)
1028 std::string datastring((char*)&data[2], datasize-2);
1029 std::istringstream is(datastring, std::ios_base::binary);
1032 is.read((char*)buf, 2);
1033 u16 len = readU16(buf);
1035 std::wstring message;
1036 for(u16 i=0; i<len; i++)
1038 is.read((char*)buf, 2);
1039 message += (wchar_t)readU16(buf);
1042 /*infostream<<"Client received chat message: "
1043 <<wide_to_narrow(message)<<std::endl;*/
1045 m_chat_queue.push_back(message);
1047 else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1049 //if(g_settings->getBool("enable_experimental"))
1053 u16 count of removed objects
1054 for all removed objects {
1057 u16 count of added objects
1058 for all added objects {
1061 u32 initialization data length
1062 string initialization data
1067 // Get all data except the command number
1068 std::string datastring((char*)&data[2], datasize-2);
1069 // Throw them in an istringstream
1070 std::istringstream is(datastring, std::ios_base::binary);
1074 // Read removed objects
1076 u16 removed_count = readU16((u8*)buf);
1077 for(u16 i=0; i<removed_count; i++)
1080 u16 id = readU16((u8*)buf);
1083 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1084 m_env.removeActiveObject(id);
1088 // Read added objects
1090 u16 added_count = readU16((u8*)buf);
1091 for(u16 i=0; i<added_count; i++)
1094 u16 id = readU16((u8*)buf);
1096 u8 type = readU8((u8*)buf);
1097 std::string data = deSerializeLongString(is);
1100 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1101 m_env.addActiveObject(id, type, data);
1106 else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1108 //if(g_settings->getBool("enable_experimental"))
1120 // Get all data except the command number
1121 std::string datastring((char*)&data[2], datasize-2);
1122 // Throw them in an istringstream
1123 std::istringstream is(datastring, std::ios_base::binary);
1125 while(is.eof() == false)
1129 u16 id = readU16((u8*)buf);
1133 u16 message_size = readU16((u8*)buf);
1134 std::string message;
1135 message.reserve(message_size);
1136 for(u16 i=0; i<message_size; i++)
1139 message.append(buf, 1);
1141 // Pass on to the environment
1143 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1144 m_env.processActiveObjectMessage(id, message);
1149 else if(command == TOCLIENT_HP)
1151 std::string datastring((char*)&data[2], datasize-2);
1152 std::istringstream is(datastring, std::ios_base::binary);
1153 Player *player = m_env.getLocalPlayer();
1154 assert(player != NULL);
1158 else if(command == TOCLIENT_MOVE_PLAYER)
1160 std::string datastring((char*)&data[2], datasize-2);
1161 std::istringstream is(datastring, std::ios_base::binary);
1162 Player *player = m_env.getLocalPlayer();
1163 assert(player != NULL);
1164 v3f pos = readV3F1000(is);
1165 f32 pitch = readF1000(is);
1166 f32 yaw = readF1000(is);
1167 player->setPosition(pos);
1168 /*player->setPitch(pitch);
1169 player->setYaw(yaw);*/
1171 infostream<<"Client got TOCLIENT_MOVE_PLAYER"
1172 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1178 Add to ClientEvent queue.
1179 This has to be sent to the main program because otherwise
1180 it would just force the pitch and yaw values to whatever
1181 the camera points to.
1184 event.type = CE_PLAYER_FORCE_MOVE;
1185 event.player_force_move.pitch = pitch;
1186 event.player_force_move.yaw = yaw;
1187 m_client_event_queue.push_back(event);
1189 // Ignore damage for a few seconds, so that the player doesn't
1190 // get damage from falling on ground
1191 m_ignore_damage_timer = 3.0;
1193 else if(command == TOCLIENT_PLAYERITEM)
1195 std::string datastring((char*)&data[2], datasize-2);
1196 std::istringstream is(datastring, std::ios_base::binary);
1198 u16 count = readU16(is);
1200 for (u16 i = 0; i < count; ++i) {
1201 u16 peer_id = readU16(is);
1202 Player *player = m_env.getPlayer(peer_id);
1206 infostream<<"Client: ignoring player item "
1207 << deSerializeString(is)
1208 << " for non-existing peer id " << peer_id
1211 } else if (player->isLocal()) {
1212 infostream<<"Client: ignoring player item "
1213 << deSerializeString(is)
1214 << " for local player" << std::endl;
1217 InventoryList *inv = player->inventory.getList("main");
1218 std::string itemstring(deSerializeString(is));
1219 if (itemstring.empty()) {
1222 <<"Client: empty player item for peer "
1223 << peer_id << std::endl;
1225 std::istringstream iss(itemstring);
1226 delete inv->changeItem(0,
1227 InventoryItem::deSerialize(iss, this));
1228 infostream<<"Client: player item for peer " << peer_id << ": ";
1229 player->getWieldItem()->serialize(infostream);
1230 infostream<<std::endl;
1235 else if(command == TOCLIENT_DEATHSCREEN)
1237 std::string datastring((char*)&data[2], datasize-2);
1238 std::istringstream is(datastring, std::ios_base::binary);
1240 bool set_camera_point_target = readU8(is);
1241 v3f camera_point_target = readV3F1000(is);
1244 event.type = CE_DEATHSCREEN;
1245 event.deathscreen.set_camera_point_target = set_camera_point_target;
1246 event.deathscreen.camera_point_target_x = camera_point_target.X;
1247 event.deathscreen.camera_point_target_y = camera_point_target.Y;
1248 event.deathscreen.camera_point_target_z = camera_point_target.Z;
1249 m_client_event_queue.push_back(event);
1251 else if(command == TOCLIENT_ANNOUNCE_TEXTURES)
1253 io::IFileSystem *irrfs = m_device->getFileSystem();
1254 video::IVideoDriver *vdrv = m_device->getVideoDriver();
1256 std::string datastring((char*)&data[2], datasize-2);
1257 std::istringstream is(datastring, std::ios_base::binary);
1260 // Stop threads while updating content definitions
1261 m_mesh_update_thread.setRun(false);
1262 // Process the remaining TextureSource queue to let MeshUpdateThread
1263 // get it's remaining textures and thus let it stop
1264 while(m_mesh_update_thread.IsRunning()){
1265 m_tsrc->processQueue();
1268 int num_textures = readU16(is);
1270 core::list<TextureRequest> texture_requests;
1272 for(int i=0; i<num_textures; i++){
1274 bool texture_found = false;
1276 //read texture from cache
1277 std::string name = deSerializeString(is);
1278 std::string sha1_texture = deSerializeString(is);
1280 std::string tpath = getTextureCacheDir() + DIR_DELIM + name;
1282 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
1285 if(fis.good() == false){
1286 infostream<<"Client::Texture not found in cache: "
1287 <<name << " expected it at: "<<tpath<<std::endl;
1291 std::ostringstream tmp_os(std::ios_base::binary);
1295 fis.read(buf, 1024);
1296 std::streamsize len = fis.gcount();
1297 tmp_os.write(buf, len);
1306 infostream<<"Client: Failed to read texture from cache\""
1307 <<name<<"\""<<std::endl;
1312 sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
1314 unsigned char *digest = sha1.getDigest();
1316 std::string digest_string = base64_encode(digest, 20);
1318 if (digest_string == sha1_texture) {
1319 // Silly irrlicht's const-incorrectness
1320 Buffer<char> data_rw(tmp_os.str().c_str(), tmp_os.str().size());
1322 // Create an irrlicht memory file
1323 io::IReadFile *rfile = irrfs->createMemoryReadFile(
1324 *data_rw, tmp_os.str().size(), "_tempreadfile");
1327 video::IImage *img = vdrv->createImageFromFile(rfile);
1329 infostream<<"Client: Cannot create image from data of "
1330 <<"received texture \""<<name<<"\""<<std::endl;
1334 m_tsrc->insertSourceImage(name, img);
1338 texture_found = true;
1342 infostream<<"Client::Texture cached sha1 hash not matching server hash: "
1343 <<name << ": server ->"<<sha1_texture <<" client -> "<<digest_string<<std::endl;
1350 //add texture request
1351 if (!texture_found) {
1352 infostream<<"Client: Adding texture to request list: \""
1353 <<name<<"\""<<std::endl;
1354 texture_requests.push_back(TextureRequest(name));
1359 m_mesh_update_thread.setRun(true);
1360 m_mesh_update_thread.Start();
1363 event.type = CE_TEXTURES_UPDATED;
1364 m_client_event_queue.push_back(event);
1367 //send Texture request
1370 u16 number of textures requested
1378 std::ostringstream os(std::ios_base::binary);
1383 writeU16(buf, TOSERVER_REQUEST_TEXTURES);
1384 os.write((char*)buf, 2);
1386 writeU16(buf,texture_requests.size());
1387 os.write((char*)buf, 2);
1390 for(core::list<TextureRequest>::Iterator i = texture_requests.begin();
1391 i != texture_requests.end(); i++) {
1392 os<<serializeString(i->name);
1396 std::string s = os.str();
1397 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1399 Send(0, data, true);
1400 infostream<<"Client: Sending request list to server " <<std::endl;
1402 else if(command == TOCLIENT_TEXTURES)
1404 io::IFileSystem *irrfs = m_device->getFileSystem();
1405 video::IVideoDriver *vdrv = m_device->getVideoDriver();
1407 std::string datastring((char*)&data[2], datasize-2);
1408 std::istringstream is(datastring, std::ios_base::binary);
1410 // Stop threads while updating content definitions
1411 m_mesh_update_thread.setRun(false);
1412 // Process the remaining TextureSource queue to let MeshUpdateThread
1413 // get it's remaining textures and thus let it stop
1414 while(m_mesh_update_thread.IsRunning()){
1415 m_tsrc->processQueue();
1420 u16 total number of texture bunches
1421 u16 index of this bunch
1422 u32 number of textures in this bunch
1430 int num_bunches = readU16(is);
1431 int bunch_i = readU16(is);
1432 m_texture_receive_progress = (float)bunch_i / (float)(num_bunches - 1);
1433 if(bunch_i == num_bunches - 1)
1434 m_textures_received = true;
1435 int num_textures = readU32(is);
1436 infostream<<"Client: Received textures: bunch "<<bunch_i<<"/"
1437 <<num_bunches<<" textures="<<num_textures
1438 <<" size="<<datasize<<std::endl;
1439 for(int i=0; i<num_textures; i++){
1440 std::string name = deSerializeString(is);
1441 std::string data = deSerializeLongString(is);
1442 // Silly irrlicht's const-incorrectness
1443 Buffer<char> data_rw(data.c_str(), data.size());
1444 // Create an irrlicht memory file
1445 io::IReadFile *rfile = irrfs->createMemoryReadFile(
1446 *data_rw, data.size(), "_tempreadfile");
1449 video::IImage *img = vdrv->createImageFromFile(rfile);
1451 errorstream<<"Client: Cannot create image from data of "
1452 <<"received texture \""<<name<<"\""<<std::endl;
1457 fs::CreateAllDirs(getTextureCacheDir());
1459 std::string filename = getTextureCacheDir() + DIR_DELIM + name;
1460 std::ofstream outfile(filename.c_str(), std::ios_base::binary | std::ios_base::trunc);
1462 if (outfile.good()) {
1463 outfile.write(data.c_str(),data.length());
1467 errorstream<<"Client: Unable to open cached texture file "<< filename <<std::endl;
1470 m_tsrc->insertSourceImage(name, img);
1475 if(m_nodedef_received && m_textures_received){
1476 // Rebuild inherited images and recreate textures
1477 m_tsrc->rebuildImagesAndTextures();
1479 // Update texture atlas
1480 if(g_settings->getBool("enable_texture_atlas"))
1481 m_tsrc->buildMainAtlas(this);
1483 // Update node textures
1484 m_nodedef->updateTextures(m_tsrc);
1488 m_mesh_update_thread.setRun(true);
1489 m_mesh_update_thread.Start();
1492 event.type = CE_TEXTURES_UPDATED;
1493 m_client_event_queue.push_back(event);
1495 else if(command == TOCLIENT_TOOLDEF)
1497 infostream<<"Client: Received tool definitions: packet size: "
1498 <<datasize<<std::endl;
1500 std::string datastring((char*)&data[2], datasize-2);
1501 std::istringstream is(datastring, std::ios_base::binary);
1503 m_tooldef_received = true;
1505 // Stop threads while updating content definitions
1506 m_mesh_update_thread.setRun(false);
1507 // Process the remaining TextureSource queue to let MeshUpdateThread
1508 // get it's remaining textures and thus let it stop
1509 while(m_mesh_update_thread.IsRunning()){
1510 m_tsrc->processQueue();
1513 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1514 m_tooldef->deSerialize(tmp_is);
1517 m_mesh_update_thread.setRun(true);
1518 m_mesh_update_thread.Start();
1520 else if(command == TOCLIENT_NODEDEF)
1522 infostream<<"Client: Received node definitions: packet size: "
1523 <<datasize<<std::endl;
1525 std::string datastring((char*)&data[2], datasize-2);
1526 std::istringstream is(datastring, std::ios_base::binary);
1528 m_nodedef_received = true;
1530 // Stop threads while updating content definitions
1531 m_mesh_update_thread.stop();
1533 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1534 m_nodedef->deSerialize(tmp_is, this);
1536 if(m_textures_received){
1537 // Update texture atlas
1538 if(g_settings->getBool("enable_texture_atlas"))
1539 m_tsrc->buildMainAtlas(this);
1541 // Update node textures
1542 m_nodedef->updateTextures(m_tsrc);
1546 m_mesh_update_thread.setRun(true);
1547 m_mesh_update_thread.Start();
1549 else if(command == TOCLIENT_CRAFTITEMDEF)
1551 infostream<<"Client: Received CraftItem definitions: packet size: "
1552 <<datasize<<std::endl;
1554 std::string datastring((char*)&data[2], datasize-2);
1555 std::istringstream is(datastring, std::ios_base::binary);
1557 m_craftitemdef_received = true;
1559 // Stop threads while updating content definitions
1560 m_mesh_update_thread.setRun(false);
1561 // Process the remaining TextureSource queue to let MeshUpdateThread
1562 // get it's remaining textures and thus let it stop
1563 while(m_mesh_update_thread.IsRunning()){
1564 m_tsrc->processQueue();
1567 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1568 m_craftitemdef->deSerialize(tmp_is);
1571 m_mesh_update_thread.setRun(true);
1572 m_mesh_update_thread.Start();
1576 infostream<<"Client: Ignoring unknown command "
1577 <<command<<std::endl;
1581 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
1583 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1584 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
1587 void Client::interact(u8 action, const PointedThing& pointed)
1589 if(connectedAndInitialized() == false){
1590 infostream<<"Client::interact() "
1591 "cancelled (not connected)"
1596 std::ostringstream os(std::ios_base::binary);
1602 [5] u32 length of the next item
1603 [9] serialized PointedThing
1605 0: start digging (from undersurface) or use
1606 1: stop digging (all parameters ignored)
1607 2: digging completed
1608 3: place block or item (to abovesurface)
1611 writeU16(os, TOSERVER_INTERACT);
1612 writeU8(os, action);
1613 writeU16(os, getPlayerItem());
1614 std::ostringstream tmp_os(std::ios::binary);
1615 pointed.serialize(tmp_os);
1616 os<<serializeLongString(tmp_os.str());
1618 std::string s = os.str();
1619 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1622 Send(0, data, true);
1625 void Client::sendSignNodeText(v3s16 p, std::string text)
1633 std::ostringstream os(std::ios_base::binary);
1637 writeU16(buf, TOSERVER_SIGNNODETEXT);
1638 os.write((char*)buf, 2);
1642 os.write((char*)buf, 6);
1644 u16 textlen = text.size();
1645 // Write text length
1646 writeS16(buf, textlen);
1647 os.write((char*)buf, 2);
1650 os.write((char*)text.c_str(), textlen);
1653 std::string s = os.str();
1654 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1656 Send(0, data, true);
1659 void Client::sendInventoryAction(InventoryAction *a)
1661 std::ostringstream os(std::ios_base::binary);
1665 writeU16(buf, TOSERVER_INVENTORY_ACTION);
1666 os.write((char*)buf, 2);
1671 std::string s = os.str();
1672 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1674 Send(0, data, true);
1677 void Client::sendChatMessage(const std::wstring &message)
1679 std::ostringstream os(std::ios_base::binary);
1683 writeU16(buf, TOSERVER_CHAT_MESSAGE);
1684 os.write((char*)buf, 2);
1687 writeU16(buf, message.size());
1688 os.write((char*)buf, 2);
1691 for(u32 i=0; i<message.size(); i++)
1695 os.write((char*)buf, 2);
1699 std::string s = os.str();
1700 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1702 Send(0, data, true);
1705 void Client::sendChangePassword(const std::wstring oldpassword,
1706 const std::wstring newpassword)
1708 Player *player = m_env.getLocalPlayer();
1712 std::string playername = player->getName();
1713 std::string oldpwd = translatePassword(playername, oldpassword);
1714 std::string newpwd = translatePassword(playername, newpassword);
1716 std::ostringstream os(std::ios_base::binary);
1717 u8 buf[2+PASSWORD_SIZE*2];
1719 [0] u16 TOSERVER_PASSWORD
1720 [2] u8[28] old password
1721 [30] u8[28] new password
1724 writeU16(buf, TOSERVER_PASSWORD);
1725 for(u32 i=0;i<PASSWORD_SIZE-1;i++)
1727 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
1728 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
1730 buf[2+PASSWORD_SIZE-1] = 0;
1731 buf[30+PASSWORD_SIZE-1] = 0;
1732 os.write((char*)buf, 2+PASSWORD_SIZE*2);
1735 std::string s = os.str();
1736 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1738 Send(0, data, true);
1742 void Client::sendDamage(u8 damage)
1744 DSTACK(__FUNCTION_NAME);
1745 std::ostringstream os(std::ios_base::binary);
1747 writeU16(os, TOSERVER_DAMAGE);
1748 writeU8(os, damage);
1751 std::string s = os.str();
1752 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1754 Send(0, data, true);
1757 void Client::sendRespawn()
1759 DSTACK(__FUNCTION_NAME);
1760 std::ostringstream os(std::ios_base::binary);
1762 writeU16(os, TOSERVER_RESPAWN);
1765 std::string s = os.str();
1766 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1768 Send(0, data, true);
1771 void Client::sendPlayerPos()
1773 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1775 Player *myplayer = m_env.getLocalPlayer();
1776 if(myplayer == NULL)
1781 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1782 our_peer_id = m_con.GetPeerID();
1785 // Set peer id if not set already
1786 if(myplayer->peer_id == PEER_ID_INEXISTENT)
1787 myplayer->peer_id = our_peer_id;
1788 // Check that an existing peer_id is the same as the connection's
1789 assert(myplayer->peer_id == our_peer_id);
1791 v3f pf = myplayer->getPosition();
1792 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
1793 v3f sf = myplayer->getSpeed();
1794 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
1795 s32 pitch = myplayer->getPitch() * 100;
1796 s32 yaw = myplayer->getYaw() * 100;
1801 [2] v3s32 position*100
1802 [2+12] v3s32 speed*100
1803 [2+12+12] s32 pitch*100
1804 [2+12+12+4] s32 yaw*100
1807 SharedBuffer<u8> data(2+12+12+4+4);
1808 writeU16(&data[0], TOSERVER_PLAYERPOS);
1809 writeV3S32(&data[2], position);
1810 writeV3S32(&data[2+12], speed);
1811 writeS32(&data[2+12+12], pitch);
1812 writeS32(&data[2+12+12+4], yaw);
1814 // Send as unreliable
1815 Send(0, data, false);
1818 void Client::sendPlayerItem(u16 item)
1820 Player *myplayer = m_env.getLocalPlayer();
1821 if(myplayer == NULL)
1824 u16 our_peer_id = m_con.GetPeerID();
1826 // Set peer id if not set already
1827 if(myplayer->peer_id == PEER_ID_INEXISTENT)
1828 myplayer->peer_id = our_peer_id;
1829 // Check that an existing peer_id is the same as the connection's
1830 assert(myplayer->peer_id == our_peer_id);
1832 SharedBuffer<u8> data(2+2);
1833 writeU16(&data[0], TOSERVER_PLAYERITEM);
1834 writeU16(&data[2], item);
1837 Send(0, data, true);
1840 void Client::removeNode(v3s16 p)
1842 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1844 core::map<v3s16, MapBlock*> modified_blocks;
1848 //TimeTaker t("removeNodeAndUpdate", m_device);
1849 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
1851 catch(InvalidPositionException &e)
1855 for(core::map<v3s16, MapBlock * >::Iterator
1856 i = modified_blocks.getIterator();
1857 i.atEnd() == false; i++)
1859 v3s16 p = i.getNode()->getKey();
1860 //m_env.getClientMap().updateMeshes(p, m_env.getDayNightRatio());
1861 addUpdateMeshTaskWithEdge(p);
1865 void Client::addNode(v3s16 p, MapNode n)
1867 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1869 TimeTaker timer1("Client::addNode()");
1871 core::map<v3s16, MapBlock*> modified_blocks;
1875 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
1876 std::string st = std::string("");
1877 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks, st);
1879 catch(InvalidPositionException &e)
1882 //TimeTaker timer2("Client::addNode(): updateMeshes");
1884 for(core::map<v3s16, MapBlock * >::Iterator
1885 i = modified_blocks.getIterator();
1886 i.atEnd() == false; i++)
1888 v3s16 p = i.getNode()->getKey();
1889 //m_env.getClientMap().updateMeshes(p, m_env.getDayNightRatio());
1890 addUpdateMeshTaskWithEdge(p);
1894 void Client::updateCamera(v3f pos, v3f dir, f32 fov)
1896 m_env.getClientMap().updateCamera(pos, dir, fov);
1899 void Client::renderPostFx()
1901 m_env.getClientMap().renderPostFx();
1904 MapNode Client::getNode(v3s16 p)
1906 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1907 return m_env.getMap().getNode(p);
1910 NodeMetadata* Client::getNodeMetadata(v3s16 p)
1912 return m_env.getMap().getNodeMetadata(p);
1915 LocalPlayer* Client::getLocalPlayer()
1917 return m_env.getLocalPlayer();
1920 void Client::setPlayerControl(PlayerControl &control)
1922 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1923 LocalPlayer *player = m_env.getLocalPlayer();
1924 assert(player != NULL);
1925 player->control = control;
1928 void Client::selectPlayerItem(u16 item)
1930 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1931 m_playeritem = item;
1932 m_inventory_updated = true;
1934 LocalPlayer *player = m_env.getLocalPlayer();
1935 assert(player != NULL);
1936 player->wieldItem(item);
1938 sendPlayerItem(item);
1941 // Returns true if the inventory of the local player has been
1942 // updated from the server. If it is true, it is set to false.
1943 bool Client::getLocalInventoryUpdated()
1945 // m_inventory_updated is behind envlock
1946 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1947 bool updated = m_inventory_updated;
1948 m_inventory_updated = false;
1952 // Copies the inventory of the local player to parameter
1953 void Client::getLocalInventory(Inventory &dst)
1955 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1956 Player *player = m_env.getLocalPlayer();
1957 assert(player != NULL);
1958 dst = player->inventory;
1961 InventoryContext *Client::getInventoryContext()
1963 return &m_inventory_context;
1966 Inventory* Client::getInventory(const InventoryLocation &loc)
1969 case InventoryLocation::UNDEFINED:
1972 case InventoryLocation::PLAYER:
1974 Player *player = m_env.getPlayer(loc.name.c_str());
1977 return &player->inventory;
1980 case InventoryLocation::NODEMETA:
1982 NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p);
1985 return meta->getInventory();
1994 Inventory* Client::getInventory(InventoryContext *c, std::string id)
1996 if(id == "current_player")
1998 assert(c->current_player);
1999 return &(c->current_player->inventory);
2003 std::string id0 = fn.next(":");
2005 if(id0 == "nodemeta")
2008 p.X = stoi(fn.next(","));
2009 p.Y = stoi(fn.next(","));
2010 p.Z = stoi(fn.next(","));
2011 NodeMetadata* meta = getNodeMetadata(p);
2013 return meta->getInventory();
2014 infostream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
2015 <<"no metadata found"<<std::endl;
2019 infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
2023 void Client::inventoryAction(InventoryAction *a)
2025 sendInventoryAction(a);
2028 ClientActiveObject * Client::getSelectedActiveObject(
2030 v3f from_pos_f_on_map,
2031 core::line3d<f32> shootline_on_map
2034 core::array<DistanceSortedActiveObject> objects;
2036 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
2038 //infostream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
2041 // After this, the closest object is the first in the array.
2044 for(u32 i=0; i<objects.size(); i++)
2046 ClientActiveObject *obj = objects[i].obj;
2048 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
2049 if(selection_box == NULL)
2052 v3f pos = obj->getPosition();
2054 core::aabbox3d<f32> offsetted_box(
2055 selection_box->MinEdge + pos,
2056 selection_box->MaxEdge + pos
2059 if(offsetted_box.intersectsWithLine(shootline_on_map))
2061 //infostream<<"Returning selected object"<<std::endl;
2066 //infostream<<"No object selected; returning NULL."<<std::endl;
2070 void Client::printDebugInfo(std::ostream &os)
2072 //JMutexAutoLock lock1(m_fetchblock_mutex);
2073 /*JMutexAutoLock lock2(m_incoming_queue_mutex);
2075 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
2076 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
2077 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
2081 u32 Client::getDayNightRatio()
2083 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2084 return m_env.getDayNightRatio();
2089 Player *player = m_env.getLocalPlayer();
2090 assert(player != NULL);
2094 void Client::setTempMod(v3s16 p, NodeMod mod)
2096 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2097 assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
2099 core::map<v3s16, MapBlock*> affected_blocks;
2100 ((ClientMap&)m_env.getMap()).setTempMod(p, mod,
2103 for(core::map<v3s16, MapBlock*>::Iterator
2104 i = affected_blocks.getIterator();
2105 i.atEnd() == false; i++)
2107 i.getNode()->getValue()->updateMesh(m_env.getDayNightRatio());
2111 void Client::clearTempMod(v3s16 p)
2113 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2114 assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
2116 core::map<v3s16, MapBlock*> affected_blocks;
2117 ((ClientMap&)m_env.getMap()).clearTempMod(p,
2120 for(core::map<v3s16, MapBlock*>::Iterator
2121 i = affected_blocks.getIterator();
2122 i.atEnd() == false; i++)
2124 i.getNode()->getValue()->updateMesh(m_env.getDayNightRatio());
2128 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server)
2130 /*infostream<<"Client::addUpdateMeshTask(): "
2131 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2134 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2139 Create a task to update the mesh of the block
2142 MeshMakeData *data = new MeshMakeData;
2145 //TimeTaker timer("data fill");
2147 // Debug: 1-6ms, avg=2ms
2148 data->fill(getDayNightRatio(), b);
2152 //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
2154 // Add task to queue
2155 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server);
2157 /*infostream<<"Mesh update input queue size is "
2158 <<m_mesh_update_thread.m_queue_in.size()
2162 // Temporary test: make mesh directly in here
2164 //TimeTaker timer("make mesh");
2166 scene::SMesh *mesh_new = NULL;
2167 mesh_new = makeMapBlockMesh(data);
2168 b->replaceMesh(mesh_new);
2174 Mark mesh as non-expired at this point so that it can already
2175 be marked as expired again if the data changes
2177 b->setMeshExpired(false);
2180 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server)
2184 infostream<<"Client::addUpdateMeshTaskWithEdge(): "
2185 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2190 v3s16 p = blockpos + v3s16(0,0,0);
2191 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2192 addUpdateMeshTask(p, ack_to_server);
2194 catch(InvalidPositionException &e){}
2197 v3s16 p = blockpos + v3s16(-1,0,0);
2198 addUpdateMeshTask(p);
2200 catch(InvalidPositionException &e){}
2202 v3s16 p = blockpos + v3s16(0,-1,0);
2203 addUpdateMeshTask(p);
2205 catch(InvalidPositionException &e){}
2207 v3s16 p = blockpos + v3s16(0,0,-1);
2208 addUpdateMeshTask(p);
2210 catch(InvalidPositionException &e){}
2213 ClientEvent Client::getClientEvent()
2215 if(m_client_event_queue.size() == 0)
2218 event.type = CE_NONE;
2221 return m_client_event_queue.pop_front();
2224 float Client::getRTT(void)
2227 return m_con.GetPeerAvgRTT(PEER_ID_SERVER);
2228 } catch(con::PeerNotFoundException &e){
2233 // IGameDef interface
2235 IToolDefManager* Client::getToolDefManager()
2239 INodeDefManager* Client::getNodeDefManager()
2243 ICraftDefManager* Client::getCraftDefManager()
2246 //return m_craftdef;
2248 ICraftItemDefManager* Client::getCraftItemDefManager()
2250 return m_craftitemdef;
2252 ITextureSource* Client::getTextureSource()
2256 u16 Client::allocateUnknownNodeId(const std::string &name)
2258 errorstream<<"Client::allocateUnknownNodeId(): "
2259 <<"Client cannot allocate node IDs"<<std::endl;
2261 return CONTENT_IGNORE;