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>
41 static std::string getTextureCacheDir()
43 return porting::path_userdata + DIR_DELIM + "cache" + DIR_DELIM + "texture";
50 TextureRequest(const std::string &name_=""):
59 QueuedMeshUpdate::QueuedMeshUpdate():
62 ack_block_to_server(false)
66 QueuedMeshUpdate::~QueuedMeshUpdate()
76 MeshUpdateQueue::MeshUpdateQueue()
81 MeshUpdateQueue::~MeshUpdateQueue()
83 JMutexAutoLock lock(m_mutex);
85 core::list<QueuedMeshUpdate*>::Iterator i;
86 for(i=m_queue.begin(); i!=m_queue.end(); i++)
88 QueuedMeshUpdate *q = *i;
94 peer_id=0 adds with nobody to send to
96 void MeshUpdateQueue::addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_server)
98 DSTACK(__FUNCTION_NAME);
102 JMutexAutoLock lock(m_mutex);
105 Find if block is already in queue.
106 If it is, update the data and quit.
108 core::list<QueuedMeshUpdate*>::Iterator i;
109 for(i=m_queue.begin(); i!=m_queue.end(); i++)
111 QueuedMeshUpdate *q = *i;
117 if(ack_block_to_server)
118 q->ack_block_to_server = true;
126 QueuedMeshUpdate *q = new QueuedMeshUpdate;
129 q->ack_block_to_server = ack_block_to_server;
130 m_queue.push_back(q);
133 // Returned pointer must be deleted
134 // Returns NULL if queue is empty
135 QueuedMeshUpdate * MeshUpdateQueue::pop()
137 JMutexAutoLock lock(m_mutex);
139 core::list<QueuedMeshUpdate*>::Iterator i = m_queue.begin();
140 if(i == m_queue.end())
142 QueuedMeshUpdate *q = *i;
151 void * MeshUpdateThread::Thread()
155 log_register_thread("MeshUpdateThread");
157 DSTACK(__FUNCTION_NAME);
159 BEGIN_DEBUG_EXCEPTION_HANDLER
163 /*// Wait for output queue to flush.
164 // Allow 2 in queue, this makes less frametime jitter.
165 // Umm actually, there is no much difference
166 if(m_queue_out.size() >= 2)
172 QueuedMeshUpdate *q = m_queue_in.pop();
179 ScopeProfiler sp(g_profiler, "Client: Mesh making");
181 scene::SMesh *mesh_new = NULL;
182 mesh_new = makeMapBlockMesh(q->data, m_gamedef);
187 r.ack_block_to_server = q->ack_block_to_server;
189 /*infostream<<"MeshUpdateThread: Processed "
190 <<"("<<q->p.X<<","<<q->p.Y<<","<<q->p.Z<<")"
193 m_queue_out.push_back(r);
198 END_DEBUG_EXCEPTION_HANDLER(errorstream)
204 IrrlichtDevice *device,
205 const char *playername,
206 std::string password,
207 MapDrawControl &control,
208 IWritableTextureSource *tsrc,
209 IWritableItemDefManager *itemdef,
210 IWritableNodeDefManager *nodedef
215 m_mesh_update_thread(this),
217 new ClientMap(this, this, control,
218 device->getSceneManager()->getRootSceneNode(),
219 device->getSceneManager(), 666),
220 device->getSceneManager(),
223 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
225 m_server_ser_ver(SER_FMT_VER_INVALID),
227 m_inventory_updated(false),
230 m_password(password),
231 m_access_denied(false),
232 m_texture_receive_progress(0),
233 m_textures_received(false),
234 m_itemdef_received(false),
235 m_nodedef_received(false)
237 m_packetcounter_timer = 0.0;
238 //m_delete_unused_sectors_timer = 0.0;
239 m_connection_reinit_timer = 0.0;
240 m_avg_rtt_timer = 0.0;
241 m_playerpos_send_timer = 0.0;
242 m_ignore_damage_timer = 0.0;
244 // Build main texture atlas, now that the GameDef exists (that is, us)
245 if(g_settings->getBool("enable_texture_atlas"))
246 m_tsrc->buildMainAtlas(this);
248 infostream<<"Not building texture atlas."<<std::endl;
254 Player *player = new LocalPlayer(this);
256 player->updateName(playername);
258 m_env.addPlayer(player);
265 //JMutexAutoLock conlock(m_con_mutex); //bulk comment-out
269 m_mesh_update_thread.setRun(false);
270 while(m_mesh_update_thread.IsRunning())
274 void Client::connect(Address address)
276 DSTACK(__FUNCTION_NAME);
277 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
278 m_con.SetTimeoutMs(0);
279 m_con.Connect(address);
282 bool Client::connectedAndInitialized()
284 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
286 if(m_con.Connected() == false)
289 if(m_server_ser_ver == SER_FMT_VER_INVALID)
295 void Client::step(float dtime)
297 DSTACK(__FUNCTION_NAME);
303 if(m_ignore_damage_timer > dtime)
304 m_ignore_damage_timer -= dtime;
306 m_ignore_damage_timer = 0.0;
308 //infostream<<"Client steps "<<dtime<<std::endl;
311 //TimeTaker timer("ReceiveAll()", m_device);
317 //TimeTaker timer("m_con_mutex + m_con.RunTimeouts()", m_device);
319 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
320 m_con.RunTimeouts(dtime);
327 float &counter = m_packetcounter_timer;
333 infostream<<"Client packetcounter (20s):"<<std::endl;
334 m_packetcounter.print(infostream);
335 m_packetcounter.clear();
339 // Get connection status
340 bool connected = connectedAndInitialized();
345 Delete unused sectors
347 NOTE: This jams the game for a while because deleting sectors
351 float &counter = m_delete_unused_sectors_timer;
359 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
361 core::list<v3s16> deleted_blocks;
363 float delete_unused_sectors_timeout =
364 g_settings->getFloat("client_delete_unused_sectors_timeout");
366 // Delete sector blocks
367 /*u32 num = m_env.getMap().unloadUnusedData
368 (delete_unused_sectors_timeout,
369 true, &deleted_blocks);*/
371 // Delete whole sectors
372 m_env.getMap().unloadUnusedData
373 (delete_unused_sectors_timeout,
376 if(deleted_blocks.size() > 0)
378 /*infostream<<"Client: Deleted blocks of "<<num
379 <<" unused sectors"<<std::endl;*/
380 /*infostream<<"Client: Deleted "<<num
381 <<" unused sectors"<<std::endl;*/
387 // Env is locked so con can be locked.
388 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
390 core::list<v3s16>::Iterator i = deleted_blocks.begin();
391 core::list<v3s16> sendlist;
394 if(sendlist.size() == 255 || i == deleted_blocks.end())
396 if(sendlist.size() == 0)
405 u32 replysize = 2+1+6*sendlist.size();
406 SharedBuffer<u8> reply(replysize);
407 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
408 reply[2] = sendlist.size();
410 for(core::list<v3s16>::Iterator
411 j = sendlist.begin();
412 j != sendlist.end(); j++)
414 writeV3S16(&reply[2+1+6*k], *j);
417 m_con.Send(PEER_ID_SERVER, 1, reply, true);
419 if(i == deleted_blocks.end())
425 sendlist.push_back(*i);
433 if(connected == false)
435 float &counter = m_connection_reinit_timer;
441 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
443 Player *myplayer = m_env.getLocalPlayer();
444 assert(myplayer != NULL);
446 // Send TOSERVER_INIT
447 // [0] u16 TOSERVER_INIT
448 // [2] u8 SER_FMT_VER_HIGHEST
449 // [3] u8[20] player_name
450 // [23] u8[28] password (new in some version)
451 // [51] u16 client network protocol version (new in some version)
452 SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2);
453 writeU16(&data[0], TOSERVER_INIT);
454 writeU8(&data[2], SER_FMT_VER_HIGHEST);
456 memset((char*)&data[3], 0, PLAYERNAME_SIZE);
457 snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
459 /*infostream<<"Client: sending initial password hash: \""<<m_password<<"\""
462 memset((char*)&data[23], 0, PASSWORD_SIZE);
463 snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
465 // This should be incremented in each version
466 writeU16(&data[51], PROTOCOL_VERSION);
468 // Send as unreliable
469 Send(0, data, false);
472 // Not connected, return
477 Do stuff if connected
481 Run Map's timers and unload unused data
483 const float map_timer_and_unload_dtime = 5.25;
484 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
486 ScopeProfiler sp(g_profiler, "Client: map timer and unload");
487 core::list<v3s16> deleted_blocks;
488 m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
489 g_settings->getFloat("client_unload_unused_data_timeout"),
492 /*if(deleted_blocks.size() > 0)
493 infostream<<"Client: Unloaded "<<deleted_blocks.size()
494 <<" unused blocks"<<std::endl;*/
498 NOTE: This loop is intentionally iterated the way it is.
501 core::list<v3s16>::Iterator i = deleted_blocks.begin();
502 core::list<v3s16> sendlist;
505 if(sendlist.size() == 255 || i == deleted_blocks.end())
507 if(sendlist.size() == 0)
516 u32 replysize = 2+1+6*sendlist.size();
517 SharedBuffer<u8> reply(replysize);
518 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
519 reply[2] = sendlist.size();
521 for(core::list<v3s16>::Iterator
522 j = sendlist.begin();
523 j != sendlist.end(); j++)
525 writeV3S16(&reply[2+1+6*k], *j);
528 m_con.Send(PEER_ID_SERVER, 1, reply, true);
530 if(i == deleted_blocks.end())
536 sendlist.push_back(*i);
546 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
548 // Control local player (0ms)
549 LocalPlayer *player = m_env.getLocalPlayer();
550 assert(player != NULL);
551 player->applyControl(dtime);
553 //TimeTaker envtimer("env step", m_device);
562 ClientEnvEvent event = m_env.getClientEvent();
563 if(event.type == CEE_NONE)
567 else if(event.type == CEE_PLAYER_DAMAGE)
569 if(m_ignore_damage_timer <= 0)
571 u8 damage = event.player_damage.amount;
573 if(event.player_damage.send_to_server)
576 // Add to ClientEvent queue
578 event.type = CE_PLAYER_DAMAGE;
579 event.player_damage.amount = damage;
580 m_client_event_queue.push_back(event);
590 float &counter = m_avg_rtt_timer;
595 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
596 // connectedAndInitialized() is true, peer exists.
597 float avg_rtt = m_con.GetPeerAvgRTT(PEER_ID_SERVER);
598 infostream<<"Client: avg_rtt="<<avg_rtt<<std::endl;
603 Send player position to server
606 float &counter = m_playerpos_send_timer;
616 Replace updated meshes
619 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
621 //TimeTaker timer("** Processing mesh update result queue");
624 /*infostream<<"Mesh update result queue size is "
625 <<m_mesh_update_thread.m_queue_out.size()
628 while(m_mesh_update_thread.m_queue_out.size() > 0)
630 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_front();
631 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
634 block->replaceMesh(r.mesh);
636 if(r.ack_block_to_server)
638 /*infostream<<"Client: ACK block ("<<r.p.X<<","<<r.p.Y
639 <<","<<r.p.Z<<")"<<std::endl;*/
650 u32 replysize = 2+1+6;
651 SharedBuffer<u8> reply(replysize);
652 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
654 writeV3S16(&reply[3], r.p);
656 m_con.Send(PEER_ID_SERVER, 1, reply, true);
662 // Virtual methods from con::PeerHandler
663 void Client::peerAdded(con::Peer *peer)
665 infostream<<"Client::peerAdded(): peer->id="
666 <<peer->id<<std::endl;
668 void Client::deletingPeer(con::Peer *peer, bool timeout)
670 infostream<<"Client::deletingPeer(): "
671 "Server Peer is getting deleted "
672 <<"(timeout="<<timeout<<")"<<std::endl;
675 void Client::ReceiveAll()
677 DSTACK(__FUNCTION_NAME);
678 u32 start_ms = porting::getTimeMs();
681 // Limit time even if there would be huge amounts of data to
683 if(porting::getTimeMs() > start_ms + 100)
689 catch(con::NoIncomingDataException &e)
693 catch(con::InvalidIncomingDataException &e)
695 infostream<<"Client::ReceiveAll(): "
696 "InvalidIncomingDataException: what()="
697 <<e.what()<<std::endl;
702 void Client::Receive()
704 DSTACK(__FUNCTION_NAME);
705 SharedBuffer<u8> data;
709 //TimeTaker t1("con mutex and receive", m_device);
710 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
711 datasize = m_con.Receive(sender_peer_id, data);
713 //TimeTaker t1("ProcessData", m_device);
714 ProcessData(*data, datasize, sender_peer_id);
718 sender_peer_id given to this shall be quaranteed to be a valid peer
720 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
722 DSTACK(__FUNCTION_NAME);
724 // Ignore packets that don't even fit a command
727 m_packetcounter.add(60000);
731 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
733 //infostream<<"Client: received command="<<command<<std::endl;
734 m_packetcounter.add((u16)command);
737 If this check is removed, be sure to change the queue
738 system to know the ids
740 if(sender_peer_id != PEER_ID_SERVER)
742 infostream<<"Client::ProcessData(): Discarding data not "
743 "coming from server: peer_id="<<sender_peer_id
748 u8 ser_version = m_server_ser_ver;
750 //infostream<<"Client received command="<<(int)command<<std::endl;
752 if(command == TOCLIENT_INIT)
757 u8 deployed = data[2];
759 infostream<<"Client: TOCLIENT_INIT received with "
760 "deployed="<<((int)deployed&0xff)<<std::endl;
762 if(deployed < SER_FMT_VER_LOWEST
763 || deployed > SER_FMT_VER_HIGHEST)
765 infostream<<"Client: TOCLIENT_INIT: Server sent "
766 <<"unsupported ser_fmt_ver"<<std::endl;
770 m_server_ser_ver = deployed;
772 // Get player position
773 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
774 if(datasize >= 2+1+6)
775 playerpos_s16 = readV3S16(&data[2+1]);
776 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
779 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
781 // Set player position
782 Player *player = m_env.getLocalPlayer();
783 assert(player != NULL);
784 player->setPosition(playerpos_f);
787 if(datasize >= 2+1+6+8)
790 m_map_seed = readU64(&data[2+1+6]);
791 infostream<<"Client: received map seed: "<<m_map_seed<<std::endl;
796 SharedBuffer<u8> reply(replysize);
797 writeU16(&reply[0], TOSERVER_INIT2);
799 m_con.Send(PEER_ID_SERVER, 1, reply, true);
804 if(command == TOCLIENT_ACCESS_DENIED)
806 // The server didn't like our password. Note, this needs
807 // to be processed even if the serialisation format has
808 // not been agreed yet, the same as TOCLIENT_INIT.
809 m_access_denied = true;
810 m_access_denied_reason = L"Unknown";
813 std::string datastring((char*)&data[2], datasize-2);
814 std::istringstream is(datastring, std::ios_base::binary);
815 m_access_denied_reason = deSerializeWideString(is);
820 if(ser_version == SER_FMT_VER_INVALID)
822 infostream<<"Client: Server serialization"
823 " format invalid or not initialized."
824 " Skipping incoming command="<<command<<std::endl;
828 // Just here to avoid putting the two if's together when
829 // making some copypasta
832 if(command == TOCLIENT_REMOVENODE)
837 p.X = readS16(&data[2]);
838 p.Y = readS16(&data[4]);
839 p.Z = readS16(&data[6]);
841 //TimeTaker t1("TOCLIENT_REMOVENODE");
843 // This will clear the cracking animation after digging
844 ((ClientMap&)m_env.getMap()).clearTempMod(p);
848 else if(command == TOCLIENT_ADDNODE)
850 if(datasize < 8 + MapNode::serializedLength(ser_version))
854 p.X = readS16(&data[2]);
855 p.Y = readS16(&data[4]);
856 p.Z = readS16(&data[6]);
858 //TimeTaker t1("TOCLIENT_ADDNODE");
861 n.deSerialize(&data[8], ser_version);
865 else if(command == TOCLIENT_BLOCKDATA)
867 // Ignore too small packet
872 p.X = readS16(&data[2]);
873 p.Y = readS16(&data[4]);
874 p.Z = readS16(&data[6]);
876 /*infostream<<"Client: Thread: BLOCKDATA for ("
877 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
878 /*infostream<<"Client: Thread: BLOCKDATA for ("
879 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
881 std::string datastring((char*)&data[8], datasize-8);
882 std::istringstream istr(datastring, std::ios_base::binary);
888 sector = m_env.getMap().emergeSector(p2d);
890 assert(sector->getPos() == p2d);
892 //TimeTaker timer("MapBlock deSerialize");
895 block = sector->getBlockNoCreateNoEx(p.Y);
899 Update an existing block
901 //infostream<<"Updating"<<std::endl;
902 block->deSerialize(istr, ser_version);
909 //infostream<<"Creating new"<<std::endl;
910 block = new MapBlock(&m_env.getMap(), p, this);
911 block->deSerialize(istr, ser_version);
912 sector->insertBlock(block);
926 u32 replysize = 2+1+6;
927 SharedBuffer<u8> reply(replysize);
928 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
930 writeV3S16(&reply[3], p);
932 m_con.Send(PEER_ID_SERVER, 1, reply, true);
936 Update Mesh of this block and blocks at x-, y- and z-.
937 Environment should not be locked as it interlocks with the
938 main thread, from which is will want to retrieve textures.
941 //m_env.getClientMap().updateMeshes(block->getPos(), getDayNightRatio());
943 Add it to mesh update queue and set it to be acknowledged after update.
945 //infostream<<"Adding mesh update task for received block"<<std::endl;
946 addUpdateMeshTaskWithEdge(p, true);
948 else if(command == TOCLIENT_INVENTORY)
953 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
956 //TimeTaker t2("mutex locking", m_device);
957 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
960 //TimeTaker t3("istringstream init", m_device);
961 std::string datastring((char*)&data[2], datasize-2);
962 std::istringstream is(datastring, std::ios_base::binary);
965 //m_env.printPlayers(infostream);
967 //TimeTaker t4("player get", m_device);
968 Player *player = m_env.getLocalPlayer();
969 assert(player != NULL);
972 //TimeTaker t1("inventory.deSerialize()", m_device);
973 player->inventory.deSerialize(is);
976 m_inventory_updated = true;
978 //infostream<<"Client got player inventory:"<<std::endl;
979 //player->inventory.print(infostream);
982 else if(command == TOCLIENT_TIME_OF_DAY)
987 u16 time_of_day = readU16(&data[2]);
988 time_of_day = time_of_day % 24000;
989 //infostream<<"Client: time_of_day="<<time_of_day<<std::endl;
997 m_env.setTimeOfDay(time_of_day);
999 u32 dr = m_env.getDayNightRatio();
1001 infostream<<"Client: time_of_day="<<time_of_day
1007 else if(command == TOCLIENT_CHAT_MESSAGE)
1015 std::string datastring((char*)&data[2], datasize-2);
1016 std::istringstream is(datastring, std::ios_base::binary);
1019 is.read((char*)buf, 2);
1020 u16 len = readU16(buf);
1022 std::wstring message;
1023 for(u16 i=0; i<len; i++)
1025 is.read((char*)buf, 2);
1026 message += (wchar_t)readU16(buf);
1029 /*infostream<<"Client received chat message: "
1030 <<wide_to_narrow(message)<<std::endl;*/
1032 m_chat_queue.push_back(message);
1034 else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1036 //if(g_settings->getBool("enable_experimental"))
1040 u16 count of removed objects
1041 for all removed objects {
1044 u16 count of added objects
1045 for all added objects {
1048 u32 initialization data length
1049 string initialization data
1054 // Get all data except the command number
1055 std::string datastring((char*)&data[2], datasize-2);
1056 // Throw them in an istringstream
1057 std::istringstream is(datastring, std::ios_base::binary);
1061 // Read removed objects
1063 u16 removed_count = readU16((u8*)buf);
1064 for(u16 i=0; i<removed_count; i++)
1067 u16 id = readU16((u8*)buf);
1070 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1071 m_env.removeActiveObject(id);
1075 // Read added objects
1077 u16 added_count = readU16((u8*)buf);
1078 for(u16 i=0; i<added_count; i++)
1081 u16 id = readU16((u8*)buf);
1083 u8 type = readU8((u8*)buf);
1084 std::string data = deSerializeLongString(is);
1087 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1088 m_env.addActiveObject(id, type, data);
1093 else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1095 //if(g_settings->getBool("enable_experimental"))
1107 // Get all data except the command number
1108 std::string datastring((char*)&data[2], datasize-2);
1109 // Throw them in an istringstream
1110 std::istringstream is(datastring, std::ios_base::binary);
1112 while(is.eof() == false)
1116 u16 id = readU16((u8*)buf);
1120 u16 message_size = readU16((u8*)buf);
1121 std::string message;
1122 message.reserve(message_size);
1123 for(u16 i=0; i<message_size; i++)
1126 message.append(buf, 1);
1128 // Pass on to the environment
1130 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1131 m_env.processActiveObjectMessage(id, message);
1136 else if(command == TOCLIENT_HP)
1138 std::string datastring((char*)&data[2], datasize-2);
1139 std::istringstream is(datastring, std::ios_base::binary);
1140 Player *player = m_env.getLocalPlayer();
1141 assert(player != NULL);
1145 else if(command == TOCLIENT_MOVE_PLAYER)
1147 std::string datastring((char*)&data[2], datasize-2);
1148 std::istringstream is(datastring, std::ios_base::binary);
1149 Player *player = m_env.getLocalPlayer();
1150 assert(player != NULL);
1151 v3f pos = readV3F1000(is);
1152 f32 pitch = readF1000(is);
1153 f32 yaw = readF1000(is);
1154 player->setPosition(pos);
1155 /*player->setPitch(pitch);
1156 player->setYaw(yaw);*/
1158 infostream<<"Client got TOCLIENT_MOVE_PLAYER"
1159 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1165 Add to ClientEvent queue.
1166 This has to be sent to the main program because otherwise
1167 it would just force the pitch and yaw values to whatever
1168 the camera points to.
1171 event.type = CE_PLAYER_FORCE_MOVE;
1172 event.player_force_move.pitch = pitch;
1173 event.player_force_move.yaw = yaw;
1174 m_client_event_queue.push_back(event);
1176 // Ignore damage for a few seconds, so that the player doesn't
1177 // get damage from falling on ground
1178 m_ignore_damage_timer = 3.0;
1180 else if(command == TOCLIENT_PLAYERITEM)
1182 std::string datastring((char*)&data[2], datasize-2);
1183 std::istringstream is(datastring, std::ios_base::binary);
1185 u16 count = readU16(is);
1187 for (u16 i = 0; i < count; ++i) {
1188 u16 peer_id = readU16(is);
1189 Player *player = m_env.getPlayer(peer_id);
1193 infostream<<"Client: ignoring player item "
1194 << deSerializeString(is)
1195 << " for non-existing peer id " << peer_id
1198 } else if (player->isLocal()) {
1199 infostream<<"Client: ignoring player item "
1200 << deSerializeString(is)
1201 << " for local player" << std::endl;
1204 InventoryList *inv = player->inventory.getList("main");
1205 std::string itemstring(deSerializeString(is));
1207 item.deSerialize(itemstring, m_itemdef);
1208 inv->changeItem(0, item);
1209 if(itemstring.empty())
1211 infostream<<"Client: empty player item for peer "
1212 <<peer_id<<std::endl;
1216 infostream<<"Client: player item for peer "
1217 <<peer_id<<": "<<itemstring<<std::endl;
1222 else if(command == TOCLIENT_DEATHSCREEN)
1224 std::string datastring((char*)&data[2], datasize-2);
1225 std::istringstream is(datastring, std::ios_base::binary);
1227 bool set_camera_point_target = readU8(is);
1228 v3f camera_point_target = readV3F1000(is);
1231 event.type = CE_DEATHSCREEN;
1232 event.deathscreen.set_camera_point_target = set_camera_point_target;
1233 event.deathscreen.camera_point_target_x = camera_point_target.X;
1234 event.deathscreen.camera_point_target_y = camera_point_target.Y;
1235 event.deathscreen.camera_point_target_z = camera_point_target.Z;
1236 m_client_event_queue.push_back(event);
1238 else if(command == TOCLIENT_ANNOUNCE_TEXTURES)
1240 io::IFileSystem *irrfs = m_device->getFileSystem();
1241 video::IVideoDriver *vdrv = m_device->getVideoDriver();
1243 std::string datastring((char*)&data[2], datasize-2);
1244 std::istringstream is(datastring, std::ios_base::binary);
1246 // Mesh update thread must be stopped while
1247 // updating content definitions
1248 assert(!m_mesh_update_thread.IsRunning());
1250 int num_textures = readU16(is);
1252 core::list<TextureRequest> texture_requests;
1254 for(int i=0; i<num_textures; i++){
1256 bool texture_found = false;
1258 //read texture from cache
1259 std::string name = deSerializeString(is);
1260 std::string sha1_texture = deSerializeString(is);
1262 // if name contains illegal characters, ignore the texture
1263 if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
1264 errorstream<<"Client: ignoring illegal texture name "
1265 <<"sent by server: \""<<name<<"\""<<std::endl;
1269 std::string tpath = getTextureCacheDir() + DIR_DELIM + name;
1271 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
1274 if(fis.good() == false){
1275 infostream<<"Client::Texture not found in cache: "
1276 <<name << " expected it at: "<<tpath<<std::endl;
1280 std::ostringstream tmp_os(std::ios_base::binary);
1284 fis.read(buf, 1024);
1285 std::streamsize len = fis.gcount();
1286 tmp_os.write(buf, len);
1295 infostream<<"Client: Failed to read texture from cache\""
1296 <<name<<"\""<<std::endl;
1301 sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
1303 unsigned char *digest = sha1.getDigest();
1305 std::string digest_string = base64_encode(digest, 20);
1307 if (digest_string == sha1_texture) {
1308 // Silly irrlicht's const-incorrectness
1309 Buffer<char> data_rw(tmp_os.str().c_str(), tmp_os.str().size());
1311 // Create an irrlicht memory file
1312 io::IReadFile *rfile = irrfs->createMemoryReadFile(
1313 *data_rw, tmp_os.str().size(), "_tempreadfile");
1316 video::IImage *img = vdrv->createImageFromFile(rfile);
1318 infostream<<"Client: Cannot create image from data of "
1319 <<"received texture \""<<name<<"\""<<std::endl;
1323 m_tsrc->insertSourceImage(name, img);
1327 texture_found = true;
1331 infostream<<"Client::Texture cached sha1 hash not matching server hash: "
1332 <<name << ": server ->"<<sha1_texture <<" client -> "<<digest_string<<std::endl;
1339 //add texture request
1340 if (!texture_found) {
1341 infostream<<"Client: Adding texture to request list: \""
1342 <<name<<"\""<<std::endl;
1343 texture_requests.push_back(TextureRequest(name));
1349 event.type = CE_TEXTURES_UPDATED;
1350 m_client_event_queue.push_back(event);
1353 //send Texture request
1356 u16 number of textures requested
1362 std::ostringstream os(std::ios_base::binary);
1367 writeU16(buf, TOSERVER_REQUEST_TEXTURES);
1368 os.write((char*)buf, 2);
1370 writeU16(buf,texture_requests.size());
1371 os.write((char*)buf, 2);
1374 for(core::list<TextureRequest>::Iterator i = texture_requests.begin();
1375 i != texture_requests.end(); i++) {
1376 os<<serializeString(i->name);
1380 std::string s = os.str();
1381 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1383 Send(0, data, true);
1384 infostream<<"Client: Sending request list to server " <<std::endl;
1386 else if(command == TOCLIENT_TEXTURES)
1388 io::IFileSystem *irrfs = m_device->getFileSystem();
1389 video::IVideoDriver *vdrv = m_device->getVideoDriver();
1391 std::string datastring((char*)&data[2], datasize-2);
1392 std::istringstream is(datastring, std::ios_base::binary);
1394 // Mesh update thread must be stopped while
1395 // updating content definitions
1396 assert(!m_mesh_update_thread.IsRunning());
1400 u16 total number of texture bunches
1401 u16 index of this bunch
1402 u32 number of textures in this bunch
1410 int num_bunches = readU16(is);
1411 int bunch_i = readU16(is);
1412 m_texture_receive_progress = (float)bunch_i / (float)(num_bunches - 1);
1413 if(bunch_i == num_bunches - 1)
1414 m_textures_received = true;
1415 int num_textures = readU32(is);
1416 infostream<<"Client: Received textures: bunch "<<bunch_i<<"/"
1417 <<num_bunches<<" textures="<<num_textures
1418 <<" size="<<datasize<<std::endl;
1419 for(int i=0; i<num_textures; i++){
1420 std::string name = deSerializeString(is);
1421 std::string data = deSerializeLongString(is);
1423 // if name contains illegal characters, ignore the texture
1424 if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
1425 errorstream<<"Client: ignoring illegal texture name "
1426 <<"sent by server: \""<<name<<"\""<<std::endl;
1430 // Silly irrlicht's const-incorrectness
1431 Buffer<char> data_rw(data.c_str(), data.size());
1432 // Create an irrlicht memory file
1433 io::IReadFile *rfile = irrfs->createMemoryReadFile(
1434 *data_rw, data.size(), "_tempreadfile");
1437 video::IImage *img = vdrv->createImageFromFile(rfile);
1439 errorstream<<"Client: Cannot create image from data of "
1440 <<"received texture \""<<name<<"\""<<std::endl;
1445 fs::CreateAllDirs(getTextureCacheDir());
1447 std::string filename = getTextureCacheDir() + DIR_DELIM + name;
1448 std::ofstream outfile(filename.c_str(), std::ios_base::binary | std::ios_base::trunc);
1450 if (outfile.good()) {
1451 outfile.write(data.c_str(),data.length());
1455 errorstream<<"Client: Unable to open cached texture file "<< filename <<std::endl;
1458 m_tsrc->insertSourceImage(name, img);
1464 event.type = CE_TEXTURES_UPDATED;
1465 m_client_event_queue.push_back(event);
1467 else if(command == TOCLIENT_TOOLDEF)
1469 infostream<<"Client: WARNING: Ignoring TOCLIENT_TOOLDEF"<<std::endl;
1471 else if(command == TOCLIENT_NODEDEF)
1473 infostream<<"Client: Received node definitions: packet size: "
1474 <<datasize<<std::endl;
1476 // Mesh update thread must be stopped while
1477 // updating content definitions
1478 assert(!m_mesh_update_thread.IsRunning());
1480 // Decompress node definitions
1481 std::string datastring((char*)&data[2], datasize-2);
1482 std::istringstream is(datastring, std::ios_base::binary);
1483 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1484 std::ostringstream tmp_os;
1485 decompressZlib(tmp_is, tmp_os);
1487 // Deserialize node definitions
1488 std::istringstream tmp_is2(tmp_os.str());
1489 m_nodedef->deSerialize(tmp_is2);
1490 m_nodedef_received = true;
1492 else if(command == TOCLIENT_CRAFTITEMDEF)
1494 infostream<<"Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF"<<std::endl;
1496 else if(command == TOCLIENT_ITEMDEF)
1498 infostream<<"Client: Received item definitions: packet size: "
1499 <<datasize<<std::endl;
1501 // Mesh update thread must be stopped while
1502 // updating content definitions
1503 assert(!m_mesh_update_thread.IsRunning());
1505 // Decompress item definitions
1506 std::string datastring((char*)&data[2], datasize-2);
1507 std::istringstream is(datastring, std::ios_base::binary);
1508 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1509 std::ostringstream tmp_os;
1510 decompressZlib(tmp_is, tmp_os);
1512 // Deserialize node definitions
1513 std::istringstream tmp_is2(tmp_os.str());
1514 m_itemdef->deSerialize(tmp_is2);
1515 m_itemdef_received = true;
1519 infostream<<"Client: Ignoring unknown command "
1520 <<command<<std::endl;
1524 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
1526 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1527 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
1530 void Client::interact(u8 action, const PointedThing& pointed)
1532 if(connectedAndInitialized() == false){
1533 infostream<<"Client::interact() "
1534 "cancelled (not connected)"
1539 std::ostringstream os(std::ios_base::binary);
1545 [5] u32 length of the next item
1546 [9] serialized PointedThing
1548 0: start digging (from undersurface) or use
1549 1: stop digging (all parameters ignored)
1550 2: digging completed
1551 3: place block or item (to abovesurface)
1554 writeU16(os, TOSERVER_INTERACT);
1555 writeU8(os, action);
1556 writeU16(os, getPlayerItem());
1557 std::ostringstream tmp_os(std::ios::binary);
1558 pointed.serialize(tmp_os);
1559 os<<serializeLongString(tmp_os.str());
1561 std::string s = os.str();
1562 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1565 Send(0, data, true);
1568 void Client::sendSignNodeText(v3s16 p, std::string text)
1576 std::ostringstream os(std::ios_base::binary);
1580 writeU16(buf, TOSERVER_SIGNNODETEXT);
1581 os.write((char*)buf, 2);
1585 os.write((char*)buf, 6);
1587 u16 textlen = text.size();
1588 // Write text length
1589 writeS16(buf, textlen);
1590 os.write((char*)buf, 2);
1593 os.write((char*)text.c_str(), textlen);
1596 std::string s = os.str();
1597 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1599 Send(0, data, true);
1602 void Client::sendInventoryAction(InventoryAction *a)
1604 std::ostringstream os(std::ios_base::binary);
1608 writeU16(buf, TOSERVER_INVENTORY_ACTION);
1609 os.write((char*)buf, 2);
1614 std::string s = os.str();
1615 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1617 Send(0, data, true);
1620 void Client::sendChatMessage(const std::wstring &message)
1622 std::ostringstream os(std::ios_base::binary);
1626 writeU16(buf, TOSERVER_CHAT_MESSAGE);
1627 os.write((char*)buf, 2);
1630 writeU16(buf, message.size());
1631 os.write((char*)buf, 2);
1634 for(u32 i=0; i<message.size(); i++)
1638 os.write((char*)buf, 2);
1642 std::string s = os.str();
1643 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1645 Send(0, data, true);
1648 void Client::sendChangePassword(const std::wstring oldpassword,
1649 const std::wstring newpassword)
1651 Player *player = m_env.getLocalPlayer();
1655 std::string playername = player->getName();
1656 std::string oldpwd = translatePassword(playername, oldpassword);
1657 std::string newpwd = translatePassword(playername, newpassword);
1659 std::ostringstream os(std::ios_base::binary);
1660 u8 buf[2+PASSWORD_SIZE*2];
1662 [0] u16 TOSERVER_PASSWORD
1663 [2] u8[28] old password
1664 [30] u8[28] new password
1667 writeU16(buf, TOSERVER_PASSWORD);
1668 for(u32 i=0;i<PASSWORD_SIZE-1;i++)
1670 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
1671 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
1673 buf[2+PASSWORD_SIZE-1] = 0;
1674 buf[30+PASSWORD_SIZE-1] = 0;
1675 os.write((char*)buf, 2+PASSWORD_SIZE*2);
1678 std::string s = os.str();
1679 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1681 Send(0, data, true);
1685 void Client::sendDamage(u8 damage)
1687 DSTACK(__FUNCTION_NAME);
1688 std::ostringstream os(std::ios_base::binary);
1690 writeU16(os, TOSERVER_DAMAGE);
1691 writeU8(os, damage);
1694 std::string s = os.str();
1695 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1697 Send(0, data, true);
1700 void Client::sendRespawn()
1702 DSTACK(__FUNCTION_NAME);
1703 std::ostringstream os(std::ios_base::binary);
1705 writeU16(os, TOSERVER_RESPAWN);
1708 std::string s = os.str();
1709 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1711 Send(0, data, true);
1714 void Client::sendPlayerPos()
1716 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1718 Player *myplayer = m_env.getLocalPlayer();
1719 if(myplayer == NULL)
1724 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1725 our_peer_id = m_con.GetPeerID();
1728 // Set peer id if not set already
1729 if(myplayer->peer_id == PEER_ID_INEXISTENT)
1730 myplayer->peer_id = our_peer_id;
1731 // Check that an existing peer_id is the same as the connection's
1732 assert(myplayer->peer_id == our_peer_id);
1734 v3f pf = myplayer->getPosition();
1735 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
1736 v3f sf = myplayer->getSpeed();
1737 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
1738 s32 pitch = myplayer->getPitch() * 100;
1739 s32 yaw = myplayer->getYaw() * 100;
1744 [2] v3s32 position*100
1745 [2+12] v3s32 speed*100
1746 [2+12+12] s32 pitch*100
1747 [2+12+12+4] s32 yaw*100
1750 SharedBuffer<u8> data(2+12+12+4+4);
1751 writeU16(&data[0], TOSERVER_PLAYERPOS);
1752 writeV3S32(&data[2], position);
1753 writeV3S32(&data[2+12], speed);
1754 writeS32(&data[2+12+12], pitch);
1755 writeS32(&data[2+12+12+4], yaw);
1757 // Send as unreliable
1758 Send(0, data, false);
1761 void Client::sendPlayerItem(u16 item)
1763 Player *myplayer = m_env.getLocalPlayer();
1764 if(myplayer == NULL)
1767 u16 our_peer_id = m_con.GetPeerID();
1769 // Set peer id if not set already
1770 if(myplayer->peer_id == PEER_ID_INEXISTENT)
1771 myplayer->peer_id = our_peer_id;
1772 // Check that an existing peer_id is the same as the connection's
1773 assert(myplayer->peer_id == our_peer_id);
1775 SharedBuffer<u8> data(2+2);
1776 writeU16(&data[0], TOSERVER_PLAYERITEM);
1777 writeU16(&data[2], item);
1780 Send(0, data, true);
1783 void Client::removeNode(v3s16 p)
1785 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1787 core::map<v3s16, MapBlock*> modified_blocks;
1791 //TimeTaker t("removeNodeAndUpdate", m_device);
1792 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
1794 catch(InvalidPositionException &e)
1798 for(core::map<v3s16, MapBlock * >::Iterator
1799 i = modified_blocks.getIterator();
1800 i.atEnd() == false; i++)
1802 v3s16 p = i.getNode()->getKey();
1803 //m_env.getClientMap().updateMeshes(p, m_env.getDayNightRatio());
1804 addUpdateMeshTaskWithEdge(p);
1808 void Client::addNode(v3s16 p, MapNode n)
1810 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1812 TimeTaker timer1("Client::addNode()");
1814 core::map<v3s16, MapBlock*> modified_blocks;
1818 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
1819 std::string st = std::string("");
1820 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks, st);
1822 catch(InvalidPositionException &e)
1825 //TimeTaker timer2("Client::addNode(): updateMeshes");
1827 for(core::map<v3s16, MapBlock * >::Iterator
1828 i = modified_blocks.getIterator();
1829 i.atEnd() == false; i++)
1831 v3s16 p = i.getNode()->getKey();
1832 //m_env.getClientMap().updateMeshes(p, m_env.getDayNightRatio());
1833 addUpdateMeshTaskWithEdge(p);
1837 void Client::updateCamera(v3f pos, v3f dir, f32 fov)
1839 m_env.getClientMap().updateCamera(pos, dir, fov);
1842 void Client::renderPostFx()
1844 m_env.getClientMap().renderPostFx();
1847 MapNode Client::getNode(v3s16 p)
1849 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1850 return m_env.getMap().getNode(p);
1853 NodeMetadata* Client::getNodeMetadata(v3s16 p)
1855 return m_env.getMap().getNodeMetadata(p);
1858 LocalPlayer* Client::getLocalPlayer()
1860 return m_env.getLocalPlayer();
1863 void Client::setPlayerControl(PlayerControl &control)
1865 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1866 LocalPlayer *player = m_env.getLocalPlayer();
1867 assert(player != NULL);
1868 player->control = control;
1871 void Client::selectPlayerItem(u16 item)
1873 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1874 m_playeritem = item;
1875 m_inventory_updated = true;
1876 sendPlayerItem(item);
1879 // Returns true if the inventory of the local player has been
1880 // updated from the server. If it is true, it is set to false.
1881 bool Client::getLocalInventoryUpdated()
1883 // m_inventory_updated is behind envlock
1884 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1885 bool updated = m_inventory_updated;
1886 m_inventory_updated = false;
1890 // Copies the inventory of the local player to parameter
1891 void Client::getLocalInventory(Inventory &dst)
1893 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1894 Player *player = m_env.getLocalPlayer();
1895 assert(player != NULL);
1896 dst = player->inventory;
1899 Inventory* Client::getInventory(const InventoryLocation &loc)
1902 case InventoryLocation::UNDEFINED:
1905 case InventoryLocation::CURRENT_PLAYER:
1907 Player *player = m_env.getLocalPlayer();
1908 assert(player != NULL);
1909 return &player->inventory;
1912 case InventoryLocation::PLAYER:
1914 Player *player = m_env.getPlayer(loc.name.c_str());
1917 return &player->inventory;
1920 case InventoryLocation::NODEMETA:
1922 NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p);
1925 return meta->getInventory();
1933 void Client::inventoryAction(InventoryAction *a)
1935 sendInventoryAction(a);
1938 ClientActiveObject * Client::getSelectedActiveObject(
1940 v3f from_pos_f_on_map,
1941 core::line3d<f32> shootline_on_map
1944 core::array<DistanceSortedActiveObject> objects;
1946 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
1948 //infostream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
1951 // After this, the closest object is the first in the array.
1954 for(u32 i=0; i<objects.size(); i++)
1956 ClientActiveObject *obj = objects[i].obj;
1958 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
1959 if(selection_box == NULL)
1962 v3f pos = obj->getPosition();
1964 core::aabbox3d<f32> offsetted_box(
1965 selection_box->MinEdge + pos,
1966 selection_box->MaxEdge + pos
1969 if(offsetted_box.intersectsWithLine(shootline_on_map))
1971 //infostream<<"Returning selected object"<<std::endl;
1976 //infostream<<"No object selected; returning NULL."<<std::endl;
1980 void Client::printDebugInfo(std::ostream &os)
1982 //JMutexAutoLock lock1(m_fetchblock_mutex);
1983 /*JMutexAutoLock lock2(m_incoming_queue_mutex);
1985 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
1986 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
1987 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
1991 u32 Client::getDayNightRatio()
1993 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1994 return m_env.getDayNightRatio();
1999 Player *player = m_env.getLocalPlayer();
2000 assert(player != NULL);
2004 void Client::setTempMod(v3s16 p, NodeMod mod)
2006 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2007 assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
2009 core::map<v3s16, MapBlock*> affected_blocks;
2010 ((ClientMap&)m_env.getMap()).setTempMod(p, mod,
2013 for(core::map<v3s16, MapBlock*>::Iterator
2014 i = affected_blocks.getIterator();
2015 i.atEnd() == false; i++)
2017 i.getNode()->getValue()->updateMesh(m_env.getDayNightRatio());
2021 void Client::clearTempMod(v3s16 p)
2023 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2024 assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
2026 core::map<v3s16, MapBlock*> affected_blocks;
2027 ((ClientMap&)m_env.getMap()).clearTempMod(p,
2030 for(core::map<v3s16, MapBlock*>::Iterator
2031 i = affected_blocks.getIterator();
2032 i.atEnd() == false; i++)
2034 i.getNode()->getValue()->updateMesh(m_env.getDayNightRatio());
2038 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server)
2040 /*infostream<<"Client::addUpdateMeshTask(): "
2041 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2044 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2049 Create a task to update the mesh of the block
2052 MeshMakeData *data = new MeshMakeData;
2055 //TimeTaker timer("data fill");
2057 // Debug: 1-6ms, avg=2ms
2058 data->fill(getDayNightRatio(), b);
2062 //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
2064 // Add task to queue
2065 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server);
2067 /*infostream<<"Mesh update input queue size is "
2068 <<m_mesh_update_thread.m_queue_in.size()
2072 // Temporary test: make mesh directly in here
2074 //TimeTaker timer("make mesh");
2076 scene::SMesh *mesh_new = NULL;
2077 mesh_new = makeMapBlockMesh(data);
2078 b->replaceMesh(mesh_new);
2084 Mark mesh as non-expired at this point so that it can already
2085 be marked as expired again if the data changes
2087 b->setMeshExpired(false);
2090 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server)
2094 infostream<<"Client::addUpdateMeshTaskWithEdge(): "
2095 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2100 v3s16 p = blockpos + v3s16(0,0,0);
2101 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2102 addUpdateMeshTask(p, ack_to_server);
2104 catch(InvalidPositionException &e){}
2107 v3s16 p = blockpos + v3s16(-1,0,0);
2108 addUpdateMeshTask(p);
2110 catch(InvalidPositionException &e){}
2112 v3s16 p = blockpos + v3s16(0,-1,0);
2113 addUpdateMeshTask(p);
2115 catch(InvalidPositionException &e){}
2117 v3s16 p = blockpos + v3s16(0,0,-1);
2118 addUpdateMeshTask(p);
2120 catch(InvalidPositionException &e){}
2123 ClientEvent Client::getClientEvent()
2125 if(m_client_event_queue.size() == 0)
2128 event.type = CE_NONE;
2131 return m_client_event_queue.pop_front();
2134 void Client::afterContentReceived()
2136 assert(m_itemdef_received);
2137 assert(m_nodedef_received);
2138 assert(m_textures_received);
2140 // Rebuild inherited images and recreate textures
2141 m_tsrc->rebuildImagesAndTextures();
2143 // Update texture atlas
2144 if(g_settings->getBool("enable_texture_atlas"))
2145 m_tsrc->buildMainAtlas(this);
2147 // Update node aliases
2148 m_nodedef->updateAliases(m_itemdef);
2150 // Update node textures
2151 m_nodedef->updateTextures(m_tsrc);
2153 // Update item textures and meshes
2154 m_itemdef->updateTexturesAndMeshes(this);
2156 // Start mesh update thread after setting up content definitions
2157 m_mesh_update_thread.Start();
2160 float Client::getRTT(void)
2163 return m_con.GetPeerAvgRTT(PEER_ID_SERVER);
2164 } catch(con::PeerNotFoundException &e){
2169 // IGameDef interface
2171 IItemDefManager* Client::getItemDefManager()
2175 INodeDefManager* Client::getNodeDefManager()
2179 ICraftDefManager* Client::getCraftDefManager()
2182 //return m_craftdef;
2184 ITextureSource* Client::getTextureSource()
2188 u16 Client::allocateUnknownNodeId(const std::string &name)
2190 errorstream<<"Client::allocateUnknownNodeId(): "
2191 <<"Client cannot allocate node IDs"<<std::endl;
2193 return CONTENT_IGNORE;