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),
228 m_inventory_from_server(NULL),
229 m_inventory_from_server_age(0.0),
232 m_password(password),
233 m_access_denied(false),
234 m_texture_receive_progress(0),
235 m_textures_received(false),
236 m_itemdef_received(false),
237 m_nodedef_received(false)
239 m_packetcounter_timer = 0.0;
240 //m_delete_unused_sectors_timer = 0.0;
241 m_connection_reinit_timer = 0.0;
242 m_avg_rtt_timer = 0.0;
243 m_playerpos_send_timer = 0.0;
244 m_ignore_damage_timer = 0.0;
246 // Build main texture atlas, now that the GameDef exists (that is, us)
247 if(g_settings->getBool("enable_texture_atlas"))
248 m_tsrc->buildMainAtlas(this);
250 infostream<<"Not building texture atlas."<<std::endl;
256 Player *player = new LocalPlayer(this);
258 player->updateName(playername);
260 m_env.addPlayer(player);
267 //JMutexAutoLock conlock(m_con_mutex); //bulk comment-out
271 m_mesh_update_thread.setRun(false);
272 while(m_mesh_update_thread.IsRunning())
275 delete m_inventory_from_server;
278 void Client::connect(Address address)
280 DSTACK(__FUNCTION_NAME);
281 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
282 m_con.SetTimeoutMs(0);
283 m_con.Connect(address);
286 bool Client::connectedAndInitialized()
288 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
290 if(m_con.Connected() == false)
293 if(m_server_ser_ver == SER_FMT_VER_INVALID)
299 void Client::step(float dtime)
301 DSTACK(__FUNCTION_NAME);
307 if(m_ignore_damage_timer > dtime)
308 m_ignore_damage_timer -= dtime;
310 m_ignore_damage_timer = 0.0;
312 //infostream<<"Client steps "<<dtime<<std::endl;
315 //TimeTaker timer("ReceiveAll()", m_device);
321 //TimeTaker timer("m_con_mutex + m_con.RunTimeouts()", m_device);
323 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
324 m_con.RunTimeouts(dtime);
331 float &counter = m_packetcounter_timer;
337 infostream<<"Client packetcounter (20s):"<<std::endl;
338 m_packetcounter.print(infostream);
339 m_packetcounter.clear();
343 // Get connection status
344 bool connected = connectedAndInitialized();
349 Delete unused sectors
351 NOTE: This jams the game for a while because deleting sectors
355 float &counter = m_delete_unused_sectors_timer;
363 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
365 core::list<v3s16> deleted_blocks;
367 float delete_unused_sectors_timeout =
368 g_settings->getFloat("client_delete_unused_sectors_timeout");
370 // Delete sector blocks
371 /*u32 num = m_env.getMap().unloadUnusedData
372 (delete_unused_sectors_timeout,
373 true, &deleted_blocks);*/
375 // Delete whole sectors
376 m_env.getMap().unloadUnusedData
377 (delete_unused_sectors_timeout,
380 if(deleted_blocks.size() > 0)
382 /*infostream<<"Client: Deleted blocks of "<<num
383 <<" unused sectors"<<std::endl;*/
384 /*infostream<<"Client: Deleted "<<num
385 <<" unused sectors"<<std::endl;*/
391 // Env is locked so con can be locked.
392 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
394 core::list<v3s16>::Iterator i = deleted_blocks.begin();
395 core::list<v3s16> sendlist;
398 if(sendlist.size() == 255 || i == deleted_blocks.end())
400 if(sendlist.size() == 0)
409 u32 replysize = 2+1+6*sendlist.size();
410 SharedBuffer<u8> reply(replysize);
411 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
412 reply[2] = sendlist.size();
414 for(core::list<v3s16>::Iterator
415 j = sendlist.begin();
416 j != sendlist.end(); j++)
418 writeV3S16(&reply[2+1+6*k], *j);
421 m_con.Send(PEER_ID_SERVER, 1, reply, true);
423 if(i == deleted_blocks.end())
429 sendlist.push_back(*i);
437 if(connected == false)
439 float &counter = m_connection_reinit_timer;
445 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
447 Player *myplayer = m_env.getLocalPlayer();
448 assert(myplayer != NULL);
450 // Send TOSERVER_INIT
451 // [0] u16 TOSERVER_INIT
452 // [2] u8 SER_FMT_VER_HIGHEST
453 // [3] u8[20] player_name
454 // [23] u8[28] password (new in some version)
455 // [51] u16 client network protocol version (new in some version)
456 SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2);
457 writeU16(&data[0], TOSERVER_INIT);
458 writeU8(&data[2], SER_FMT_VER_HIGHEST);
460 memset((char*)&data[3], 0, PLAYERNAME_SIZE);
461 snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
463 /*infostream<<"Client: sending initial password hash: \""<<m_password<<"\""
466 memset((char*)&data[23], 0, PASSWORD_SIZE);
467 snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
469 // This should be incremented in each version
470 writeU16(&data[51], PROTOCOL_VERSION);
472 // Send as unreliable
473 Send(0, data, false);
476 // Not connected, return
481 Do stuff if connected
485 Run Map's timers and unload unused data
487 const float map_timer_and_unload_dtime = 5.25;
488 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
490 ScopeProfiler sp(g_profiler, "Client: map timer and unload");
491 core::list<v3s16> deleted_blocks;
492 m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
493 g_settings->getFloat("client_unload_unused_data_timeout"),
496 /*if(deleted_blocks.size() > 0)
497 infostream<<"Client: Unloaded "<<deleted_blocks.size()
498 <<" unused blocks"<<std::endl;*/
502 NOTE: This loop is intentionally iterated the way it is.
505 core::list<v3s16>::Iterator i = deleted_blocks.begin();
506 core::list<v3s16> sendlist;
509 if(sendlist.size() == 255 || i == deleted_blocks.end())
511 if(sendlist.size() == 0)
520 u32 replysize = 2+1+6*sendlist.size();
521 SharedBuffer<u8> reply(replysize);
522 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
523 reply[2] = sendlist.size();
525 for(core::list<v3s16>::Iterator
526 j = sendlist.begin();
527 j != sendlist.end(); j++)
529 writeV3S16(&reply[2+1+6*k], *j);
532 m_con.Send(PEER_ID_SERVER, 1, reply, true);
534 if(i == deleted_blocks.end())
540 sendlist.push_back(*i);
550 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
552 // Control local player (0ms)
553 LocalPlayer *player = m_env.getLocalPlayer();
554 assert(player != NULL);
555 player->applyControl(dtime);
557 //TimeTaker envtimer("env step", m_device);
566 ClientEnvEvent event = m_env.getClientEvent();
567 if(event.type == CEE_NONE)
571 else if(event.type == CEE_PLAYER_DAMAGE)
573 if(m_ignore_damage_timer <= 0)
575 u8 damage = event.player_damage.amount;
577 if(event.player_damage.send_to_server)
580 // Add to ClientEvent queue
582 event.type = CE_PLAYER_DAMAGE;
583 event.player_damage.amount = damage;
584 m_client_event_queue.push_back(event);
594 float &counter = m_avg_rtt_timer;
599 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
600 // connectedAndInitialized() is true, peer exists.
601 float avg_rtt = m_con.GetPeerAvgRTT(PEER_ID_SERVER);
602 infostream<<"Client: avg_rtt="<<avg_rtt<<std::endl;
607 Send player position to server
610 float &counter = m_playerpos_send_timer;
620 Replace updated meshes
623 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
625 //TimeTaker timer("** Processing mesh update result queue");
628 /*infostream<<"Mesh update result queue size is "
629 <<m_mesh_update_thread.m_queue_out.size()
632 while(m_mesh_update_thread.m_queue_out.size() > 0)
634 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_front();
635 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
638 block->replaceMesh(r.mesh);
640 if(r.ack_block_to_server)
642 /*infostream<<"Client: ACK block ("<<r.p.X<<","<<r.p.Y
643 <<","<<r.p.Z<<")"<<std::endl;*/
654 u32 replysize = 2+1+6;
655 SharedBuffer<u8> reply(replysize);
656 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
658 writeV3S16(&reply[3], r.p);
660 m_con.Send(PEER_ID_SERVER, 1, reply, true);
666 If the server didn't update the inventory in a while, revert
667 the local inventory (so the player notices the lag problem
668 and knows something is wrong).
670 if(m_inventory_from_server)
672 float interval = 10.0;
673 float count_before = floor(m_inventory_from_server_age / interval);
675 m_inventory_from_server_age += dtime;
677 float count_after = floor(m_inventory_from_server_age / interval);
679 if(count_after != count_before)
681 // Do this every <interval> seconds after TOCLIENT_INVENTORY
682 // Reset the locally changed inventory to the authoritative inventory
683 Player *player = m_env.getLocalPlayer();
684 player->inventory = *m_inventory_from_server;
685 m_inventory_updated = true;
690 // Virtual methods from con::PeerHandler
691 void Client::peerAdded(con::Peer *peer)
693 infostream<<"Client::peerAdded(): peer->id="
694 <<peer->id<<std::endl;
696 void Client::deletingPeer(con::Peer *peer, bool timeout)
698 infostream<<"Client::deletingPeer(): "
699 "Server Peer is getting deleted "
700 <<"(timeout="<<timeout<<")"<<std::endl;
703 void Client::ReceiveAll()
705 DSTACK(__FUNCTION_NAME);
706 u32 start_ms = porting::getTimeMs();
709 // Limit time even if there would be huge amounts of data to
711 if(porting::getTimeMs() > start_ms + 100)
717 catch(con::NoIncomingDataException &e)
721 catch(con::InvalidIncomingDataException &e)
723 infostream<<"Client::ReceiveAll(): "
724 "InvalidIncomingDataException: what()="
725 <<e.what()<<std::endl;
730 void Client::Receive()
732 DSTACK(__FUNCTION_NAME);
733 SharedBuffer<u8> data;
737 //TimeTaker t1("con mutex and receive", m_device);
738 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
739 datasize = m_con.Receive(sender_peer_id, data);
741 //TimeTaker t1("ProcessData", m_device);
742 ProcessData(*data, datasize, sender_peer_id);
746 sender_peer_id given to this shall be quaranteed to be a valid peer
748 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
750 DSTACK(__FUNCTION_NAME);
752 // Ignore packets that don't even fit a command
755 m_packetcounter.add(60000);
759 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
761 //infostream<<"Client: received command="<<command<<std::endl;
762 m_packetcounter.add((u16)command);
765 If this check is removed, be sure to change the queue
766 system to know the ids
768 if(sender_peer_id != PEER_ID_SERVER)
770 infostream<<"Client::ProcessData(): Discarding data not "
771 "coming from server: peer_id="<<sender_peer_id
776 u8 ser_version = m_server_ser_ver;
778 //infostream<<"Client received command="<<(int)command<<std::endl;
780 if(command == TOCLIENT_INIT)
785 u8 deployed = data[2];
787 infostream<<"Client: TOCLIENT_INIT received with "
788 "deployed="<<((int)deployed&0xff)<<std::endl;
790 if(deployed < SER_FMT_VER_LOWEST
791 || deployed > SER_FMT_VER_HIGHEST)
793 infostream<<"Client: TOCLIENT_INIT: Server sent "
794 <<"unsupported ser_fmt_ver"<<std::endl;
798 m_server_ser_ver = deployed;
800 // Get player position
801 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
802 if(datasize >= 2+1+6)
803 playerpos_s16 = readV3S16(&data[2+1]);
804 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
807 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
809 // Set player position
810 Player *player = m_env.getLocalPlayer();
811 assert(player != NULL);
812 player->setPosition(playerpos_f);
815 if(datasize >= 2+1+6+8)
818 m_map_seed = readU64(&data[2+1+6]);
819 infostream<<"Client: received map seed: "<<m_map_seed<<std::endl;
824 SharedBuffer<u8> reply(replysize);
825 writeU16(&reply[0], TOSERVER_INIT2);
827 m_con.Send(PEER_ID_SERVER, 1, reply, true);
832 if(command == TOCLIENT_ACCESS_DENIED)
834 // The server didn't like our password. Note, this needs
835 // to be processed even if the serialisation format has
836 // not been agreed yet, the same as TOCLIENT_INIT.
837 m_access_denied = true;
838 m_access_denied_reason = L"Unknown";
841 std::string datastring((char*)&data[2], datasize-2);
842 std::istringstream is(datastring, std::ios_base::binary);
843 m_access_denied_reason = deSerializeWideString(is);
848 if(ser_version == SER_FMT_VER_INVALID)
850 infostream<<"Client: Server serialization"
851 " format invalid or not initialized."
852 " Skipping incoming command="<<command<<std::endl;
856 // Just here to avoid putting the two if's together when
857 // making some copypasta
860 if(command == TOCLIENT_REMOVENODE)
865 p.X = readS16(&data[2]);
866 p.Y = readS16(&data[4]);
867 p.Z = readS16(&data[6]);
869 //TimeTaker t1("TOCLIENT_REMOVENODE");
871 // This will clear the cracking animation after digging
872 ((ClientMap&)m_env.getMap()).clearTempMod(p);
876 else if(command == TOCLIENT_ADDNODE)
878 if(datasize < 8 + MapNode::serializedLength(ser_version))
882 p.X = readS16(&data[2]);
883 p.Y = readS16(&data[4]);
884 p.Z = readS16(&data[6]);
886 //TimeTaker t1("TOCLIENT_ADDNODE");
889 n.deSerialize(&data[8], ser_version);
893 else if(command == TOCLIENT_BLOCKDATA)
895 // Ignore too small packet
900 p.X = readS16(&data[2]);
901 p.Y = readS16(&data[4]);
902 p.Z = readS16(&data[6]);
904 /*infostream<<"Client: Thread: BLOCKDATA for ("
905 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
906 /*infostream<<"Client: Thread: BLOCKDATA for ("
907 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
909 std::string datastring((char*)&data[8], datasize-8);
910 std::istringstream istr(datastring, std::ios_base::binary);
916 sector = m_env.getMap().emergeSector(p2d);
918 assert(sector->getPos() == p2d);
920 //TimeTaker timer("MapBlock deSerialize");
923 block = sector->getBlockNoCreateNoEx(p.Y);
927 Update an existing block
929 //infostream<<"Updating"<<std::endl;
930 block->deSerialize(istr, ser_version, false);
937 //infostream<<"Creating new"<<std::endl;
938 block = new MapBlock(&m_env.getMap(), p, this);
939 block->deSerialize(istr, ser_version, false);
940 sector->insertBlock(block);
954 u32 replysize = 2+1+6;
955 SharedBuffer<u8> reply(replysize);
956 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
958 writeV3S16(&reply[3], p);
960 m_con.Send(PEER_ID_SERVER, 1, reply, true);
964 Update Mesh of this block and blocks at x-, y- and z-.
965 Environment should not be locked as it interlocks with the
966 main thread, from which is will want to retrieve textures.
969 //m_env.getClientMap().updateMeshes(block->getPos(), getDayNightRatio());
971 Add it to mesh update queue and set it to be acknowledged after update.
973 //infostream<<"Adding mesh update task for received block"<<std::endl;
974 addUpdateMeshTaskWithEdge(p, true);
976 else if(command == TOCLIENT_INVENTORY)
981 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
984 //TimeTaker t2("mutex locking", m_device);
985 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
988 //TimeTaker t3("istringstream init", m_device);
989 std::string datastring((char*)&data[2], datasize-2);
990 std::istringstream is(datastring, std::ios_base::binary);
993 //m_env.printPlayers(infostream);
995 //TimeTaker t4("player get", m_device);
996 Player *player = m_env.getLocalPlayer();
997 assert(player != NULL);
1000 //TimeTaker t1("inventory.deSerialize()", m_device);
1001 player->inventory.deSerialize(is);
1004 m_inventory_updated = true;
1006 delete m_inventory_from_server;
1007 m_inventory_from_server = new Inventory(player->inventory);
1008 m_inventory_from_server_age = 0.0;
1010 //infostream<<"Client got player inventory:"<<std::endl;
1011 //player->inventory.print(infostream);
1014 else if(command == TOCLIENT_TIME_OF_DAY)
1019 u16 time_of_day = readU16(&data[2]);
1020 time_of_day = time_of_day % 24000;
1021 //infostream<<"Client: time_of_day="<<time_of_day<<std::endl;
1029 m_env.setTimeOfDay(time_of_day);
1031 u32 dr = m_env.getDayNightRatio();
1033 infostream<<"Client: time_of_day="<<time_of_day
1039 else if(command == TOCLIENT_CHAT_MESSAGE)
1047 std::string datastring((char*)&data[2], datasize-2);
1048 std::istringstream is(datastring, std::ios_base::binary);
1051 is.read((char*)buf, 2);
1052 u16 len = readU16(buf);
1054 std::wstring message;
1055 for(u16 i=0; i<len; i++)
1057 is.read((char*)buf, 2);
1058 message += (wchar_t)readU16(buf);
1061 /*infostream<<"Client received chat message: "
1062 <<wide_to_narrow(message)<<std::endl;*/
1064 m_chat_queue.push_back(message);
1066 else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1068 //if(g_settings->getBool("enable_experimental"))
1072 u16 count of removed objects
1073 for all removed objects {
1076 u16 count of added objects
1077 for all added objects {
1080 u32 initialization data length
1081 string initialization data
1086 // Get all data except the command number
1087 std::string datastring((char*)&data[2], datasize-2);
1088 // Throw them in an istringstream
1089 std::istringstream is(datastring, std::ios_base::binary);
1093 // Read removed objects
1095 u16 removed_count = readU16((u8*)buf);
1096 for(u16 i=0; i<removed_count; i++)
1099 u16 id = readU16((u8*)buf);
1102 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1103 m_env.removeActiveObject(id);
1107 // Read added objects
1109 u16 added_count = readU16((u8*)buf);
1110 for(u16 i=0; i<added_count; i++)
1113 u16 id = readU16((u8*)buf);
1115 u8 type = readU8((u8*)buf);
1116 std::string data = deSerializeLongString(is);
1119 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1120 m_env.addActiveObject(id, type, data);
1125 else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1127 //if(g_settings->getBool("enable_experimental"))
1139 // Get all data except the command number
1140 std::string datastring((char*)&data[2], datasize-2);
1141 // Throw them in an istringstream
1142 std::istringstream is(datastring, std::ios_base::binary);
1144 while(is.eof() == false)
1148 u16 id = readU16((u8*)buf);
1152 u16 message_size = readU16((u8*)buf);
1153 std::string message;
1154 message.reserve(message_size);
1155 for(u16 i=0; i<message_size; i++)
1158 message.append(buf, 1);
1160 // Pass on to the environment
1162 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1163 m_env.processActiveObjectMessage(id, message);
1168 else if(command == TOCLIENT_HP)
1170 std::string datastring((char*)&data[2], datasize-2);
1171 std::istringstream is(datastring, std::ios_base::binary);
1172 Player *player = m_env.getLocalPlayer();
1173 assert(player != NULL);
1174 u8 oldhp = player->hp;
1180 // Add to ClientEvent queue
1182 event.type = CE_PLAYER_DAMAGE;
1183 event.player_damage.amount = oldhp - hp;
1184 m_client_event_queue.push_back(event);
1187 else if(command == TOCLIENT_MOVE_PLAYER)
1189 std::string datastring((char*)&data[2], datasize-2);
1190 std::istringstream is(datastring, std::ios_base::binary);
1191 Player *player = m_env.getLocalPlayer();
1192 assert(player != NULL);
1193 v3f pos = readV3F1000(is);
1194 f32 pitch = readF1000(is);
1195 f32 yaw = readF1000(is);
1196 player->setPosition(pos);
1197 /*player->setPitch(pitch);
1198 player->setYaw(yaw);*/
1200 infostream<<"Client got TOCLIENT_MOVE_PLAYER"
1201 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1207 Add to ClientEvent queue.
1208 This has to be sent to the main program because otherwise
1209 it would just force the pitch and yaw values to whatever
1210 the camera points to.
1213 event.type = CE_PLAYER_FORCE_MOVE;
1214 event.player_force_move.pitch = pitch;
1215 event.player_force_move.yaw = yaw;
1216 m_client_event_queue.push_back(event);
1218 // Ignore damage for a few seconds, so that the player doesn't
1219 // get damage from falling on ground
1220 m_ignore_damage_timer = 3.0;
1222 else if(command == TOCLIENT_PLAYERITEM)
1224 std::string datastring((char*)&data[2], datasize-2);
1225 std::istringstream is(datastring, std::ios_base::binary);
1227 u16 count = readU16(is);
1229 for (u16 i = 0; i < count; ++i) {
1230 u16 peer_id = readU16(is);
1231 Player *player = m_env.getPlayer(peer_id);
1235 infostream<<"Client: ignoring player item "
1236 << deSerializeString(is)
1237 << " for non-existing peer id " << peer_id
1240 } else if (player->isLocal()) {
1241 infostream<<"Client: ignoring player item "
1242 << deSerializeString(is)
1243 << " for local player" << std::endl;
1246 InventoryList *inv = player->inventory.getList("main");
1247 std::string itemstring(deSerializeString(is));
1249 item.deSerialize(itemstring, m_itemdef);
1250 inv->changeItem(0, item);
1251 if(itemstring.empty())
1253 infostream<<"Client: empty player item for peer "
1254 <<peer_id<<std::endl;
1258 infostream<<"Client: player item for peer "
1259 <<peer_id<<": "<<itemstring<<std::endl;
1264 else if(command == TOCLIENT_DEATHSCREEN)
1266 std::string datastring((char*)&data[2], datasize-2);
1267 std::istringstream is(datastring, std::ios_base::binary);
1269 bool set_camera_point_target = readU8(is);
1270 v3f camera_point_target = readV3F1000(is);
1273 event.type = CE_DEATHSCREEN;
1274 event.deathscreen.set_camera_point_target = set_camera_point_target;
1275 event.deathscreen.camera_point_target_x = camera_point_target.X;
1276 event.deathscreen.camera_point_target_y = camera_point_target.Y;
1277 event.deathscreen.camera_point_target_z = camera_point_target.Z;
1278 m_client_event_queue.push_back(event);
1280 else if(command == TOCLIENT_ANNOUNCE_TEXTURES)
1282 io::IFileSystem *irrfs = m_device->getFileSystem();
1283 video::IVideoDriver *vdrv = m_device->getVideoDriver();
1285 std::string datastring((char*)&data[2], datasize-2);
1286 std::istringstream is(datastring, std::ios_base::binary);
1288 // Mesh update thread must be stopped while
1289 // updating content definitions
1290 assert(!m_mesh_update_thread.IsRunning());
1292 int num_textures = readU16(is);
1294 core::list<TextureRequest> texture_requests;
1296 for(int i=0; i<num_textures; i++){
1298 bool texture_found = false;
1300 //read texture from cache
1301 std::string name = deSerializeString(is);
1302 std::string sha1_texture = deSerializeString(is);
1304 // if name contains illegal characters, ignore the texture
1305 if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
1306 errorstream<<"Client: ignoring illegal texture name "
1307 <<"sent by server: \""<<name<<"\""<<std::endl;
1311 std::string tpath = getTextureCacheDir() + DIR_DELIM + name;
1313 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
1316 if(fis.good() == false){
1317 infostream<<"Client::Texture not found in cache: "
1318 <<name << " expected it at: "<<tpath<<std::endl;
1322 std::ostringstream tmp_os(std::ios_base::binary);
1326 fis.read(buf, 1024);
1327 std::streamsize len = fis.gcount();
1328 tmp_os.write(buf, len);
1337 infostream<<"Client: Failed to read texture from cache\""
1338 <<name<<"\""<<std::endl;
1343 sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
1345 unsigned char *digest = sha1.getDigest();
1347 std::string digest_string = base64_encode(digest, 20);
1349 if (digest_string == sha1_texture) {
1350 // Silly irrlicht's const-incorrectness
1351 Buffer<char> data_rw(tmp_os.str().c_str(), tmp_os.str().size());
1353 // Create an irrlicht memory file
1354 io::IReadFile *rfile = irrfs->createMemoryReadFile(
1355 *data_rw, tmp_os.str().size(), "_tempreadfile");
1358 video::IImage *img = vdrv->createImageFromFile(rfile);
1360 infostream<<"Client: Cannot create image from data of "
1361 <<"received texture \""<<name<<"\""<<std::endl;
1365 m_tsrc->insertSourceImage(name, img);
1369 texture_found = true;
1373 infostream<<"Client::Texture cached sha1 hash not matching server hash: "
1374 <<name << ": server ->"<<sha1_texture <<" client -> "<<digest_string<<std::endl;
1381 //add texture request
1382 if (!texture_found) {
1383 infostream<<"Client: Adding texture to request list: \""
1384 <<name<<"\""<<std::endl;
1385 texture_requests.push_back(TextureRequest(name));
1391 event.type = CE_TEXTURES_UPDATED;
1392 m_client_event_queue.push_back(event);
1395 //send Texture request
1398 u16 number of textures requested
1404 std::ostringstream os(std::ios_base::binary);
1409 writeU16(buf, TOSERVER_REQUEST_TEXTURES);
1410 os.write((char*)buf, 2);
1412 writeU16(buf,texture_requests.size());
1413 os.write((char*)buf, 2);
1416 for(core::list<TextureRequest>::Iterator i = texture_requests.begin();
1417 i != texture_requests.end(); i++) {
1418 os<<serializeString(i->name);
1422 std::string s = os.str();
1423 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1425 Send(0, data, true);
1426 infostream<<"Client: Sending request list to server " <<std::endl;
1428 else if(command == TOCLIENT_TEXTURES)
1430 io::IFileSystem *irrfs = m_device->getFileSystem();
1431 video::IVideoDriver *vdrv = m_device->getVideoDriver();
1433 std::string datastring((char*)&data[2], datasize-2);
1434 std::istringstream is(datastring, std::ios_base::binary);
1436 // Mesh update thread must be stopped while
1437 // updating content definitions
1438 assert(!m_mesh_update_thread.IsRunning());
1442 u16 total number of texture bunches
1443 u16 index of this bunch
1444 u32 number of textures in this bunch
1452 int num_bunches = readU16(is);
1453 int bunch_i = readU16(is);
1454 m_texture_receive_progress = (float)bunch_i / (float)(num_bunches - 1);
1455 if(bunch_i == num_bunches - 1)
1456 m_textures_received = true;
1457 int num_textures = readU32(is);
1458 infostream<<"Client: Received textures: bunch "<<bunch_i<<"/"
1459 <<num_bunches<<" textures="<<num_textures
1460 <<" size="<<datasize<<std::endl;
1461 for(int i=0; i<num_textures; i++){
1462 std::string name = deSerializeString(is);
1463 std::string data = deSerializeLongString(is);
1465 // if name contains illegal characters, ignore the texture
1466 if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
1467 errorstream<<"Client: ignoring illegal texture name "
1468 <<"sent by server: \""<<name<<"\""<<std::endl;
1472 // Silly irrlicht's const-incorrectness
1473 Buffer<char> data_rw(data.c_str(), data.size());
1474 // Create an irrlicht memory file
1475 io::IReadFile *rfile = irrfs->createMemoryReadFile(
1476 *data_rw, data.size(), "_tempreadfile");
1479 video::IImage *img = vdrv->createImageFromFile(rfile);
1481 errorstream<<"Client: Cannot create image from data of "
1482 <<"received texture \""<<name<<"\""<<std::endl;
1487 fs::CreateAllDirs(getTextureCacheDir());
1489 std::string filename = getTextureCacheDir() + DIR_DELIM + name;
1490 std::ofstream outfile(filename.c_str(), std::ios_base::binary | std::ios_base::trunc);
1492 if (outfile.good()) {
1493 outfile.write(data.c_str(),data.length());
1497 errorstream<<"Client: Unable to open cached texture file "<< filename <<std::endl;
1500 m_tsrc->insertSourceImage(name, img);
1506 event.type = CE_TEXTURES_UPDATED;
1507 m_client_event_queue.push_back(event);
1509 else if(command == TOCLIENT_TOOLDEF)
1511 infostream<<"Client: WARNING: Ignoring TOCLIENT_TOOLDEF"<<std::endl;
1513 else if(command == TOCLIENT_NODEDEF)
1515 infostream<<"Client: Received node definitions: packet size: "
1516 <<datasize<<std::endl;
1518 // Mesh update thread must be stopped while
1519 // updating content definitions
1520 assert(!m_mesh_update_thread.IsRunning());
1522 // Decompress node definitions
1523 std::string datastring((char*)&data[2], datasize-2);
1524 std::istringstream is(datastring, std::ios_base::binary);
1525 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1526 std::ostringstream tmp_os;
1527 decompressZlib(tmp_is, tmp_os);
1529 // Deserialize node definitions
1530 std::istringstream tmp_is2(tmp_os.str());
1531 m_nodedef->deSerialize(tmp_is2);
1532 m_nodedef_received = true;
1534 else if(command == TOCLIENT_CRAFTITEMDEF)
1536 infostream<<"Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF"<<std::endl;
1538 else if(command == TOCLIENT_ITEMDEF)
1540 infostream<<"Client: Received item definitions: packet size: "
1541 <<datasize<<std::endl;
1543 // Mesh update thread must be stopped while
1544 // updating content definitions
1545 assert(!m_mesh_update_thread.IsRunning());
1547 // Decompress item definitions
1548 std::string datastring((char*)&data[2], datasize-2);
1549 std::istringstream is(datastring, std::ios_base::binary);
1550 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1551 std::ostringstream tmp_os;
1552 decompressZlib(tmp_is, tmp_os);
1554 // Deserialize node definitions
1555 std::istringstream tmp_is2(tmp_os.str());
1556 m_itemdef->deSerialize(tmp_is2);
1557 m_itemdef_received = true;
1561 infostream<<"Client: Ignoring unknown command "
1562 <<command<<std::endl;
1566 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
1568 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1569 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
1572 void Client::interact(u8 action, const PointedThing& pointed)
1574 if(connectedAndInitialized() == false){
1575 infostream<<"Client::interact() "
1576 "cancelled (not connected)"
1581 std::ostringstream os(std::ios_base::binary);
1587 [5] u32 length of the next item
1588 [9] serialized PointedThing
1590 0: start digging (from undersurface) or use
1591 1: stop digging (all parameters ignored)
1592 2: digging completed
1593 3: place block or item (to abovesurface)
1596 writeU16(os, TOSERVER_INTERACT);
1597 writeU8(os, action);
1598 writeU16(os, getPlayerItem());
1599 std::ostringstream tmp_os(std::ios::binary);
1600 pointed.serialize(tmp_os);
1601 os<<serializeLongString(tmp_os.str());
1603 std::string s = os.str();
1604 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1607 Send(0, data, true);
1610 void Client::sendSignNodeText(v3s16 p, std::string text)
1618 std::ostringstream os(std::ios_base::binary);
1622 writeU16(buf, TOSERVER_SIGNNODETEXT);
1623 os.write((char*)buf, 2);
1627 os.write((char*)buf, 6);
1629 u16 textlen = text.size();
1630 // Write text length
1631 writeS16(buf, textlen);
1632 os.write((char*)buf, 2);
1635 os.write((char*)text.c_str(), textlen);
1638 std::string s = os.str();
1639 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1641 Send(0, data, true);
1644 void Client::sendInventoryAction(InventoryAction *a)
1646 std::ostringstream os(std::ios_base::binary);
1650 writeU16(buf, TOSERVER_INVENTORY_ACTION);
1651 os.write((char*)buf, 2);
1656 std::string s = os.str();
1657 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1659 Send(0, data, true);
1662 void Client::sendChatMessage(const std::wstring &message)
1664 std::ostringstream os(std::ios_base::binary);
1668 writeU16(buf, TOSERVER_CHAT_MESSAGE);
1669 os.write((char*)buf, 2);
1672 writeU16(buf, message.size());
1673 os.write((char*)buf, 2);
1676 for(u32 i=0; i<message.size(); i++)
1680 os.write((char*)buf, 2);
1684 std::string s = os.str();
1685 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1687 Send(0, data, true);
1690 void Client::sendChangePassword(const std::wstring oldpassword,
1691 const std::wstring newpassword)
1693 Player *player = m_env.getLocalPlayer();
1697 std::string playername = player->getName();
1698 std::string oldpwd = translatePassword(playername, oldpassword);
1699 std::string newpwd = translatePassword(playername, newpassword);
1701 std::ostringstream os(std::ios_base::binary);
1702 u8 buf[2+PASSWORD_SIZE*2];
1704 [0] u16 TOSERVER_PASSWORD
1705 [2] u8[28] old password
1706 [30] u8[28] new password
1709 writeU16(buf, TOSERVER_PASSWORD);
1710 for(u32 i=0;i<PASSWORD_SIZE-1;i++)
1712 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
1713 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
1715 buf[2+PASSWORD_SIZE-1] = 0;
1716 buf[30+PASSWORD_SIZE-1] = 0;
1717 os.write((char*)buf, 2+PASSWORD_SIZE*2);
1720 std::string s = os.str();
1721 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1723 Send(0, data, true);
1727 void Client::sendDamage(u8 damage)
1729 DSTACK(__FUNCTION_NAME);
1730 std::ostringstream os(std::ios_base::binary);
1732 writeU16(os, TOSERVER_DAMAGE);
1733 writeU8(os, damage);
1736 std::string s = os.str();
1737 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1739 Send(0, data, true);
1742 void Client::sendRespawn()
1744 DSTACK(__FUNCTION_NAME);
1745 std::ostringstream os(std::ios_base::binary);
1747 writeU16(os, TOSERVER_RESPAWN);
1750 std::string s = os.str();
1751 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1753 Send(0, data, true);
1756 void Client::sendPlayerPos()
1758 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1760 Player *myplayer = m_env.getLocalPlayer();
1761 if(myplayer == NULL)
1766 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1767 our_peer_id = m_con.GetPeerID();
1770 // Set peer id if not set already
1771 if(myplayer->peer_id == PEER_ID_INEXISTENT)
1772 myplayer->peer_id = our_peer_id;
1773 // Check that an existing peer_id is the same as the connection's
1774 assert(myplayer->peer_id == our_peer_id);
1776 v3f pf = myplayer->getPosition();
1777 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
1778 v3f sf = myplayer->getSpeed();
1779 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
1780 s32 pitch = myplayer->getPitch() * 100;
1781 s32 yaw = myplayer->getYaw() * 100;
1786 [2] v3s32 position*100
1787 [2+12] v3s32 speed*100
1788 [2+12+12] s32 pitch*100
1789 [2+12+12+4] s32 yaw*100
1792 SharedBuffer<u8> data(2+12+12+4+4);
1793 writeU16(&data[0], TOSERVER_PLAYERPOS);
1794 writeV3S32(&data[2], position);
1795 writeV3S32(&data[2+12], speed);
1796 writeS32(&data[2+12+12], pitch);
1797 writeS32(&data[2+12+12+4], yaw);
1799 // Send as unreliable
1800 Send(0, data, false);
1803 void Client::sendPlayerItem(u16 item)
1805 Player *myplayer = m_env.getLocalPlayer();
1806 if(myplayer == NULL)
1809 u16 our_peer_id = m_con.GetPeerID();
1811 // Set peer id if not set already
1812 if(myplayer->peer_id == PEER_ID_INEXISTENT)
1813 myplayer->peer_id = our_peer_id;
1814 // Check that an existing peer_id is the same as the connection's
1815 assert(myplayer->peer_id == our_peer_id);
1817 SharedBuffer<u8> data(2+2);
1818 writeU16(&data[0], TOSERVER_PLAYERITEM);
1819 writeU16(&data[2], item);
1822 Send(0, data, true);
1825 void Client::removeNode(v3s16 p)
1827 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1829 core::map<v3s16, MapBlock*> modified_blocks;
1833 //TimeTaker t("removeNodeAndUpdate", m_device);
1834 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
1836 catch(InvalidPositionException &e)
1840 for(core::map<v3s16, MapBlock * >::Iterator
1841 i = modified_blocks.getIterator();
1842 i.atEnd() == false; i++)
1844 v3s16 p = i.getNode()->getKey();
1845 //m_env.getClientMap().updateMeshes(p, m_env.getDayNightRatio());
1846 addUpdateMeshTaskWithEdge(p);
1850 void Client::addNode(v3s16 p, MapNode n)
1852 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1854 TimeTaker timer1("Client::addNode()");
1856 core::map<v3s16, MapBlock*> modified_blocks;
1860 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
1861 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks);
1863 catch(InvalidPositionException &e)
1866 //TimeTaker timer2("Client::addNode(): updateMeshes");
1868 for(core::map<v3s16, MapBlock * >::Iterator
1869 i = modified_blocks.getIterator();
1870 i.atEnd() == false; i++)
1872 v3s16 p = i.getNode()->getKey();
1873 //m_env.getClientMap().updateMeshes(p, m_env.getDayNightRatio());
1874 addUpdateMeshTaskWithEdge(p);
1878 void Client::updateCamera(v3f pos, v3f dir, f32 fov)
1880 m_env.getClientMap().updateCamera(pos, dir, fov);
1883 void Client::renderPostFx()
1885 m_env.getClientMap().renderPostFx();
1888 MapNode Client::getNode(v3s16 p)
1890 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1891 return m_env.getMap().getNode(p);
1894 NodeMetadata* Client::getNodeMetadata(v3s16 p)
1896 return m_env.getMap().getNodeMetadata(p);
1899 LocalPlayer* Client::getLocalPlayer()
1901 return m_env.getLocalPlayer();
1904 void Client::setPlayerControl(PlayerControl &control)
1906 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1907 LocalPlayer *player = m_env.getLocalPlayer();
1908 assert(player != NULL);
1909 player->control = control;
1912 void Client::selectPlayerItem(u16 item)
1914 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1915 m_playeritem = item;
1916 m_inventory_updated = true;
1917 sendPlayerItem(item);
1920 // Returns true if the inventory of the local player has been
1921 // updated from the server. If it is true, it is set to false.
1922 bool Client::getLocalInventoryUpdated()
1924 // m_inventory_updated is behind envlock
1925 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1926 bool updated = m_inventory_updated;
1927 m_inventory_updated = false;
1931 // Copies the inventory of the local player to parameter
1932 void Client::getLocalInventory(Inventory &dst)
1934 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1935 Player *player = m_env.getLocalPlayer();
1936 assert(player != NULL);
1937 dst = player->inventory;
1940 Inventory* Client::getInventory(const InventoryLocation &loc)
1943 case InventoryLocation::UNDEFINED:
1946 case InventoryLocation::CURRENT_PLAYER:
1948 Player *player = m_env.getLocalPlayer();
1949 assert(player != NULL);
1950 return &player->inventory;
1953 case InventoryLocation::PLAYER:
1955 Player *player = m_env.getPlayer(loc.name.c_str());
1958 return &player->inventory;
1961 case InventoryLocation::NODEMETA:
1963 NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p);
1966 return meta->getInventory();
1974 void Client::inventoryAction(InventoryAction *a)
1977 Send it to the server
1979 sendInventoryAction(a);
1982 Predict some local inventory changes
1984 a->clientApply(this, this);
1987 ClientActiveObject * Client::getSelectedActiveObject(
1989 v3f from_pos_f_on_map,
1990 core::line3d<f32> shootline_on_map
1993 core::array<DistanceSortedActiveObject> objects;
1995 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
1997 //infostream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
2000 // After this, the closest object is the first in the array.
2003 for(u32 i=0; i<objects.size(); i++)
2005 ClientActiveObject *obj = objects[i].obj;
2007 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
2008 if(selection_box == NULL)
2011 v3f pos = obj->getPosition();
2013 core::aabbox3d<f32> offsetted_box(
2014 selection_box->MinEdge + pos,
2015 selection_box->MaxEdge + pos
2018 if(offsetted_box.intersectsWithLine(shootline_on_map))
2020 //infostream<<"Returning selected object"<<std::endl;
2025 //infostream<<"No object selected; returning NULL."<<std::endl;
2029 void Client::printDebugInfo(std::ostream &os)
2031 //JMutexAutoLock lock1(m_fetchblock_mutex);
2032 /*JMutexAutoLock lock2(m_incoming_queue_mutex);
2034 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
2035 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
2036 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
2040 u32 Client::getDayNightRatio()
2042 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2043 return m_env.getDayNightRatio();
2048 Player *player = m_env.getLocalPlayer();
2049 assert(player != NULL);
2053 void Client::setTempMod(v3s16 p, NodeMod mod)
2055 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2056 assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
2058 core::map<v3s16, MapBlock*> affected_blocks;
2059 ((ClientMap&)m_env.getMap()).setTempMod(p, mod,
2062 for(core::map<v3s16, MapBlock*>::Iterator
2063 i = affected_blocks.getIterator();
2064 i.atEnd() == false; i++)
2066 i.getNode()->getValue()->updateMesh(m_env.getDayNightRatio());
2070 void Client::clearTempMod(v3s16 p)
2072 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2073 assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
2075 core::map<v3s16, MapBlock*> affected_blocks;
2076 ((ClientMap&)m_env.getMap()).clearTempMod(p,
2079 for(core::map<v3s16, MapBlock*>::Iterator
2080 i = affected_blocks.getIterator();
2081 i.atEnd() == false; i++)
2083 i.getNode()->getValue()->updateMesh(m_env.getDayNightRatio());
2087 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server)
2089 /*infostream<<"Client::addUpdateMeshTask(): "
2090 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2093 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2098 Create a task to update the mesh of the block
2101 MeshMakeData *data = new MeshMakeData;
2104 //TimeTaker timer("data fill");
2106 // Debug: 1-6ms, avg=2ms
2107 data->fill(getDayNightRatio(), b);
2111 //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
2113 // Add task to queue
2114 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server);
2116 /*infostream<<"Mesh update input queue size is "
2117 <<m_mesh_update_thread.m_queue_in.size()
2121 // Temporary test: make mesh directly in here
2123 //TimeTaker timer("make mesh");
2125 scene::SMesh *mesh_new = NULL;
2126 mesh_new = makeMapBlockMesh(data);
2127 b->replaceMesh(mesh_new);
2133 Mark mesh as non-expired at this point so that it can already
2134 be marked as expired again if the data changes
2136 b->setMeshExpired(false);
2139 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server)
2143 infostream<<"Client::addUpdateMeshTaskWithEdge(): "
2144 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2149 v3s16 p = blockpos + v3s16(0,0,0);
2150 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2151 addUpdateMeshTask(p, ack_to_server);
2153 catch(InvalidPositionException &e){}
2156 v3s16 p = blockpos + v3s16(-1,0,0);
2157 addUpdateMeshTask(p);
2159 catch(InvalidPositionException &e){}
2161 v3s16 p = blockpos + v3s16(0,-1,0);
2162 addUpdateMeshTask(p);
2164 catch(InvalidPositionException &e){}
2166 v3s16 p = blockpos + v3s16(0,0,-1);
2167 addUpdateMeshTask(p);
2169 catch(InvalidPositionException &e){}
2172 ClientEvent Client::getClientEvent()
2174 if(m_client_event_queue.size() == 0)
2177 event.type = CE_NONE;
2180 return m_client_event_queue.pop_front();
2183 void Client::afterContentReceived()
2185 assert(m_itemdef_received);
2186 assert(m_nodedef_received);
2187 assert(m_textures_received);
2189 // Rebuild inherited images and recreate textures
2190 m_tsrc->rebuildImagesAndTextures();
2192 // Update texture atlas
2193 if(g_settings->getBool("enable_texture_atlas"))
2194 m_tsrc->buildMainAtlas(this);
2196 // Update node aliases
2197 m_nodedef->updateAliases(m_itemdef);
2199 // Update node textures
2200 m_nodedef->updateTextures(m_tsrc);
2202 // Update item textures and meshes
2203 m_itemdef->updateTexturesAndMeshes(this);
2205 // Start mesh update thread after setting up content definitions
2206 m_mesh_update_thread.Start();
2209 float Client::getRTT(void)
2212 return m_con.GetPeerAvgRTT(PEER_ID_SERVER);
2213 } catch(con::PeerNotFoundException &e){
2218 // IGameDef interface
2220 IItemDefManager* Client::getItemDefManager()
2224 INodeDefManager* Client::getNodeDefManager()
2228 ICraftDefManager* Client::getCraftDefManager()
2231 //return m_craftdef;
2233 ITextureSource* Client::getTextureSource()
2237 u16 Client::allocateUnknownNodeId(const std::string &name)
2239 errorstream<<"Client::allocateUnknownNodeId(): "
2240 <<"Client cannot allocate node IDs"<<std::endl;
2242 return CONTENT_IGNORE;